feat: move frame paths to left column with strength + logic index switch

Each frame path row (start/middle/end) now has:
- path input with preview
- strength float (default 1.0)
- switch linked to the corresponding logic index bit

Switches and logic index are bidirectionally synced.
end_frame → logic index → switches mirror chain preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 11:38:30 +02:00
parent 2619d2c7e2
commit fec843f804
2 changed files with 47 additions and 24 deletions
+44 -24
View File
@@ -316,7 +316,10 @@ def render_batch_processor(state: AppState):
'seed', 'cfg', 'camera', 'flf', KEY_SEQUENCE_NUMBER, 'seed', 'cfg', 'camera', 'flf', KEY_SEQUENCE_NUMBER,
'frame_to_skip', 'end_frame', 'logic index', 'transition', 'vace_length', 'frame_to_skip', 'end_frame', 'logic index', 'transition', 'vace_length',
'input_a_frames', 'input_b_frames', 'reference switch', 'vace schedule', 'input_a_frames', 'input_b_frames', 'reference switch', 'vace schedule',
'middle frame path', 'video file path', 'start frame path', 'end frame path', 'start frame path', 'start frame strength',
'middle frame path', 'middle frame strength',
'end frame path', 'end frame strength',
'video file path',
} }
standard_keys.update(lora_keys) standard_keys.update(lora_keys)
@@ -542,6 +545,7 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
ui.separator() ui.separator()
# --- Prompts + Settings (2-column) --- # --- Prompts + Settings (2-column) ---
frame_switches = [] # populated below, used for bidirectional sync with logic index
with ui.splitter(value=66).classes('w-full') as splitter: with ui.splitter(value=66).classes('w-full') as splitter:
with splitter.before: with splitter.before:
dict_textarea('General Prompt', seq, 'general_prompt').classes( dict_textarea('General Prompt', seq, 'general_prompt').classes(
@@ -553,6 +557,28 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
dict_textarea('Specific Negative', seq, 'negative').classes( dict_textarea('Specific Negative', seq, 'negative').classes(
'w-full q-mt-sm').props('outlined rows=2') 'w-full q-mt-sm').props('outlined rows=2')
# --- Frame paths (start / middle / end) ---
logic_val = int(seq.get('logic index', 0))
for bit, img_label, img_key, str_key in [
(0, 'Start Frame', 'start frame path', 'start frame strength'),
(1, 'Middle Frame', 'middle frame path', 'middle frame strength'),
(2, 'End Frame', 'end frame path', 'end frame strength'),
]:
ui.label(img_label).classes('text-caption text-weight-bold q-mt-sm')
with ui.row().classes('w-full items-center no-wrap q-mt-xs'):
inp = dict_input(ui.input, 'Path', seq, img_key).classes(
'col').props('outlined dense input-style="text-align: right"')
img_path = Path(seq.get(img_key, '')) if seq.get(img_key) else None
if (img_path and img_path.exists() and
img_path.suffix.lower() in IMAGE_EXTENSIONS):
with ui.dialog() as dlg, ui.card():
ui.image(str(img_path)).classes('w-full')
ui.button(icon='visibility', on_click=dlg.open).props('flat dense')
str_inp = dict_number('Strength', seq, str_key, default=1.0,
step=0.05, format='%.2f').style(
'width:80px').props('outlined dense')
sw = ui.switch(value=bool((logic_val >> bit) & 1))
frame_switches.append(sw)
with splitter.after: with splitter.after:
# Mode # Mode
@@ -594,32 +620,26 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
'0: none 1: start 2: middle 3: start+middle\n' '0: none 1: start 2: middle 3: start+middle\n'
'4: end 5: start+end 6: middle+end 7: all' '4: end 5: start+end 6: middle+end 7: all'
) )
def _mirror_to_logic_index(ef=ef_input, li=li_input, s=seq):
v = s.get('end_frame', 0)
s['logic index'] = v
li.set_value(v)
ef_input.on('blur', lambda _, m=_mirror_to_logic_index: m())
ef_input.on('update:model-value', lambda _, m=_mirror_to_logic_index: m())
dict_input(ui.input, 'Video File Path', seq, 'video file path').props( dict_input(ui.input, 'Video File Path', seq, 'video file path').props(
'outlined input-style="text-align: right"').classes('w-full') 'outlined input-style="text-align: right"').classes('w-full')
# Image paths with preview # Bidirectional sync: end_frame → logic index → switches, and switches → logic index
for img_label, img_key in [ def _mirror_end_to_logic(li=li_input, switches=frame_switches, s=seq):
('Start Frame Path', 'start frame path'), v = int(s.get('end_frame', 0))
('Middle Frame Path', 'middle frame path'), s['logic index'] = v
('End Frame Path', 'end frame path'), li.set_value(v)
]: for b, sw in enumerate(switches):
with ui.row().classes('w-full items-center'): sw.set_value(bool((v >> b) & 1))
inp = dict_input(ui.input, img_label, seq, img_key).classes(
'col').props('outlined input-style="text-align: right"') def _sync_switches_to_logic(li=li_input, switches=frame_switches, s=seq):
img_path = Path(seq.get(img_key, '')) if seq.get(img_key) else None v = sum(int(sw.value) << b for b, sw in enumerate(switches))
if (img_path and img_path.exists() and s['logic index'] = v
img_path.suffix.lower() in IMAGE_EXTENSIONS): li.set_value(v)
with ui.dialog() as dlg, ui.card():
ui.image(str(img_path)).classes('w-full') ef_input.on('blur', lambda _, m=_mirror_end_to_logic: m())
ui.button(icon='visibility', on_click=dlg.open).props('flat dense') ef_input.on('update:model-value', lambda _, m=_mirror_end_to_logic: m())
for frame_sw in frame_switches:
frame_sw.on('update:model-value', lambda _, s=_sync_switches_to_logic: s())
# --- Resolutions (8 fixed slots) --- # --- Resolutions (8 fixed slots) ---
resolutions = seq.setdefault('resolutions', []) resolutions = seq.setdefault('resolutions', [])
+3
View File
@@ -47,8 +47,11 @@ DEFAULTS = {
"reference switch": 1, "reference switch": 1,
"video file path": "", "video file path": "",
"start frame path": "", "start frame path": "",
"start frame strength": 1.0,
"middle frame path": "", "middle frame path": "",
"middle frame strength": 1.0,
"end frame path": "", "end frame path": "",
"end frame strength": 1.0,
# --- LoRAs (name as STRING, strength as FLOAT) --- # --- LoRAs (name as STRING, strength as FLOAT) ---
"lora 1 high": "", "lora 1 high": "",