refactor: remove ALL custom paste handlers — clean slate per COE review
After 9 clipboard commits that kept breaking each other, COE review determined: ttyd uses ClipboardAddon, not custom handlers. Ctrl+Shift+V on Linux and Cmd+V on macOS both trigger native browser paste events that xterm.js catches via its hidden textarea handler. Zero custom paste code needed. Deleted synthetic ClipboardEvent dispatch, readText calls, and all paste-related comments. Kept: Ctrl+Shift+C copy, auto-copy on selection, OSC 52, Ctrl+F search.
This commit is contained in:
@@ -364,13 +364,9 @@ function openTerminal(sessionName, remoteId) {
|
|||||||
_term.open(container);
|
_term.open(container);
|
||||||
|
|
||||||
// --- Clipboard integration ---
|
// --- Clipboard integration ---
|
||||||
// Ctrl+Shift+C: copy selection to system clipboard (Ctrl+C still sends SIGINT)
|
// Copy: Ctrl+Shift+C intercepts and copies selection to system clipboard
|
||||||
// Ctrl+Shift+V: Ctrl+Shift+V does NOT trigger a native browser paste event (unlike Cmd+V
|
// Paste: handled natively by xterm.js (browser paste event → hidden textarea → onData → WebSocket)
|
||||||
// on macOS). We read the clipboard via navigator.clipboard.readText() and dispatch a
|
// Cmd+V (macOS) and Ctrl+Shift+V (Linux) both trigger native browser paste events
|
||||||
// synthetic ClipboardEvent on xterm.js's hidden textarea. xterm.js catches this via its
|
|
||||||
// built-in handlePasteEvent listener → prepareTextForTerminal → bracketTextForPaste
|
|
||||||
// → triggerDataEvent → onData → WebSocket. This is the same path as Cmd+V and correctly
|
|
||||||
// handles CR/LF normalization, bracketed paste mode, and UTF-8 encoding.
|
|
||||||
_term.attachCustomKeyEventHandler(function(e) {
|
_term.attachCustomKeyEventHandler(function(e) {
|
||||||
if (e.type !== 'keydown') return true;
|
if (e.type !== 'keydown') return true;
|
||||||
|
|
||||||
@@ -381,24 +377,6 @@ function openTerminal(sessionName, remoteId) {
|
|||||||
return false; // prevent xterm from processing
|
return false; // prevent xterm from processing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ctrl+Shift+V → paste from system clipboard via synthetic ClipboardEvent on xterm textarea.
|
|
||||||
// Dispatching on _term.textarea triggers xterm.js's built-in handlePasteEvent, which
|
|
||||||
// handles CR/LF normalization, bracketed paste mode, and correct UTF-8 encoding —
|
|
||||||
// the exact same code path as a native Cmd+V paste on macOS.
|
|
||||||
if (e.ctrlKey && e.shiftKey && (e.key === 'V' || e.code === 'KeyV')) {
|
|
||||||
if (navigator.clipboard && navigator.clipboard.readText && _term && _term.textarea) {
|
|
||||||
navigator.clipboard.readText().then(function(text) {
|
|
||||||
if (!text || !_term || !_term.textarea) return;
|
|
||||||
var pasteEvent = new ClipboardEvent('paste', {
|
|
||||||
clipboardData: new DataTransfer(),
|
|
||||||
});
|
|
||||||
pasteEvent.clipboardData.setData('text/plain', text);
|
|
||||||
_term.textarea.dispatchEvent(pasteEvent);
|
|
||||||
}).catch(function() {});
|
|
||||||
}
|
|
||||||
return false; // prevent xterm from processing the raw key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ctrl+F → open search bar
|
// Ctrl+F → open search bar
|
||||||
if (e.ctrlKey && !e.shiftKey && (e.key === 'f' || e.key === 'F' || e.code === 'KeyF')) {
|
if (e.ctrlKey && !e.shiftKey && (e.key === 'f' || e.key === 'F' || e.code === 'KeyF')) {
|
||||||
_openSearch();
|
_openSearch();
|
||||||
|
|||||||
@@ -993,25 +993,22 @@ test('terminal.js loads xterm-addon-image for inline graphics', () => {
|
|||||||
assert.ok(source.includes('ImageAddon'), 'must reference ImageAddon');
|
assert.ok(source.includes('ImageAddon'), 'must reference ImageAddon');
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Ctrl+Shift+V: synthetic paste event on xterm textarea ---
|
// --- Ctrl+Shift+V: xterm.js handles paste natively, no custom interception ---
|
||||||
|
|
||||||
test('terminal.js Ctrl+Shift+V dispatches synthetic paste event on textarea', () => {
|
test('terminal.js does NOT intercept Ctrl+Shift+V in attachCustomKeyEventHandler', () => {
|
||||||
// Ctrl+Shift+V does NOT trigger a native browser paste event (unlike Cmd+V on macOS).
|
// COE review: every custom paste handler we built caused either double-paste or encoding issues.
|
||||||
// Fix: read clipboard via navigator.clipboard.readText(), then dispatch a synthetic
|
// On Linux, Ctrl+Shift+V is a native browser paste shortcut — it fires a paste event on the
|
||||||
// ClipboardEvent on xterm.js's hidden textarea. This goes through xterm.js's built-in
|
// focused textarea, xterm.js catches it natively. On macOS, Cmd+V does the same.
|
||||||
// handlePasteEvent which handles CR/LF normalization, bracketed paste, and correct encoding.
|
// Zero custom paste code needed.
|
||||||
const source = fs.readFileSync(new URL('../terminal.js', import.meta.url), 'utf8');
|
const source = fs.readFileSync(new URL('../terminal.js', import.meta.url), 'utf8');
|
||||||
const handlerStart = source.indexOf('attachCustomKeyEventHandler');
|
const handlerStart = source.indexOf('attachCustomKeyEventHandler');
|
||||||
const handlerEnd = source.indexOf('// Auto-copy:', handlerStart);
|
const handlerEnd = source.indexOf('onSelectionChange', handlerStart);
|
||||||
const handlerBlock = source.substring(handlerStart, handlerEnd);
|
const handlerBlock = source.substring(handlerStart, handlerEnd);
|
||||||
assert.ok(
|
// Must NOT have any V key interception
|
||||||
handlerBlock.includes("e.key === 'V'") || handlerBlock.includes("e.code === 'KeyV'"),
|
assert.ok(!handlerBlock.includes("e.key === 'V'"),
|
||||||
'attachCustomKeyEventHandler must intercept Ctrl+Shift+V to trigger paste',
|
'must NOT intercept Ctrl+Shift+V — xterm.js handles paste natively via browser events');
|
||||||
);
|
assert.ok(!handlerBlock.includes("e.code === 'KeyV'"),
|
||||||
assert.ok(
|
'must NOT intercept KeyV — xterm.js handles paste natively via browser events');
|
||||||
handlerBlock.includes('ClipboardEvent') || handlerBlock.includes('dispatchEvent'),
|
|
||||||
'must dispatch synthetic paste event so xterm.js handlePasteEvent handles encoding correctly',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- UTF-8 output decoding via TextDecoder ---
|
// --- UTF-8 output decoding via TextDecoder ---
|
||||||
|
|||||||
Reference in New Issue
Block a user