summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorTimo Clasen <[email protected]>2025-11-04 20:28:03 +0100
committerGitHub <[email protected]>2025-11-04 13:28:03 -0600
commit8a9a474df625f578034f36b9bf7ea1315bfbde28 (patch)
treece45d99ae61a11222b87de3c8386ca160e201abc /packages
parent52e2b40610da07898339e8f23190fd3b18d09611 (diff)
downloadopencode-8a9a474df625f578034f36b9bf7ea1315bfbde28.tar.gz
opencode-8a9a474df625f578034f36b9bf7ea1315bfbde28.zip
feat(TUI): add autocomplete readline style keybinds (#3717)
Co-authored-by: Aiden Cline <[email protected]>
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx13
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx30
2 files changed, 34 insertions, 9 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx
index 66a312e5b..b9ba4a9ba 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx
@@ -22,15 +22,16 @@ export type CommandOption = DialogSelectOption & {
function init() {
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
+ const [suspendCount, setSuspendCount] = createSignal(0)
const dialog = useDialog()
const keybind = useKeybind()
const options = createMemo(() => {
return registrations().flatMap((x) => x())
})
+ const suspended = () => suspendCount() > 0
- let keybinds = true
useKeyboard((evt) => {
- if (!keybinds) return
+ if (suspended()) return
for (const option of options()) {
if (option.keybind && keybind.match(option.keybind, evt)) {
evt.preventDefault()
@@ -50,8 +51,9 @@ function init() {
}
},
keybinds(enabled: boolean) {
- keybinds = enabled
+ setSuspendCount((count) => count + (enabled ? -1 : 1))
},
+ suspended,
show() {
dialog.replace(() => <DialogCommand options={options()} />)
},
@@ -83,7 +85,10 @@ export function CommandProvider(props: ParentProps) {
const keybind = useKeybind()
useKeyboard((evt) => {
- if (keybind.match("command_list", evt) && dialog.stack.length === 0) {
+ if (value.suspended()) return
+ if (dialog.stack.length > 0) return
+ if (evt.defaultPrevented) return
+ if (keybind.match("command_list", evt)) {
evt.preventDefault()
dialog.replace(() => <DialogCommand options={value.options} />)
return
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
index 812496ddc..a4c1f33d9 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
@@ -393,11 +393,31 @@ export function Autocomplete(props: {
},
onKeyDown(e: KeyEvent) {
if (store.visible) {
- if (e.name === "up") move(-1)
- if (e.name === "down") move(1)
- if (e.name === "escape") hide()
- if (e.name === "return" || e.name === "tab") select()
- if (["up", "down", "return", "tab", "escape"].includes(e.name)) e.preventDefault()
+ const name = e.name?.toLowerCase()
+ const ctrlOnly = e.ctrl && !e.meta && !e.shift
+ const isNavUp = name === "up" || (ctrlOnly && name === "p")
+ const isNavDown = name === "down" || (ctrlOnly && name === "n")
+
+ if (isNavUp) {
+ move(-1)
+ e.preventDefault()
+ return
+ }
+ if (isNavDown) {
+ move(1)
+ e.preventDefault()
+ return
+ }
+ if (name === "escape") {
+ hide()
+ e.preventDefault()
+ return
+ }
+ if (name === "return" || name === "tab") {
+ select()
+ e.preventDefault()
+ return
+ }
}
if (!store.visible) {
if (e.name === "@") {