Skip to content

[AI] Replace payee and category autocomplete filter/sort with fzf fuzy search#7261

Open
RiiD wants to merge 7 commits intoactualbudget:masterfrom
RiiD:feat/fzf-autocomplete
Open

[AI] Replace payee and category autocomplete filter/sort with fzf fuzy search#7261
RiiD wants to merge 7 commits intoactualbudget:masterfrom
RiiD:feat/fzf-autocomplete

Conversation

@RiiD
Copy link

@RiiD RiiD commented Mar 22, 2026

Description

This PR replaces the custom substring-based filter and sort logic in PayeeAutocomplete and CategoryAutocomplete with the fzf fuzzy search library.

Why is this needed?

When adding transactions manually, finding the right payee or category quickly matters a lot. The existing autocomplete only matches substrings — so if you have many payees starting with "Home" (Home Depot, Home Hardware, Home Insurance...), you have to type almost the full name before the right one surfaces. There's no way to narrow down by word initials or non-contiguous characters.

With fzf, you can type hdep and immediately get Home Depot as the top result — the search understands word boundaries and ranks results by match quality. This is the kind of fuzzy search users are accustomed to from editors, terminals, and most modern search UIs.

This benefits anyone who adds transactions manually and has a large payee list, which is a common workflow for Actual users who prefer hands-on budgeting.

Changes:

  • Payee search: fuzzy matching with smart-case (lowercase = case-insensitive, uppercase = case-sensitive)
  • Category search: fuzzy matching across both group name and category name (e.g. food groc matches Food › Groceries)
  • Results ranked by match quality instead of a fixed substring priority

Before/After

Payee autocomplete - before Payee autocomplete - after Category autocomplete - before Category autocomplete - after

Related issue(s)

None

Testing

  1. Open the app and navigate to any account
  2. Add or edit a transaction and focus the Payee field
  3. Type a partial query and verify fuzzy results appear ranked by relevance (e.g. ms should surface MS Office before Mostafa Supermarket before Maxims Bakery)
  4. Type an uppercase query (e.g. MS) and verify only case-matching results appear
  5. Focus the Category field and type a group name prefix (e.g. food) — verify categories from that group appear
  6. Type food groc and verify Food › Groceries matches

Checklist

  • Release notes added (see link above)
  • No obvious regressions in affected areas
  • Self-review has been performed - I understand what each change in the code does and why it is needed

Bundle Stats

Bundle Files count Total bundle size % Changed
desktop-client 27 12.51 MB → 12.09 MB (-431.71 kB) -3.37%
loot-core 1 4.83 MB → 4.83 MB (+302 B) +0.01%
api 4 4.06 MB → 4.06 MB (+304 B) +0.01%
cli 1 7.88 MB 0%
View detailed bundle stats

desktop-client

Total

Files count Total bundle size % Changed
27 12.51 MB → 12.09 MB (-431.71 kB) -3.37%
Changeset
File Δ Size
node_modules/fzf/dist/fzf.es.js 🆕 +21.09 kB 0 B → 21.09 kB
node_modules/@tanstack/query-core/build/modern/environmentManager.js 🆕 +296 B 0 B → 296 B
node_modules/@babel/runtime/helpers/esm/inheritsLoose.js 📈 +23 B (+10.70%) 215 B → 238 B
node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js 📈 +23 B (+8.33%) 276 B → 299 B
node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js 📈 +23 B (+7.42%) 310 B → 333 B
node_modules/@babel/runtime/helpers/esm/extends.js 📈 +23 B (+6.22%) 370 B → 393 B
node_modules/react-i18next/dist/es/useTranslation.js 📈 +278 B (+5.13%) 5.3 kB → 5.57 kB
node_modules/@tanstack/query-core/build/modern/removable.js 📈 +21 B (+3.51%) 598 B → 619 B
node_modules/@tanstack/query-core/build/modern/query.js 📈 +236 B (+2.36%) 9.77 kB → 10 kB
node_modules/@tanstack/query-core/build/modern/onlineManager.js 📈 +20 B (+1.64%) 1.19 kB → 1.21 kB
node_modules/@tanstack/query-core/build/modern/focusManager.js 📈 +20 B (+1.55%) 1.26 kB → 1.28 kB
src/build-shims.js 📈 +4 B (+1.08%) 370 B → 374 B
node_modules/@tanstack/react-query/build/modern/useBaseQuery.js 📈 +21 B (+0.93%) 2.19 kB → 2.21 kB
node_modules/@tanstack/query-core/build/modern/retryer.js 📈 +21 B (+0.70%) 2.92 kB → 2.94 kB
package.json 📈 +18 B (+0.55%) 3.22 kB → 3.24 kB
node_modules/i18next/dist/esm/i18next.js 📈 +328 B (+0.44%) 72.61 kB → 72.93 kB
src/util/versions.ts 📈 +2 B (+0.22%) 904 B → 906 B
src/components/transactions/TransactionMenu.tsx 📈 +16 B (+0.19%) 8.29 kB → 8.3 kB
node_modules/@use-gesture/core/dist/use-gesture-core.esm.js 📈 +4 B (+0.05%) 7.45 kB → 7.45 kB
node_modules/@tanstack/query-core/build/modern/queryObserver.js 📉 -2 B (-0.01%) 13.71 kB → 13.71 kB
node_modules/downshift/dist/downshift.esm.mjs 📉 -36 B (-0.03%) 105.83 kB → 105.79 kB
src/components/autocomplete/CategoryAutocomplete.tsx 📉 -240 B (-1.53%) 15.36 kB → 15.13 kB
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger

Asset File Size % Changed
static/js/useTransactionBatchActions.js 4.29 MB → 4.31 MB (+21.59 kB) +0.49%
static/js/FormulaEditor.js 846.44 kB → 847.11 kB (+680 B) +0.08%
static/js/index.js 3.23 MB → 3.23 MB (+18 B) +0.00%

Smaller

Asset File Size % Changed
static/js/theme.js 484.67 kB → 30.68 kB (-453.99 kB) -93.67%

Unchanged

Asset File Size % Changed
static/js/BackgroundImage.js 119.98 kB 0%
static/js/ReportRouter.js 1021.25 kB 0%
static/js/TransactionList.js 81.29 kB 0%
static/js/ca.js 185.57 kB 0%
static/js/da.js 104.66 kB 0%
static/js/de.js 177.58 kB 0%
static/js/en-GB.js 7.16 kB 0%
static/js/en.js 170.68 kB 0%
static/js/es.js 172.13 kB 0%
static/js/fr.js 177.57 kB 0%
static/js/indexeddb-main-thread-worker-e59fee74.js 13.46 kB 0%
static/js/it.js 168.97 kB 0%
static/js/narrow.js 354.12 kB 0%
static/js/nb-NO.js 154.72 kB 0%
static/js/nl.js 111.58 kB 0%
static/js/pl.js 88.34 kB 0%
static/js/pt-BR.js 180.5 kB 0%
static/js/resize-observer.js 18.03 kB 0%
static/js/sv.js 80.58 kB 0%
static/js/th.js 179.94 kB 0%
static/js/uk.js 213.14 kB 0%
static/js/wide.js 418 B 0%
static/js/workbox-window.prod.es5.js 7.28 kB 0%

loot-core

Total

Files count Total bundle size % Changed
1 4.83 MB → 4.83 MB (+302 B) +0.01%
Changeset
File Δ Size
node_modules/i18next/dist/esm/i18next.js 📈 +302 B (+0.40%) 74.53 kB → 74.83 kB
View detailed bundle breakdown

Added

Asset File Size % Changed
kcab.worker.By_0WaVr.js 0 B → 4.83 MB (+4.83 MB) -

Removed

Asset File Size % Changed
kcab.worker.Dmj0rSrb.js 4.83 MB → 0 B (-4.83 MB) -100%

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged
No assets were unchanged


api

Total

Files count Total bundle size % Changed
4 4.06 MB → 4.06 MB (+304 B) +0.01%
Changeset
File Δ Size
node_modules/i18next/dist/esm/i18next.js 📈 +304 B (+0.41%) 72.62 kB → 72.92 kB
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger

Asset File Size % Changed
index.js 3.84 MB → 3.84 MB (+304 B) +0.01%

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
from-Bl-Hslp4.js 167.73 kB 0%
multipart-parser-BnDysoMr.js 8.1 kB 0%
src-iMkUmuwR.js 43.64 kB 0%

cli

Total

Files count Total bundle size % Changed
1 7.88 MB 0%
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
cli.js 7.88 MB 0%

@actual-github-bot actual-github-bot bot changed the title [AI] Replace payee and category autocomplete filter/sort with fzf fuzy search [WIP] [AI] Replace payee and category autocomplete filter/sort with fzf fuzy search Mar 22, 2026
@netlify
Copy link

netlify bot commented Mar 22, 2026

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit b64180a
🔍 Latest deploy log https://app.netlify.com/projects/actualbudget/deploys/69c1423d2f374f0008bda6e9
😎 Deploy Preview https://deploy-preview-7261.demo.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Contributor

👋 Hello contributor!

We would love to review your PR! Before we can do that, please make sure:

  • ✅ All CI checks pass
  • ✅ The PR is moved from draft to open (if applicable)
  • ✅ The "[WIP]" prefix is removed from the PR title
  • ✅ All CodeRabbit code review comments are resolved (if you disagree with anything - reply to the bot with your reasoning so we can read through it). The bot will eventually approve the PR.

We do this to reduce the TOIL the core contributor team has to go through for each PR and to allow for speedy reviews and merges.

For more information, please see our Contributing Guide.

@netlify
Copy link

netlify bot commented Mar 22, 2026

Deploy Preview for actualbudget-storybook ready!

Name Link
🔨 Latest commit c2d47b6
🔍 Latest deploy log https://app.netlify.com/projects/actualbudget-storybook/deploys/69c041b0a59f200008248029
😎 Deploy Preview https://deploy-preview-7261--actualbudget-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Mar 22, 2026

Deploy Preview for actualbudget-website ready!

Name Link
🔨 Latest commit 6a2bb06
🔍 Latest deploy log https://app.netlify.com/projects/actualbudget-website/deploys/69c04254e27e530008e4d23b
😎 Deploy Preview https://deploy-preview-7261.www.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@RiiD RiiD force-pushed the feat/fzf-autocomplete branch from a94b595 to c2d47b6 Compare March 22, 2026 19:23
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 22, 2026

📝 Walkthrough

Walkthrough

Adds fzf as a runtime dependency and replaces custom normalization/filter/sort logic in two autocomplete components with Fzf-based fuzzy matching; adds a release note entry describing the change.

Changes

Cohort / File(s) Summary
Dependency Addition
packages/desktop-client/package.json
Added fzf (^0.5.2) as a production/runtime dependency.
Category Autocomplete
packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
Replaced normalization + substring filtering and a custom sort with Fzf.find fuzzy matching. Preserves and prepends the split suggestion; removed getNormalisedString, defaultFilterSuggestion, and customSort usage.
Payee Autocomplete
packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx
Switched filtering/sorting to Fzf fuzzy matching (uses selector combining group and name), limits results to 100 when applicable, extracts/conditionally re-prepends the 'new' sentinel, and removes it when an exact normalized match is found.
Release Notes
upcoming-release-notes/7261.md
Added a release notes entry documenting the refactor to use fzf in PayeeAutocomplete and CategoryAutocomplete.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through names and split the new,

Fzf found crumbs where once I blew,
"New" I tuck and splits I keep,
Fuzzy hops now leap so neat,
— cheers from a busy little rabbit 🥕

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly explains the changes: replacing substring-based filter/sort with fzf fuzzy search in PayeeAutocomplete and CategoryAutocomplete components, providing context on why this is needed, detailing specific improvements, and including testing steps.
Title check ✅ Passed The title accurately describes the main change: replacing autocomplete filter/sort logic with fzf fuzzy search in payee and category components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx (1)

499-533: ⚠️ Potential issue | 🟠 Major

Apply the same matcher to nearby payees.

This only runs Fzf over realSuggestions; nearby payees are still filtered on a separate path and then prepended/highlighted ahead of these results. That leaves one dropdown using two different matching rules, so the default selected row can come from nearby-payee matching instead of the new fuzzy payee search. Nearby payees should go through the same matcher/casing rules before the list is merged.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx`
around lines 499 - 533, filterSuggestions currently only runs Fzf on
realSuggestions, leaving nearby-payee items filtered by a different path and
merged afterwards; change it so nearby payees are passed through the same
matcher/casing rules before merging: locate filterSuggestions and instead of
directly prepending nearby items, run the same Fzf matcher (using selector: item
=> item.name ?? '' and limit: 100) against the nearby-payees list (or construct
a single combined input to Fzf that includes nearby items flagged) so both sets
are matched equivalently, map Fzf results to items, deduplicate by id, reapply
the existing 'new' sentinel logic (showNew, exact-match normalization with
getNormalisedString) and then return the merged, de-duplicated ordered results
so nearby payees cannot bypass the fuzzy matching used for realSuggestions.
packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx (1)

258-279: ⚠️ Potential issue | 🟠 Major

Don't feed a globally ranked result list into the grouped renderer unchanged.

fzf.find(value) now returns a single relevance-ordered list, but CategoryList later buckets that list back by group.id while keeping the original indexes. Once matches interleave groups, lower-ranked rows from the first group render above higher-ranked rows from later groups, and arrow-key highlighting jumps because the visual order no longer matches the suggestion order. Preserve the ranked order while searching, or render group headers inline on first occurrence instead of regrouping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx`
around lines 258 - 279, filterSuggestions currently returns a single
relevance-ordered list (from Fzf.find) which is later re-bucketed by
CategoryList causing visual/order inconsistencies; fix by preserving ranked
order and emitting group header sentinel items so group headers render inline
instead of regrouping: update filterSuggestions to iterate the ranked filtered
results (result.item), track first-seen group ids, and when encountering a new
group insert a synthetic header item (e.g., id like `group-header:<group.id>` or
a flag on the item) before that group's first real item (also keep splitItem
handling), so CategoryList can render headers inline without reordering the
ranked suggestions.
🧹 Nitpick comments (1)
packages/desktop-client/package.json (1)

21-23: Keep fzf in devDependencies for desktop-client.

packages/desktop-client intentionally uses a dev-only dependency manifest, so introducing a separate dependencies block makes this package inconsistent with the rest of the app packaging setup.

♻️ Suggested manifest change
-  "dependencies": {
-    "fzf": "^0.5.2"
-  },
   "devDependencies": {
@@
     "downshift": "9.0.10",
+    "fzf": "^0.5.2",
     "hyperformula": "^3.1.1",
Based on learnings, `packages/desktop-client/package.json` intentionally keeps runtime packages under `devDependencies` only for this bundled app.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/desktop-client/package.json` around lines 21 - 23, The package.json
for packages/desktop-client incorrectly adds "fzf" under "dependencies"; move
"fzf": "^0.5.2" from the top-level "dependencies" block into the existing
"devDependencies" section so the desktop-client stays a dev-only manifest
(ensure you remove the now-empty "dependencies" block if nothing else lives
there and keep the version string intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx`:
- Around line 258-279: filterSuggestions currently returns a single
relevance-ordered list (from Fzf.find) which is later re-bucketed by
CategoryList causing visual/order inconsistencies; fix by preserving ranked
order and emitting group header sentinel items so group headers render inline
instead of regrouping: update filterSuggestions to iterate the ranked filtered
results (result.item), track first-seen group ids, and when encountering a new
group insert a synthetic header item (e.g., id like `group-header:<group.id>` or
a flag on the item) before that group's first real item (also keep splitItem
handling), so CategoryList can render headers inline without reordering the
ranked suggestions.

In `@packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx`:
- Around line 499-533: filterSuggestions currently only runs Fzf on
realSuggestions, leaving nearby-payee items filtered by a different path and
merged afterwards; change it so nearby payees are passed through the same
matcher/casing rules before merging: locate filterSuggestions and instead of
directly prepending nearby items, run the same Fzf matcher (using selector: item
=> item.name ?? '' and limit: 100) against the nearby-payees list (or construct
a single combined input to Fzf that includes nearby items flagged) so both sets
are matched equivalently, map Fzf results to items, deduplicate by id, reapply
the existing 'new' sentinel logic (showNew, exact-match normalization with
getNormalisedString) and then return the merged, de-duplicated ordered results
so nearby payees cannot bypass the fuzzy matching used for realSuggestions.

---

Nitpick comments:
In `@packages/desktop-client/package.json`:
- Around line 21-23: The package.json for packages/desktop-client incorrectly
adds "fzf" under "dependencies"; move "fzf": "^0.5.2" from the top-level
"dependencies" block into the existing "devDependencies" section so the
desktop-client stays a dev-only manifest (ensure you remove the now-empty
"dependencies" block if nothing else lives there and keep the version string
intact).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46471b83-eb0a-47a6-a3f4-9c52e6bc3ce7

📥 Commits

Reviewing files that changed from the base of the PR and between b6fbc7d and a94b595.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • packages/desktop-client/package.json
  • packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
  • packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx (1)

268-272: Consider adding a result limit for consistency with PayeeAutocomplete.

PayeeAutocomplete uses limit: 100 in its fzf options, but CategoryAutocomplete doesn't set a limit. While category lists are typically smaller than payee lists, adding a consistent limit would align the implementations and prevent potential performance issues if category lists grow.

♻️ Suggested fix
       const fzf = new Fzf(realSuggestions, {
         selector: item =>
           item.group ? item.group.name + ' ' + item.name : item.name,
+        limit: 100,
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx`
around lines 268 - 272, In CategoryAutocomplete, when constructing the Fzf
instance (the variable fzf built from realSuggestions with selector), add a
limit option (e.g., limit: 100) to match PayeeAutocomplete and cap results;
update the Fzf instantiation in CategoryAutocomplete to include limit: 100 so
filtered (the result of fzf.find(value).map(...)) is bounded and consistent with
PayeeAutocomplete behavior.
packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx (1)

455-463: Inconsistent filtering: nearby payees use substring matching while regular payees use fuzzy search.

filteredNearbyPayees still uses defaultFilterSuggestion (substring matching), whereas the main payee list now uses fzf fuzzy matching. This inconsistency means a query like hdep would match "Home Depot" in regular payees but not in nearby payees.

Consider applying fzf filtering to nearby payees as well for consistent UX.

♻️ Suggested fix
 const filteredNearbyPayees = useMemo(() => {
   if (!nearbyPayeesWithType.length || !rawPayee) {
     return nearbyPayeesWithType;
   }

-  return nearbyPayeesWithType.filter(payee => {
-    return defaultFilterSuggestion(payee, rawPayee);
-  });
+  const fzf = new Fzf(nearbyPayeesWithType, {
+    selector: item => item.name ?? '',
+  });
+  return fzf.find(rawPayee).map(result => result.item);
 }, [nearbyPayeesWithType, rawPayee]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx`
around lines 455 - 463, filteredNearbyPayees uses substring matching via
defaultFilterSuggestion while the main payee list uses the fzf fuzzy matcher,
causing inconsistent results; update the filteredNearbyPayees memo to use the
same fuzzy filtering function used by the main payee list (the fzf-based
matcher) instead of defaultFilterSuggestion, preserving any existing
tie-break/order logic and ensuring rawPayee is passed the same way; reference
the symbols filteredNearbyPayees, nearbyPayeesWithType, rawPayee and replace
defaultFilterSuggestion with the project’s fzf filter function (the same one
used for regular payees) and import it if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx`:
- Around line 268-272: In CategoryAutocomplete, when constructing the Fzf
instance (the variable fzf built from realSuggestions with selector), add a
limit option (e.g., limit: 100) to match PayeeAutocomplete and cap results;
update the Fzf instantiation in CategoryAutocomplete to include limit: 100 so
filtered (the result of fzf.find(value).map(...)) is bounded and consistent with
PayeeAutocomplete behavior.

In `@packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx`:
- Around line 455-463: filteredNearbyPayees uses substring matching via
defaultFilterSuggestion while the main payee list uses the fzf fuzzy matcher,
causing inconsistent results; update the filteredNearbyPayees memo to use the
same fuzzy filtering function used by the main payee list (the fzf-based
matcher) instead of defaultFilterSuggestion, preserving any existing
tie-break/order logic and ensuring rawPayee is passed the same way; reference
the symbols filteredNearbyPayees, nearbyPayeesWithType, rawPayee and replace
defaultFilterSuggestion with the project’s fzf filter function (the same one
used for regular payees) and import it if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8725ed81-f60d-4f83-9462-e3a7012e8305

📥 Commits

Reviewing files that changed from the base of the PR and between a94b595 and 6a2bb06.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • packages/desktop-client/package.json
  • packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
  • packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx
  • upcoming-release-notes/7261.md
✅ Files skipped from review due to trivial changes (2)
  • packages/desktop-client/package.json
  • upcoming-release-notes/7261.md

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 22, 2026
@github-actions
Copy link
Contributor

VRT tests ❌ failed. View the test report.

To update the VRT screenshots, comment /update-vrt on this PR. The VRT update operation takes about 50 minutes.

@RiiD
Copy link
Author

RiiD commented Mar 22, 2026

/update-vrt

Auto-generated by VRT workflow

PR: actualbudget#7261
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx (1)

497-532: Fzf integration looks correct; minor redundancy in condition.

The fuzzy search implementation properly separates the 'new' sentinel, applies Fzf filtering with appropriate limits, and handles exact-match removal logic correctly.

One minor observation: at line 517, value && value !== '' is redundant since a truthy check on a string already excludes empty strings.

🔧 Optional: Simplify redundant condition
-    const showNew = newItem && value && value !== '' && !focusTransferPayees;
+    const showNew = newItem && value && !focusTransferPayees;

As per coding guidelines: "the React Compiler automatically memoizes component bodies; omit manual useCallback, useMemo, and React.memo when adding or refactoring code" - so the inline function definition without useCallback is appropriate for this codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx`
around lines 497 - 532, The condition that decides whether to show the 'new'
sentinel is redundant: in the expression that sets showNew inside
filterSuggestions, replace the redundant check "value && value !== ''" with a
single truthy check "value" (refer to the showNew assignment that uses newItem,
value, and focusTransferPayees) so the logic remains the same but without the
needless duplicate empty-string comparison.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx`:
- Around line 497-532: The condition that decides whether to show the 'new'
sentinel is redundant: in the expression that sets showNew inside
filterSuggestions, replace the redundant check "value && value !== ''" with a
single truthy check "value" (refer to the showNew assignment that uses newItem,
value, and focusTransferPayees) so the logic remains the same but without the
needless duplicate empty-string comparison.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 03c68389-2581-48a1-827f-8d5a12f77c68

📥 Commits

Reviewing files that changed from the base of the PR and between 6a2bb06 and c2ab761.

📒 Files selected for processing (3)
  • packages/desktop-client/package.json
  • packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
  • packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/desktop-client/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx

@RiiD RiiD changed the title [WIP] [AI] Replace payee and category autocomplete filter/sort with fzf fuzy search [AI] Replace payee and category autocomplete filter/sort with fzf fuzy search Mar 22, 2026
@StephenBrown2
Copy link
Contributor

This looks great. I hope it gets approved and merged!

@matt-fidd matt-fidd self-assigned this Mar 23, 2026
@matt-fidd
Copy link
Member

Hey! Thanks for this, it's a good idea.

Looking at fzf, it looks pretty unmaintained, with none of its dependencies updated since 2023. Could you have a look to see if there are any alternatives that are more actively maintained? https://github.com/ajitid/fzf-for-js

@matt-fidd
Copy link
Member

There has also just been a change merged to improve searching, have a look at #6972 and see what you think to that solution. It has also introduced some conflicts to your branch.

@StephenBrown2
Copy link
Contributor

If a library is stable, does it need to constantly update?

https://www.npmjs.com/package/react-fzf exists and was published 5 months ago, but just adds a wrapper around the fzf package for react.

A different alternative may be https://www.npmjs.com/package/flashfuzzy, which uses all the cool new things, like Rust and WASM.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants