feat(desktop): always-on widget with a machine-wide global face - #5
Closed
fzn0728 wants to merge 1 commit into
Closed
feat(desktop): always-on widget with a machine-wide global face#5fzn0728 wants to merge 1 commit into
fzn0728 wants to merge 1 commit into
Conversation
The overlay no longer lives only inside a bound Claude Desktop session. When no exact session binding exists, the widget can now show a "global" face with machine-wide totals across Claude Code, Codex, and Cline. - overlay-bridge: new global-snapshot command returning identity plus usage-history totals (today, lifetime, streak), memoized for 60s - runtime UI: data-mode=global face — TODAY label, identity header, lifetime/streak from meterStats, idle gauge; settings identity rendering shared between session and global faces - native overlay: menu-bar StatusBarController with show/always-visible toggles persisted to visibility.json, LaunchAgent bootout on quit so KeepAlive stops resurrecting the app, and Codex window following via codexBundleID Tests: 145/145 pass (node --test). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@fzn0728 is attempting to deploy a commit to the t54 labs Team on Vercel. A member of the Team first needs to authorize it. |
Collaborator
Author
|
Withdrawing this for now — I want to rethink the product rationale for an always-on face before asking you to weigh in, particularly the visibility trade-off it makes against Claude compatibility invariant 3. Will reopen or resubmit once that's settled. Sorry for the noise. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Outcome
The Claude companion currently has exactly one reason to be on screen: a frontmost Claude window with an exactly-bound Code session. Every other moment — a Chat window, a Codex window in front, the desktop — it is invisible, so the numbers it collects are only reachable while you happen to be in the one place you already know what you spent.
This adds a second, non-session face so the widget can stay useful outside a bound session, and a menu-bar item so it is findable at all times.
User-visible behavior
New "global" face. When there is no exact session binding, the panel can render machine-wide totals — today, current streak, lifetime — read from the existing usage-history cache, with the identity handle in the header and the gauge parked at idle. It is a different face, not a fallback:
status: "global",binding.exact: false, no session id, no per-session numbers.Menu-bar item. A
gaugestatus item withShow Widget,Always Show on Desktop,Open Dashboard…,Grant Accessibility Access…, andQuit. The two toggles persist tovisibility.jsonin the existing state directory, so a relaunch keeps the user's choice. The app is a Dock-less LaunchAgent accessory, so before this there was no user-reachable surface to hide, show, or quit it.Quit actually quits.
Quitand the settings power button nowlaunchctl bootoutthe LaunchAgent before terminating, soKeepAlivestops resurrecting the process. Previously quitting was a no-op from the user's point of view.Codex window following. A frontmost Codex window gets the global face pinned to it, the same way a Claude window does.
Invariant this deliberately changes — please review this part first
Claude compatibility check 3 in CONTRIBUTING reads: "Chat, Cowork, Settings, auxiliary windows, and non-frontmost Claude hide the panel."
This PR changes that. Those cases now show the global face instead of hiding, and with
Always Show on Desktopenabled the panel also parks on the desktop when no host is frontmost.I read the intent behind that invariant as never show one session's numbers while the user is looking at something else, and that intent is kept strictly: the non-session face carries no session id and no session totals, and
binding.exactstaysfalse. But the letter of the rule changes, and it is your call whether that trade is right. Both behaviors are reachable —Show Widgetoff restores full hiding,Always Show on Desktopoff restores host-only visibility — and I can flip the defaults to opt-in if you would rather the current behavior stay the default.The exact-binding invariant itself is untouched: a frontmost Claude window with a Code session still binds only by exact
local_<uuid>and still gets the session face, with no recency or display-name fallback anywhere in the new path.Implementation
overlay-bridge.mjs— newglobal-snapshotcommand returning identity plus usage-history totals, memoized for 60s because the native layer polls on a steady cadence. No new collection: it reads the cache that community sync already maintains.runtime/token-meter-ui.js—data-mode=globalface; settings identity, version line, and privacy toggle rendering factored out and shared with the session face rather than duplicated.TokenMeterClaudeOverlay.swift—StatusBarController,WidgetPreferences, LaunchAgent bootout,codexBundleIDwindow following.Security and privacy
No new data leaves the machine and no new collection is added. The global face reads the same local usage-history cache the meter already builds, the privacy toggle keeps working identically on both faces, and
visibility.jsonholds two booleans. The companion stays independent of Claude.app — nothing here quits, relaunches, or touches the host.Test evidence
npm test— 180/180 pass.npm run check— clean (73 JS, 22 shell, 20 Markdown files).swiftc -typecheckon all three native sources — exit 0, no diagnostics.test/claude-overlay-bridge.test.mjsasserts theglobal-snapshotresponse shape over the real stdio protocol, andtest/runtime-ui-layout.test.mjsasserts the global face renders machine-wide totals with no session.Branch is rebased onto
af48705(v0.3.1) and is 1 commit ahead, 0 behind.Not covered, flagging honestly
docs/install-claude-desktop.mdand the integration README likely want a paragraph on the menu bar and the two visibility toggles — happy to add that here once you have said whether the defaults should stay as they are, since the wording depends on that answer.