history_tree.py:
- Cycle protection in generate_graph() parent walk
- KeyError → .get() for malformed node data in commit() and generate_graph()
- UUID collision check with for/else raise in commit() and _migrate_legacy()
- RuntimeError → ValueError for consistent exception handling
tab_timeline_ng.py:
- Re-parent children walks to surviving ancestor for batch deletes
- Branch tip deletion re-points to parent instead of removing branch
- Cycle protection in _walk_branch_nodes and _find_branch_for_node
- Full data.clear() restore instead of merge in _restore_node
- Safe .get('data', {}) in restore and preview
- Reset stale branch selection after node deletion
- json.dumps for safe JS string escaping in graphviz renderer
tab_batch_ng.py:
- NaN/inf rejection in dict_number with math.isfinite()
- _safe_int used in recalc_vace, update_mode_label, frame_to_skip
- Uncaught ValueError from htree.commit() caught with user notification
tab_comfy_ng.py:
- asyncio.get_event_loop() → get_running_loop()
utils.py:
- Atomic writes for save_config and save_snippets
- save_config extra_data can't override explicit last_dir/favorites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sync_to_db: use ON CONFLICT for duplicate sequence numbers
- history_tree: html.escape() for Graphviz DOT labels
- tab_timeline_ng: cap history_tree_backup to 10 entries
- tab_batch_ng: add _safe_int() helper for VACE settings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each branch gets a unique subtle tint (grey, blue, purple, coral,
teal, sand) so sub-branches are visually distinguishable. HEAD
(yellow) and branch tip (green) colors still take priority.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
splines=ortho triggers a trapezoid-table overflow assertion in
graphviz's dot layout engine on complex graphs. polyline gives
similar angled edges without the crash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduces font sizes, padding, spacing, and note truncation length
specifically for vertical (TB) mode to improve usability.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Combine stable and WIP timeline tabs into one with all features:
view switcher, restore/rename/delete, and data preview panel.
Add adaptive graph spacing based on node count, show full dates
and branch names on node labels, increase label truncation to 25
chars, and drop streamlit-agraph dependency.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- save_json() now writes to a temp file then uses os.replace() for atomic writes
- Replace hardcoded "batch_data", "history_tree", "prompt_history", "sequence_number"
strings with constants (KEY_BATCH_DATA, etc.) across all modules
- Add 29 unit tests for history_tree, utils, and json_loader
- Add type hints to public functions in utils.py, json_loader.py, history_tree.py
- Remove ALLOWED_BASE_DIR restriction that blocked navigating outside app CWD
- Fix path text input not updating on navigation by using session state key
- Add unpin button (❌) for removing pinned folders
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace bare except clauses with specific exceptions (JSONDecodeError, IOError, ValueError, TypeError)
- Add path traversal protection restricting navigation to ALLOWED_BASE_DIR
- Sanitize iframe URLs with scheme validation and html.escape to prevent XSS
- Extract duplicate to_float/to_int to module-level helpers in json_loader.py
- Replace silent modulo wrapping with clamped bounds checking via get_batch_item()
- Remove hardcoded IP 192.168.1.51:5800, default to empty string
- Add try/except around fragile batch history string parsing
- Add JSON schema validation (dict type check) in read_json_data()
- Add Python logging framework, replace print() calls
- Consolidate session state initialization into loop with defaults dict
- Guard streamlit_agraph import with try/except ImportError
- Add backup snapshot before history node deletion
- Add cycle detection in HistoryTree.commit()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>