fix(tui): restore cursorUp start-of-line jump when input is non-empty (#5789)

This commit is contained in:
4h9fbZ
2026-06-16 15:14:17 +02:00
committed by GitHub
parent 06d8c54de2
commit 3039f3e17d
2 changed files with 16 additions and 4 deletions

View File

@@ -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 { private isOnFirstVisualLine(): boolean {
const visualLines = this.buildVisualLineMap(this.lastWidth); const visualLines = this.buildVisualLineMap(this.lastWidth);
const currentVisualLine = this.findCurrentVisualLine(visualLines); const currentVisualLine = this.findCurrentVisualLine(visualLines);
@@ -803,7 +807,10 @@ export class Editor implements Component, Focusable {
// Arrow key navigation (with history support) // Arrow key navigation (with history support)
if (kb.matches(data, "tui.editor.cursorUp")) { 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); this.navigateHistory(-1);
} else if (this.isOnFirstVisualLine()) { } else if (this.isOnFirstVisualLine()) {
// Already at top - jump to start of line // Already at top - jump to start of line

View File

@@ -79,7 +79,7 @@ describe("Editor component", () => {
assert.strictEqual(editor.getText(), "first"); 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); const editor = new Editor(createTestTUI(), defaultEditorTheme);
editor.addToHistory("prompt"); editor.addToHistory("prompt");
@@ -87,12 +87,16 @@ describe("Editor component", () => {
editor.handleInput("\x1b[D"); editor.handleInput("\x1b[D");
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"); assert.strictEqual(editor.getText(), "prompt");
editor.handleInput("\x1b[B"); // Down - restores draft editor.handleInput("\x1b[B"); // Down - restores draft
assert.strictEqual(editor.getText(), "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", () => { it("navigates forward through history with Down arrow", () => {
@@ -104,6 +108,7 @@ describe("Editor component", () => {
editor.setText("draft"); editor.setText("draft");
// Go to oldest // Go to oldest
editor.handleInput("\x1b[A"); // start of draft
editor.handleInput("\x1b[A"); // third editor.handleInput("\x1b[A"); // third
editor.handleInput("\x1b[A"); // second editor.handleInput("\x1b[A"); // second
editor.handleInput("\x1b[A"); // first editor.handleInput("\x1b[A"); // first