- Remove unused `random` import and `KEY_PROMPT_HISTORY` from app.py - Remove dead session state keys: edit_history_idx, append_prompt, rand_seed - Clean up extra blank lines in json_loader.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
224 lines
8.2 KiB
Python
224 lines
8.2 KiB
Python
import streamlit as st
|
||
from pathlib import Path
|
||
|
||
# --- Import Custom Modules ---
|
||
from utils import (
|
||
load_config, save_config, load_snippets, save_snippets,
|
||
load_json, save_json, generate_templates, DEFAULTS,
|
||
KEY_BATCH_DATA, KEY_SEQUENCE_NUMBER,
|
||
resolve_path_case_insensitive,
|
||
)
|
||
from tab_batch import render_batch_processor
|
||
from tab_timeline import render_timeline_tab
|
||
from tab_comfy import render_comfy_monitor
|
||
from tab_raw import render_raw_editor
|
||
|
||
# ==========================================
|
||
# 1. PAGE CONFIGURATION
|
||
# ==========================================
|
||
st.set_page_config(layout="wide", page_title="AI Settings Manager")
|
||
|
||
# ==========================================
|
||
# 2. SESSION STATE INITIALIZATION
|
||
# ==========================================
|
||
_SESSION_DEFAULTS = {
|
||
"snippets": load_snippets,
|
||
"loaded_file": lambda: None,
|
||
"last_mtime": lambda: 0,
|
||
"ui_reset_token": lambda: 0,
|
||
"active_tab_name": lambda: "🚀 Batch Processor",
|
||
}
|
||
|
||
if 'config' not in st.session_state:
|
||
st.session_state.config = load_config()
|
||
st.session_state.current_dir = Path(st.session_state.config.get("last_dir", Path.cwd()))
|
||
|
||
for key, factory in _SESSION_DEFAULTS.items():
|
||
if key not in st.session_state:
|
||
st.session_state[key] = factory()
|
||
|
||
# ==========================================
|
||
# 3. SIDEBAR (NAVIGATOR & TOOLS)
|
||
# ==========================================
|
||
with st.sidebar:
|
||
st.header("📂 Navigator")
|
||
|
||
# --- Path Navigator ---
|
||
# Sync widget to current_dir on first load or after external change
|
||
if "nav_path_input" not in st.session_state or st.session_state.get("_sync_nav_path"):
|
||
st.session_state.nav_path_input = str(st.session_state.current_dir)
|
||
st.session_state._sync_nav_path = False
|
||
|
||
def _on_path_change():
|
||
new_path = st.session_state.nav_path_input
|
||
p = resolve_path_case_insensitive(new_path)
|
||
if p is not None and p.is_dir():
|
||
st.session_state.current_dir = p
|
||
st.session_state.config['last_dir'] = str(p)
|
||
save_config(st.session_state.current_dir, st.session_state.config['favorites'])
|
||
st.session_state.loaded_file = None
|
||
# Always resync widget to canonical path form
|
||
st.session_state._sync_nav_path = True
|
||
|
||
st.text_input("Current Path", key="nav_path_input", on_change=_on_path_change)
|
||
|
||
# --- Favorites System ---
|
||
if st.button("📌 Pin Folder", use_container_width=True):
|
||
if str(st.session_state.current_dir) not in st.session_state.config['favorites']:
|
||
st.session_state.config['favorites'].append(str(st.session_state.current_dir))
|
||
save_config(st.session_state.current_dir, st.session_state.config['favorites'])
|
||
st.rerun()
|
||
|
||
favorites = st.session_state.config['favorites']
|
||
if favorites:
|
||
def _on_fav_jump():
|
||
sel = st.session_state._fav_radio
|
||
if sel != "Select..." and sel != str(st.session_state.current_dir):
|
||
st.session_state.current_dir = Path(sel)
|
||
st.session_state._sync_nav_path = True
|
||
|
||
st.radio(
|
||
"Jump to:",
|
||
["Select..."] + favorites,
|
||
index=0,
|
||
key="_fav_radio",
|
||
label_visibility="collapsed",
|
||
on_change=_on_fav_jump,
|
||
)
|
||
|
||
# Unpin buttons for each favorite
|
||
for fav in favorites:
|
||
fc1, fc2 = st.columns([4, 1])
|
||
fc1.caption(fav)
|
||
if fc2.button("❌", key=f"unpin_{fav}"):
|
||
st.session_state.config['favorites'].remove(fav)
|
||
save_config(st.session_state.current_dir, st.session_state.config['favorites'])
|
||
st.rerun()
|
||
|
||
st.markdown("---")
|
||
|
||
# --- Snippet Library ---
|
||
st.subheader("🧩 Snippet Library")
|
||
with st.expander("Add New Snippet"):
|
||
snip_name = st.text_input("Name", placeholder="e.g. Cinematic")
|
||
snip_content = st.text_area("Content", placeholder="4k, high quality...")
|
||
if st.button("Save Snippet"):
|
||
if snip_name and snip_content:
|
||
st.session_state.snippets[snip_name] = snip_content
|
||
save_snippets(st.session_state.snippets)
|
||
st.success(f"Saved '{snip_name}'")
|
||
st.rerun()
|
||
|
||
if st.session_state.snippets:
|
||
st.caption("Click to Append to Prompt:")
|
||
for name, content in st.session_state.snippets.items():
|
||
col_s1, col_s2 = st.columns([4, 1])
|
||
if col_s1.button(f"➕ {name}", use_container_width=True):
|
||
st.rerun()
|
||
if col_s2.button("🗑️", key=f"del_snip_{name}"):
|
||
del st.session_state.snippets[name]
|
||
save_snippets(st.session_state.snippets)
|
||
st.rerun()
|
||
|
||
st.markdown("---")
|
||
|
||
# --- File List & Creation ---
|
||
json_files = sorted(list(st.session_state.current_dir.glob("*.json")))
|
||
json_files = [f for f in json_files if f.name != ".editor_config.json" and f.name != ".editor_snippets.json"]
|
||
|
||
if not json_files:
|
||
if st.button("Generate Templates"):
|
||
generate_templates(st.session_state.current_dir)
|
||
st.rerun()
|
||
|
||
with st.expander("Create New JSON"):
|
||
new_filename = st.text_input("Filename", placeholder="my_prompt_vace")
|
||
if st.button("Create"):
|
||
if not new_filename.endswith(".json"): new_filename += ".json"
|
||
path = st.session_state.current_dir / new_filename
|
||
first_item = DEFAULTS.copy()
|
||
first_item[KEY_SEQUENCE_NUMBER] = 1
|
||
data = {KEY_BATCH_DATA: [first_item]}
|
||
save_json(path, data)
|
||
st.rerun()
|
||
|
||
# --- File Selector ---
|
||
selected_file_name = None
|
||
if json_files:
|
||
file_names = [f.name for f in json_files]
|
||
if 'file_selector' not in st.session_state:
|
||
st.session_state.file_selector = file_names[0]
|
||
if st.session_state.file_selector not in file_names:
|
||
st.session_state.file_selector = file_names[0]
|
||
|
||
selected_file_name = st.radio("Select File", file_names, key="file_selector")
|
||
else:
|
||
st.info("No JSON files in this folder.")
|
||
if 'file_selector' in st.session_state:
|
||
del st.session_state.file_selector
|
||
st.session_state.loaded_file = None
|
||
|
||
# --- GLOBAL MONITOR TOGGLE (NEW) ---
|
||
st.markdown("---")
|
||
show_monitor = st.checkbox("Show Comfy Monitor", value=True)
|
||
|
||
# ==========================================
|
||
# 4. MAIN APP LOGIC
|
||
# ==========================================
|
||
if selected_file_name:
|
||
file_path = st.session_state.current_dir / selected_file_name
|
||
|
||
# --- FILE LOADING & AUTO-SWITCH LOGIC ---
|
||
if st.session_state.loaded_file != str(file_path):
|
||
data, mtime = load_json(file_path)
|
||
st.session_state.data_cache = data
|
||
st.session_state.last_mtime = mtime
|
||
st.session_state.loaded_file = str(file_path)
|
||
|
||
# Clear transient states
|
||
if 'restored_indicator' in st.session_state: del st.session_state.restored_indicator
|
||
|
||
# --- AUTO-SWITCH TAB LOGIC ---
|
||
st.session_state.active_tab_name = "🚀 Batch Processor"
|
||
|
||
else:
|
||
data = st.session_state.data_cache
|
||
|
||
st.title(f"Editing: {selected_file_name}")
|
||
|
||
# --- CONTROLLED NAVIGATION ---
|
||
# Removed "🔌 Comfy Monitor" from this list
|
||
tabs_list = [
|
||
"🚀 Batch Processor",
|
||
"🕒 Timeline",
|
||
"💻 Raw Editor"
|
||
]
|
||
|
||
if st.session_state.active_tab_name not in tabs_list:
|
||
st.session_state.active_tab_name = tabs_list[0]
|
||
|
||
current_tab = st.radio(
|
||
"Navigation",
|
||
tabs_list,
|
||
key="active_tab_name",
|
||
horizontal=True,
|
||
label_visibility="collapsed"
|
||
)
|
||
|
||
st.markdown("---")
|
||
|
||
# --- RENDER EDITOR TABS ---
|
||
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":
|
||
render_timeline_tab(data, file_path)
|
||
|
||
elif current_tab == "💻 Raw Editor":
|
||
render_raw_editor(data, file_path)
|
||
|
||
# --- GLOBAL PERSISTENT MONITOR ---
|
||
if show_monitor:
|
||
st.markdown("---")
|
||
with st.expander("🔌 ComfyUI Monitor", expanded=True):
|
||
render_comfy_monitor() |