Adds a 'preserve' toggle to LoRAStack and LoRAStackDynamic (advanced). A LoRA
tagged preserve=True is exempt from the two mechanisms that delete a style in a
conflict merge:
- sparsification (DARE/DELLA/conflict-aware): its diff is never trimmed/dropped
- TIES sign-election: instead of being zeroed where it holds the minority sign,
its full contribution is added on top of the TIES-merged content
Plumbed as a 6th stack-tuple field / dict key through _normalize_stack,
_merge_one_group -> _merge_diffs(preserve_flags=...), and the IS_CHANGED cache
keys + LoRAConflictEditor tuple rebuilds (so toggling it invalidates caches and
survives the editor). LoRAStackDynamic JS migration extended 8->9 widgets/slot
(preserve appended at each slot tail; legacy 7- and 8-slot saves re-padded).
The auto-strength exclusion from the design was descoped (single uniform scale is
baked into the linear path + group-cache replay; the reduction is mild/floor-bounded).
7 new tests (PreserveFlagTests); conflict-editor tuple test updated to 6-tuple; 302 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The litegraph workflow format restores widget values positionally. The
enabled_{i} toggle (e2b6c94) was added as the FIRST widget of each slot,
so workflows saved before it have 7 values/slot instead of 8. Without
migration the positional restore shifted every per-slot value by one: the
LoRA path landed in the enabled toggle and each lora_name combo received
"None", silently wiping the stack on load (the API/prompt format is
name-keyed so it stayed correct — only the UI restore broke).
Add migrateWidgetsValues() and override node.configure to re-insert a
default enabled=true at each slot head before litegraph applies the saved
array. Detects layout by slot-region divisibility (7/slot legacy vs
8/slot current), is idempotent, and no-ops on unrecognized lengths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Override _displayValue getter on combo widgets to strip directory
paths, showing only the filename for readability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The widget now controls visibility of multiple advanced settings (conflict
mode, key filter) beyond just strength sliders, so the name better reflects
its purpose.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate key_filter from a stack-wide setting on LoRAStackDynamic to a
per-LoRA per-slot control. Users can now filter keys independently for
each LoRA (e.g. "only take unique VACE keys from LoRA #2") without
chaining multiple stacker nodes.
- LoRAStack: add key_filter dropdown (all/shared_only/unique_only)
- LoRAStackDynamic: replace node-level key_filter with per-slot
key_filter_{i}; hide conflict_mode and key_filter in simple mode
- _normalize_stack: propagate key_filter from both tuple and dict formats
- _compute_cache_key: include per-entry key_filter, remove sentinel logic
- _merge_one_prefix: filter per-LoRA in both low-rank and full-rank paths
using raw n_loras from Pass 1
- _build_report: show key_filter per-LoRA instead of globally
- Add LoRAOptimizerSimple with sensible defaults; rename existing
optimizer to "LoRA Optimizer (Advanced)"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switching dropdown->text copies COMBO values to text fields.
Switching text->dropdown matches text values against COMBO options
by exact path, filename stem, or path suffix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename "mode" to "strength_mode" to avoid confusion with "input_mode".
Restore base_model_filter for dropdown mode (hidden in text mode).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New "input_mode" widget switches between dropdown and text modes:
- "dropdown": COMBO dropdowns to browse and pick LoRAs (original behavior)
- "text": STRING text inputs for typing short names or connecting text nodes
Both modes share the same strength/conflict_mode controls. Text mode
resolves short names (filename without path/extension) to full paths
via _resolve_lora_name. JS toggles lora_name_N vs lora_name_text_N
visibility based on input_mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace COMBO dropdowns with STRING text inputs so users can type
short LoRA names (e.g. "Milena_20260216180133") or connect text nodes
directly. Names are resolved to full paths via _resolve_lora_name.
Remove base_model_filter (no longer applicable without COMBO widgets).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the autocomplete JS class (was ~155 lines). Instead, resolve
short LoRA names server-side: typing "Milena_20260216180133" matches
against installed LoRAs by filename stem, so it resolves to e.g.
"ZImageTurbo/character/Milena_20260216180133.safetensors". No Lora
Manager dependency needed — just plain text input with smart matching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a multiline text widget where users can type LoRA paths directly
(one per line) with optional strength values. Supports three formats:
path.safetensors (strength 1.0), path:0.8 (both), path:0.8:0.5
(model:clip). When ComfyUI-Lora-Manager is installed, a lightweight
autocomplete dropdown suggests matches via its API. Works without
Lora Manager too (no autocomplete, user types exact paths).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix absolute→relative path matching (endsWith against fullLoraList)
- Fix page_size cap (paginate through all pages, build local map)
- Add explicit base_model_filter param to build_stack (no kwargs leak)
- Add retry when widget options not yet populated
- Preserve selected LoRA value when filter changes (no silent reset)
- Handle workflow restore (apply filter after interceptor setup)
- Extract interceptWidgetValue helper, add configurable:true
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a dropdown that filters LoRA dropdowns by base model type (SDXL,
Flux, Wan, etc.) using ComfyUI-Lora-Manager's API. If Lora Manager is
not installed, the filter widget is automatically hidden and dropdowns
show the full unfiltered list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow users to control how each LoRA behaves in contested weight regions.
'low_conflict' applies only where the LoRA agrees with the majority sign,
'high_conflict' applies only where it disagrees. Defaults to 'all' (no
filtering) with zero overhead when unused.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auto-strength now uses pairwise cosine similarity for exact vector-sum
energy normalization instead of assuming orthogonal LoRAs
- Save Merged LoRA defaults to ComfyUI loras folder; supports absolute paths
- Add JS extension for LoRA Stack (Dynamic) widget show/hide
- Fix LTX architecture detection for middle_block/mid_block keys
- Update banner SVG with new feature tags
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>