Commit Graph
55 Commits
Author SHA1 Message Date
ausboss 5bf037d9b9 Crop + Rotate + Pad: the amber note under the size keeps still while you drag
With Resize on, the note that says how much the rounding stretches the
picture came and went every few pixels of a drag, and each time the picture
area and the rows under it jumped by a line.

- No note appears or changes while a pointer is down. A note the drag makes
  untrue goes away, and the note for the new size comes once, 0.2 s after
  the release. A click, a typed number or a new picture still shows its
  note at once; a run of arrow clicks or a held arrow key shows the first
  value and the last.
- The note has a line of its own that is there whenever Resize is on, with
  text or empty, so a note that comes or goes moves nothing. The Even out
  button no longer makes that line a pixel taller.
- The size line only grows while you drag: a step that comes and goes
  ("round to 8") no longer wraps it in and out.
- The full editor's size box and status panel show the same settled note,
  and the status panel keeps its height mid-drag.

The image node and both video nodes share this code. Even out now works its
padding out at the click, because the note can be a moment behind the values.
2026-10-13 16:22:14 -05:00
ausboss 4fe448ccbe Video Crop + Rotate + Pad: pick an output video from a list, nothing is typed
The second source tab was Server file, a box for a typed path. It is now
Outputs: the videos in ComfyUI's output folder, newest first, in the same
list with a preview that the Uploads tab uses. The list asks the server
each time it opens (/ausboss/transform/video/outputs, no parameters), so a
video rendered a moment ago is in it.

The picked video is saved the way ComfyUI names a file in one of its
folders ("clip.mp4 [output]"). resolve_server_file reads that form and
nothing else: a whole path, a drive, "~", ".." and a network path are
refused from the text alone, and a link that leads out is refused once the
path is resolved. A workflow saved with a typed path shows nothing picked
and a line that asks for the pick.
2026-10-13 11:45:31 -05:00
ausboss 97a51f7db5 Server file takes a folder and a name; no route sends a file by its path
Video Crop + Rotate + Pad (Frame and Clip): Server file mode read a whole
path that had to lie inside ComfyUI's input, output or temp folder. It now
takes the folder and the name (output/clip.mp4) and joins them below that
folder. A whole path, a drive, "~", ".." and a network path are refused
from the text alone, and a link that leads out is refused once the path is
resolved. The hint under the field says what to type when a whole path is
in it.

LoRA Loader: the preview route sends a small JPEG made from the picture
beside the LoRA instead of the file itself.

resolve_input_path refuses when ComfyUI's folders are missing instead of
opening the name unchecked. A route's error text that names a place on the
server's disk is replaced whole (nodes/_route_errors.py), on the LoRA
routes too.
2026-10-13 10:34:11 -05:00
ausboss 74de1107c6 Crop and Pad loaders: list a picture the MaskEditor saved
ComfyUI calls a media input missing when its value is not in the
widget's list. The MaskEditor writes the file it saved into the image
widget, and no list has that file until the lists are refreshed, so
the node was flagged the next time it came out of bypass. Both nodes
now add a picture to their list once it has loaded. Load Image + Pad
also follows a picture written that way, as the crop node does.
2026-10-12 21:48:45 -05:00
ausboss 5ec02ef356 Divisible by: no strip of fill on an edge without padding
Image Crop + Rotate + Pad and both Video Crop + Rotate + Pad nodes rounded
the canvas up to Divisible by and put the pixels left over on the right and
bottom, whatever was padded. A 1916 x 821 picture with 3 px of padding on
the left and Divisible by 24 came to 1920 wide, with 1 px of fill on the
right edge, which had no padding. With Resize on, that strip was smeared
into the picture's last column, and the stitched result kept it as a light
line.

The canvas now rounds one axis at a time by Load Image + Pad's rule
(_pad_helpers._axis_layout): the leftover joins a side that was padded, the
right or bottom one when both are, and an axis nobody padded has its crop
trimmed evenly to the multiple below. A picture smaller than one multiple
still grows on the far side. With no padding on an axis the size therefore
rounds down, not up: 1000 x 750 at 64 gives 960 x 704, was 1024 x 768.
Inputs and outputs are unchanged.

The editor uses the same rule (axisLayout, resolvePadding and keptCrop in
js/shared/transform_geometry.mjs), so the picture on the node, the size
line and the editor match the run. The size line names fill added on any
side and pixels trimmed, and the centre buttons count fill that sits on the
left or top. The Divisible by tooltip and help pages describe the rule, and
the Krea 2 Outpaint Model Patch warning no longer says Divisible by rounds
the other axis up.

tests/test_transform_engine.py and tests/transform_geometry.test.mjs share
one fixture table, so the editor and the run are held to the same numbers.
2026-10-11 20:41:52 -05:00
ausboss d0910073b1 Show blend: no tint on an edge without padding
In the Crop + Rotate + Pad editor, Show blend could draw a teal ramp along
the bottom or the right edge of a picture where there was no padding. The
run never blended there: only the preview was wrong. A 1916 x 821 picture
with 100 px of padding on top showed it along the bottom.

The preview drew the generated area on a small raster with one scale for
both axes, while the raster's size was rounded per axis. 921 rows came to
184.58 raster rows on a raster 185 tall, so the last row was only part
covered by the picture and read as generated, and the blend grew that row
into a ramp.

generatedAreaMask (js/shared/stitch_preview.mjs) now works the area out:
the turned picture cut to the crop, mapped onto the raster with each axis
by its own scale, each cell's share measured. An edge of the picture on the
canvas edge is exactly 0, also after a quarter turn. See-through parts are
still drawn from the picture, on the same raster. Feather, grow and blend
are unchanged, and so are the ramps at real seams.

tests/fixtures/show_blend_canvas.json holds the run's blend for eight
settings at the preview's size. tests/test_show_blend_fixture.py holds it
to the run and tests/stitch_preview.test.mjs holds the preview to it.
2026-10-11 16:54:16 -05:00
ausboss 02a07b84f3 Image Crop + Rotate + Pad: Painted area can keep the picture under the paint
A part painted in the MaskEditor (see-through in the saved file) was
always handed on as flat fill. The new optional painted_area input,
"fill" by default, can be set to "keep picture": the mask is unchanged
and the image, original and prompt_image outputs carry the stored
picture there. The editor's Inpaint & Stitch section has the switch.
2026-10-08 22:50:12 -05:00
ausboss 1ff1a863fb Crop editors: spell Center the US way
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.
2026-10-06 17:49:38 -05:00
ausboss be8b071c55 Ctrl + Enter runs the workflow from the boxes in our popups
2.6.2 let app shortcuts through from the boxes on a node face. Six more
boxes still stopped every key: the LoRA picker's search box, the trigger
word and template name boxes, the gear menu's boxes, the picture and video
picker's filter, and the server file path box on the video nodes. They use
keepKeyInField now, and a search box no longer treats Ctrl + Enter as a pick.

A test reads every keydown handler under js/ and fails on one that stops a
key before looking at it. The Workflow Note editor and the keep or discard
question are the two that own the keyboard on purpose.
2026-10-05 09:35:15 -05:00
ausboss 41bde6a3ff Show the MaskEditor mask on Image Crop + Rotate + Pad's picture
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.
2026-10-02 20:02:24 -05:00
ausboss 7d019d15d7 Add corner handles and Centre buttons to Crop + Rotate + Pad
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.
2026-10-02 19:45:54 -05:00
ausboss 0257e71ab8 Let the rotate knob turn past upside down
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.
2026-10-02 19:45:10 -05:00
ausboss 8988c63f27 Divisible by: explain Round canvas to and step it 1, 8, 16
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.
2026-10-02 19:18:16 -05:00
ausboss 0cd8a31fd5 Crop + Rotate + Pad: keep the Resize off | on pill inside the card 2026-09-30 01:00:59 -05:00
ausboss f7c6803f2b Crop + Rotate + Pad: the new notes no longer make the node taller
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.
2026-09-29 22:04:25 -05:00
ausboss 653cf2c8cc Merge branch 'fix/transform-first-pick-refit' into fix/clip-node-ux
# Conflicts:
#	CHANGELOG.md
#	js/shared/transform_editor.mjs
2026-09-29 21:43:45 -05:00
ausboss 5b2f2508ab Preview and padlock: two leftovers after the first pass
- 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.
2026-09-29 21:40:23 -05:00
ausboss fe7f301264 Crop + Rotate + Pad: clearer padlock, Fit, added space and editor
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.
2026-09-29 21:33:25 -05:00
ausboss 58b450d21e Crop + Rotate + Pad: the editor's Multiple and Steps are Align and Step
The node face calls canvas_multiple Align and resolution_steps Step; the
full-screen editor called the same two settings Multiple and Steps.
2026-09-29 16:41:58 -05:00
ausboss 8ed782de89 Crop + Rotate + Pad: one state per ratio button, a padlock, a real turn
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.
2026-09-29 16:22:27 -05:00
ausboss f1fedbf345 Crop + Rotate + Pad: keep the picture on the node big enough to see
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.
2026-09-29 16:05:38 -05:00
ausboss 5babcc7ed6 Keep a run's results on the workflow tab that queued it
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.
2026-09-29 15:53:03 -05:00
ausboss b62bda9ba2 Crop + Rotate + Pad: the first file after a blank load is a new picture
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.
2026-09-29 05:15:06 -05:00
ausboss 2a3268f934 Cards and panels: leave them out of saved workflows
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.
2026-09-29 03:20:45 -05:00
ausboss c4a0e011e6 Merge fix/crop-rotate-pad: rotation keeps the crop, size readout, Clip Length (B2 dropped)
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.
2026-09-26 22:25:48 -05:00
ausboss d3e2c4bdd5 Merge feat/editor-cancel-discard (#73): the editor Cancel change fix/crop-rotate-pad was built on
# Conflicts:
#	js/shared/transform_editor.mjs
2026-09-26 22:22:59 -05:00
ausboss a3a9be54bf Crop + Rotate + Pad: rotation keeps the crop, size readout, Clip Length
- Rotating keeps the crop's size over the same part of the picture from
  the knob, slider, number box and Reset rotation (cropForRotation).
- A readout under the picture names every step that sets the output size
  (crop -> pad -> align -> resize) and warns when the resize Step
  stretches the picture or Align pads fill ahead of a resize; ticking
  Resize shows Megapixels and Step on the face.
- Locked-aspect crops use integer maths (_ratio_box / ratioBox), so the
  size shown equals the run.
- Image Crop + Rotate + Pad's four resize inputs are optional, so API
  prompts saved before them validate again.
- Video Crop + Rotate + Pad -> Clip: one Length switch replaces the Fixed
  frames and Limit boxes; OUT sits on the last output frame; a connected
  fixed_frames / frame_load_cap takes over the length.
- The stitcher pastes the unfaded canvas when feather > 0
  (transform_pil_layers / keep_unfaded). Superseded by mask-only feather
  and dropped in the next commit.

Carries a copy of the editor Cancel / discard-prompt change from
feat/editor-cancel-discard (#73), which this work was built on.
2026-09-26 22:21:16 -05:00
ausboss 1ef1b31918 Crop + Rotate + Pad: feather softens the mask only; image stitch inputs
The image and video transform nodes faded the picture into the fill
colour across the feather ramp. Outpaint models read that ramp as
content (the Qwen 2.1 outpaint LoRA painted a darker band along a tilted
edge, LTX's IC-LoRA left feathered bars unpainted), and the stitcher
kept the faded pixels, so Stitch's colour match measured its drift
against them and pulled the fill toward grey (d(ab) 18.5 at feather 24 on
a straight pad, 2.6 now). Feather now shapes only the mask and the stitch
blend built from it; the picture keeps a hard edge against the fill, so a
feathered canvas is the feather-0 canvas pixel for pixel.

Image Crop + Rotate + Pad gains the clip node's optional stitch_blend
(32) and stitch_grow (0) inputs, and its editor shows the same Inpaint &
Stitch section with Show blend. The editor's live fade overlay goes with
the fade.
2026-09-26 22:21:09 -05:00
ausboss 235225000c Editors: Cancel with a discard-changes prompt
The Crop + Rotate + Pad editor gains Cancel beside Save & close; Escape
now cancels. Changed edits ask first (shared js/shared/discard_prompt.mjs)
and Discard restores the node to its state when the editor opened. The
Workflow Note editor asks before Cancel, Escape or a backdrop click drops
edits, and a JSON edit carried back to the form no longer outlives Cancel.
2026-09-26 17:59:48 -05:00
ausboss 153eb3a25d Preview the file under the pointer in every Source list
A native select draws its option list itself, so hovering a name gave
nothing to go on: you picked by filename alone. Load Image + Pad, Load
Video and the crop / rotate / pad editors now open a list that shows the
hovered picture, or plays the hovered clip muted with its size and
length, and filters as you type; arrow keys, Enter and Escape work.

The picker (js/shared/media_picker.mjs) only reads the combo widget's
options and writes the chosen value back, so saved workflows, undo and
the API are unchanged. Pure parts live in media_list.mjs with tests.
2026-09-25 09:02:20 -05:00
ausboss 83b8f299f1 Give the three Crop + Rotate + Pad faces the same controls
The image node had a feather on/off checkbox and no fill colour on its
face while the video nodes showed a fill swatch and a feather amount,
and only the image node had a full Reset. All three now show the same
canvas row (fill, feather in px, and the resize budget where the node
has one) and the same Open editor | Reset crop | Reset buttons.
2026-09-24 00:29:49 -05:00
ausboss 216c885b75 Show every pack message through one toast helper
Four extensions each carried their own copy of the toast-or-console
fallback, transform upload failures used a blocking alert(), and a
failed video drop onto Load Video only logged to the console. They all
go through showToast now, so every message looks and behaves the same.
2026-09-24 00:13:30 -05:00
ausboss 4fe99e6764 Make the image transform face Reset match its tooltip
The quick Reset under Image Crop + Rotate + Pad promised rotation, crop
and padding but ran the editor's full reset, which also restored fill,
feather and Align. It now resets only the shape, the same keys a source
swap resets. The editor's Reset transform tooltip names Align, which it
does reset.
2026-09-24 00:06:47 -05:00
ausboss 70c15690e8 Keep the video transform panels visible when zoomed out
The frontend hides a DOM widget below a zoom threshold unless it opts
out. The video transform nodes mounted their panel through the one
addDOMWidget call without hideOnZoom: false, so zooming out left an
empty box. A guard test now checks every DOM widget in the pack.
2026-09-23 23:55:50 -05:00
ausboss 4cb2af204a Release 2.1.0: Qwen workflows and polished image/video controls 2026-09-22 15:23:07 -05:00
ausboss 3fd4d37f85 Contain media reads and saves within ComfyUI folders 2026-09-10 09:45:40 -05:00
ausboss beb98ad6a8 Video timeline overhaul, outpaint-ready clip canvas, example styling
Video Crop + Rotate + Pad -> Clip / -> Frame:
- The rail under the preview is a timeline: playhead scrubbing on the node
  face, frame-accurate IN/OUT handles that carry the playhead, Set IN/OUT
  and I/O keys in the editor; js/shared/timeline_math.mjs mirrors the
  backend's inclusion rule and emits hundredth-grid seconds.
- Scrubbing no longer flickers: storyboard tiles only stand in when nearer
  than the frame on stage and are decoded at their target times; the scrub
  session returns the frame it already decoded instead of the next one;
  served previews are cached.
- Aspect lock on the format chips (pad, lock, clear) with lockPadding in
  transform_geometry; Lock aspect in the editor.
- A new source keeps fill, feather, resize, Snap and Limit and re-pads to
  the lit chip; a canvas row (fill, feather, resize) sits on the node face.
- The clip node declares a black fill and feather 0 as its defaults and the
  editor's Reset all follows the node's own declaration, so a fresh node or
  a reset never lands on the grey, feathered canvas the LTX IC-LoRA cannot
  paint; image nodes keep grey/24.

Examples:
- LTX 2.3 Video Outpaint: Select Frame -> core Generate Text writes the
  positive prompt; note rewritten with the tested rules (53 renders over
  six clips: only fill #000000 + feather 0 decide painting).
- Core model loaders coloured and Workflow Notes grown to fit across the
  other examples.
2026-09-09 17:03:01 -05:00
ausboss 699208dadc Prepare 2.0.0 release candidate 2026-09-07 19:56:23 -05:00
ausboss dd118c7386 Image Crop + Rotate + Pad: megapixel resize, quick row, relocated size readout
- Optional resize of the transformed output to a pixel budget, with core
  Scale Image to Total Pixels semantics: megapixels x 1024^2, aspect
  preserved, each dimension rounded to resolution_steps. The image uses
  the chosen filter (lanczos/area/bicubic/bilinear/nearest-exact); the
  mask always resizes bilinear so feathered edges cannot ring. The math
  is mirrored py+js (scale_to_megapixels / scaleToMegapixels) and
  fixture-tested on both sides; the new widgets append after the stable
  V1 set so saved workflows keep loading, and IS_CHANGED fingerprints
  them.
- A quick row under the node's canvas: Reset (rotation, crop, padding),
  a Feather toggle that remembers the amount it turns off, and the
  Resize toggle with its megapixel scrub box.
- The editor gains a Resize output section (megapixels and steps as
  scrub controls, method select) and its status names the exact resized
  size.
- The output size readout moved off the pixels: centered below the image,
  flipping above when clipped, and showing the resize target.
- CHANGELOG entries for this round and the two commits before it.
2026-08-30 22:14:39 -05:00
ausboss 591f8505d2 Redraw the transform panel when its element resizes
Video Crop + Rotate + Pad drew its preview stretched after a resize. The
canvas is CSS-stretched to whatever box the panel is given, but
prepareCanvas() sizes the backing store from the element at DRAW time, so
a box that changes shape without a redraw shows the last frame at the
wrong aspect ratio.

The panel's ResizeObserver was created inside a `kind === "image"` branch,
grouped with the interactive drag handlers because the comment there
reasons about hit-testing. Hit-testing is only half of what it does. The
image node redrew on resize and looked right; the video node - a passive
preview with no other reason to redraw - did not.

Latent until panels started taking the node's leftover height: before
that the box could not change shape without a width change, and a width
change forced a relayout that redrew anyway.

The observer now runs for both kinds; only the drag handlers stay
image-only. Two tests hold the line - one fails if any canvas-painting
panel ships without a resize observer, one fails if this observer is
gated on node kind again. Verified by reintroducing the bug: the second
test fails and names the offending condition.
2026-08-17 23:18:22 -05:00
ausboss 9262c515e5 Let every node panel follow the node's height
Dragging a node taller only ever widened its stage. The frontend
arranges widgets in one pass:

  if (w.computeSize)            -> fixed height, kept OUT of the split
  else if (w.computeLayoutSize) -> joins distributeSpace(freeSpace, ...)
  else                          -> one standard widget row

It is an else-if, so a widget declaring both is pinned by computeSize
and its computeLayoutSize is never called. Every stage, player and
filmstrip here derived that fixed height from the node's WIDTH, so the
extra height became dead space underneath. Compare was fixed by hand;
the other six had the same defect.

Adds js/shared/panel_layout.mjs with one fillNodeHeight() that deletes
computeSize and declares a floor with no ceiling - distributeSpace reads
a missing maxSize as Infinity, so the panel takes whatever is left.
Applied to Compare, Save Video, Load Video, Frame Chooser, Load Image +
Pad, Image Crop+Rotate+Pad, and the shared transform panel that Video
Crop+Rotate+Pad mounts. Removing computeSize also removed where those
nodes got their opening height, so Save Video, Load Video and Load Image
+ Pad now state a default; a saved size still wins through onConfigure.

Measured in the browser: all seven grow 1:1 with the node and clamp at
their own floor.

The panel audit now enforces it - a DOM panel is either a stage that
calls fillNodeHeight and never assigns computeSize, or is listed as
fixed by design - so a new panel cannot quietly reintroduce this.
2026-08-17 22:15:16 -05:00
ausboss c15c6c9317 Implement the pack audit batch: on-node pad editors, feature gaps, removals
The audit compared every node against ComfyUI core and the major packs;
this lands its outcomes in one branch.

On-node visual editing (the old Codex-pack interaction, rebuilt):
- New Load Image + Pad: image loader with whole-edge pad dragging on the
  node canvas, align-to-multiple rounding, and a megapixel target that
  rescales the source before padding so the mask seam stays binary.
- Pad Image gains the same handle canvas, fed by an execution-time
  snapshot of its input; wireframe with diagnostic empty states before
  the first run. Widgets, outputs, and saved workflows unchanged.
- Image Crop + Rotate + Pad's compact panel is now a live stage - crop,
  pad, and rotate handles on the node, one shared implementation with
  the full-screen editor so the two surfaces cannot drift.

Feature gaps closed against the ecosystem:
- Crop For Inpaint: target_megapixels, selectable rescale_algorithm
  (recorded in the stitcher for the paste-back), and extend_* inputs
  that grow the frame itself for outpainting.
- Save Video: format choice - mp4 h264, mp4 h265, webm vp9 with Opus
  (resampled to 48 kHz); CRF drives all three.
- Load Video: every_nth thinning (fps output divides to match) and a
  max_frames cap that ends the decode early.
- LoRA Loader: saved stack templates behind a bar button, and a console
  warning when a LoRA's base-model family disagrees with the checkpoint.
- LM Studio Chat: history in/out for multi-turn chains and a json_schema
  widget for structured replies.
- Drop Shadow: multiply blend and a shadow_mask output. Align Image:
  offset outputs for un-aligning after sampling. Color Match:
  first_frame reference mode as a one-node video flicker fix.

Removed (backed up in git history): Select Frame Range and the Video
Bundle family - core ImageFromBatch and the core VIDEO wire now cover
both jobs. Dead NODE_ID module variables cleaned out of every node file.

455 Python tests, 216 JS tests, validate_nodes.py green on 19 node files.
2026-08-17 10:49:55 -05:00
ausboss d877784132 Merge branch 'feat/reliability-hardening' into feat/editor-polish
# Conflicts:
#	js/shared/transform_editor.mjs
2026-08-12 22:56:33 -05:00
ausboss c612fd24ec Support user-editable aspect ratio presets 2026-08-12 22:55:32 -05:00
ausboss d4ceccf103 Add Alt+E command to open the selected transform editor 2026-08-12 22:54:14 -05:00
ausboss 098a57080d Parse fill_color tolerantly on both sides 2026-08-12 22:53:56 -05:00
ausboss c383fdf2f0 Notify the graph tracker after DOM widget commits 2026-08-12 22:51:26 -05:00
ausboss ac8cf4832c Attach rotate knob to the rotated image corner, feather QoL, live fade preview
The rotation knob previously anchored to axis-aligned bounding boxes, so
under rotation or padding it floated in space, visually attached to
nothing. It now rides the actual top-right corner of the rotated image
quad (short stem, offset outward along the corner's radial direction)
and orbits with the image as the angle changes - the grab-the-object's-
corner model design tools use. Verified at 0/30/-60/135 degrees: the
drawn knob sits exactly at the computed rotated-corner position.

Feather quality of life: default is now 24 (a no-op until padding or
rotation creates generated area), and the slider is paired with a number
box with single-step arrows for granular control; both stay in sync and
values above the slider range remain typeable up to 4096.

The sidebar Preview now renders the feather fade live: it builds the
generated-area mask (padding, rotation corners, source alpha), blurs it,
draws it twice to match the engine's edge-pinned ramp, tints it with the
fill color, and overlays - so tweaking feather shows the real blend as
you type. Verified: preview pixels near the crop edge move measurably
toward the fill color as feather increases.
2026-07-25 00:21:08 -05:00
ausboss c4d609cf7e Add sidebar section legends and a live output preview panel
Implements the mocked-up sidebar refinements: each section header now
carries a small marker matching its on-canvas handle (cyan square for
crop, green rotate knob, orange diamond for padding), teaching the
editor's color language without explanatory text, and a Preview section
at the bottom of the sidebar renders the actual output composite - fill
background, rotated source clipped to the crop, padded canvas, no
overlays - live while dragging any handle.

Browser-verified: all three markers render, sections appear in order
(Crop, Rotate, Padding & mask, View & reset, Preview), and the preview
canvas resizes and repaints when rotation and padding change, with fill
color in the padding corners and image content in the center.
2026-07-25 00:01:04 -05:00
ausboss e4a0d68573 Fix feather seam, anchor rotate knob to outermost corner, color key buttons
Feathering had two defects: the mask used max(original, blur), and a
blurred step edge sits at ~50% on the boundary, so the mask jumped
255->127 in one pixel - the visible seam line - and the output image was
never blended at all, ending in a hard cut against the fill. The mask
ramp is now pinned to full strength exactly at the edge (double the blur
and clip, then max with the original for thin slivers), and the visible
output fades into the fill color across the same ramp, so outpaints
blend smoothly in both the image and the mask.

The rotation knob now anchors to the outermost top-right corner of
source-union-output, so it stays visually attached when padding grows
the canvas instead of floating where the source corner used to be.

Save & close is now filled with the AusBoss accent; Reset all is styled
as a destructive red button.

22 Python + 9 JS tests pass, including new asserts that the mask ramp
holds >=200 on the first kept column (was ~127) and that the image
visibly blends into the fill. Live-browser verified: knob tracks the
outermost corner under 140/90px padding, buttons computed styles match.
2026-07-24 23:54:43 -05:00