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:
@@ -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.
|
* Re-evaluate the shared badge for every item currently rendered in the Files list.
|
||||||
* Call this after the outgoing grants cache has been refreshed.
|
* Call this after the outgoing grants cache has been refreshed.
|
||||||
@@ -504,4 +519,4 @@ function refreshSharedBadges() {
|
|||||||
_component?.refreshSharedBadges();
|
_component?.refreshSharedBadges();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { addItem, filesView, loadFiles, refreshSharedBadges };
|
export { addItem, filesView, loadFiles, refreshSharedBadges, renderItems };
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* displays the enriched results returned by the server.
|
* displays the enriched results returned by the server.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addItem as filesViewAddItem, loadFiles } from '../../app/filesView.js';
|
import { loadFiles, renderItems } from '../../app/filesView.js';
|
||||||
import { app } from '../../app/state.js';
|
import { app } from '../../app/state.js';
|
||||||
import { ui } from '../../app/ui.js';
|
import { ui } from '../../app/ui.js';
|
||||||
import { getAuthHeaders } from './fileOperations.js';
|
import { getAuthHeaders } from './fileOperations.js';
|
||||||
@@ -198,15 +198,10 @@ const search = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render folders (server-provided enriched data)
|
// Render the whole result set (server-provided enriched data) in one
|
||||||
results.folders.forEach((folder) => {
|
// batched pass — per-item inserts would trigger a DOM scan, a smooth
|
||||||
filesViewAddItem(folder);
|
// scroll and a highlight pulse for each of up to 100 rows.
|
||||||
});
|
renderItems([...results.folders, ...results.files]);
|
||||||
|
|
||||||
// Render files (server-provided enriched data)
|
|
||||||
results.files.forEach((file) => {
|
|
||||||
filesViewAddItem(file);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user