Merge PR #5512: fix(tui): stop PageUp/PageDown stepping prompt history at idle (@roboomp)

This commit is contained in:
can1357
2026-07-14 23:11:10 +02:00
3 changed files with 28 additions and 13 deletions
+1
View File
@@ -95,6 +95,7 @@
- Fixed mid-prompt skill autocomplete so Tab and Enter accept the highlighted `/skill:<name>` 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
+6 -13
View File
@@ -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")) {
+21
View File
@@ -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);