A new Utility node for explaining a workflow on the canvas. The text grows
to the largest size that fits the note, so it reads without zooming in, and
each point gets a thin teal line beside it.
An arrow emoji in the text (or the arrow row under the editor) becomes a
teal arrow in the line. Click a node while editing, then press an arrow, and
the arrow is linked to that node: clicking it later brings the node into
view, the way Workflow Switches does. The link is kept in the text as #id,
so editing the words never moves it.
The node has one hidden text input and no outputs, so it never runs.
The row next to Fill, Feather and Resize said Centre. It now says Center,
and so do the two button tooltips and the three crop help pages. Code
names and comments are unchanged, since nobody sees them.
Choosing a folder in the file explorer makes the browser ask its own
question ("Upload 74 files to this site?"). A page can neither word that
question nor skip it, and it does not say where the files go.
- While the file explorer is open, the node shows a note: all the folder's
pictures are copied into your ComfyUI input folder, and the browser's
upload question is this copy.
- While it copies, the node names the folder and where its pictures go,
and how far the copy is.
- The big-folder question, the message afterwards and the Browse and
empty-gallery entries say "copy" too.
- Help page: what the browser's question means, and that a dropped folder
is copied without it.
A folder dropped on the node, or chosen in the file explorer from Browse or
Add, is copied into ComfyUI's input folder by the browser through ComfyUI's
own upload, and the node moves to the copy. A folder with more than 300
pictures or more than 1 GB asks first. Bringing the same folder in again
writes over its copies.
A whole path pasted into Folder is read as the folder it names when it lies
inside ComfyUI's input or output folder. Any other path is still refused,
and the node now says what to do instead, with a button that opens the file
explorer. The server reads nothing outside those two folders.
A loader for many pictures. The card chooses the folder inside ComfyUI's
input or output folder, and a thumbnail gallery under it ticks the pictures
to use. All in one run sends every picked picture through as a list; one
per run loads one picture each run and moves on when a run is queued. It
gives image, mask, filename, index and count. Pictures dropped on the node
or added with Add are uploaded into the input folder.
Folder names and picked names are attacker-controlled: a folder is only
read as a path below the input or output folder, links that lead out are
refused, and picked names are compared with what the folder holds, never
opened.
32 of the ? help pages checked against the code and corrected.
Wrong claims fixed: Stitch Inpaint's empty-area check (20/255), what a
new video keeps on both Video Crop + Rotate + Pad pages, Load Video's
frame picker and player height, where Run Timer lists earlier runs,
inpainting on the Krea 2 Outpaint Model Patch page, Workflow Note's
banner layout and pack detection, Image Resize's pad bars and mask,
Save Image's path preview and tags, Free Memory's steps, and Latent
Size's sockets.
Image Crop + Rotate + Pad now follows the node from top to bottom; the
saved values no row shows moved under Technical details.
Controls are named the way the node shows them, with the input name
beside. Pages that opened with jargon now open with what the node does
and when to use it. LoRA Loader gained the Civitai lookup, suggested
strength and the gear options it did not name.
A width+height box with a 0 in it takes its shape from the source, so only rounding and Multiple separate the two. Pad answered that with a bar 1 or 2 px wide: a 1000x700 picture with Multiple 16 came out 1008x704 with a 2 px bar. The picture now fills the box. The output size is unchanged, and a box with both sides set pads as before.
Picking a frame writes IN. With the frame at or after OUT the node held a start after its end, and the next run stopped with "Load Video needs start_seconds smaller than end_seconds" until a handle was moved.
Fill holes fills gaps that lie fully inside the mask, with the same fill
Mask Refine uses. Free VRAM takes SAM 3 and its text reader out of the
graphics card's memory right after the search, with ComfyUI's own unload
of one model, also when the search found nothing. Both are off by
default.
Registers the node, gives it a widget card and the shared preview panel
with a FIND tool that runs the node alone, a readout of how many things
matched and how sure SAM 3 was, and the stop message on the node's own
face. A stopped run shows the plain picture, not the last find. Each name
asks SAM 3 for up to 16 matches, and Sureness starts at 0.7.
The pack's server still never contacts a host. The info card's button has the
browser ask civitai.com about one file by its SHA256, then the server saves
the answer beside the LoRA as <model>.civitai.info after checking it names the
same file (size, shape and hash). The old Python HTTP call stays gone.
release_preflight now allows a raw fetch() only in js/cache_guard and
js/lora_loader. SECURITY.md, AGENTS.md, the README and the LoRA Loader help
page describe the change.
Wired straight into a Qwen Image 2.1 sampler, the node's latent made a
picture twice as wide and tall as the size shown (1024 x 768 came out
2048 x 1536). The latent now carries how far it is downsampled, as
ComfyUI's own empty-latent nodes do, so ComfyUI fits it to a model that
packs pixels differently. Other models are unchanged.
The MaskEditor saves its picture into the image widget's stored value
without calling the widget back, so the node kept showing the old picture
and nobody could tell a mask was there. The stage now follows a source
changed that way (on the node's draw, and when the MaskEditor sets the
node's preview, which covers Nodes 2.0 too), keeping the framing.
The mask is the picture's alpha. The stage reads it once per picture by the
run's own 90% rule and draws the picture as the run uses it: kept pixels
solid, painted parts empty over the fill, with Show blend's teal on top.
The teal turns, crops and pads with the picture, in the full editor too,
and Show blend's paste mask now includes the painted parts. A gear on the
image source box turns the teal off for this browser.
Four orange corner handles sit outside the canvas corners, on the same
ring as the side diamonds. Dragging one makes the canvas bigger or
smaller and keeps its shape: the padding on that corner's two sides
changes and the opposite corner holds still. Alt (Option on a Mac)
changes all four sides about the centre, and can be pressed or let go
mid-drag. A corner stops when one of its sides runs out of padding.
Two Centre buttons, on the node face next to Fill and Feather and in the
editor's Padding section, put the picture in the middle side to side or
top to bottom. They only move padding from one side to the other, so the
canvas keeps its size and a lit ratio stays lit; with Divisible by on,
its strip counts, so the visible bands come out even.
The rotate knob took the spot the top-right corner handle now needs, so
it moves to the nearest free gap on the handle ring when blocked, and it
keeps its place on its arm while it turns. In the editor, Alt-drag still
pans everywhere except on a corner handle.
Frontend only: the node inputs are unchanged.
A picture turned to 180 degrees could not be turned back with the knob:
dragging one way it stayed at -180, and the other way it snapped back to
-180 once the pointer passed straight left of the picture. Turning
clockwise through 180 also made it jump.
The turn was the pointer's angle minus its angle when the drag began.
atan2 flips from +180 to -180 straight left of the centre, so crossing
that line read as a whole turn back, and the clamp to -180..180 then
pinned the picture at the end of the range.
The knob is now followed move by move, each step the short way round
(knobStep), and the rotation wraps instead of clamping (wrapDegrees):
past 180 it carries on from -180. The Degrees box keeps its range.
Round canvas to on Image Crop + Rotate + Pad and both Video Crop + Rotate
+ Pad nodes is now labelled Divisible by. Its tooltip says what it does
(a few pixels of fill on the right and bottom so the width and height
divide evenly by the number), when to use it (models that need sizes
divisible by 8, 16 or 32) and that 1 is off. The input is still
canvas_multiple, so saved workflows keep their value.
Its arrows and drags added 8 to whatever was there, so from 1 (off) they
went 9, 17, 25. makeScrubInput gets a snap option: whole steps land on
multiples of the step (1, 8, 16, 24 and back down to 1), while Shift's
fine step and typed values are left alone. The resize Step box on the
same nodes and Multiple on Load Image + Pad had the same problem and use
it too.
The editor's status panel now refreshes when Divisible by changes, as it
already did for every other size control.
The unpainted check counted a flat block only within 10/255 of the fill.
Krea 2 can hand a 128 gray fill back as a flat 114 to 118, so a picture
with gray corners got no word. Flat blocks now count within 20/255, and so
does the picture's own flat area that is taken off first, so a flat wall
continued flat still stays quiet.
The message told still pictures that dark footage paints better when
brightened first. That line is now only for videos, in plainer words.
The warning had no changelog entry yet; it has one now.
A see-through part of a picture - a cutout's missing background, an oval
photo's corners, a hole - kept its stored colour blended over the fill.
That colour is often black or the old background, so the model got a
dark or light ring round the picture and Stitch Inpaint pasted part of it
back. A pixel less than 90% as solid as the picture's most solid pixel is
now empty, like a corner a turn leaves: filled with the fill colour,
marked in the mask, new area for both seams. Kept pixels are fully solid
in their own colour.
A new last output, prompt_image, is the image with see-through parts on
white instead of the fill, for the node that writes the prompt: shown the
gray fill, a prompt writer describes a gray backdrop and the model keeps
it. original shows see-through parts on white instead of black.
A picture at least 90% solid everywhere goes through exactly as before;
the new tests hold every output and both seams to the old transform.
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.
A node sized for the old face could grow into the node below once the new
notes showed. Now:
- The size line names the lit ratio where it acted ("pad to 16:9
1821x1024") instead of adding a line, and "already 9:16: pick another
ratio or turn it" sits on the same line when it fits.
- The Even out, To start and Full clip buttons fit their lines.
- "= the whole clip. Shorten it to move IN" fits beside the Length on the
example workflow's node.
- A new picture only ever shrinks the picture area to its shape; it never
grows the node.
Core Load Image hands out a 64x64 mask of zeros when the picture has no
mask painted on it, and picking a new picture after painting one gives
the same. Krea 2 Inpaint Masked then stopped inside Crop For Inpaint with
"Mask size (64, 64) does not match image size (1216, 832)".
Crop For Inpaint, Color Match and Mask Refine (with its picture wired)
now say "No mask painted: right-click your picture, choose Open in
MaskEditor, paint the area, then click Save." A mask made for another
picture names both sizes. LaMa Inpaint used to resize the blank stand-in
and hand the picture back unchanged; it says the same now. An empty mask
of the picture's own size still runs as before: Crop For Inpaint takes
the whole picture and stitches it back untouched.
The help pages, the README and the changelog use the new names (Round
canvas to, Frames for, Server file, the Resize switch) and describe the
always-scrubbing timeline and its grip, the padlock rules, the "already
this shape" note, the resize warning and Even out, the preview switch
bringing its picture back, Compare's labels and the Workflow Note's Use it.
An outpaint whose new area still shows the plain fill color used to come
back as a finished-looking picture with bars. Stitch Inpaint now measures
how much of the new area is still flat at the fill color, beyond what the
source picture itself has, and when a quarter of it (or half of any
checked frame) is left, says so in the console and in a toast.
The check lives in its own helper, works for images and video and for any
fill color, and stays quiet on dark scenes that are painted.
Blend in kept the model's colour as painted, so a model that paints
lighter, darker or warmer left the new area off-tone. Classic's Tone
match is no answer on turned pictures: it reads the grey rim a turn
leaves on the picture's edge as drift, and its flat offset lifts the
black of a dark new area.
With Tone match on, blend in now compares the model's version of the
strip it redrew with the picture at the same pixels (tone layers over
the same clean picture pixels, clipped highlights left out), fits the
drift as curves over brightness with a pinned and a free part, checks
the fit on held-out tiles, adds a local gain along the edge only when
that holds out too, and takes the result back off the model's picture
before the blend. Blacks stay black; nothing changes when the check
fails, the model kept the colours, or there is no feather to read. A
clip is measured once.
Classic is unchanged (its pins), and blend in at Tone match 0 is pinned
to the 2.4.0 stitch. The card no longer dims Tone match under blend in.
A dark or colourful picture padded with the #808080 gray came back from
Krea 2 AnyPaint with the gray still there: a gray frame around the picture,
or gray corners on a turned one. AnyPaint was trained with the new area of
its reference filled with the median colour of the picture, so a gray patch
next to a dark photo reads to it as part of the scene and it paints it back.
It did the same with every Krea 2 model file tried.
Krea 2 Encode gets an optional mask input, appended last. When it is wired,
the new area of the reference (mask above 0.5) is refilled with the median
colour of the rest before it reaches the VAE and the vision tower. The
canvas the rest of the graph uses keeps its gray. Without the link nothing
changes.
Prototype of the ratio row, for review. A chip used to stay lit after a
drag changed the shape (16:9 lit on a 1889x1280 canvas), a second tap on
it locked and a third cleared every pad, the portrait/landscape button
threw away hand edits, a locked diamond dragged inward did not move, and
Crop | Pad looked like two more ratio chips.
- A chip is lit only while the canvas (crop plus padding, before Align)
has its ratio; the pick (properties.ausboss_fit_aspect) is dropped when
a gesture leaves it, so a new clip is only fitted to a shape the canvas
still has. Without a picture a tapped ratio waits for the first one.
- Tap = reach the ratio (pad, or crop with Fit on crop), tap the lit one =
back to the whole picture. No other chip state.
- Holding the shape is a padlock button (ausboss_aspect_lock): drags keep
the lit ratio, or the custom one, from the moment you grab a handle.
- The orientation button turns the shape: pad mode pads the current crop
to the turned ratio, crop mode turns the crop box about its centre.
Nothing to turn (no picture, untouched, square): only the labels turn.
- Custom / Source caption, and the real ratio on the size line.
- Fit is a segmented pill; switching it re-reaches a lit ratio.
- Rotation keeps a lit ratio (and a held custom one).
- Dragging the picture where the crop spans the source slides it inside
its padding (slidePadding).
- The editor's Ratio list applies at once; Hold shape is the padlock.
Saved properties and widget values keep their names and meaning; an old
locked chip opens with the padlock on. Pure rules in transform_geometry
(canvasSize, aspectMatches, turnAspect, turnedCrop, padAround,
slidePadding, ratioLabel, isUntouched) with node:test coverage.
The editor's model table had columns for file, folder, size and URL but
none for the row's note, and the form read each row back without it. Save
(from the form or the JSON view) wrote every model with an empty note, so
the grey line under each file name was gone after the first edit. On a
note with model notes, Cancel and Escape also asked to discard changes
when nothing had changed, because the form never matched the saved card.
The table now builds its header and cells from MODEL_COLUMNS, which has
one column for every field a model row keeps, and reads rows back with
rowFromCells. That adds a Note column. tests/workflow_note.test.mjs checks
that the columns cover every kept field and that an unchanged table saves
back the same card.
On frontend 1.53.6 (classic canvas and Nodes 2.0), the LTX 2.3 Video
Outpaint example keeps its five model notes through a how-to edit and
Save; Cancel with no edits closes without asking; a note edited in the
new column and a model added with a note both show and survive a save and
reload. The table fits the dialog without scrolling sideways from 700 to
1800 px wide.
A note saved a few pixels shorter than its content showed a scrollbar and
hid its last row. The LTX 2.3 Video Outpaint note in the smaller example
layout (440 x 810) was 6 px short, so its links sat behind the scrollbar.
The classic canvas keeps a node's saved height; Nodes 2.0 already sized
the note to its content.
The note now measures its card's natural height at the node's width and
grows the node to fit when a workflow opens, after an edit, after the file
check, and when the node's width changes, since the text rewraps. It never
shrinks the node. The load fit runs before ComfyUI snapshots the opened
workflow, so a note that grows on load does not mark the workflow modified.
A corner drag stops at the fit, as it does in Nodes 2.0: holdNodeMinHeight
lifts the node's computeSize height to the measured floor. A card less
than half a pixel short is left alone, because Chrome rounds that away and
draws no scrollbar. Nodes 2.0 is untouched.
Replace it with the version that pads with Image Crop + Rotate + Pad,
with its sample picture and a new thumbnail. Point the README's Load
Image + Pad section at Klein 9B Outpaint, and update the example table,
the outpaint caption, the Krea 2 Outpaint Model Patch help page and the
CHANGELOG. Raise the Registry archive budget to 18 MB.
Keep Krea 2 Outpaint, Klein 9B Outpaint, LTX 2.3 Video Outpaint,
Qwen Image 2.1 Edit + Realign and Simple Video Watermark Remover, and add
Krea 2 Inpaint Masked and Krea 2 Rotate + Outpaint with their sample
pictures. Remove the other sixteen examples and the sample picture only
they used.
Update the README example table and links, the Realign to Source help
page, a test comment, the CHANGELOG and the pyproject description, and
raise the Registry archive budget to 13 MB.