Skip to content

tmux-style server-side screen model for terminals (kills the replay-artifact class for good) #231

Description

@parsakhaz

follow-up to #230. that issue stops new garbage at the source; this one is the architectural fix that makes the whole artifact class impossible.

the realization

tmux repaints near-perfectly when you reattach from a different-sized client because tmux is not a recorder, it's a terminal emulator in the middle. it parses every escape sequence into a server-side screen model (cell grid + history with soft-wrap flags), and on attach it re-wraps soft-wrapped lines to the new width, pokes the live program with a resize so the prompt/TUI repaints itself, then synthesizes FRESH escape sequences from the model to paint the client. it never replays the original byte stream. redraw-from-model instead of replay-from-recording is the entire trick.

(footnote: even tmux's scrollback keeps hard-wrapped redraws from narrow sessions. nobody can reflow line breaks the shell emitted as output. but the visible screen and all soft-wrapped history restore perfectly.)

where pane stands

we're halfway there. xterm.js in the renderer IS that screen model: it tracks soft wraps and reflows them on live resizes, which is why sash drags look fine. we deviate from tmux in one place: persistence/restore replays the raw byte recording (TerminalPanel.tsx ~406-415 reset+replay, restore ~830-852), which faithfully reproduces whatever rendering was baked into the bytes.

proposal

run a headless terminal emulator in the main process per pty (@xterm/headless exists for exactly this):

  • main process feeds pty output into the headless instance, which becomes the authoritative grid + scrollback
  • renderers paint from serialized snapshots of that model (serialize addon works on headless) instead of replaying raw bytes, then stream the live tail
  • on restore/refresh/restart: snapshot at current size, soft wraps reflow, no replay artifacts ever
  • alt-screen TUIs keep the existing behavior (resize-and-let-the-program-repaint, already special-cased at TerminalPanel.tsx ~397-404)

what makes this a real project, not a quick fix

  • the raw-replay path is load-bearing today: app-restart persistence, the refresh-on-activate pattern, 50k-line scrollback
  • memory cost: one headless emulator per terminal panel in the main process, scrollback lives there
  • the db persistence story changes: persist the serialized model (or rebuild it from raw bytes once at startup) instead of/alongside the raw stream
  • need to be careful that the renderer's live xterm and the headless model can't drift (single writer: main process)

do #230 first, it's 90% of the user-visible pain for a fraction of this effort.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions