45 Commits
Author SHA1 Message Date
tackandClaude Opus 4.7 9dc7e2dbe8 fix(sampler): round panel dims to %8 for latent, crop VAE output back
Panels whose width_px/height_px weren't multiples of 8 were silently
replaced with the red-X placeholder: ComfyUI's EmptyLatentImage floors
dims to the nearest /8, so the VAE-decoded image came back 1-7 px shy
of the requested size, tripping the strict shape equality check in
ComicBatchGenerator and sending all retry attempts down the
retry_then_skip branch.

run_panel_sampler now rounds (w, h) UP to the nearest multiple of 8
before calling empty_latent and crops the decoded image back to the
exact requested (h, w). The now-redundant shape check in
batch_generator is dropped in favour of an at-least-as-large check
inside run_panel_sampler.

Adds RoundedShapeBackend (mimics ComfyUI's /8 rounding) and a
regression test for a 301x150 panel to prevent the bug returning.
WrongShapeBackend now returns a too-small image so halt-mode still
catches genuinely broken backends.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 17:16:42 +08:00
tack de394c3f26 feat(script-loader): add auto mode, file dropdown, quote-stripping
UX fixes driven by cross-machine testing feedback:

- mode="auto" (new default) picks the filled input automatically, so users
  no longer get "mode=file requires json_file" when they filled json_path
  without switching the mode dropdown.
- json_file now renders as a dropdown of .json files under ComfyUI's
  input/ directory (falls back to plain STRING if folder_paths or the
  directory is unavailable).
- Surrounding quotes on json_path/json_file are stripped automatically —
  Windows "Copy as Path" pastes quoted strings and that used to surface
  as an opaque "file not found".
- Clearer errors for "no input" / "multiple inputs" in auto mode, and a
  hint on path-not-found pointing at the quote issue.

Tests: 117 -> 121 (4 new auto-mode cases).
2026-04-23 16:57:39 +08:00
tack af9d5cdedf fix: expose NODE_CLASS_MAPPINGS at repo root for ComfyUI discovery
ComfyUI loads custom_nodes/<folder>/__init__.py and reads
NODE_CLASS_MAPPINGS from there. Our implementation lived one level
down at tk_comfyui_batch_image/__init__.py, so ComfyUI never saw any
node mappings when the repo was cloned into ComfyUI/custom_nodes/.

Add a thin wrapper __init__.py at repo root that re-exports the
mappings from the inner package. Lets the git folder name be anything
(e.g. tk_comfyui_comic_tool on GitHub) while keeping the internal
Python package name stable (tk_comfyui_batch_image).
2026-04-23 16:38:21 +08:00
tack 3cd0cac170 build: add requirements.txt so ComfyUI-Manager auto-installs deps on startup
pyproject.toml is the Python-ecosystem standard but ComfyUI-Manager
looks for requirements.txt at the custom-node root. Mirror the three
runtime deps (Pillow / jsonschema / numpy) there so cloning the repo
into ComfyUI/custom_nodes/ and restarting is enough — no manual pip.
2026-04-23 16:33:17 +08:00
tack c29855aa34 chore: prune dev-internal specs + plans; update README for end users
Remove docs/superpowers/ (M1+M2 specs and plans — internal dev artifacts
kept in the controller's local session only). The AI-facing skill pack
at docs/skills/comic-script-authoring/ stays — that's the shipping
deliverable.

README rewritten for the clone-into-ComfyUI/custom_nodes use case:
lists all four nodes (including ComicSamplerOverride), documents the
validator CLI, points at the skill pack for three audiences (Claude
Code / generic LLM / humans), and updates capability/limitation
sections to reflect post-M2 state.
2026-04-23 16:28:21 +08:00
tack b7cb2e07df Merge branch 'm2-validation'
M2 milestone: Layer 2 validator + CLI + skill pack.

- 5 semantic rules (R1 page_index, R2 panel_index, R3 layout fits,
  R4 panel width fits, R5 page_template vs explicit dims)
- Standalone CLI: python -m tk_comfyui_batch_image.validate, with
  --json / --max-errors and exit codes 0/1/2/3
- schema_export: core/schema.py → schema.json + drift test
- Skill pack at docs/skills/comic-script-authoring/: SKILL.md,
  PROMPT_TEMPLATE.md, README.md, 5 screenplay+output.json examples
- ComicScriptLoader now runs Layer 1 + Layer 2 validation

Final: 117 tests pass, ruff clean, all 5 examples validate.
2026-04-23 16:15:20 +08:00
tack 8811fb955e polish(m2): review follow-ups — tighten CLI suppress, generalise R5 preset check, document layer=0
Addresses the LOW/MEDIUM items flagged in the Task 8-10 and Task 3-6
code reviews:

- validator.py: R5 no longer hardcodes the preset enum; any non-custom
  template value is treated as a preset. Future enum additions in
  schema.py flow through automatically.
- validate.py: narrow contextlib.suppress(Exception) to specific
  exception types (AttributeError / io.UnsupportedOperation / OSError),
  and move the contextlib/io imports to the module-top stdlib block.
- validator.py: document the layer=0 convention (CLI I/O / pre-validation)
  on the CheckError dataclass field so future readers understand why
  validate.py emits layer=0 for missing-file / JSON-parse errors.
- test_cli_validate.py: add regression test for the "missing file in
  --json mode becomes layer=0 + exit 1" behaviour previously uncovered.
2026-04-23 16:14:54 +08:00
tack ea59260035 docs(skill-pack): PROMPT_TEMPLATE + README for generic LLMs and humans 2026-04-23 16:08:13 +08:00
tack ab461c22ce docs(skill-pack): SKILL.md for Claude Code + superpowers 2026-04-23 16:06:47 +08:00
tack a068cfb5d3 feat(skill-pack): examples 02-05 covering align, override, rtl, 4-layer prompts 2026-04-23 16:03:11 +08:00
tack 700b133611 feat(skill-pack): example 01 minimal + parametric regression test 2026-04-23 16:03:01 +08:00
tackandClaude Sonnet 4.6 f57d81d58b feat(schema): export COMIC_SCHEMA to skill pack schema.json + drift test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 15:58:40 +08:00
tack 98e5a7c3e2 feat(cli): multi-file summary + --max-errors truncation 2026-04-23 15:54:39 +08:00
tack c223d22981 feat(cli): --json machine-readable output mode 2026-04-23 15:53:07 +08:00
tack 6825d8520a feat(cli): validate entrypoint with exit codes + human output 2026-04-23 15:51:37 +08:00
tack 13254ea334 refactor(script_loader): use validate() for Layer 1 + Layer 2 safety net 2026-04-23 15:47:06 +08:00
tack 5a1feaf777 feat(validator): R5 page_template vs explicit dimensions 2026-04-23 15:41:00 +08:00
tack 7a14398a16 feat(validator): R4 panel width fits page 2026-04-23 15:39:56 +08:00
tack 6cd6e4c788 feat(validator): R3 vertical_stack layout fits page 2026-04-23 15:39:07 +08:00
tack 820d5eaece feat(validator): R2 panel_index continuity 2026-04-23 15:38:12 +08:00
tack d2abcdf514 refactor(validator): R1 use accumulate-all for consistency with R2-R5
Code review flagged that R1's early-exit (chosen in 1793f8d to satisfy
a len(errs)==1 assertion) would diverge from Tasks 3-6 which use
accumulate-all. Retrofit R1 + relax the test to use any()/next() so a
user fixes all continuity gaps in one validate pass. Also deepcopy the
page template in the fixture helper to avoid shared-ref leaks in later
rule tests.
2026-04-23 15:34:38 +08:00
tack 1793f8d957 feat(validator): R1 page_index continuity 2026-04-23 15:28:44 +08:00
tack 24122a189d feat(validator): scaffold CheckError + collect_errors for Layer 2 2026-04-23 15:22:36 +08:00
tackandClaude Opus 4.7 d9e241ad2e docs: M2 implementation plan (15 tasks, TDD per rule)
Plan derived from M2 spec — breaks validator Layer 2, CLI, schema export,
skill pack, and examples into 15 bite-sized tasks. Each task lists exact
file paths, test code, implementation code, shell commands, and commit
message so a fresh engineer can execute task-by-task without context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:15:17 +08:00
tackandClaude Opus 4.7 5127cf36bb docs: M2 spec — validation layer 2 + skill delivery pack
Design for M2 milestone covering:
- Layer 2 semantic validator (5 rules, M1 feature subset)
- Standalone CLI with human + --json output modes
- schema.json as single source of truth (exported from core/schema.py)
- Skill pack for AI-as-storyboarder workflow: SKILL.md, PROMPT_TEMPLATE,
  README, and 5 examples each with screenplay.md + output.json
- Canvas-size defaults (1080x1920) to anchor AI composition judgments

Layer 3 pre-generation checks, shape_group/polygon rules, and non-custom
page_template dimension tables are explicitly deferred to later milestones.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:06:00 +08:00
tackandClaude Opus 4.7 1aef0f2ced refactor: M1 polish pass (I2, I3, I5, I7)
Four minor improvements flagged in the M1 holistic review:

- I7: Extract core/image_ops.py with arr_to_pil / pil_to_arr / hex_to_rgb.
  Removes the three duplicated copies scattered across compositor,
  batch_generator, and page_composer. Sets up cleaner reuse for M2/M3
  when validator and polygon mask code need the same helpers.

- I5: Move `import torch` out of module top-level in both node files,
  into the methods that actually use it. Keeps the ComfyUI node scan
  cheap — torch is the heaviest dependency by far.

- I3: Rename the `retries` widget to `max_attempts` (default 3, min 1).
  The old name was misleading: retries=2 meant 3 total attempts.
  Log lines now read "attempt N/M" for clarity.

- I2: Add manifest_version: 1 to cache manifests. manifest_matches()
  now treats missing/mismatched versions as stale, so a future hash
  schema change can safely invalidate old caches by bumping the
  constant in core/cache_manager.py.

Tests updated to new kwargs, plus 2 new regression tests covering the
manifest-version behaviour. 74/74 pass, ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 14:43:00 +08:00
tackandClaude Opus 4.7 c810408f9b feat(nodes): add ComicSamplerOverride for runtime sampler tweaks
Optional node that plugs between ComicScriptLoader and ComicBatchGenerator
when the user wants to experiment with cfg / steps / sampler without
editing the JSON. Any widget left at its sentinel value ((keep), 0, 0.0,
-1.0) is ignored; non-sentinel widgets overwrite every panel's sampler,
including panels that had per-panel sampler_override in the JSON.

Workflow stays JSON-authoritative by default: don't insert the node and
the JSON is the single source of truth (I4 / spec §14). Insert it only
for ad-hoc experimentation.

Panels are rebuilt via dataclasses.replace — the input SolvedScript is
never mutated, keeping C1's no-mutation invariant. Cache hashing picks
up the new sampler values automatically.

Registered as "Comic Sampler Override (experimental)" in NODE_CLASS_MAPPINGS.
5 tests cover: passthrough, single-key override, no-mutation, override
winning over per-panel sampler_override, and mixed partial override.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 14:35:06 +08:00
tackandClaude Opus 4.7 ad9dda654f refactor(batch_generator): drop redundant sampler widgets (I4)
Schema requires default_sampler at book level to contain all 5 keys
(sampler_name, scheduler, steps, cfg, denoise), and the normalizer
always populates panel.sampler from it. The node-level widgets were
dead code — the merge `{**widget, **panel.sampler}` always collapsed
to panel.sampler.

Remove the 5 sampler widgets from INPUT_TYPES so the node stays faithful
to the design principle "schema is the single source of truth"
(spec §14). JSON's default_sampler + per-panel sampler_override are now
the only way to configure sampler params.

positive_suffix / negative_suffix widgets remain — they are explicit
run-time overrides, not duplicates of JSON config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 14:25:54 +08:00
tackandClaude Opus 4.7 08ab053790 fix(batch_generator): stop mutating input panels & persist skip placeholder
C1: generate() baked widget defaults + prompt suffixes into the caller's
SolvedPanel, breaking cache invalidation on widget edits and leaking
mutated prompts into the pass-through comic_script output. Replace the
in-place mutation with dataclasses.replace into a local run_panel used
for hashing and sampler invocation; the input script is never touched.

C2: retry_then_skip exhausted retries returned a red-X placeholder but
wrote nothing to disk, so the next run re-sampled the failing panel
instead of staying skipped. Persist placeholder PNG + manifest with
{"status": "skipped"} so manifest_matches keeps the panel skipped.

Also assert sampler-returned shape == (h, w, 3) and add a defensive
shape check in page_composer before cropping.

Regression tests added: no-mutation, skip persistence across runs, and
shape-mismatch halt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 14:08:55 +08:00
tackandClaude Sonnet 4.6 4738a10321 docs: document M1 nodes + restore NODE_CLASS_MAPPINGS export
Overwrite README.md with full M1 documentation (nodes table, install,
test, limitations). Restore NODE_CLASS_MAPPINGS / NODE_DISPLAY_NAME_MAPPINGS
in __init__.py now that all three node modules exist; import order fixed
for ruff I001.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:52:06 +08:00
tackandClaude Sonnet 4.6 1e4ccca032 test: add E2E integration test + example ComfyUI workflow
Add test_integration_e2e.py that exercises the full loader → generator
→ composer pipeline with GreyBackend, asserting 2 pages, 4 panels, and
cache-hit behaviour on second run (PNG round-trip tolerance atol=1/255).

Add examples/workflows/m1_basic_vertical_stack.json as a reference
ComfyUI workflow JSON wiring CheckpointLoaderSimple → ComicScriptLoader
→ ComicBatchGenerator → ComicPageComposer → SaveImage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:48:28 +08:00
tackandClaude Sonnet 4.6 9d3a587cf6 style(core,nodes,tests): apply ruff import sort & modern type annotations
Auto-fix all I001 (unsorted imports), UP007 (Optional → X | None),
UP035 (typing.Iterable → collections.abc), B905 (zip strict=),
and F401 (unused imports) violations across core/, nodes/, and tests/.
No behaviour change; 64 tests still pass after fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:48:15 +08:00
tackandClaude Sonnet 4.6 c97190fe2e feat(nodes): add ComicPageComposer (rect + bbox metadata)
Assembles per-panel IMAGE tensors onto full page canvases via compose_page,
emits panel_bboxes_json metadata, and passes the original panel stack through.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:41:56 +08:00
tackandClaude Sonnet 4.6 33c7eb5098 feat(nodes): add ComicBatchGenerator with resume + retry
Implements panel-level cache-skip (manifest hash), per-panel retry loop,
halt vs retry_then_skip failure modes, and white-padding before stacking
mixed-height panels into a single (N,H,W,3) tensor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:36:45 +08:00
tackandClaude Sonnet 4.6 1ca2e5e5d7 feat(nodes): add ComicScriptLoader (file/path/inline modes)
Implements the ComfyUI input node that loads, validates, and normalizes
comic script JSON via three modes (file, path, inline), plus conftest.py
with a folder_paths stub for CI and 6 TDD tests covering all modes and
error handling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:29:59 +08:00
tackandClaude Sonnet 4.6 0c1ed9b310 feat(core): add rect-only page compositor with border
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:25:28 +08:00
tack 0fd29a4665 feat(core): add backend-agnostic sampler runner 2026-04-23 12:22:42 +08:00
tack 3943226e07 feat(core): add panel-hash cache manager for resume
SHA256-based panel fingerprinting over content fields (excludes bbox_topleft),
with manifest read/write and stale-detection for skip-on-resume logic.
2026-04-23 12:18:50 +08:00
tackandClaude Sonnet 4.6 7b965a4ee6 feat(core): add script normalizer (defaults + layout + prompts + seeds)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:14:18 +08:00
tackandClaude Sonnet 4.6 a215e64af0 feat(core): add vertical_stack rect layout solver
Implements solve_vertical_stack() with left/center/right alignment,
gutter support, and LayoutError for overflow and oversized panels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:08:50 +08:00
tackandClaude Sonnet 4.6 97079a66fe feat(core): add 4-layer prompt builder
Implements build_prompt_pair() joining style/character/page/scene layers
with suffix support; skips empty/whitespace/trailing-comma fragments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:04:38 +08:00
tackandClaude Sonnet 4.6 36640fef93 feat(core): add schema validator with path-aware errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:00:03 +08:00
tackandClaude Sonnet 4.6 08c39dbe8c feat(core): add M1 JSON schema (rect-only) + fixtures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:55:29 +08:00
tack 26f860cc71 feat(core): scaffold package + SolvedScript/Page/Panel types 2026-04-23 11:48:59 +08:00
tack e76cd2bd3d docs: add design spec + M1 implementation plan 2026-04-23 11:43:42 +08:00