fix: match non-ASCII folder names in Open Project search#25662
fix: match non-ASCII folder names in Open Project search#25662ysm-dev wants to merge 3 commits intoanomalyco:devfrom
Conversation
- Normalize search query and fuzzysort comparison keys to NFC at the server search, client directory search, generic filtered list, and recent-project rows. Returned paths stay in original form. - Defer filter updates while the list search input is in IME composition; apply once on compositionend. Gate Arrow keys and Ctrl+N/Ctrl+P on isComposing.
| { key: "search" }, | ||
| ) | ||
| .map((x) => x.obj.target) as T[] | ||
| } |
There was a problem hiding this comment.
This still misses the keyed path. needle is NFC, but fuzzysort reads raw values from filterKeys, so an NFD Korean path returned by file search can get wrapped as title and filtered out again. dumb fix: normalize the keyed values too, or make callers use a normalized search key.
There was a problem hiding this comment.
Good catch — pushed f70eeb85d. The keyed branch now resolves nested paths (e.g. provider.name) and runs .normalize("NFC") on each key's string value before handing it to fuzzysort, so all callers get the fix without changes (file-mention popover, line-comment mention, Cmd+K palette, model dialogs).
Verified with bun typecheck across app / ui / opencode, plus a runtime check: NFD display value vs NFC 한 query now matches; dotted provider.name resolves correctly; multi-key [title, description, category] works; ASCII queries unchanged.
Written by Opus 4.7
Per Hona's review: the keyed branch only normalized the needle. Items
referenced by string filterKeys were still passed raw to fuzzysort, so
NFD paths returned by file search (e.g. macOS) never matched an NFC
needle typed via an IME. Resolve nested paths ("provider.name") and
normalize string field values to NFC inside the hook so all callers
benefit (file mention popover, line-comment mention, Cmd+K palette,
model dialogs).
| items.map((target) => ({ target, search: target.normalize("NFC") })), | ||
| { key: "search", limit: searchLimit }, | ||
| ) | ||
| .map((item) => item.obj.target) |
There was a problem hiding this comment.
This fixes the backend match, but TUI still drops it after this. find.files returns the original NFD path here, then prompt autocomplete runs a second raw fuzzysort over obj.value and gets 0 results for NFC Korean input. lazy fix: normalize that TUI comparison key too, keep returning the original path.
There was a problem hiding this comment.
Pushed b72aba644. The TUI fuzzysort entry points (prompt/autocomplete.tsx and the shared dialog-select.tsx) now NFC-normalize the needle and the per-key comparison values, so an NFC IME query matches an NFD path returned by find.files. The selected option still carries the original path/value (verified: matched.value and matched.path remain NFD), so file inserts and on-disk lookups are unaffected.
Skipped dialog-model.tsx and the provider.ts "did you mean" lookups since their data is ASCII identifiers — happy to fold those in if you'd rather have it uniform.
Verified with workspace-wide bun turbo typecheck (13/13) and a runtime harness mirroring the autocomplete keys shape: NFC needle vs NFD value returns the option; ASCII regression unaffected.
Written by Opus 4.7
Per Hona's review: even with the server file search normalized, the TUI prompt autocomplete and the shared dialog-select reran fuzzysort over raw item strings, dropping NFD paths returned by find.files for NFC needles typed via an IME. Convert string keys to function keys that return NFC values; the original path/value remains on the selected option so file inserts and on-disk lookups stay correct.
Issue for this PR
Closes #25661
Type of change
What does this PR do?
Open Project search in web/serve returned no results when typing a Korean folder name on macOS. Two causes:
fuzzysort's code-point compare never matched the same visible string.Fix:
fuzzysortcomparison keys to NFC at every entry point (packages/opencode/src/file/index.tsserver search,packages/app/src/components/dialog-select-directory.tsxclient async directory search,packages/ui/src/hooks/use-filtered-list.tsxgeneric filter, recent-project search rows). Returned filesystem paths stay in their original form so normalization-sensitive filesystems still resolve.applyFilterwhile the<List>search input is in IME composition; apply once oncompositionend. Gate Arrow keys and Ctrl+N/Ctrl+P onisComposingso co-resident IMEs don't move the list selection during composition.How did you verify your code works?
bun typecheckfrompackages/app,packages/ui,packages/opencode.bun turbo typecheck(push hook): all 13 typecheck tasks pass.mkdir ~/한국어테스트→ Open Project → type한→ folder now appears (was empty before).fuzzysort.go("한", ["<NFD-한>-test/"])returns[]; same query against the NFC-normalized comparison key returns the original NFD path.Screenshots / recordings
Before:
After:
Checklist