v0.39.0 — Gallery event delegation
v0.39.0 — Gallery event delegation
The last big perf hit on huge libraries: every tile attached its own click / contextmenu / dragstart / dragend / dragover / dragleave / drop handlers, plus a checkbox.onclick on the inner element. For a 700-tile library in Manual sort mode that created ~4900 handler closures per render, each capturing checkedIds / syncWidget / render / refresh / openPromptModal / etc. via lexical scope. Render-then-discard cycle hit GC hard on every filter or sort change.
What changed
Replaced with a single set of grid-level delegated handlers attached once per gallery instance. Each delegated handler:
- resolves the affected tile via
e.target.closest('.pl-tile') - reads
dataset.promptId - looks up the entry via
prompts.find/lastVisible.findIndex - routes to the same logic as before
Five new helpers (private to buildGallery)
_entryFromTile(tile) → entry from prompts[]
_indexFromTile(tile) → index in lastVisible[]
_toggleSelectionAt(id, ...) → plain + shift-range click
_openAddPrompt() → '+' tile click handler
_openTileContextMenu(e, p) → right-click menu builder
Per-tile creation cost
| Operation | Before (per tile) | After (per tile) |
|---|---|---|
createElement + setAttrs |
yes | yes |
| Closure constructions | 7–9 | 0 |
| Inner-DOM build | yes | yes |
For 700 tiles in Manual sort: ~4900 closures NOT created on render.
Cumulative gallery perf wins (v0.36 → v0.39)
| Action on 700-entry library | v0.36 | v0.39 |
|---|---|---|
| Type one search character | full grid rebuild + 4900 closures | debounced 80 ms + DocumentFragment + 0 closures |
| Click a tile to select | full grid rebuild + 4900 closures | one tile's classList toggled |
| Shift-range select 100 tiles | full grid rebuild + 4900 closures | 100 tiles' classes toggled |
| Arrow-key navigation | full grid rebuild + 4900 closures per keystroke | 2 DOM mutations per keystroke |
| Bulk clear selection | full grid rebuild + 4900 closures | N tiles' classes removed |
| Sort change | full grid rebuild + 4900 closures | full rebuild + 0 closures (correct order needed) |
| Right-click for context menu | per-tile closure already there | one delegated handler per gallery |
Search-as-you-type, click-to-select, and arrow-key nav on 500–700 entry libraries should feel instant. Every interaction except a genuine reorder is now O(few-DOM-mutations) instead of O(visible-tiles).
Backward compat
All changes internal — no socket / widget / workflow JSON shape changes.
Install / upgrade
cd ComfyUI/custom_nodes/ComfyUI-GrimmRibbity
git pull
# restart ComfyUI / hard-refresh browser tab