Commit Graph
100 Commits
Author SHA1 Message Date
EthanfelandClaude Opus 4.6 b36200faaa Detect stale project references and offer clear/recreate options
When current_project in config references a project not found in the
database, show a warning with buttons to either clear the reference
or recreate the project instead of displaying a misleading label.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 23:36:28 +01:00
EthanfelandClaude Opus 4.6 5aac1677f7 Fix page reset on save: async I/O and avoid needless deepcopy
Root cause: save_json + sync_to_db blocked the event loop while
serializing the growing history tree (all snapshots) as JSON,
causing NiceGUI websocket timeout and client reconnect.

- Skip history tree in snapshot deepcopy (copied only to discard)
- Move save_json/sync_to_db to asyncio.to_thread in all callbacks
- Make save_and_snap, commit, sort_by_number, shift_fts,
  _add_sequence, apply_mass_update async

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 23:17:53 +01:00
EthanfelandClaude Opus 4.6 f3ad3e01bc Fix page reset on save: disable reload, handle orphaned timers
- Set reload=False to prevent watchfiles from triggering full page
  reloads when JSON/DB files are written during save
- Timeline graph poll timer (200ms) now deactivates itself when its
  parent slot is deleted during UI refresh
- Comfy monitor poll timer catches RuntimeError from deleted parent
  slots instead of crashing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 22:50:11 +01:00
EthanfelandClaude Opus 4.6 efd0a31426 Restrict reload watcher to .py files to prevent page reload on save
reload=True was watching all file changes, causing full page reloads
when save_json/save_config wrote .json and .json.tmp files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 22:30:57 +01:00
EthanfelandClaude Opus 4.6 b042fe4368 Fix 25+ bugs across rounds 4-8 of comprehensive code review
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>
2026-03-02 10:23:14 +01:00
EthanfelandClaude Opus 4.6 04b9ed0e27 Fix 4 bugs: SQL conflict handling, HTML escaping, backup cap, safe int cast
- 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>
2026-03-01 21:03:13 +01:00
EthanfelandClaude Opus 4.6 1b8d13f7c4 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 <noreply@anthropic.com>
2026-03-01 20:41:00 +01:00
EthanfelandClaude Opus 4.6 497e6b06fb Fix 7 bugs: async proxies, mode default, JS key serialization, validation
- Use asyncio.to_thread for proxy endpoints to avoid blocking event loop
- Add mode to DEFAULTS so it doesn't silently insert 0
- Use JSON serialization for keys in project_dynamic.js (with comma fallback)
- Validate path exists in change_path, friendly error on duplicate rename
- Remove unused exp param from rename closure
- Use deepcopy for DEFAULTS consistently

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:29:24 +01:00
EthanfelandClaude Opus 4.6 993fc86070 Auto-detect change notes when saving without a manual note
Compares current batch data against the last snapshot to generate
descriptive notes like "Added seq 3; Changed: prompt, seed".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:24:07 +01:00
EthanfelandClaude Opus 4.6 c9bcc735f4 Change refresh input from INT to combo dropdown (off/on)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:29:30 +01:00
EthanfelandClaude Opus 4.6 dc8f44f02b Add refresh input to ProjectLoaderDynamic to force re-fetch
Changing the refresh value triggers the node to re-fetch keys from
the API, picking up any new or modified fields in the data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:27:51 +01:00
EthanfelandClaude Opus 4.6 2a6b4f5245 Add mode integer field above sequence number in batch cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 01:07:09 +01:00
EthanfelandClaude Opus 4.6 60d1162700 Fix async callbacks: make rename/change_path directly async
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 00:42:00 +01:00
EthanfelandClaude Opus 4.6 204fc4ea85 Add rename for sequences and projects, per-sub color cycling, project path editing
- 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>
2026-03-01 00:40:35 +01:00
Ethanfel 033b3415c2 Merge branch 'feature/sqlite-project-db' 2026-03-01 00:32:07 +01:00
EthanfelandClaude Opus 4.6 2ccc3821d6 Add visual distinction for subsequence cards with teal border
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>
2026-03-01 00:31:00 +01:00
EthanfelandClaude Opus 4.6 187b85b054 Clean up: remove unnecessary info.outputs logic, set label on reused slots
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>
2026-03-01 00:12:30 +01:00
EthanfelandClaude Opus 4.6 a0d8cb8bbf Fix: set output label alongside name for LiteGraph rendering
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>
2026-03-01 00:10:50 +01:00
EthanfelandClaude Opus 4.6 d55b3198e8 Fix output names not surviving page refresh in ProjectLoaderDynamic
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>
2026-02-28 23:43:34 +01:00
EthanfelandClaude Opus 4.6 bf2fca53e0 Remove JSONLoaderDynamic, handled by ComfyUI-JSON-Dynamic extension
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>
2026-02-28 23:18:39 +01:00
EthanfelandClaude Opus 4.6 5b71d1b276 Fix output name persistence: use comma-separated like reference impl
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>
2026-02-28 23:11:04 +01:00
EthanfelandClaude Opus 4.6 027ef8e78a Fix ProjectLoaderDynamic output names lost on page reload
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>
2026-02-28 23:04:14 +01:00
EthanfelandClaude Opus 4.6 86693f608a Remove 9 redundant JSON loader nodes, keep only JSONLoaderDynamic
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>
2026-02-28 22:36:33 +01:00
Ethanfel 615755ba44 Revert "Remove 9 redundant JSON loader nodes, keep only JSONLoaderDynamic"
This reverts commit 4b09491242.
2026-02-28 22:36:25 +01:00
EthanfelandClaude Opus 4.6 4b09491242 Remove 9 redundant JSON loader nodes, keep only JSONLoaderDynamic
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>
2026-02-28 22:33:51 +01:00
EthanfelandClaude Opus 4.6 4b5fff5c6e Improve ProjectLoaderDynamic UX: single node, error feedback, auto-refresh
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>
2026-02-28 22:16:08 +01:00
EthanfelandClaude Opus 4.6 d07a308865 Harden ROLLBACK against I/O errors in transactions
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>
2026-02-28 21:44:12 +01:00
EthanfelandClaude Opus 4.6 c4d107206f Fix 4 bugs from third code review
- 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>
2026-02-28 21:38:37 +01:00
EthanfelandClaude Opus 4.6 b499eb4dfd Fix 8 bugs from second code review
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>
2026-02-28 21:32:35 +01:00
EthanfelandClaude Opus 4.6 ba8f104bc1 Fix 6 bugs found during code review
- 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>
2026-02-28 21:25:31 +01:00
EthanfelandClaude Opus 4.6 6b7e9ea682 Port dynamic node improvements from ComfyUI-JSON-Dynamic
- 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>
2026-02-28 21:15:56 +01:00
EthanfelandClaude Opus 4.6 c15bec98ce Add SQLite project database + ComfyUI connector nodes
- 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>
2026-02-28 21:12:05 +01:00
EthanfelandClaude Opus 4.6 0d8e84ea36 Add .gitignore with worktrees, pycache, pytest_cache
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 20:59:39 +01:00
EthanfelandClaude Opus 4.6 e2f30b0332 Color graph nodes by branch for visual distinction
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>
2026-02-28 20:22:45 +01:00
EthanfelandClaude Opus 4.6 24f9b7d955 Fix graph interactivity: use querySelector instead of NiceGUI DOM ID
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>
2026-02-28 00:27:00 +01:00
EthanfelandClaude Opus 4.6 d56f6d8170 Fix graphviz crash: use polyline splines instead of ortho
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>
2026-02-28 00:21:43 +01:00
EthanfelandClaude Opus 4.6 f2980a9f94 Fix graph: NiceGUI blocks script tags in ui.html()
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>
2026-02-28 00:20:32 +01:00
EthanfelandClaude Opus 4.6 4e3ff63f6a Fix graph interactivity: embed JS/CSS inline in HTML
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>
2026-02-28 00:19:26 +01:00
EthanfelandClaude Opus 4.6 6e01cab5cd Fix graph interactivity: use NiceGUI element ref and requestAnimationFrame
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>
2026-02-28 00:15:56 +01:00
EthanfelandClaude Opus 4.6 16ed81f0db Fix tiny graph: keep SVG natural size, scroll on overflow
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>
2026-02-28 00:14:25 +01:00
EthanfelandClaude Opus 4.6 d98cee8015 Fix timeline graph height: remove invalid SVG height="auto"
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>
2026-02-28 00:11:03 +01:00
EthanfelandClaude Opus 4.6 2ebf3a4fcd Interactive timeline graph: click nodes to select in node manager
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>
2026-02-28 00:06:13 +01:00
EthanfelandClaude Opus 4.6 a4cb979131 Remove stale Streamlit references from comments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:18:37 +01:00
EthanfelandClaude Opus 4.6 9a3f7b7b94 Remove old Streamlit UI files superseded by NiceGUI migration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:17:38 +01:00
Ethanfel d8597f201a Merge nicegui-migration: full NiceGUI web UI 2026-02-27 22:16:34 +01:00
EthanfelandClaude Opus 4.6 8911323832 Branch-grouped navigation for timeline node manager
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>
2026-02-27 22:15:56 +01:00
EthanfelandClaude Opus 4.6 af5eafaf4d Right-align path inputs to show filename instead of directory prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 00:02:14 +01:00
EthanfelandClaude Opus 4.6 29750acf58 Match Shift button height to input field
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:28:35 +01:00
EthanfelandClaude Opus 4.6 da789e68ad Two-column VACE layout, inline mode reference button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:26:45 +01:00
EthanfelandClaude Opus 4.6 79755c286b Move VACE Settings to full-width section below splitter columns
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:23:37 +01:00
EthanfelandClaude Opus 4.6 39a1b98924 Fix history snapshot corruption, missing dir crash, stale batch delete
- 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>
2026-02-26 18:11:11 +01:00
EthanfelandClaude Opus 4.6 d3dbd4645a Remove Promote button (legacy single-file editor feature)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:05:19 +01:00
EthanfelandClaude Opus 4.6 d795671763 Display LoRA strength with one decimal place (1.0 not 1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:03:56 +01:00
EthanfelandClaude Opus 4.6 9f141ba42f Fix input sync bugs, improve LoRA UX, and harden edge cases
- 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>
2026-02-26 18:02:24 +01:00
EthanfelandClaude Opus 4.6 7931060d43 Fix number inputs not syncing to dict until blur
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>
2026-02-26 17:49:09 +01:00
EthanfelandClaude Opus 4.6 3264845e68 Add dual-pane batch processor with independent file state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:41:25 +01:00
EthanfelandClaude Opus 4.6 fe2c6445ef Constrain main content area to 1200px max-width
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:29:08 +01:00
EthanfelandClaude Opus 4.6 710a8407d2 Overhaul UI: new color palette, spacing, and visual hierarchy
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>
2026-02-26 17:27:02 +01:00
EthanfelandClaude Opus 4.6 97748ab8ff Fix VACE schedule default mismatch introduced in refactor
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>
2026-02-26 17:01:09 +01:00
EthanfelandClaude Opus 4.6 b0125133f1 Refactor for readability: declare state attrs, extract helpers, deduplicate
- Declare dynamic attributes (_render_main, _load_file, etc.) in AppState
  dataclass instead of monkey-patching at runtime
- Extract max_main_seq_number() and FRAME_TO_SKIP_DEFAULT in batch tab
- Add commit() closure in _render_sequence_card to deduplicate save/notify/refresh
- Add default param to dict_number(), replace hand-rolled CFG/VACE/custom bindings
- Extract _delete_nodes() helper in timeline to deduplicate single/batch delete
- Split 230-line render_timeline refreshable into 4 focused section helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:56:40 +01:00
EthanfelandClaude Opus 4.6 a8c9a0376d Fix number inputs saving whole numbers as floats in JSON
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>
2026-02-26 16:30:20 +01:00
EthanfelandClaude Opus 4.6 9c171627d8 Fix mass update not refreshing UI after applying changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:54:12 +01:00
EthanfelandClaude Opus 4.6 b7a7d8c379 Update README for NiceGUI migration
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>
2026-02-25 14:28:46 +01:00
EthanfelandClaude Opus 4.6 3928f4d225 Fix select options not pushing to browser and remaining shallow copies
- 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>
2026-02-25 14:22:40 +01:00
EthanfelandClaude Opus 4.6 a0d58d8982 Fix multiple bugs found in code review
- 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>
2026-02-25 14:16:28 +01:00
EthanfelandClaude Opus 4.6 b6f31786c6 Style NiceGUI to closely match Streamlit dark theme
Exact Streamlit colors: #0E1117 background, #262730 secondary,
#FF4B4B primary accent, #FAFAFA text, rgba borders. Match input
styling, border-radius, sidebar width, tab indicators, and
separator colors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:09:00 +01:00
EthanfelandClaude Opus 4.6 f48098c646 Use splitter for 2-column sequence layout matching Streamlit
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>
2026-02-25 11:38:34 +01:00
EthanfelandClaude Opus 4.6 3bbbdc827c Fix drawer JavaScript timeout by setting explicit initial value
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:34:41 +01:00
EthanfelandClaude Opus 4.6 79a47e034e Switch to dark theme to match original Streamlit look
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:33:25 +01:00
EthanfelandClaude Opus 4.6 d5fbfe765e Fix UI readability and clipping issues
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>
2026-02-25 11:30:44 +01:00
EthanfelandClaude Opus 4.6 f6d5ebfe34 Migrate web UI from Streamlit to NiceGUI
Replace the Streamlit-based UI (app.py + tab_*.py) with an event-driven
NiceGUI implementation. This eliminates 135 session_state accesses,
35 st.rerun() calls, and the ui_reset_token hack. Key changes:

- Add main.py as NiceGUI entry point with sidebar, tabs, and file navigation
- Add state.py with AppState dataclass replacing st.session_state
- Add tab_batch_ng.py (batch processor with blur-binding, VACE calc)
- Add tab_timeline_ng.py (history tree with graphviz, batch delete)
- Add tab_raw_ng.py (raw JSON editor)
- Add tab_comfy_ng.py (ComfyUI monitor with polling timer)
- Remove Streamlit dependency from utils.py (st.error → logger.error)
- Remove Streamlit mock from tests/test_utils.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:53:47 +01:00
EthanfelandClaude Opus 4.6 bdcc05f388 Fix mode-unaware base_length recovery in VACE calculation
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>
2026-02-24 19:24:06 +01:00
EthanfelandClaude Opus 4.6 31da900502 Add mode-aware VACE frame calculation with 4n+1 snap
- 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>
2026-02-24 19:16:52 +01:00
EthanfelandClaude Opus 4.6 f8f71b002d Change CFG default to 1.5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:12:48 +01:00
EthanfelandClaude Opus 4.6 bc75e7f341 Add CFG input to batch processor UI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:10:37 +01:00
EthanfelandClaude Opus 4.6 6a3b72c035 Remove dead code: unused imports, session state keys, blank lines
- 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>
2026-02-23 17:40:41 +01:00
EthanfelandClaude Opus 4.6 387d4d874c Remove Single Editor tab (dead code)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 17:14:09 +01:00
EthanfelandClaude Opus 4.6 7261f2c689 Change input_a_frames and input_b_frames default to 16
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 17:12:24 +01:00
EthanfelandClaude Opus 4.6 2263c3f598 Store vace_length as total (base + input_a + input_b) in JSON
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>
2026-02-23 17:11:11 +01:00
EthanfelandClaude Opus 4.6 7252fa3855 Fix load_dynamic missing output_types parameter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 17:03:03 +01:00
EthanfelandClaude Opus 4.6 a747f86daa Persist output types across save/load via hidden output_types widget
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>
2026-02-23 16:16:50 +01:00
EthanfelandClaude Opus 4.6 f5e242950d Auto-detect output types (INT, FLOAT, STRING) on dynamic node refresh
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>
2026-02-23 16:11:30 +01:00
EthanfelandClaude Opus 4.6 dfab5e12ab Rewrite README with SVG diagrams and Dynamic node documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:08:33 +01:00
EthanfelandClaude Opus 4.6 a08f2676f5 Fix tall node on creation by resizing after removing default outputs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:01:01 +01:00
EthanfelandClaude Opus 4.6 3255fe76dc Preserve connections on Refresh when keys are added or reordered
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>
2026-02-23 16:00:16 +01:00
EthanfelandClaude Opus 4.6 0d44944192 Fix dynamic node outputs using LiteGraph removeOutput/addOutput API
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>
2026-02-23 15:57:06 +01:00
EthanfelandClaude Opus 4.6 8cc244e8be Add case-insensitive path resolution for Current Path input
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>
2026-02-23 15:52:48 +01:00
EthanfelandClaude Opus 4.6 e841e9b76b Add JSONLoaderDynamic node with JS frontend for auto-discovered outputs
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>
2026-02-23 15:47:22 +01:00
EthanfelandClaude Opus 4.6 a4717dfab6 Add vace_length field, move end_frame out of VACE settings, revert type coercion
- 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>
2026-02-23 15:26:13 +01:00
EthanfelandClaude Opus 4.6 3718975d99 Add image preview popover for reference and FLF path fields
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>
2026-02-23 13:49:17 +01:00
EthanfelandClaude Opus 4.6 40ffdcf671 Clean up DEFAULTS: remove unused settings, add end_frame and transition
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>
2026-02-23 13:34:21 +01:00
EthanfelandClaude Opus 4.6 81ecb91835 Fix Clone Next inserting between parent and its sub-segments
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>
2026-02-22 18:34:59 +01:00
EthanfelandClaude Opus 4.6 e196ad27f5 Update templates to batch-only and remove single file option
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>
2026-02-22 17:04:39 +01:00
EthanfelandClaude Opus 4.6 bd628b062e Clear stale file selector when navigating to empty folder
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>
2026-02-22 17:01:50 +01:00
EthanfelandClaude Opus 4.6 1abae0de22 Fix path navigator using on_change with deferred sync
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>
2026-02-22 17:00:37 +01:00
EthanfelandClaude Opus 4.6 64472c7850 Fix nav_path_input write-after-widget error on invalid path
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>
2026-02-22 16:54:52 +01:00
EthanfelandClaude Opus 4.6 907e7efd68 Fix path navigator by replacing on_change with inline check
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>
2026-02-22 16:53:06 +01:00
EthanfelandClaude Opus 4.6 0cfe9c9d4b Fix Current Path input reverting on directory change
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>
2026-02-22 16:37:53 +01:00
EthanfelandClaude Opus 4.6 563dba5a0c Fix frame_to_skip shift by caching saved value in session state
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>
2026-02-15 01:39:21 +01:00
EthanfelandClaude Opus 4.6 b7164ae167 Replace legacy import with source sequence selector
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>
2026-02-15 01:07:29 +01:00