From c1a1bc776c35cf395df7fd946152db33b53bf426 Mon Sep 17 00:00:00 2001 From: leonardo3130 Date: Fri, 5 Dec 2025 13:54:58 +0100 Subject: [PATCH 1/4] fix: fix history navigations issues described in #66 --- src/index.tsx | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 547d98a..807b235 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -57,6 +57,8 @@ const Terminal = ({ const [history, setHistory] = useState([]); const [currentLineInput, setCurrentLineInput] = useState(""); + const [tmpInputValue, setTmpInputValue] = useState(""); + const [cursorPos, setCursorPos] = useState(0); const scrollIntoViewRef = useRef(null); @@ -91,9 +93,17 @@ const Terminal = ({ // If we're not currently looking at history (oldIndex === -1) and user presses ArrowUp, jump to the last entry. if (oldIndex === -1 && direction === -1) { + setTmpInputValue(currentLineInput); return history.length - 1; } + // If we're in the last history entry and user presses ArrowDown, go back to the temporary input value. + if (oldIndex === history.length - 1 && direction === 1) { + setCurrentLineInput(tmpInputValue); + setTmpInputValue(""); + return -1; + } + // If oldIndex === -1 and direction === 1 (ArrowDown), keep -1 (nothing to go to). if (oldIndex === -1 && direction === 1) { return -1; @@ -119,15 +129,19 @@ const Terminal = ({ setCursorPos(0); // history update - setHistory((previousHistory) => - currentLineInput.trim() === "" || - previousHistory[previousHistory.length - 1] === currentLineInput.trim() - ? previousHistory - : [...previousHistory, currentLineInput], - ); - setHistoryIndex(-1); + if (currentLineInput.trim() !== "") { + setHistory((previousHistory) => + previousHistory[previousHistory.length - 1] === + currentLineInput.trim() + ? previousHistory + : [...previousHistory, currentLineInput], + ); + } + setHistoryIndex(-1); setCurrentLineInput(""); + setTmpInputValue(""); + setTimeout( () => scrollIntoViewRef?.current?.scrollIntoView({ From 22d3cbfdc94822079144fa5a6624782a231b5c15 Mon Sep 17 00:00:00 2001 From: leonardo3130 Date: Fri, 5 Dec 2025 13:56:24 +0100 Subject: [PATCH 2/4] feat: additional tests for terminal history navigation --- tests/terminal.history.spec.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/terminal.history.spec.tsx b/tests/terminal.history.spec.tsx index d2bd5dc..41410da 100644 --- a/tests/terminal.history.spec.tsx +++ b/tests/terminal.history.spec.tsx @@ -103,3 +103,31 @@ describe("Terminal history persistence", () => { expect(input).toHaveValue("first"); }); }); + +test("preserves current input when navigating history and returning back down", () => { + localStorage.setItem("terminal-history", JSON.stringify(["old1", "old2"])); + + render( { }} />); + const input = screen.getByPlaceholderText("Terminal Hidden Input"); + + fireEvent.change(input, { target: { value: "new-typing" } }); + + fireEvent.keyDown(input, { key: "ArrowUp" }); + expect(input).toHaveValue("old2"); + + fireEvent.keyDown(input, { key: "ArrowDown" }); + expect(input).toHaveValue("new-typing"); +}); + +test("does not clear input when ArrowDown is pressed while at latest history entry", () => { + localStorage.setItem("terminal-history", JSON.stringify(["cmdA", "cmdB"])); + + render( { }} />); + const input = screen.getByPlaceholderText("Terminal Hidden Input"); + + fireEvent.keyDown(input, { key: "ArrowUp" }); + expect(input).toHaveValue("cmdB"); + + fireEvent.keyDown(input, { key: "ArrowDown" }); + expect(input).toHaveValue(""); +}); From 37200637adee0a8d0917adf947de33fa10f4c518 Mon Sep 17 00:00:00 2001 From: Jon Bake Date: Sun, 14 Dec 2025 16:07:54 -0600 Subject: [PATCH 3/4] Minor - comment clarification Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 807b235..2f4248f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -97,7 +97,7 @@ const Terminal = ({ return history.length - 1; } - // If we're in the last history entry and user presses ArrowDown, go back to the temporary input value. + // If we're at the most recent history entry and user presses ArrowDown, go back to the temporary input value. if (oldIndex === history.length - 1 && direction === 1) { setCurrentLineInput(tmpInputValue); setTmpInputValue(""); From 57b3398c0eee6e7320ce0a4621d87d96dc5570af Mon Sep 17 00:00:00 2001 From: Jon Bake Date: Sun, 14 Dec 2025 16:08:52 -0600 Subject: [PATCH 4/4] Move tests to "Terminal history persistence block Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/terminal.history.spec.tsx | 38 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/terminal.history.spec.tsx b/tests/terminal.history.spec.tsx index 41410da..c803f43 100644 --- a/tests/terminal.history.spec.tsx +++ b/tests/terminal.history.spec.tsx @@ -102,32 +102,32 @@ describe("Terminal history persistence", () => { expect(input).toHaveValue("first"); }); -}); -test("preserves current input when navigating history and returning back down", () => { - localStorage.setItem("terminal-history", JSON.stringify(["old1", "old2"])); + test("preserves current input when navigating history and returning back down", () => { + localStorage.setItem("terminal-history", JSON.stringify(["old1", "old2"])); - render( { }} />); - const input = screen.getByPlaceholderText("Terminal Hidden Input"); + render( { }} />); + const input = screen.getByPlaceholderText("Terminal Hidden Input"); - fireEvent.change(input, { target: { value: "new-typing" } }); + fireEvent.change(input, { target: { value: "new-typing" } }); - fireEvent.keyDown(input, { key: "ArrowUp" }); - expect(input).toHaveValue("old2"); + fireEvent.keyDown(input, { key: "ArrowUp" }); + expect(input).toHaveValue("old2"); - fireEvent.keyDown(input, { key: "ArrowDown" }); - expect(input).toHaveValue("new-typing"); -}); + fireEvent.keyDown(input, { key: "ArrowDown" }); + expect(input).toHaveValue("new-typing"); + }); -test("does not clear input when ArrowDown is pressed while at latest history entry", () => { - localStorage.setItem("terminal-history", JSON.stringify(["cmdA", "cmdB"])); + test("does not clear input when ArrowDown is pressed while at latest history entry", () => { + localStorage.setItem("terminal-history", JSON.stringify(["cmdA", "cmdB"])); - render( { }} />); - const input = screen.getByPlaceholderText("Terminal Hidden Input"); + render( { }} />); + const input = screen.getByPlaceholderText("Terminal Hidden Input"); - fireEvent.keyDown(input, { key: "ArrowUp" }); - expect(input).toHaveValue("cmdB"); + fireEvent.keyDown(input, { key: "ArrowUp" }); + expect(input).toHaveValue("cmdB"); - fireEvent.keyDown(input, { key: "ArrowDown" }); - expect(input).toHaveValue(""); + fireEvent.keyDown(input, { key: "ArrowDown" }); + expect(input).toHaveValue(""); + }); });