perf(files): breadcrumb no longer blocks the listing; ancestors cached
Every folder navigation awaited rebuildBreadCrumb() — one sequential fetch per ancestor level — before requesting the first page, so a depth-8 folder paid ~8 RTTs of dead time before the content even started loading. And nothing was cached: navigating between sibling folders re-fetched the same ancestors every time. - filesModel: session cache id → FolderItem for breadcrumb resolution (warm navigations rebuild the trail with zero fetches), invalidated on rename/move (single + batch) via invalidateFolderMeta. The trail is now built locally and committed atomically, guarded by a generation token so a rebuild superseded by a faster follow-up navigation can no longer interleave writes into the newer trail. - filesView.loadFiles: the rebuild runs concurrently with the first page fetch; crumbs, history and title update when it resolves. Navigation latency becomes max(listing, ancestor chain) instead of their sum — and ~equal to the listing alone once the cache is warm. The home-folder fallback for inaccessible targets is preserved by reloading the listing when the rebuild had to reset app.currentPath. https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
This commit is contained in:
@@ -438,9 +438,17 @@ async function loadFiles(options = { insertHistory: true }) {
|
||||
}
|
||||
}
|
||||
|
||||
await rebuildBreadCrumb();
|
||||
ui.updateBreadcrumb();
|
||||
updateHistory(options.insertHistory ?? true);
|
||||
// Rebuild the breadcrumb concurrently with the first page fetch —
|
||||
// the listing does not depend on it, and awaiting the ancestor
|
||||
// chain first added one round-trip per depth level before the
|
||||
// content even started loading. Crumbs, history and title update
|
||||
// when it resolves (skipped when superseded by a newer navigation).
|
||||
const requestedPath = app.currentPath;
|
||||
const breadcrumbReady = rebuildBreadCrumb().then((committed) => {
|
||||
if (!committed) return;
|
||||
ui.updateBreadcrumb();
|
||||
updateHistory(options.insertHistory ?? true);
|
||||
});
|
||||
|
||||
clearTimeout(spinnerTimeout);
|
||||
|
||||
@@ -457,6 +465,17 @@ async function loadFiles(options = { insertHistory: true }) {
|
||||
// Hand off to _loadPage (re-use cursor/groupBy state just reset above).
|
||||
_loading = false; // _loadPage sets its own guard
|
||||
await _loadPage({ isFirstPage: true });
|
||||
await breadcrumbReady;
|
||||
|
||||
// rebuildBreadCrumb falls back to the home folder when the
|
||||
// requested folder is inaccessible (deleted / permission revoked).
|
||||
// The old sequential flow got the home listing for free; reload to
|
||||
// match it.
|
||||
if (app.currentPath !== requestedPath) {
|
||||
ui.resetFilesList();
|
||||
_nextCursor = null;
|
||||
await _loadPage({ isFirstPage: true });
|
||||
}
|
||||
|
||||
// Deep-link: open a specific file if requested via app.viewFile.
|
||||
// We don't have a flat file list anymore (cursor pages), so only try
|
||||
|
||||
Reference in New Issue
Block a user