From a9133903224330565beb376be063bc59eeccb9b6 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Mon, 21 Sep 2026 23:24:38 -0700 Subject: [PATCH] feat: the page bar becomes a control, and `[`/`]` reach the modes about the document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three tickets on one subject: paging. improvise-rw6l — clicking a page chip opens a menu of that axis's items, with a down-arrow saying so. Almost nothing had to be derived: `PageAxis::items` was already projected and `ModelCommand::SetPageSelection` was already on the wire. The one gap was that `RenderPageAxis` dropped the names, keeping the position and count improvise-p1ym needed for `[`/`]`. `items` is relayed INSIDE `cycle` rather than beside it, so there is one predicate and not two: a Filter axis has no cycle, therefore no arrow, no menu, no items, and composes nothing if a click reaches it — all four off `PageAxis::cycle`, the one home of gotcha 27's split. improvise-771e — the browser tile bar said "Quarter Pag". `short_label` is three characters so the TUI aligns in a fixed-width terminal (gotcha 26); a browser has no column to fit. `render.rs` asks `plain_label` instead; `short_label` is untouched. Note what pinned the truncation was `harness/tests/drill.spec.js`, not `tests/grid_frame_agreement.rs`, which does not mention the tile bar. improvise-wiew — `[`/`]` move from Normal onto a new `document_keymap` between the global and the modes whose subject IS the document (Normal, the three panels, TileSelect; RecordsNormal through Normal). Help and the import wizard take the global directly. Per the user's ruling: an overlay has a subject of its own, and these are the first shared keys that MUTATE the document — under the help overlay that is an invisible change to the grid it is covering. Boundary crossings to flag, both outside the brief's stated write scope and neither a wire change: - `resolve.rs` gains `Click::PageItem` and `lib.rs` a `click_page_item` binding. Composing the command anywhere else would put a derivation in the page, which design-principles §5 forbids; `Command`/`ModelCommand` are untouched. - `improvise-command/src/command/keymap.rs` gains three tests. The two existing guards were the mechanism that forced the wiew argument, so they had to be answered where they live. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH --- context/code/clients.md | 78 +++++++++- context/code/protocol.md | 63 ++++++++ context/test-evidence/clients.md | 96 ++++++++++++ context/test-evidence/protocol.md | 87 +++++++++++ .../improvise-command/src/command/keymap.rs | 108 +++++++++++++ crates/improvise-protocol/src/keymap.rs | 28 +++- crates/improvise-protocol/src/resolve.rs | 66 ++++++++ crates/improvise-wasm-client/src/lib.rs | 7 + crates/improvise-wasm-client/src/render.rs | 81 ++++++++-- harness/tests/contract.spec.js | 6 +- harness/tests/drill.spec.js | 4 +- harness/tests/pages.spec.js | 125 +++++++++++++++ web/app.js | 147 ++++++++++++++++-- web/index.html | 7 + web/shell.js | 55 ++++++- web/style.css | 70 ++++++++- web/tests/dispatch.test.js | 90 ++++++++++- 17 files changed, 1076 insertions(+), 42 deletions(-) create mode 100644 harness/tests/pages.spec.js diff --git a/context/code/clients.md b/context/code/clients.md index 514b4281..9b5b21d2 100644 --- a/context/code/clients.md +++ b/context/code/clients.md @@ -1630,10 +1630,80 @@ now spelled twice — `src/ui/grid.rs` and `web/shell.js::gridPlan` — which fa the `indicator()` precedent (the collapse glyph is spelled in both ends while the *fact* is shared) and because the alternative is a protocol-level string table for one word; `invariant(a_ui_string_is_keyed_by_a_command_never_by_a_key)` -is where it belongs if a second such string ever appears. And the page bar is -**display-only**: it shows the cycle position so `[`/`]` have an affordance, but -clicking an entry does nothing, because composing a page change from a click -needs a `click_page` on the wasm surface that does not exist yet. +is where it belongs if a second such string ever appears. + +### The page bar is a control (improvise-rw6l) + +It was display-only until improvise-rw6l, and this file said so, naming the +missing `click_page` on the wasm surface as the reason. That is now built, and +it cost less than the note implied: `PageAxis::items` was already projected and +`ModelCommand::SetPageSelection` was already on the wire. The only gap was that +`RenderPageAxis` **dropped the names** — it carried a position and a count, +which is everything improvise-p1ym needed for `[`/`]` and nothing a menu needs. + +**The widening puts `items` inside `cycle`, not beside it.** That is the whole +design: one predicate, not two. A Filter axis has no `cycle`, so it has no +items, no down-arrow, is painted as a `` rather than a `