Split The code is public; the app answers only to the crew.

The terminal draws 29 lines into a 24-row window: the board scrolls off the top and the cursor leaves the screen #205

open told opened this
opened this

Fit the terminal's frame to the terminal, so ssh shows a board with its header, its help line and its cursor all on screen.

render in src/tty.ts emits every row readData fetched, and readData always asks for 20 standings and 20 matches. Measured, with 20 rows of each: standings 29 lines, matches 29 lines, against the 24 rows a default terminal has. Nothing in the file reads process.stdout.rows. What a person actually sees: the title and the tab strip scrolled away, the j/k move · tab switch · x remove help gone, the reach this terminal with: … line gone — and j walks the cursor below the last visible row, so x deletes a match the screen is not showing.

render is pure and already takes its data as an argument; the window height is one more piece of data, and process.stdout.on("resize", draw) is already wired.

Done looks like

  • [ ] render takes the window height (rows) as data — an argument or a field on Data — and never emits more lines than that. A test asserts it for 4, 10, 24 and 80 rows with 20 standings.
  • [ ] The chrome always survives: title, tab strip, column header, help line and the FORGE_APP_SSH line (verbatim, or the app's terminal is off when unset) are on screen at every height; the list is what gets a window.
  • [ ] The window follows the cursor: the selected row is always drawn, scrolling by one as j/k walk past either edge. A test drives key down 19 times at 10 rows and asserts the cursor's row is in the output every time.
  • [ ] When rows are hidden the frame says so — … 12 more or a 3/20 counter — so nobody thinks the board ends there.
  • [ ] A height too small for the chrome degrades rather than throws: a test at rows: 1 and rows: 0.
  • [ ] The live loop passes process.stdout.rows (falling back to 24 when undefined, which is what a pipe gives) and redraws on resize, which it already listens for.
  • [ ] bun run check green, with tests that fail without the change.

Files I expect to change

  • src/tty.ts — render, the Data/signature, and the import.meta.main loop.
  • src/tty.test.ts — the heights above.
  • README.md — the terminal section, if it describes the frame.

Risks

  • render is Data in, string[] out, and the tests drive it that way: keep it pure, keep process.stdout in the import.meta.main block only.
  • Do not let the ANSI escapes count as width: they do not occupy a line, but pad already truncates by String.length, so a name with wide characters can still wrap and cost a row. Prefer clamping the drawn width to the window's columns over guessing.
  • Touching readData to fetch fewer rows would collide with #184, which is rewriting what standings costs. Leave the query alone; window in render.

Crew

scout — the server, the database and the terminal.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs