From 387d4d874cbce7da33f53320e078497e4b932c87 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Mon, 23 Feb 2026 17:14:09 +0100 Subject: [PATCH] Remove Single Editor tab (dead code) Co-Authored-By: Claude Opus 4.6 --- README.md | 16 +--- app.py | 16 +--- tab_single.py | 250 -------------------------------------------------- 3 files changed, 5 insertions(+), 277 deletions(-) delete mode 100644 tab_single.py diff --git a/README.md b/README.md index 2e7bb07..ef21b79 100644 --- a/README.md +++ b/README.md @@ -38,19 +38,6 @@ A visual dashboard for managing, versioning, and batch-processing JSON configura -

-E -Single File Editor -

- -- Visual editing of Prompts, Seeds, LoRAs, Camera, FLF, VACE params -- Custom key-value parameters that persist and flow to ComfyUI -- Conflict protection against external file modifications -- Snippet library for reusable prompt fragments - - - -

B Batch Processor @@ -60,6 +47,8 @@ Batch Processor - Import settings from any file or history entry - Per-shot custom keys (e.g. Shot 1: `fog: 0.5`, Shot 2: `fog: 0.0`) - Clone, reorder, and manage sequences visually +- Conflict protection against external file modifications +- Snippet library for reusable prompt fragments @@ -329,7 +318,6 @@ ComfyUI-JSON-Manager/ ├── app.py # Streamlit main entry point & navigator ├── utils.py # I/O, config, defaults, case-insensitive path resolver ├── history_tree.py # Git-style branching engine -├── tab_single.py # Single file editor UI ├── tab_batch.py # Batch processor UI ├── tab_timeline.py # Visual timeline UI ├── tab_comfy.py # ComfyUI server monitor diff --git a/app.py b/app.py index 79a3753..29685e0 100644 --- a/app.py +++ b/app.py @@ -9,7 +9,6 @@ from utils import ( KEY_BATCH_DATA, KEY_PROMPT_HISTORY, KEY_SEQUENCE_NUMBER, resolve_path_case_insensitive, ) -from tab_single import render_single_editor from tab_batch import render_batch_processor from tab_timeline import render_timeline_tab from tab_comfy import render_comfy_monitor @@ -28,9 +27,8 @@ _SESSION_DEFAULTS = { "loaded_file": lambda: None, "last_mtime": lambda: 0, "edit_history_idx": lambda: None, - "single_editor_cache": lambda: DEFAULTS.copy(), "ui_reset_token": lambda: 0, - "active_tab_name": lambda: "📝 Single Editor", + "active_tab_name": lambda: "🚀 Batch Processor", } if 'config' not in st.session_state: @@ -187,11 +185,7 @@ if selected_file_name: st.session_state.edit_history_idx = None # --- AUTO-SWITCH TAB LOGIC --- - is_batch = KEY_BATCH_DATA in data or isinstance(data, list) - if is_batch: - st.session_state.active_tab_name = "🚀 Batch Processor" - else: - st.session_state.active_tab_name = "📝 Single Editor" + st.session_state.active_tab_name = "🚀 Batch Processor" else: data = st.session_state.data_cache @@ -201,7 +195,6 @@ if selected_file_name: # --- CONTROLLED NAVIGATION --- # Removed "🔌 Comfy Monitor" from this list tabs_list = [ - "📝 Single Editor", "🚀 Batch Processor", "🕒 Timeline", "💻 Raw Editor" @@ -221,10 +214,7 @@ if selected_file_name: st.markdown("---") # --- RENDER EDITOR TABS --- - if current_tab == "📝 Single Editor": - render_single_editor(data, file_path) - - elif current_tab == "🚀 Batch Processor": + if current_tab == "🚀 Batch Processor": render_batch_processor(data, file_path, json_files, st.session_state.current_dir, selected_file_name) elif current_tab == "🕒 Timeline": diff --git a/tab_single.py b/tab_single.py deleted file mode 100644 index fd3afff..0000000 --- a/tab_single.py +++ /dev/null @@ -1,250 +0,0 @@ -import streamlit as st -import random -from utils import DEFAULTS, save_json, get_file_mtime, KEY_BATCH_DATA, KEY_PROMPT_HISTORY, KEY_SEQUENCE_NUMBER - -def render_single_editor(data, file_path): - is_batch_file = KEY_BATCH_DATA in data or isinstance(data, list) - - if is_batch_file: - st.info("This is a batch file. Switch to the 'Batch Processor' tab.") - return - - col1, col2 = st.columns([2, 1]) - - # Unique prefix for this file's widgets + Version Token (Fixes Restore bug) - fk = f"{file_path.name}_v{st.session_state.ui_reset_token}" - - # --- FORM --- - with col1: - with st.expander("🌍 General Prompts (Global Layer)", expanded=False): - gen_prompt = st.text_area("General Prompt", value=data.get("general_prompt", ""), height=100, key=f"{fk}_gp") - gen_negative = st.text_area("General Negative", value=data.get("general_negative", DEFAULTS["general_negative"]), height=100, key=f"{fk}_gn") - - st.write("📝 **Specific Prompts**") - current_prompt_val = data.get("current_prompt", "") - if 'append_prompt' in st.session_state: - current_prompt_val = (current_prompt_val.strip() + ", " + st.session_state.append_prompt).strip(', ') - del st.session_state.append_prompt - - new_prompt = st.text_area("Specific Prompt", value=current_prompt_val, height=250, key=f"{fk}_sp") - new_negative = st.text_area("Specific Negative", value=data.get("negative", ""), height=100, key=f"{fk}_sn") - - # Seed - col_seed_val, col_seed_btn = st.columns([4, 1]) - seed_key = f"{fk}_seed" - - with col_seed_btn: - st.write("") - st.write("") - if st.button("🎲 Randomize", key=f"{fk}_rand"): - st.session_state[seed_key] = random.randint(0, 999999999999) - st.rerun() - - with col_seed_val: - seed_val = st.session_state.get('rand_seed', int(data.get("seed", 0))) - new_seed = st.number_input("Seed", value=seed_val, step=1, min_value=0, format="%d", key=seed_key) - data["seed"] = new_seed - - # LoRAs - st.subheader("LoRAs") - l_col1, l_col2 = st.columns(2) - loras = {} - lora_keys = ["lora 1 high", "lora 1 low", "lora 2 high", "lora 2 low", "lora 3 high", "lora 3 low"] - for i, k in enumerate(lora_keys): - with (l_col1 if i % 2 == 0 else l_col2): - loras[k] = st.text_input(k.title(), value=data.get(k, ""), key=f"{fk}_{k}") - - # Settings - st.subheader("Settings") - spec_fields = {} - spec_fields["camera"] = st.text_input("Camera", value=str(data.get("camera", DEFAULTS["camera"])), key=f"{fk}_cam") - spec_fields["flf"] = st.text_input("FLF", value=str(data.get("flf", DEFAULTS["flf"])), key=f"{fk}_flf") - - # Explicitly track standard setting keys to exclude them from custom list - standard_keys = { - "general_prompt", "general_negative", "current_prompt", "negative", "prompt", "seed", - "camera", "flf", KEY_BATCH_DATA, KEY_PROMPT_HISTORY, KEY_SEQUENCE_NUMBER, "ui_reset_token", - "model_name", "vae_name", "steps", "cfg", "denoise", "sampler_name", "scheduler" - } - standard_keys.update(lora_keys) - - if "vace" in file_path.name: - vace_keys = ["frame_to_skip", "input_a_frames", "input_b_frames", "reference switch", "vace schedule", "reference path", "video file path", "reference image path"] - standard_keys.update(vace_keys) - - spec_fields["frame_to_skip"] = st.number_input("Frame to Skip", value=int(data.get("frame_to_skip", 81)), key=f"{fk}_fts") - spec_fields["input_a_frames"] = st.number_input("Input A Frames", value=int(data.get("input_a_frames", 16)), key=f"{fk}_ia") - spec_fields["input_b_frames"] = st.number_input("Input B Frames", value=int(data.get("input_b_frames", 16)), key=f"{fk}_ib") - spec_fields["reference switch"] = st.number_input("Reference Switch", value=int(data.get("reference switch", 1)), key=f"{fk}_rsw") - spec_fields["vace schedule"] = st.number_input("VACE Schedule", value=int(data.get("vace schedule", 1)), key=f"{fk}_vsc") - for f in ["reference path", "video file path", "reference image path"]: - spec_fields[f] = st.text_input(f.title(), value=str(data.get(f, "")), key=f"{fk}_{f}") - elif "i2v" in file_path.name: - i2v_keys = ["reference image path", "flf image path", "video file path"] - standard_keys.update(i2v_keys) - - for f in i2v_keys: - spec_fields[f] = st.text_input(f.title(), value=str(data.get(f, "")), key=f"{fk}_{f}") - - # --- CUSTOM PARAMETERS LOGIC --- - st.markdown("---") - st.subheader("🔧 Custom Parameters") - - # Filter keys: Only those NOT in the standard set - custom_keys = [k for k in data.keys() if k not in standard_keys] - - keys_to_remove = [] - - if custom_keys: - for k in custom_keys: - c1, c2, c3 = st.columns([1, 2, 0.5]) - c1.text_input("Key", value=k, disabled=True, key=f"{fk}_ck_lbl_{k}", label_visibility="collapsed") - val = c2.text_input("Value", value=str(data[k]), key=f"{fk}_cv_{k}", label_visibility="collapsed") - data[k] = val - - if c3.button("🗑️", key=f"{fk}_cdel_{k}"): - keys_to_remove.append(k) - else: - st.caption("No custom keys added.") - - # Add New Key Interface - with st.expander("➕ Add New Parameter"): - nk_col, nv_col = st.columns(2) - new_k = nk_col.text_input("Key Name", key=f"{fk}_new_k") - new_v = nv_col.text_input("Value", key=f"{fk}_new_v") - - if st.button("Add Parameter", key=f"{fk}_add_cust"): - if new_k and new_k not in data: - data[new_k] = new_v - st.rerun() - elif new_k in data: - st.error(f"Key '{new_k}' already exists!") - - # Apply Removals - if keys_to_remove: - for k in keys_to_remove: - del data[k] - st.rerun() - - # --- ACTIONS & HISTORY --- - with col2: - current_state = { - "general_prompt": gen_prompt, "general_negative": gen_negative, - "current_prompt": new_prompt, "negative": new_negative, - "seed": new_seed, **loras, **spec_fields - } - - # MERGE CUSTOM KEYS - for k in custom_keys: - if k not in keys_to_remove: - current_state[k] = data[k] - - st.session_state.single_editor_cache = current_state - - st.subheader("Actions") - current_disk_mtime = get_file_mtime(file_path) - is_conflict = current_disk_mtime > st.session_state.last_mtime - - if is_conflict: - st.error("⚠️ CONFLICT: Disk changed!") - if st.button("Force Save"): - data.update(current_state) - save_json(file_path, data) # No return val in new utils - st.session_state.last_mtime = get_file_mtime(file_path) # Manual Update - st.session_state.data_cache = data - st.toast("Saved!", icon="⚠️") - st.rerun() - if st.button("Reload File"): - st.session_state.loaded_file = None - st.rerun() - else: - if st.button("💾 Update File", use_container_width=True): - data.update(current_state) - save_json(file_path, data) - st.session_state.last_mtime = get_file_mtime(file_path) - st.session_state.data_cache = data - st.toast("Updated!", icon="✅") - - st.markdown("---") - archive_note = st.text_input("Archive Note") - if st.button("📦 Snapshot to History", use_container_width=True): - entry = {"note": archive_note if archive_note else "Snapshot", **current_state} - if KEY_PROMPT_HISTORY not in data: data[KEY_PROMPT_HISTORY] = [] - data[KEY_PROMPT_HISTORY].insert(0, entry) - data.update(entry) - save_json(file_path, data) - st.session_state.last_mtime = get_file_mtime(file_path) - st.session_state.data_cache = data - st.toast("Archived!", icon="📦") - st.rerun() - - # --- FULL HISTORY PANEL --- - st.markdown("---") - st.subheader("History") - history = data.get(KEY_PROMPT_HISTORY, []) - - if not history: - st.caption("No history yet.") - - for idx, h in enumerate(history): - note = h.get('note', 'No Note') - - with st.container(): - if st.session_state.edit_history_idx == idx: - with st.expander(f"📝 Editing: {note}", expanded=True): - edit_note = st.text_input("Note", value=note, key=f"h_en_{idx}") - edit_seed = st.number_input("Seed", value=int(h.get('seed', 0)), key=f"h_es_{idx}") - edit_gp = st.text_area("General P", value=h.get('general_prompt', ''), height=60, key=f"h_egp_{idx}") - edit_gn = st.text_area("General N", value=h.get('general_negative', ''), height=60, key=f"h_egn_{idx}") - edit_sp = st.text_area("Specific P", value=h.get('prompt', ''), height=100, key=f"h_esp_{idx}") - edit_sn = st.text_area("Specific N", value=h.get('negative', ''), height=60, key=f"h_esn_{idx}") - - hc1, hc2 = st.columns([1, 4]) - if hc1.button("💾 Save", key=f"h_save_{idx}"): - h.update({ - 'note': edit_note, 'seed': edit_seed, - 'general_prompt': edit_gp, 'general_negative': edit_gn, - 'prompt': edit_sp, 'negative': edit_sn - }) - save_json(file_path, data) - st.session_state.last_mtime = get_file_mtime(file_path) - st.session_state.data_cache = data - st.session_state.edit_history_idx = None - st.rerun() - if hc2.button("Cancel", key=f"h_can_{idx}"): - st.session_state.edit_history_idx = None - st.rerun() - - else: - with st.expander(f"#{idx+1}: {note}"): - st.caption(f"Seed: {h.get('seed', 0)}") - st.text(f"SPEC: {h.get('prompt', '')[:40]}...") - - view_data = {k:v for k,v in h.items() if k not in ['prompt', 'negative', 'general_prompt', 'general_negative', 'note']} - st.json(view_data, expanded=False) - - bh1, bh2, bh3 = st.columns([2, 1, 1]) - - if bh1.button("Restore", key=f"h_rest_{idx}", use_container_width=True): - data.update(h) - if 'prompt' in h: data['current_prompt'] = h['prompt'] - save_json(file_path, data) - st.session_state.last_mtime = get_file_mtime(file_path) - st.session_state.data_cache = data - - # Refresh UI - st.session_state.ui_reset_token += 1 - - st.toast("Restored!", icon="⏪") - st.rerun() - - if bh2.button("✏️", key=f"h_edit_{idx}"): - st.session_state.edit_history_idx = idx - st.rerun() - - if bh3.button("🗑️", key=f"h_del_{idx}"): - history.pop(idx) - save_json(file_path, data) - st.session_state.last_mtime = get_file_mtime(file_path) - st.session_state.data_cache = data - st.rerun()