summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoseph Campuzano <[email protected]>2026-02-02 16:29:43 -0600
committerGitHub <[email protected]>2026-02-02 16:29:43 -0600
commitf9aa20913146d5b5f192cdf7db5e49f8694323b0 (patch)
treeddf1e957b1cbadaebad92e3ec78c755fa5c34410
parentf86f654cdab5d9824982aa49629a7bb7330e7abd (diff)
downloadopencode-f9aa20913146d5b5f192cdf7db5e49f8694323b0.tar.gz
opencode-f9aa20913146d5b5f192cdf7db5e49f8694323b0.zip
fix: fixes issue where the autocomplete tui dialog flickers while typing (#11641)
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx20
1 files changed, 15 insertions, 5 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 5f66dc822..30f6f1c79 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
@@ -129,6 +129,16 @@ export function Autocomplete(props: {
return props.input().getTextRange(store.index + 1, props.input().cursorOffset)
})
+ // filter() reads reactive props.value plus non-reactive cursor/text state.
+ // On keypress those can be briefly out of sync, so filter() may return an empty/partial string.
+ // Copy it into search in an effect because effects run after reactive updates have been rendered and painted
+ // so the input has settled and all consumers read the same stable value.
+ const [search, setSearch] = createSignal("")
+ createEffect(() => {
+ const next = filter()
+ setSearch(next ? next : "");
+ })
+
// When the filter changes due to how TUI works, the mousemove might still be triggered
// via a synthetic event as the layout moves underneath the cursor. This is a workaround to make sure the input mode remains keyboard so
// that the mouseover event doesn't trigger when filtering.
@@ -208,7 +218,7 @@ export function Autocomplete(props: {
}
const [files] = createResource(
- () => filter(),
+ () => search(),
async (query) => {
if (!store.visible || store.visible === "/") return []
@@ -378,9 +388,9 @@ export function Autocomplete(props: {
const mixed: AutocompleteOption[] =
store.visible === "@" ? [...agentsValue, ...(filesValue || []), ...mcpResources()] : [...commandsValue]
- const currentFilter = filter()
+ const searchValue = search()
- if (!currentFilter) {
+ if (!searchValue) {
return mixed
}
@@ -388,7 +398,7 @@ export function Autocomplete(props: {
return prev
}
- const result = fuzzysort.go(removeLineRange(currentFilter), mixed, {
+ const result = fuzzysort.go(removeLineRange(searchValue), mixed, {
keys: [
(obj) => removeLineRange((obj.value ?? obj.display).trimEnd()),
"description",
@@ -398,7 +408,7 @@ export function Autocomplete(props: {
scoreFn: (objResults) => {
const displayResult = objResults[0]
let score = objResults.score
- if (displayResult && displayResult.target.startsWith(store.visible + currentFilter)) {
+ if (displayResult && displayResult.target.startsWith(store.visible + searchValue)) {
score *= 2
}
const frecencyScore = objResults.obj.path ? frecency.getFrecency(objResults.obj.path) : 0