Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,11 @@ if/when one lands — `index-engine.md` §5.)*
for the overlay layer), every overlay traps and restores it, `⇧F10` is the keyboard's right-click,
and the Settings dialog (⌘,) is a tabbed surface whose rail follows the ARIA `tabs` pattern over
`ui/src/settingstabs.ts` — General / Embedding / **Keyboard**, the last being the whole chord table
(`ui/src/shortcuts.ts`) that `?` jumps straight to. The four obligations a new surface owes are in
(`ui/src/shortcuts.ts`) that `?` jumps straight to. Chords themselves are declared once in
`ui/src/bindings.ts` — the keyboard registry — and the dispatcher, the editor's keymap and that
sheet all derive from it, so none of the three can drift; `conflicts()` fails the suite on two
commands sharing a keystroke in one scope, and `ui/src/editorkeys.ts` checks B2's chords against
CodeMirror's own ~100 stock bindings so an upgrade can't quietly take one. The four obligations a new surface owes are in
[`crates/b2-desktop/CLAUDE.md`](crates/b2-desktop/CLAUDE.md).

### The `Vault` façade (`b2-core/src/vault.rs`)
Expand Down
19 changes: 15 additions & 4 deletions crates/b2-desktop/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,21 @@ Every new surface owes all four. They are cheap while you're building it and exp
whatever opened it on close. `Escape` dismisses innermost-first; `Enter` confirms. All of this is
one hook — `syncOverlayFocus()` in `main.ts`, called at the end of every `render()` and acting only
on the open/close *edge*, so a toast timer's repaint never steals focus mid-⇥.
4. **Discoverable.** A chord nobody can find is not kept. Add the row to `ui/src/shortcuts.ts` (the
`?` sheet) **in the same change** that wires it, and put the chord in the control's `title` — and,
where the action lives in a menu, beside the menu item, which is where a keyboard user learns the
shortcut that lets them skip the menu next time.
4. **Discoverable.** A chord nobody can find is not kept. A chord is **declared once**, in
`ui/src/bindings.ts` — id, chord, scope — and everything else derives: the handler matches it with
`isBound(e, id)`, the editor's keymap takes it from `chordFor(id)`, and the `?` sheet
(`ui/src/shortcuts.ts`) names the id rather than spelling the chord. So add the binding, then add
its row to the sheet: `shortcuts.test.ts` fails on a binding no row documents, which is what makes
"in the same change" a rule the suite keeps rather than one you have to remember. Put the chord in
the control's `title` too — projected with `displayKeys([id])`, never typed out — and, where the
action lives in a menu, beside the menu item, which is where a keyboard user learns the shortcut
that lets them skip the menu next time.

Two things the registry will tell you before a user does. `conflicts()` fails the suite if your
chord already means something else in the same scope, so pick the scope honestly — it's what
separates "⏎ commits *this* dialog" from a clash. And `editorkeys.test.ts` compares B2's chords
against CodeMirror's ~100 stock bindings, so if your chord needs to work while the note is being
edited, that check is what proves the editor isn't already using it.

### Where the pieces live

Expand Down
8 changes: 5 additions & 3 deletions ui/src/anomalies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// index still say so, and it stops existing the moment a pass comes back clean. A panel
// reopened after a restart shows nothing until the next pass re-derives the same anomaly.

import { displayKeys } from "./bindings.ts";
import type { B2idCollision, RestampedNote } from "./types.ts";

/** The slice of a `ProjectReport` the anomaly surfaces read (GH #81). */
Expand All @@ -26,9 +27,10 @@ export interface IndexAnomalies {
}

/** The chord that opens the review panel — quoted in the ping, so the toast tells you
* how to get the detail back after it clears. Its row in the `?` sheet
* (shortcuts.ts) and its wiring (main.ts) spell the same chord; keep the three in step. */
export const REVIEW_CHORD = "⇧⌘A";
* how to get the detail back after it clears. Projected from the keyboard registry
* (bindings.ts), which is what keeps it equal to the wiring and to the `?` sheet
* rather than being a third place the same chord is spelled by hand. */
export const REVIEW_CHORD = displayKeys(["anomalies.toggle"]);

/**
* One path an anomaly row names, and what B2 can do with it.
Expand Down
Loading