Fix browser terminal paste for hosting clients.
Deploy Ladill Hosting / deploy (push) Successful in 42s

Use bracketed paste, reliably bind paste capture, stop blocking mousedown focus, and give shells a short prompt with horizontal-scroll readline so long pasted commands do not wrap incorrectly.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-13 18:02:02 +00:00
co-authored by Cursor
parent 335c0fea61
commit a3ba2ac45b
3 changed files with 55 additions and 7 deletions
+40 -5
View File
@@ -101,10 +101,11 @@ window.hostingInteractiveTerminal = (config = {}) => {
this.queueInput(data);
}
});
// Delay initial fit to ensure container has dimensions
requestAnimationFrame(() => {
this.fitAddon.fit();
this.bindTerminalInputCapture();
this.term.focus();
});
@@ -113,7 +114,7 @@ window.hostingInteractiveTerminal = (config = {}) => {
this.resizeObserver = new ResizeObserver(() => this.queueResize());
this.resizeObserver.observe(this.$refs.terminal);
}
window.addEventListener('resize', () => this.queueResize());
window.addEventListener('beforeunload', () => this.shutdown(true));
@@ -256,7 +257,23 @@ window.hostingInteractiveTerminal = (config = {}) => {
},
handleTerminalPaste(event) {
if (!this.shouldCaptureTerminalInput(event)) {
if (!this.sessionId) {
return;
}
const terminalElement = this.$refs.terminal;
const target = event.target;
const inTerminal = Boolean(
terminalElement
&& target instanceof Node
&& (terminalElement === target || terminalElement.contains(target))
);
const termFocused = Boolean(
this.term?.textarea && document.activeElement === this.term.textarea
);
// Panel paste often targets the wrapper/div, not the xterm textarea.
if (!this.terminalActive && !inTerminal && !termFocused) {
return;
}
@@ -266,14 +283,21 @@ window.hostingInteractiveTerminal = (config = {}) => {
return;
}
this.terminalActive = true;
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation?.();
this.queueInput(text.replace(/\r?\n/g, '\r'));
// Bracketed paste keeps bash/readline from mangling long pasted commands.
const normalized = text
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.replace(/\n/g, '\r');
this.queueInput(`\x1b[200~${normalized}\x1b[201~`);
},
shouldCaptureTerminalInput(event) {
if (!this.sessionId || !this.terminalActive) {
if (!this.sessionId) {
return false;
}
@@ -281,9 +305,19 @@ window.hostingInteractiveTerminal = (config = {}) => {
const terminalElement = this.$refs.terminal;
if (terminalElement && target instanceof Node && terminalElement.contains(target)) {
this.terminalActive = true;
return true;
}
if (this.term?.textarea && document.activeElement === this.term.textarea) {
this.terminalActive = true;
return true;
}
if (!this.terminalActive) {
return false;
}
if (!(target instanceof HTMLElement)) {
return false;
}
@@ -535,6 +569,7 @@ window.hostingInteractiveTerminal = (config = {}) => {
console.log('[Terminal] focusTerminal called');
if (this.term) {
this.terminalActive = true;
this.bindTerminalInputCapture();
this.$refs.terminal?.focus();
this.term.focus();
console.log('[Terminal] terminalActive set to true');