perf(files): page the Files browser and resolve files without listing the workspace
Merged
WA
1d ago+5792+5792+0123456789012345678901234567890123456789-1456-1456-0123456789012345678901234567890123456789
Summary
Moves every workspace-file consumer off the full workspace list onto the paged, search and lookup reads from #9435, so a large workspace no longer downloads and re-downloads its whole file list.
Files browser
- Virtualized infinite table (the Logs pattern): one page of the open folder at a time, loading more on scroll, server-side sort (name, size, created, updated, type, owner), filters (kind, size, uploader) and workspace-wide search, with folder sizes from the folder-stats read
- Folders interleave with the paged files through the shared resource comparator, exactly as before; pinned files still lead their folder
- The server prefetch seeds the first page the URL asks for (or the open file's record) instead of "the whole list or nothing"
Everything else that read the whole list
- Single-file views (viewer, diff embed, mothership view, embedded file, share deep link) read the record by id, seeded from any cached read
- Chips, tab names, table file cells, the image-dimension adapter and stale-key recovery use batched lookups, classified per value
- Mentions, the
@/plus menus, add-resource, Cmd+K and search/replace use one picker hook: recent files, or server search once typing settles - The workflow file-upload picker pages its folder-scoped list; the sidebar flyout loads a folder level when it opens
- Writes patch only the reads they can't reorder; rename refetches lists and searches once it lands; content saves write the returned record through; unobserved lists are trimmed before invalidation
Server
- Kind, size and uploader filters and Type/Owner sorts on the paged list; Type and kind come from one set of rules shared with the project file browser (tables in
lib/workspace-files/browser.ts, SQL inlib/workspace-files/file-type-sql.ts), so both browsers classify files identically - Ranked search matching the palette: exact, prefix, substring or all words, typos via trigram similarity, word-start initials (run only when the indexed match comes up short), and files under matching folders
folder-statstakes several parents in one readResource.Tableseparates the load-more spinner from the sentinel gate (isFetching, defaulting toisLoadingMore, so other tables are unchanged)
The unpaged list response and the public v1 API are unchanged.
Type of Change
- New feature
- Other: performance
Testing
- Integration (real Postgres, migrate- and push-provisioned; workspace-files, uploads/workspace, projects/files suites): kind/size/uploader filters and bucket edges, filtered paging, Type and Owner sorts against Postgres collation in both directions, folder-index plan, JS/SQL type and kind parity for every known extension (plus unknown ones), ranked search (typos, word order, initials, folder paths, scope), multi-parent folder stats
- Unit: paging and cursor resume, re-sort placeholders (never another folder's rows), opening a file from the list without a request, deleted file reads as gone, rename optimism/rollback and post-rename order, content-save write-through, cache trimming and scoped patches, lookup batching and isolation, picker pending/settled states, stale saved filters, search placeholders scoped to the workspace, the table resuming paging after a background refetch
bun run lint,bun run check:audits(61),bun run type-check,check:test-patterns,check:migrations(no new migrations)- Not exercised in a browser yet: scrolling feel, drag-and-drop with virtualization, and the menus are covered by tests only
Checklist
- Code follows project style guidelines
- Self-reviewed my changes
- Tests added/updated and passing (new tests pass the
test-auditauthoring gate) - No new warnings introduced
- I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)