Files
improvise/web/demo.improv
T
Edward Langley a550201fae feat(web): the page spawns the worker, opens a demo, and reads a key
Three pieces of the standalone deployment, all in web/.

improvise-djm — app.js picks its peer instead of assuming a socket. The
wasm bundle is the same artifact either way (improvise-xks6), so the
difference is confined to one factory: `?ws=` selects the websocket, its
absence spawns `new Worker('./worker.js?doc=&seed=', {type:'module'})`.
The websocket path is KEPT, because improvise-ws-server is how a
TUI-hosted document is shared and nothing else can reach it; the presence
of the parameter is the whole selection mechanism, since a second knob
naming the same fact is a second thing to get wrong.

improvise-bck — web/demo.improv, built against an assertion rather than
by flipping an axis. examples/demo.improv leaves Customer/Date on None,
so every cell is an Aggregate and nothing is editable. The new demo puts
exactly one category on each visible axis (Product on row, _Measure on
column, Quarter on page) and carries Profit = Revenue - Cost, so a
visitor sees the formula move beside the cell they typed in, with no
keystroke and no scrolling. Two tests hold it: tests/web_demo_model.rs
projects the whole grid through grid_of and asserts every CellId is
Record (ADR 002 — that is what `editable` means), and post_message.rs
drives the shipped bytes through the real seed -> VFS -> open -> Snapshot
chain. The first was proved discriminating by pointing it at
examples/demo.improv, where it names the aggregate at (0,0).

One category per axis is asserted too: GridCache slash-joins a
multi-level tuple (gotcha 44), so a second category on an axis puts that
known gap on the front page.

improvise-hwpi — all four. A keydown listener on window feeds
Client::on_key_event, so the classifier is reachable at last; a frame
that will not fold now writes to #status instead of only the console; the
socket has an error listener, so "not running" no longer reads like
"still connecting"; and close() has a re-entry guard. Plus one the ticket
could not have predicted: a worker whose start() rejects is caught inside
worker.js and fires no `error` on the main thread, so the same silence
was about to be rebuilt in the new path — a first-frame watchdog covers
both deployments, because "the peer never answered" is the one condition
that means the same thing on each.

Verified: cargo test --workspace 1159 passing (1155 + 4), clippy clean,
fmt clean, actionlint clean, ./web/build.sh end to end (345 KB / 123 KB
gzipped client, 1.33 MB / 498 KB gzipped worker), every call present in
the generated .d.ts with the right arity, every relative reference in the
assembled site resolving. NOT verified: there is no browser here. No line
of JavaScript in this commit has ever run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 19:28:27 -07:00

58 lines
2.0 KiB
Plaintext

v2025-04-09
# Corner Coffee — 2025
Initial View: Plan
## View: Plan
Product: row
_Measure: column
Quarter: page, Q1
_Index: none
_Dim: none
format: ,.0
## Formulas
- Profit = Revenue - Cost
## Category: _Measure
- Revenue, Cost
## Category: Product
- Espresso, Latte, |Cold Brew|, Pastry
## Category: Quarter
- Q1, Q2, Q3, Q4
## Data
Product=Espresso, Quarter=Q1, _Measure=Revenue = 18400
Product=Espresso, Quarter=Q1, _Measure=Cost = 6200
Product=Espresso, Quarter=Q2, _Measure=Revenue = 17100
Product=Espresso, Quarter=Q2, _Measure=Cost = 5900
Product=Espresso, Quarter=Q3, _Measure=Revenue = 15800
Product=Espresso, Quarter=Q3, _Measure=Cost = 5400
Product=Espresso, Quarter=Q4, _Measure=Revenue = 20300
Product=Espresso, Quarter=Q4, _Measure=Cost = 6900
Product=Latte, Quarter=Q1, _Measure=Revenue = 24600
Product=Latte, Quarter=Q1, _Measure=Cost = 9100
Product=Latte, Quarter=Q2, _Measure=Revenue = 23800
Product=Latte, Quarter=Q2, _Measure=Cost = 8800
Product=Latte, Quarter=Q3, _Measure=Revenue = 22400
Product=Latte, Quarter=Q3, _Measure=Cost = 8300
Product=Latte, Quarter=Q4, _Measure=Revenue = 27900
Product=Latte, Quarter=Q4, _Measure=Cost = 10200
Product=|Cold Brew|, Quarter=Q1, _Measure=Revenue = 9200
Product=|Cold Brew|, Quarter=Q1, _Measure=Cost = 3400
Product=|Cold Brew|, Quarter=Q2, _Measure=Revenue = 14600
Product=|Cold Brew|, Quarter=Q2, _Measure=Cost = 5300
Product=|Cold Brew|, Quarter=Q3, _Measure=Revenue = 17300
Product=|Cold Brew|, Quarter=Q3, _Measure=Cost = 6200
Product=|Cold Brew|, Quarter=Q4, _Measure=Revenue = 8100
Product=|Cold Brew|, Quarter=Q4, _Measure=Cost = 3000
Product=Pastry, Quarter=Q1, _Measure=Revenue = 11500
Product=Pastry, Quarter=Q1, _Measure=Cost = 5200
Product=Pastry, Quarter=Q2, _Measure=Revenue = 10900
Product=Pastry, Quarter=Q2, _Measure=Cost = 4900
Product=Pastry, Quarter=Q3, _Measure=Revenue = 10200
Product=Pastry, Quarter=Q3, _Measure=Cost = 4600
Product=Pastry, Quarter=Q4, _Measure=Revenue = 13400
Product=Pastry, Quarter=Q4, _Measure=Cost = 6000