Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 8a9a474

Browse files
feat(TUI): add autocomplete readline style keybinds (anomalyco#3717)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
1 parent 52e2b40 commit 8a9a474

2 files changed

Lines changed: 34 additions & 9 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ export type CommandOption = DialogSelectOption & {
2222

2323
function init() {
2424
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
25+
const [suspendCount, setSuspendCount] = createSignal(0)
2526
const dialog = useDialog()
2627
const keybind = useKeybind()
2728
const options = createMemo(() => {
2829
return registrations().flatMap((x) => x())
2930
})
31+
const suspended = () => suspendCount() > 0
3032

31-
let keybinds = true
3233
useKeyboard((evt) => {
33-
if (!keybinds) return
34+
if (suspended()) return
3435
for (const option of options()) {
3536
if (option.keybind && keybind.match(option.keybind, evt)) {
3637
evt.preventDefault()
@@ -50,8 +51,9 @@ function init() {
5051
}
5152
},
5253
keybinds(enabled: boolean) {
53-
keybinds = enabled
54+
setSuspendCount((count) => count + (enabled ? -1 : 1))
5455
},
56+
suspended,
5557
show() {
5658
dialog.replace(() => <DialogCommand options={options()} />)
5759
},
@@ -83,7 +85,10 @@ export function CommandProvider(props: ParentProps) {
8385
const keybind = useKeybind()
8486

8587
useKeyboard((evt) => {
86-
if (keybind.match("command_list", evt) && dialog.stack.length === 0) {
88+
if (value.suspended()) return
89+
if (dialog.stack.length > 0) return
90+
if (evt.defaultPrevented) return
91+
if (keybind.match("command_list", evt)) {
8792
evt.preventDefault()
8893
dialog.replace(() => <DialogCommand options={value.options} />)
8994
return

packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,31 @@ export function Autocomplete(props: {
393393
},
394394
onKeyDown(e: KeyEvent) {
395395
if (store.visible) {
396-
if (e.name === "up") move(-1)
397-
if (e.name === "down") move(1)
398-
if (e.name === "escape") hide()
399-
if (e.name === "return" || e.name === "tab") select()
400-
if (["up", "down", "return", "tab", "escape"].includes(e.name)) e.preventDefault()
396+
const name = e.name?.toLowerCase()
397+
const ctrlOnly = e.ctrl && !e.meta && !e.shift
398+
const isNavUp = name === "up" || (ctrlOnly && name === "p")
399+
const isNavDown = name === "down" || (ctrlOnly && name === "n")
400+
401+
if (isNavUp) {
402+
move(-1)
403+
e.preventDefault()
404+
return
405+
}
406+
if (isNavDown) {
407+
move(1)
408+
e.preventDefault()
409+
return
410+
}
411+
if (name === "escape") {
412+
hide()
413+
e.preventDefault()
414+
return
415+
}
416+
if (name === "return" || name === "tab") {
417+
select()
418+
e.preventDefault()
419+
return
420+
}
401421
}
402422
if (!store.visible) {
403423
if (e.name === "@") {

0 commit comments

Comments
 (0)