Commit Graph
5 Commits
Author SHA1 Message Date
Bohdan Triapitsyn 84d7303346 feat(desktop): add macOS dock badge for chats with unseen activity
Show a count of chats (root sessions) with unseen activity on the macOS
dock icon. The count is computed in the existing tray snapshot (full
cross-project list, not the capped tray view; a subtask's unseen rolls up
to its root only when subtask notifications are enabled) and pushed to the
main process over the existing desktop_tray_update IPC, which calls
app.setBadgeCount (0 clears it). The badge clears as sessions are marked
seen on window focus.

Add a Dock badge toggle in Appearance settings (default on, persisted,
darwin desktop only), localized across all dictionaries, with a matching
settings-search entry whose availability mirrors the render guard exactly.
2026-06-28 01:07:52 +03:00
Bohdan Triapitsyn 89aa389e6c fix(tray): show live activity for every session, not just one
The tray showed the busy indicator for at most one session at a time.
Several gaps in how per-session status was sourced stacked up to that:

- Status was derived by iterating each sync child store's session list,
  so a busy session missing from the list (created moments earlier from
  another window, the tray, or the API while session.created raced or
  the list got trimmed) was invisible even though the store's
  session_status map already held its busy entry.
- The upstream /session/status endpoint is directory-scoped — querying
  it without a directory only covers the server's own cwd, so there was
  no authoritative cross-project snapshot to fall back on.
- Status events for directories without a child store were dropped by
  the sync dispatcher, so sessions in unopened projects always rendered
  idle.

Fix, layer by layer:

- Add a cross-project session-status store (sync/global-session-status)
  fed two ways: the sync dispatcher now records status-bearing events
  (session.status / session.idle / session.error) for ALL directories,
  and the tray polls /session/status per visible-session directory to
  seed initial state and reconcile missed events. Snapshots clear stale
  entries both by directory key and by session id, so canonicalized
  (realpath) directory mismatches can't strand a busy entry.
- Read live status straight from each child store's session_status map
  instead of via its session list, and never let one store's idle entry
  clobber another store's busy/retry for the same session.
- Resolve a session as active when either source (child stores or the
  cross-project map) reports busy/retry, instead of letting the synced
  store's idle shadow the fallback.

Verified end-to-end in the dev shell: two sessions running concurrently
in different projects — including a brand-new session in the open
project root, the exact case that failed — now both show busy in the
tray, and both return to idle when they finish.
2026-06-10 14:01:10 +03:00
Bohdan Triapitsyn b27e8efb5f feat(desktop): per-session metadata and status icons in the tray menu
Enrich each session row in the macOS tray menu and refine its layout.

- Add a "project · branch" sublabel to every session row, resolved from the
  session directory: project-root sessions map to their project + live/cached
  git branch; worktree sessions map back to their parent project and use the
  worktree's branch. Branch resolution falls back live VCS → git store → worktree
  metadata, with normalized directory keys.
- Replace the inline text status glyph with a native left-aligned status icon
  (vertically centred across the title + sublabel). Idle rows use a transparent
  placeholder so every row shares the same gutter and both text lines align.
- Status icons use the app icon set: pulse (busy), check (unread), error-warning
  (error), loop-right (retry); all rendered as tinted template images.
- Drop the unread count "(n)" from the row label — the check icon already
  signals it and the number wasn't self-explanatory.
- Show the first 8 sessions inline; the rest stay in the overflow submenu.
- Subscribe the tray to the projects, worktree and git stores so subtitles stay
  current.
2026-06-10 01:58:11 +03:00
Bohdan Triapitsyn ae46060e1d feat(desktop): add provider usage submenu to the macOS tray
Surface rate-limit usage in the tray, mirroring the header/mobile usage view.

- New "Usage (Used/Remaining)" submenu groups enabled providers with their
  window limits (e.g. 5-Hour, Weekly Limit, Credits) and per-window values,
  reusing the quota store and the same formatting helpers as the rest of the UI.
- Honors the "configured to show" rule: only providers the user enabled for the
  dropdown and that report as configured are shown; when none qualify the submenu
  is omitted entirely.
- Tray-side data: build usage groups in useTraySync, push on quota-store changes,
  do one initial fetch for enabled providers on launch, and refresh on a
  desktop-only interval that respects the user's auto-refresh setting (no change
  to web behavior).
- Rows are read-only (greyed) info items; provider flush, windows indented.
- Show the first 8 sessions inline and move the rest into the overflow submenu.
2026-06-10 00:50:20 +03:00
Bohdan Triapitsyn 9cf79a8890 feat(desktop): macOS menu bar tray with live session state and mini-chat UX
Add an always-visible macOS status bar (tray) item that surfaces OpenChamber's
live state and acts as a quick launcher, plus a series of related desktop UX
fixes around mini-chat, window routing, notifications and shortcuts.

Tray (new):
- Monochrome template cube glyph that adapts to the menu bar light/dark.
- Icon-driven activity indicator: a smooth, eased, infinite "breathing" fill
  while sessions are busy; a static filled cube when finished sessions are left
  unread; a plain outline when idle. Text counters next to the icon only for
  actionable states (pending approvals, errors).
- Menu lists active sessions (status glyph, branch, unread count) with overflow
  rolled into a submenu; pending permission/question approvals with inline
  Allow once / Allow always / Deny; quick actions (New Session, New Mini Chat,
  Show OpenChamber, Quit). Header shows the active instance name
  ("Local OpenChamber" or the remote host label) for multi-window clarity.
- Session list sourced from the global (cross-project) sessions store, sorted by
  last-updated, independent of which directories are currently open; live
  status/unread/branch merged in from directory sync stores where available.
  Sub-session (multi-run) activity rolls up to the parent row.
- Event-driven updates (global store + directory stores + notifications +
  registry) with a short debounce; polling kept only as a slow safety net.

Tray/window routing:
- Opening a session from the tray targets the surface the user was last on: if a
  mini-chat is active it switches that existing window to the session in place
  (no new window); otherwise the main window (revealed without a reload).
- app.activate (dock click) restores the last-focused/minimized window instead
  of spawning a new main window; only creates one when nothing is left.
- "Open in main window" and tray session-open now create the main window when
  none exists, queuing the session as a pending deep-link so it opens once the
  fresh renderer is ready.

Mini chat:
- New Mini Chat is now a customizable shortcut, exposed in Settings > Shortcuts,
  in the File menu (hint only, renderer owns the binding), and in the tray.
- Themed splash backdrop on window open to remove the white flash / flicker;
  dismissed once content is ready, leaving the content's single cube logo.
- Mini-chat can switch sessions in place via openchamber:open-session.

Notifications:
- The active/selected session only counts as "seen" when the window is focused,
  so turns completing while the app is backgrounded raise an unread marker;
  refocusing the window clears it.
2026-06-10 00:20:23 +03:00