summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSherlock Holmes <[email protected]>2025-12-19 14:40:16 +0800
committerGitHub <[email protected]>2025-12-19 00:40:16 -0600
commit6a802c01cd80741544a97dee5aad99d82642cb18 (patch)
tree590b3e2428754412ed0f3ae98fcf5ce406b895b4
parent14146428dd3eeec8fb827b08a6ac209c71698872 (diff)
downloadopencode-6a802c01cd80741544a97dee5aad99d82642cb18.tar.gz
opencode-6a802c01cd80741544a97dee5aad99d82642cb18.zip
feat(tui): implement smooth scrolling for autocomplete dropdown navigation (#5559)
Co-authored-by: Aiden Cline <[email protected]> Co-authored-by: Aiden Cline <[email protected]>
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx27
1 files changed, 23 insertions, 4 deletions
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 bf656e890..b2221a3b6 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
@@ -1,4 +1,4 @@
-import type { BoxRenderable, TextareaRenderable, KeyEvent } from "@opentui/core"
+import type { BoxRenderable, TextareaRenderable, KeyEvent, ScrollBoxRenderable } from "@opentui/core"
import fuzzysort from "fuzzysort"
import { firstBy } from "remeda"
import { createMemo, createResource, createEffect, onMount, onCleanup, For, Show, createSignal } from "solid-js"
@@ -369,7 +369,7 @@ export function Autocomplete(props: {
store.visible === "@" ? [...agents(), ...(files() || [])] : [...commands()]
).filter((x) => x.disabled !== true)
const currentFilter = filter()
- if (!currentFilter) return mixed.slice(0, 10)
+ if (!currentFilter) return mixed
const result = fuzzysort.go(currentFilter, mixed, {
keys: [(obj) => obj.display.trimEnd(), "description", (obj) => obj.aliases?.join(" ") ?? ""],
limit: 10,
@@ -395,7 +395,19 @@ export function Autocomplete(props: {
let next = store.selected + direction
if (next < 0) next = options().length - 1
if (next >= options().length) next = 0
+ moveTo(next)
+ }
+
+ function moveTo(next: number) {
setStore("selected", next)
+ if (!scroll) return
+ const viewportHeight = Math.min(height(), options().length)
+ const scrollBottom = scroll.scrollTop + viewportHeight
+ if (next < scroll.scrollTop) {
+ scroll.scrollBy(next - scroll.scrollTop)
+ } else if (next + 1 > scrollBottom) {
+ scroll.scrollBy(next + 1 - scrollBottom)
+ }
}
function select() {
@@ -497,6 +509,8 @@ export function Autocomplete(props: {
return 1
})
+ let scroll: ScrollBoxRenderable
+
return (
<box
visible={store.visible !== false}
@@ -508,7 +522,12 @@ export function Autocomplete(props: {
{...SplitBorder}
borderColor={theme.border}
>
- <box backgroundColor={theme.backgroundMenu} height={height()}>
+ <scrollbox
+ ref={(r: ScrollBoxRenderable) => (scroll = r)}
+ backgroundColor={theme.backgroundMenu}
+ height={height()}
+ scrollbarOptions={{ visible: false }}
+ >
<For
each={options()}
fallback={
@@ -535,7 +554,7 @@ export function Autocomplete(props: {
</box>
)}
</For>
- </box>
+ </scrollbox>
</box>
)
}