diff --git a/src/tui/tui-input-history.test.ts b/src/tui/tui-input-history.test.ts new file mode 100644 index 000000000..1a6870b99 --- /dev/null +++ b/src/tui/tui-input-history.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it, vi } from "vitest"; + +import { createEditorSubmitHandler } from "./tui.js"; + +describe("createEditorSubmitHandler", () => { + it("adds submitted messages to editor history", () => { + const editor = { + setText: vi.fn(), + addToHistory: vi.fn(), + }; + + const handler = createEditorSubmitHandler({ + editor, + handleCommand: vi.fn(), + sendMessage: vi.fn(), + }); + + handler("hello world"); + + expect(editor.setText).toHaveBeenCalledWith(""); + expect(editor.addToHistory).toHaveBeenCalledWith("hello world"); + }); + + it("trims input before adding to history", () => { + const editor = { + setText: vi.fn(), + addToHistory: vi.fn(), + }; + + const handler = createEditorSubmitHandler({ + editor, + handleCommand: vi.fn(), + sendMessage: vi.fn(), + }); + + handler(" hi "); + + expect(editor.addToHistory).toHaveBeenCalledWith("hi"); + }); + + it("does not add empty submissions to history", () => { + const editor = { + setText: vi.fn(), + addToHistory: vi.fn(), + }; + + const handler = createEditorSubmitHandler({ + editor, + handleCommand: vi.fn(), + sendMessage: vi.fn(), + }); + + handler(" "); + + expect(editor.addToHistory).not.toHaveBeenCalled(); + }); + + it("routes slash commands to handleCommand", () => { + const editor = { + setText: vi.fn(), + addToHistory: vi.fn(), + }; + const handleCommand = vi.fn(); + const sendMessage = vi.fn(); + + const handler = createEditorSubmitHandler({ + editor, + handleCommand, + sendMessage, + }); + + handler("/models"); + + expect(editor.addToHistory).toHaveBeenCalledWith("/models"); + expect(handleCommand).toHaveBeenCalledWith("/models"); + expect(sendMessage).not.toHaveBeenCalled(); + }); + + it("routes normal messages to sendMessage", () => { + const editor = { + setText: vi.fn(), + addToHistory: vi.fn(), + }; + const handleCommand = vi.fn(); + const sendMessage = vi.fn(); + + const handler = createEditorSubmitHandler({ + editor, + handleCommand, + sendMessage, + }); + + handler("hello"); + + expect(editor.addToHistory).toHaveBeenCalledWith("hello"); + expect(sendMessage).toHaveBeenCalledWith("hello"); + expect(handleCommand).not.toHaveBeenCalled(); + }); +}); diff --git a/src/tui/tui.ts b/src/tui/tui.ts index a5e6e34d7..bf777f133 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -36,6 +36,30 @@ import type { export { resolveFinalAssistantText } from "./tui-formatters.js"; export type { TuiOptions } from "./tui-types.js"; +export function createEditorSubmitHandler(params: { + editor: { + setText: (value: string) => void; + addToHistory: (value: string) => void; + }; + handleCommand: (value: string) => Promise | void; + sendMessage: (value: string) => Promise | void; +}) { + return (text: string) => { + const value = text.trim(); + params.editor.setText(""); + if (!value) return; + + // Enable built-in editor prompt history navigation (up/down). + params.editor.addToHistory(value); + + if (value.startsWith("/")) { + void params.handleCommand(value); + return; + } + void params.sendMessage(value); + }; +} + export async function runTui(opts: TuiOptions) { const config = loadConfig(); const initialSessionInput = (opts.session ?? "").trim(); @@ -473,16 +497,11 @@ export async function runTui(opts: TuiOptions) { }); updateAutocompleteProvider(); - editor.onSubmit = (text) => { - const value = text.trim(); - editor.setText(""); - if (!value) return; - if (value.startsWith("/")) { - void handleCommand(value); - return; - } - void sendMessage(value); - }; + editor.onSubmit = createEditorSubmitHandler({ + editor, + handleCommand, + sendMessage, + }); editor.onEscape = () => { void abortActive();