fix: fixes issue where the autocomplete tui dialog flickers while typing (#11641)

This commit is contained in:
Joseph Campuzano
2026-02-02 16:29:43 -06:00
committed by GitHub
parent f86f654cda
commit f9aa209131

View File

@@ -129,6 +129,16 @@ export function Autocomplete(props: {
return props.input().getTextRange(store.index + 1, props.input().cursorOffset) 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 // 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 // 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. // that the mouseover event doesn't trigger when filtering.
@@ -208,7 +218,7 @@ export function Autocomplete(props: {
} }
const [files] = createResource( const [files] = createResource(
() => filter(), () => search(),
async (query) => { async (query) => {
if (!store.visible || store.visible === "/") return [] if (!store.visible || store.visible === "/") return []
@@ -378,9 +388,9 @@ export function Autocomplete(props: {
const mixed: AutocompleteOption[] = const mixed: AutocompleteOption[] =
store.visible === "@" ? [...agentsValue, ...(filesValue || []), ...mcpResources()] : [...commandsValue] store.visible === "@" ? [...agentsValue, ...(filesValue || []), ...mcpResources()] : [...commandsValue]
const currentFilter = filter() const searchValue = search()
if (!currentFilter) { if (!searchValue) {
return mixed return mixed
} }
@@ -388,7 +398,7 @@ export function Autocomplete(props: {
return prev return prev
} }
const result = fuzzysort.go(removeLineRange(currentFilter), mixed, { const result = fuzzysort.go(removeLineRange(searchValue), mixed, {
keys: [ keys: [
(obj) => removeLineRange((obj.value ?? obj.display).trimEnd()), (obj) => removeLineRange((obj.value ?? obj.display).trimEnd()),
"description", "description",
@@ -398,7 +408,7 @@ export function Autocomplete(props: {
scoreFn: (objResults) => { scoreFn: (objResults) => {
const displayResult = objResults[0] const displayResult = objResults[0]
let score = objResults.score let score = objResults.score
if (displayResult && displayResult.target.startsWith(store.visible + currentFilter)) { if (displayResult && displayResult.target.startsWith(store.visible + searchValue)) {
score *= 2 score *= 2
} }
const frecencyScore = objResults.obj.path ? frecency.getFrecency(objResults.obj.path) : 0 const frecencyScore = objResults.obj.path ? frecency.getFrecency(objResults.obj.path) : 0