Repository navigation
feat(term): synchronized output (DEC mode 2026) via the sync-output capability - #139
natemoo-re wants to merge 5 commits into
Conversation
… capability Full-screen frames wrap in BSU/ESU when RuntimeCapabilities.syncOutput is true; the flag rides reduce() per frame so a capability flip takes effect on the next rendered frame with no invalidation signal. Zero-diff frames emit zero bytes even when wrapped, and line mode is never wrapped. Renderer-spec 7.9 discharges the deferral recorded in 7.8.
Changeset suggestionThe current changeset no longer matches this PR. This review contains a corrected replacement. Why: Changeset package scope does not match the affected packages confidently. View the proposed changeset---
'@bomb.sh/tty': minor
---
Adds synchronized output (DEC mode 2026) to full-screen rendering.
When `RuntimeCapabilities.syncOutput` is true, every full-screen frame is wrapped in Begin/End Synchronized Update (`CSI ? 2026 h` / `CSI ? 2026 l`) so the terminal presents the frame as one atomic repaint without tearing. No-op frames and line-mode output are never wrapped.
`Capabilities.syncOutput` is raised by three evidence sources, in precedence order: a DECRPM 2026 probe reply, an XTGETTCAP `Sync` reply (value discarded; only support is recorded), and detection-time seeding from the `Sync` extended capability in the terminfo entry or a non-empty `TMUX` environment variable. Probe replies outrank seeded evidence in both directions, so a negative DECRPM reply turns the capability off even when detection seeded it.
#### Migration
`Capabilities` gains a required `syncOutput` field. Any object literal passed to `createTerm` as `terminfo.capabilities` must include it:
```diff
terminfo: {
capabilities: {
colors: 256,
trueColor: false,
bce: true,
autoMargin: true,
xenl: true,
altScreen: true,
styledUnderline: false,
+ syncOutput: false,
},
}
```
`createTermNative` now requires a `RenderCapabilities` argument as its third parameter:
```diff
- const native = await createTermNative(width, height);
+ const native = await createTermNative(width, height, { syncOutput: false });
```Review this changeset manually If this draft is correct, react with 🚀 and Cooper will commit it to this branch. |
| @@ -0,0 +1,7 @@ | |||
| --- | |||
There was a problem hiding this comment.
Changeset needs revision.
Changeset package scope does not match the affected packages confidently.
View the proposed replacement
---
'@bomb.sh/tty': minor
---
Adds synchronized output (DEC mode 2026) to full-screen rendering.
When `RuntimeCapabilities.syncOutput` is true, every full-screen frame is wrapped in Begin/End Synchronized Update (`CSI ? 2026 h` / `CSI ? 2026 l`) so the terminal presents the frame as one atomic repaint without tearing. No-op frames and line-mode output are never wrapped.
`Capabilities.syncOutput` is raised by three evidence sources, in precedence order: a DECRPM 2026 probe reply, an XTGETTCAP `Sync` reply (value discarded; only support is recorded), and detection-time seeding from the `Sync` extended capability in the terminfo entry or a non-empty `TMUX` environment variable. Probe replies outrank seeded evidence in both directions, so a negative DECRPM reply turns the capability off even when detection seeded it.
#### Migration
`Capabilities` gains a required `syncOutput` field. Any object literal passed to `createTerm` as `terminfo.capabilities` must include it:
```diff
terminfo: {
capabilities: {
colors: 256,
trueColor: false,
bce: true,
autoMargin: true,
xenl: true,
altScreen: true,
styledUnderline: false,
+ syncOutput: false,
},
}
```
`createTermNative` now requires a `RenderCapabilities` argument as its third parameter:
```diff
- const native = await createTermNative(width, height);
+ const native = await createTermNative(width, height, { syncOutput: false });
```|
Size Increased — +1.6 KB 124.2 KB unpacked |
commit: |
Per the approved spec amendment (terminfo-spec §6.1/§6.3/§7.1/§7.2/§8/§9.1, renderer-spec §7.9): syncOutput is raised only on positive evidence from three channels. - Probe row 8: XTGETTCAP Sync (DCS + q 53796e63) as its own request — some terminals honor only one capability per XTGETTCAP request, so it must not merge into the RGB;Tc query. A value-carrying reply confirms; a reply naming Sync without a value denies. Sync replies never touch the colordepth tally. - Static Capabilities gains syncOutput, seeded from the Sync extended capability in the parsed terminfo entry and from TMUX environment evidence (tmux consumes mode 2026 from inner applications). - TINV-3 precedence: probe replies outrank detect-time seeding in both directions; runtimeFromStatic now flows the seeded field through. The reply's sequence value is discarded in v1: emission remains the DEC mode 2026 form, unchanged from §7.9's normative rules.
| mode, | ||
| row, | ||
| dt, | ||
| currentCaps.syncOutput ? 1 : 0, |
There was a problem hiding this comment.
I thought we were going to move capabilities into the native state rather than copy them over on every call.
There was a problem hiding this comment.
That was indeed the direction we discussed! Oversight on my part, I'll take another pass at this.
Capabilities were copied into reduce() on every render. The renderer now owns them: set_capabilities(ct, flags) stores a CAP_* bitfield on the Clayterm instance, and reduce() reads it from there. The host pushes flags at creation and again from term.update() only when a folded event changes them. init() zeroes instance state, so the native wrapper re-pushes after every re-init; a resize keeps the wrap.
input.h already defines CAP_SYNC_OUTPUT as a CapabilityEvent key (5). Any translation unit including both headers silently redefined it.
Implements synchronized output (DEC mode 2026), previously deferred in renderer-spec §7.8.
When
RuntimeCapabilities.syncOutputis true, each full-screen frame is wrapped in BSU/ESU (CSI ? 2026 h/CSI ? 2026 l) so the terminal presents it as one atomic repaint, which prevents tearing mid-frame.