Public: anyone can read this; the crew can change it.

The terminal, under test: src/tty.ts split so every screen is a pure function of state #57

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`src/tty.ts` now splits along the line it already had: `screen(state)` builds a frame as a pure function — the loop's state, the actions' data and the forge's environment in, the lines of the screen out, with no database, no `process.stdout`, no `process.env` and no clock — and `run()` is the loop around it, guarded by `import.meta.main` so importing the module does not start a terminal. `src/tty.…

shipped production is serving it deploy 6ecafc0

opened this

src/tty.ts now splits along the line it already had: screen(state) builds a frame as a pure function — the loop's state, the actions' data and the forge's environment in, the lines of the screen out, with no database, no process.stdout, no process.env and no clock — and run() is the loop around it, guarded by import.meta.main so importing the module does not start a terminal. src/tty.test.ts is new: sixteen tests over the empty lobby as player and as guest, one open table, a table you are seated at, a full one, the key legend for each, the charge shown only at your own seat, the status line after a named refusal, the name-a-table prompt, cursor clamping and the frame itself. Escape sequences live in one named block at the top with a plain() that strips them, so the tests assert the words a person reads.

Three things fell out of the split, and are the parts worth reading: the selected table's seats now come from the row listTables already returned (redacted for its viewer) instead of a second tableState read — same text, one query fewer; the legend is data (keys(who, table) — a key, what it does, whether it writes) rendered to the same line as before, so the guest test asserts "no key that writes" against the rules rather than against a string; and pressing o as a guest now asks openTable and prints its refusal, so the sentence "Sign in to open a table." is the action's, not the terminal's.

Two outputs change deliberately. With FORGE_APP_SSH unset the header says the terminal is off: FORGE_APP_SSH is not set instead of printing a blank line (set, it is still printed verbatim and never guessed), and every value drawn from data — title, host name, player name, the command — has control characters stripped, so a name in the database cannot move the cursor. Everything else is byte-identical; I diffed the real frames before and after against a seeded database, as guest and as player. I did not touch what the keys do: #14 builds on these tests. bun test is green, 98 tests. To try it: FORGE_APP_DATA=./data bun run src/tty.ts, or open the preview's terminal with the command in FORGE_APP_SSH.

🤖 Generated with Claude Code

Closes #41

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs