Find/replace in the active file and follow workspace symlinks#39
Conversation
Two editor changes. Find in File is now a real find-and-replace. Cmd+F opens the app's own overlay instead of CodeMirror's panel, the arrow keys step through matches and select each one in the editor while focus stays in the field, the results preview scrolls to keep the active match visible, and Cmd+R reveals a replace row with Replace and Replace All. Standard edit keybinds (Select All, word jumps, cut/copy/paste) now work in every textbox after wiring the macOS Edit menu's Select All item. Symlinks used to be rejected outright. The editor now follows them: a target inside the workspace opens and expands with no fuss, and a target that escapes the workspace asks first with a Cancel / Trust once / Trust for workspace prompt. "Trust once" lasts the session, "Trust for workspace" is persisted. Symlinked entries get a link icon in the tree, with a separate mark and the target path shown when they point outside the workspace. Renaming or deleting a symlink only touches the link, never its target. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
There was a problem hiding this comment.
Code Review
This pull request introduces support for handling external symbolic links with user trust prompts, alongside a new find-and-replace feature in the active file. The review feedback highlights several critical improvements: first, the workspace root path should be canonicalized before performing starts_with checks in workspace.rs to ensure consistent path comparisons; second, multiple document changes dispatched to CodeMirror 6 in EditorPane.tsx must be sorted and deduplicated to prevent runtime crashes; and finally, side effects in App.tsx should be moved out of the React state updater callback to avoid anti-patterns.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR enhances the IDE’s in-editor search UX by adding a first-party Find/Replace overlay for the active file, and improves workspace filesystem support by following symlinks with a user trust gate when targets escape the workspace root.
Changes:
- Add Find/Replace-in-active-file overlay (Cmd/Ctrl+F, Cmd/Ctrl+R) with match navigation, replace/replace-all commands, and preview-window scrolling.
- Follow workspace symlinks (including directory listing) with metadata surfaced to the UI and a trust prompt for external targets (session/workspace trust).
- Extend the Tauri/HTTP API surface and persisted UI state to carry symlink/trust metadata and allow external symlink traversal when permitted.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tauri.ts | Extends frontend API wrappers and types for symlink metadata and external-symlink allowance. |
| src/tauri.test.ts | Updates API transport tests to assert the new allowExternalSymlinks argument is forwarded. |
| src/styles.css | Adds styling for symlink markers in the tree and the new Find/Replace overlay layout/actions. |
| src/EditorPane.tsx | Removes CodeMirror search keymap and adds editor-side replace application + match-range reveal selection. |
| src/editorCommands.ts | Introduces replace-related editor commands and payload types for match-targeted edits. |
| src/currentFileSearch.ts | Adds currentFileResultWindow helper for scrolling the match preview window with the active match. |
| src/currentFileSearch.test.ts | Adds tests for the new preview-window behavior and edge cases. |
| src/App.tsx | Implements Find/Replace overlay behavior, match navigation, replace command dispatch, and external symlink trust prompt/persistence. |
| src/App.test.tsx | Adds tests for keyboard-driven find/replace flow and external symlink trust prompt behavior; updates existing expectations for new args. |
| src-tauri/src/workspace.rs | Adds symlink metadata to FileEntry, enables safe symlink following with external trust gating, and ensures rename/delete affect links not targets. |
| src-tauri/src/workspace_index.rs | Ensures background indexing never follows external symlinks and populates default symlink metadata in indexed entries. |
| src-tauri/src/lib.rs | Threads allow_external_symlinks through Tauri commands and persists trust_external_symlinks; wires macOS Edit menu Select All. |
| src-tauri/src/http_server.rs | Keeps hosted API behavior consistent with “never follow external symlinks” for background/hosted endpoints. |
| src-tauri/src/git_attribution.rs | Updates path resolution for attribution with the new symlink-aware resolver + revised error reason. |
Comments suppressed due to low confidence (1)
src/tauri.ts:493
allowExternalSymlinksis only included ininvokeArgs, but hosted mode uses the request URL and doesn't serializeinvokeArgs. As a result, hostedread_filerequests cannot opt into following external symlinks after trust is granted.
const params = new URLSearchParams({ path });
if (maxOpenBytes !== undefined) {
params.set("maxOpenBytes", String(maxOpenBytes));
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57a2adc592
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
- Report a symlinked file's target mtime so saving through the link doesn't false-positive "file changed on disk" - Sort and de-overlap replace ranges before dispatch so CodeMirror can't throw on an out-of-order payload; give the change array an explicit type - Move the replace-toggle focus side effect out of the state updater - Drop Alt+Enter as a Replace All trigger; only Cmd/Ctrl+Enter - Replace All now recomputes over the full file, not the capped preview set Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
Clears RUSTSEC-2026-0190 (unsoundness in anyhow's Error::downcast_mut), which fails the cargo audit --deny warnings gate in CI. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
- Git attribution no longer follows symlinks outside the workspace, so it can't read external files past the trust gate (in-workspace links still work) - Thread the trust grant into the rename source resolution so renaming an entry inside a trusted external symlinked dir works like create/write - Clamp replace offsets to the line's start as well as its end, guarding against malformed negative column offsets - Bound Replace All to a sane match limit instead of an unbounded scan, and report when the cap is hit Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
The find input auto-closes the overlay on blur when the query is empty. Clicking the Replace toggle or focusing the replace input blurred the find input and could close the whole find/replace UI before the user typed anything. The blur handler now ignores focus moves that stay inside the find/replace group. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
Summary
Test plan
cargo test(159 pass) — new tests cover symlink resolution, logical-path listing for symlinked dirs, FileEntry symlink facts, and rename/delete of a symlink touching only the linknpm test(307 pass) — new tests cover the find/replace keyboard flow, the result-window math, and the external-symlink trust promptnpm run build,npm run menu:check.claude/skills) opens and expands; an external symlink shows the trust prompt and follows after trust; Cmd+F find/replace works with the editor focused🤖 Generated with Claude Code
https://claude.ai/code/session_014F5fq81S92ZZi59Xp9aGue