Commit Graph
66 Commits
Author SHA1 Message Date
ausboss 4b5d6429d8 Rename Compare to Image Compare A/B
The old display name led with the mechanism. "Image Compare A/B" says
what the node is first, which is what someone scanning the menu reads.
"image compare" joins the search aliases; "compare" and "a/b" stay.

The mapping key AUSBOSS_NODES_Compare is untouched - it is the
workflow-compatibility contract - so saved workflows are unaffected.
2026-08-17 22:15:16 -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 772f78d031 Remove Pad Image
Load Image + Pad does the same job from the same stage and starts from
the file, so the only thing the second node added was taking an IMAGE
wire - one idea holding two slots in the menu.

Deletes the node, its panel, its help doc and its stage-only shared
helpers (padEmptyStateText, findPadPreview), and drops it from
NODE_MODULES, PUBLIC_NODE_IDS, the README and the panel guard list.

The padding helpers are untouched; Load Image + Pad and Align Image both
use them. Two console labels there named the removed node, so
_fill_tensor now takes the source name - Align Image passes its own
pad_color, and a colour it cannot read is no longer reported against
padding. The BHWC and mode errors read "Padding"/"Pad mode" instead.

The on-node pad stage moves to ComfyUI-AusBoss-Lab, which has its own
AUSBOSS_LAB_PadImage.
2026-08-17 21:59:07 -05:00
ausboss 139082a9d9 Fix LoRA mismatch detection, LM Studio reasoning, Compare height; add pad stitcher
Six fixes found while testing every node against real workflows.

LoRA Loader: the "wrong base model" warning never fired on a modern stack.
It matched comfy's model class name against a table covering 8 of the 81
classes comfy ships, so Krea 2, Qwen, WAN, LTXV, Z-Image and friends read as
"unknown" and skipped the check entirely - an SD 1.5 LoRA on Krea 2 patched
nothing, changed nothing, and said nothing. It now measures the applied-patch
count across model and CLIP before and after each row and warns when a row
patched nothing, which needs no table, no LoRA metadata, and works on every
model family. base_model_family also stopped mining ss_sd_model_name: that is
the trainer's source filename, and reading substrings out of it labelled any
..._v1.safetensors as SD1.5 and anything containing xl as SDXL.

LM Studio Chat: reasoning returned in the server's own reasoning_content field
now reaches the thinking output. Only inline <think> tags were read, so with
gemma/qwen-style hybrids the whole reply landed in a field the node never
looked at and text came back blank. An empty answer now explains itself,
naming the max_tokens budget when the model ran out of room mid-thought.

Compare: the stage follows the node's height, not only its width. Its panel
declared a computeSize, and the widget layout gives any widget that defines one
a fixed height and leaves it out of the leftover-space split, so dragging the
node taller only added dead space under the image.

Pad Image / Load Image + Pad: a stitcher output, so an outpaint can put the
source back exactly. Padding now builds the same stitcher shape Crop For
Inpaint emits - the crop is simply the whole canvas - so the existing Stitch
Inpaint node serves both and no second stitch node has to exist. Appended
last, so saved workflows keep their links.

Settings menus: editing one row no longer reverts the others. Each save handed
back a value set rebuilt from stored defaults, silently replacing every other
row on a node whose workflow differed from those defaults.

LoRA templates popover: Save was clipped off the panel edge and each saved
row's delete x was stretched across the whole row - one blanket
.ausboss-lora-menu button rule outranked the specialized buttons inside it.
2026-08-17 20:47:27 -05:00
ausboss 6f3adb1ad9 Release 1.1.1: fix the registry metadata 1.1.0 shipped wrong
The ComfyUI version floor never reached the registry. comfy-cli reads it from
a [tool.comfy] key named requires-comfyui; the pack declared
supported_comfyui_version, which the parser ignores, so 1.1.0 published with
an empty floor and Manager had nothing to refuse an incompatible install on.

Also declares Operating System :: OS Independent, which is where the registry
sources supported_os from, and publishes the license as a readable name rather
than the literal string {"file": "LICENSE"} that a file reference
serializes to. Accelerator classifiers stay off rather than asserting hardware
nobody has tested.

Verified by parsing pyproject.toml through comfy-cli itself: the floor now
reads >=0.27.1 and supported_os reads ['OS Independent']. No node behaviour
changes.
2026-08-17 17:48:26 -05:00
ausboss cc477c4ec4 Release 1.1.0
Closes the Unreleased section into 1.1.0 and syncs the version across
pyproject.toml and AUSBOSS_JS_VERSION, which release_preflight.py compares so
a fresh install never trips the stale-browser warning.

First version published to the Comfy Registry. 1.0.0 was never tagged or
released anywhere, so it stays as changelog history rather than becoming the
registry's first entry describing a fifth of what it contains.
2026-08-17 17:38:38 -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 4e435e3013 Trade the display-name suffix for the 🆎 signature and unclip the chat toolbar
Every display name swaps ' (AusBoss)' for ' 🆎' - menus, docs, tooltips,
the README, and the example workflow's notes. Mapping keys never change,
so saved workflows keep loading; 'ausboss' keeps finding everything via
the category, the AUSBOSS_NODES_ prefix, and SEARCH_ALIASES. Python error
messages drop the suffix entirely so exception text stays ASCII for
cp1252 consoles.

The LM Studio endpoint toolbar declared no getMinHeight, so the frontend
gave its wrapper a 12px sliver and the buttons clipped; it now declares
32px through every sizing path, and the buttons breathe at 22px.
2026-08-16 20:29:38 -05:00
ausboss 0303d42e61 Fix A/B Compare's HOLD trap with dedicated mode buttons
In hold mode the stage captured the pointer on press, which retargeted the
release and ate the mode button's click - one way into HOLD, no way out.
SLIDE and HOLD are now two labeled buttons, and presses born on the
toolbar never reach the stage behaviors.
2026-08-16 19:42:31 -05:00
ausboss 43cffbce0d Restyle the LoRA Loader around one control language and a gear menu
Full-width filled Add button, master-toggle bar with the gear, rows in an
inset stack with a dashed empty state, system-ui info glyph. The gear
holds default strength, strength step, separate model/CLIP strengths, the
trigger-word separator (a hidden widget the output honors), hide-extension,
thumbnail, and Civitai-lookup preferences.
2026-08-16 19:42:22 -05:00
ausboss f39908de05 Adopt the brand scheme pack-wide and add the title-bar help badge
The teal pair the video nodes hard-painted becomes the AusBoss scheme row
and the pack-wide default; Load/Save Video stop painting themselves so the
appearance setting and per-node menu govern every node the same way. Every
AusBoss node also gets a quiet ? badge whose card is built from the node's
own DESCRIPTION and tooltips.
2026-08-16 19:42:15 -05:00
ausboss ae931c9b36 Add shared per-node settings and help-card infrastructure
A schema-driven gear menu (toggles, numbers, text, segmented choices)
persisted per node type in localStorage, a reusable info-card popup, a
widget show/hide helper that keeps widgets_values stable, and a hand-drawn
gear icon - the plumbing the LoRA Loader and LM Studio gears build on.
2026-08-16 19:42:08 -05:00
ausboss 8ee79fedd8 Discard planted widget widths so panels track their node
The frontend sizes a DOM widget's wrapper as widget.width ?? node.width
(GraphView, useDomWidget updateWidgets), and LiteGraph's layout plants
widget.width during draws. Once planted it outranks the node width
forever: resize the node narrower and the wrapper keeps the old width,
parking the panel's right edge - and the LoRA row's strength and info
controls - outside the border. None of the containment guards could
reach it: border-box and the overflow clip act at the wrapper's edge,
which is precisely the thing that is too wide, and the minimum-width
paths only stop the node going BELOW a floor, not the wrapper staying
ABOVE the node.

Diagnosed from a console measurement in the reporting browser (wrapper
styled 470px on a 328px node, scale 1.21) and reproduced exactly by
planting widget.width = 470: overflow +183px. keepDomWidgetWidthAuto
defines width as a discard-writes accessor, so the frontend's lookup
stays on its node-width fallback for the life of the widget. All six
DOM panels call it, the policy test requires it, and the fix was
verified end-to-end in a live classic-renderer session: with the plant
attempted, the wrapper tracked the node through narrow, add-row,
linked, grow and shrink at -13px inside the border throughout.
2026-08-15 21:50:39 -05:00
ausboss 5e6cdb2ecc Fix the fifteen defects the review found
Frame Chooser, mostly. A pause did not survive a workflow load: LiteGraph
clears a graph by removing every node, so undo, a tab switch, Clear
Workflow and opening another file all fired the teardown a deleted node
uses to release its pause, and killed a run that was still going. That
now only fires for a real deletion. Answering "keep all" wrote the batch
out as 1,2,...,N into pick_list, so the next run dropped whatever the
enumeration did not reach or failed outright on a shorter clip; it
writes the empty answer it was given. And pick_list is only written back
under "keep last selection" - it pre-answers the node, so filling it in
automatically meant a chooser left at the default paused exactly once.

Refine Mask: smooth applies only the change it made to the jaggy edge
rather than binarizing everything, so an already-soft matte keeps its
alpha (binary masks stay bit-identical), and blur now reaches the
matting solve instead of being thresholded back out of the trimap.

Frame Interpolate's copy path honours batch_size; it gathered every
copied frame at once, which on a long clip at an integer multiple was a
multi-gigabyte allocation on the frames device that no setting bounded.

Subgraph execution ids are resolved by walking the chain instead of
stripping the prefix, which could match an unrelated node with the same
number. Status and runtime badges appear inside subgraphs again, and a
subgraph pause renders on the node that actually paused.

Also: the Custom colour scheme no longer strips every node's colour when
its stored colour is unreadable; Drop Shadow and Pad Image name their
own widget for an unreadable colour instead of blaming Transform; a
non-ASCII token and a non-object JSON body answer 400 rather than
throwing a 500 and stranding the pause; pick_list rejects Unicode
digits, which used to raise during validation or silently read as a
frame nobody typed.

Tooling: validate_nodes parses NODE_MODULES rather than matching it
anywhere in the text, and an unregistered module is an error - it used
to exit 0 printing nothing. It also refuses mapping keys declared
outside nodes/, which is how the registry fixtures came to be advertised
to ComfyUI-Manager as installable nodes; they are .py.txt now. And the
video test no longer leaves ComfyUI on sys.path, which was flipping
every fail-soft seam for whatever ran next.
2026-08-15 00:00:15 -05:00
ausboss 75eb4e732a Merge pull request #24 from ausboss/feat/ui-adoption
Polish frontend adoption and appearance controls
2026-08-14 10:34:58 -05:00
ausboss 5016d96ba9 Settle a chooser pause once, whoever gets there first
Continue, cancel and the expiring countdown were all writing straight to
the pending object and reporting success. Two clicks a frame apart both
returned 200 with the second selection winning; a cancel behind a keep
turned a continuing run into an interrupt; the timer could discard an
answer the browser had just been told had landed.

Every terminal path now goes through claim_pause, which records the one
outcome under the store lock and tells every later claim it lost. The
route answers 410 for those, the panel treats 409/410 as already-handled
rather than an answer failure, and a resolved pause stops being offered
to a page that reloads before its waiter unwinds. A rejected selection
claims nothing, so the cancel a deleted node posts behind it still
releases the graph.

tests/test_chooser_pause.py drives the races with real threads; ten of
its nineteen tests fail against the previous unguarded write.
2026-08-14 08:29:02 -05:00
ausboss 60d22f9d8e Guard the Frame Chooser against a double-submitted answer
Keep selected, Keep all, Cancel, Enter, Escape and the cancel a deleted
node sends all posted without checking whether an answer was already in
flight, so two quick Enters put the same one-use token on the wire twice
and the duplicate's 409 landed as "Answer failed" over the success the
first request had already painted. Every path now goes through a latch on
the panel state: a second submit never fires, the controls read as
unavailable until the reply lands, and the latch clears on success or
failure so a genuine network error can still be retried. Each submission
also captures the pause it was posted for, and a reply is dropped when the
panel has since resolved or moved to another token - which covers both the
late rejection and a new pause arriving on the same node mid-flight. The
latch and the staleness predicate live in js/shared/frame_chooser.mjs with
node:test coverage.
2026-08-13 23:00:05 -05:00
ausboss a0f77f7587 Keep a custom tint's title and body visually distinct 2026-08-13 20:16:48 -05:00
ausboss e7f2bf147a Add live per-node progress badges
Nodes can push a status line to their own badge through the new
push_node_status helper, which is a silent no-op without a live
PromptServer so a status update can never fail the run reporting it.
The LaMa inpainter emits "frame i/N" alongside its progress bar,
threading the node id in through a hidden UNIQUE_ID input -- hidden
entries never become widgets, so its visible inputs and saved
widgets_values are unchanged.

The badge rides its own Chrome setting, on by default: unlike the
runtime badge it only exists while a node is running, and it is
retired when the node moves on or the run ends, fails, or is stopped.
2026-08-13 20:13:58 -05:00
ausboss 0766fca721 Polish the Frame Chooser panel with keys and unseen-pause notices
A pause that starts out of sight - hidden browser tab, node scrolled off
the canvas, or another workflow or subgraph fronted - now raises one
toast naming the node and pulses the waiting workflow tab when the
frontend exposes it. A filmstrip the user can see stays quiet, and the
notice latches on the pause token so re-renders never repeat it; the
check runs a beat after the pause lands, once the canvas has placed the
widget. The focused panel takes 1-9 to toggle a frame, A and N to flip
the whole selection, Enter to keep, and Escape to cancel, consuming only
mapped keys and leaving anything typed into a field alone. Clicks inside
the panel are ignored for 400ms after it appears, so a click aimed at
the canvas cannot answer a pause that just popped up. The decidable
parts - key map, cooldown, visibility - live in js/shared with
node:test coverage.
2026-08-13 20:12:44 -05:00
ausboss a7ef883e81 Add a custom node tint setting 2026-08-13 17:56:02 -05:00
ausboss 5ed13dfc13 Reject stale Frame Chooser answers 2026-08-13 17:55:53 -05:00
ausboss 497ab28ec1 Add countdown timeout policy to Frame Chooser pauses
New optional widgets timeout_seconds (0 = wait forever) and on_timeout
(keep all / keep first / keep last / cancel). The blocking wait arms a
monotonic deadline, emits a once-per-second tick event the panel renders
as a countdown in the header, and applies the policy on expiry; cancel
raises the same interrupt as pressing stop. A done event releases the
panel in every open tab when the timer answers.
2026-08-13 17:55:53 -05:00
copilot-swe-agent[bot]andausboss a5625ccdb6 Merge remote-tracking branch 'origin/main' into feat/video-bundle-compare
# Conflicts:
#	__init__.py
#	scripts/validate_nodes.py

Co-authored-by: ausboss <102309304+ausboss@users.noreply.github.com>
2026-08-13 16:35:53 +00:00
ausboss c745db25c5 Merge pull request #19 from ausboss/feat/video-core-robustness
Video core robustness: memory guard, lazy audio, bt709 tagging, mp4 drag-back
2026-08-13 11:30:54 -05:00
ausboss 96993a3add Merge pull request #10 from ausboss/feat/editor-polish
Editor polish: filename tokens, tolerant colors, Alt+E, aspect presets
2026-08-13 11:30:33 -05:00
ausboss 7a79fcce41 Merge pull request #15 from ausboss/feat/status-chrome
Status chrome: favicon queue status and per-node runtime badges
2026-08-13 11:30:10 -05:00
ausboss 302ea79203 Merge pull request #14 from ausboss/feat/lora-v2
LoRA Loader v2: master toggle, range tints, folder picker, hover thumbnails
2026-08-13 11:29:50 -05:00
ausboss 0c2106e59d Merge pull request #17 from ausboss/feat/frame-chooser
Add the Frame Chooser node
2026-08-13 11:28:00 -05:00
ausboss 9b14b78d43 Serialize widget values by name on the video and transform nodes
Load Video, Save Video, and both Crop + Rotate + Pad nodes now write
workflow widgets_values as a {widgetName: value} object and restore by
name on configure, so adding or reordering a widget can no longer
scramble saved workflows. Legacy positional arrays still load through
an order-based migration that validates each value against its widget
(number ranges, combo membership, booleans) and repairs misfits to the
creation defaults. An unreadable payload warns once and falls back to
defaults instead of breaking the load. The queue path is untouched:
prompts read widget.value by name and never consult widgets_values.
2026-08-13 09:48:13 -05:00
ausboss 67c430c7f1 Accept and display timecodes in the Load Video trim fields
The IN/OUT fields now render h:mm:ss.s timecodes (95.5 shows as 1:35.5)
and accept typed input as bare seconds, m:ss, or h:mm:ss. Malformed
entries restore the previous value instead of jumping the handle to
zero. The center label gains a quiet selected-of-source readout with
the source resolution and duration in its tooltip, and the inputs
carry ghost hints for the accepted forms.
2026-08-13 09:48:13 -05:00
ausboss 8446c7651a Restore embedded workflows from dropped mp4s 2026-08-13 09:44:54 -05:00
ausboss d41b316efd Merge remote-tracking branch 'origin/main' into feat/frame-chooser 2026-08-13 09:43:47 -05:00
ausboss b8a29e9525 Add the Frame Chooser filmstrip panel 2026-08-13 09:43:08 -05:00
ausboss a0667f8c93 Merge remote-tracking branch 'origin/main' into feat/video-bundle-compare
# Conflicts:
#	__init__.py
#	scripts/validate_nodes.py
2026-08-13 09:42:35 -05:00
ausboss 728bf63a1f Add the A/B Compare node with a slide and hold panel
An OUTPUT_NODE that saves temp previews of each batch's first frame
and renders them stacked in a DOM panel: slide mode clips B to the
pointer with a seam line, hold mode flashes B while pressed. image_a
passes through unchanged so the comparison can sit mid-graph. Pure
clip and mode logic lives in js/shared/compare.mjs under node:test.
2026-08-13 09:40:56 -05:00
ausboss 2f39804290 Add per-node runtime badges after a run
AusBoss.Chrome.NodeRuntime (default off - it adds visual noise) times
each node from the api's executing events: execution_start resets the
accumulator and clears stale badges, every node handoff closes out the
previous node's elapsed time, and revisits from batched loops sum. The
elapsed seconds land on the node object and a chained
LGraphNode.onDrawForeground (installed once, via chainCallback) draws a
small rounded badge at the node's top-right, skipped below canvas scale
0.5. The duration formatter (49ms / 1.50s / 1m35s) and the
accumulate-elapsed transition are pure in js/shared/chrome.mjs with
node:test coverage.
2026-08-13 09:39:25 -05:00
ausboss a6e8a89784 Show queue status in the favicon and tab title
New pack-wide chrome feature: while a prompt is executing the tab icon
swaps to a runtime-drawn active variant of the brand teal rounded
square, and the tab title carries a "(N)" queue-depth prefix that
disappears when the queue drains. AusBoss.Chrome.FaviconStatus
(default on) tears everything down through one AbortController and
restores the browser-default icon and title immediately when disabled.
Title composition is pure in js/shared/chrome.mjs with node:test
coverage, including the never-double-prefix guarantee.
2026-08-13 09:39:07 -05:00
ausboss 460e946778 Polish the LoRA Loader rows, picker, and info card 2026-08-13 09:38:45 -05:00
ausboss 5ef00235d7 Merge remote-tracking branch 'origin/main' into feat/editor-polish
# Conflicts:
#	.gitignore
2026-08-13 09:35:19 -05:00
ausboss 148c11eb6c Merge pull request #7 from ausboss/feat/lora-loader
Add the LoRA Loader node
2026-08-13 09:33:53 -05:00
ausboss 2a656a3af2 Merge pull request #9 from ausboss/feat/appearance-refinements
Appearance: adaptive title ink, per-node color menu, subgraph sweeps
2026-08-13 09:33:30 -05:00
ausboss 603eac1e69 Merge pull request #8 from ausboss/feat/reliability-hardening
Reliability hardening: undo tracking, stale-cache warning, preflight
2026-08-13 09:31:48 -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 b350c7af54 Merge branch 'feat/reliability-hardening' into feat/appearance-refinements
# Conflicts:
#	js/appearance/index.js
2026-08-12 22:56:15 -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 d6875fcaf8 Add the LoRA Loader node with stacked rows and trigger words 2026-08-12 22:54:06 -05:00
ausboss 098a57080d Parse fill_color tolerantly on both sides 2026-08-12 22:53:56 -05:00
ausboss 350c886928 Preview the upstream input on Refine Mask and LaMa Inpaint 2026-08-12 22:52:13 -05:00