diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index ee272ba44..4a019a693 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -95,6 +95,7 @@ - Fixed mid-prompt skill autocomplete so Tab and Enter accept the highlighted `/skill:` suggestion and Backspace dismisses the popup immediately after removing the triggering slash ([#4619](https://github.com/can1357/oh-my-pi/issues/4619)). - Fixed submitted slash-command arguments treating `@` file-reference tokens as prompt-composer autocomplete triggers when the command does not define argument completions. ([#4600](https://github.com/can1357/oh-my-pi/issues/4600)) - Fixed box-drawing tree lines (`├── item` — directory layouts, decision trees) in prose shearing apart when they wrap: continuation rows now hang under the node text with ancestor rails carried through (`├` → `│`, `└` → blank) instead of restarting at column 0. Applies to prose paragraphs (including inside blockquotes) only when a line with a branch-connector prefix (`├──`, `└─`, …) actually overflows; fitting lines, non-tree prose, and code blocks render byte-for-byte as before. +- Fixed PageUp/PageDown on an idle (empty) prompt editor stepping through prompt history like the Up/Down arrows instead of paging the draft. The paging keys now only scroll the editor viewport (a no-op on a short draft) and never touch prompt history ([#4754](https://github.com/can1357/oh-my-pi/issues/4754)). ## [16.3.10] - 2026-07-06 diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index 157690e1d..9c12ec451 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -1364,21 +1364,14 @@ export class Editor implements Component, Focusable { } else if (kb.matches(data, "tui.editor.cursorLineEnd")) { this.#moveToLineEnd(); } - // Page navigation (PageUp/PageDown) + // Page navigation (PageUp/PageDown): page the editor viewport only. On a + // short draft this is a no-op — it never steps prompt history (that stays + // on Up/Down), so an idle empty editor swallows the keys instead of + // surprising the user by loading the previous prompt (#4754). else if (kb.matches(data, "tui.editor.pageUp")) { - if (this.#isEditorEmpty()) { - this.#navigateHistory(-1); - } else if (this.#historyIndex > -1 && this.#isOnFirstVisualLine()) { - this.#navigateHistory(-1); - } else { - this.#pageScroll(-1); - } + this.#pageScroll(-1); } else if (kb.matches(data, "tui.editor.pageDown")) { - if (this.#historyIndex > -1 && this.#isOnLastVisualLine()) { - this.#navigateHistory(1); - } else { - this.#pageScroll(1); - } + this.#pageScroll(1); } // Forward delete (Fn+Backspace or Delete key, including Shift+Delete) else if (kb.matches(data, "tui.editor.deleteCharForward") || matchesKey(data, "shift+delete")) { diff --git a/packages/tui/test/editor.test.ts b/packages/tui/test/editor.test.ts index c8b314373..65503cbd8 100644 --- a/packages/tui/test/editor.test.ts +++ b/packages/tui/test/editor.test.ts @@ -2012,6 +2012,27 @@ describe("Editor component", () => { expect(editor.getCursor()).toEqual({ line: 6, col: 2 }); }); + it("PageUp/PageDown on an idle editor never step prompt history (#4754)", () => { + const editor = new Editor(defaultEditorTheme); + + editor.addToHistory("first prompt"); + editor.addToHistory("second prompt"); + editor.render(80); + expect(editor.getText()).toBe(""); + + editor.handleInput("\x1b[5~"); // PageUp on empty editor + expect(editor.getText()).toBe(""); + + editor.handleInput("\x1b[6~"); // PageDown on empty editor + expect(editor.getText()).toBe(""); + + // While browsing history (entered via Up), PageUp must not advance it. + editor.handleInput("\x1b[A"); // Up - shows "second prompt" + expect(editor.getText()).toBe("second prompt"); + editor.handleInput("\x1b[5~"); // PageUp - stays put + expect(editor.getText()).toBe("second prompt"); + }); + it("moves correctly through wrapped visual lines without getting stuck", () => { const editor = new Editor(defaultEditorTheme);