diff --git a/fktry/lanes/LANE-UI.md b/fktry/lanes/LANE-UI.md index cadd4f5..8eccec8 100644 --- a/fktry/lanes/LANE-UI.md +++ b/fktry/lanes/LANE-UI.md @@ -471,3 +471,120 @@ breaks for no good reason. **NEXT (suggested):** verify RENDER's tint; research rig in reference.ts so a pack economy exists to test against; scrolling tab strip if the catalog grows again; era-skinned tech panel once eras mean something visually (§8). + +### Round 5 (MEGA) — 2026-07-18 — Opus 4.8 + +**SHIPPED:** All nine orders. New modules: `power.ts` was round 4; this round adds +`radio.ts` (SPEAKER bridge + echo stand-in), `tuner.ts` (the dock), `screenview.ts` +(enlarge), and tests `tuner.test.ts`, `wildlife.test.ts`, `screenview.test.ts`. +**194 UI tests** (was 145); full suite 446/446; `npm run check` clean. + +1. **Scrolling tab strip** — one row, never wraps, active tab scrolled into view on + `[`/`]`. Found and fixed a real bug doing it: see the hitbox finding below. +2. **Inspect mode published** — `setSelection({mode:'inspect',entity})` on open, cleared + on close (X, Esc, or the unit being demolished under it). Holding a tool supersedes it + and shuts the panel, so panel and highlight can't disagree. +3. **Wildlife surfaced** — infestation toasts in voice; MOSQUITO EXPOSURE + severity in + the inspector, read from `snap.wildlife` by `target`; hatch warnings deduped one per + pile id. +4. **Research tests migrated** to SIM's `testkit.grantResearch`/`grantAllResearch` — my + round-4 hand-rolled save-blob edit is gone, and with it the coupling to SIM's schema. + Tint + highlight verified, see below. +5. **First-brownout mercy toast** — once per session, on the first brownout event. +6. **Camera hint** in the idle line; **tab hitbox adjudicated** (below). +7. **`no seam` jam voice** — "THERE IS NOTHING DOWN THERE. MOVE." SIM emits this reason + at index.ts:498, so it's live copy, not speculative. +8. **THE SCREEN click-to-enlarge** — ~2.5×, dimmed backdrop, Esc/click closes. +9. **THE TUNER DOCK** — hidden until the fossil is excavated, then slides in with dial + (drag + ←/→), AM/FM/SW, volume + mute, and a station readout in voice. + +**VERIFIED — live, in the browser** (screenshots taken of each): +- **Cold-start mercy toast**: placed one extractor with no generator → BROWNOUT, and + under it "NOTHING IS GENERATING. THE COMMITTEE SUGGESTS: POWER." That's the DoD's + opening requirement. +- **THE SCREEN enlarge**: the live WebGL sky at 2.5× centred over a dimmed HUD; Esc + restored its `style` attribute to `null` and its dock rect to y=10, w=322 — exactly as + found. +- **THE TUNER DOCK**: excavated SIM's real seeded fossil → dock slid in bottom-right with + "AUDIO BUS RESTORED. THE BITSTREAM HAS A VOICE.", dial, FM active, VOL/MUTE, hint line. +- **RENDER's demolition tint — the item owed since round 3 is CLOSED.** Armed remove, + hovered a machine, and it renders outlined in demolition red. Screenshot taken. +- Build bar with padlocks on the POWER page (DECODE ASIC free; SOFTWARE DECODER and ASIC + COOLER greyed + 🔒 + REQUIRES:), reference factory healthy at GEN 220 / DRAW 158. + +**VERIFIED — headless** (`live.test.ts`, real sim + real DOM, 19 tests): mercy toast fires +once and *stays* once across a second brownout; inspect selection published on open and +dropped both on Esc and when the inspected unit is demolished; the tuner stays hidden +until `relics[0].found` flips and then reveals with the ceremony toast and the toast +standoff; full research loop (lock → pick → deliver → unlock → place) now driven through +`grantResearch` and the lab's real per-pack timing. + +**DECISIONS:** +- **THE SCREEN is enlarged in place, not mirrored.** I built the mirror first; it rendered + black. THE SCREEN is a WebGL canvas, and `drawImage` from a WebGL context without + `preserveDrawingBuffer` returns nothing. Asking SCREEN to enable that flag would cost + them a buffer copy every frame for an occasional feature, so I enlarge the real element + with inline styles and restore its `style` attribute verbatim on close (tested, + including the case where SCREEN later adds inline styles of their own). +- **The radio bridge is guarded** (`radio.ts`). `src/audio` is still the stub — no `radio` + export exists — so the dock resolves a control at runtime and otherwise drives a silent + echo stand-in, which is why every dial is testable today. **LANE-SCREEN: publish + `window.__fktrySpeaker = { radio }` (or tell me the export name you prefer) and the dock + drives real audio with zero change here.** The interface I'm driving is exactly your + documented one: `setFrequency / setBand / volume / mute / getState / subscribe`, with + `getState()` returning `{freqMHz, band, volume, muted, station}` — `station: string|null` + is what flips the readout to "SIGNAL LOCKED". `isLive()` reports which one is attached. +- Wildlife exposure shows the swarm's **severity** (`WildlifeState.size`), not a + sim-confirmed throughput number — SIM owns the real penalty and doesn't expose one. + Labelled as exposure rather than a promise about output. + +**BLOCKED/BROKEN (read this part):** +- **The tab-click bug was REAL, and I fixed it.** Reproduced with a genuine mouse click: + a click on POWER did nothing. `elementFromPoint` at the button's own centre returned the + *strip container*, not the button — the horizontal scrollbar was being painted over the + lower half of every tab and swallowing clicks there, which is exactly why keyboard + paging still worked. Fixed by giving the scrollbar its own lane (`padding-bottom: 7px` + ≥ scrollbar height); the full button height now hit-tests to the button, and a real + click activates POWER. **Separately**, `computer` clicks via `ref` deliver *no* events to + the page at all (document-level capture recorded nothing); coordinate clicks work fine. + So: real bug, now fixed, plus a harness quirk on top of it. +- **DATA's commission queue head-of-line blocks, and it stalls the whole game.** The v5 + queue opens with `raw-payload` (20 raw MDAT ORE), but the reference factory refines every + scrap of its ore and never ships it — so the head commission can never complete and all + 19 behind it are unreachable. Probed: 12k ticks ships hf-dust, chroma-slurry, + macroblock-bricks, melt and anchor-slab, and `commissionDone` fires **zero** times. My + tests now build the obvious player rig (extractor → belt → uplink) to pay it. Worth a + design look: either the opening commission should want something the tutorial factory + ships, or an unsatisfiable head shouldn't block the tray. +- **Machines only push onto belts, never into an adjacent machine.** Cost me a while to + find; noting it because it's non-obvious and unstated anywhere in CONTRACTS. +- **RENDER doesn't consume the inspect selection yet** — no `inspect` anywhere in + `src/render`. I publish it correctly (verified live: `{mode:'inspect',entity:105}`), so + the highlight lights up whenever they read it. **Not owed by me any more; owed to me.** +- **Stale ghost on keyboard disarm**: main.ts only refreshes the ghost on `pointermove`, so + Esc-ing out of remove mode leaves the demolition tint on screen until the mouse moves. + Visible in my inspect screenshot. Cosmetic, and it's main.ts's glue, not mine. +- **SIM landed wildlife AND relics mid-round, again** — my round-start grep found neither. + Fifth round running. It's why my wildlife/relic UI could be verified against the real sim + at all, so this is a good problem, but the pattern is now completely reliable: I re-probe + a *running* sim before writing NOTES, never a grep from round-start. +- Three of my live tests failed mid-round from SIM/DATA changes, not my code (proved by + stashing my diff and re-running). Also hit `DUST_SPILL_GRACE is not defined` from SIM + mid-edit, which resolved itself between two consecutive commands. +- Harness, for the record: the browser pane backgrounds the tab, which pauses rAF **and + CSS transitions**. I lost time reading a transitioned `bottom` that never advanced and + briefly thought the CSS was broken; `transition:none` proved it computes correctly. Same + root cause as the paused sim loop from rounds 3-4. + +**CONTRACT REQUEST:** none. v5/v6 gave me everything these orders needed. + +**PROPOSAL:** `UIBus.audio?: { radio?: RadioControl }` — the tuner currently reaches THE +SPEAKER through a global handshake because `ui.init` never sees the audio module, which is +the same shape of problem as `pickTile` in round 1 and the `__remove` sentinel in round 3. +One optional field on the bus would make it typed and testable instead of a documented +convention. Not blocking: the guarded bridge works. + +**NEXT (suggested):** wire the real radio the moment SCREEN publishes it (and describe how +it sounds in NOTES, since they're doing the same); inspect highlight once RENDER reads the +selection; a station-locked readout screenshot once there are real stations; commission +head-of-line blocking is a design question for you, not a UI fix.