When cloning a parent sequence, the new sequence now inserts after the
parent's last sub-segment instead of directly after the parent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Templates now generate batch_prompt_i2v.json and
batch_prompt_vace_extend.json as batch files. Create New JSON
always creates batch files since single mode is deprecated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removes leftover file_selector and loaded_file state when the current
directory has no JSON files, preventing stale data from persisting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The inline check caused mismatches between typed and resolved paths.
Now uses on_change callback that always sets _sync_nav_path flag,
so the widget is synced to the canonical current_dir path on the
next rerun before the widget renders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use _sync_nav_path flag to defer the revert to the next rerun, before
the widget is instantiated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The on_change callback had timing issues with Streamlit's session state,
causing user input to be discarded. Now checks the widget value inline
after render and triggers rerun on valid directory change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The nav_path_input was force-overwritten on every rerun, causing
Streamlit to discard user edits before the on_change callback could
process them. Now only syncs on first load or external changes
(favorites). Also resets loaded_file on dir change and reverts
widget on invalid paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The old_fts value was read from the data dict which gets mutated
in-place by widget renders, so on button click rerun delta was always 0.
Now the saved value is captured once per ui_reset_token cycle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Source file picker now shows all sequences in the selected file with a
dropdown, replacing the outdated history entry selector. Both "From
Source" and per-sequence "Copy Source" buttons use the selected sequence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sub-segments use parent*1000+index numbering (e.g. 2001 = Sub #2.1) so
ComfyUI nodes can reference them via integer sequence_number without
code changes. Adds clone-as-sub button, visual distinction in expander
labels, sort-by-number button, and fixes auto-numbering/frame_to_skip
shift to work correctly with sub-segments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a "Shift ↓" button next to Frame to Skip in VACE Settings that
applies the change delta to all sequences with a higher sequence_number.
Uses sequence_number field ordering, not array position.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_batch_item now searches for the item whose sequence_number field
matches the requested number, instead of using array position. Falls
back to index-based lookup for data without sequence_number fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a "Select to Delete" toggle that enables batch deletion mode.
When active, clicking graph nodes or checking linear log checkboxes
selects them (highlighted in red), and a "Delete N Nodes" button
performs batch deletion with backup, branch tip cleanup, and HEAD
reassignment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete sequence: add ui_reset_token increment to prevent shifted
sequences from inheriting deleted sequence's widget state
- Promote: update data_cache with single_data so UI reflects the
file format change without requiring a manual refresh
- Mass update & clone: use copy.deepcopy to avoid shared mutable
references between sequences
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Explicitly reassign st.session_state.data_cache after mass update
to ensure Streamlit picks up in-place mutations to the batch data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increment ui_reset_token after mass update save so Streamlit
widgets re-read their values, matching all other save operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use brighter node colors (yellow, green, light blue) and white font
for better visibility on dark backgrounds.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Uses streamlit-agraph for interactive node selection. Clicking a node
restores that version. Falls back to static graphviz if not installed.
Requires: pip install streamlit-agraph
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Vertical graphs now render at natural size instead of stretching to
fill container width, which was making nodes appear giant.
Co-Authored-By: Claude Opus 4.5 <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>
Allows propagating field values from one sequence to multiple/all other
sequences. Includes source selector, field multi-select, target checkboxes
with Select All toggle, preview, and history snapshot on apply.
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>
st.radio was called with an empty list when no JSON files existed,
causing Streamlit to error and only render the sidebar.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These fields were previously gated behind filename checks ("vace"/"i2v"
in filename), hiding them when the filename didn't match. Since DEFAULTS
includes all these keys, always render them.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch creation now seeds one item from DEFAULTS (which includes all
VACE/I2V keys) instead of creating an empty batch_data list.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set nav_path_input before the widget renders and use on_change callbacks
instead of modifying widget state after instantiation.
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>