diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index 30ab1632..98cd110f 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -184,7 +184,6 @@ export class Editor implements Component, Focusable { // Bracketed paste mode buffering private pasteBuffer: string = ""; private isInPaste: boolean = false; - private pendingShiftEnter: boolean = false; // Prompt history for up/down navigation private history: string[] = []; @@ -448,21 +447,6 @@ export class Editor implements Component, Focusable { return; } - if (this.pendingShiftEnter) { - if (data === "\r") { - this.pendingShiftEnter = false; - this.addNewLine(); - return; - } - this.pendingShiftEnter = false; - this.insertCharacter("\\"); - } - - if (data === "\\") { - this.pendingShiftEnter = true; - return; - } - // Ctrl+C - let parent handle (exit/clear) if (kb.matches(data, "copy")) { return; @@ -602,8 +586,7 @@ export class Editor implements Component, Focusable { data === "\x1b\r" || data === "\x1b[13;2~" || (data.length > 1 && data.includes("\x1b") && data.includes("\r")) || - (data === "\n" && data.length === 1) || - data === "\\\r" + (data === "\n" && data.length === 1) ) { this.addNewLine(); return; @@ -613,6 +596,15 @@ export class Editor implements Component, Focusable { if (kb.matches(data, "submit")) { if (this.disableSubmit) return; + // Workaround for terminals without Shift+Enter support: + // If char before cursor is \, delete it and insert newline instead of submitting. + const currentLine = this.state.lines[this.state.cursorLine] || ""; + if (this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === "\\") { + this.handleBackspace(); + this.addNewLine(); + return; + } + let result = this.state.lines.join("\n").trim(); for (const [pasteId, pasteContent] of this.pastes) { const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g"); diff --git a/packages/tui/src/components/input.ts b/packages/tui/src/components/input.ts index 87c19f08..369ea2fb 100644 --- a/packages/tui/src/components/input.ts +++ b/packages/tui/src/components/input.ts @@ -19,7 +19,6 @@ export class Input implements Component, Focusable { // Bracketed paste mode buffering private pasteBuffer: string = ""; private isInPaste: boolean = false; - private pendingShiftEnter: boolean = false; getValue(): string { return this.value; @@ -68,22 +67,6 @@ export class Input implements Component, Focusable { return; } - if (this.pendingShiftEnter) { - if (data === "\r") { - this.pendingShiftEnter = false; - if (this.onSubmit) this.onSubmit(this.value); - return; - } - this.pendingShiftEnter = false; - this.value = `${this.value.slice(0, this.cursor)}\\${this.value.slice(this.cursor)}`; - this.cursor += 1; - } - - if (data === "\\") { - this.pendingShiftEnter = true; - return; - } - const kb = getEditorKeybindings(); // Escape/Cancel diff --git a/packages/tui/test/editor.test.ts b/packages/tui/test/editor.test.ts index 12859797..61341d3b 100644 --- a/packages/tui/test/editor.test.ts +++ b/packages/tui/test/editor.test.ts @@ -283,8 +283,17 @@ describe("Editor component", () => { }); }); - describe("Shift+Enter handling", () => { - it("treats split VS Code Shift+Enter as a newline", () => { + describe("Backslash+Enter newline workaround", () => { + it("inserts backslash immediately (no buffering)", () => { + const editor = new Editor(createTestTUI(), defaultEditorTheme); + + editor.handleInput("\\"); + + // Backslash should be visible immediately, not buffered + assert.strictEqual(editor.getText(), "\\"); + }); + + it("converts standalone backslash to newline on Enter", () => { const editor = new Editor(createTestTUI(), defaultEditorTheme); editor.handleInput("\\"); @@ -293,7 +302,7 @@ describe("Editor component", () => { assert.strictEqual(editor.getText(), "\n"); }); - it("inserts a literal backslash when not followed by Enter", () => { + it("inserts backslash normally when followed by other characters", () => { const editor = new Editor(createTestTUI(), defaultEditorTheme); editor.handleInput("\\"); @@ -301,6 +310,35 @@ describe("Editor component", () => { assert.strictEqual(editor.getText(), "\\x"); }); + + it("does not trigger newline when backslash is not immediately before cursor", () => { + const editor = new Editor(createTestTUI(), defaultEditorTheme); + let submitted = false; + + editor.onSubmit = () => { + submitted = true; + }; + + editor.handleInput("\\"); + editor.handleInput("x"); + editor.handleInput("\r"); + + // Should submit, not insert newline (backslash not at cursor) + assert.strictEqual(submitted, true); + }); + + it("only removes one backslash when multiple are present", () => { + const editor = new Editor(createTestTUI(), defaultEditorTheme); + + editor.handleInput("\\"); + editor.handleInput("\\"); + editor.handleInput("\\"); + assert.strictEqual(editor.getText(), "\\\\\\"); + + editor.handleInput("\r"); + // Only the last backslash is removed, newline inserted + assert.strictEqual(editor.getText(), "\\\\\n"); + }); }); describe("Unicode text editing behavior", () => { diff --git a/packages/tui/test/input.test.ts b/packages/tui/test/input.test.ts index 98bd71d4..fc61f5e9 100644 --- a/packages/tui/test/input.test.ts +++ b/packages/tui/test/input.test.ts @@ -3,23 +3,28 @@ import { describe, it } from "node:test"; import { Input } from "../src/components/input.js"; describe("Input component", () => { - it("treats split VS Code Shift+Enter as submit", () => { + it("submits value including backslash on Enter", () => { const input = new Input(); let submitted: string | undefined; - input.setValue("hello"); input.onSubmit = (value) => { submitted = value; }; + // Type hello, then backslash, then Enter + input.handleInput("h"); + input.handleInput("e"); + input.handleInput("l"); + input.handleInput("l"); + input.handleInput("o"); input.handleInput("\\"); input.handleInput("\r"); - assert.strictEqual(submitted, "hello"); - assert.strictEqual(input.getValue(), "hello"); + // Input is single-line, no backslash+Enter workaround + assert.strictEqual(submitted, "hello\\"); }); - it("inserts a literal backslash when not followed by Enter", () => { + it("inserts backslash as regular character", () => { const input = new Input(); input.handleInput("\\");