Repository navigation
feat(files): load the Files panel one folder at a time, sort it, filter locally - #633
Draft
shenlvkang-collab wants to merge 3 commits into
Draft
shenlvkang-collab wants to merge 3 commits into
shenlvkang-collab wants to merge 3 commits into
Conversation
The path picker (mobile Path key) is the fast way to browse: one folder per request, any allowed root. It could not move files, so the Files panel was still needed for that, and a desktop browser could not reach the picker at all. - GET /api/filesystem/download: any regular file the picker can list, sent as an attachment. - POST /api/filesystem/upload: one multipart file into the folder on screen. Same-origin check (shared with paste-image), per-IP rate bucket, bounded folder probe, sanitized basename, blocked-name check, O_EXCL|O_NOFOLLOW creation with " (n)" suffixes (never overwrites, never follows a planted symlink), partial files unlinked. 1GB cap (CODEMAN_MAX_PICKER_UPLOAD_BYTES). - Both resolve through resolveFilesystemPickerPath(), so they reach exactly what browse can list, including the multi-user root scoping. - Picker `fileTransfer` option: a download link per file row and an Upload button (multi-select, XHR progress) that reloads the folder and selects the last upload. On for the Path key and the new quick-browser button in the Files panel header; Link Existing stays browse-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The key already exists further down the table; an object literal keeps the last one, so the first copy was dead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er locally Opening the Files panel walked the whole workspace (depth 5, up to 5,000 nodes) on every open, refresh and session switch, and every keystroke in its search box re-walked it on the server. On WSL /mnt/d (9P, a stat per entry) that measured 13.6-14.1 s to open a 3,700-file workspace and 4.3 s per search, against 0.34-0.47 s for the path picker's one-folder listing of the same root. - `GET /api/sessions/:id/files?lazy=1[&path=<dir>]` lists one directory: folders without `children` (not loaded), `mtimeMs` on every entry, workspace-confined through validateSessionFilePath(), hidden segments need showHidden. The non-lazy response is unchanged. - The panel loads the root lazily and fetches a folder when it is expanded (loading row, error row that retries on collapse/expand, epoch-guarded). - A sort menu (name A-Z/Z-A, newest/oldest first, folders first), per device, same modes as the path picker. - Typing filters the already-loaded nodes in the browser with no request (ordered-substring `*` match, never a RegExp); Enter runs the existing workspace search. A folder hit the lazy tree has not loaded yet is opened by loading each ancestor in turn. Expand All opens only loaded folders. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Opening the Files panel walks the whole workspace (depth 5, up to 5,000 nodes) on every open, refresh and session switch. Every keystroke in its search box (after the 250 ms debounce) walks it again on the server. On a slow mount this is the whole cost of the panel. Measured on WSL
/mnt/d(9P, a stat per entry) against one real workspace (3,723 files, 1,277 dirs, truncated):What changed
GET /api/sessions/:id/files?lazy=1[&path=<workspace-relative dir>]lists one directory.children, which means "not loaded".mtimeMs.pathis workspace-confined throughvalidateSessionFilePath()(realpath).showHidden.node_modulescosts one row.*, never a RegExp).N matches in loaded folders · Enter searches the whole workspace.Validation
New
test/routes/file-tree-lazy.test.ts(6, real temp dirs) covers: one-level shape and order, mtime, generated folders listed, subfolder paths, hidden opt-in, traversal and symlink escape refused, missing folder (404) and a file (400), and the unchanged default tree.New lazy block in
test/file-browser-search.test.ts(6) covers:The existing 94 tests in that file pass; 9 expected root-load URLs changed from
depth=5tolazy=1.The file-routes, path-picker, hidden-toggle and five i18n suites pass (292 tests across 14 files). Typecheck, lint, Prettier and frontend syntax pass.
Playwright against a built server, 13 checks:
🤖 Generated with Claude Code