Named, portable profiles for the Image Pool: a Pool Profile companion node
(create/select/rename/delete/duplicate/export-import) outputs a POOL_PROFILE
id into the pool's new optional input; profile or pool_id wins. Registry
(name->id) in profiles.json; live edit-time grid switch via cross-node
propagation. TDD plan with a pure stdlib profiles layer incl. zip round-trip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Blocking text gate: pauses, shows incoming text in an editable box, Pass
emits the edited text. Optional any-type signal input + signal passthrough
output for ordering. Reuses gate_bus via an additive string payload channel
with a should_cancel hook so the Pass-only gate still honors global Cancel
(processing_interrupted). TDD plan; comfy imports stay lazy for testability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After a route choice the node now keeps the image and shows a 'Run from here'
re-queue button instead of blanking. The last painted mask is remembered and
auto-re-stashed on each new pause (with a Clear control) so it is not lost
between runs. The preview image area now scales with the node width.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Interactive chooser/router: pauses execution, shows the image with up to
10 labeled route buttons + edit-mask + stop. Chosen route gets the image,
others ExecutionBlocker-ed; gate-painted mask on a fixed output; stop
raises InterruptProcessingException. TDD plan with a pure torch-free
gate_bus; lazy comfy imports keep node logic unit-testable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MaskEditor alpha comes through as 0 in painted areas; bake 255-a so the
MASK output is white where painted (region of interest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DOM-widget width doesn't reliably track the node width in frontend
1.45, so force a fixed column count clipped the grid and the width floor
blocked resizing down. Use flex-wrap (cells wrap to fit, never clip),
drop the computeSize width floor (resize freely), and re-sync the widget
width + reflow on manual resize.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ComfyUI snaps the node to computeSize() on selection, and computeSize's
width ignores DOM widgets, so the grid collapsed/clipped on click.
Override node.computeSize to floor the width at the column-fit width.
Lay the grid out as a fixed 4 columns (128px cells) with the node sized
to fit; base width doubled to 560.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Index badge, has-mask dot, count, and empty-state message were added in
Phase 1; add drag affordances (grab cursor, cell hover) now that
reorder exists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pool.reorder() permutes slots (validated permutation) and keeps the
active selection on its slot; exposed via /grid_pool/reorder. The grid
thumbnails are drag handles; dropping on another cell reorders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Right-click 'Detach pool (new id)' assigns a fresh UUID so a cloned
node gets its own independent pool.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the per-slot mask button to ComfyUI's MaskEditor (frontend 1.45):
point the editor at the slot image via node.images + previewMediaType,
open it through the Comfy.MaskEditor.OpenMaskEditor command, poll for
the saved clipspace ref, bake the alpha channel into a grayscale mask
(white = painted) and POST it to /grid_pool/set_mask.
Also fixes DOM-widget sizing for frontend 1.45: size via the getMinHeight
option (the computeLayoutSize path) with NO max, so the grid fills and
grows with the node instead of detaching/locking on click; hide pool_id
via widget.hidden; suppress node.imgs so a registered output never
reserves a preview strip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Write a per-slot grayscale mask sidecar (img_XXXX.mask.png) and record
it on the slot. Add the multipart /grid_pool/set_mask route.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>