feat(desktop): fast file search with VS Code fuzzy scorer#3136
Conversation
…e search Replace Fuse.js with VS Code's path-aware fuzzy scorer (scoreItemFuzzy + compareItemsByFuzzyScore), remove 30s TTL index rebuilds, pre-warm search index on workspace open, and encapsulate CommandPalette as a self-contained component with v1/v2 search variants. - Port VS Code's fuzzyScorer.ts (MIT) with full item scoring pipeline - Score filename (label) separately from path with label prefix boost - Remove fuse.js dependency, SearchDialog, KeywordSearch, ScopeToggle - Encapsulate CommandPalette: owns query state, dialog UI, search hook - Add v2 search variant using workspaceTrpc (direct host service) - Pre-warm search index in WorkspaceFilesystemManager - Remove debounce from useFileSearch (no longer needed)
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughMoved command-palette state from a shared hook into the component, replaced Fuse.js scoring with a VS Code–style fuzzy scorer, consolidated search/dialog UI into a single CommandPalette implementation, removed several legacy search components/hooks, added useV2FileSearch, and pre-warmed workspace search indexes in the host service. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant CP as CommandPalette (Renderer)
participant Hook as useV2FileSearch
participant TRPC as workspaceTrpc
participant Host as HostService
participant FS as Workspace FS Index
participant Tabs as useTabsStore
rect rgba(135,206,235,0.5)
User->>CP: Open palette / type query
CP->>Hook: call with workspaceId + query
Hook->>TRPC: filesystem.searchFiles(query, limit)
TRPC->>Host: IPC request for search
Host->>FS: ensure index (getSearchIndex pre-warmed)
Host->>FS: perform search on index
FS-->>Host: results
Host-->>TRPC: results
TRPC-->>Hook: results
Hook-->>CP: normalized results
end
rect rgba(144,238,144,0.5)
User->>CP: Select file
CP->>Tabs: addFileViewerPane(workspaceId, filePath)
Tabs-->>CP: pane added
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
Greptile SummaryThis PR replaces Fuse.js with a port of VS Code's
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant WM as WorkspaceFilesystemManager
participant SI as search.ts (getSearchIndex)
participant FW as FileWatcher (patchSearchIndexesForRoot)
participant CP as CommandPalette
WM->>SI: getSearchIndex() [pre-warm]
Note over SI: buildSearchIndex starts (async)
SI-->>WM: promise stored in searchIndexBuilds
FW->>SI: patchSearchIndexesForRoot(events)
Note over SI: cached=undefined → delete in-flight ref, continue
Note over SI: buildSearchIndex resolves ⚠️ stale
SI->>SI: searchIndexCache.set(cacheKey, staleItems)
Note over SI: file-watcher events lost in cache
CP->>SI: searchFiles(query)
SI-->>CP: results from stale index
|
| const buildPromise = buildSearchIndex(options) | ||
| .then((index) => { | ||
| if (getSearchIndexVersion(cacheKey) === buildVersion) { | ||
| searchIndexCache.set(cacheKey, { index, builtAt: Date.now() }); | ||
| } | ||
| .then((items) => { | ||
| searchIndexCache.set(cacheKey, items); | ||
| searchIndexBuilds.delete(cacheKey); | ||
| return index; | ||
| return items; |
There was a problem hiding this comment.
Race condition: stale index overwrites cache when watcher fires during initial build
When patchSearchIndexesForRoot is called while the initial index build is in-flight:
cachedisundefined, sosearchIndexBuilds.delete(cacheKey)is called and the loopcontinues — correctly skipping the patch.- However, the background
buildSearchIndexpromise is still running. When it resolves, it unconditionally callssearchIndexCache.set(cacheKey, items)with the pre-event snapshot. - The watcher event's changes (added/removed files) are permanently lost in the cache until the next watcher event.
The old code protected against this with a version counter (buildVersion / getSearchIndexVersion check in the .then() callback) that prevented a stale build from overwriting the cache. That protection was removed.
A minimal fix is to track a "generation" counter per cache key in the new code and guard the .then() write:
// Increment generation before cancelling the in-flight build
searchIndexVersions.set(cacheKey, (searchIndexVersions.get(cacheKey) ?? 0) + 1);
searchIndexBuilds.delete(cacheKey);Then in buildSearchIndex.then():
.then((items) => {
if (currentGeneration === searchIndexVersions.get(cacheKey)) {
searchIndexCache.set(cacheKey, items);
}
searchIndexBuilds.delete(cacheKey);
return items;
})Use cmdk primitives for keyboard navigation and item selection while keeping our custom fixed positioning, 672px width, and shouldFilter=false to skip cmdk's internal scoring. Add enter key indicator on selected item. Re-export CommandPrimitive from @superset/ui/command.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/workspace-fs/src/search.ts (1)
287-296:⚠️ Potential issue | 🟠 MajorGuard cache writes from stale in-flight builds.
invalidateSearchIndex(),invalidateAllSearchIndexes(), andpatchSearchIndexesForRoot()only drop the promise reference; they do not cancel the running scan. Thisthenstill writes the stale snapshot intosearchIndexCache, so filesystem events that land during an initial build can be lost until the next invalidation.🛠️ Proposed fix
- const buildPromise = buildSearchIndex(options) + let buildPromise!: Promise<SearchIndexEntry[]>; + buildPromise = buildSearchIndex(options) .then((items) => { - searchIndexCache.set(cacheKey, items); - searchIndexBuilds.delete(cacheKey); + if (searchIndexBuilds.get(cacheKey) === buildPromise) { + searchIndexCache.set(cacheKey, items); + searchIndexBuilds.delete(cacheKey); + } return items; }) .catch((error) => { - searchIndexBuilds.delete(cacheKey); + if (searchIndexBuilds.get(cacheKey) === buildPromise) { + searchIndexBuilds.delete(cacheKey); + } throw error; });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/workspace-fs/src/search.ts` around lines 287 - 296, The build promise may become stale after invalidation because invalidateSearchIndex/invalidateAllSearchIndexes/patchSearchIndexesForRoot only remove the promise reference; so update the resolution and rejection handlers for the promise created in buildSearchIndex to first verify that the in-flight promise is still the current one in searchIndexBuilds for the given cacheKey (e.g., compare searchIndexBuilds.get(cacheKey) === buildPromise) before writing to searchIndexCache or deleting the entry; if it is not the same promise, do not write the stale items to searchIndexCache and only delete the map entry if it still points to this buildPromise. Ensure the same guard is applied in both the .then and .catch branches around searchIndexCache.set and searchIndexBuilds.delete to prevent stale writes and premature deletes.
♻️ Duplicate comments (2)
packages/workspace-fs/src/fuzzy-scorer.ts (2)
348-349:⚠️ Potential issue | 🟠 MajorMake the scorer cache key unambiguous.
Concatenating
labelanddescriptionwithout separators lets different items alias to the same cache entry ("ab"+"c"vs"a"+"bc"). When that happens, one file'sIItemScoregets reused for another and ranking/highlighting becomes wrong.🛠️ Proposed fix
- const cacheHash = `${label}${description ?? ""}${allowNonContiguousMatches}${query.normalized}`; + const cacheHash = [ + label, + description ?? "", + allowNonContiguousMatches ? "1" : "0", + query.normalized, + ].join("\0");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/workspace-fs/src/fuzzy-scorer.ts` around lines 348 - 349, The cache key construction for cacheHash is ambiguous because it concatenates label and description without separators, causing collisions and incorrect reuse of IItemScore entries; fix this by building an unambiguous key (e.g., serialize a tuple/object or insert explicit separators) that includes label, description, allowNonContiguousMatches and query.normalized (references: cacheHash, label, description, allowNonContiguousMatches, query.normalized, cache, IItemScore) so each distinct item yields a unique cache entry and prevents cross-item score/highlight reuse.
833-858:⚠️ Potential issue | 🟡 MinorKeep quoted queries as a single exact-match piece.
expectExactMatchis computed from the full input, but this still splits on spaces before buildingvalues. A query like"foo bar"turns into two fuzzy terms instead of one contiguous phrase, so quoted searches can match with arbitrary gaps.🛠️ Proposed fix
- const originalSplit = original.split(MULTIPLE_QUERY_VALUES_SEPARATOR); - if (originalSplit.length > 1) { - for (const originalPiece of originalSplit) { - const expectExactMatchPiece = queryExpectsExactMatch(originalPiece); - const { - pathNormalized: pathNormalizedPiece, - normalized: normalizedPiece, - normalizedLowercase: normalizedLowercasePiece, - } = normalizeQuery(originalPiece); - - if (normalizedPiece) { - if (!values) { - values = []; - } - - values.push({ - original: originalPiece, - originalLowercase: originalPiece.toLowerCase(), - pathNormalized: pathNormalizedPiece, - normalized: normalizedPiece, - normalizedLowercase: normalizedLowercasePiece, - expectContiguousMatch: expectExactMatchPiece, - }); - } - } - } + if (!expectExactMatch) { + const originalSplit = original.split(MULTIPLE_QUERY_VALUES_SEPARATOR); + if (originalSplit.length > 1) { + for (const originalPiece of originalSplit) { + const expectExactMatchPiece = queryExpectsExactMatch(originalPiece); + const { + pathNormalized: pathNormalizedPiece, + normalized: normalizedPiece, + normalizedLowercase: normalizedLowercasePiece, + } = normalizeQuery(originalPiece); + + if (normalizedPiece) { + if (!values) { + values = []; + } + + values.push({ + original: originalPiece, + originalLowercase: originalPiece.toLowerCase(), + pathNormalized: pathNormalizedPiece, + normalized: normalizedPiece, + normalizedLowercase: normalizedLowercasePiece, + expectContiguousMatch: expectExactMatchPiece, + }); + } + } + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/workspace-fs/src/fuzzy-scorer.ts` around lines 833 - 858, The code splits the full query by MULTIPLE_QUERY_VALUES_SEPARATOR even when the user provided a quoted phrase, causing a quoted term like "\"foo bar\"" to be treated as two fuzzy pieces; to fix, detect quoted queries before splitting (e.g., check if original starts and ends with a quote or contains an unescaped quoted segment) and in that case do not split—treat the entire original as one piece, compute expectExactMatch with queryExpectsExactMatch(original) (or normalized piece) and push a single value with expectContiguousMatch=true; otherwise continue splitting and building values as currently done using normalizeQuery and normalizeLowercase variables.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx (1)
6-7: Co-locate the palette's search adapter next to this component.Pulling one hook from a route folder and another from the sidebar makes
CommandPalettedepend on two unrelated UI surfaces. A small local adapter hook would keep this component self-contained and make the new API easier to reuse.
As per coding guidelines, "Co-locate component dependencies (utils, hooks, constants, config, stories) next to the file using them".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx` around lines 6 - 7, CommandPalette currently imports two disparate hooks (useV2FileSearch from the route and useFileSearch from the sidebar) causing cross-surface coupling; create a new local adapter hook (e.g., useCommandPaletteFileSearch) colocated next to CommandPalette.tsx that normalizes whichever internal search API you need, move any mapping/transform logic into that hook, update CommandPalette to import only useCommandPaletteFileSearch (remove direct imports of useV2FileSearch and useFileSearch), and ensure the new hook exposes the same interface CommandPalette expects (search term input, results, loading/error states, and any pagination handlers).
🤖 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/workspace-fs/src/search.ts`:
- Around line 287-296: The build promise may become stale after invalidation
because
invalidateSearchIndex/invalidateAllSearchIndexes/patchSearchIndexesForRoot only
remove the promise reference; so update the resolution and rejection handlers
for the promise created in buildSearchIndex to first verify that the in-flight
promise is still the current one in searchIndexBuilds for the given cacheKey
(e.g., compare searchIndexBuilds.get(cacheKey) === buildPromise) before writing
to searchIndexCache or deleting the entry; if it is not the same promise, do not
write the stale items to searchIndexCache and only delete the map entry if it
still points to this buildPromise. Ensure the same guard is applied in both the
.then and .catch branches around searchIndexCache.set and
searchIndexBuilds.delete to prevent stale writes and premature deletes.
---
Duplicate comments:
In `@packages/workspace-fs/src/fuzzy-scorer.ts`:
- Around line 348-349: The cache key construction for cacheHash is ambiguous
because it concatenates label and description without separators, causing
collisions and incorrect reuse of IItemScore entries; fix this by building an
unambiguous key (e.g., serialize a tuple/object or insert explicit separators)
that includes label, description, allowNonContiguousMatches and query.normalized
(references: cacheHash, label, description, allowNonContiguousMatches,
query.normalized, cache, IItemScore) so each distinct item yields a unique cache
entry and prevents cross-item score/highlight reuse.
- Around line 833-858: The code splits the full query by
MULTIPLE_QUERY_VALUES_SEPARATOR even when the user provided a quoted phrase,
causing a quoted term like "\"foo bar\"" to be treated as two fuzzy pieces; to
fix, detect quoted queries before splitting (e.g., check if original starts and
ends with a quote or contains an unescaped quoted segment) and in that case do
not split—treat the entire original as one piece, compute expectExactMatch with
queryExpectsExactMatch(original) (or normalized piece) and push a single value
with expectContiguousMatch=true; otherwise continue splitting and building
values as currently done using normalizeQuery and normalizeLowercase variables.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx`:
- Around line 6-7: CommandPalette currently imports two disparate hooks
(useV2FileSearch from the route and useFileSearch from the sidebar) causing
cross-surface coupling; create a new local adapter hook (e.g.,
useCommandPaletteFileSearch) colocated next to CommandPalette.tsx that
normalizes whichever internal search API you need, move any mapping/transform
logic into that hook, update CommandPalette to import only
useCommandPaletteFileSearch (remove direct imports of useV2FileSearch and
useFileSearch), and ensure the new hook exposes the same interface
CommandPalette expects (search term input, results, loading/error states, and
any pagination handlers).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ee3292f7-acbe-44a9-8e09-5f51fee541d2
📒 Files selected for processing (8)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useV2FileSearch/useV2FileSearch.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsxapps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsxapps/desktop/src/renderer/screens/main/components/CommandPalette/index.tsapps/desktop/src/renderer/stores/tabs/store.tspackages/ui/src/components/ui/command.tsxpackages/workspace-fs/src/fuzzy-scorer.tspackages/workspace-fs/src/search.ts
✅ Files skipped from review due to trivial changes (3)
- packages/ui/src/components/ui/command.tsx
- apps/desktop/src/renderer/stores/tabs/store.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useV2FileSearch/useV2FileSearch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/desktop/src/renderer/screens/main/components/CommandPalette/index.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx (1)
116-126:⚠️ Potential issue | 🟡 MinorLabel the include/exclude glob inputs.
These fields rely on placeholder text only. Once the user types, assistive tech loses the field purpose, which makes the filters hard to use non-visually.
♿ Proposed fix
<input + aria-label="Include files glob pattern" value={includePattern} onChange={(e) => setIncludePattern(e.target.value)} placeholder="files to include (glob)" className="h-8 rounded border bg-transparent px-2 text-xs outline-none placeholder:text-muted-foreground" /> <input + aria-label="Exclude files glob pattern" value={excludePattern} onChange={(e) => setExcludePattern(e.target.value)} placeholder="files to exclude (glob)" className="h-8 rounded border bg-transparent px-2 text-xs outline-none placeholder:text-muted-foreground" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx` around lines 116 - 126, The include/exclude glob inputs in CommandPalette rely solely on placeholder text so their purpose is lost once typed; update the JSX for the inputs (the ones using includePattern/setIncludePattern and excludePattern/setExcludePattern) to provide accessible labels—either add visually-hidden <label> elements associated via id for each input or add explicit aria-label attributes (e.g., aria-label="Files to include (glob)" and aria-label="Files to exclude (glob)") so assistive technology always knows the field purpose.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx`:
- Around line 48-49: The component currently derives `results` unconditionally
from `v2Search.results` or `v1Search.searchResults`, which keeps placeholderData
shown when `query` is empty; update the logic that computes `results` (the
`results` binding/variable in `CommandPalette.tsx`) to return an empty array
when `query` (or the local query state) is blank/whitespace (e.g., `query.trim()
=== ""`), otherwise use `v2Search.results` or `v1Search.searchResults`; apply
the same guard wherever results are computed/used (including the other block
around the Enter/action handlers at the lines referenced) so Enter cannot act on
stale placeholder items.
---
Duplicate comments:
In
`@apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx`:
- Around line 116-126: The include/exclude glob inputs in CommandPalette rely
solely on placeholder text so their purpose is lost once typed; update the JSX
for the inputs (the ones using includePattern/setIncludePattern and
excludePattern/setExcludePattern) to provide accessible labels—either add
visually-hidden <label> elements associated via id for each input or add
explicit aria-label attributes (e.g., aria-label="Files to include (glob)" and
aria-label="Files to exclude (glob)") so assistive technology always knows the
field purpose.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: dac8ee3b-56c8-4657-b58e-ef0eebdab14c
📒 Files selected for processing (3)
apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsxapps/desktop/src/renderer/screens/main/components/CommandPalette/hooks/useV2FileSearch/index.tsapps/desktop/src/renderer/screens/main/components/CommandPalette/hooks/useV2FileSearch/useV2FileSearch.ts
✅ Files skipped from review due to trivial changes (1)
- apps/desktop/src/renderer/screens/main/components/CommandPalette/hooks/useV2FileSearch/index.ts
| const results = variant === "v2" ? v2Search.results : v1Search.searchResults; | ||
|
|
There was a problem hiding this comment.
Don't render placeholder matches for an empty query.
Both search hooks preserve prior matches via placeholderData, so deriving results unconditionally means backspacing to blank — or reopening after setQuery("") — can leave the previous hit list visible with no active search. That also leaves Enter acting on stale items.
🩹 Proposed fix
+ const hasQuery = query.trim().length > 0;
- const results = variant === "v2" ? v2Search.results : v1Search.searchResults;
+ const results = hasQuery
+ ? variant === "v2"
+ ? v2Search.results
+ : v1Search.searchResults
+ : [];
...
- {results.length === 0 && (
+ {hasQuery && results.length === 0 && (Also applies to: 131-136
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/screens/main/components/CommandPalette/CommandPalette.tsx`
around lines 48 - 49, The component currently derives `results` unconditionally
from `v2Search.results` or `v1Search.searchResults`, which keeps placeholderData
shown when `query` is empty; update the logic that computes `results` (the
`results` binding/variable in `CommandPalette.tsx`) to return an empty array
when `query` (or the local query state) is blank/whitespace (e.g., `query.trim()
=== ""`), otherwise use `v2Search.results` or `v1Search.searchResults`; apply
the same guard wherever results are computed/used (including the other block
around the Enter/action handlers at the lines referenced) so Enter cannot act on
stale placeholder items.
…e search Hybrid approach from upstream 589a7c7 (superset-sh#3136): - Add fuzzy-scorer.ts (VS Code's path-aware fuzzy scoring, MIT licensed) - Replace Fuse.js fallback in searchFiles() with scoreItemFuzzy/compareItemsByFuzzyScore - Keep exact-match fast path, content search, replace API, and all UI untouched - Keep Fuse.js import for backward compat (still used by FileSearchIndex maps)
cherry-pick方式で内容を取り込み済みの14コミットをgit履歴上もマージ済みにする。 取り込み済み (cherry-pick / 手動移植): - be22b46 superset-sh#3125 — スキップ (下記参照) - 88bc7fb superset-sh#3127 — Revert DA1 ✓ - 92d0ff9 superset-sh#3054 — DA1 fix ✓ - c48450e superset-sh#3093 — file viewer pane fix ✓ - fffa8db superset-sh#3128 — version 1.4.7 ✓ - 589a7c7 superset-sh#3136 — fuzzy scorer (ハイブリッド方式) ✓ - ceb8c81 superset-sh#3150 — Electron 40.8.5 ✓ - 8922b94 superset-sh#3137 — terminalId分離 ✓ - c7508e5 superset-sh#3152 — GitHub無料化 ✓ - 2b91f11 superset-sh#3155 — v2 terminal theme ✓ - b8b11af superset-sh#3154 — TUI dimension fix ✓ - 7599ace superset-sh#3149 — v2 sidebar file tree (手動統合) ✓ - 4d7c612 superset-sh#3174 — DnD重複削除 ✓ - 864977d superset-sh#3157 — Host Service分離 ✓ 意図的にスキップ: - be22b46 superset-sh#3125 (GitHub polling簡素化) フォーク独自のGitHubSyncService (バックエンド集中ポーリング) と 設計が異なるため不採用。upstreamはフロントエンドhover駆動、フォークは バックエンドキャッシュウォーマー方式。詳細は githubQueryPolicy.ts と github-sync-service.ts のFORK NOTEを参照。 ゴースト・マージ復元 (revert 134cfd5 で消失した内容): - 538f306 superset-sh#3120 — Patch vuln ✓ - 1588d20 superset-sh#3108 — terminal lifecycle分離 ✓ - 59426f6 superset-sh#3122 — file tree + FilePane + Alert refactor (手動統合) ✓ - 10d9a5d superset-sh#3097 — tiptap line-height ✓ - 337a9ae superset-sh#3121 — Codex hooks削除 ✓
Summary
scoreItemFuzzy+compareItemsByFuzzyScore) for dramatically better search ranking — filename matches now properly rank above path-only matchesCommandPaletteas a self-contained component withv1/v2search variants (v2 usesworkspaceTrpcdirect to host service)SearchDialog,KeywordSearch,ScopeToggle,useCommandPalette, debounce logicTest plan
package.jsonfiles should rank above files inpackages/directorySummary by cubic
Replaced
fuse.jswith VS Code’s path‑aware fuzzy scorer for faster, more accurate Quick Open; rebuilt the command palette on Radix +cmdkwith v1/v2 variants and pre‑warmed indexes so the first search is instant.New Features
CommandPalettewith keyboard nav and Enter hint; v1 exposes include/exclude globs, v2 usesworkspaceTrpcviauseV2FileSearch(limit 50, hook co‑located with the component).cmdkintegration withshouldFilter=falseto rely on our scorer.Refactors
workspace-fsand rewrote file/content ranking withscoreItemFuzzy/compareItemsByFuzzyScore; removedfuse.js.SearchDialog,KeywordSearch,ScopeToggle, anduseCommandPalette; index stays current via watcher patches; removed TTL and request debouncing; pre‑warm viagetSearchIndexin the host.CommandPrimitivefrom@superset/ui/command; fixed lint/type errors infuzzy-scorer.Written for commit 99bb3e0. Summary will update on new commits.
Summary by CodeRabbit
Refactor
New Features
Chores