Skip to content

feat: fzf when adding widgets#293

Merged
sirmalloc merged 3 commits intosirmalloc:mainfrom
1Git2Clone:main
Apr 8, 2026
Merged

feat: fzf when adding widgets#293
sirmalloc merged 3 commits intosirmalloc:mainfrom
1Git2Clone:main

Conversation

@1Git2Clone
Copy link
Copy Markdown
Contributor

Summary

  • Widget picker now uses fuzzy (subsequence) matching as a fallback when the query has no exact substring match — characters must appear in order but need not be adjacent
  • Selection resets to the top-ranked result on every keystroke instead of staying pinned to the previously-selected widget type
  • Matched characters are highlighted in the picker list: yellow-bold for unselected rows, green-bold for the selected row

Changes

File Change
src/utils/widgets.ts Add computeFuzzyScore() with span/consecutive/boundary scoring; add fuzzy tiers to filterWidgetCatalog() fallback; export getMatchSegments() for highlight rendering
src/tui/components/items-editor/input-handlers.ts Reset selectedType to null on every typing/backspace keystroke so normalizePickerState always selects the best match
src/tui/components/ItemsEditor.tsx Import getMatchSegments and render highlighted segments in both the top-level search and widget-level picker lists
src/utils/__tests__/widgets.test.ts 10 new tests covering fuzzy ranking, no-match, exact-beats-fuzzy, and all getMatchSegments cases
src/tui/components/items-editor/__tests__/input-handlers.test.ts 2 new tests verifying selection resets to best match on typing in both category and widget search levels

Design decisions

  • Exact substring matches (scores 0–4) always outrank fuzzy matches — fuzzy tiers start at 1000/2000/3000 with enough headroom that no fuzzy quality score bleeds into the next tier
  • Fuzzy scoring uses a greedy leftmost subsequence find, penalised by span and start position, rewarded for consecutive runs (+5 each) and word boundary hits (+3 each, treating
    spaces, -, and _ as boundaries)
  • getMatchSegments mirrors the same exact-then-fuzzy priority so highlighted characters always agree with why an entry was ranked where it was

Test plan

  • All tests pass (bun test)
  • Type check and lint clean (bun run lint)
  • Manual test: searching gb in the picker surfaces Git Branch as the top fuzzy match with G and B highlighted
  • Manual test: searching vim with an existing Version widget selected jumps the cursor to Vim Mode, not Version

1Git2Clone and others added 3 commits April 7, 2026 00:10
Adds fuzzy matching to the widget picker as a fallback when the query
has no exact substring match anywhere.

Characters must appear in order but need not be adjacent; matches are
scored by span, consecutive runs, and word boundaries, and ranked below
all exact matches.

Selection now resets to the top-ranked result on every keystroke instead
of staying pinned to the previously-selected widget type.

Matched characters are highlighted in the picker list: yellow-bold for
unselected rows, green-bold for the selected row.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Give display-name initialism matches their own ranking path so abbreviation searches like tw, tc, ti, and to select the intended widget instead of description or type fallbacks. Keep picker highlighting aligned with the ranking logic and add regressions for the reported fuzzy-match cases.
@sirmalloc sirmalloc merged commit aed41dc into sirmalloc:main Apr 8, 2026
3 checks passed
@sirmalloc
Copy link
Copy Markdown
Owner

Thanks for this, will publish soon when the next release is ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants