From 8ad5c0806db558749c10d753b02aeb065ca11a29 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Mon, 21 Sep 2026 22:29:18 -0700 Subject: [PATCH] feat(web): dismissable notice, a raised minibuffer, and a styled shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three things the user reported after looking at the deployed page, in the order they cost a visitor something. The deployment notice was ten dense lines of configuration reference — ?doc=, ?seed=, ?ws=, wss:// and reverse proxies — above the tile bar, the category tree and the grid on every visit: about 230px of an 820px window. It is now a
, so the collapsing is the BROWSER's (keyboard operable, and correct with app.js absent or still loading) and the only thing the page adds is remembering the answer. It starts COLLAPSED, and collapsing withholds nothing: the one fact a first visitor needs — the document lives in this tab, is saved to this browser's private storage and never leaves the machine — is the , which never collapses. What a stored value means is a decision and lives in shell.js; touching localStorage is a browser API and stays in app.js, wrapped, with every value that is not one of the two spellings read as "unanswered". The minibuffer was a 14px line in the page's bottom-left corner, up to 800px from the cell being typed into. It is now a raised bar over the grid. The CONTENT is untouched — RenderMinibuffer is built from `state.view.mode.minibuffer()`, the same semantic value the TUI draws — so this is position, weight and contrast, and there is no second derivation of what a prompt says. MinibufColor still resolves here exactly as draw.rs::minibuf_fg resolves it for the terminal. style.css stops being "legibility only": layered surfaces, one accent, a filled badge for the mode, chips for the two axis strips, one selection treatment, tabular numerals in the grid, and a focus ring on the one keyboard surface the page has. The palette is the landing page's, so the two read as one product. Asserted (harness/tests/contract.spec.js, Chromium): the notice starts collapsed and remembers, in BOTH directions across a reload; it opens from the keyboard, which the page's own keydown handler must not claim; the minibuffer is inside the viewport, OVERLAPS #grid — the assertion a static bottom bar cannot satisfy — and is larger than body text. Each was proved discriminating by injecting the fault and watching it fail. NOT asserted, and named as such in context/code/clients.md: colour, contrast, spacing, weight, the grouped and multi-level header DOM, either theme, and the localStorage-throws path. Those are argued and read. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH --- context/code/clients.md | 91 ++++++ context/test-evidence/clients.md | 76 +++++ harness/tests/contract.spec.js | 111 +++++++ web/app.js | 38 +++ web/index.html | 57 ++-- web/shell.js | 24 ++ web/style.css | 533 +++++++++++++++++++++++-------- web/tests/shell.test.js | 38 +++ 8 files changed, 822 insertions(+), 146 deletions(-) diff --git a/context/code/clients.md b/context/code/clients.md index 50fff94a..96080d7c 100644 --- a/context/code/clients.md +++ b/context/code/clients.md @@ -1644,6 +1644,97 @@ client resolves nothing under them. `BrowserKey` drops anything held with the platform meta key precisely so `Cmd-R` does not arrive as a bare `r`; the page does not claim it either. +### The deployment notice, and the one thing this page remembers + +`#backend-notice` is a `
`. The collapsing is therefore the **browser's** +— keyboard operable, and correct with `app.js` absent, still loading, or thrown +— and the only thing the page adds is remembering the answer. That split is the +whole design: a static notice is what it was, and a notice whose open state is +decided in JavaScript would be a fourth thing that can be wrong on a page whose +job is not to be wrong about its own peer. + +**It starts collapsed for a visitor who has never answered**, and the argument +is that collapsing withholds nothing. The one fact a first-time visitor needs — +the document lives in this tab, is saved to this browser's private storage, and +never leaves the machine — is the ``, which never collapses. What is +behind the disclosure is `?doc=`, `?seed=`, `?ws=`, the `wss://` default and the +reverse proxy a plain-`ws://` server needs: *configuration reference*, looked up +rather than read, and irrelevant until a visitor has decided they want a second +document or a shared one. Expanded, it cost about 230px of an 820px window and +pushed the tile bar, the category tree and the grid below the fold on every +visit. The page is an application; it should open as one. + +**The state is read through `shell.js` and stored in `app.js`**, on the usual +split: what a stored value *means* is a decision and is `noticeOpen`, while +touching `localStorage` is a browser API and stays in the page. `localStorage` +**throws** in a private window and in a context with site data blocked, and +returns `null` having simply forgotten, so every access is wrapped and there are +exactly two recognised spellings. Everything else — absent, empty, a value an +older page wrote — is *unanswered*, which is the default rather than a third +state: a page that treated forgetting as its own case would have to decide what +to do about it, and there is nothing to decide. + +What is stored is per-viewer and per-origin by construction. It never reaches +Rust, never crosses to a peer, and is not state the application has any opinion +about — which is exactly the class browser storage is for, and exactly why it is +not a `ViewState` field (gotcha 30). + +**The page's own keydown listener must not claim the summary's keys.** `Enter` +is claimed by the client (it is forwarded upstream), and the listener calls +`preventDefault()` on anything claimed — so without an explicit guard the +disclosure would open by pointer and be dead by keyboard, which is the whole +reason for using `
` rather than a `