diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index 724dd460..bedd0105 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -390,6 +390,10 @@ export class Editor implements Component, Focusable { } } + private isEditorEmpty(): boolean { + return this.state.lines.length === 1 && this.state.lines[0] === ""; + } + private isOnFirstVisualLine(): boolean { const visualLines = this.buildVisualLineMap(this.lastWidth); const currentVisualLine = this.findCurrentVisualLine(visualLines); @@ -803,7 +807,10 @@ export class Editor implements Component, Focusable { // Arrow key navigation (with history support) if (kb.matches(data, "tui.editor.cursorUp")) { - if (this.isOnFirstVisualLine() && this.history.length > 0) { + if ( + this.isOnFirstVisualLine() && + (this.isEditorEmpty() || this.historyIndex > -1 || this.state.cursorCol === 0) + ) { this.navigateHistory(-1); } else if (this.isOnFirstVisualLine()) { // Already at top - jump to start of line diff --git a/packages/tui/test/editor.test.ts b/packages/tui/test/editor.test.ts index 2c8a7b62..0f33370e 100644 --- a/packages/tui/test/editor.test.ts +++ b/packages/tui/test/editor.test.ts @@ -79,7 +79,7 @@ describe("Editor component", () => { assert.strictEqual(editor.getText(), "first"); }); - it("restores draft on Down arrow after browsing history", () => { + it("jumps to start before entering history from a non-empty draft", () => { const editor = new Editor(createTestTUI(), defaultEditorTheme); editor.addToHistory("prompt"); @@ -87,12 +87,16 @@ describe("Editor component", () => { editor.handleInput("\x1b[D"); editor.handleInput("\x1b[D"); - editor.handleInput("\x1b[A"); // Up - shows "prompt" + editor.handleInput("\x1b[A"); // Up - jumps to start before history browsing + assert.strictEqual(editor.getText(), "draft"); + assert.deepStrictEqual(editor.getCursor(), { line: 0, col: 0 }); + + editor.handleInput("\x1b[A"); // Up at start - shows "prompt" assert.strictEqual(editor.getText(), "prompt"); editor.handleInput("\x1b[B"); // Down - restores draft assert.strictEqual(editor.getText(), "draft"); - assert.deepStrictEqual(editor.getCursor(), { line: 0, col: 3 }); + assert.deepStrictEqual(editor.getCursor(), { line: 0, col: 0 }); }); it("navigates forward through history with Down arrow", () => { @@ -104,6 +108,7 @@ describe("Editor component", () => { editor.setText("draft"); // Go to oldest + editor.handleInput("\x1b[A"); // start of draft editor.handleInput("\x1b[A"); // third editor.handleInput("\x1b[A"); // second editor.handleInput("\x1b[A"); // first