fix: use _term.paste() for Ctrl+Shift+V — enables bracketed paste mode
Raw WebSocket send bypassed xterm.js's paste pipeline, so multiline text was sent without bracketed paste markers (ESC[200~...ESC[201~). The shell treated each newline as Enter, executing lines separately. Fix: _term.paste(text) goes through the proper pipeline — converts CR/LF, wraps in bracketed paste markers, then fires onData → WebSocket.
This commit is contained in:
@@ -375,8 +375,12 @@ function openTerminal(sessionName, remoteId) {
|
|||||||
e.preventDefault(); // suppress browser native paste event (prevents double-paste via xterm textarea)
|
e.preventDefault(); // suppress browser native paste event (prevents double-paste via xterm textarea)
|
||||||
if (navigator.clipboard && navigator.clipboard.readText) {
|
if (navigator.clipboard && navigator.clipboard.readText) {
|
||||||
navigator.clipboard.readText().then(function(text) {
|
navigator.clipboard.readText().then(function(text) {
|
||||||
if (text && _ws && _ws.readyState === WebSocket.OPEN) {
|
if (text && _term) {
|
||||||
_ws.send(_encodePayload(0x30, text));
|
// Use xterm.js paste() pipeline: converts \r\n→\r, wraps in bracketed
|
||||||
|
// paste markers (ESC[200~...ESC[201~) so the shell treats multiline text
|
||||||
|
// as a paste block rather than executing each line on Enter.
|
||||||
|
// Fires onData → our WebSocket handler automatically.
|
||||||
|
_term.paste(text);
|
||||||
}
|
}
|
||||||
}).catch(function() {});
|
}).catch(function() {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1012,6 +1012,33 @@ test('terminal.js Ctrl+Shift+V handler calls preventDefault to suppress double p
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Multiline paste (bracketed paste mode) ---
|
||||||
|
|
||||||
|
test('terminal.js Ctrl+Shift+V uses _term.paste() for bracketed paste mode', () => {
|
||||||
|
// Regression: the handler sent raw bytes via _ws.send(_encodePayload(0x30, text)),
|
||||||
|
// bypassing xterm.js's paste pipeline. Multiline text had no bracketed paste markers
|
||||||
|
// (ESC[200~...ESC[201~), so the shell treated each \n as Enter — executing each line
|
||||||
|
// separately instead of pasting as a block.
|
||||||
|
// Fix: use _term.paste(text) which goes through the proper pipeline.
|
||||||
|
const source = fs.readFileSync(new URL('../terminal.js', import.meta.url), 'utf8');
|
||||||
|
|
||||||
|
// Locate the Ctrl+Shift+V block
|
||||||
|
const vIdx = source.indexOf("e.key === 'V'");
|
||||||
|
assert.ok(vIdx !== -1, "must have a Ctrl+Shift+V key check");
|
||||||
|
const blockStart = source.lastIndexOf('if (', vIdx);
|
||||||
|
const blockEnd = source.indexOf('return false', vIdx);
|
||||||
|
const vBlock = source.substring(blockStart, blockEnd);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
vBlock.includes('_term.paste'),
|
||||||
|
'Ctrl+Shift+V must use _term.paste() for proper bracketed paste mode support',
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
!vBlock.includes('_encodePayload') && !vBlock.includes('_ws.send'),
|
||||||
|
'Ctrl+Shift+V must NOT send raw bytes via WebSocket (bypasses bracketed paste wrapping)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// --- Federation reconnect routing ---
|
// --- Federation reconnect routing ---
|
||||||
|
|
||||||
test('terminal.js reconnect uses federation connect path for remote sessions', () => {
|
test('terminal.js reconnect uses federation connect path for remote sessions', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user