Repository navigation
feat(files): Files pane with tree, preview and add-to-prompt (#1506 H1) - #1508
Conversation
Lazy per-directory listing that drops gitignored entries unless asked, a whole-tree list for the filter, and a read-only preview with a 1 MB cap, binary sniff and image data URLs. Both reuse resolveUnderRoot and add a realpath check so a symlink in the checkout cannot read outside it. Remote projects are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Files pane picks reuse the review-comment chip. An `excerpt` flag keeps them in the prompt without a note (blank diff comments are still dropped), and the chip shows an 'Add a note' hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lazy tree of the thread checkout: directories load on expand, a toggle shows gitignored entries, a fuzzy filter searches the whole tree, and arrows/Enter/Home/End drive it. The preview shows highlighted read-only text (lazy highlighter), rendered markdown with a source toggle, images, and too-large/binary states. Click and shift-click line numbers, then Add to prompt to send the range as a composer chip. Open in uses the editor the worktree menu remembers. Loaded with lazyNamed, so only a small stub lands in the main chunk. Remote projects say Files is not available. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/App.tsx # src/components/ThreadView.tsx
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8c6445e996
ℹ️ 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".
| if (rel.startsWith("..") || path.isAbsolute(rel)) { | ||
| throw new Error("Path is outside the thread workspace"); | ||
| } | ||
| return { abs: resolved, rel: path.relative(root, resolved) }; |
There was a problem hiding this comment.
Open the verified path without a symlink race
When another process is modifying the checkout, it can replace a workspace symlink after realpath(resolved) passes but before readPreview calls stat/readFile: this function returns the unchecked lexical resolved path rather than an already-open, verified file handle. A sandboxed workspace process can therefore race a preview request and make the Electron main process read a file outside the workspace, defeating the newly added realpath guard; open the target without following a replacement symlink and validate/read through that same handle.
Useful? React with 👍 / 👎.
| const all = await fs.promises.readdir(root, { | ||
| recursive: true, | ||
| withFileTypes: true, | ||
| }); |
There was a problem hiding this comment.
Bound the fallback traversal before materializing entries
When the Files filter is used in a non-git Scratch workspace, readdir({ recursive: true }) walks and materializes the entire directory tree before the 20,000-file slice is applied. A Scratch folder containing a large generated dependency or build tree can therefore consume unbounded main-process memory and block the IPC request despite the documented whole-tree cap; use an incremental walk that stops once the cap is reached.
Useful? React with 👍 / 👎.
| async function resolveRealUnderRoot(root, raw) { | ||
| const resolved = resolveUnderRoot(root, raw); |
There was a problem hiding this comment.
Allow in-workspace names beginning with two dots
For a legitimate workspace file or directory named ..foo, the inherited resolveUnderRoot check treats path.relative(root, resolved) === "..foo" as an escape because it tests startsWith(".."). The tree lists such an entry, but this added preview/directory-resolution path then rejects it as outside the workspace; the containment check should distinguish the .. path segment from ordinary names beginning with two dots.
Useful? React with 👍 / 👎.
Item H1 of #1506. This ships the declared but unshipped
filespane (in the Views menu).What changed
.gitis never shown.openIn.js.Security:
files:treeandfiles:readgo throughresolveUnderRoot, plus a new realpath check. The existing check alone would have let a symlink inside the checkout read outside it. Tests cover../, outside absolute paths, and symlinked files and dirs.Verified independently, on main merged with this branch and #1507:
vite buildpasses.Merge note: the branch includes a merge of main resolving prop conflicts with #1505 in
App.tsxandThreadView.tsx(kept both sides).🤖 Generated with Claude Code