perf(search): render result sets in one batched pass

displaySearchResults inserted up to 100 results one at a time via
addItem({ scroll: true, highlight: true }) — each insert ran two DOM
scans (duplicate guard + row lookup), a smooth scrollIntoView and a
highlight pulse, so every live-search keystroke triggered ~100 competing
smooth-scrolls and O(n²) container scans.

Add filesView.renderItems(): a single component.render() pass (one
DocumentFragment, zero per-item scans/scrolls). addItem keeps the
scroll/highlight affordances for its documented purpose — optimistic
single-item inserts after upload/create.

https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
This commit is contained in:
Claude
2026-06-10 09:01:42 +00:00
parent 3c64354a0d
commit ca698f73d1
2 changed files with 21 additions and 11 deletions
+16 -1
View File
@@ -496,6 +496,21 @@ async function loadFiles(options = { insertHistory: true }) {
}
}
/**
* Replace the list contents with a whole result set in one batched render
* (single DocumentFragment pass — no per-item DOM scans, smooth-scrolls or
* highlight pulses). Used by search to display results; optimistic
* single-item inserts should keep using `addItem`.
*
* @param {Array<FileItem|FolderItem>} items
*/
function renderItems(items) {
const component = _ensureComponent();
if (!component) return;
document.getElementById('files-container-error')?.classList.add('hidden');
component.render(items);
}
/**
* Re-evaluate the shared badge for every item currently rendered in the Files list.
* Call this after the outgoing grants cache has been refreshed.
@@ -504,4 +519,4 @@ function refreshSharedBadges() {
_component?.refreshSharedBadges();
}
export { addItem, filesView, loadFiles, refreshSharedBadges };
export { addItem, filesView, loadFiles, refreshSharedBadges, renderItems };