Skip to content

feat(webui): add search functionality to file browser#1228

Merged
Audionut merged 3 commits intoAudionut:masterfrom
thatonePRguy:feat/file-browser-search
Feb 11, 2026
Merged

feat(webui): add search functionality to file browser#1228
Audionut merged 3 commits intoAudionut:masterfrom
thatonePRguy:feat/file-browser-search

Conversation

@thatonePRguy
Copy link

@thatonePRguy thatonePRguy commented Feb 9, 2026

This PR adds search functionality to the file browser in the web UI sidebar.

Some key points:

  • Introduces a new endpoint: /api/browse_search
  • Searches all browse roots so files can be found without having to expand folders
  • Matches names using fuzzy matching so that common separators (dots, dashes, underscores, etc) are normalized
  • Debounced at 300ms

Summary by CodeRabbit

  • New Features
    • File browser search with real-time, debounced querying and live results
    • Search UI in the left File Browser with clear button, loading indicator, results count and “No results” messaging
    • Intelligent name matching with prioritized folder results and selectable search entries for quick navigation

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

A file browser search feature was added: a backend /api/browse_search endpoint that tokenizes queries, traverses configured browse roots, matches names by ordered whole-word subsequences with extension and safety checks, and a frontend debounced search UI that queries this API and displays results in the file browser.

Changes

Cohort / File(s) Summary
Backend Search API
web_ui/server.py
Added _BROWSE_SEARCH_SEP_RE and new /api/browse_search route: authenticates via token/session, validates CSRF/same-origin for web clients, tokenizes queries, recursively walks configured browse roots with safety checks, matches ordered whole-word subsequences, filters by allowed extensions, sorts (folders first), and returns JSON results with error handling.
Frontend Search UI
web_ui/static/js/app.js
Added debounced file browser search state and handler (300ms), async POST to /api/browse_search with stale-result guarding, loading/error states, result rendering (path/parent handling, selection styling), and conditional rendering of search results vs. directory tree in the left pane.

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Frontend (React)
    participant Server as API Server (/api/browse_search)
    participant FS as File System (os.walk)

    User->>Frontend: Type search query
    Note over Frontend: Debounce 300ms, set loading
    Frontend->>Server: POST query + auth token
    Server->>Server: Validate auth, CSRF, same-origin
    Server->>Server: Tokenize query with _BROWSE_SEARCH_SEP_RE
    Server->>FS: Recursively traverse configured browse roots
    FS-->>Server: Return directories & files
    Server->>Server: Match ordered whole-word subsequences, filter extensions, perform path safety checks
    Server->>Server: Sort results (folders first), truncate if needed
    Server-->>Frontend: Return JSON (results, count, truncated)
    Frontend->>Frontend: Verify query still current, clear loading, render results
    Frontend-->>User: Display matching files/folders
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through roots both near and far,

Tokenized crumbs like a searching star.
Folders first, then files in line,
Debounced clicks, results that shine.
A floppy-eared cheer — search works just fine! 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(webui): add search functionality to file browser' accurately and concisely describes the main change: adding search functionality to the file browser, which is the primary focus of both backend and frontend modifications.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@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

🤖 Fix all issues with AI agents
In `@web_ui/static/js/app.js`:
- Around line 786-788: The display logic that computes the parent path uses
item.path.lastIndexOf('/') and thus misses Windows backslashes; update the
separator computation in the code that sets separatorIdx/parentPath (referencing
item.path, separatorIdx, parentPath) to consider both separators (e.g., take
Math.max(item.path.lastIndexOf('/'), item.path.lastIndexOf('\\'))), then keep
the existing substring logic so parentPath is correctly derived on Windows and
POSIX paths.

Copy link

@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

🤖 Fix all issues with AI agents
In `@web_ui/static/js/app.js`:
- Around line 752-773: The search handler should check response.ok before
calling response.json and handle non-2xx responses by setting an empty result
(using setFileBrowserSearchResults) and avoiding parsing errors; update the
async callback in the setTimeout to inspect response.ok and branch to the same
error/result-handling path used on catches, preserving the early-return that
compares fileBrowserSearchQuery.current to searchQuery. Also add a component
cleanup effect (useEffect with a return cleanup) that clears the pending timeout
stored in fileBrowserSearchTimer.current on unmount so the delayed callback
cannot call setFileBrowserSearchResults or setFileBrowserSearchLoading after the
component is unmounted. Ensure you reference/modify the existing
fileBrowserSearchTimer.current, fileBrowserSearchQuery.current,
setFileBrowserSearchResults and setFileBrowserSearchLoading symbols.

Comment on lines +752 to +773
fileBrowserSearchTimer.current = setTimeout(async () => {
try {
const response = await apiFetch(`${API_BASE}/browse_search?q=${encodeURIComponent(searchQuery)}`);
const data = await response.json();
// Early return if the search has changed since this request
if (fileBrowserSearchQuery.current !== searchQuery) return;
if (data.success) {
setFileBrowserSearchResults(data);
} else {
setFileBrowserSearchResults({ items: [], query: searchQuery, count: 0 });
}
} catch (error) {
console.error('File browser search failed:', error);
if (fileBrowserSearchQuery.current === searchQuery) {
setFileBrowserSearchResults({ items: [], query: searchQuery, count: 0 });
}
} finally {
if (fileBrowserSearchQuery.current === searchQuery) {
setFileBrowserSearchLoading(false);
}
}
}, 300); //300ms debounce so we dont spam requests for every keystroke
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing response.ok check before parsing JSON.

If the server returns a non-2xx status (e.g. 500), response.json() will either throw a parse error on an HTML error page or silently yield unexpected data. Check response.ok before parsing, similar to how executeCommand handles it (line 1009).

Additionally, the debounce timer stored in fileBrowserSearchTimer.current is never cleaned up on component unmount. If the component unmounts mid-debounce, the callback fires and calls setFileBrowserSearchResults / setFileBrowserSearchLoading on an unmounted component.

Proposed fix

Add a response.ok guard:

       const response = await apiFetch(`${API_BASE}/browse_search?q=${encodeURIComponent(searchQuery)}`);
+      if (!response.ok) {
+        throw new Error(`Search request failed (${response.status})`);
+      }
       const data = await response.json();

Add a cleanup effect near the other search state declarations:

useEffect(() => {
  return () => {
    if (fileBrowserSearchTimer.current) {
      clearTimeout(fileBrowserSearchTimer.current);
    }
  };
}, []);
🤖 Prompt for AI Agents
In `@web_ui/static/js/app.js` around lines 752 - 773, The search handler should
check response.ok before calling response.json and handle non-2xx responses by
setting an empty result (using setFileBrowserSearchResults) and avoiding parsing
errors; update the async callback in the setTimeout to inspect response.ok and
branch to the same error/result-handling path used on catches, preserving the
early-return that compares fileBrowserSearchQuery.current to searchQuery. Also
add a component cleanup effect (useEffect with a return cleanup) that clears the
pending timeout stored in fileBrowserSearchTimer.current on unmount so the
delayed callback cannot call setFileBrowserSearchResults or
setFileBrowserSearchLoading after the component is unmounted. Ensure you
reference/modify the existing fileBrowserSearchTimer.current,
fileBrowserSearchQuery.current, setFileBrowserSearchResults and
setFileBrowserSearchLoading symbols.

@Audionut Audionut merged commit f35a556 into Audionut:master Feb 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants