Skip to content

[codex] fix workspace search regressions#2979

Merged
Kitenite merged 7 commits into
mainfrom
fix-search-regression
Mar 30, 2026
Merged

[codex] fix workspace search regressions#2979
Kitenite merged 7 commits into
mainfrom
fix-search-regression

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Mar 29, 2026

Summary

This PR fixes the recent workspace search regression and a few adjacent desktop-editor issues reported by users.

What changed

  • restore reliable file search by prioritizing exact filename and exact path hits before fuzzy matching
  • normalize separator-heavy queries so filename/path lookups behave better
  • rebuild file-search indexes after directory-level changes so renamed or moved folders do not leave stale results behind
  • remove the user-facing keyword/codebase search entrypoint from the workspace UI and hotkeys
  • add .astro language detection so Astro files get HTML highlighting
  • persist and reapply desktop zoom level on renderer reloads so Cmd+R does not temporarily forget font size

Root cause

The file search path had regressed in two ways:

  • ranking relied too heavily on fuzzy matching, so exact filename/path lookups could be drowned out by weaker fuzzy path matches
  • directory rename or move events could leave the cached search index stale

The initial fix corrected search quality but introduced an avoidable hot-path scan on every query. The final implementation keeps the correctness fix while using indexed exact-match maps and only falling back to Fuse when there is no exact hit.

User impact

  • searching by full filename works again
  • exact path searches return immediately and rank correctly
  • stale results after folder renames should no longer persist
  • keyword/codebase search is removed from the workspace surface since it was not useful
  • Astro static site files are highlighted correctly
  • zoom/font size survives reloads more reliably

Validation

  • bun test packages/workspace-fs/src/search.test.ts
  • bunx biome check packages/workspace-fs/src/search.ts packages/workspace-fs/src/search.test.ts
  • earlier targeted validation also passed for:
  • bun test apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.test.ts
  • bunx biome check apps/desktop/src/main/windows/main.ts apps/desktop/src/shared/detect-language.ts apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.test.ts apps/desktop/src/shared/hotkeys.ts 'apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx' packages/workspace-fs/src/search.ts packages/workspace-fs/src/search.test.ts

Benchmark notes

Local microbenchmarks on the search backend show the final implementation is much faster for the main user complaint, exact filename/path searches, while index construction is somewhat slower and separator-free compact fuzzy queries remain slower:

  • real repo, exact filename search.ts: 38.1ms -> 0.022ms
  • real repo, exact path packages/workspace-fs/src/search.ts: 273.3ms -> 0.045ms
  • real repo, exact filename page.tsx: 37.4ms -> 0.080ms
  • real repo, compact query workspacefssearchts: 73.6ms -> 142.8ms
  • real repo, index build: 3.7ms -> 6.8ms

These were backend function timings, not end-to-end renderer profiling.


Summary by cubic

Fixes workspace file search by prioritizing exact filename/path matches via indexed lookups, canonicalizing index and query paths, returning all compact collisions, and rebuilding indexes after directory renames. Removes the keyword search surface, adds .astro detection, and makes desktop zoom persist and restore across reloads.

  • Bug Fixes

    • Exact filename/path and compact matches now outrank fuzzy; map-based lookups with Fuse as fallback (packages/workspace-fs).
    • Canonicalizes stored index paths and normalizes relative path queries; returns all compact path collisions; directory rename/move triggers a full index rebuild to avoid stale results.
    • Zoom level is saved on change and re-applied on every renderer load (apps/desktop).
  • Refactors

    • Removed keyword/codebase search UI and meta+shift+f hotkey (apps/desktop).
    • Added .astro → HTML language detection.

Written for commit 1484cf2. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Window zoom level now persists and restores across sessions
    • Search ranks exact filename matches ahead of fuzzy results and better handles compact-path collisions
  • Bug Fixes

    • .astro files are now detected and treated as HTML
  • Removed

    • Keyword search UI and its hotkey removed from the workspace dashboard
  • Tests

    • Added tests for search index renames, ranking, normalization, and path-collision cases

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e8f14521-9fc6-4f06-925d-df229e98aacb

📥 Commits

Reviewing files that changed from the base of the PR and between c2a6123 and 1484cf2.

📒 Files selected for processing (1)
  • packages/workspace-fs/src/search.ts

📝 Walkthrough

Walkthrough

Refactors desktop window zoom persistence and startup sequencing; removes keyword-search UI and its hotkey; maps .astro files to "html"; and enriches workspace file search indexing with normalized/compact keys, exact-match retrieval, and new tests.

Changes

Cohort / File(s) Summary
Astro Language Detection
apps/desktop/src/shared/detect-language.ts, apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.test.ts
Add .astro"html" mapping and extend test to assert page.astro is detected as "html".
Keyword Search Removal
apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx, apps/desktop/src/shared/hotkeys.ts
Remove KeywordSearch usage and remove KEYWORD_SEARCH hotkey definition and related UI wiring.
Zoom Level Management (main process)
apps/desktop/src/main/windows/main.ts
Persist renderer zoom separately: initialize in-memory zoom from saved state, apply on each did-finish-load, listen for zoom-changed to update persisted zoom and trigger debounced state saves; gate initial maximize/show until first load.
Search Indexing Enhancement
packages/workspace-fs/src/search.ts, packages/workspace-fs/src/search.test.ts
Enrich search index entries with normalized (lower*) and compact (compact*) keys and lookup maps; add createSearchIndexEntry / createFileSearchIndex; add exact-match retrieval and updated Fuse config; rebuild/invalidate index on directory patches; add tests for renames, ranking, normalization, and compact-path collisions.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Main as Main Process
participant Store as WindowStateStore
participant Window as BrowserWindow
participant WebContents as Renderer (webContents)

Main->>Store: read persisted window state (bounds + zoomLevel)
Main->>Window: create BrowserWindow with bounds
WebContents->>Main: on('did-finish-load') (may fire multiple times)
Main->>WebContents: apply persisted zoomLevel
WebContents->>Main: emit 'zoom-changed' (newZoom)
Main->>Main: update in-memory persistedZoomLevel
Main->>Store: debounced save window state (bounds + zoomLevel)
Window->>Store: on('move'/'resize'/'close') persist bounds + persistedZoomLevel

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through code with careful claws,
Astro now speaks HTML laws,
The keyword burrow left the map,
Zoom remembers every gap,
Search grew tidy — I applaud with paws.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[codex] fix workspace search regressions' clearly summarizes the main change—fixing file search bugs and adjacent issues as detailed in the description.
Description check ✅ Passed The description covers all required template sections: summary, related context (root cause, user impact, validation), and type (bug fix). It is comprehensive and well-structured with clear explanations.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-search-regression

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 29, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/workspace-fs/src/search.ts (1)

167-188: Potential collision in itemsByCompactRelativePath map.

The compactRelativePath normalization strips all separators, so files like foo/bar.ts, foo-bar.ts, and foo_bar.ts would all normalize to foobarts, and only the last-indexed entry would be stored. This could cause some exact matches to be missed.

Consider using a multi-value map (like itemsByCompactName) for consistency:

♻️ Proposed change to use multi-value maps for path lookups
 function createFileSearchIndex(items: SearchIndexEntry[]): FileSearchIndex {
 	const itemsByLowerName = new Map<string, SearchIndexEntry[]>();
 	const itemsByCompactName = new Map<string, SearchIndexEntry[]>();
-	const itemsByLowerRelativePath = new Map<string, SearchIndexEntry>();
-	const itemsByCompactRelativePath = new Map<string, SearchIndexEntry>();
+	const itemsByLowerRelativePath = new Map<string, SearchIndexEntry[]>();
+	const itemsByCompactRelativePath = new Map<string, SearchIndexEntry[]>();

 	for (const item of items) {
 		addSearchIndexMapEntry(itemsByLowerName, item.lowerName, item);
 		addSearchIndexMapEntry(itemsByCompactName, item.compactName, item);
-		itemsByLowerRelativePath.set(item.lowerRelativePath, item);
-		itemsByCompactRelativePath.set(item.compactRelativePath, item);
+		addSearchIndexMapEntry(itemsByLowerRelativePath, item.lowerRelativePath, item);
+		addSearchIndexMapEntry(itemsByCompactRelativePath, item.compactRelativePath, item);
 	}

This would also require updating the FileSearchIndex interface and collectExactFileSearchMatches to handle arrays for these maps.

🤖 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 167 - 188, The
itemsByCompactRelativePath map in createFileSearchIndex currently stores a
single SearchIndexEntry per compactRelativePath which causes collisions (e.g.,
foo/bar.ts, foo-bar.ts → same key) and loses matches; change
itemsByCompactRelativePath (and itemsByLowerRelativePath for consistency) to map
string → SearchIndexEntry[] (multi-value map) by using addSearchIndexMapEntry
like itemsByCompactName does, update the FileSearchIndex type to reflect arrays
for these maps, and then update collectExactFileSearchMatches (and any other
consumers) to handle arrays returned from
itemsByCompactRelativePath/itemsByLowerRelativePath, flattening/concatenating
entries and preserving deduplication/ordering as before.
apps/desktop/src/main/windows/main.ts (1)

246-249: Optional: read zoom level once in debouncedSave().

Line 246 and Line 248 call getZoomLevel() twice back-to-back. Cache once to keep the saved value and in-memory value perfectly aligned.

Small cleanup
-			saveWindowState({
+			const zoomLevel = window.webContents.getZoomLevel();
+			saveWindowState({
 				x: bounds.x,
 				y: bounds.y,
 				width: bounds.width,
 				height: bounds.height,
 				isMaximized,
-				zoomLevel: window.webContents.getZoomLevel(),
+				zoomLevel,
 			});
-			persistedZoomLevel = window.webContents.getZoomLevel();
+			persistedZoomLevel = zoomLevel;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/windows/main.ts` around lines 246 - 249, In
debouncedSave, avoid calling window.webContents.getZoomLevel() twice; call it
once (e.g., const zoom = window.webContents.getZoomLevel()) and use that single
value for both the saved payload (zoomLevel) and persistedZoomLevel so the
in-memory and persisted values are identical; update the code paths that set
zoomLevel and persistedZoomLevel inside debouncedSave to use the cached zoom
variable.
🤖 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/main/windows/main.ts`:
- Around line 253-256: The zoom-changed handler reads a stale value because
getZoomLevel() runs before the change is applied; update the handler registered
on window.webContents.on("zoom-changed") to defer reading
window.webContents.getZoomLevel() by one tick (e.g., use setTimeout(..., 0) or
process.nextTick) before assigning persistedZoomLevel and calling
debouncedSave(), so the stored zoom reflects the new applied level.

---

Nitpick comments:
In `@apps/desktop/src/main/windows/main.ts`:
- Around line 246-249: In debouncedSave, avoid calling
window.webContents.getZoomLevel() twice; call it once (e.g., const zoom =
window.webContents.getZoomLevel()) and use that single value for both the saved
payload (zoomLevel) and persistedZoomLevel so the in-memory and persisted values
are identical; update the code paths that set zoomLevel and persistedZoomLevel
inside debouncedSave to use the cached zoom variable.

In `@packages/workspace-fs/src/search.ts`:
- Around line 167-188: The itemsByCompactRelativePath map in
createFileSearchIndex currently stores a single SearchIndexEntry per
compactRelativePath which causes collisions (e.g., foo/bar.ts, foo-bar.ts → same
key) and loses matches; change itemsByCompactRelativePath (and
itemsByLowerRelativePath for consistency) to map string → SearchIndexEntry[]
(multi-value map) by using addSearchIndexMapEntry like itemsByCompactName does,
update the FileSearchIndex type to reflect arrays for these maps, and then
update collectExactFileSearchMatches (and any other consumers) to handle arrays
returned from itemsByCompactRelativePath/itemsByLowerRelativePath,
flattening/concatenating entries and preserving deduplication/ordering as
before.
🪄 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: b5075d8a-a2e2-47c9-97b4-63b48490c98a

📥 Commits

Reviewing files that changed from the base of the PR and between 1ac52d4 and 2c4797a.

📒 Files selected for processing (7)
  • apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.test.ts
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx
  • apps/desktop/src/shared/detect-language.ts
  • apps/desktop/src/shared/hotkeys.ts
  • packages/workspace-fs/src/search.test.ts
  • packages/workspace-fs/src/search.ts
💤 Files with no reviewable changes (1)
  • apps/desktop/src/shared/hotkeys.ts

Comment thread apps/desktop/src/main/windows/main.ts
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/workspace-fs/src/search.ts">

<violation number="1" location="packages/workspace-fs/src/search.ts:47">
P1: Maps for path-based exact matching incorrectly store single items instead of arrays, causing valid files to be dropped when path normalizations collide.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread packages/workspace-fs/src/search.ts
Copy link
Copy Markdown
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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/workspace-fs/src/search.ts`:
- Around line 451-453: The exact-path query is only replacing backslashes and
isn't run through the same text normalizer, causing misses for inputs like
"./src/file.ts"; change the normalizedPathQuery to apply the same
normalizeSearchText pipeline after normalizing separators (e.g., compute
normalizedPathQuery = normalizeSearchText(lowerQuery.replace(/\\/g, "/"))) and
use that value for the itemsByLowerRelativePath lookup so exact relative-path
lookups use the identical normalization as compactQuery.
🪄 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: 82a515c5-48da-4364-a948-79ad866b6b77

📥 Commits

Reviewing files that changed from the base of the PR and between 83745b5 and 7ca4eb3.

📒 Files selected for processing (2)
  • packages/workspace-fs/src/search.test.ts
  • packages/workspace-fs/src/search.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/workspace-fs/src/search.test.ts

Comment thread packages/workspace-fs/src/search.ts
Copy link
Copy Markdown
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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/desktop/src/main/windows/main.ts (1)

314-314: Redundant assignment on window close.

The assignment persistedZoomLevel = zoomLevel has no effect here since the window is closing and the variable goes out of scope. Consider removing it for clarity.

Optional cleanup
 		zoomLevel,
 	});
-	persistedZoomLevel = zoomLevel;

 	browserManager.unregisterAll();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/windows/main.ts` at line 314, The assignment
persistedZoomLevel = zoomLevel inside the window close handler is redundant
because persistedZoomLevel is not used after the window is closed; remove the
line to avoid dead code. Locate the close event callback in main.ts where
persistedZoomLevel and zoomLevel are referenced and delete the assignment
persistedZoomLevel = zoomLevel, leaving any other cleanup/close logic intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/workspace-fs/src/search.ts`:
- Around line 137-149: The helper is storing raw absolutePath and
lowerRelativePath which breaks deletes/lookups that expect canonicalized keys;
before returning, compute canonicalAbsolutePath =
normalizeAbsolutePath(absolutePath) and canonicalLowerRelativePath =
normalizePathForGlob(relativePath).toLowerCase() (and use normalizePathForGlob
for compactRelativePath as well) then replace the stored absolutePath and
lowerRelativePath with those canonical values so the index (itemsByPath) and
exact-path queries use the same normalized forms.

---

Nitpick comments:
In `@apps/desktop/src/main/windows/main.ts`:
- Line 314: The assignment persistedZoomLevel = zoomLevel inside the window
close handler is redundant because persistedZoomLevel is not used after the
window is closed; remove the line to avoid dead code. Locate the close event
callback in main.ts where persistedZoomLevel and zoomLevel are referenced and
delete the assignment persistedZoomLevel = zoomLevel, leaving any other
cleanup/close logic intact.
🪄 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: 405e6dc8-43ce-4282-bd97-8104084321aa

📥 Commits

Reviewing files that changed from the base of the PR and between 7ca4eb3 and c2a6123.

📒 Files selected for processing (3)
  • apps/desktop/src/main/windows/main.ts
  • packages/workspace-fs/src/search.test.ts
  • packages/workspace-fs/src/search.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/workspace-fs/src/search.test.ts

Comment thread packages/workspace-fs/src/search.ts Outdated
@Kitenite Kitenite merged commit 89cb032 into main Mar 30, 2026
15 checks passed
@Kitenite Kitenite deleted the fix-search-regression branch March 30, 2026 02:55
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.

1 participant