feat(protocol): one home for UI strings (improvise-x7sw, ktv1, k099)
Three tickets asked one question — where do UI strings live relative to a
serde wire enum — and answering it three times is the disease this unit exists
to cure. One answer, in `improvise-protocol/src/help.rs`:
**A UI string is keyed by a command name, never by a key, and never hangs off
the enum that crosses the wire.** `AppMode::label`/`hint_text` are gone;
`help::mode_label`/`help::hint_line` replace them, so the enum a client
deserializes carries no English at all (improvise-k099). **And wherever a
string names a key, the key is looked up rather than spelled**:
`Hint { keys, command, caption }` takes `keys` off `Keymaps::default_keymaps()`,
`command` is the semantic value a localized front end keys off, and `caption`
is the default English — the `AxisColor`/`axis_fg` shape.
- **improvise-x7sw**: `help::which_key` is the which-key popup's rows, keys and
captions together. The client already holds the keymaps, which is the answer
the ticket preferred, so `ViewState::transient_prefix` stays an address.
- **improvise-ktv1**: the help pages are data — `help::pages()` — with every
key row derived. `src/ui/help.rs` keeps its ratatui styles and loses ~310
lines of content; `web/app.js` paints rows it does not author, and
`harness/tests/shell.spec.js` asserts that `?` really draws them.
**What this deletes is the measure of it.**
`every_hint_key_resolves_in_its_mode` and its 60-line hint tokenizer are gone:
they existed to hold a hand-written string honest against the keymap, and the
string is derived now, so the property is true by construction
(`doctrine(a_consolidation_that_keeps_its_compatibility_test_is_unfinished)`).
`Keymap::binding_hints` goes with them — `which_key` is the one walker, and the
binding dump reads it. What replaced the test checks the *other* direction, a
mode advertising a command nothing binds, which was never checkable before.
Two defects the derivation exposed, both fixed here: `key_label` dropped
modifiers, so Ctrl-arrows read as bare arrows, and `Char(' ')` printed a literal
space instead of `Space`.
**What is deliberately not derived**, and says so: the import wizard's keys
(its whole keymap is one `Any` binding into `HandleWizardKey`, which decides
per step inside the command — a real hole, left visible), `gg`/`yy`/`ZZ` and
the ex-commands (`HelpLine::Literal`), and the captions, which no test can
judge. `context/code/protocol.md` §8 has the argument.
Cost, stated: the hints changed shape. They are longer, they name arrow keys
beside `hjkl`, and two sub-lists on the Panels page lost their visual indent.
That is the price of the keys being true.
Evidence: 1321 tests pass, and the 7 Chromium shell tests pass against the
assembled site. Two faults were injected into the help overlay — dropping the
`paintHelp` call, and removing the `mode != Help` guard so it is never hidden —
and each fails the new spec.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -398,3 +398,41 @@ An expanded name the cache does not carry must not shift the flattening:
|
||||
`expanded_cats` is per-client and survives a document swap, so it **routinely**
|
||||
names categories that are gone. A flattening that indexed by expanded-name
|
||||
would go off the end here.
|
||||
|
||||
### only_quit_advertises_nothing
|
||||
|
||||
improvise-2hi, carried over from `only_quit_has_no_hint` when the hints moved
|
||||
out of `AppMode` (improvise-k099). The old `hint_text` had a `_ => ""` arm, so
|
||||
the modes without hints were invisible; `ExportPrompt` was blank too
|
||||
(improvise-1c9), and the status bar went empty at the one moment the user has
|
||||
to be told what to type. Only `Quit` may say nothing — it is never rendered.
|
||||
|
||||
What it asserts now is one step stronger, because `hint_line` is derived: a
|
||||
mode goes quiet not by someone forgetting a string but by every command it
|
||||
advertises becoming unbound, which
|
||||
`every_advertised_hint_names_a_binding_that_exists` catches first.
|
||||
|
||||
### every_advertised_hint_names_a_binding_that_exists
|
||||
|
||||
The replacement for `every_hint_key_resolves_in_its_mode` (improvise-2hi),
|
||||
which is **deleted**, and the deletion is the point
|
||||
(`doctrine(a_consolidation_that_keeps_its_compatibility_test_is_unfinished)`).
|
||||
|
||||
That test existed because `AppMode::hint_text` was a hand-written string
|
||||
promising what the keys do, with nothing tying it to the keymaps. It walked
|
||||
every `Key:verb` token in every hint through a 60-line tokenizer that had to
|
||||
know the hint dialect — named keys, `Ctrl-<c>`, `F/C/V` alternatives, runs like
|
||||
`hjkl`, the em-dash prose tail, CommandMode's ex-commands — and panicked on a
|
||||
token it could not classify, because a token it skipped was exactly the one
|
||||
that would go unchecked.
|
||||
|
||||
None of that is needed now. A hint names a **command**, and its keys are looked
|
||||
up in `Keymaps::default_keymaps()`, so "the hint names a key that resolves" is
|
||||
true by construction and cannot be otherwise. What remains checkable, and is
|
||||
what this test checks, is the other direction: a mode advertising a command
|
||||
that nothing binds. That was never checkable before.
|
||||
|
||||
Note what neither version reaches, and gotcha 14 stated for the old one: that
|
||||
the *caption* is accurate. `i:edit` where `i` enters records mode still passes.
|
||||
The caption is English beside a command name, and only a reader can judge it.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user