diff --git a/CLAUDE.md b/CLAUDE.md index 09e2161..e4027e7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -245,7 +245,11 @@ if/when one lands — `index-engine.md` §5.)* syntax-highlighted from CodeMirror's own grammar registry (`@codemirror/language-data`, lazily loaded one language per chunk) — `ui/src/highlight.ts` drives *every* surface (reading view, live preview, source mode) from one resolver and one theme-aware `tok-*` palette, so a fence looks the - same read or edited. + same read or edited. The GUI is **keyboard-complete** (invariant K1): the file tree follows the + ARIA `tree` pattern over `ui/src/treenav.ts`'s row order — the *same* order `render.ts` paints, so + the arrows and the eye can't disagree — every overlay traps and restores focus, `⇧F10` is the + keyboard's right-click, and `?` shows the whole chord table (`ui/src/shortcuts.ts`). 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`) diff --git a/crates/b2-core/src/link.rs b/crates/b2-core/src/link.rs index 971180b..499c57b 100644 --- a/crates/b2-core/src/link.rs +++ b/crates/b2-core/src/link.rs @@ -236,6 +236,9 @@ fn extract_explanation(tail: &str) -> Option { mod tests { use super::*; + /// One expected link, as the case tables spell it: `(type, target, caption, embed, typed)`. + type Expected<'a> = (&'a str, &'a str, Option<&'a str>, bool, bool); + /// Shorthand: parse one line, return `(type, target, caption, embed, typed)`. fn parsed(line: &str) -> Vec<(String, String, Option, bool, bool)> { parse_links(line) @@ -246,7 +249,7 @@ mod tests { #[test] fn markdown_forms_yield_references_with_caption_and_embed() { - let cases: &[(&str, &[(&str, &str, Option<&str>, bool, bool)])] = &[ + let cases: &[(&str, &[Expected])] = &[ // ![alt](path) — embed with caption ( "See ![a sailboat](img/IMG_2041.jpg) here.", diff --git a/crates/b2-desktop/CLAUDE.md b/crates/b2-desktop/CLAUDE.md index 8ec8598..35fefba 100644 --- a/crates/b2-desktop/CLAUDE.md +++ b/crates/b2-desktop/CLAUDE.md @@ -89,6 +89,68 @@ add a UI concern to `b2-core`, that's the signal you're putting logic in the wro GUI/reindex threads), and the *implied* default scoped to `b2=debug` (not bare `debug`) so Tauri/wry/hyper tracing stays out of the file. `main` must hold the returned `WorkerGuard` for the whole run. +## The keyboard contract (invariant K1) + +[invariants.md](../../docs/design/invariants.md) **K1** — *B2 is fully operable from the keyboard; the +mouse is an accelerator, never a requirement* — names this file as its elaboration home. This is it. +K1 governs the **GUI**: the `b2` CLI satisfies it by nature, so everything below is about +`b2-desktop` + [`ui/`](../../ui) ([#78](https://github.com/AlteredCraft/B2/issues/78)). + +**The rule, in one line: no action reachable only by pointer.** If a gesture exists only as a click, +a right-click, or a drag, it is a bug — not a missing nicety. + +### The four obligations + +Every new surface owes all four. They are cheap while you're building it and expensive to retrofit. + +1. **Reachable.** A focusable control in a sensible tab order, or a documented chord. A `
` you + attach a click handler to is a mouse-only control; make it a `