diff --git a/packages/tui/src/mini/footer.prompt.tsx b/packages/tui/src/mini/footer.prompt.tsx index 998066a1943..af5af2d4e24 100644 --- a/packages/tui/src/mini/footer.prompt.tsx +++ b/packages/tui/src/mini/footer.prompt.tsx @@ -41,6 +41,7 @@ import { createPromptHistory, displayCharAt, displaySlice, + EXIT_COMMANDS, isExitCommand, isCompactCommand, mentionTriggerIndex, @@ -531,7 +532,9 @@ export function createPromptState(input: PromptInput): PromptState { display: "/compact", description: "compact older session context to free space", } satisfies SlashOption, - { kind: "slash", name: "exit", display: "/exit", description: "close OpenCode" } satisfies SlashOption, + ...EXIT_COMMANDS.map( + (name) => ({ kind: "slash", name, display: `/${name}`, description: "close OpenCode" }) satisfies SlashOption, + ), ] const hidden = new Set(builtins.map((item) => item.name)) return [ @@ -566,11 +569,7 @@ export function createPromptState(input: PromptInput): PromptState { return fuzzysort .go(next, mixed, { - keys: [ - (item) => (item.kind === "mention" ? item.value : item.name).trimEnd(), - "display", - "description", - ], + keys: [(item) => (item.kind === "mention" ? item.value : item.name).trimEnd(), "display", "description"], }) .map((item) => item.obj) }) @@ -1054,7 +1053,7 @@ export function createPromptState(input: PromptInput): PromptState { const cursor = area.cursorOffset const head = parseSlashHead(area.plainText) - const local = !shell() && (next.name === "new" || next.name === "exit") + const local = !shell() && (next.name === "new" || isExitCommand(`/${next.name}`)) const separator = !shell() && !local && head && /\s/.test(area.plainText[head.end] ?? "") ? "" : " " const text = `/${next.name}${separator}` diff --git a/packages/tui/src/mini/prompt.shared.ts b/packages/tui/src/mini/prompt.shared.ts index 9090fb6e472..ac9bf0f4026 100644 --- a/packages/tui/src/mini/prompt.shared.ts +++ b/packages/tui/src/mini/prompt.shared.ts @@ -44,9 +44,11 @@ export function promptSame(a: RunPrompt, b: RunPrompt): boolean { ) } +export const EXIT_COMMANDS = ["exit", "quit", "q"] + export function isExitCommand(input: string): boolean { const text = input.trim().toLowerCase() - return text === "/exit" || text === "/quit" || text === ":q" + return text === ":q" || EXIT_COMMANDS.some((name) => text === `/${name}`) } export function isNewCommand(input: string): boolean { diff --git a/packages/tui/test/mini/runtime.queue.test.ts b/packages/tui/test/mini/runtime.queue.test.ts index 85186d0237a..111ef4feaad 100644 --- a/packages/tui/test/mini/runtime.queue.test.ts +++ b/packages/tui/test/mini/runtime.queue.test.ts @@ -61,7 +61,7 @@ describe("run runtime queue", () => { await task }) - test("treats /exit as a close command", async () => { + test.each(["/exit", "/quit", "/q", ":q"])("treats %s as a close command", async (text) => { const ui = createFooterApiFixture() let calls = 0 @@ -72,7 +72,7 @@ describe("run runtime queue", () => { }, }) - ui.submit("/exit") + ui.submit(text) await task expect(calls).toBe(0)