Add synchronized output support (DEC mode 2026)

Implement synchronized output mode (CSI ? 2026 h/l) which allows
applications to batch terminal updates and render them atomically,
preventing screen tearing during rapid output.

Features:
- BSU (CSI ? 2026 h) pauses rendering, buffering row updates
- ESU (CSI ? 2026 l) flushes buffer and renders atomically
- Configurable timeout via synchronizedOutputTimeout option (default 5s)
- Exposed via terminal.modes.synchronizedOutputMode

Closes #3375

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Chris Lloyd
2025-12-04 11:16:25 -08:00
co-authored by Claude
parent 46c510a76d
commit 7228e2e2dc
10 changed files with 602 additions and 10 deletions
+8
View File
@@ -1969,6 +1969,9 @@ export class InputHandler extends Disposable implements IInputHandler {
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
this._coreService.decPrivateModes.bracketedPasteMode = true;
break;
case 2026: // synchronized output (https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036)
this._coreService.decPrivateModes.synchronizedOutput = true;
break;
}
}
return true;
@@ -2197,6 +2200,11 @@ export class InputHandler extends Disposable implements IInputHandler {
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
this._coreService.decPrivateModes.bracketedPasteMode = false;
break;
case 2026: // synchronized output (https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036)
this._coreService.decPrivateModes.synchronizedOutput = false;
// Trigger a full refresh now that the synchronized output block has ended
this._onRequestRefreshRows.fire(undefined);
break;
}
}
return true;
+1
View File
@@ -102,6 +102,7 @@ export class MockCoreService implements ICoreService {
origin: false,
reverseWraparound: false,
sendFocus: false,
synchronizedOutput: false,
wraparound: true
};
public onData: Event<string> = new Emitter<string>().event;
+1
View File
@@ -273,6 +273,7 @@ export interface IDecPrivateModes {
origin: boolean;
reverseWraparound: boolean;
sendFocus: boolean;
synchronizedOutput: boolean;
wraparound: boolean; // defaults: xterm - true, vt100 - false
}
+1
View File
@@ -22,6 +22,7 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
origin: false,
reverseWraparound: false,
sendFocus: false,
synchronizedOutput: false,
wraparound: true // defaults: xterm - true, vt100 - false
});
+1
View File
@@ -37,6 +37,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
scrollSensitivity: 1,
screenReaderMode: false,
smoothScrollDuration: 0,
synchronizedOutputTimeout: 5000,
macOptionIsMeta: false,
macOptionClickForcesSelection: false,
minimumContrastRatio: 1,
+1
View File
@@ -259,6 +259,7 @@ export interface ITerminalOptions {
scrollOnUserInput?: boolean;
scrollSensitivity?: number;
smoothScrollDuration?: number;
synchronizedOutputTimeout?: number;
tabStopWidth?: number;
theme?: ITheme;
windowsMode?: boolean;