malkuthroandClaude Opus 4.7 bebc6f618d feat(sidebar): right-click menu pack — Duplicate, Tags, + New directory move (#59)
* 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>
2026-05-05 08:59:13 +02:00
2025-09-28 14:48:07 +02:00

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 the Resize Image V2 node from kijai/ComfyUI-KJNodes (GPL-3.0 — see forks/THIRD_PARTY.md for the full attribution + change log). The legacy bare-name EasyResize ID 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 + MASK batch of length total_frames from 1–4 keyframe images placed at explicit frame indices (with start_frame offset). 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_POSES from 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_1 namespace. 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. See forks/THIRD_PARTY.md for 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 + MASK batches 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.json and 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.

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

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 existing KOLOOK_FORKS_DIR).
  • Real paths live in a local, gitignored .env file. The committed .env.example declares 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/
  • 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'd radinace2.3.3, so we pin by SHA)
  • Node IDs in the v2_3_3 fork are exposed verbatim as Easy_hdr_VAE_encode / Easy_hdr_VAE_decode (the __koolook_v2_3_3 namespace suffix is skipped via SKIP_VERSION_SUFFIX in versions/v2_3_3/__init__.py because the Koolook-specific names already differentiate them from upstream).
  • Each version folder should include UPSTREAM_PIN.yaml to keep parity with external pinned references.
  • The historical v1_0_1 folder was removed in v0.1.5; see forks/THIRD_PARTY.md.

Version Pinning (Portable Across PCs/macOS)

  • Track every external repo in forks/forks_manifest.yaml with:
    • 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-koolook
    • radiance-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

  1. Open ComfyUI and access the Manager interface.
  2. Go to "Install Custom Nodes" > "Install via Git URL".
  3. Paste the repository URL: https://github.com/malkuthro/ComfyUI-Koolook.git.
  4. Install and restart ComfyUI.
  5. 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

  1. Clone this repository into your ComfyUI/custom_nodes/ directory:
    git clone https://github.com/malkuthro/ComfyUI-Koolook.git
    
  2. Ensure __init__.py and config.json are in the cloned folder.
  3. 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

  1. Add the "Wan 2.2 Easy Prompt" node to your workflow under "Koolook/Wan_Video".
  2. Select options from the dropdowns (e.g., Light Source: "Sunny lighting", Lighting Type: "Soft lighting").
  3. Add custom text in the "body" field for main prompt content.
  4. Optionally connect a CLIP for conditioning output.
  5. 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).
  6. To customize fields:
    • Edit config.json (see example below).
    • Restart ComfyUI to apply changes.
  7. 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)

  1. Add the "Easy Resize (Koolook)" node (ID EasyResize_Koolook) to your workflow under "Koolook/Image".
  2. Connect an IMAGE (and optional MASK) input.
  3. 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.
  4. Connect the outputs: IMAGE and MASK for further processing, INT width/height for resolution-aware nodes like samplers or upscalers.
  5. 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.json at 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 — EasyAIPipeline builds VFX output paths/filenames from shot/version inputs and creates the output directory.
  • k_easy_image_batch.py — easy_ImageBatch builds aligned IMAGE+MASK keyframe batches with placeholder fill.
  • k_easy_track.py — KoolookLoadCameraPosesAbsolute loads 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_3 set; modified node sources live in forks/radiance_koolook/versions/v2_3_3/nodes_vae.py. The wrapper supports the __koolook_vX_Y_Z ID suffix pattern but lets specific Koolook-named IDs (like Easy_hdr_VAE_encode/decode) ship without the suffix via the SKIP_VERSION_SUFFIX set.
  • 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 exposes WEB_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: Missing config.json or 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

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-Koolook must 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.

S
Description
No description provided
Readme GPL-3.0
220 MiB
Languages
Python 67.9%
JavaScript 30.3%
HTML 1%
CSS 0.5%
Shell 0.2%
Other 0.1%