* feat(sidebar): right-click menu pack — Duplicate, Tags, + New directory move Three sibling sidebar slices bundled because they all extend the same workflow-row right-click menu in `web/sidebar/tree.js` and share a small schema/store surface in `workflows_store.js`. Refactor cleanup is held back for a separate PR (issue #47) so this diff stays a "no behavior change to existing flows" feature add. Changes ------- - Right-click "Duplicate…" → name modal with `<name> (copy)` default; deep-clones the source graph into a new entry in the same directory via `saveWorkflowEntry`. The duplicate inherits the source's tags so the user's categorization carries over. Same-name duplicates fall through to the existing archive-on-collision behavior. Closes #58. - Right-click "Tags…" → chip-row modal with add/remove. Each edit fires its own `persistMutation` so changes survive a mid-edit close. New `showTagsModal` in `modals.js` mirrors the existing `showInputModal` / `showConfirmModal` shape (escape-key teardown, overlay-click dismissal centralized in `makeModalShell`). - New Tags sidebar section between Workflows. Flat list of tag folders; each tag holds the workflows carrying it (sorted A→Z). Click loads the workflow from its real path. Archived workflows are filtered out of the section so the active view stays clean — their tags persist on the entry, so a restore brings them back. Search matches tag name OR workflow name. - Right-click "+ New directory…" / "+ New subdirectory under <path>…" in the Move-to flow. Both create a fresh directory and move the workflow into it as one atomic mutation; if the move can't land, the new directory is rolled back so the cache never leaks an empty orphan. Closes #57. Schema additions (back-compatible) ---------------------------------- - Workflow entries gain optional `tags: string[]`. `normalizeDirNode` trims, drops empties, and dedupes case-sensitively, so old entries without a tags field load as `tags: []` and the rest of the code can assume the field always exists. Pre-v0.3 stores load fine. - New exports in `workflows_store.js`: `getWorkflowTags`, `addTag`, `removeTag`. `getWorkflowGraph` is now also exported so the duplicate flow can deep-clone without reaching into the cache directly. Why one PR for three issues --------------------------- All three slices touch `workflowRowContextMenu` in `tree.js` and the mutator API in `workflows_store.js`. A coherent UX-feature scope ("next batch of workflow menu enhancements") reviews more cheaply as one diff than three near-identical PRs. Issue #47 (refactor / tidiness pass) stays separate because mixing refactor and feature in the same diff defeats the point of the split — that work is queued for a follow-up PR. Testing ------- - `python tools/preflight_release.py --skip manager-meta -v` PASS (static / dispatch / workflows fixtures, all 3 stages). - `node --input-type=module --check` passes on each modified JS file. - Browser smoke test: pending — needs a ComfyUI session for the user to verify the three menu actions and the Tags section against a real workflow store. The CI gates are Python-only and don't catch JS regressions in the sidebar by themselves. Related ------- - Closes #57 (move-to: + New directory…) - Closes #58 (Duplicate) - Implements the right-click + section parts of #56 (Tags); the issue body's `addTag` / `removeTag` / `listAllTags()` ops landed under slightly different names (`addTag`, `removeTag`, `gatherTags`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sidebar): address review findings on PR #59 Self-review pass against the validated agent findings. Five fixes total across the three issues' surface, holding refactor/file-size concerns for follow-up #47 widening. Fixes ----- - **HIGH** `normalizeDirNode` now rejects non-string `tags` elements outright instead of `String(raw).trim()` coercing them. `null` / `undefined` / `true` / `123` no longer silently surface as the literal tag strings `"null"` / `"undefined"` / etc. — `stats.dropped` increments instead so the existing console warning catches malformed migrations. Closes review HIGH-1. - **HIGH** Tags modal now distinguishes "no tags yet" from "workflow no longer exists." The `getCurrentTags` adapter no longer collapses `null` to `[]`, and `renderChips` renders an explicit gone-state with disabled add input + descriptive placeholder. Prevents users typing into a doomed modal when a concurrent action (another tab, drag-and-drop) deletes the workflow under them. Closes review HIGH-3. - **MEDIUM** Duplicate flow re-validates source existence at submit-time inside the `mutate` callback. Without it, a concurrent delete of the source between menu-open and submit would let `saveWorkflowEntry`'s `ensureDirectoryAtPath` silently re-create the parent directory and paste a clone of the captured graph — effectively undoing someone else's delete. Now returns `false` on missing source, surfacing "Could not duplicate — <name> no longer exists." Closes review MEDIUM-6. - **MEDIUM** Duplicate flow now routes inherited tags through the public `addTag` mutator instead of writing `dir.workflows[newName].tags` directly. Keeps the tree.js UI layer outside the store's internal shape. The whole sequence still rides on one `persistMutation` snapshot — commit failure rolls back save + tag adds together. Closes review MEDIUM-5. - **LOW** `gatherTags` comment block clarifies that grouping is case-sensitive but search filter is case-insensitive substring. The comment previously said "Comparison is case-sensitive" which was technically true for grouping but misleading on its own. Skipped with rationale ---------------------- - **HIGH-2 modal cache desync on persist failure** — agent analysis was wrong on trace. `renderChips` reads from cache, X-click does not optimistically remove the chip from DOM, and `restore()` reverts cache to pre-mutation. DOM and cache stay consistent across every commit-failure path. Cross-tab updates aren't reflected, but that's a project-wide cache-coherency concern, not specific to this PR. - **HIGH-4 tree.js size** — real architectural concern but follow-up only. Track on #47 widening (or a sibling extraction issue). Adding extraction in this PR would mix refactor with feature. - **MEDIUM-7 tag API asymmetry** — agent claimed `addTag`'s parameter reassignment differed from neighbors, but verified the existing convention IS parameter reassignment (`addDirectory`, `renameDirectory`, `renameWorkflow`, `moveDirectory` all do `name = (name || "").trim();`). The defensive-copy difference between `getWorkflowTags` and `getWorkflowGraph` is intentional. - **MEDIUM-8 top-level "Archive" name** — pre-existing semantic shared by toolbar Create-directory and save-modal "+ New directory." Restricting only the right-click new-dir entry would be inconsistent. Filing as a separate cross-cutting issue is the cleaner path. - **LOW magic strings / modals.js size** — both queued for #47. Validation ---------- - `python tools/preflight_release.py --skip manager-meta -v` PASS. - `node --input-type=module --check` on all three modified JS files — clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ComfyUI-Koolook-Nodes
This repository provides custom nodes for ComfyUI tailored for VFX and AI image/video generation workflows.
For change history see CHANGELOG.md. For workflow conventions and naming see CLAUDE.md and the glossary. Full project documentation lives under docs/.
Koolook nodes (root package)
| Node | Display name | Category | Source |
|---|---|---|---|
EasyWan22Prompt |
Wan 2.2 Easy Prompt (Koolook) | Koolook/Wan_Video |
k_easy_wan22_prompt.py |
EasyResize_Koolook |
Easy Resize (Koolook) | Koolook/Image |
k_easy_resize.py |
EasyAIPipeline |
Easy AI Pipeline (Koolook) | Koolook/Pipeline |
k_ai_pipeline.py |
easy_ImageBatch |
Easy Image Batch (Koolook) | Koolook/Image |
k_easy_image_batch.py |
KoolookLoadCameraPosesAbsolute |
Koolook Load Camera Poses (Absolute Path) | Koolook/Camera |
k_easy_track.py |
Brief descriptions:
- Wan 2.2 Easy Prompt: Dynamic prompt builder driven by
config.json(light source, lighting type, time of day, etc.). Outputs a comma-separated prompt string and optional CLIP conditioning. - Easy Resize (Koolook) —
EasyResize_Koolook: Aspect-aware image resize that enforces divisibility (e.g.,32) for model compatibility. Modes: stretch, letterbox, pillarbox; padding and cropping supported. Originally inspired by theResize Image V2node from kijai/ComfyUI-KJNodes (GPL-3.0 — seeforks/THIRD_PARTY.mdfor the full attribution + change log). The legacy bare-nameEasyResizeID is kept as a deprecated alias for saved-workflow compatibility. - Easy AI Pipeline: Aggregates VFX shot parameters (shot duration, seed, base path, shot name, AI method, version) into a fully formatted output file path with overwrite protection. For organized writes from Save EXR / Save Image.
- Easy Image Batch: Builds an
IMAGE+MASKbatch of lengthtotal_framesfrom 1–4 keyframe images placed at explicit frame indices (withstart_frameoffset). Empty frames are filled with black/gray and the mask marks empty=white / occupied=black. Useful for sparse keyframe control with Wan-style video models. - Koolook Load Camera Poses (Absolute Path): Loads RealEstate10k-style
CAMERACTRL_POSESfrom any absolute path (no ComfyUI path stripping), with overrideable pose width/height. Pairs with AnimateDiff CameraCtrl downstream.
Radiance Koolook nodes (v2_3_3 fork — video-friendly VAE)
These come from forks/radiance_koolook/versions/v2_3_3/ and load through the root __init__.py. They are slim, rank-agnostic re-implementations of the upstream Radiance v2.3.3 VAE encoder/decoder that work correctly with video VAEs (Wan 2.2, Hunyuan, CogVideoX, LTX) — they skip the upstream's 4K cosine-blend tile engine, which conflicts with video VAEs that already handle their own internal stitching.
| Node ID | Display name | Category |
|---|---|---|
Easy_hdr_VAE_encode |
Easy HDR VAE Encode (Koolook) | Koolook/VAE |
Easy_hdr_VAE_decode |
Easy HDR VAE Decode (Koolook) | Koolook/VAE |
History note: the v0.1.0–v0.1.4 releases also shipped a much larger
versions/v1_0_1/fork that wrapped 26 Radiance v1.0 classes (HDR, EXR, OCIO, log curves, etc.) under the__koolook_v1_0_1namespace. Those wrappers were vestigial — never used by Koolook authors — and were removed in v0.1.5. Users who want that functionality should install upstream Radiance directly. Seeforks/THIRD_PARTY.mdfor full attribution and removal rationale.
See forks/README.md for the full fork workflow.
Features
- Dynamic Inputs for Prompts: Fields and dropdowns in Wan 2.2 Easy Prompt are defined in
config.json, allowing easy customization without code changes—great for VFX artists iterating on lighting or camera parameters. - Image Resizing with Aspect Control: Easy Resize ensures precise resolutions for AI models, with options for keep_proportion preservation, padding colors, and device selection (CPU/CUDA). Supports masks for compositing in VFX.
- Pipeline Path Generation: Easy AI Pipeline assembles VFX-style output paths and filenames from shot/version inputs, with optional version disabling and overwrite protection.
- Sparse Keyframe Batches: Easy Image Batch constructs aligned
IMAGE+MASKbatches from up to four keyframe images, ready for sparse video-model control. - Camera Pose Loading: Koolook Load Camera Poses (Absolute Path) reads RealEstate10k pose TXT files from any disk location and emits
CAMERACTRL_POSES. - Startup Loading: Nodes load configurations at ComfyUI launch; edit
config.jsonand restart to update. - Output Formatting: Wan 2.2 Easy Prompt outputs comma-separated strings (skipping "none"), compatible with prompt builders. Easy Resize outputs resized images, masks, and width/height integers.
- Error Handling: Fallbacks for missing/invalid JSON in Wan 2.2 Easy Prompt; validation for aspect ratios and divisibility in Easy Resize.
- Compatibility: Works with ComfyUI-Manager for easy installation via Git URL. The Koolook root nodes and the slim v2_3_3 VAE wrappers have no extra Python dependencies beyond
torch(already required by ComfyUI itself).
RunPod
RunPod management has been moved to a separate private repository for active operations and iteration.
- Runtime/deployment scaffolding is no longer maintained in this public repo.
- This public repo remains focused on the ComfyUI nodes/plugin code.
Release & Stability
For production usage, install a pinned stable tag/commit instead of following moving branch heads.
- Release procedure:
docs/maintainers/releasing.md(canonical, step-by-step) - Comfy Registry API (deprecate / yank versions):
docs/maintainers/registry-api.md - Node versioning rules (how to change a node without breaking saved workflows):
docs/maintainers/node-versioning.md - Per-release checklist template:
.github/ISSUE_TEMPLATE/release_checklist.md - Change history:
CHANGELOG.md - Fork tracking:
forks/THIRD_PARTY.md,forks/forks_manifest.yaml - Fork workflow guide:
forks/README.md
Tagging Policy (from now on)
- Repo releases:
vMAJOR.MINOR.PATCH(example:v0.2.0). - Fork code versions in paths use underscore format:
v1_0_1,v2_3_3. - Comfy node ID namespace suffixes match fork version paths:
__koolook_v1_0_1. - External raw checkout naming:
- pinned:
<repo>-v<version>-koolook - rolling upstream:
<repo>-main-upstream
- pinned:
Sibling Projects
Some related work lives in sibling folders/repositories outside MAIN. They are consulted (read-only knowledge sources, references, etc.) but never imported by MAIN at runtime. To keep the public repo free of machine-specific absolute paths, every sibling is referenced through an environment variable.
Conventions
- Variable prefix:
KOLOOK_*(matches the existingKOLOOK_FORKS_DIR). - Real paths live in a local, gitignored
.envfile. The committed.env.exampledeclares the variable names with placeholder values. - Public docs and committed code reference siblings only via the env var name (and an optional portable relative default like
../<folder>). Never hardcode an absolute path with a username. - A sibling project is a runtime-optional reference. If the variable is unset and no default is documented, agents/code must treat the sibling as unavailable and continue gracefully.
Registered siblings
| Role | Env var | Portable default | Status |
|---|---|---|---|
| External forks root (raw upstream clones) | KOLOOK_FORKS_DIR |
../ComfyUI-Forks |
documented in forks/forks_manifest.yaml |
| ComfyUI knowledge database | KOLOOK_COMFYUI_KB_DIR |
../ComfyUI_knowledge___DB |
read-only knowledge source |
| Personal knowledge database | KOLOOK_PERSONAL_KB_DIR |
(no default — user-specific) | optional |
Local setup
cp .env.example .env
# Edit .env to point at the real paths on your machine.
Do not commit .env. The default .gitignore already excludes .env, .env.local, and .env.*.local.
External Fork Workflow (No Vendoring in MAIN)
This repository is the MAIN control repo. Large third-party repositories must stay outside MAIN.
- Default external forks root (relative):
../ComfyUI-Forks - MAIN stores only:
- wrappers under
forks/ - provenance/tracking under
forks/
- wrappers under
- MAIN does not store full external trees (no vendored copies).
Radiance Koolook Version Layout
- Package entry path:
forks/radiance_koolook/__init__.py - Active version path:
forks/radiance_koolook/versions/v2_3_3/ - Modified node source is local and tracked in MAIN:
forks/radiance_koolook/versions/v2_3_3/nodes_vae.py
- External checkout is reference-only raw upstream:
- default location:
../ComfyUI-Forks/radiance-v2.3.3-koolook - baseline used for comparison: upstream commit
f262f47ddfda01ece154bf80c22769b1e4cef795(the v2.3.3 release commit; the cleanest tag pointing at it is the typo'dradinace2.3.3, so we pin by SHA)
- default location:
- Node IDs in the v2_3_3 fork are exposed verbatim as
Easy_hdr_VAE_encode/Easy_hdr_VAE_decode(the__koolook_v2_3_3namespace suffix is skipped viaSKIP_VERSION_SUFFIXinversions/v2_3_3/__init__.pybecause the Koolook-specific names already differentiate them from upstream). - Each version folder should include
UPSTREAM_PIN.yamlto keep parity with external pinned references. - The historical
v1_0_1folder was removed in v0.1.5; seeforks/THIRD_PARTY.md.
Version Pinning (Portable Across PCs/macOS)
- Track every external repo in
forks/forks_manifest.yamlwith:source_ref(tag/version)pinned_commit(exact hash used in production)external_checkout.relative_path_from_forks_root
- Keep checkout folder names versioned, e.g.:
radiance-v1.0.1-koolookradiance-v2.3.3-koolook
Machine Setup Example
From the parent directory that contains ComfyUI-Koolook:
mkdir -p ../ComfyUI-Forks
cd ../ComfyUI-Forks
git clone https://github.com/fxtdstudios/radiance.git radiance-v1.0.1-koolook
cd radiance-v1.0.1-koolook
git checkout f1b8ae330848fa08aba24c9d3e355cb432d3515b
This external folder is for upstream comparison/sync work only.
Installation
Via ComfyUI-Manager (Recommended)
- Open ComfyUI and access the Manager interface.
- Go to "Install Custom Nodes" > "Install via Git URL".
- Paste the repository URL:
https://github.com/malkuthro/ComfyUI-Koolook.git. - Install and restart ComfyUI.
- Nodes appear under these categories in the node search:
Koolook/Camera— Koolook Load Camera Poses (Absolute Path)Koolook/Image— Easy Resize (Koolook), Easy Image Batch (Koolook)Koolook/Pipeline— Easy AI Pipeline (Koolook)Koolook/VAE— Easy HDR VAE Encode/Decode (Koolook)Koolook/Wan_Video— Wan 2.2 Easy Prompt (Koolook)
Manual Installation
- Clone this repository into your
ComfyUI/custom_nodes/directory:git clone https://github.com/malkuthro/ComfyUI-Koolook.git - Ensure
__init__.pyandconfig.jsonare in the cloned folder. - Restart ComfyUI.
The root Koolook nodes have no additional Python dependencies. To enable the Radiance Koolook v1.0.1 nodes that handle EXR / OCIO / HDR work, install the optional packages: OpenEXR, Imath, PyOpenColorIO, colour-science, opencv-python, and imageio.
Usage
Wan 2.2 Easy Prompt
- Add the "Wan 2.2 Easy Prompt" node to your workflow under "Koolook/Wan_Video".
- Select options from the dropdowns (e.g., Light Source: "Sunny lighting", Lighting Type: "Soft lighting").
- Add custom text in the "body" field for main prompt content.
- Optionally connect a CLIP for conditioning output.
- Connect the STRING outputs (combined_prompt or fields_only) to a prompt input, such as the "prompt" slot in the PromptJSON node (from NeuralSamurAI/ComfyUI-PromptJSON).
- To customize fields:
- Edit
config.json(see example below). - Restart ComfyUI to apply changes.
- Edit
- VFX Tip: Use for generating consistent lighting/shot descriptions in video generation workflows, like with AnimateDiff or SVD.
Example config.json
This is the default configuration based on Wan 2.2 parameters:
{
"category": "Koolook/Wan_Video",
"node_name": "Wan_2.2",
"fields": [
{
"name": "light_source",
"type": "combo",
"options": ["none", "Sunny lighting", "Artificial lighting", "Moonlighting", "Practical lighting", "Firelighting", "Fluorescent lighting", "Overcast lighting", "Mixed lighting"],
"default": "none"
},
{
"name": "lighting_type",
"type": "combo",
"options": ["none", "Soft lighting", "Hard lighting", "Top lighting", "Side lighting", "Underlighting", "Edge lighting", "Silhouette lighting", "Low contrast lighting", "High contrast lighting"],
"default": "none"
},
// ... (additional fields like time_of_day, shot_size, etc.)
],
"output_type": "STRING"
}
Easy Resize (Koolook)
- Add the "Easy Resize (Koolook)" node (ID
EasyResize_Koolook) to your workflow under "Koolook/Image". - Connect an IMAGE (and optional MASK) input.
- Set the base dimension (Width or Height), base size, aspect ratio (e.g., "16:9" for landscape, "9:16" for portrait), and other options like keep_proportion (stretch, letterbox, pillarbox), pad_color (e.g., "0, 0, 0" for black), crop_position, divisible_by, and device.
- Connect the outputs: IMAGE and MASK for further processing, INT width/height for resolution-aware nodes like samplers or upscalers.
- VFX Tip: Use for preprocessing footage in multi-resolution pipelines, ensuring divisibility for efficient GPU processing in tools like IPAdapter or ControlNet.
Node Code Overview
The core logic is split across files for modularity:
Root Koolook nodes:
- k_easy_wan22_prompt.py — loads
config.jsonat import time, builds inputs dynamically (combos for dropdowns, strings for text), and formats selections into prompt strings with optional CLIP conditioning. - k_easy_resize.py — parses aspect ratios, computes divisible dimensions, resizes with
common_upscale, and handles padding/cropping. Inspired by Resize Image V2 from ComfyUI-KJNodes. - k_ai_pipeline.py —
EasyAIPipelinebuilds VFX output paths/filenames from shot/version inputs and creates the output directory. - k_easy_image_batch.py —
easy_ImageBatchbuilds alignedIMAGE+MASKkeyframe batches with placeholder fill. - k_easy_track.py —
KoolookLoadCameraPosesAbsoluteloads RealEstate10k pose TXT files from any path.
Forked/wrapped nodes:
- forks/radiance_koolook/ — version-namespaced wrapper for the Radiance fork. Currently exposes the
v2_3_3set; modified node sources live inforks/radiance_koolook/versions/v2_3_3/nodes_vae.py. The wrapper supports the__koolook_vX_Y_ZID suffix pattern but lets specific Koolook-named IDs (likeEasy_hdr_VAE_encode/decode) ship without the suffix via theSKIP_VERSION_SUFFIXset. - forks/forks_manifest.yaml, forks/THIRD_PARTY.md, forks/README.md — fork tracking metadata.
Glue and frontend:
- __init__.py — imports all node packages, merges
NODE_CLASS_MAPPINGS/NODE_DISPLAY_NAME_MAPPINGS, and exposesWEB_DIRECTORY = "./web". - web/ — JavaScript extensions loaded by ComfyUI for the Koolook nodes (e.g.,
ai_pipeline.js).
For advanced customizations (e.g., different output formats or additional resize modes), modify the execute or adjust_to_aspect methods in the respective files.
Troubleshooting
- ComfyUI Won't Start: Check console logs for errors (run
python main.py). Common: Missingconfig.jsonor invalid JSON—use a validator like jsonlint.com. - No Fields Visible in Wan 2.2 Easy Prompt: Indicates JSON load failure; the node falls back to empty inputs.
- Errors in Easy Resize: Ensure aspect ratio is "w:h" format, divisible_by >=1, and pad_color is "r, g, b" floats (0.0-1.0).
- Updates: Pull latest from GitHub and restart.
Credits
- Based on ComfyUI custom node examples.
- Parameters for Wan 2.2 Easy Prompt derived from Wan 2.2 docs (https://alidocs.dingtalk.com/i/nodes/EpGBa2Lm8aZxe5myC99MelA2WgN7R35y).
- Easy Resize inspired by ComfyUI-KJNodes (https://github.com/kijai/ComfyUI-KJNodes), specifically the Resize Image V2 node in image_nodes.py.
- Compatible with nodes like PromptJSON for LLM workflows.
License
GPL-3.0 (see LICENSE).
This package incorporates and adapts code from
fxtdstudios/radiance (also
GPL-3.0) under forks/radiance_koolook/. Per GPL-3.0 §5(c), the entire
work is licensed as GPL-3.0. Attribution and per-fork modification notes
are tracked in forks/THIRD_PARTY.md and
forks/forks_manifest.yaml.
Note for downstream users: any work that incorporates, links to, or derives from
ComfyUI-Koolookmust itself be distributed under GPL-3.0 (or a compatible license). If you need a non-copyleft license for any piece of this code, contact the maintainers — some Koolook-original nodes (those with no upstream Radiance lineage) may be available under a more permissive license on request.