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>
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>