fix(tui): stop registering one resize listener per transcript row - #43562
fix(tui): stop registering one resize listener per transcript row#43562kitlangton wants to merge 2 commits into
Conversation
AssistantFooter and SessionImages each called useTerminalDimensions(),
which subscribes a renderer resize listener per mounted component.
SessionImages did so even when it renders nothing, and it mounts per user
message, per tool part, and per grouped tool section, so listener count
grew linearly with transcript length and tripped Bun's EventTarget warning
("11 resize listeners added to [CliRenderer]") within a few prompts.
The session route already subscribes once; expose that as a reactive
terminal size on the session context and read it from the row components.
Review follow-ups: back the context's terminal getters with per-axis memos so width readers do not re-run on height-only resizes (and vice versa), and document that the context's bare width is content width.
|
Follow-ups from review passes on this diff, plus a demo recording. Applied in 7784767:
Checked and deliberately not changed: no per-row Demo: drive run against this branch. Four replies with assistant footers, then a width sweep 100 → 80 → 60 → 44 → 34 → 27 → 24 → 30 → 40 → 70 → 100 crossing the 28/36-column footer breakpoints in both directions, then a prompt submitted at 44 columns. Footer adapts at every width; zero listener warnings in the TUI logs (the pre-fix baseline warned at 11 mounted footers). recording-320b724a-718c-4bc4-9bb4-e9b50df733d3.mp4 |
What
Listener count on the renderer grew linearly with transcript length, tripping Bun's EventTarget warning (
"11 resize listeners added to [CliRenderer]. MaxListeners is undefined...") within about four prompts of a live session and climbing into the dozens over a long one.Before / After
Before: every
AssistantFooter(one per assistant message) and everySessionImages(one per user message, per tool part, and per grouped tool section — even when it renders nothing, since the hook ran before the empty-images guard) calleduseTerminalDimensions(), which registers a rendererresizelistener for the lifetime of the mounted row. Transcript rows stay mounted for the life of the session view, so a 10-prompt drive session logged the leak warning with zero actual resizes.After: the session route's single existing
useTerminalDimensions()subscription is exposed as a reactiveterminalsize on the session context, and the row components read that instead. The same 10-prompt session registers no extra listeners and logs no warning.How
packages/tui/src/routes/session/index.tsx:terminal: { width, height }backed by reactive getters over the route-leveldimensions()memo, with a doc comment steering row components away from the hook.AssistantFooterreadsctx.terminal.width;SessionImagesreadsctx.terminal.height. Reactivity is preserved — the getters track the same signal the hook wrapped.Scope
Only the two per-row subscribers. App-chrome
useTerminalDimensions()call sites (prompt, tabs, dialogs, toasts) are bounded and untouched. NosetMaxListenerssuppression — the growth is fixed rather than silenced.Testing
bun run --cwd packages/tui typecheckresize listenerswarnings in TUI stderr; the same script against the v2 baseline warns at 11 listeners. Footer model/duration hiding still responds to width (getters remain reactive).