Tray-anchored flyout: resize, drag-reorder, zoom (+ Claude OAuth auto-refresh) - #120
Conversation
|
Thanks for the PR, I will review this. |
Thermo-nuclear code quality reviewRequesting changes on structure, not behavior.
|
Rework "Pop Out Dashboard" into a tray-anchored flyout (SurfaceMode::TrayPanel)
above the taskbar icon, so it no longer collides with the PopOut window
("Show Window").
- Anchored popover: always-on-top, blur-dismiss, skip-taskbar; left-click on the
tray toggles it. The frontend owns sizing: auto-fit to content until the user
drags an edge, then remember the manual size (persisted via set_flyout_size).
- DPI-safe sizing: use Tauri physical units end-to-end (innerSize + PhysicalSize)
behind an in-flight guard, fixing a scale-factor growth bug on 150% displays.
- Resize grips: a borderless WebView2 can't drive native edge-resize, so expose
top/left/corner grips that call startResizeDragging. This needs the
core:window:allow-start-resize-dragging capability, now granted.
- Keep the flyout open during resize/drag: the Win32 modal size loop and OLE
DoDragDrop each fire a transient Focused(false); a gesture guard suppresses
blur-dismiss, re-armed on refocus or after a 15s timeout.
- Provider drag-reorder: set dragDropEnabled=false so wry stops hijacking
Chromium's native drop target, then persist order via reorder_providers and
emit settings-changed so the detail cards follow.
- Zoom: a Windows-volume-style slider in the footer applies CSS zoom to the
surface (trayScalePercent 100-200%) and re-measures live; the footer pins to
the bottom when user-sized, and provider cards go side-by-side past 640px.
- Positioning: with no remembered geometry and no tray anchor, prefer the
primary monitor so the flyout/popout can't open off-screen on a secondary
display.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Claude Code OAuth access token lives only ~8h. CodexBar read it but never used the refresh token, so usage requests failed with "sign-in expired" until the CLI happened to refresh it. Refresh proactively against the token endpoint when the token is near expiry, atomically writing back the claudeAiOauth block (preserving mcpOAuth) with an in-memory fallback and a re-read guard against a concurrent CLI refresh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Show Window Addresses review items nesszer#3 and nesszer#4 (structure, not behavior). The "Pop Out Dashboard" flyout and "Show Window" were two mutually-exclusive states of the single main window, so opening one closed the other. Give the flyout its own OS window (label "flyout", modeled on the detached settings/ floatbar windows) so the two coexist; the flyout keeps blur-to-dismiss. StoredSize map (load_size/save_size) in geometry_store backs set_flyout_size, so the surface command layer no longer leaks geometry-store coordinate details or blurs size-vs-position semantics. window rather than a "mode != TrayPanel" negative special-case threaded through shared window-layout code. - New shell/flyout_window.rs: builder with disable_drag_drop_handler() (required for the provider-grid HTML5 drag-reorder), open/toggle/hide/is_open, and its own Focused-blur handler porting all four anti-flicker guards. - Tray left-click and "Pop Out Dashboard" open the flyout window; "Show Window" stays on the main window. Both can now be open at once.
…che by source Addresses review items #1 and nesszer#2 (structure/ownership, not behavior). #1 — `providers/claude/oauth.rs` had grown to ~1138 lines mixing usage fetching, refresh HTTP, refreshed-token caching, credential persistence, JSON patching and tests. Split into a directory module: - oauth/mod.rs — ClaudeOAuthFetcher: usage fetching + orchestration (fetch/fetch_usage/rate-limit/snapshot) + types. - oauth/refresh.rs — the OAuth token refresh HTTP call + its response type. - oauth/credentials_store.rs — credential loading (env/file/keyring), persistence, and the refreshed-credentials cache. The public surface is unchanged: `pub use oauth::ClaudeOAuthFetcher` still resolves (directory module), and ClaudeOAuthFetcher's external API (new/fetch/ fetch_with_access_token) is untouched. nesszer#2 — the refreshed-credential cache was a single process-global `Option<ClaudeOAuthCredentials>` shared across every credential source. Because `load_credentials` tries env→file→keyring and env tokens have no `expires_at`, a file-refreshed token could shadow a freshly-read environment token (the `(Some(_), None) => true` freshness arm). The cache is now `HashMap<CredentialSource, _>` keyed by an explicit `CredentialSource { Environment, File(path), Keyring(account) }`; `load_credentials` returns the source and `ensure_fresh_credentials` threads it through the cache lookup/store. Regression test `env_source_not_shadowed_by_file_cache` covers it. Behavior-preserving. cargo check clean; core `codexbar` 490 tests, tauri 280 tests, frontend 127 tests all green.
844caf3 to
135a064
Compare
|
Thanks for the thorough review. Rebased onto current #1 — split
Public surface is unchanged: #2 — key the refreshed-credential cache. Replaced the process-global #3 — #4 — TrayPanel sizing policy. The flyout now lives in its own dedicated OS window (label Rebase note. Two of the upstream commits touched the same code paths. In particular, folding the separate-window flyout on top of #121 (vertical-taskbar tray placement) required routing the flyout's no-anchor reanchor fallback through Verification (Windows 11):
|
Summary
Reworks the tray experience and fixes several Windows-specific bugs found along the way. Rebased onto current
main(0.38.0); the diff is the two commits below.Tray flyout (commit 1)
SurfaceMode::TrayPanel): always-on-top, blur-to-dismiss, skip-taskbar; left-clicking the tray toggles it. Previously "Pop Out Dashboard" and "Show Window" both mapped to the same PopOut window and collided.set_flyout_size).innerSize/PhysicalSize) behind an in-flight guard. Fixes a grow-on-every-open bug on 150% displays where logical / physical / Win32 scale factors disagreed.startResizeDragging. Root cause of "grips appear but never work": thecore:window:allow-start-resize-draggingcapability was never granted, so every call was denied by the ACL and swallowed by a.catch. Now granted (pluscore:window:defaultforinnerSize/scaleFactor, which the sizing bookkeeping depends on). ACL-gated calls no longer swallow errors silently.DoDragDropeach fire a transientFocused(false); without a guard the flyout hid the instant you pressed a grip or started a drag. A gesture-scoped guard suppresses blur-dismiss during the gesture, re-armed on refocus or after a 15s timeout.dragDropEnabled: falseon the main window so wry stops replacing Chromium's native OLE drop target (that hijack silently killed all in-page HTML5 drag-and-drop;dragover/dropnever fired). Order persists viareorder_providers, which now emitssettings-changedso the detail cards follow the new order.zoomto the surface (trayScalePercent, 100–200%, live preview + debounced persist), re-measured live so auto-fit tracks it; the footer pins to the bottom when user-sized; provider cards lay out side-by-side past 640px.Claude OAuth auto-refresh (commit 2)
The Claude Code OAuth access token lives only ~8h. CodexBar reads it but never uses the refresh token, so usage requests fail with "sign-in expired" until the CLI happens to refresh it. This refreshes proactively when the token is near expiry, atomically writing back the
claudeAiOauthblock (preservingmcpOAuth), with an in-memory fallback if the write fails and a re-read guard against a concurrent CLI refresh.Maintainer heads-up (please read before merging this commit): the refresh works by calling Anthropic's OAuth token endpoint with the Claude Code
client_id— constants that ship inside the official client, used here only to renew the user's own token. It's the same "reuse the user's existing Claude credentials" category the provider already relies on to read usage, extended to keep the session alive — but it depends on an endpoint Anthropic hasn't publicly documented, so it could break if they change it. If you'd rather not carry that, commit 1 (the tray flyout) stands entirely on its own — say the word and I'll drop commit 2 or move it to its own PR.Verification
tsc --noEmit: cleancargo test -p codexbar-desktop-tauri): 269 passedtauri build --no-bundle: success (release, ~1m)🤖 Generated with Claude Code