T opens the tech tree — every tech grouped by era in era order, cost chips in
item colours, era prefix stripped from node names (the heading already says
it), UNLOCKS: tooltips in machine names not ids. Pick a node to dispatch
setResearch; picking the active one stands it down; ratified nodes are inert.
The v4 gating rule lives in a pure research.ts: an id any tech claims is locked
until that tech completes, and an id no tech mentions is always available — so
belts never padlock. Locked machines are greyed with a padlock and a
REQUIRES: <TECH> tooltip, and can't be picked by click or by hotkey. The
padlock falls off in the same frame `researched` lands, and the player never
keeps holding something they can no longer build.
With no ResearchState at all the safe reading is that gates hold rather than
fall open, and the panel says RESEARCH OFFLINE instead of rendering a tree that
can't do anything.
Accent precedence now matches RENDER exactly: MachineDef.accent, then the
first-output derivation, then the codex kind table. RESEARCH joins the page
order for the new 'lab' kind.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A misplaced machine is finally undoable by mouse. The REMOVE button sits
with the tabs rather than on a page, because it's a mode, not a machine, and
it has to be reachable from any page. It stays armed between clicks —
demolition is usually plural — and clicking open ground does nothing, so a
stray click never reads as a broken tool. Toast: "UNIT RECLAIMED. THE FLOOR
REMEMBERS."
LANE-RENDER: the hover hook is the __remove ghost sentinel. The UI has no
Renderer reference and the only channel to the ghost is selectedBuild(),
which main.ts feeds to setGhost — so while remove is armed, bus._sel reads
{def: '__remove', dir: 0}. Key the demolition tint off that def id. It's safe
because sim's place handler ignores unknown defs, so main.ts's click-to-place
fires a place for __remove and nothing happens; the UI dispatches the real
remove itself. Exported as REMOVE_DEF and pinned by a test.
A magic string shared between two lanes via a NOTES file is not a design;
contract request filed for UIBus.setGhostMode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kind tabs alone don't deliver "every machine hotkey-reachable at any catalog
size": REFINE holds 14 crafters and 1-9 reaches nine. So a page caps at nine
and an oversized kind spills into numbered pages (REFINE 1/2, REFINE 2/2).
The 21-machine catalog lands as 1+3+9+5+2+1 across six pages, every slot
hotkeyed. Pagination is pure (pages.ts) so the invariant can be tested rather
than eyeballed.
Selecting a machine from anywhere pulls the bar to the page that holds it, so
the hot button is always the visible one.
hotkeys.ts splits into a pure KeyRouter (map + dispatch, no window) and a thin
window binding, which lets the keymap be tested in node.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The five M1 surfaces, replacing the stub:
- buildbar.ts: one button per machine, hotkeys 1-9, R rotates, Esc or
right-click clears. LANE-DATA shipped 21 machines, so the bar wraps
rather than running off both edges of the viewport; slots 10-21 are
click-only for now (paging proposed in NOTES).
- topstrip.ts: bandwidth meter going amber at 85% and red + flashing
BROWNOUT over 100%, buffer, shipped ticker + per-item chips, tick or
HALTED. The brownout is legible from this panel alone, which is the
one failure the player has to read across the room.
- inspector.ts: name, codex flavor, intake/output chips with live
buffers, process bar, heat, jam reason. Holds an entity id and
re-looks-up each frame, so a removed unit closes itself.
- fax.ts: the commission card with wants-as-chips and a FAX SENT stamp
on commissionDone.
- toasts.ts: max 4, 6s TTL, colored per event kind.
Click-to-inspect is not wired: the UI cannot reach renderer.pickTile
(UIBus exposes only dispatch/selectedBuild, and ui.init never sees the
Renderer). TAB cycles the unit manifest as a stopgap so the inspector is
reachable without a console. Contract request filed in NOTES.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>