Skip to content

fix(vterm): track SGR per cell so re-attach replays colors#24

Merged
aksOps merged 1 commit into
mainfrom
fix/vterm-color-replay
Jun 11, 2026
Merged

fix(vterm): track SGR per cell so re-attach replays colors#24
aksOps merged 1 commit into
mainfrom
fix/vterm-color-replay

Conversation

@aksOps

@aksOps aksOps commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Problem

Re-attaching to a session rendered it black and white: vterm stored bare runes and discarded SGR by design, so Redraw (the replay a new attach client receives) repainted the screen colorless. The SIGWINCH nudge only rescues full-screen TUIs; inline agents (claude, omp) never repaint their history.

Fix

Per-cell SGR tracking + attribute-aware replay:

  • cell = rune + attr (16/256/truecolor fg+bg, bold/dim/italic/underline/blink/reverse/strike).
  • Real SGR parser incl. colon sub-parameter forms (38:5:196, 38:2::r:g:b, 4:0) and 58 underline-color skip.
  • Back-color-erase: erases/scroll-reveals/inserts fill with the current background (colored bars replay intact); resize keeps bg-colored blank rows consistently with Redraw.
  • Redraw repaints each cell with its original attributes; reset-prefixed transitions; reset before the cursor park.
  • Capture/peek stays plain text by contract; history stays plain (scrollback replays uncolored — only the visible screen carries attributes).

Test plan

  • 6 new tests: color replay (16/256/truecolor), bright+bg, attribute-off codes, colon forms, BCE, EL-with-bg; existing 28 vterm tests unchanged except the Redraw prefix assertion (now expects the leading attribute reset).
  • Full suite + -race green; adversarially reviewed (one finding — resize/Redraw blank-row inconsistency — fixed in-branch).

The emulator stored bare runes and discarded SGR by design, so the
replay a re-attaching client receives (Redraw) repainted the screen
black and white; the follow-up resize nudge only rescues full-screen
TUIs that fully repaint on SIGWINCH - inline agents (claude, omp) never
repaint their history, so everything stayed monochrome until new output
arrived.

Track attributes per cell and replay them:
  - cell = rune + attr (16/256/truecolor fg+bg, bold/dim/italic/
    underline/blink/reverse/strike), stamped from the terminal's current
    SGR state
  - a real SGR parser, including colon sub-parameter forms (38:5:196,
    38:2::r:g:b, 4:0) and a 58 underline-color skip
  - back-color-erase: erases, scroll-revealed rows, and inserted blanks
    fill with the current background so colored bars replay intact;
    resize keeps bg-colored blank rows the same way Redraw paints them
  - Redraw repaints each cell with its original attributes (reset
    prefix, per-run transitions, reset before the cursor park)

Capture/peek stays plain text by contract, and history stays plain
rowText - scrollback replays uncolored; only the visible screen carries
attributes.
@sonarqubecloud

Copy link
Copy Markdown

@aksOps aksOps merged commit 4439270 into main Jun 11, 2026
13 checks passed
@aksOps aksOps deleted the fix/vterm-color-replay branch June 11, 2026 08:04
aksOps added a commit that referenced this pull request Jun 13, 2026
…oll and arrows (#28)

Reported against v0.2.1: on a session's first launch scroll and arrow keys
work, but after detach+re-attach (or resume) they go dead.

Root cause, same family as #24 (which replayed SGR colors): the vterm
captured screen content but discarded the agent's input-affecting DEC
private modes. setPrivateMode treated mouse tracking, bracketed paste, and
cursor visibility as "no effect on captured text" and never tracked
application cursor keys (DECCKM ?1) or keypad mode at all; Redraw emitted
only SGR + content + cursor position. On first attach the agent's mode
setup flowed live through the PTY to the attached client, so it worked. On
detach the attach client resets those modes; on re-attach the host sends
only Redraw(), leaving the client in default mode while the still-running
agent expects application-cursor (SS3) arrows and mouse reporting -- so
arrows are ignored and the wheel does nothing (v0.2.1's ?1007 change
removed the wheel->arrow fallback that had masked it).

The vterm now tracks the input/cursor-affecting modes (DECCKM ?1, mouse
?1000/1002/1003 + focus ?1004 + encodings ?1005/1006/1015, bracketed paste
?2004, cursor visibility ?25, application keypad ESC=) and Redraw replays
the ones that differ from their power-on default, before the grid content,
so a re-attaching or resuming client lands in the agent's expected state.
Capture stays plain text; alt-screen and SGR replay are unchanged.

Adds unit coverage for each replayed mode (incl. default suppression,
toggle-off, RIS reset, alt-screen, ordering) and an end-to-end
TestReattachReplaysAgentPrivateModes that detaches and re-attaches to a
live host and asserts the modes come back.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant