Commit Graph
9 Commits
Author SHA1 Message Date
digital-garbageandClaude Sonnet 5 9e3adf250d feat: an "Add group/node" card in the pipeline window
Groups the pipeline window into a card grid with a "+ Add group/node"
card beside it -- matching v4's card-per-loader layout instead of a
separate toolbar button. Picking it offers a new empty group or a node
outside any group (landed in the existing "Other" bucket).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 18:24:07 +03:00
digital-garbageandClaude Sonnet 5 7a226830b1 feat: GGUF loader support, and fix silent pipeline-editor state loss
GGUF: port v4's gguf_support.py (backend selection, content-based .gguf
detection, dequantize fallback) and wire it into both the diffusion-model
and CLIP loaders.

Pipeline editor: reopening the Models-and-pipeline settings window was
silently discarding the whole edited pipeline (a loaded preset, every
loader file, every sampler setting) because GET /api/pipeline is
deliberately stateless server-side and boot.js never fed its own
already-saved slots back into a freshly remounted editor -- found live
while walking through the MiniMax-H3 setup flow. Fixed by re-validating
the saved slots through check() on reopen instead of always replaying
the raw stateless default.

Also fixes, found via the extensive_testing adversarial loop over both
of the above: a stale file-listing cache in the pipeline window (no way
to see a newly-added model file without a full page reload), and a
chain of races/leaks in its own refresh/commit error-banner handling
that surfaced across five review rounds -- a rejection from a superseded
request overwriting a fresher success, a failure banner never clearing
on success, banners bleeding across groups on navigation, and a
commit()'s refusal landing on the wrong view after the user backs out
before it answers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 18:14:46 +03:00
digital-garbage 33e2b02f4d app: a "Load preset..." button in the pipeline window, wired to the new route
The last missing piece connecting the backend preset mechanism to something
a person can actually click: pipeline.js gets a presets() fetcher matching
load/describe/check/search's own shape, and pipeline_window.js's group-card
index offers a "Load preset..." button (only when a caller passes one) that
lists what /api/pipeline/presets returns and replaces the pipeline through
the same commit()/check() path every other structural edit already goes
through -- a preset that does not actually build is refused here, same as
a hand-drawn bad wire would be, never shown as if it had loaded.

Writing the test for this caught a real bug before it shipped: open()'s own
destructuring silently dropped `presets` before ever reaching mount(), so
the button would never have appeared no matter what boot.js passed it.
2026-09-11 12:06:09 +03:00
digital-garbageandClaude Sonnet 5 93bdd27b32 app, core: a node's outputs are visible, and model family detection
Every node's own OUTPUTS were invisible on its own panel in the pipeline
editor -- wiring an input already let you pick a source, but opening the
node that PRODUCES something told you nothing about what it makes or
whether anything reads it. Found live, by a real user just looking at a
node, after two full review rounds that only ever checked the consuming
side. Fixed with a read-only "Outputs" section per node, naming each
output, its type, and who (if anyone) currently wires from it.

Also: model family detection. core/probe.py reads a safetensors header
(tensor names only, no weights) and asks every installed model module,
through the same capability-lookup mechanism already used for
empty_latent/decode, whether the file is its own -- core never names a
family itself. A real, adversarially-found bug in the ported signature:
one of MiniMax H3's three candidate prefixes doesn't correspond to any
prefix ComfyUI's own loader actually resolves to, traced through
comfy/model_detection.py and comfy/sd.py -- the probe could have called a
file H3 that ComfyUI's real loader would refuse. Removed, and pinned with
a dedicated test module. A second finding narrowed the folder search to
only the folders FunPack's own loaders actually read from.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 10:54:28 +03:00
digital-garbageandClaude Sonnet 5 75e9e9f686 core, app: pipeline wiring, and Restart/auto-stash for the updater
Pipeline wiring: core/graph.py gains wire()/unwire(), and the "Models and
pipeline" editor can now actually connect one node's output to another's
input -- sockets and widgets alike, which is what makes "linked inputs"
(several widgets sharing one Primitive node's value) buildable at all,
without a second mechanism. Three adversarial review rounds found and fixed
real bugs along the way: check()'s request builder silently dropped the
wire-specific fields; the widgets loop had no wiring UI at all despite the
backend supporting it, and a widget already holding a link rendered as an
editable box with the wrong (default) value; wiring an input that already
had an unsaved drafted value let the stale value silently win on Save. Also
fixed, found live rather than by review: the Add-node picker stacked two
disagreeing search boxes (an outer server search and filterList's own
redundant client-side one).

Updates window: "Restart ComfyUI" is now a plain, always-available button,
not just something offered when an update already landed. Rolling back or
switching branch on a dirty tree now auto-stashes instead of refusing --
updating still refuses, unchanged, since pulling new commits onto changes
in progress is not something to do without asking. Three review rounds
here too: stashing before reading the rollback target shifted what HEAD@{1}
meant (a real reflog hazard, not just reasoned about); a stash's disclosure
was lost if the step right after it failed; that fix was too narrow and
missed pull()'s own failures inside checkout(pull_after=True) -- the actual
default path -- and rollback()'s own reset failing; and finally, the
broadened fix still only caught GitUpdateError, missing subprocess-level
failures like a hung fetch timing out, which is the realistic failure mode
on a rented GPU box's network.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 19:25:06 +03:00
digital-garbage e6e4dfa882 app: mount every Settings section in place instead of deep-linking out
Picking Node packs, Updates, the log or Temp files used to close the
Settings window and open that feature's own separate modal, with no way
back except reopening Settings from the menu bar -- not what v4 does,
and not what "unified" is supposed to mean. Each of those five files
(plus pipeline_window.js) now exports mount({ setFooter, close }), the
same content its standalone open() already built, taking the modal
chrome's footer and close from whoever hosts it instead of owning a
modal directly. open() is unchanged for anyone opening one alone --
pipeline_window.test.js and every Playwright spec for these five prove
it, unedited, still passing.

Also fixes two findings from the adversarial pass on the earlier
deep-link version: filterList's search only matched a section's visible
label/hint, so a section findable only by a keyword (e.g. "loaders" for
"Models and pipeline") silently vanished from the filtered list; and
createSettingsWindow's "one at a time" state lived at module scope, so
a second independent instance would have redrawn the first one's modal
instead of opening its own. Both now have regression tests.
2026-09-07 01:06:02 +03:00
digital-garbageandClaude Opus 5 3a1c76d64e app: two of the same window, and an edit undone by an older one
The button opened another window on every press -- reported from a real
session as two of the same modal. Two windows over one pipeline are two drafts
of it: whichever is saved last wins, and the other was edited against a
pipeline that had already moved. One window now, focused rather than stacked,
and a fresh one after it is closed.

commit() took whatever answer arrived. Moving a node carries the whole slot
list and removing one carries an id, so the second answers first and the first
then describes a pipeline that no longer exists -- putting a removed node back
with no refusal and nothing said. The picker guarded this already; the path
that decides what gets queued did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 17:20:30 +03:00
digital-garbageandClaude Opus 5 1295f1a365 app: an edit outlived the node it was for
A draft is keyed by slot id, and a slot keeps its id when it is pointed at a
different node. An unsaved steps stayed attached across a swap -- and since
most samplers declare steps, and the server only refuses an input the new node
does NOT declare, it was written on the next Save with nothing saying it had
never been chosen for that node.

The fake server in the tests did not implement replace, which is why no test
could see it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 16:41:54 +03:00
digital-garbageandClaude Opus 5 8bfd28950d app: the models and pipeline window
One wide modal, a card per group, and a group that opens into a node list
beside that node/'s parameters, saved a group at a time. Groups travel on the
slot, so a pipeline keeps its own arrangement and a new group is a slot
claiming a name nobody used.

Values are drafted until Save; adding, removing, replacing and moving a node
go to the server as they happen, because only it can say whether the result
still wires up.

An unset dropdown reads "not set" rather than showing the first file as though
it had been chosen -- v4 recorded that trap, and picking the entry the box
already shows fires no change, so the one click that looks like the fix does
nothing.

New: region.stack, a frame header, a modal footer that can be refilled.

Not yet: wiring. A socket says so where a control would be.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 16:36:01 +03:00