fix(terminal): a replay strips the 8-bit C1 form of OSC 52 too (#611 merge-time fix)

xterm.js still parses OSC 52 introduced by C1 OSC (\x9d) and ended by C1 ST
(\x9c). OSC52_STRIP_RE matched only the 7-bit forms, so a direct-PTY session
that emitted the C1 form re-copied old text on every replay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-13 18:17:18 +02:00
1 parent f073742cf7
commit 836402c2c4
2 files changed
+5 -2

No files matched your search

+3 -2
View File
@@ -1566,8 +1566,9 @@ const DEC_SYNC_STRIP_RE = /\x1b\[\?2026[hl]/g;
// OSC 52 clipboard writes stripping — a replay re-feeds the raw byte history, and
// xterm parses it after chunkedTerminalWrite has already released the load
// gate, so an OSC 52 left in would re-copy old text on every tab switch.
const OSC52_STRIP_RE = /\x1b\]52;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
// gate, so an OSC 52 left in would re-copy old text on every tab switch. The
// 8-bit C1 forms (OSC \x9d, ST \x9c) too: xterm.js still parses those.
const OSC52_STRIP_RE = /(?:\x1b\]|\x9d)52;[^\x07\x1b\x9c]*(?:\x07|\x1b\\|\x9c)/g;
// The same OSC 52 text arriving again within this window is one copy delivered
// several ways (direct, tmux load-buffer -w, tmux set-clipboard forwarding).
const OSC52_DEDUPE_MS = 1000;
+2
View File
@@ -208,6 +208,8 @@ describe('main-terminal replay over a real xterm', () => {
const re = (context.globalThis as { __re: RegExp }).__re;
const input = `a\x1b]52;c;${b64('x')}\x07b\x1b]52;;${b64('y')}\x1b\\c\x1b]0;title\x07d`;
expect(input.replace(re, '')).toBe('abc\x1b]0;title\x07d');
// The 8-bit C1 forms (OSC \x9d, ST \x9c), which xterm.js also parses.
expect(`a\x9d52;c;${b64('x')}\x9cb\x1b]52;c;${b64('y')}\x9cc`.replace(re, '')).toBe('abc');
});
it('still copies a live OSC 52 written after the replay', async () => {