From brand-assets/ masters (C + orbiting-dot on brand blue):
- Regenerated PWA icons (192/512/maskable), apple-touch (180), favicons
(32/16 + root favicon.ico) from the 1024 master; bumped manifest to v3.
- Rebuilt desktop/build/icon.ico as a proper multi-size ICO (16..256,
PNG-compressed) to fix the tiny/blurry taskbar icon.
- Updated mobile masters (resources/icon.png 1024, splash.png/splash-dark.png
2732) for capacitor-assets to regenerate native icons/splash.
- Wired the animated branded orbit loader into the app boot 'Loading…' screen;
added favicon/theme-color links to index + home heads.
- logo.png (horizontal wordmark) left untouched — masters have no wordmark.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Provisioning now keys the Biz Connect account on bz.bizgazeUserId (the same
value whether the person signs in with email or mobile) instead of the typed
identifier, so both logins resolve to a single contact. Legacy rows get the
person-id stamped on next login; an existing duplicate created under the same
identifier is folded in via a transactional users.mergeInto() that reassigns
all messages/memberships/reactions/votes/favorites/ownership to the survivor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Loads @twemoji/api (jsDelivr) and renders emojis as Twemoji images in messages, the emoji
picker, and reactions (twemojify at each render point). Picker inserts via data-emoji so it
survives the <img> swap. Falls back to plain Unicode if the CDN is unavailable. build batch25.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pinned 'You' chat at the top of the list (always available); messaging yourself works
(to===me), with no self push/echo notification and a 'Message yourself' header. Self is
filtered from the normal contacts and shows no status dot. build batch24.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conversations DTO now returns last_status (sent/delivered/read for DMs; sent for groups) for
my last message. The list row shows the matching tick (single/double, blue when read) and a
red 'Draft:' indicator when there's unsent text. Live 'sent' on send/receive; upgrades to
delivered/read on refresh. e2e 119.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Unsent text is saved per conversation (survives switching chats and reloads) and restored
when you reopen the chat; cleared on send. (#1)
- Emoji picker: stopPropagation on tab/grid clicks so switching category no longer closes it
(the re-render was detaching the clicked node -> outside-click handler fired). (#6)
build batch23.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- node-notifier's WindowsToaster forwards raw opts to SnoreToast, so inject -tb (reply box)
+ -p (image). Reuses its named-pipe + result parsing (exit 5 = TextEntered). No pwsh needed.
Logs the raw toast result to userData/toast-debug.log to confirm the reply field on real HW.
- home.html: notifAvatarDataUrl draws the DM sender's pic / group's DP (else colored initials)
to a round PNG and passes it as the toast image. Reply -> sendReplyTo; click -> open chat.
- dropped powertoast (ESM + needs pwsh 7, absent here). build batch22.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reply to a chat straight from the OS notification, no app switch:
- node-notifier (bundles SnoreToast) renders a native Windows toast with a reply box —
Electron's own Notification can't do Windows inline reply.
- main.js reply-notification handler resolves {text}|{open}|null; preload exposes replyNotify.
- home.html notify() routes chat toasts through it on desktop: a typed reply -> sendReplyTo()
POSTs to /api/messages without opening the app; a click opens the chat. Web/PWA path unchanged.
- Works only in the installed app (needs the installer's AppUserModelID). desktop 0.1.2, build batch21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Downloads/updates:
- config: DOWNLOADS_DIR (override to a mounted volume in prod).
- static.js: serves /downloads/* (installer, latest.yml, .blockmap) with range support for
resumable + differential auto-updates; /download/windows redirects to the current .exe
(stable link). Landing page gets a "Download the Windows desktop app" button (hidden in-app).
Install tracking (who installed the app):
- db app_installs + repos.appInstalls (upsert by install_id, fills in the user on sign-in).
- POST /api/v1/telemetry/install (records install + user once authenticated);
GET /api/v1/admin/installs (admin: list installs with user/version/os/last-seen).
- desktop main.js: stable per-install id in userData, exposed via preload
(bizConnectNative.installId/version/os); home.html reportInstall() posts it after login.
- e2e: +2 checks (telemetry recorded, admin sees it). 119/119. build batch20.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- home.html: login is now a real <form> with a submit button, so Enter (or click) both
submit natively — replaces the ad-hoc keydown handler. (build batch19; header stays removed)
- desktop/main.js: app.setAppUserModelId('com.bizgaze.connect.desktop') so Windows resolves
the installed shortcut and toasts read "Biz Connect" instead of "electron.app.BizConnect".
- desktop version → 0.1.1; installer rebuilt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only the chat LIST had styled scrollbars; the chat message area and every other scroll
region fell back to the OS scrollbar, which in the Electron/Windows shell shows classic
up/down stepper arrows. Added a global ::-webkit-scrollbar style (thin, rounded thumb,
scrollbar-button hidden) + scrollbar-width:thin to home.html, share.html, connect.html,
dashboard.html. (home build batch18)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the rail's unread-chat count onto the Windows taskbar icon (overlay) and the
macOS/Linux dock badge. updateRailUnread() draws a small red count badge on a canvas
and hands it to the shell via bizConnectNative.setUnread(count, dataUrl); main sets it
with win.setOverlayIcon + app.setBadgeCount. Clears at 0, shows 99+ past 99. No-op in a
browser/mobile. (home.html build batch17)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- desktop/main.js: grant media (camera/mic), display-capture, notifications, clipboard,
fullscreen, pointerLock for the app origin (Electron denies these by default, which
silently broke meetings' camera/mic). Adds setPermissionRequestHandler +
setPermissionCheckHandler on the app session.
- Clicking a notification now raises + focuses the window: preload exposes
bizConnectNative.focusApp(), main handles 'focus-window' IPC, and the web notify()
onclick calls it when running in the desktop shell. (home.html build batch16)
- CLIENTS.md: Phase D — inline-reply notifications (Windows Toast RemoteInput /
Android RemoteInput / iOS UNTextInputNotificationAction) queued right after packaging.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
home.html: in a Capacitor app shell, setupPush() now uses the native FCM/APNs path
instead of Web Push — requests permission, registers, POSTs the OS device token to
/api/v1/devices, deep-links on notification tap (selectChat), and unregisters the
token on logout. Web Notification prompts are suppressed on native. Fully inert in a
normal browser (Web Push unchanged). build batch15.
CLIENTS.md Phase B push items checked off.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chat / shared media:
- Media/Docs/Links: clean underline tabs (green active), audio & video now
classified as Media and rendered as tiles (download + headphone/play +
duration) instead of broken-image glyphs; image thumbnails -> lightbox
- Drag-and-drop a file/video/image onto a conversation to send it
- Fix: removed #chatPanel{position:relative} override that collapsed the
conversation pane (messages spilled into a clipped right-edge strip)
- "Media, links & docs" row cleaned up (no folder/placeholder icon); media
popup keeps the back arrow, drops the redundant close button
Presence / status:
- Single current-status row with an arrow that expands Available/Away/On leave
- On leave = circle with minus, In a call = solid red indicators
- Fix: selected-status tick now follows the chosen option
Icons: added headphones + play; bumped icons.js cache-bust to v4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Logo was dark-on-blue (low contrast); now centered on a white tile like the
header treatment. Icon URLs versioned (?v=2) so browsers/installs fetch the new
ones. Build marker -> pwa2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- manifest.json (standalone display, theme color, maskable icons 192/512).
- generated square icons + apple-touch-icon (180) from the logo.
- apple-mobile-web-app + theme-color meta in home.html.
- sw.js gets a no-op fetch handler so it meets installability criteria (still
no caching). static.js serves .json/.webmanifest with correct MIME.
- Installing as a PWA also unlocks Web Push on iOS (Apple requires Add to Home Screen).
Build marker -> pwa1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Browsers were serving a cached old home.html on normal reloads (only incognito/
hard-refresh got the new one). HTML now sends Cache-Control: no-store; versioned
assets keep ETag revalidation. Bumps build marker to push4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
subscribePush() swallowed all errors, so if pushManager.subscribe() failed
(e.g. called before the service worker was active) nobody ever subscribed and
there was no trace. Now: await serviceWorker.ready before subscribing, and
console.log/warn each step so the real failure is visible. Server send path
verified independently (web-push builds valid VAPID requests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The server echoes the sender's own message over WS before returning the HTTP
response, so onChatMessage could append it before sendMessage's await resolved,
then sendMessage appended again -> double. Both append paths now dedup by id.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Page-level Notifications can't fire when a tab is frozen/closed (and never on
mobile), which is why recipients on another tab/app got nothing. Adds a
notification-only service worker (sw.js, no caching) + Web Push:
- push.js: optional web-push wrapper (no-op unless web-push installed AND
VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY set -> app unaffected if unconfigured).
- push_subscriptions table + R.pushSubs repo (upsert by endpoint, prune dead).
- /api/push/vapid|subscribe|unsubscribe; DM + group message routes also send a
Web Push to recipients.
- Client registers /sw.js, subscribes when permission granted; hidden-tab popups
are left to push to avoid double-notifying (pushActive flag); SW suppresses the
OS popup when a tab is visible. Removes the old code that unregistered SWs.
Requires (prod, once): npm install + VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY/VAPID_SUBJECT env.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- callEnd is now a rotated-handset hang-up icon (was a phone-off placeholder).
- All pages reference /icons.js?v=3 so browsers/proxies fetch the corrected
file instead of a stale cached copy (fixes 'old end icon' + icons not
appearing until a re-render when an old/404 icons.js was cached).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Server echoes your own message back over WS (multi-tab/device sync) and
sendMessage already appended it optimistically; onChatMessage now skips the
append if the id is already in the thread.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User-facing
- New post-login home (/home): chat rail + Share/Connect (embedded) + Meeting; login lives here when logged out
- Landing: "Log in with BizGaze" + no-login screen share
- Console replaced by a role-scoped Dashboard (/dashboard): admins see all team sessions, others see only their own; stats + CSV/PDF export
- Recordings saved as MP4 (H.264/AAC) with WebM fallback; old .webm still downloadable
- Fix: duplicate "Sign in" on the login card
Auth / integration
- BizGaze as identity provider: /api/login validates against BIZGAZE_LOGIN_URL (env-gated) and provisions a local user
- Phase 2 start: /api/v1 alias for all /api routes; Authorization: Bearer accepted across HTTP + WS; login returns a token (for native desktop/mobile clients)
Backend refactor (Phase 1, behavior-preserving)
- Split server.js into config/lib/session/presence/routes/static/signaling + repos (data-access) + bizgaze (service)
- All SQL behind repos.js, tenant-scoped (tenantId == team_id for now)
- e2e updated to current flow (21/21 pass before and after)
Docs: ARCHITECTURE.md (target architecture + phased plan), CLAUDE.md repo layout, .env.example BIZGAZE_LOGIN_URL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>