Split LoRA keys into separate name and strength fields

Store lora name and strength as independent JSON keys instead of the
combined <lora:name:strength> format. Legacy format is auto-migrated
on load. Timeline preview updated to show all 6 LoRA slots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 22:21:48 +01:00
parent 074e36f883
commit be9c95ffbd
3 changed files with 41 additions and 30 deletions
+27 -20
View File
@@ -290,8 +290,9 @@ def render_batch_processor(state: AppState):
ui.button('From Source', icon='file_download', on_click=add_from_source) ui.button('From Source', icon='file_download', on_click=add_from_source)
# --- Standard / LoRA / VACE key sets --- # --- Standard / LoRA / VACE key sets ---
lora_keys = ['lora 1 high', 'lora 1 low', 'lora 2 high', 'lora 2 low', lora_keys = ['lora 1 high', 'lora 1 high strength', 'lora 1 low', 'lora 1 low strength',
'lora 3 high', 'lora 3 low'] 'lora 2 high', 'lora 2 high strength', 'lora 2 low', 'lora 2 low strength',
'lora 3 high', 'lora 3 high strength', 'lora 3 low', 'lora 3 low strength']
standard_keys = { standard_keys = {
'name', 'mode', 'general_prompt', 'general_negative', 'current_prompt', 'negative', 'prompt', 'name', 'mode', 'general_prompt', 'general_negative', 'current_prompt', 'negative', 'prompt',
'seed', 'cfg', 'camera', 'flf', KEY_SEQUENCE_NUMBER, 'seed', 'cfg', 'camera', 'flf', KEY_SEQUENCE_NUMBER,
@@ -543,21 +544,28 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
with ui.expansion('LoRA Settings', icon='style').classes('w-full'): with ui.expansion('LoRA Settings', icon='style').classes('w-full'):
for lora_idx in range(1, 4): for lora_idx in range(1, 4):
for tier, tier_label in [('high', 'High'), ('low', 'Low')]: for tier, tier_label in [('high', 'High'), ('low', 'Low')]:
k = f'lora {lora_idx} {tier}' name_key = f'lora {lora_idx} {tier}'
raw = str(seq.get(k, '')) str_key = f'lora {lora_idx} {tier} strength'
inner = raw.replace('<lora:', '').replace('>', '')
# Split "name:strength" or just "name" # Migrate legacy <lora:name:strength> format
if ':' in inner: raw = str(seq.get(name_key, ''))
parts = inner.rsplit(':', 1) if raw.startswith('<lora:'):
lora_name = parts[0] inner = raw.replace('<lora:', '').replace('>', '')
try: if ':' in inner:
lora_strength = float(parts[1]) parts = inner.rsplit(':', 1)
except ValueError: seq[name_key] = parts[0]
lora_name = inner try:
lora_strength = 1.0 seq[str_key] = float(parts[1])
except ValueError:
seq[str_key] = 1.0
else:
seq[name_key] = inner
seq.setdefault(str_key, 1.0)
else: else:
lora_name = inner seq.setdefault(str_key, 1.0)
lora_strength = 1.0
lora_name = seq.get(name_key, '')
lora_strength = seq.get(str_key, 1.0)
with ui.row().classes('w-full items-center q-gutter-sm'): with ui.row().classes('w-full items-center q-gutter-sm'):
ui.label(f'L{lora_idx} {tier_label}').classes( ui.label(f'L{lora_idx} {tier_label}').classes(
@@ -573,10 +581,9 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
format='%.1f', format='%.1f',
).props('outlined dense').style('max-width: 80px') ).props('outlined dense').style('max-width: 80px')
def _lora_sync(key=k, n_inp=name_input, s_inp=strength_input): def _lora_sync(nk=name_key, sk=str_key, n_inp=name_input, s_inp=strength_input):
name = n_inp.value or '' seq[nk] = n_inp.value or ''
strength = s_inp.value if s_inp.value is not None else 1.0 seq[sk] = s_inp.value if s_inp.value is not None else 1.0
seq[key] = f'<lora:{name}:{strength:.1f}>' if name else ''
name_input.on('blur', lambda _, s=_lora_sync: s()) name_input.on('blur', lambda _, s=_lora_sync: s())
name_input.on('update:model-value', lambda _, s=_lora_sync: s()) name_input.on('update:model-value', lambda _, s=_lora_sync: s())
+8 -7
View File
@@ -604,13 +604,14 @@ def _render_preview_fields(item_data: dict):
with ui.expansion('LoRA Configuration'): with ui.expansion('LoRA Configuration'):
with ui.row().classes('w-full q-gutter-md'): with ui.row().classes('w-full q-gutter-md'):
for lora_idx in range(1, 4): for lora_idx in range(1, 4):
with ui.column(): for tier, tier_label in [('high', 'High'), ('low', 'Low')]:
ui.input(f'L{lora_idx} Name', with ui.column():
value=item_data.get(f'lora {lora_idx} high', '')).props( ui.input(f'L{lora_idx} {tier_label} Name',
'readonly outlined dense') value=item_data.get(f'lora {lora_idx} {tier}', '')).props(
ui.input(f'L{lora_idx} Str', 'readonly outlined dense')
value=str(item_data.get(f'lora {lora_idx} low', ''))).props( ui.input(f'L{lora_idx} {tier_label} Str',
'readonly outlined dense') value=str(item_data.get(f'lora {lora_idx} {tier} strength', 1.0))).props(
'readonly outlined dense')
vace_keys = ['frame_to_skip', 'vace schedule', 'video file path'] vace_keys = ['frame_to_skip', 'vace schedule', 'video file path']
if any(k in item_data for k in vace_keys): if any(k in item_data for k in vace_keys):
+6 -3
View File
@@ -50,9 +50,12 @@ DEFAULTS = {
"flf image path": "", "flf image path": "",
# --- LoRAs --- # --- LoRAs ---
"lora 1 high": "", "lora 1 low": "", "lora 1 high": "", "lora 1 high strength": 1.0,
"lora 2 high": "", "lora 2 low": "", "lora 1 low": "", "lora 1 low strength": 1.0,
"lora 3 high": "", "lora 3 low": "" "lora 2 high": "", "lora 2 high strength": 1.0,
"lora 2 low": "", "lora 2 low strength": 1.0,
"lora 3 high": "", "lora 3 high strength": 1.0,
"lora 3 low": "", "lora 3 low strength": 1.0
} }
CONFIG_FILE = Path(".editor_config.json") CONFIG_FILE = Path(".editor_config.json")