* feat(sidebar): add curated nodes sidebar tab with favorites
New "Curated Nodes" sidebar tab. Shows Koolook nodes auto-grouped by
subcategory under a "Nodes (favorites)" root. Users add favorites from
any installed pack from inside ComfyUI via:
- "+" button: adds the currently selected canvas node(s)
- Right-click any canvas node -> "Add to Curated Sidebar"
Picks persist in browser localStorage (per-browser, survives ComfyUI
restarts). Hover any entry -> "x" to remove. Search filter auto-expands
all folders so matches stay visible; pack/category folders are
collapsed by default otherwise to keep the panel compact.
"Download" export button copies current picks as JSON suitable for
web/curated_defaults.json -- committed to ship as the package's
defaults. On first ComfyUI load with empty localStorage, the seeder
reads curated_defaults.json into localStorage so fresh installs get the
curated set pre-loaded. Existing users keep their localStorage
untouched.
Pure JS, no build step. Uses the documented
app.extensionManager.registerSidebarTab API (stable since
ComfyUI_frontend v1.2.4).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(sidebar): document curated-defaults workflow + seed initial 9 picks
Adds docs/maintainers/curated-sidebar.md covering the full Export ->
paste -> commit flow for shipping distributed favorites with the
package, including storage-key reference, reset procedure, and the
intentional "seed once per browser" semantics.
CLAUDE.md gets a short pointer so the trigger ("update curated
defaults" or pasting an exported picks JSON) is discoverable on the
agent side too. Maintainer docs index linked.
Seeds web/curated_defaults.json with the maintainer's first 9 picks
(GetImageSizeAndCount, GetNode, ImageScaleBy, LoadEXR, NukeConstant,
NukeOCIOColorSpace, SaveEXRFrames, Text Multiline, easy showAnything).
Fresh installs from this version forward will get them pre-loaded into
their localStorage on first ComfyUI launch; existing users' picks are
untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(sidebar): trim curated-sidebar.md to a one-screen quick reference
Drops the verbose data-flow diagram and the localStorage-vs-userdata
rationale. Keeps the four action steps, the storage-key map, the reset
block, and the four "things that surprised us" footnotes capturing the
non-obvious bits (paste is manual, other users can't reach the repo,
seed-once-per-browser, no publish on web/ pushes). 81 lines removed,
20 added; same file path so existing links still resolve.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(sidebar): workflows section with archive + iteration docs
Adds a Workflows section to the Curated Nodes sidebar tab, sharing the
search field and tree pattern with the existing nodes feature.
Capabilities:
- Save entire canvas or current selection as a workflow.
- Save modal: New name / Use existing name (archive previous) /
Modify existing name. The Workflow-name field is shown only when
needed; "Base on existing" dropdown drives prefill for Use/Modify.
- Auto-archive on same-name save: previous version renamed with
timestamp suffix and flagged archived; new entry takes the slot.
Archive sub-folder appears in each directory only when populated and
is rendered above the active list, so the latest sits at the bottom.
- Right-click on workflow: Load / Rename / Move to other directory /
Move to archive (or Restore from archive) / Delete.
- Right-click on directory: Rename / Delete (with confirm if non-empty).
- Loading a workflow names the tab via loadGraphData's 4th arg
(verified against ComfyUI_frontend src/scripts/app.ts) so the tab
flips from "Unsaved Workflow (N)" to the saved name.
Storage: ComfyUI /userdata/koolook_workflows.json (primary), with a
localStorage fallback if /userdata is unreachable. Distributed defaults
seeded from web/workflow_defaults.json on first ComfyUI load with empty
workflow data -- same one-time-per-install pattern as
curated_defaults.json.
Other panel changes:
- Three labeled rows above the tree (search; NODES action bar;
WORKFLOWS action bar), with thin dividers between them and before
the tree.
- Folder expansion state preserved across re-renders via a pathStates
Map; saving never collapses the directory you were viewing.
- Alphabetical (case-insensitive) sort everywhere via a single
compareNames helper -- pack names, subcategories, directories,
workflow names, dropdown options.
- "Use existing name (archive previous)" action label includes the
parenthetical hint about archive behavior.
Docs:
- New docs/maintainers/dev-iteration-loop.md captures the
dev-sync->restart->verify pattern, agent trigger phrases, persistence
boundaries, and push/publish gates. First read when returning to UI
work after a break.
- New docs/maintainers/workflows-sidebar.md covers workflow storage,
action semantics, archive behavior, and reset procedure.
- docs/maintainers/README.md indexes both.
- CLAUDE.md "dev-sync" section now points at the iteration-loop doc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(sidebar): address review findings — seed gating + commit checks
Targets the data-integrity findings from the multi-agent PR review:
- BLOCKING: seed flag is now only set after the underlying save/persist
succeeded. `saveUserPicks` returns a boolean; both seeders bail out
(leaving the flag unset for next-load retry) on storage failure.
Previously a transient storage failure during first-load could
permanently strand a user with an empty store and the seeded flag set.
- BLOCKING: `normalizeWorkflowsStore` now strictly validates each
workflow entry — drops entries missing a graph object, coerces
`archived` to a strict boolean (so a stray "false" string can't
accidentally hide a workflow), and logs the dropped count.
- HIGH: save handlers now check `commit()`'s return value and surface a
"Save failed" toast when both /userdata and the localStorage fallback
rejected the write. Previously the user saw "Saved" even though
nothing was persisted. The canvas and selection handlers share a
`saveAndToast` helper to keep the failure path consistent.
- HIGH: `saveCanvasBtn` now checks `canvasIsNonEmpty()` before
serializing. A `serialize()` throw is no longer misreported as
"Canvas is empty." — distinct toasts for distinct failure modes.
- HIGH: `loadWorkflowsStore` now logs (instead of silently swallowing)
parse failures on the localStorage fallback, and warns when /userdata
is reachable but a stale fallback blob still exists from an earlier
outage — gives the user a recovery breadcrumb.
- HIGH: `loadUserPicks` logs parse failures so a corrupt picks blob is
visible in the console before the next save overwrites it.
Skipped from the review with rationale:
- multi-tab seed race: sub-second window, both tabs write identical
defaults, no observable wrong state — navigator.locks not worth it
- splitting `koolook_sidebar.js` and decomposing `renderPanel`:
refactor scope, deferred to follow-up
- `loadGraphData` rename comparison: agent's claim was wrong; existing
`wf.filename !== wfName` matches the upstream `filename` shape (bare
name, no extension) we verified earlier against ComfyUI_frontend
v1.44.x source
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(sidebar): second-pass review fixes — rollback, idempotency, corrupt-file handling
Addresses the residual findings from the post-625aa97 review pass.
BLOCKING:
- patchCanvasMenu is now idempotent. Adds a __koolookCuratedPatched
sentinel on LGraphCanvas.prototype so a soft-refresh / re-import
doesn't stack duplicate "Add to Curated Sidebar" entries on every
node's right-click menu. (LGraphCanvas is shared with every other
pack, so non-idempotent prototype mutation was visible globally.)
HIGH:
- persistWorkflowsToServer now returns "server" | "fallback" | false
instead of just true/false. seedWorkflowDefaultsIfNeeded only marks
WORKFLOWS_SEEDED_KEY when the seed actually reached /userdata —
previously a fallback-only seed (when /userdata was unreachable on
first install) marked the user as "seeded" and they would have an
empty server file forever after recovery.
- All 8 commit() callsites that mutate workflowsCache (saveAndToast,
move, archive, restore, rename workflow, delete workflow, rename
directory, delete directory + delete-non-empty-directory, create
directory) now go through a new persistMutation helper that:
- snapshots the cache before mutation
- bails (toast no-op) if mutate returns false
- rolls back the cache + re-renders if commit() returns false
- toasts persistFailedMessage so the user isn't told a write
succeeded when both /userdata and the localStorage fallback
rejected it
Pairs with snapshotCache() helper for explicit snapshot/restore.
- addToMyPicks tri-state ("added" | "duplicate" | "failed") so callers
can distinguish "already in favorites" from "save failed" on
localStorage quota / private-mode failure. Both call sites (the +
toolbar button and the right-click "Add to Curated Sidebar" menu
entry) updated to handle all three states.
- fetchWorkflowsFromServer now distinguishes a network failure from a
parse failure of an existing-but-corrupt /userdata file. Returns
SERVER_FILE_CORRUPT (a Symbol) on parse error; loadWorkflowsStore
surfaces a toast and returns { corrupt: true }; setup() skips
seedWorkflowDefaultsIfNeeded entirely so we don't auto-overwrite a
recoverable-but-unreadable file with stock defaults.
- loadWorkflowOntoCanvas snapshots the current canvas before
app.loadGraphData and restores it on throw. Previously a workflow
with a now-unregistered node type could destroy the canvas partway
through and leave the user with neither the old graph nor the new.
- Stale localStorage fallback detection (when /userdata is reachable
but a fallback blob from a previous outage is still present) now
surfaces a toast in addition to the console warning, so a user who
doesn't have DevTools open gets a recovery breadcrumb.
MEDIUM:
- Modal escape-handler leak fixed via centralized close(). Every
dismissal path (Escape key, overlay click, Cancel/OK calling
overlay.remove()) now tears down the document-level keydown listener.
Existing call sites that use overlay.remove() keep working — the
remove method itself routes through cleanup.
Skipped from the review with rationale:
- subcategoryFor cosmetic mismatch fallback at L1000-1008 — silent
rewrite is rare-path defensive, not load-bearing; defer.
- Optional toast on seedDefaultsIfNeeded storage failure — console
warning is sufficient for a development-time scenario; defer.
- File size / module split (1864 lines) — refactor scope, deferred to
follow-up per prior commit message.
- Magic sentinel strings ("__new__", action enum strings) — pure
cleanup; defer.
- dataset.expanded boolean-string compare — pathStates Map already
carries booleans; cleanup defer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>