diff --git a/src/index.tsx b/src/index.tsx index 547d98a..2f4248f 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 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(""); + 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({ diff --git a/tests/terminal.history.spec.tsx b/tests/terminal.history.spec.tsx index d2bd5dc..c803f43 100644 --- a/tests/terminal.history.spec.tsx +++ b/tests/terminal.history.spec.tsx @@ -102,4 +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"])); + + 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(""); + }); });