Paint everything except a person or a pet, turn on fill holes, and it
filled the unpainted subject too, so the part meant to stay was repainted.
The new optional max_hole_size input (a percent of the picture, 0 to 100)
sets the biggest hole fill holes fills. The default, 0, fills every hole
exactly as before, so saved workflows and API prompts run the same; at 2
the gaps a quick brush leaves still fill and a subject left unpainted
stays. Areas touching the picture's edge are never holes, as before.
- Hole sizes come from scipy's labels, the same 4-connected holes its
fill finds; the torch fallback labels the same 8-connected holes as its
own fill. Each picture in a batch is measured on its own, after expand.
- The card gets a Max hole size row with a % unit, dimmed while Fill holes
is off, and a resetUnknown fallback of 0 for 2.x saves that hold the
card's empty value in its slot.
- Tests: a hole above the limit stays, holes up to it fill, 0 matches the
old fill, edge-touching areas never fill, the torch fallback agrees,
batches, and older saves opening with 0.
- Node API snapshot, help page and changelog.
holdsUnknownValue judged only switches and choices, so a number input
appended after a release could not be given a resetUnknown fallback: an
older save hands it the card's empty value, and it stayed there, which the
prompt then fails to convert. A number widget now holds only a finite
number. Only widgets named in a card's fallbacks are ever reset, so no
existing node changes.
A workflow saved before 2.4.0 holds the widget card's own empty value in
the slot keep_inside took when it was appended. The frontend restores
widget values by position, so keep_inside loaded as "", the card showed it
off and the queued prompt sent "", which ComfyUI reads as False: the old
padded crop. An API prompt saved before 2.4.0 has no keep_inside and runs
with the node's default, True. The same workflow gave two different crops.
The card now puts True, the node's default, back on a keep_inside that is
neither true nor false before it draws, the way it already resets an
unknown Seam on Stitch Inpaint. The resetUnknown check moves into
widget_card_math.mjs (holdsUnknownValue, resetUnknownValues) and learns
switches: the old check only knew a choice's options, so it would have
reset every saved switch. A saved true or false is left alone.
The backend is unchanged. ComfyUI converts a BOOLEAN "" to False before
VALIDATE_INPUTS or the node sees it, so an API prompt that carries "" (an
API export made on 2.4.0) still runs with it off.
The card wrote a widget's value and ran its callback, but never called
node.onWidgetChanged, which is where ComfyUI's frontend re-scans a node
for missing media. Picking an existing file in Load Image + Pad's Source
list left the red outline, the "Media input missing" toast and the Setup
required entry on, still naming the old file.
Every card control now writes through commitWidgetValue, which sets the
value, runs the callback and then calls onWidgetChanged, in the order
LiteGraph's own widgets use.
Adds tests for it, plus a changelog entry.
The card keeps its old look and height. Seam has no row and no socket
now: a small gear in the card's top-right corner opens a menu with the
two choices, each with one plain line. With blend in on, Tone match and
Fix edge halo stay in place but dim and stop taking input, with a hover
hint, and a "blend in" chip beside the gear shows the mode.
The seam widget is a socketless combo, so the frontend makes no input
slot for it in either renderer; a stale unlinked slot from an earlier
save is dropped on load. The backend input is unchanged.
The widget card gains two generic options for this: rows can be muted
by the current values, and a card can carry small corner tools that
every row's control keeps clear of. A gear-menu choice can list one
line per option.