Skip to content

Commit c806d05

Browse files
committed
Preserve typed text when Enter hits zero slash matches
1 parent 7e224e9 commit c806d05

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

src/tui/shell.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5313,8 +5313,12 @@ export function handleSlashPopupKey(shell: AppShell, key: KeyEvent): boolean {
53135313
!key.option
53145314
) {
53155315
closeSlashPopup(shell)
5316+
// Zero matches: nothing to dispatch. Preserve the typed text instead of
5317+
// wiping it — pre-refresh this state was unreachable (popup closed on
5318+
// zero matches, so Enter fell through to normal prompt handling).
5319+
if (!active) return true
53165320
setPromptText(shell, "")
5317-
if (active) dispatchPaletteSelection(shell, active)
5321+
dispatchPaletteSelection(shell, active)
53185322
return true
53195323
}
53205324

src/tui/slash-popup-gate.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,49 @@ describe("/ popup keeps a queued gate queued across a filter refresh", () => {
150150
}
151151
})
152152
})
153+
154+
test("Enter on zero matches closes the popup, keeps the typed text, and drains a queued gate", async () => {
155+
await withShell(async ({ shell, press }) => {
156+
const emitter = new EventEmitter()
157+
const dispose = wireGates(emitter, shell)
158+
const disposeClosedSpy = onOverlayClosed(shell, () => {})
159+
try {
160+
press("/")
161+
press("m")
162+
press("o")
163+
press("z")
164+
expect(shell.prompt.value).toBe("/moz")
165+
expect(shell.paletteCommands).toEqual([])
166+
expect(isSlashPopupOpen(shell)).toBe(true)
167+
168+
let resolved: unknown
169+
emitter.emit("permission.gate", {
170+
request: {
171+
tool: "run_shell",
172+
action: "Run shell command",
173+
subject: "bun test",
174+
scopes: [],
175+
},
176+
resolve: (outcome: unknown) => {
177+
resolved = outcome
178+
},
179+
})
180+
expect(shell.overlayKind).toBe("palette")
181+
expect(resolved).toBeUndefined()
182+
183+
// Enter with no active command must not wipe the typed text.
184+
press("Enter")
185+
expect(isSlashPopupOpen(shell)).toBe(false)
186+
expect(shell.prompt.value).toBe("/moz")
187+
188+
// Popup close is a genuine dismiss: the queued gate drains onto it.
189+
await Bun.sleep(60)
190+
expect(shell.overlayKind).toBe("permissions")
191+
expect(resolved).toBeUndefined()
192+
} finally {
193+
disposeClosedSpy()
194+
dispose()
195+
}
196+
})
197+
})
153198
})

0 commit comments

Comments
 (0)