Skip to content

cli: the boot chrome needs an owner for terminal lifecycle (cursor + resize) #20

Description

@amondnet

Two defects in packages/cli/src/ui share one unmade decision, so they are tracked together. Both were raised in review of #15 and deliberately not fixed there.

1. The live region miscounts its rows after a terminal resize

LiveRegion repaints by moving up #liveRowCount - 1 lines and clearing to the end of the screen. That count is logical rows, and one logical row is one screen line only while the row fits the terminal width.

Narrow the terminal while the boot row is animating and a previously painted row reflows onto more than one physical line. The next repaint then moves up by the logical count, which lands the cursor inside the wrapped block, and ESC[0J clears only from there down — leaving the first wrapped line stranded above it. Every later repaint inherits the wrong origin.

Raised by cubic on #15: #15 (packages/cli/src/ui/live-region.ts)

2. The hardware cursor is never hidden

LiveRegion exposes hideCursor() / showCursor() and boot-row.ts, its only consumer, calls neither. The cursor sits at the end of the animated row and jumps on every repaint — for the whole ~30s of an image pull and adapter bootstrap.

Raised in a local review pass on the same branch (packages/cli/src/ui/boot-row.ts).

Why one issue

Both fixes are process-level terminal state that must be undone on every exit path, including SIGINT:

  • The resize fix needs a SIGWINCH listener, which leaks if stop() does not remove it.
  • The cursor fix needs the cursor restored on Ctrl-C. A hidden cursor never restored is a worse failure than the jumping one it fixes: it outlives the process and the user's terminal stays broken.

So the question is not either fix in isolation — it is who owns terminal lifecycle in this package, and how stop() relates to signal teardown. please dev has no command yet (docs/dev-tui.md), and the answer likely belongs with whatever ends up owning the handoff to @ai-sdk/tui, which takes the alternate screen buffer and does its own teardown.

Suggested shape

One teardown registry per boot-chrome session: enter/exit pairs registered together, run on stop() and on SIGINT/SIGTERM, idempotent. Then the resize listener and the cursor hide both become registrations rather than one-off calls.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:proposalProposal for discussiontype:bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions