import streamlit as st import random import json from utils import DEFAULTS, save_json, get_file_mtime from history_tree import HistoryTree def render_single_editor(data, file_path): is_batch_file = "batch_data" in data or isinstance(data, list) if is_batch_file: st.warning("⚠️ This file looks like a Batch file. Please switch to the 'Batch Processor' tab.") return # Check external modification current_mtime = get_file_mtime(file_path) if st.session_state.last_mtime != 0 and current_mtime > st.session_state.last_mtime: st.error("⚠️ File has been modified externally! Save will overwrite.") # --- TOP ROW: MODELS --- st.subheader("🤖 Models") c1, c2 = st.columns(2) with c1: data["model_name"] = st.text_input("Checkpoint", value=data.get("model_name", "")) with c2: data["vae_name"] = st.text_input("VAE", value=data.get("vae_name", "")) # --- PROMPTS --- st.markdown("---") st.subheader("📝 Prompts") if 'append_prompt' in st.session_state: current_p = data.get("positive_prompt", "") if current_p: current_p += "\n" data["positive_prompt"] = current_p + st.session_state.append_prompt del st.session_state.append_prompt data["positive_prompt"] = st.text_area("Positive Prompt", value=data.get("positive_prompt", ""), height=150) data["negative_prompt"] = st.text_area("Negative Prompt", value=data.get("negative_prompt", ""), height=100) # --- MAIN SETTINGS --- st.markdown("---") st.subheader("⚙️ Settings") col1, col2, col3 = st.columns(3) with col1: data["steps"] = st.number_input("Steps", value=int(data.get("steps", 20))) data["cfg"] = st.number_input("CFG", value=float(data.get("cfg", 7.0))) with col2: data["denoise"] = st.number_input("Denoise", value=float(data.get("denoise", 1.0))) data["sampler_name"] = st.text_input("Sampler", value=data.get("sampler_name", "euler")) with col3: data["scheduler"] = st.text_input("Scheduler", value=data.get("scheduler", "normal")) # Seed Logic s_row1, s_row2 = st.columns([3, 1]) with s_row2: st.write("") st.write("") if st.button("🎲"): st.session_state.rand_seed = random.randint(0, 999999999999) st.rerun() with s_row1: current_seed = st.session_state.get('rand_seed', int(data.get("seed", -1))) val = st.number_input("Seed", value=current_seed) data["seed"] = val # --- ADVANCED SECTIONS --- with st.expander("🎥 Camera & FLF Settings"): data["camera"] = st.text_input("Camera Motion", value=data.get("camera", "static")) data["flf"] = st.number_input("FLF", value=float(data.get("flf", 0.0))) data["frame_to_skip"] = st.number_input("Frames to Skip (VACE)", value=int(data.get("frame_to_skip", 81))) data["vace schedule"] = st.number_input("VACE Schedule", value=int(data.get("vace schedule", 1))) with st.expander("📂 File Paths"): data["video file path"] = st.text_input("Video Input Path", value=data.get("video file path", "")) data["reference image path"] = st.text_input("Reference Image Path", value=data.get("reference image path", "")) # --- LORAS (Reverted to plain text to avoid float/string crashes) --- st.subheader("💊 LoRAs") l1, l2, l3 = st.columns(3) with l1: data["lora 1 high"] = st.text_input("LoRA 1 Name", value=data.get("lora 1 high", "")) data["lora 1 low"] = st.text_input("LoRA 1 Strength", value=str(data.get("lora 1 low", ""))) with l2: data["lora 2 high"] = st.text_input("LoRA 2 Name", value=data.get("lora 2 high", "")) data["lora 2 low"] = st.text_input("LoRA 2 Strength", value=str(data.get("lora 2 low", ""))) with l3: data["lora 3 high"] = st.text_input("LoRA 3 Name", value=data.get("lora 3 high", "")) data["lora 3 low"] = st.text_input("LoRA 3 Strength", value=str(data.get("lora 3 low", ""))) # --- CUSTOM PARAMETERS --- st.markdown("---") st.caption("🔧 Custom Parameters") standard_keys = list(DEFAULTS.keys()) + ["history_tree", "prompt_history"] custom_keys = [k for k in data.keys() if k not in standard_keys] if custom_keys: keys_to_remove = [] for k in custom_keys: ck1, ck2, ck3 = st.columns([1, 2, 0.5]) ck1.text_input("Key", value=k, disabled=True, key=f"ck_lbl_{k}", label_visibility="collapsed") data[k] = ck2.text_input("Value", value=str(data[k]), key=f"cv_{k}", label_visibility="collapsed") if ck3.button("🗑️", key=f"cdel_{k}"): keys_to_remove.append(k) if keys_to_remove: for k in keys_to_remove: del data[k] save_json(file_path, data) st.rerun() with st.expander("➕ Add Parameter"): nk, nv = st.columns(2) new_k = nk.text_input("New Key") new_v = nv.text_input("New Value") if st.button("Add Parameter"): if new_k and new_k not in data: data[new_k] = new_v save_json(file_path, data) st.rerun() # --- SAVE ACTIONS --- st.markdown("---") c_save, c_snap = st.columns([1, 2]) with c_save: if st.button("💾 Save Changes", use_container_width=True): save_json(file_path, data) st.toast("Saved!", icon="✅") with c_snap: with st.popover("📸 Save Snapshot (History)", use_container_width=True): note = st.text_input("Snapshot Note", placeholder="e.g. Changed lighting") if st.button("Confirm Snapshot"): tree_data = data.get("history_tree", {}) htree = HistoryTree(tree_data) snapshot = data.copy() if "history_tree" in snapshot: del snapshot["history_tree"] htree.commit(snapshot, note=note if note else "Manual Snapshot") data["history_tree"] = htree.to_dict() save_json(file_path, data) st.toast("Snapshot Saved!", icon="📸")