From bc2035eee55fef7bca0000ada5977bc0a5a956c9 Mon Sep 17 00:00:00 2001 From: ethanfel Date: Sat, 3 Jan 2026 16:22:26 +0100 Subject: [PATCH] Update app.py --- app.py | 151 ++++++++++++++++++++++++--------------------------------- 1 file changed, 64 insertions(+), 87 deletions(-) diff --git a/app.py b/app.py index 20a540c..8fd194d 100644 --- a/app.py +++ b/app.py @@ -1,59 +1,49 @@ import streamlit as st import random from pathlib import Path -import time # --- Import Custom Modules --- -from db_manager import DatabaseManager -# We retain your UI modules, but they should now just receive data/dicts +from utils import ( + load_config, save_config, load_snippets, save_snippets, + load_json, save_json, generate_templates, DEFAULTS +) from tab_single import render_single_editor from tab_batch import render_batch_processor from tab_timeline import render_timeline_tab from tab_timeline_wip import render_timeline_wip from tab_comfy import render_comfy_monitor -# Defined locally to avoid missing utils import, or keep your utils for DEFAULTS only -DEFAULTS = { - "positive_prompt": "", - "negative_prompt": "", - "seed": -1, - "steps": 20, - "cfg": 7.0, -} - # ========================================== -# 1. PAGE CONFIGURATION & DB INIT +# 1. PAGE CONFIGURATION # ========================================== -st.set_page_config(layout="wide", page_title="AI Settings Manager (DB)") - -# Initialize Database Manager -if 'db' not in st.session_state: - st.session_state.db = DatabaseManager() +st.set_page_config(layout="wide", page_title="AI Settings Manager") # ========================================== # 2. SESSION STATE INITIALIZATION # ========================================== -# Load App Config from DB if 'config' not in st.session_state: - st.session_state.config = st.session_state.db.load_app_config() - - # --- MIGRATION CHECK --- - # If DB is empty, try to migrate from the folder in 'last_dir' or current cwd - if st.session_state.db.is_empty(): - migration_target = st.session_state.config.get("last_dir", Path.cwd()) - st.session_state.db.migrate_from_json(migration_target) - # Reload config after potential migration - st.session_state.config = st.session_state.db.load_app_config() - + st.session_state.config = load_config() st.session_state.current_dir = Path(st.session_state.config.get("last_dir", Path.cwd())) if 'snippets' not in st.session_state: - st.session_state.snippets = st.session_state.db.load_snippets() + st.session_state.snippets = load_snippets() if 'loaded_file' not in st.session_state: st.session_state.loaded_file = None -# Track the active tab state +if 'last_mtime' not in st.session_state: + st.session_state.last_mtime = 0 + +if 'edit_history_idx' not in st.session_state: + st.session_state.edit_history_idx = None + +if 'single_editor_cache' not in st.session_state: + st.session_state.single_editor_cache = DEFAULTS.copy() + +if 'ui_reset_token' not in st.session_state: + st.session_state.ui_reset_token = 0 + +# Track the active tab state for programmatic switching if 'active_tab_name' not in st.session_state: st.session_state.active_tab_name = "📝 Single Editor" @@ -64,21 +54,20 @@ with st.sidebar: st.header("📂 Navigator") # --- Path Navigator --- - # Note: User still navigates folders to FIND projects, but projects live in DB. new_path = st.text_input("Current Path", value=str(st.session_state.current_dir)) if new_path != str(st.session_state.current_dir): p = Path(new_path) if p.exists() and p.is_dir(): st.session_state.current_dir = p st.session_state.config['last_dir'] = str(p) - st.session_state.db.save_app_config(st.session_state.config) + save_config(st.session_state.current_dir, st.session_state.config['favorites']) st.rerun() # --- Favorites System --- if st.button("📌 Pin Current Folder"): 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)) - st.session_state.db.save_app_config(st.session_state.config) + save_config(st.session_state.current_dir, st.session_state.config['favorites']) st.rerun() fav_selection = st.radio( @@ -93,15 +82,15 @@ with st.sidebar: st.markdown("---") - # --- Snippet Library (DB Backed) --- + # --- 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.db.save_snippet(snip_name, snip_content) - st.session_state.snippets = st.session_state.db.load_snippets() # Reload + st.session_state.snippets[snip_name] = snip_content + save_snippets(st.session_state.snippets) st.success(f"Saved '{snip_name}'") st.rerun() @@ -113,47 +102,43 @@ with st.sidebar: st.session_state.append_prompt = content st.rerun() if col_s2.button("🗑️", key=f"del_snip_{name}"): - st.session_state.db.delete_snippet(name) - st.session_state.snippets = st.session_state.db.load_snippets() + del st.session_state.snippets[name] + save_snippets(st.session_state.snippets) st.rerun() st.markdown("---") - # --- File List (From DB) --- - # We query the DB for projects that belong to the current directory - db_files = st.session_state.db.get_projects_in_dir(st.session_state.current_dir) + # --- 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 Project"): - new_filename = st.text_input("Project Name", placeholder="my_prompt_vace") + with st.expander("Create New JSON"): + new_filename = st.text_input("Filename", placeholder="my_prompt_vace") is_batch = st.checkbox("Is Batch File?") if st.button("Create"): - if not new_filename.endswith(".json"): new_filename += ".json" # Keep extension for legacy feel/compatibility - - # Init Data + if not new_filename.endswith(".json"): new_filename += ".json" + path = st.session_state.current_dir / new_filename if is_batch: data = {"batch_data": []} else: data = DEFAULTS.copy() if "vace" in new_filename: data.update({"frame_to_skip": 81, "vace schedule": 1, "video file path": ""}) - - # Save to DB - full_path = st.session_state.current_dir / new_filename - st.session_state.db.save_project(full_path, data, is_batch) + elif "i2v" in new_filename: data.update({"reference image path": "", "flf image path": ""}) + save_json(path, data) st.rerun() # --- File Selector --- if 'file_selector' not in st.session_state: - st.session_state.file_selector = db_files[0] if db_files else None + st.session_state.file_selector = json_files[0].name if json_files else None + if st.session_state.file_selector not in [f.name for f in json_files] and json_files: + st.session_state.file_selector = json_files[0].name - # If list is empty or current selection not in list, reset - if db_files and (st.session_state.file_selector not in db_files): - st.session_state.file_selector = db_files[0] - - selected_file_name = None - if db_files: - selected_file_name = st.radio("Select Project", db_files, key="file_selector") - else: - st.info("No projects found in this folder.") + selected_file_name = st.radio("Select File", [f.name for f in json_files], key="file_selector") # ========================================== # 4. MAIN APP LOGIC @@ -161,17 +146,22 @@ with st.sidebar: if selected_file_name: file_path = st.session_state.current_dir / selected_file_name - # --- LOAD FROM DB --- - # We reload if the file changed OR if we just performed a save (to get updates) + # --- FILE LOADING & AUTO-SWITCH LOGIC --- if st.session_state.loaded_file != str(file_path): - data = st.session_state.db.load_project(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 'append_prompt' in st.session_state: del st.session_state.append_prompt + if 'rand_seed' in st.session_state: del st.session_state.rand_seed + if 'restored_indicator' in st.session_state: del st.session_state.restored_indicator + st.session_state.edit_history_idx = None # --- AUTO-SWITCH TAB LOGIC --- + # If the file has 'batch_data' or is a list, force Batch tab. + # Otherwise, force Single tab. is_batch = "batch_data" in data or isinstance(data, list) if is_batch: st.session_state.active_tab_name = "🚀 Batch Processor" @@ -183,7 +173,8 @@ if selected_file_name: st.title(f"Editing: {selected_file_name}") - # --- NAVIGATION --- + # --- CONTROLLED NAVIGATION (REPLACES ST.TABS) --- + # Using radio buttons allows us to change 'active_tab_name' programmatically above. tabs_list = [ "📝 Single Editor", "🚀 Batch Processor", @@ -192,40 +183,26 @@ if selected_file_name: "🔌 Comfy Monitor" ] - # Sync radio with session state + # Ensure active tab is valid (safety check) + 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, - index=tabs_list.index(st.session_state.active_tab_name) if st.session_state.active_tab_name in tabs_list else 0, + key="active_tab_name", # Binds to session state horizontal=True, label_visibility="collapsed" ) - st.session_state.active_tab_name = current_tab st.markdown("---") - # --- RENDER TABS --- - # Note: Inside your render functions (tab_single.py etc), you likely have a "Save" button. - # You must update those files to call st.session_state.db.save_project() instead of save_json(). - # OR, we can handle the save here if we pass a callback or check for state changes. - - # For now, assuming render functions modify 'data' dictionary in place: - + # --- RENDER SELECTED TAB --- if current_tab == "📝 Single Editor": - # Pass data. Return value could be ignored if render modifies dict in place - render_single_editor(data, file_path) - - # SAVE BUTTON for Single Editor (Global override) - if st.button("💾 Save Changes to DB", key="save_main"): - st.session_state.db.save_project(file_path, data) - st.success("Saved to Database!") + render_single_editor(data, file_path) elif current_tab == "🚀 Batch Processor": - # We pass the list of 'db_files' instead of 'json_files' - render_batch_processor(data, file_path, db_files, st.session_state.current_dir, selected_file_name) - if st.button("💾 Save Batch to DB", key="save_batch"): - st.session_state.db.save_project(file_path, data, is_batch=True) - st.success("Batch Saved!") + 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)