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 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.
Many shared workflows save their loaders blank so people load their own
picture. Pressing Run first started the run, and core Load Image failed
inside it with "[Errno 21] Is a directory". Now the run stops before it
is sent: one message, "Load a picture first:" and the node's name, and
the loader gets a red outline (on the classic canvas and in Nodes 2.0).
It covers core Load Image, Load Image (as Mask), Load Image (from
Outputs) and Load Video, and the pack's Load Image + Pad, Image Crop +
Rotate + Pad, Load Video and both Video Crop + Rotate + Pad nodes (the
file or the local path, whichever the mode reads). It looks at the
prompt the frontend is about to send and walks it the way ComfyUI does,
so bypassed and muted loaders, loaders fed by a link, loaders no output
needs, loaders behind a lazy input and outputs left out of "Queue
selected output nodes" never stop a run. Prompts sent straight to the
API are not checked.
The frontend has no extension hook that can stop a queue, so
app.queuePrompt is wrapped; it only reads, and builds the prompt a
second time only when a blank loader is on the canvas. Anything
unexpected lets the run through unchanged.
When ComfyUI does reject a prompt, a check that reads several inputs
comes back once per input. api.queuePrompt's answer is tidied so each
AusBoss node shows one entry per message, on the input the message is
about. Other packs' nodes are left as ComfyUI sends them.
- A node dragged taller while its preview was off keeps that height when
the preview comes back.
- A new picture with no ratio lit starts with the padlock off, as the
untouched picture has no shape to hold.
The note said "installed" for a model kept in a subfolder while the
workflow's loader still asked for the bare file name, which ComfyUI could
not find and marked red. Such a row now says where the file is ("in
LTXV 2.3/") and has a Use it button that points those loaders at the found
copy. The check stays on the ComfyUI server's own model lists.
The A | B badge stayed in the middle of the picture while the split line
moved. Now A rides just left of the line and B just right of it, on the
picture each one names; with one picture showing, its label sits alone.
Shift is the fine step on every number box, but on whole-number boxes such
as Feather the fine step was the step, so Shift did nothing. There it now
slows the drag four times instead. Pressing or letting go of Shift during
a drag carries on from the current value instead of jumping.
The padlock:
- Reset and tapping the lit ratio turn it off.
- It does nothing on the untouched picture (the row says Source), so a
trim there only trims.
- The row says Held while it is on, and a locked drag splits the new
bands evenly (each move is solved from where the drag started).
- Reset crop also removes the bands the lock added.
- The lit ratio and the lock share the pack's solid teal "on" look;
hover is a light outline.
Shapes and sizes:
- A picture within about 1% of a ratio counts as that ratio: tapping 9:16
on a nearly 9:16 photo adds no 1 px band.
- While a ratio is lit, the line under the picture says what it did,
and "Already 9:16: nothing to add. Pick another ratio or turn it." when
there was nothing to do (a saved ratio re-applies to every new picture).
- The resize warning reads "1.5% taller: each side rounds to 32 px"; its
tooltip names a Step that avoids it, and in pad mode Even out adds a few
pixels of padding so nothing stretches.
- Fit dims and says "pick a ratio first" until a ratio is lit.
- Over the picture, the cursor and small arrows show only the ways it can
move; with nowhere to go it is a plain pointer.
Drawing:
- Added space is drawn in the real fill colour with a faint hatch, and
only the picture the crop cuts away is darkened, so black bands on the
black stage show.
- The rotate knob keeps clear of the padding diamonds and crop squares.
- On a big node the controls stay at most 460 px wide, centred, and only
the picture grows. A new picture fits the picture area to its shape,
never taller than the height you gave it.
Words: Align is "Round canvas to", Local path is "Server file", Resize is
an off | on switch, and the canvas row labels read like the others.
The full editor is built from the node's own controls: the ratio row with
its turn button and padlock, the Fit switch, and number boxes for the
rotation (Shift for 0.1 degree) in place of the coarse slider. Ratios
from ausboss_presets.json that no button shows sit in a More list. The
size box keeps clear of the handles, and the editor's button style no
longer reaches the number boxes.
Switching a preview off and on again (Mask Refine, LaMa Inpaint, Select
Frame, Save Image) left an empty box until the next run, and the node came
back shorter than it was.
Now the node remembers its height when the preview goes off (saved with
the workflow) and returns to it, with the last result in the box. If the
node ran while its preview was off, the box says "Run the workflow again
to see it here" rather than showing an older picture. With nothing to show
yet it says "Run the workflow to see it here".
Mask Refine's AUTO button now says what it does in its tooltip.
With Length on, pressing between IN and OUT was a hidden "move the whole
clip" grip. With a Length as long as the clip it had nowhere to go, so the
bar and IN seemed frozen and nothing said why.
Now a press or drag anywhere on the bar moves the playhead, Length on or
off. The kept part carries a small grip that slides the clip (IN still
slides it while Length is on); a free window slides with its IN and OUT
together. When the Length already covers the whole clip, the Length row
says so: "= the whole clip. Shorten it to move IN."
Also:
- Snap reads "Frames for: LTX (8n+1) / Wan (4n+1) / any"; the stored
values are unchanged.
- "To start" and "Full clip" look like buttons.
- Turning Length off no longer moves the playhead, as the help page says.
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.
Both panels filled only from onExecuted. Going to another workflow tab and
back rebuilds the nodes and restores the tab's results in ComfyUI's store
without an onExecuted, so they said "Run to preview" over a result they
had just made, in both renderers. On onNodeOutputsUpdated (a tab's results
restored) an empty panel loads its node's stored record. The output key
helper moves to shared/prompt_scope.mjs for the three users.
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 stage could be squeezed to about 130 px on all three nodes: the
declared floors counted the rows around it short, and the Clip node's rows
alone take 450 px. A padded portrait clip ended up a sliver in a 16:9
canvas.
- The stage's floor follows its width (stageHeightForWidth: 0.6 x width,
200 to 340 px). The panel's minimum is the measured rows around the
stage plus that floor, so a corner drag stops there; before the panel
is on screen a per-kind estimate stands in. A new node opens at it.
- keepStageRoom grows the node to the same floor when a row appears later
(Resize, a wrapping readout), and sets it as the stage's CSS minimum so
Nodes 2.0, which sizes nodes by content, holds it too.
- exactMinWidth and holdVueNodeMinWidth make 330 the real minimum width in
both renderers (the classic corner drag jumped to 434; Nodes 2.0 went
down to 225 and crushed the chip row).
- The source card's "Choose an uploaded ..." line hides once a file is
chosen, which is the room older layouts need for the new floor.
- The LTX 2.3 example moves the two nodes under the Clip node down for its
new height; the contract and a live load find no overlaps.
Every workflow tab in a page shares one server connection, and ComfyUI
files a node's results by node id. A run from one tab that finished while
another tab was open landed on the open tab's node with the same id. On
Video Crop + Rotate + Pad -> Clip that was a second video under the
buttons that grew the node over the one below; Show Text and Seed also
saved the other workflow's text and seed into this one, and Save Video and
Compare replaced their own result.
- The three Crop + Rotate + Pad nodes stand down ComfyUI's image and video
previews (they send no picture of their own), in both renderers.
- suppressCoreVideoPreview sets hideOutputImages, so Nodes 2.0 no longer
draws Save Video's clip a second time under its player.
- shared/prompt_scope.mjs notes which graph queued each run (the queue
call is wrapped for the prompt id). For a result from another tab, the
AusBoss node here gets this tab's own stored result back, every pack
onExecuted handler returns early, and the result is handed to the owner
tab's AusBoss nodes when that tab is open again. Core nodes are left
alone.
Tests: prompt_scope (scope rules plus a policy check that every
onExecuted handler asks isForeignRun first) and core_preview (every
transform kind suppresses both previews; video suppression sets
hideOutputImages) fail on main and pass here.
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.
Undo and redo rebuild every node in place, with the same ids. Nodes 2.0
usually keeps each node's face, and the slot that holds a DOM panel only
mounts an element when it is first made. So after Ctrl+Z every AusBoss
node kept showing the removed node's panel, the rebuilt node's panel
never reached the screen, and changes made on that face never reached
the queued prompt.
When an AusBoss node is removed, the panels it has on screen are
remembered by graph, node id and widget name. When a node with that id
is configured, each of its off-screen panels takes the place of the
remembered one if that one still sits on this node's face. Classic mode,
new and pasted nodes, and faces the frontend mounted itself are left
alone.
A transform node is created on the first file in the input folder. When
a workflow saved with an empty loader configured it blank, the node kept
remembering that first file, because a blank source never replaced the
remembered one. Picking the first file then looked like no change: the
workflow's saved rotation, crop and padding stayed and a lit ratio was
not refit. Any other file reset as it should.
The node's first sync and a workflow load now remember what the node
holds, blank included (rememberedSource in transform_geometry.mjs). Only
a blank the user makes, such as Local path before a path is typed, keeps
the last source, so going back to the same file is still no change.
Covers the image node and both video nodes.
In Nodes 2.0 a node's saved height is only a minimum and its content can
make it taller. The preview panel's picture was in normal flow, so a
portrait result drawn at full width grew Save Image from 730 to 1276 px in
the Krea 2 Outpaint example, over the Run Timer under it. Nodes 2.0 also
drew its own copy of the result under the panel, because the panel never
set hideOutputImages.
- The stage's picture is out of flow and centred, so the stage gets its
size from the node and the picture scales to fit, in both renderers.
- suppressCoreImagePreview sets hideOutputImages; classic never reads it.
- In Nodes 2.0 that copy was the only thing filling node.imgs, so the
panel falls back to the node's stored output (app.nodeOutputs) and
re-reads it when a workflow tab restores its outputs. node.imgs still
wins where it exists, so classic keeps its old path.
Select Frame, Mask Refine and LaMa Inpaint share the panel and grew the
same way. Core Save Image and Preview Image keep their size in Nodes 2.0.
Classic is unchanged.
Tests: the stage-picture guard in panel_guards, hideOutputImages in
core_preview and the stored-output lookup in input_preview fail on main
and pass here.
Every card and DOM panel was added with options.serialize = false, which
only keeps a widget out of the queued prompt. The frontend saves each
widget whose own serialize is not false and hands saved values back by
position, so each card and panel saved an empty value of its own after the
node's real values, and an input appended to the node later opened holding
it (Stitch Inpaint's seam, Crop For Inpaint's keep_inside). Every DOM widget
now also sets widget.serialize = false, as the frontend's own previews do,
so new saves carry only real values.
Older saves still end in those empty values, and loading them now skips
the cards and panels. On every node that saves by position they come after
all real values (Save Image's card sits mid-list, but Save Image saves by
name), so nothing moves. On frontend 1.53.6 the examples (the 21 from
before the trim and the 7 now) and 14 older saved workflows queue
byte-identical prompts before and after, keep every value, and save and
reopen cleanly.
An input appended later still needs a fallback for those older saves.
tests/saved_widget_values.test.mjs checks that every DOM widget sets the
flag, that the examples keep their values with the panels skipped, and that
each input appended to a node whose older saves end in an empty value is
declared and, as a widget input, has a card resetUnknown fallback that
fires. tests/test_card_fallbacks.py (ComfyUI's Python) checks each fallback
is the input's own default. AGENTS.md states the rule.
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.
Resolution Master is renamed Latent Size: the old name belongs to another
node pack. The node id AUSBOSS_NODES_Resolution is unchanged, so saved
workflows and API graphs load and run as before. Workflows only store a
title that differs from the default, so they pick up the new name; a saved
title that starts with "Resolution Master 🆎" gets the new name in its
place on load. The example, its thumbnail and the README image follow.
- Latent Size can be resized: wider stretches the chips, fields and size
picks, and taller gives the stage the room. It opens at 340 x 531 as
before and goes down to 320, where the size row tightens so W, H and MP
stay whole.
- LoRA Loader: gear -> Long names, end (the default, as before) or middle,
which keeps the checkpoint number at the end of a name in view. The
picker opens wide enough for long names and shortens the folder first.
- The frontend adds 104 px to a DOM panel's minWidth before using it as
the node's floor (LGraphNode.computeSize), so a node that opened
narrower jumped wider the moment its corner was touched: the LoRA Loader
could not go under 424. fillNodeHeight gains exactMinWidth, which
measures that padding and takes it back off, and holdVueNodeMinWidth
lends the floor to Nodes 2.0, whose corner drag reads the node's inline
min-width instead. Both nodes use them; the other panels keep their old
floors for now.
- README: the workflow-tools image shows Latent Size alone; the Seed card
is gone.
Once the readout held a time, drawReadout ran a canvas filter blur on the
graph canvas three times a frame (LED halo, digits, tenths). Chrome sends
every filtered draw through an offscreen layer the size of the whole
canvas, so the entire graph redrew about 5x slower: 60 -> ~17 fps while
panning a test graph (issue #77).
The blurred passes now go into a small cached layer (cachedLayer in
js/shared/run_timer.mjs) that is repainted only when the digits, LED,
size or zoom bucket change; each frame copies it with drawImage and draws
the crisp LED and digits directly. The readout is pixel-identical within
one colour step.
- Qwen Image 2.1 Edit + Realign: type a short edit and Qwen3-VL 8B, the
text encoder the edit already loads, writes the full instruction. The
consistency LoRA sits in a LoRA Loader row, switched off.
- LTX 2.3 Video Outpaint: the distilled LoRA and the outpaint IC-LoRA load
in one LoRA Loader instead of two core loaders. The workflow checks now
refuse core LoRA loaders in examples.
- Realign to Source: description, tooltips and help page rewritten in
plain words, with the technical detail at the end of the help page.
- Settings menus: in a short window the rows no longer squeeze together
and overlap; the menu scrolls instead.
- AGENTS.md: text people read starts with the plainest version.
An optional source_image socket pads an image from another node instead
of the chosen file, so a step before the pad (flattening a transparent
picture onto white, say) no longer has to be saved and loaded back.
- The wire wins over the file; validation skips the file check while it
is connected and runs core's link-type check itself, since asking for
input_types turns core's off for the node.
- A wired run hands the canvas a small temp preview (one file per node,
overwritten each run) and the true source size, so the stage draws the
last image it padded; before the first run it shows an outline.
- Card rows gain linkedBy, so the Source row dims while a wire replaces it.
- Appended and optional with no widget: saved workflows and API prompts
load unchanged. Node API snapshot refreshed.
Resolved:
- node_image_crop_rotate_pad.py: optional inputs are the four resize
inputs, then stitch_blend, then stitch_grow; node_api.json regenerated
with test_node_api.py --update (no breaking change against main).
- test_transform_outputs.py: the optional-inputs test expects the stitch
inputs after the resize ones.
- transform_editor.mjs: the Inpaint & Stitch section and its status line
serve the image node too (feather branch) beside the size-chain status
(this branch); the Cancel code is #73's, line for line.
- LTX example note and the Clip help page carry both branches' wording.
- CHANGELOG keeps every bullet.