Commit Graph
407 Commits
Author SHA1 Message Date
Jason Hoku 90dcdc9551 fix: florence2_hires inline path — use upscale_random, not bare random
UnboundLocalError at runtime: 'cannot access local variable random where it
is not associated with a value'. Cause: Python scopes 'random' as a local
of the entire _run_jobs function (it's imported as a bare name later in the
function at L2471 etc), so any earlier reference triggers UnboundLocalError.

The inline upscale loop imports 'import random as upscale_random' at L1797
specifically to avoid this — my Florence2 code (and seedvr2's at L2035)
used bare 'random.randint' which shadowed the local. Switched both my sites
+ the pre-existing seedvr2 site to upscale_random.

Tests still 96/96 green.
2026-05-18 08:43:46 -07:00
Jason Hoku 448cea1516 fix: florence2_hires inline path — use create_image_metadata, not dict(meta)
'meta' isn't assigned until L2324, AFTER the inline upscale loop. My
dispatch used dict(meta) twice (sentinel + success), both NameError'd at
runtime with 'cannot access local variable meta where it is not associated
with a value'. Switched both to create_image_metadata(conf, ...) matching
the normal hires/model upscale pattern at L2184.

(The pre-existing seedvr2 inline dispatch at L2025 has the same bug —
out of scope for this fix.)
2026-05-18 08:33:32 -07:00
Jason Hoku b492267ec2 fix: florence2_hires — relative-then-absolute import fallback for in-function imports
Runtime error reproduced by user: 'No module named ltx_video_generation'.

The project's pattern is relative imports (.ltx_video_generation), which work
when ComfyUI loads the file inside the package context. My in-function lazy
imports used the absolute form (works in tests where conftest puts the node
root on sys.path, fails at runtime where it isn't).

Fix: each of the 5 in-function imports now tries relative first, falls back
to absolute on ImportError. Tests still pass (96/96) via the absolute branch;
runtime now succeeds via the relative branch.
2026-05-18 08:21:54 -07:00
Jason Hoku 6e399091fa fix: florence2_hires — wire dispatch into the INLINE upscale path
The inline upscale loop in generation_orchestrator._run_jobs (~L1875) runs
per-job when 'Upscale Pipeline' is enabled in the Builder. It had a seedvr2
elif but no florence2_hires elif — so my mode fell through to combos=[] and
silently did nothing. (The upscale_runner.py path I wired previously only
covers DASHBOARD-triggered upscales, not inline runs.)

Adds the parallel florence2_hires dispatch to the inline path:
- Decodes the just-generated latent to PIL via decode_latent_with_vae
- Forces model_source='from_builder' (the loaded model is what we want for
  inline runs; from_manifest would trigger a redundant reload)
- Honors no-detection sentinel + chained-step re-encode flow
- Manifest entry has upscale_source='inline' (vs 'dashboard' for the
  dashboard path)

Preflight guard uses a module-global since 'job' doesn't exist in this
scope (each job is just a loop iteration here).

Test count unchanged (96 passing) — this is plumbing-only; unit tests
already cover run_florence2_step + manifest builders. Real-Comfy smoke
test is the verification.
2026-05-18 07:27:13 -07:00
Jason Hoku 6ab8eb64af docs+bump: README — add Florence2 Hi-Res Fix section + v1.21.0 -> v1.22.0 2026-05-18 01:40:14 -07:00
Jason Hoku e88b4bad6f feat: builder — render Florence2 step sub-panel with 14 fields
Adds the Florence2 Hi-Res Fix option to the upscale mode dropdown and a
sub-panel that renders when selected: 4 detection fields, 4 crop/resize fields,
5 mask-shape fields (grow_expand + 4 feather sides), model/lora source dropdown,
and the no-detection dropdown. Hires sampling fields (denoise/steps/cfg/sampler
/scheduler) are reused from the existing hires_only mode rendering — Florence2
sets showHires=true so they appear naturally. Combos counter treats Florence2
as 1-output-per-input (no cartesian multiplier).
2026-05-18 01:38:03 -07:00
Jason Hoku 67eba954e1 feat: builder — default florence2 step config + load-time migration 2026-05-18 01:34:35 -07:00
Jason Hoku 37a7b6aecf test: florence2_hires — regression test for state_to_configs_json passthrough 2026-05-18 01:34:05 -07:00
Jason Hoku 49b1406cff feat: upscale_runner — florence2_hires mode dispatch + manifest entry builders
Adds an elif block to _run_upscale_thread that handles mode='florence2_hires',
delegating per-image work to florence2_hires.run_florence2_step() and using
two new pure-Python helpers (build_florence2_manifest_entry,
build_florence2_no_detection_entry) to construct manifest entries.

The pure-Python helpers are unit-tested in test_florence2_manifest_entry.py;
the wiring itself relies on the user's smoke test (mirroring the LTX pattern).
2026-05-18 01:33:11 -07:00
Jason Hoku 4354a1a14f feat: florence2_hires — full per-image pipeline (crop -> resize -> sample -> uncrop) 2026-05-18 01:29:24 -07:00
Jason Hoku 78d6f7d966 feat: florence2_hires — run_florence2_step skeleton + no-detection path 2026-05-18 01:27:22 -07:00
Jason Hoku 115e093dcf feat: florence2_hires — per-image checkpoint+lora cache for from_manifest 2026-05-18 01:26:07 -07:00
Jason Hoku d8713fc4cc feat: florence2_hires — model loading with module-level cache 2026-05-18 01:24:59 -07:00
Jason Hoku a0fbe14272 feat: florence2_hires — preflight + node lookup + tests 2026-05-18 01:24:00 -07:00
Jason Hoku afe37cc21c feat: florence2_hires — pure-Python paste_into_image + round-trip tests 2026-05-18 01:23:06 -07:00
Jason Hoku 712ec83253 feat: florence2_hires — pure-Python crop_image_by_mask + tests
Also: conftests now try to load real torch before installing the torch stub,
so tests that genuinely need real torch tensors (crop/paste, integration)
work when the Comfy embedded Python is used. Other tests are unaffected.
2026-05-18 01:20:29 -07:00
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