Skip to content

fix(file-explorer): search folder workspace roots#6567

Merged
nwparker merged 1 commit into
mainfrom
nwparker/fix-5939
Jun 29, 2026
Merged

fix(file-explorer): search folder workspace roots#6567
nwparker merged 1 commit into
mainfrom
nwparker/fix-5939

Conversation

@nwparker

@nwparker nwparker commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🧑‍🤝‍🧑 Impact & ELI5

1) What's broken today (ELI5). If you open Orca on the top folder of a "monorepo" (one big folder that holds several sub-projects), the file explorer's name search is completely broken. The full file tree shows up fine — but the moment you type anything into the search box (like package), the whole list goes blank and shows zero results. Clear the box and everything comes back. So the search isn't filtering, it's just wiping the screen. This hits anyone who opens a workspace at a monorepo root (a "folder" workspace), on any OS; for those people file search simply does not work — a real blocker, not a cosmetic annoyance. People who open one specific sub-folder instead of the whole monorepo were never affected.

2) What this PR does (ELI5). When you search, Orca first has to figure out "which folder am I searching in?" There were two address books for that: one for normal git checkouts, and a fuller one that also knows about plain folders opened as workspaces. The search was only consulting the git-only address book, so for a monorepo-root folder it found nothing, decided it had no place to look, and returned an empty list. This PR points the search at the fuller address book that also knows about folder workspaces. Oh, so now searching a monorepo root actually returns matching files instead of going blank. (As a bonus, it also fixes a related case where searching over a remote/SSH folder could stay empty until the app finished loading its data.)

3) Tradeoffs / regressions — honest answer. No regressions — nothing is disabled, no flag is turned off, no new setup, and no behavior is lost. The fix only adds a working lookup path for folder workspaces; the old shared connection-lookup helper that other features rely on (terminal, delete-workspace, tab creation, etc.) is kept untouched and behaves exactly as before. One small implementation detail: the search now "watches" the connection info live (a store subscription) instead of taking a one-time snapshot — this is precisely what fixes the remote/SSH stale case, and it's safe because the watched value is a stable primitive and the synthetic folder entry is cached and reused, so there are no extra re-renders or render loops. One honest scope note (not a regression): this PR fixes "search returns nothing." The issue's secondary wish — surfacing matches buried deep inside nested sub-project directories under the monorepo root — is handled separately in companion PR #6481, so very deeply nested matches remain out of scope here.


Summary

Fixes #5939. The file-explorer name search returned zero results whenever the workspace root was a monorepo parent opened as a FolderWorkspace (folder-scan), even though the full tree still rendered.

Root cause: useRuntimeFileListForWorktree (src/renderer/src/components/quick-open-file-list.ts) resolved its worktree via useWorktreeById, which only reads worktreesByRepo (git worktrees). For a folder-workspace activeWorktreeId, that key is not in worktreesByRepo, so worktree was null, worktreePath became null, and the effect early-returned with setFiles([]). The tree still rendered because useFileExplorerTree gets worktreePath separately.

This change:

  • Resolves the Quick Open file-list target through getKnownWorktreeById, which resolves folder workspaces to a synthetic Worktree whose path = folderPath (folderWorkspaceToWorktree), restoring a non-null worktreePath.
  • Extracts a pure getRuntimeFileListTarget helper for the path/exclude resolution so it is directly unit-testable.
  • Makes connectionId a reactive store selector via the newly exported getConnectionIdFromState, so SSH folder roots are not left stale after store hydration. The existing getConnectionId wrapper is preserved for all other callers (Terminal, DeleteWorktreeDialog, tab-create, pty transport, etc.).

This supersedes community PR #6170 (the code becomes ours; original author credited via Co-authored-by). It does not close #6170.

Screenshots

No visual change to chrome/layout. This is a data-loading regression: with the fix, typing a substring in the file-explorer name search now returns matching files for a folder-workspace root instead of an empty list. End-to-end Electron repro was attempted over CDP but the CDP session repeatedly dropped mid-interaction in this environment and window.__store was not exposed in the dev build, so visual capture was infeasible; the regression is instead proven deterministically by a hook-level test (see PR comment for evidence).

Testing

  • pnpm lint (scoped: oxlint on the 5 touched files — clean)
  • pnpm typecheck (tsgo -p config/tsconfig.tc.web.json and config/tsconfig.node.json — clean)
  • pnpm test (focused: see below)
  • pnpm build
  • Added or updated high-quality tests that would catch regressions

Focused tests run:

  • vitest run src/renderer/src/components/quick-open-file-list.test.ts src/renderer/src/components/quick-open-file-list.react.test.tsx src/renderer/src/lib/connection-context.test.ts — 17 passed
  • vitest run src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/components/right-sidebar/useFileExplorerVisibleRowProjection.test.ts — 25 passed
  • vitest run --environment happy-dom src/renderer/src/components/right-sidebar/FileExplorer.test.tsx — 31 passed

The new quick-open-file-list.react.test.tsx renders the real hook against the real zustand store for a repo-less folder workspace and asserts listRuntimeFiles is invoked with the folder's worktreePath. Reverting the fix (resolving via worktreesByRepo only) makes it fail with listRuntimeFiles was not called — i.e. it reproduces the bug.

AI Review Report

Reviewed for correctness, edge cases, cross-platform behavior, and store-reactivity:

  • Re-render safety: getConnectionIdFromState returns a primitive (no new-object churn); getKnownWorktreeById returns cached synthetic worktrees (WeakMap-keyed) and stored worktree objects, so the new useAppStore selectors are reference-stable and do not loop.
  • Caller regression: all getConnectionId callers go through the preserved wrapper (getConnectionIdFromState(useAppStore.getState(), id)); behavior is unchanged.
  • Cross-platform: no metaKey / path-separator assumptions introduced; path containment continues to flow through the existing Windows-aware isNestedWorktreePath. macOS/Linux/Windows behavior is identical here.
  • SSH/remote: connectionId reactivity fixes the stale-after-hydration case for SSH folder roots, which is the SSH-relevant path.
  • The Pick<AppState, 'folderWorkspaces' | 'projectGroups' | 'repos' | 'worktreesByRepo'> param to getConnectionIdFromState typechecks against the store slices.

Security Audit

Since this supersedes a community PR, I re-audited the adopted changes assuming the original author was untrusted:

  • No new network calls, eval, dynamic import, secret access, or process spawning.
  • No path traversal: the change only selects an already-resolved workspace root path and passes it through the existing listRuntimeFiles IPC; no new path is constructed or de-referenced.
  • No auth, permissions, payments, uploads, or dependency changes.
  • IPC surface is unchanged — same listRuntimeFiles({ settings, worktreeId, worktreePath, connectionId }, { rootPath, excludePaths }) call shape.

Notes

  • The reported bug ("search returns zero results") is fixed by this PR. The issue's secondary ask — surfacing matches that live deeper inside nested sub-project directories under the monorepo root — is tracked by companion PR Show nested sub-project files in file-explorer name search #6481 (deeper nested-repo traversal) and is independent of this fix.
  • Future suggestion: the react test uses a manual createRoot/act/flushEffects harness; a lighter @testing-library/react renderHook could simplify it if/when that dependency is standard in the renderer test suite. Left as-is to keep the diff minimal and consistent with the adopted PR.

Made with Orca 🐋

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9c6cd46d-3e41-4a6d-95b4-f6744804c906

📥 Commits

Reviewing files that changed from the base of the PR and between 258e23e and 3b9a9b5.

📒 Files selected for processing (5)
  • src/renderer/src/components/quick-open-file-list.react.test.tsx
  • src/renderer/src/components/quick-open-file-list.test.ts
  • src/renderer/src/components/quick-open-file-list.ts
  • src/renderer/src/lib/connection-context.test.ts
  • src/renderer/src/lib/connection-context.ts

📝 Walkthrough

Walkthrough

getConnectionId is refactored to delegate to a new exported getConnectionIdFromState that accepts a state slice directly. A new RuntimeFileListTarget type and getRuntimeFileListTarget function are added to centralize the canList decision and excludeRequest construction for folder workspaces resolved outside registered repo worktrees. useRuntimeFileListForWorktree is updated to use these helpers and gates its fetch effect on target.canList. Tests are added for getConnectionIdFromState, getRuntimeFileListTarget, and the hook's React lifecycle.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly describes the main change: fixing file-explorer searches for folder workspace roots.
Description check ✅ Passed The description includes all required sections and sufficiently details the change, testing, AI review, security, and platform considerations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@nwparker

Copy link
Copy Markdown
Contributor Author

Evidence

Regression guard proves the bug (before/after)

The new hook test src/renderer/src/components/quick-open-file-list.react.test.tsx renders the real useRuntimeFileListForWorktree against the real zustand store for a repo-less folder workspace and asserts listRuntimeFiles is called with the folder's root path.

Before the fix (temporarily reverting the hook to resolve via worktreesByRepo only, i.e. the old useWorktreeById behavior), the test FAILS — exactly reproducing the reported "zero results / no listing" bug:

 FAIL  src/renderer/src/components/quick-open-file-list.react.test.tsx > useRuntimeFileListForWorktree > lists a repo-less SSH folder workspace after folder metadata hydrates
Error: listRuntimeFiles was not called
 ❯ waitForListRuntimeFilesCall src/renderer/src/components/quick-open-file-list.react.test.tsx:85:9
 Test Files  1 failed (1)
      Tests  1 failed (1)

After the fix (resolving via getKnownWorktreeById), the same test passes and listRuntimeFiles is invoked with worktreePath: '/srv/platform', connectionId: 'ssh-1' once the folder metadata hydrates.

Focused test run (after fix)

$ vitest run --config config/vitest.config.ts \
    src/renderer/src/components/quick-open-file-list.test.ts \
    src/renderer/src/components/quick-open-file-list.react.test.tsx \
    src/renderer/src/lib/connection-context.test.ts

 Test Files  3 passed (3)
      Tests  17 passed (17)

Nearby regression suites:

$ vitest run ... worktree-runtime-owner.test.ts useFileExplorerVisibleRowProjection.test.ts
 Test Files  2 passed (2)
      Tests  25 passed (25)

$ vitest run --environment happy-dom ... right-sidebar/FileExplorer.test.tsx
 Test Files  1 passed (1)
      Tests  31 passed (31)

Lint / typecheck

$ oxlint <5 touched files>            -> exit 0 (clean)
$ tsgo --noEmit -p config/tsconfig.tc.web.json   -> exit 0
$ tsgo --noEmit -p config/tsconfig.node.json     -> exit 0

Electron repro note

I launched the dev build from this worktree over CDP (port 9335, confirmed instance 5939 @ nwparker/fix-5939). The CDP session repeatedly dropped mid-interaction (Session closed) and the dev build does not expose window.__store, so seeding a monorepo-parent folder workspace and driving the name-search input to capture a screenshot was infeasible in this environment. The hook-level test above is a stronger, deterministic proof of the exact regression path than a screenshot would be.

The file-explorer name search returned zero results whenever the
workspace root was a monorepo parent opened as a FolderWorkspace.
useRuntimeFileListForWorktree resolved its worktree via useWorktreeById,
which only reads worktreesByRepo, so a folder-workspace key resolved to
null and the effect early-returned with setFiles([]).

Resolve the Quick Open file-list target through getKnownWorktreeById so
folder workspaces supply their root path (folderPath) even when absent
from worktreesByRepo, and recompute connectionId from subscribed store
state via getConnectionIdFromState so SSH folder roots are not left
stale after hydration.

Supersedes community PR #6170; adds a dedicated getConnectionIdFromState
unit test that asserts resolution from a passed-in state snapshot.

Fixes #5939

Co-authored-by: bennewell35 <142949922+bennewell35@users.noreply.github.com>
@nwparker nwparker force-pushed the nwparker/fix-5939 branch from ddd35be to 3b9a9b5 Compare June 29, 2026 06:31
@nwparker nwparker merged commit 09c8b34 into main Jun 29, 2026
1 check passed
@nwparker nwparker deleted the nwparker/fix-5939 branch June 29, 2026 06:43
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.

[Bug]: File explorer name search returns no results when workspace root is a monorepo parent project

1 participant