From 1b8d13f7c4af6867bcd4d76f40190b6e1d345b1c Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sun, 1 Mar 2026 20:41:00 +0100 Subject: [PATCH] Fix 7 bugs: bounds checks, deepcopy, time import, JS keys, unused import - Add bounds check on src_batch index in add_from_source and copy_source - Guard delete callback against stale index after rapid clicks - Replace __import__('time').time() with time.time() in sync_to_db - Use deepcopy(DEFAULTS) consistently in utils.py and main.py - Use JSON.stringify in JS onConfigure fallback path for key storage - Read state.show_comfy_monitor for checkbox initial value - Remove unused KEY_BATCH_DATA import from tab_projects_ng Co-Authored-By: Claude Opus 4.6 --- main.py | 5 +++-- tab_batch_ng.py | 9 +++++---- tab_projects_ng.py | 2 +- utils.py | 13 +++++++------ web/project_dynamic.js | 4 ++-- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index 611adb6..0965964 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import copy import json import logging from pathlib import Path @@ -481,7 +482,7 @@ def render_sidebar(state: AppState, dual_pane: dict): if not fn.endswith('.json'): fn += '.json' path = state.current_dir / fn - first_item = DEFAULTS.copy() + first_item = copy.deepcopy(DEFAULTS) first_item[KEY_SEQUENCE_NUMBER] = 1 save_json(path, {KEY_BATCH_DATA: [first_item]}) new_fn_input.set_value('') @@ -514,7 +515,7 @@ def render_sidebar(state: AppState, dual_pane: dict): state.show_comfy_monitor = e.value state._render_main.refresh() - ui.checkbox('Show Comfy Monitor', value=True, on_change=on_monitor_toggle) + ui.checkbox('Show Comfy Monitor', value=state.show_comfy_monitor, on_change=on_monitor_toggle) # Register REST API routes for ComfyUI connectivity (uses the shared DB instance) diff --git a/tab_batch_ng.py b/tab_batch_ng.py index a88c3cf..857986f 100644 --- a/tab_batch_ng.py +++ b/tab_batch_ng.py @@ -273,7 +273,7 @@ def render_batch_processor(state: AppState): item = copy.deepcopy(DEFAULTS) src_batch = _src_cache['batch'] sel_idx = src_seq_select.value - if src_batch and sel_idx is not None: + if src_batch and sel_idx is not None and int(sel_idx) < len(src_batch): item.update(copy.deepcopy(src_batch[int(sel_idx)])) elif _src_cache['data']: item.update(copy.deepcopy(_src_cache['data'])) @@ -398,7 +398,7 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state, item = copy.deepcopy(DEFAULTS) src_batch = src_cache['batch'] sel_idx = src_seq_select.value - if src_batch and sel_idx is not None: + if src_batch and sel_idx is not None and int(sel_idx) < len(src_batch): item.update(copy.deepcopy(src_batch[int(sel_idx)])) elif src_cache['data']: item.update(copy.deepcopy(src_cache['data'])) @@ -453,8 +453,9 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state, # Delete def delete(idx=i): - batch_list.pop(idx) - commit() + if idx < len(batch_list): + batch_list.pop(idx) + commit() ui.button(icon='delete', on_click=delete).props('color=negative') diff --git a/tab_projects_ng.py b/tab_projects_ng.py index 5212a84..dbab978 100644 --- a/tab_projects_ng.py +++ b/tab_projects_ng.py @@ -7,7 +7,7 @@ from nicegui import ui from state import AppState from db import ProjectDB -from utils import save_config, sync_to_db, KEY_BATCH_DATA +from utils import save_config, sync_to_db logger = logging.getLogger(__name__) diff --git a/utils.py b/utils.py index 634f07f..75269ab 100644 --- a/utils.py +++ b/utils.py @@ -1,3 +1,4 @@ +import copy import json import logging import os @@ -182,7 +183,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None: top_level = {k: v for k, v in data.items() if k not in (KEY_BATCH_DATA, KEY_HISTORY_TREE)} if not df: - now = __import__('time').time() + now = time.time() cur = db.conn.execute( "INSERT INTO data_files (project_id, name, data_type, top_level, created_at, updated_at) " "VALUES (?, ?, ?, ?, ?, ?)", @@ -192,7 +193,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None: else: df_id = df["id"] # Update top_level metadata - now = __import__('time').time() + now = time.time() db.conn.execute( "UPDATE data_files SET top_level = ?, updated_at = ? WHERE id = ?", (json.dumps(top_level), now, df_id), @@ -206,7 +207,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None: if not isinstance(item, dict): continue seq_num = int(item.get(KEY_SEQUENCE_NUMBER, 0)) - now = __import__('time').time() + now = time.time() db.conn.execute( "INSERT INTO sequences (data_file_id, sequence_number, data, updated_at) " "VALUES (?, ?, ?, ?)", @@ -216,7 +217,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None: # Sync history tree history_tree = data.get(KEY_HISTORY_TREE) if history_tree and isinstance(history_tree, dict): - now = __import__('time').time() + now = time.time() db.conn.execute( "INSERT INTO history_trees (data_file_id, tree_data, updated_at) " "VALUES (?, ?, ?) " @@ -237,10 +238,10 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None: def generate_templates(current_dir: Path) -> None: """Creates batch template files if folder is empty.""" - first = DEFAULTS.copy() + first = copy.deepcopy(DEFAULTS) first[KEY_SEQUENCE_NUMBER] = 1 save_json(current_dir / "batch_prompt_i2v.json", {KEY_BATCH_DATA: [first]}) - first2 = DEFAULTS.copy() + first2 = copy.deepcopy(DEFAULTS) first2[KEY_SEQUENCE_NUMBER] = 1 save_json(current_dir / "batch_prompt_vace_extend.json", {KEY_BATCH_DATA: [first2]}) diff --git a/web/project_dynamic.js b/web/project_dynamic.js index 0100382..ec42718 100644 --- a/web/project_dynamic.js +++ b/web/project_dynamic.js @@ -251,8 +251,8 @@ app.registerExtension({ } else if (this.outputs.length > 1) { // Widget values empty but serialized dynamic outputs exist — sync widgets const dynamicOutputs = this.outputs.slice(1); - if (okWidget) okWidget.value = dynamicOutputs.map(o => o.name).join(","); - if (otWidget) otWidget.value = dynamicOutputs.map(o => o.type).join(","); + if (okWidget) okWidget.value = JSON.stringify(dynamicOutputs.map(o => o.name)); + if (otWidget) otWidget.value = JSON.stringify(dynamicOutputs.map(o => o.type)); } this.setSize(this.computeSize());