crates/b2-desktop/tauri.conf.json sets no menu, so Tauri's Menu::default() applies. That means ⌘Q, ⌘W, ⌘M, ⌘H, ⌥⌘H, ⌘Z, ⇧⌘Z, ⌘X, ⌘C, ⌘V, ⌘A and ⌃⌘F are live in the app today, appear in no documentation, and can't — nothing enumerates them.
They're also invisible to the keyboard registry by construction: AppKit dispatches menu key equivalents inside NSApplication.sendEvent before the key window's responder chain, so these never reach the webview's keydown handler at all. (This is also why the Edit menu is load-bearing rather than decorative — without it, copy/paste in the webview breaks.)
Why it's a gap and not just untidiness. Invariant K1 promises every action has a keyboard path and that the path is findable. Since #118 the ? sheet is a projection of ui/src/bindings.ts, and shortcuts.test.ts fails on a binding no row documents — so every binding is now guaranteed discoverable. These aren't bindings, they're inherited, so they sit outside that guarantee entirely.
The work. Declare the menu explicitly in b2-desktop rather than introspecting an opaque default — you can't document what you can't enumerate, and a declared menu is B2's own data. Then export its accelerators through an ordinary #[tauri::command] (deserialize → one call → serialize, per the thin-adapter rules) and fold them into the registry as reserved chords, so conflicts() sees them and the sheet can list them.
Worth doing on its own merits, and a sensible prerequisite for shortcut customization: a rebinding UI can't warn about a chord nothing can enumerate.
Source: docs/design/invariants.md K1, crates/b2-desktop/CLAUDE.md (the four obligations), ui/src/bindings.ts (module header, on what the registry does and doesn't own).
Left over from the keyboard-registry work in #118 — Layer 3 of the collision-detection plan.
crates/b2-desktop/tauri.conf.jsonsets no menu, so Tauri'sMenu::default()applies. That means ⌘Q, ⌘W, ⌘M, ⌘H, ⌥⌘H, ⌘Z, ⇧⌘Z, ⌘X, ⌘C, ⌘V, ⌘A and ⌃⌘F are live in the app today, appear in no documentation, and can't — nothing enumerates them.They're also invisible to the keyboard registry by construction: AppKit dispatches menu key equivalents inside
NSApplication.sendEventbefore the key window's responder chain, so these never reach the webview's keydown handler at all. (This is also why the Edit menu is load-bearing rather than decorative — without it, copy/paste in the webview breaks.)Why it's a gap and not just untidiness. Invariant K1 promises every action has a keyboard path and that the path is findable. Since #118 the
?sheet is a projection ofui/src/bindings.ts, andshortcuts.test.tsfails on a binding no row documents — so every binding is now guaranteed discoverable. These aren't bindings, they're inherited, so they sit outside that guarantee entirely.The work. Declare the menu explicitly in
b2-desktoprather than introspecting an opaque default — you can't document what you can't enumerate, and a declared menu is B2's own data. Then export its accelerators through an ordinary#[tauri::command](deserialize → one call → serialize, per the thin-adapter rules) and fold them into the registry as reserved chords, soconflicts()sees them and the sheet can list them.Worth doing on its own merits, and a sensible prerequisite for shortcut customization: a rebinding UI can't warn about a chord nothing can enumerate.
Source:
docs/design/invariants.mdK1,crates/b2-desktop/CLAUDE.md(the four obligations),ui/src/bindings.ts(module header, on what the registry does and doesn't own).Left over from the keyboard-registry work in #118 — Layer 3 of the collision-detection plan.