- Sequences: add rename button with name shown in expansion header
- Subsequences: cycle through 6 distinct border colors by sub-index
- Projects: add rename and change path buttons with DB methods
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The subsegment-card CSS class was not being applied to subsequence
expansion items. Add the class conditionally and include the teal
accent CSS rules with a 6px left border.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The actual fix was setting slot.label alongside slot.name. Reverted
onConfigure to read from widget values (which work correctly) and
ensured label is set on both new and reused output slots.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LiteGraph renders slot.label over slot.name — we were updating name
but the display uses label.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Read output names from info.outputs (serialized node data) instead of
hidden widget values, which ComfyUI may not persist across reloads.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The separate ComfyUI-JSON-Dynamic extension provides the same node.
Removes json_loader.py, web/json_dynamic.js, and their tests. Only
ProjectLoaderDynamic remains in this extension.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
JSON.stringify format for hidden widget values didn't survive ComfyUI's
serialization round-trip. Switch to comma-separated strings matching
the proven ComfyUI-JSON-Dynamic implementation. Remove properties-based
approach in favor of the simpler, working pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hidden widget values for output_keys/output_types were not reliably
restored by ComfyUI on workflow reload. Store keys/types in
node.properties (always persisted by LiteGraph) as primary storage,
with hidden widgets as fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
JSONLoaderDynamic auto-discovers keys at runtime, making the hardcoded
Standard, Batch, and Custom nodes unnecessary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
JSONLoaderDynamic auto-discovers keys at runtime, making the hardcoded
Standard, Batch, and Custom nodes unnecessary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove 3 redundant hardcoded nodes (Standard/VACE/LoRA), keeping only the
Dynamic node. Add total_sequences INT output (slot 0) for loop counting.
Add structured error handling: _fetch_json returns typed error dicts,
load_dynamic raises RuntimeError with descriptive messages, JS shows
red border/title on errors. Add 500ms debounced auto-refresh on widget
changes. Add 404s for missing project/file in API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If the original error (e.g., disk full) also prevents ROLLBACK from
executing, catch and suppress the ROLLBACK failure so the original
exception propagates cleanly and the connection isn't left in a
permanently broken state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix delete_proj not persisting cleared current_project to config:
page reload after deleting active project restored deleted name,
silently breaking all DB sync
- Fix sync_to_db crash on non-dict batch_data items: add isinstance
guard matching import_json_file
- Fix output_types ignored in load_dynamic: parse declared types and
use to_int()/to_float() to coerce values, so downstream ComfyUI
nodes receive correct types even when API returns strings
- Fix backward-compat comma-split for types not trimming whitespace:
legacy workflows with "STRING, INT" got types " INT" breaking
ComfyUI connection type-matching
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HIGH:
- Fix JS TypeError on empty API response: validate keys/types are arrays
before using them; add HTTP status check (resp.ok)
- Fix BEGIN IMMEDIATE conflict: set isolation_level=None (autocommit) on
SQLite connection so explicit transactions work without implicit ones
MEDIUM:
- Fix import_json_file non-atomic: wrap entire operation in BEGIN/COMMIT
with ROLLBACK on error — no more partial imports
- Fix crash on non-dict batch_data items: skip non-dict elements
- Fix comma-in-key corruption: store keys/types as JSON arrays in hidden
widgets instead of comma-delimited strings (backward-compat fallback)
- Fix blocking I/O in API routes: change async def to def so FastAPI
auto-threads the synchronous SQLite calls
LOW:
- Fix missing ?. on app.graph.setDirtyCanvas in refreshDynamicOutputs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix NameError: pass state to _render_vace_settings (tab_batch_ng.py)
- Fix non-atomic sync_to_db: use BEGIN IMMEDIATE transaction with rollback
- Fix create_secondary() missing db/current_project/db_enabled fields
- Fix URL encoding: percent-encode project/file names in API URLs
- Fix import_json_file crash on re-import: upsert instead of insert
- Fix dual DB instances: share single ProjectDB between UI and API routes
- Also fixes top_level metadata never being updated on existing data_files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Deferred output cleanup (_configured flag + queueMicrotask) to prevent
breaking links when other nodes (e.g. Kijai Set/Get) resolve outputs
during graph loading
- file_not_found error handling in refresh to keep existing outputs intact
- Fallback widget sync in onConfigure when widget values are empty but
serialized outputs exist
Applied to both json_dynamic.js and project_dynamic.js.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- db.py: ProjectDB class with SQLite schema (projects, data_files,
sequences, history_trees), WAL mode, CRUD, import, and query helpers
- api_routes.py: REST API endpoints on NiceGUI/FastAPI for ComfyUI
to query project data over the network
- project_loader.py: ComfyUI nodes (ProjectLoaderDynamic, Standard,
VACE, LoRA) that fetch data from NiceGUI REST API via HTTP
- web/project_dynamic.js: Frontend JS for dynamic project loader node
- tab_projects_ng.py: Projects management tab in NiceGUI UI
- state.py: Added db, current_project, db_enabled fields
- main.py: DB init, API route registration, projects tab
- utils.py: sync_to_db() dual-write helper
- tab_batch_ng.py, tab_raw_ng.py, tab_timeline_ng.py: dual-write
sync calls after save_json when project DB is enabled
- __init__.py: Merged project node class mappings
- tests/test_db.py: 30 tests for database layer
- tests/test_project_loader.py: 17 tests for ComfyUI connector nodes
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>
The c{id} DOM ID pattern was wrong. Use document.querySelector with
the timeline-graph class instead, with retries until g.node elements
are present in the DOM.
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>
Move JS back to ui.run_javascript() with retry-based DOM lookup
using NiceGUI's element ID (c{id}). CSS stays inline via style tag.
Retries up to 10 times at 50ms intervals to handle Vue async render.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous approach used ui.run_javascript with getElement() which
failed due to Vue rendering timing. Now embeds the script and style
directly inside the HTML content so there are no DOM lookup or
timing issues — the script runs inline when parsed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The click handlers weren't attaching because getElementById couldn't
find the container — Python's id() generated IDs that didn't survive
NiceGUI's DOM rendering. Now uses getElement() with the NiceGUI
element ID and defers JS via requestAnimationFrame to ensure the
DOM is ready.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stop replacing the SVG's width/height attributes — this was shrinking
the graph to fit the container. Instead keep graphviz's native pt
dimensions and let the scroll container handle overflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop the fixed height attribute entirely instead of setting it to
"auto", which SVGs don't support. The viewBox attribute handles
proportional scaling when only width="100%" is set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add click-to-select functionality to the graphviz SVG timeline graph.
Clicking a node highlights it with an amber border, auto-switches the
branch selector, and updates the node manager panel. The SVG is now
responsive (100% width, scroll container) instead of fixed-size.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace flat dropdown with branch selector showing node counts,
scrollable node list with HEAD/tip badges, and inline actions panel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Deep-copy node data on restore to prevent edits from mutating
stored history snapshots
- Guard glob calls against non-existent current_dir
- Read current selection at delete time instead of using stale
render-time capture
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sync dict_input/dict_textarea/LoRA inputs on update:model-value
(not just blur) to prevent silent data loss on quick saves
- Split LoRA into name + strength fields, default strength to 1.0
- Stack LoRAs one per line instead of 3-card row
- Collapse "Add New Sequence from Source File" into expansion
- Add file selector to Pane A in dual-pane mode
- Clear secondary pane state on directory change
- Fix file radio resetting to first file on refresh
- Handle bare-list JSON files and inf/nan edge cases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dict_number() only wrote to seq[key] on blur, so changing a value
(e.g. via spinner arrows) and immediately clicking Save could race
the save ahead of the blur on the server. Now also syncs on
update:model-value so the dict is always current.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace red accent with amber, add Inter font, introduce 4-level depth
palette via CSS variables, expand padding/gaps, wrap sidebar and content
sections in cards, add section/subsection header typography classes, and
style scrollbars for dark theme. Pure visual changes — no functional or
data-flow modifications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dict_number() defaulted to 0 while mode_label used default of 1,
causing visual inconsistency when 'vace schedule' key is missing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NiceGUI's ui.number returns float values, so seeds, steps, dimensions
etc. were being stored as floats (e.g. 42.0) instead of integers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update badge, installation instructions, port references, and file
structure to reflect the migration from Streamlit to NiceGUI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use set_options() instead of direct .options assignment (3 locations)
so dropdown changes actually reach the browser
- Wrap res.json() in try/except for non-JSON server responses
- Deep copy in create_batch and promote to match rest of codebase
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- save_config calls now pass full config to preserve comfy settings
- Mass update section moved inside refreshable to stay in sync
- Deep copy source data to prevent shared mutable references
- Clipboard copy uses json.dumps instead of repr() for safe JS
- Comfy monitor uses async IO (run_in_executor) to avoid blocking
- Auto-timeout now updates checkbox and refreshes live view UI
- Image URLs properly URL-encoded with urllib.parse.urlencode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces row/col grid with a resizable splitter at 66/34 ratio,
matching the original Streamlit st.columns([2, 1]) layout. Removes
extra card wrapper from sequences to maximize content width.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add page/sidebar background contrast, wrap action button rows,
ensure dark text in inputs, and improve timeline card highlight colors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The recovery formula now matches the storage formula per mode:
End Extend subtracts only A, Pre Extend subtracts only B.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Formula changes per VACE schedule: End Extend uses base+A,
Pre Extend uses base+B, others use base+A+B
- Snap total to 4n+1 to match VACE sampler
- Show mode name label next to schedule number
- Add mode reference popover with all formulas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
UI shows the base value (vace_length - input_a - input_b) for editing,
saves the computed total back to the JSON.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Types were lost on workflow reload because only key names were stored.
Now both keys and types are saved in hidden widgets and restored by
onConfigure, so colored connector dots persist without needing Refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API route now returns types alongside keys. JS sets output slot type
accordingly, giving correct colored dots and type-safe connections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diff new keys against existing outputs instead of remove-all/add-all.
Reuses slot objects for matching key names so links stay connected.
Only disconnects links on keys that were actually removed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Direct array manipulation bypassed LiteGraph's internal slot tracking,
causing output names to show as defaults instead of JSON key names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Walks each path component and matches against actual directory entries
when an exact match fails on Linux. Widget resyncs to the corrected
canonical path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dynamic node reads JSON keys and exposes them as outputs automatically
via 32 AnyType slots managed by a JS extension (show/hide/rename).
Includes /json_manager/get_keys API route, bool-safe type handling,
and workflow save/reload support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Moved end_frame to main settings area (i2v field, not VACE)
- Added vace_length (default 49) with computed output display
showing vace_length + input_a + input_b
- Reverted custom param type coercion (ComfyUI handles conversion)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows a clickable eye button next to each image path input that
opens a popover with the image preview when the path is valid.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed steps, cfg, sampler_name, scheduler, denoise, model_name, and
vae_name that were showing as custom parameters. Added end_frame (0)
and transition (1-2) with VACE Settings widgets. Set default
general_negative prompt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>