Commit Graph
391 Commits
Author SHA1 Message Date
Jason Hoku 3cb946927e feat: florence2_hires — mask-select index parser + tests 2026-05-18 01:12:01 -07:00
Jason Hoku f8822bc0b5 feat: florence2_hires — megapixel scaling math + tests 2026-05-18 01:11:25 -07:00
Jason Hoku 75296ad0ba Merge pull request #20 from DRuggeri/main
Allow "-" when matching lora strengths to accomodate negative numbers
2026-05-17 15:37:42 -07:00
Jason Hoku a31edc476b Bump Ver 2026-05-17 15:36:41 -07:00
Jason HokuandClaude Opus 4.7 580fcc2f58 fix: orchestrator normalizes "None" VAE to "Default" at every read site
The state_to_configs_json fix (a3ad111) correctly omits the "vae" key
when nothing is selected, but the orchestrator still crashed when a
config carried "vae": "None" from any other path — legacy saved state,
Revise modal edits, distributed worker job specs, etc. Even a fresh
Builder UI node could trigger this if any consumer pushed "None" back
into the entry.

Defense in depth: at all 4 read sites in generation_orchestrator.py,
normalize "None"/"none"/empty/falsy values to "Default" before the
"!= Default" gate that decides whether to load a per-config VAE. With
"Default" the orchestrator falls back to the checkpoint's bundled VAE
instead of calling load_vae_by_name("None") which raises
FileNotFoundError.

Sites normalized: check_if_job_completed VAE comparison (~line 227),
first-conf VAE load (~line 1029, the user's crash trace), per-config
VAE switching in the main loop (~line 1454), distributed-mode VAE
switching (~line 2885).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 15:33:16 -07:00
Jason HokuandClaude Opus 4.7 2157f7e2a9 fix: Deep Shrink uses V3 dispatch (PatchModelAddDownscale is io.ComfyNode now)
Runtime error: "'PatchModelAddDownscale' object has no attribute 'patch'".

Current ComfyUI (verified in this install's comfy_extras/nodes_model_
downscale.py) ships PatchModelAddDownscale as a V3 io.ComfyNode subclass
with a classmethod execute() returning io.NodeOutput. The agent's
original .patch() instance call only works on the legacy V1 class.

Now dispatches V3-first (subclass check + cls.execute()) with a V1
fallback (FUNCTION attribute, default "patch") for older builds. Unwraps
NodeOutput.args[0] for V3, tuple[0] for V1, bare value otherwise. Same
pattern as ltx_video_generation._call_node / _unwrap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 11:25:17 -07:00
Jason HokuandClaude Sonnet 4.6 7c809e5dfb feat: integrate Kohya Deep Shrink (PatchModelAddDownscale) into Builder UI
Adds a new SUB-GROUP 4 in the Builder UI's "Extra Model & Sampling
Options" section with all 7 params from ComfyUI's built-in
PatchModelAddDownscale node:
- block_number (int, default 3)
- downscale_factor (float, default 2.0)
- start_percent / end_percent (float 0-1, default 0.0 / 0.35)
- downscale_after_skip (bool, default true)
- downscale_method / upscale_method (combo: bicubic/nearest-exact/
  bilinear/area/bislerp, default bicubic)

Toggle checkbox controls whether the patch is applied. State plumbs:
config_array.use_deep_shrink/* (JS) -> state_to_configs_json -> per-
config entries -> expand_configs base_combos -> generate_image -> looks
up PatchModelAddDownscale via NODE_CLASS_MAPPINGS and calls .patch() to
patch the model before sampling. Logs an "Applied
PatchModelAddDownscale" line on each gen for visibility.

Mirrors the existing model_sampling_override / use_advanced_sampling
patterns for consistency. Hidden on LTX video config arrays since the
patch targets the SDXL/SD UNet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 12:22:25 -07:00
Jason HokuandClaude Opus 4.7 a1cb7c3346 chore: vae_batch_size + flush_batch_every default 4 → 1
User feedback: 1 is a more sensible default for typical generation —
smaller VAE batches keep VRAM lower, and flushing every gen makes the
dashboard show progress immediately.

Updated everywhere:
- sampler_node run_tests fallback defaults (post-widget-removal)
- conf-builder-main.js initial state + migration backfill
- conf-builder-config-management.js renderRunSettingsSection
  state-init defaults + _addRunSetting defaultValue
- config_builder_node.py state_to_configs_json emit fallback +
  except-clause fallback for both fields

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:56:19 -07:00
Jason HokuandClaude Opus 4.7 eff0594d78 fix: dashboard card LoRA row stays single-line for stacked LoRAs
User reported that cards with many stacked LoRAs grew very tall and
broke the 1-9 quick-favorite hotkeys.

Root cause: createCard rendered all stacked LoRAs as a multi-line block
("name1\nname2\nname3\n..."). Many LoRAs = tall card. The virtual
scroller positions rows on a fixed stride, so tall cards overflowed
into adjacent rows. _quickFavoriteByPosition clusters visible cards
into rows by y-center with rowTolerance=20 — overflowing cards land
in the wrong row group and the Nth-card mapping breaks.

Fix: stacked LoRAs now render as "<first_name> +N more" on a single
line (e.g. "DMD2 +4 more"). Full list still available via tooltip on
hover (title attribute). Card heights stay uniform and the row
clustering works correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:43:55 -07:00
Jason HokuandClaude Opus 4.7 7e9eade2c6 feat: always-visible help note on UltimateSamplerGrid node
After the widget cleanup (30e3aa9), the SamplerGridTester is nearly
empty — just one required input (configs_json), six optional sockets,
and a single output. New users won't know how to wire it.

Adds a small footnote (~44px tall) at the bottom of the node via a
non-serialized DOM widget:

  "Connect the configs_json from the Builder node to this node, and
   connect this node to the Dashboard node"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:34:47 -07:00
Jason HokuandClaude Opus 4.7 62c2a0f9ab refactor: remove session_name output socket from UltimateConfigBuilder
session_name is now carried inside configs_json via _session_settings
(emitted unconditionally as of e570f6b, honored by sampler_node.run_tests).
The dedicated output socket was redundant — and now that the
SamplerGridTester's session_name widget is gone (commit 30e3aa9),
nothing on the downstream side reads it anyway.

Workflows that wired BuilderUI.session_name -> somewhere will have that
wire silently dropped by ComfyUI on load. No action needed for users
who only use the configs_json wire (the typical setup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:48:36 -07:00
Jason HokuandClaude Opus 4.7 67cb0f5acf feat: Builder UI — larger default node size + smaller preview panel default
Two UX defaults users hit on first drop:

1. Default node size was ~200x100 (Comfy's default), making the HTML
   overlay invisible. Bumped to 720x900 on fresh create. Existing
   workflows keep their saved size (only the max-with-current is
   applied, gated by an _uscgSizeInitialized flag so the size override
   doesn't fight user resizes).

2. Output Preview panel defaulted to 450px which dominated the UI on
   smaller nodes. Reduced to 175px (~8 lines of JSON visible at the
   11px/1.4-leading default font). The drag handle still lets users
   grow it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:45:19 -07:00
Jason HokuandClaude Opus 4.7 30e3aa9a94 refactor: slim SamplerGridTester to configs_json + optional inputs
Removes 15 widgets from the SamplerGridTester INPUT_TYPES. The Builder
UI is now the single source for run-level settings via _session_settings
in configs_json. Optional inputs (6 inputs) and the hidden unique_id are
kept; everything else is replaced by hardcoded defaults at the top of
run_tests.

Removed widgets:
- ckpt_name, positive_text, negative_text, seed, denoise
- vae_batch_size, resolutions_json, session_name
- overwrite_existing, flush_batch_every, add_random_seeds_to_gens
- lora_triggerwords_mode, remote_vae_endpoint
- save_conditioning_cache_to_file, enable_model_cache

Defaults set internally (overridden by _session_settings when present):
- ckpt_name: first available checkpoint (fallback so standalone use with
  "model": "Default" does not crash the orchestrator with None)
- positive_text="", negative_text="", seed=0, denoise="1.0"
- vae_batch_size=4, resolutions_json='[[1024, 1024]]', session_name="my_session"
- overwrite_existing=False, flush_batch_every=4, add_random_seeds_to_gens=0
- lora_triggerwords_mode="None", remote_vae_endpoint="None"
- save_conditioning_cache_to_file=False, enable_model_cache=False

Existing workflows: ComfyUI silently drops widget values for removed
INPUT_TYPES keys. Workflows that ran on prior versions will use the new
internal defaults; users who customized those widgets need to set the
same values in the Builder UI Run Settings instead (the previous commit
made all Builder UI run-settings authoritative so they always override).

UX: the Generator node is now a near-empty box with one required input
(configs_json) and 6 optional sockets. The user-facing controls all
live in the Builder UI. This simplifies the graph and removes a
recurring source of confusion (widgets that were silently overridden).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:26:56 -07:00
Jason HokuandClaude Opus 4.7 e570f6b58b fix: Run Settings — start_at_job persistence + unconditional override emission
Three issues in 7a2c3c9:

1. start_at_job's onchange handler used setDirtyCanvas() instead of
   saveState() — changes never reached the lora_config widget value.
   Fixed to call saveState().

2. The 6 run-settings emissions were gated on "non-default" values
   (e.g. lora_triggerwords_mode only emitted when != "None"). Result:
   a user setting Builder UI to its default could not override the
   Generator widget if the user had previously set that widget to
   something non-default. Builder UI is now authoritative — emit all
   values unconditionally. start_at_job and image_format also
   switched to unconditional emission for consistency.

3. session_name had no plumbing path. Added emission in
   state_to_configs_json and an override branch in sampler_node so
   the Builder UI's session_name field overrides the generator widget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:25:10 -07:00
Jason HokuandClaude Opus 4.7 a3ad11116a fix: VAE — honor bypass state + omit "vae" key when nothing selected
Two bugs in state_to_configs_json's VAE emission:

1. Bypass (unchecked) VAEs were still sent through — vae_bypass_states
   was never consulted in the filter. Now filtered alongside the
   None/empty check.

2. When no VAE remained (empty list, all bypassed, or only "None"),
   the config emitted "vae": "None" — the orchestrator interpreted
   that as a model filename and threw "vae model with filename 'None'
   not found". Now the "vae" key is OMITTED entirely so the generator
   falls back to its default behavior (no per-config VAE override),
   matching the pattern already used for text_encoders/clip_type/etc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:40:16 -07:00
Jason HokuandClaude Opus 4.7 f18cc833ea chore: render Run Settings above Distribution Settings (very top of UI)
User wanted Run Settings to be the FIRST section, above everything
including Distribution Settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:29:55 -07:00
Jason HokuandClaude Opus 4.7 7a2c3c905e feat: Builder UI Run Settings — top-of-UI placement + 6 generator-node overrides
Phase 1 of consolidating run-level config into the Builder UI. The
Builder UI's "Run Settings" section is now rendered at the TOP of the UI
(was previously below config arrays) and gains six new fields with help
tooltips:

- overwrite_existing (bool, default false)
- flush_batch_every (int, default 4)
- lora_triggerwords_mode (enum: None / Append To End / Append To Start /
  Read From Config, default None)
- save_conditioning_cache_to_file (bool, default false)
- enable_model_cache (bool, default false)
- vae_batch_size (int, default 4, range -1..64)

State plumbs through state_to_configs_json -> _session_settings ->
sampler_node.run_tests -> run_generation_loop. Builder UI values
override the corresponding widgets on UltimateSamplerGrid when set
(non-default). Widget values remain the fallback so existing workflows
keep working without changes.

Logs an "⚙️ <name> overridden by Builder UI: <value>" line whenever an
override fires, so it's obvious in the console.

Next: once the user has tested this thoroughly, the corresponding
widgets on the SamplerGridTester node can be removed/hidden to
simplify the generator UX (deferred — coordinate with user).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:21:14 -07:00
Daniel Ruggeri 81947630ee Allow "-" when matching lora strengths to accomodate negative numbers 2026-05-09 22:11:28 -05:00
Jason HokuandClaude Opus 4.7 4821433b24 fix: restore double-click-to-favorite on card images
The lightbox-click revert (4a318e6) stripped both onclick AND ondblclick
from the <img> tag. The intent was to remove the accidental-tap-opens-
lightbox behavior, but the double-click-to-favorite was collateral damage.
Videos kept their ondblclick handler; images lost it.

Restoring ondblclick="toggleFavorite(this)" on <img>. No onclick — single
click still does nothing (panning safe).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:48:35 -07:00
Jason HokuandClaude Opus 4.7 0860d19ad9 chore: bump version 1.19.0 → 1.20.0
Release rolls up ~77 commits across:
- Single-source-of-truth state→configs_json transformer (Python authoritative;
  preview now POSTs /configbuilder/preview; deleted ~280 LOC of duplicated JS).
- Builder UI: layout fix (HTML+textarea now coexist in the node), unified
  Compare Strengths section, locked-semantics single-bracket lora arrays
  (no Cartesian explosion), per-entry seed override.
- Dashboard: Quick Filter (faceted with sub-filter drilldown), Logic Filter
  (numeric comparisons with float tolerance), Reset All Filters button,
  Delete All Rejected button, Lightbox arrow-key navigation in Revise modal.
- Orchestrator: ETA pre-flight scan + effective-progress display, configurable
  image format (webp/png/jpg) plumbed through main + remote VAE + upscale +
  distributed paths.
- Bulletproof Revise: detects connected configs_json input, disconnects to
  inject edited values, restores after queue. Honors per-entry seed.
- LTX 2.3 finalization (Phase A + B), CONTRIBUTING.md, schema-drift validator
  cleanup, dashboard filter highlight visibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:59:52 -07:00
Jason HokuandClaude Opus 4.7 35e6be3a54 fix: include preview panel CSS + drop JSON Preview sidebar nav (orphan)
These changes belong with the earlier "preview panel uses /configbuilder/preview"
work (b6e2344) but were dropped from that commit. Folding in now:

- Adds .cb-preview-panel + resize-handle + header/content CSS used by the
  renderPreviewSection function shipped in 991631a.
- Removes the obsolete addNavIcon("📄", "JSON Preview", ...) sidebar entry.
- Cleans the cb-sec-preview ID out of the scroll-spy section list.

No behavior change beyond what was already committed — fills in the
visual/CSS gap the committed JS depends on.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:59:51 -07:00
Jason HokuandClaude Opus 4.7 5482878edc fix: drop seed/denoise/clip_type from schema-drift validator
These keys legitimately aren't set in pre-orchestrator configs:
- seed: filled by expand_configs (node-level seed widget or per-entry override)
- denoise: filled by run_generation_loop from the denoise widget
- clip_type: only set when model_type != "checkpoint"

The validator was a stale leftover from the pre-single-source-of-truth era.
Trimming false positives. Still warns on actual typos / dropped fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:52:43 -07:00
Jason HokuandClaude Opus 4.7 df6aaa6559 fix: expand_configs honors per-entry seed override (for Revise modal)
User reported that changing seed in the Revise modal had no effect —
the new seed was overwritten and the orchestrator's check_if_job_completed
matched the original item's seed, skipping the gen.

Root cause: config_utils.expand_configs unconditionally wrote
"seed": <global node seed> for every config, ignoring entry["seed"]
from the configs_json. Builder UI doesn't emit entry["seed"] (it relies
on the node-level seed widget), so this regression only surfaced when
something else explicitly set per-entry seed — exactly what triggerGen
does.

Fix: use entry["seed"] when present and non-empty, otherwise fall back
to the global seed. Builder UI flow unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:42:34 -07:00
Jason HokuandClaude Opus 4.7 6f9862f2b7 feat: Revise modal — rename CANCEL→EXIT and keep open after Generate New
Two QoL tweaks:
- CANCEL button text → EXIT (clearer when paired with the new "stays open"
  behavior — there's nothing to "cancel" after queuing).
- Generate New no longer closes the modal. The QUEUED! flash on the button
  is enough confirmation. Users can keep tweaking sampler/cfg/prompt and
  queueing additional revisions of the same image without re-opening.
  Close manually via EXIT or Esc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:38:04 -07:00
Jason HokuandClaude Opus 4.7 f937773d8f fix: Revise modal "Generate New" actually uses edited values
User reported edited sampler/cfg/steps/prompt were ignored — the original
config ran and got skipped as already-existing.

Root cause: configs_json on UltimateSamplerGrid is declared as both a
widget AND an input socket. In the typical setup (UltimateConfigBuilder
wired into UltimateSamplerGrid), the input link OVERRIDES the widget at
queue time. The previous triggerGen set widget.value but Comfy used the
upstream node's output instead.

Fix: detect the connected link, disconnect before setting the widget
value, queue the prompt, then restore the link after queue submission
completes. Defensively also update widget.inputEl, widget.element,
fire widget.callback, and setDirtyCanvas so all known widget-update
channels are hit.

Adds clear error messaging when no UltimateSamplerGrid node exists,
multi-node warning when there's more than one, and console logs at each
critical step for future debugging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:31:41 -07:00
Jason HokuandClaude Opus 4.7 1745fd9966 fix: Quick Filter dropdown positioning, click-outside leak, lora_strength
Four bugs in 111e24d:

1. Dropdown positions were zoom-dependent / offset because JS overrode
   the HTML's "position: absolute; top: 100%" with "position: fixed" +
   getBoundingClientRect() coords. Inside the dashboard iframe (with
   Comfy's transformed canvas as ancestor), position:fixed becomes
   relative to that ancestor instead of viewport. Fix: wrap button +
   menus in a position:relative anchor div (#quick-filter-anchor), drop
   all JS coord setting, let CSS handle positioning.

2. Click-outside handler stayed registered after items used
   e.stopPropagation, so reopening required toggling another UI element
   first. Fix: named _qfMenusOutsideHandler with explicit
   _qfAttachOutsideHandler/_qfDetachOutsideHandler pair; capture phase
   (third arg true) bypasses stopPropagation; _closeQuickFilterMenus
   now calls _qfDetachOutsideHandler; addQuickFilter calls
   _closeQuickFilterMenus upfront.

3. Value menu was anchored to a hidden element (type menu hidden BEFORE
   value menu opened), so getBoundingClientRect returned zeros and value
   menu rendered at viewport (0, 6). Fixed by Bug 1 (CSS positioning);
   anchorEl param dropped from _openQuickFilterValueMenu.

4. lora_strength wasn't in _qfAllTypes / _qfFacetableTypes /
   _qfTypeLabels / _qfExtractItemValue / _quickFilterMatches. Now
   selectable: extracts first lora's first strength from item.lora,
   compares with 1e-6 float tolerance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:01:07 -07:00
Jason HokuandClaude Opus 4.7 111e24ddc8 feat: Quick Filter creation system (faceted dashboard filtering)
Adds a third filter mode alongside main buttons / search / logic.
User clicks "+ Add Quick Filter" -> picks a type (LoRA/CFG/etc.) ->
picks a value -> adds a chip. Each chip can be expanded to show
"sub-filters": values of OTHER filter types that exist within the
subset matching this chip's primary. Click a sub-filter button to
add it as another chip (ANDed with existing).

Designed as faceted search: drill-down by selecting facets one at
a time. Each chip is independent and ANDed with the others (matching
the GitHub discussion's "logical && between boxes" semantic).

LoRA matching handles stacks ("name1 + name2"), so a "DMD2"
quickFilter matches items where DMD2 is one of multiple stacked
loras. Numeric types (cfg/steps/denoise/seed) use 1e-6 float
tolerance matching the Logic Filter convention.

Reset All Filters and the R key both clear quickFilters along with
the other filter mechanisms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:50:16 -07:00
Jason HokuandClaude Opus 4.7 5804ac7309 fix: ETA progress shows effective progress (existing + new) not iteration idx
User feedback: when 25/26 items already exist and only 1 new is being
generated, dashboard showed "job 14/26" or similar (skip path doesn't
update progress). Now displays effective progress: existing_count +
new_completed / total. So a session with 25 pre-existing shows 25/26
immediately, then 26/26 after the 1 new gen.

- Adds new_completed counter (separate from loop-index current_job)
- Replaces current_job with existing_count+new_completed in dashboard-
  facing log/send_sync (lines 2107, 2114, 2123, 2133)
- Sends an initial progress update right after pre-flight so the user
  sees the correct "already done" count before the first gen, clearing
  any stale values from a prior run
- Other current_job references (LTX dispatch, image-gen log, start_at_job
  gate, periodic save, final send, distributed mode) keep iteration-
  index semantic — different contexts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:26:04 -07:00
Jason HokuandClaude Opus 4.7 4a318e68d6 revert: remove single-click-to-open-lightbox on card images
User feedback: still triggers accidentally during panning. Reverts the
click-to-open feature added in 8790f97 (with timer-based fix in
4672116). REVISE button remains the only way to open the modal.

In-modal keyboard navigation (arrow keys, spacebar, 1-9 favorite, ESC)
added in 8790f97 stays intact — that part of the lightbox feature is
useful and uncontroversial.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:25:51 -07:00
Jason HokuandClaude Opus 4.7 1f5adeb799 fix: extend image_format plumbing to remote VAE / upscales / distributed
Commit 490c519 added a webp/png/jpg dropdown but only flush_batch_with_vae
honored it. Three other save sites still hardcoded WEBP:
- flush_batch_with_remote_vae (image_generation.py)
- upscale_runner.py (3 sites in inline + deferred upscale paths)
- distribution_worker.py (PIL bytes shipped back to master)

Now all save sites read image_format from session_settings (orchestrator)
or the job spec (distributed) and match the format/extension.

Changes:
- remote_vae.py: RemoteVAEDecodeWorker accepts image_format kwarg; _worker
  uses it to pick format/extension at save time (the real save site for the
  remote VAE path — flush_batch_with_remote_vae only queues to the worker)
- generation_orchestrator.py: passes image_format to all RemoteVAEDecodeWorker
  constructors (initialize_remote_vae + 3 per-config sites); adds image_format
  to all 8 existing_data["meta"] writes so upscale_runner can read it from disk
- upscale_runner.py: reads image_format from manifest meta at thread start;
  uses _fmt/_ext locals at all 3 save sites (seedvr2, latent-decode, PIL)
- distribution_manager.py: injects _session_image_format into worker job
  config alongside existing _session_cooldown passthrough
- distribution_worker.py: reads _session_image_format from config; encodes
  bytes in the chosen format; stamps _image_ext on meta so master uses it
- distribution_routes.py: pops _image_ext from meta to build filename with
  correct extension before writing bytes to disk

Default behavior unchanged: all paths fall back to webp if image_format
is absent or unrecognised.

Code review found this in 490c519.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:29:52 -07:00
Jason HokuandClaude Opus 4.7 b4db3af7b3 docs: fix CONTRIBUTING.md factual errors
Code review found multiple inaccuracies:
- Instructed contributors to update a non-existent convertStateToConfigs()
  JS function (was deliberately removed — single source of truth lives in
  Python state_to_configs_json).
- Wrong line numbers for generate_config (~465 → ~837) and default state
  (~80 → ~103); onConfigure migration reference replaced with the actual
  async init block at ~484.
- Overstated safe_print module shadow scope (only 2 files, not all).
- Referenced missing LICENSE file (pyproject.toml declares it but the file
  does not exist in the repo root).

Replaced with correct architectural guidance pointing to
state_to_configs_json, accurate line numbers, scoped safe_print claim,
and license info from pyproject.toml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:26:32 -07:00
Jason HokuandClaude Opus 4.7 46721165ba fix: lightbox image click/dblclick conflict + remove dead favIcon var
Card <img> previously had both onclick=openM and ondblclick=toggleFavorite.
Browsers fire click twice before dblclick, so double-clicking toggled
favorite AND opened the modal (twice). Mobile tap-to-scroll also
accidentally opened the lightbox.

Replace with a 250ms-delayed click handler (_cardImgClick) that
distinguishes single (open modal) from double (favorite). Remove
unused favIcon variable in the modal 1-9 keypress handler.

Code review found these in commit 8790f97 (lightbox feature).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:24:38 -07:00
Jason HokuandClaude Opus 4.7 4b6a9b7299 fix: Logic Filter = and != use float tolerance, not strict equality
parseFloat-then-=== fails for common stored values (e.g. cfg=7.0
filter against item.cfg=7.0000000001 from float arithmetic). Use
1e-6 epsilon for both = and != operators.

Code review found this in commit bcadb7e (Logic Filter feature).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:23:55 -07:00
Jason HokuandClaude Opus 4.7 8790f9771a feat: dashboard - Lightbox image click + arrow-key navigation in Revise modal
Clicking a card image now opens the Revise modal as a lightbox. Adds a
navigation bar with prev/next buttons and a position counter. Arrow keys
(← →), Space/Shift+Space navigate between filtered items; 1-9 favorites
the current image. Grid keyboard shortcuts are suppressed while the modal
is open to prevent conflicts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:13:19 -07:00
Jason HokuandClaude Opus 4.7 bcadb7e4e3 feat: dashboard - Logic Filter (numeric comparison filtering)
Adds a Logic Filter panel in the filter modal with field/operator/value
dropdowns (supports lora_strength, cfg, steps, denoise, width, height,
seed). Multiple filters apply with AND logic. Chips display active filters
with one-click removal. Integrates into data pipeline via matchesLogicFilters()
helper. Reset All Filters also clears logic filters.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:11:58 -07:00
Jason HokuandClaude Opus 4.7 b95d2d0cfc feat: dashboard - Reset All Filters button + R key resets filter UI
Adds a 'Reset All' button to the filter modal header that re-selects all
filter buttons and clears search/logic filters. Updates the R key shortcut
to also reset the filter UI before reloading cards so visual state stays
consistent with the data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:10:25 -07:00
Jason HokuandClaude Opus 4.7 7821c60d98 feat: dashboard - Delete All Rejected button
Adds a 'Delete All Rejected Items' button in the cog menu next to the
existing 'Delete All Non Favorited Items' button. Adds the matching
/config_tester/delete_rejected backend endpoint that removes rejected
item files and updates the manifest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:09:47 -07:00
Jason HokuandClaude Opus 4.7 490c5196ef feat: configurable image save format (webp/png/jpg)
Adds a dropdown to the Builder UI's Run Settings section letting users
pick the saved image file format. Default remains webp (current
behavior). State plumbed through state_to_configs_json ->
_session_settings -> orchestrator -> save_image_to_disk.

PNG users will see lossless saves; JPG users smaller files at quality 95.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:05:10 -07:00
Jason HokuandClaude Opus 4.7 a491a88f48 docs: add CONTRIBUTING.md
Welcomes PRs, documents dev setup, test commands, key architecture
notes (single-source-of-truth transformer, manifest field names,
V1/V3 node dispatch), and a PR checklist.

Closes user request from GitHub feedback thread.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:02:37 -07:00
Jason HokuandClaude Opus 4.7 c45058adf3 feat: pre-flight scan counts already-existing configs in ETA
Scans the planned configs list once before the generation loop and
counts how many already have output files on disk (the same skip path
used by check_if_job_completed). Displays the count in the running
ETA line: "job X/Y (P%) | ETA: ... | N existing".

Helps users gauge how much actual work remains when re-running a
session that's been partially completed in a prior run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:02:19 -07:00
Jason HokuandClaude Opus 4.7 8c8759d487 fix: strengthen dashboard filter highlight visibility for steps/cfg/upscale/type
The CSS rules and variables (--accent-steps/cfg/upscale/media) were already
added in 2c3851e; user reporting the bug was viewing a stale inlined dashboard
HTML generated before that commit. Since html_generator.py inlines report.css
at generation time, regeneration is required for CSS edits to take effect.

Added belt-and-suspenders box-shadow: inset to the four filter types so that
even if a theme override or cached page interferes with background, the inset
ring remains visible. Added a code comment documenting the regeneration
requirement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:02:05 -07:00
Jason HokuandClaude Opus 4.7 4fb4fea5ed fix: lock Compare Strengths to single-bracket form (no Cartesian)
When the user types '0.5, 1.0' they want 2 outputs (lora at 0.5, lora
at 1.0 — both model and clip mirroring), not 4 outputs (Cartesian
m×c). The bug was that we wrote both _model AND _clip side-channel
arrays, so Python emitted 'name:[m]:[c]' which the orchestrator
correctly interpreted as Cartesian.

JS: collapse the two-input compare section to a single 'Strengths:'
input. Never write _clip to lora_weight_arrays.

Python: process_lora_array now emits 'name:[m_arr]' (no clip part)
when only _model array is present. The generator's existing
parse_lora_definition defaults clip=model when the third segment
is missing, giving N configs with locked semantics.

Drops the Cartesian path entirely. Backward compat retained: if a
saved workflow has both arrays (legacy), the dual-bracket form is
still emitted so the orchestrator can Cartesian-expand as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:27:39 -07:00
Jason HokuandClaude Opus 4.7 2913fc606a feat: unified Compare Strengths section with labeled Model+Clip inputs
Replaces the two separate '+ Compare Strengths' / '+ Compare CLIP'
buttons with a single button that toggles a section showing two
labeled inputs ('Model Strengths:' and 'Clip Strengths:'). When
Strength Lock is ON, only the Model input shows (CLIP mirrors
automatically — same as today).

Also fixes button-text-not-updating bug: previously the button
required a full re-render to swap between '+ Compare Strengths' and
'✕ Array' text, so toggling the input visible left the button stuck
at '+ Compare Strengths' until page reload. Activation now persists
arrays as [1] (Python passes through unchanged) and uses array
existence rather than length>1 as the active indicator, so reloads
correctly restore the active state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:27:49 -07:00
Jason HokuandClaude Opus 4.7 991631aec4 docs: update conf-builder-readme to reflect /configbuilder/preview endpoint
convertStateToConfigs() was deleted in the previous commit; the JS
preview now POSTs to the new endpoint and Python is the sole transformer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:59:48 -07:00
Jason HokuandClaude Opus 4.7 b6e2344f9b feat: preview panel uses /configbuilder/preview endpoint
Replaces synchronous JS convertStateToConfigs (~280 lines) with a
debounced (800ms) fetch to the new endpoint. Preview now renders
literally what generate_config produces — drift impossible by
construction.

Stale-state badge surfaces network failures without blocking editing.
Loading placeholder appears during the first-paint debounce window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:59:10 -07:00
Jason HokuandClaude Opus 4.7 9ff76dd8ab feat: add POST /configbuilder/preview endpoint
Both runtime (generate_config) and preview now invoke
state_to_configs_json — single source of truth, drift-proof.
The Builder UI will fetch this endpoint instead of running a
parallel JS transformer (next commit removes that JS code).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:55:49 -07:00
Jason HokuandClaude Opus 4.7 3e9f9316c9 test: add 6 golden regression tests for state_to_configs_json
Covers single-lora pass-through, multi-lora combine with ' + ',
folder ref un-expansion, folder+brackets excluded from combine,
bypassed lora exclusion, and ltx_video model_type propagation.
Also tightens the existing bracket-array test from substring match
to exact string equality and removes the unused pytest import.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:54:12 -07:00
Jason HokuandClaude Opus 4.7 d07657c85b refactor: extract state_to_configs_json as single transformer
Eliminates the JS<->Python drift bug class by making one Python function
the source of truth for state -> configs_json. Both the runtime
(generate_config) and the new preview endpoint (next commit) call it.
Adds golden test for the lora-strength-arrays bracket case that just
broke (regression guard).

Also loads the real config_builder_node module in conftest.py (instead
of a bare stub) so that tests against UltimateConfigBuilder methods work
correctly. Stubs network_utils before loading to satisfy the relative
import.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:44:12 -07:00
Jason Hoku ef0062fce8 fix: ltx pickers route through modelLists object so refresh-models button updates them 2026-04-27 12:14:39 -07:00
Jason Hoku 15bd7ae2dd fix: localStorage cache version bump + persist latent_upscale_models, clearAllCaches resets all upscale fields 2026-04-27 12:07:41 -07:00