Skip to content

Port upstream 0.67.0: provider switcher shortcut editor and persistence (stacked on #693) - #700

Draft
Finesssee wants to merge 2 commits into
port/micro-0.67.0-provider-switcher-keysfrom
port/micro-0.67.0-provider-switcher-shortcut-editor
Draft

Finesssee wants to merge 2 commits into
port/micro-0.67.0-provider-switcher-keysfrom
port/micro-0.67.0-provider-switcher-shortcut-editor

Conversation

@Finesssee

Copy link
Copy Markdown
Collaborator

Summary

PR 2 of 2 for the deferred upstream 0.67.0 "provider switcher keyboard shortcuts" feature. PR 1 (#693) added the fixed keys (Left/Right, Ctrl+1..9). This PR makes them configurable:

  • New switcher_shortcuts setting (overrides only; omitted when everything is default), validated in Rust (rust/src/switcher_shortcuts.rs) and mirrored in apps/desktop-tauri/src/lib/switcherShortcuts.ts.
  • The settings snapshot exposes the fully resolved map; the settings patch carries overrides and replaces the stored map ({} restores defaults).
  • New Settings > Menu > Provider switcher shortcuts editor (record, clear to none, reset to defaults, inline localized errors for duplicate / reserved / invalid keys).
  • The tray flyout and pop-out switcher hook follow the configured shortcuts.
  • An invalid stored map falls back to defaults on load with a tracing warning; it does not break the rest of the settings.
  • ShortcutCapture gained optional compose, recordingHint and emptyLabel props so the editor reuses it without a mode flag.
  • docs/CONFIGURATION.md documents the editor, the switcher_shortcuts key and the rules.

Approved design

From the approved note design-provider-switcher-shortcuts.md, implemented as written:

  • Grammar follows upstream: modifier order ctrl alt shift; keys are left, right, ,, or an ASCII letter/digit; none disables an action. cmd is accepted as an alias for ctrl.
  • Reserved: ctrl+r, ctrl+q, ctrl+,, ctrl+w, and letters/digits/comma with only shift or no modifier. Duplicates and unknown actions are rejected.
  • 11 actions: previous, next, select1..select9. Defaults: left, right, ctrl+1..9.
  • Storage: only non-default overrides in settings.json; the snapshot exposes the resolved map.
  • The menu settings tab id is reused, so the tab whitelist is unchanged.
  • Open questions resolved with the note's recommendations.

Upstream reference

steipete/CodexBar 0.67.0, provider switcher keyboard shortcuts (tag-pinned read only).

Ported / Deferred

Ported: persistence, validation (Rust + TS), snapshot/patch plumbing, Settings > Menu editor, hook wiring, docs.

Deferred: the portable-preferences key switcherShortcuts, because portable preferences does not exist on this base. It can be added when that feature lands.

Validation

  • cargo +1.98.0 fmt --all
  • cargo +1.98.0 clippy --workspace --all-targets -- -D warnings (both manifests): clean
  • cargo +1.98.0 test -p codexbar: 2175 passed, 0 failed
  • cargo +1.98.0 test -p codexbar-desktop-tauri: 465 passed, 1 failed. commands::tests::bootstrap_payload_exposes_every_provider_variant reports 79 catalog entries vs 78 active providers. get_bootstrap_state adds a deprecated provider to the catalog when it is enabled in the machine-local settings, so this depends on the developer machine's state and is not touched by this change (this PR only adds a field to the settings snapshot). CI runs on a clean profile.
  • pnpm test (vitest): 71 files, 449 tests passed
  • pnpm run build (tsc + vite): passed; lint has only pre-existing warnings

Affected areas

  • rust/src/switcher_shortcuts.rs (+ tests.rs), rust/src/settings.rs, rust/src/settings/raw.rs, rust/src/locale.rs, rust/src/locale/en-US.ftl
  • apps/desktop-tauri/src-tauri/src/commands/settings.rs, commands/bridge.rs
  • apps/desktop-tauri/src/: lib/switcherShortcuts.ts, hooks/useProviderSwitcherKeys.ts, hooks/useTrayPanelController.ts, surfaces/PopOutPanel.tsx, surfaces/settings/SwitcherShortcutsSection.tsx, surfaces/settings/tabs/DisplayTab.tsx, components/ShortcutCapture.tsx, i18n/keys.ts, types/bridge.ts
  • docs/CONFIGURATION.md

UI proof

This PR changes Settings chrome (Menu tab). CUA / desktop proof has not been captured: the automation run that produced this PR is not permitted to launch the desktop app. Coverage is Vitest component tests (SwitcherShortcutsSection.test.tsx, 8 tests) only. A CUA retest at CODEXBAR_PROOF_MODE=settings:menu on a fresh debug build is still required before merge; hence this is a draft.

Adds the switcher_shortcuts setting (overrides only, validated in Rust and mirrored in TS), the resolved map in the settings snapshot, and a Settings > Menu editor. The switcher hook now follows the configured shortcuts.
@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Thermo-nuclear review

Head reviewed: 3b02210e (stacked on #693). Spec: design-provider-switcher-shortcuts.md (upstream 0.67.0 ProviderSwitcherShortcuts, tag-pinned). Verdict: no blocking findings; nothing to fix.

Correctness against the spec

  • Grammar and rules match the approved note: modifier order ctrl alt shift, keys left|right|,|[a-z0-9], cmd folded to ctrl, none disables and frees its key, duplicates and unknown actions rejected, reserved ctrl+r/q/,/w, letters/digits/comma need ctrl or alt. alt+f4 / alt+tab fall out as invalid (F4/Tab are outside the grammar), as the note allows.
  • Rust (switcher_shortcuts.rs) and TS (lib/switcherShortcuts.ts) were compared branch by branch on empty modifier lists, duplicate modifiers (ctrl+cmd+1), whitespace, none with modifiers, and non-ASCII keys. They agree.
  • Storage is override-only, skip_serializing_if keeps settings.json clean, the snapshot exposes the resolved map, and the patch replaces the stored map ({} resets). An invalid stored map is sanitized on load with a tracing::warn! and never blocks the rest of the settings. A rejected patch leaves the stored value untouched (covered by a test).
  • Both surfaces (tray panel and pop-out) read settings.switcherShortcuts through useSettings, which re-fetches on settings-changed, so an edit in the detached Settings window reaches them live.

Structure and size

  • No file crosses 1000 lines (bridge.rs was already 1063, +4; settings.rs 1437 to 1444, already over). New logic lives in its own module plus a sibling tests.rs, and the editor is its own component rather than more branching in DisplayTab.
  • ShortcutCapture gained compose / recordingHint / emptyLabel. These are a function and two strings with the previous behavior as defaults, not a boolean mode flag, so I am not asking for a split.
  • Locale keys are en-US only. Other catalogs are already 100 to 200 keys behind en-US and fall back, so this matches repo practice.
  • No new dependencies. Reserved and default tables are const arrays, no magic.

Non-blocking notes (left as is, no change requested)

  1. Rust and TS each carry a copy of the grammar (~100 lines each). The approved note asks for frontend validation for live feedback, so this is by design. The keep in step comments plus mirrored test cases are the guard; a shared fixture would be the next step if it ever drifts.
  2. resolve_or_default (Rust) and the try/catch in resolveSwitcherShortcuts (TS) are defensive only: load already sanitizes and writes are validated. They are cheap and the snapshot conversion is infallible, so I would not spend a refactor on them.
  3. The snapshot carries the resolved map while the patch carries overrides. This asymmetry is what the note specifies and it is tested in both directions.

Validation I re-ran

  • cargo +1.98.0 fmt --all -- --check: clean.
  • vitest run on switcherShortcuts, useProviderSwitcherKeys, SwitcherShortcutsSection, ShortcutCapture: 4 files, 49 tests passed.
  • Full cargo and clippy results are the PR-body numbers; the one failing Tauri test named there (bootstrap_payload_exposes_every_provider_variant) is machine-state dependent and untouched here.

UI proof

Settings chrome changed (Menu tab). A proof build from this head and a proof kit are being prepared for the CUA run at CODEXBAR_PROOF_MODE=settings:menu; the PR stays draft until that is captured.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Thermo-nuclear review follow-up (second pass)

Reviewed by Codex gpt-6-luna (xhigh); verified and validated by Claude. The earlier review comment stands; this pass found and fixed the following.

Fixed:

  • P2 lib/switcherShortcuts.ts: letter shortcuts were read from event.code, which follows US key positions and breaks configured letters on other layouts. Letters now use event.key; digits and comma still use code so Shift+1 and Shift+, keep working. Tests added.
  • P2 locale: the 12 switcher-shortcut strings were missing from es-MX, ja-JP, ko-KR and ru-RU (English fallback). Added them plus a regression test.
  • P2 ShortcutCapture / SwitcherShortcutsSection: Record and Clear buttons and the status chip had no row context for screen readers. Added per-action accessible names and a component test (also corrected one assertion in that test: the default Next binding is right, so Clear is enabled).

Left:

  • Fresh-build CUA proof (including WebView2 handling of Ctrl+digit) is still outstanding; it needs a desktop build and run, which this pass did not do. PR stays draft.
  • Tauri crate clippy/tests not re-run: no Rust files in that crate changed.

Commands run: cargo +1.98.0 fmt --all -- --check; cargo +1.98.0 clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings; cargo +1.98.0 test --manifest-path rust/Cargo.toml --lib -- switcher_shortcut locale (32 passed); vitest on switcherShortcuts, useProviderSwitcherKeys, SwitcherShortcutsSection, ShortcutCapture (53 passed); tsc --noEmit clean.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

CUA proof (rerun)

Build commit: 66dc1c4212db0a785ebf3e9dcc3606c04c8c800f (current PR head, "Address thermo review"). Debug build via pnpm --dir apps/desktop-tauri run tauri:build:debug.

Proof-only patch (throwaway, restored with git restore, never committed): root Cargo.toml [patch.crates-io] dirs = { path = ".../proof-shim/dirs" }, which redirects home/config/data/cache dirs under CODEXBAR_PROOF_HOME. Settings live in an isolated profile (Codex only, theme auto, seeded Codex 42% / 67% via CODEXBAR_SEED_USAGE_JSON). No provider fetch is involved in this change.

Driven with the cua-driver CLI only, background delivery only (UIA invoke, PostMessage keys, window-state screenshots), proof windows on the second monitor.

# Assertion Result
0 No real email/account from the machine visible PASS
1 Menu tab shows "Provider switcher shortcuts" with helper text, 11 rows (left, right, ctrl+1..9), Record/Clear per row, Reset to defaults PASS
2 Theme stays dark under auto, no clipped text PASS
3 Previous provider: Record then Backspace shows "Disabled" and its Clear is disabled PASS
4 Item 5: Record then bare a shows "That key is reserved. Letters, digits and comma need Ctrl or Alt; ..." and the chip is unchanged PASS
5 Item 3: Record then right (taken by Next) shows "Go to item 3: That key is already assigned to another action." (red) and the chip stays ctrl+3 PASS
6 A following valid edit (item 2 set to left) succeeds and clears the error line PASS
7 Reset to defaults restores all 11 chips to left, right, ctrl+1..9 PASS
8 Persistence: Previous disabled + item 2 = left survive an app restart; other rows default PASS
9 Live effect in tray panel with that map: bare left selects the Codex tile (Session 42%, Secondary 67%) PASS
10 Modifier combinations (Shift+Right, Ctrl+Alt+2, Ctrl+4 duplicate, Ctrl+R reserved) and the new aria-label / layout-aware letter-key changes NOT TESTED: background key delivery of modifier combos is refused by the WebView2 surface (background_unavailable), and foreground input was not used because the desktop is in use. Covered by the unit tests only.

Notes: settings.json is DPAPI-wrapped, so the on-disk map was not inspected; persistence was checked through the UI after restart.

Screenshots (local, not committed), C:\Users\FSOS\AppData\Local\Win-CodexBar\port-audit\proof\700\shots\: 01-tall.png (initial), 05-backspace.png, 06-invalid-a.png, 07-duplicate.png, 08-success-left.png, 09-reset.png, 11-persist-after.png, 12-tray-before.png, 13-tray-after-left.png.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant