ComfyUI's "edit attention" (wrap selection in (token:weight)) is a global
window keydown listener that acts when a <textarea> is focused. The text
gate editor is a textarea, but its keydown handler called stopPropagation
on EVERY key, so the event never bubbled to window and weighting never
fired — notably when using the node as a prompt text node in protected mode.
Now stopPropagation is skipped for the weighting shortcut (Ctrl/Cmd + ↑/↓)
so it reaches the global handler; all other keys are still stopped so
typing/space can't trigger litegraph canvas shortcuts. The weighting edit
goes through execCommand, which fires our oninput -> stored_text stays synced.
Verified against the verbatim editAttention from the shipped frontend:
whole-word weighting, existing-weight decrement, and no-selection word
expansion all round-trip; plain keys stay stopped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The editor content was only restored on reload in protected mode, and
stored_text was only synced on keystroke (oninput). So in the default
pause mode edited text came back empty after refresh/reload-workflow,
and upstream text passed without a keystroke was never captured.
Now applyPersistedMode restores the editor from stored_text in BOTH
modes, and syncStored also fires when upstream text arrives (socket)
and on Pass — so whatever text is shown/edited survives a reload.
Verified against the shipped litegraph serialize()/configure() widget
semantics: default-mode + pass-without-typing round-trips now restore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
computeSize alone left the collapsed pill visible in the 1.47 frontend.
Set widget.hidden=true (what getVisibleWidgets filters on), matching the
pool node's hideWidget. Value still serializes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a '🔒 Protected (text node)' toggle. When on, the DOM editor is a free text
box whose value mirrors into the hidden stored_text widget; the node outputs that
text and ignores upstream (no pause, socket events ignored). Persists via the
protected + stored_text widgets; restored on configure. stored_text is single-line
so it hides cleanly (pool_id trick).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same latent bug as the text gate: a bare app.queuePrompt(0,1) enqueues but
doesn't kick off execution in the 1.47 frontend. Execute the Comfy.QueuePrompt
command (the Run button's path), with app.queuePrompt as a legacy fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A bare app.queuePrompt(0,1) enqueues but skips the command-level run setup in
the 1.47 frontend, so the prompt never kicked off — you had to press Run
manually. Execute the Comfy.QueuePrompt command (same path as the Run button /
Ctrl+Enter) instead, with app.queuePrompt as a legacy fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run-from-here now preserves the edited text via an explicit _tgKeepEdit flag
set when the button is pressed, instead of comparing incoming vs last text.
A non-deterministic upstream (random/seeded prompt) regenerates text on every
re-queue, which made the old comparison clobber the edit. Normal toolbar Queue
still shows fresh upstream text.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connecting a Pool Profile no longer overwrites the pool's pool_id. The pool is
switched only when the user actively selects a profile in the dropdown; picking
an empty profile while a pool with images is connected offers to copy those
images into it (new seed_profile op + /grid_pool/profiles/seed route), so the
current pool is never silently lost.
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>
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>
Render the pool as an in-node thumbnail grid with paste/drop/upload
ingest, click-to-select (active border), inline label editing, and
delete. Toolbar (upload/refresh/count) sits at the bottom per ComfyUI
convention; the node auto-grows to fit content.
pool_id is a declared STRING input (not a hidden input): ComfyUI only
fills hidden inputs for built-in types, but forwards every serialized
widget value by name. The JS mints a per-node UUID and hides the widget
via widget.hidden=true (frontend 1.45), keeping it serialized.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>