Commit Graph

56 Commits

Author SHA1 Message Date
Ethanfel 2619d2c7e2 feat: move resolutions into collapsible expansion above VACE Settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 11:33:36 +02:00
Ethanfel 03dcb1c13a feat: add tooltip to Logic Index explaining binary flag mapping
Bit 0 = start frame, bit 1 = middle frame, bit 2 = end frame.
Tooltip shows the full 0-7 truth table on hover.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:41:03 +02:00
Ethanfel 932295ed27 fix: replace direction:rtl with text-align:right on path inputs
direction:rtl caused path characters to render in wrong order.
text-align:right right-aligns the text (shows end of path) without
breaking the character display order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:21:55 +02:00
Ethanfel a5da8b26f4 feat: add 'logic index' field mirroring end_frame
Temporary field to ease node migration. Initializes to end_frame's
value and stays in sync whenever end_frame changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:14:25 +02:00
Ethanfel 5bc2838b21 feat: rename 'reference image path' to 'start frame path'
Updates DEFAULTS, standard_keys, UI label, timeline known_keys.
Migration auto-renames old key on load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:11:43 +02:00
Ethanfel a7a4794adb feat: rename 'flf image path' to 'end frame path'
Updates DEFAULTS, standard_keys, UI label, timeline known_keys.
Migration auto-renames old key on load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:10:09 +02:00
Ethanfel d33ce4da38 feat: rename 'reference path' to 'middle frame path'
Updates DEFAULTS, standard_keys, UI label, and timeline known_keys.
Adds _migrate_key_renames() called on load_json to auto-migrate
existing JSON files with the old key name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:09:08 +02:00
Ethanfel 20be3204b3 fix: await all commit() calls in sequence card event handlers
commit() is async but was called without await from sync handlers
(clone_next, clone_end, clone_sub, delete, copy_source, del_custom,
add_param, _sync_entry, _randomize) — causing saves and UI refreshes
to silently never run. Made all handlers async and added await.

Also fixed for i,entry loop shadowing the card's i parameter,
which was causing _render_vace_settings to receive the wrong index.
Removed unawaited render-time commit() in resolutions init block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 12:59:25 +02:00
Ethanfel 55900e7c43 feat: 8 resolution slots with per-slot seed + node outputs seed
- Resolution entries expanded from 6 to 8 fixed slots
- Each slot now stores [w, h, seed] (migrates old [w, h] entries to [w, h, 0])
- UI adds seed number input + casino randomize button per row
- ProjectResolution node now outputs (width, height, seed) instead of (width, height)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 11:27:11 +02:00
Ethanfel ca26da303c fix: persist resolutions on init and on every value change
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 01:22:08 +02:00
Ethanfel f97f8a0616 feat: resolutions — 6 fixed slots, always visible
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 01:11:07 +02:00
Ethanfel 4b51d3c95d feat: simplify resolutions UI — fixed key, index-labeled rows, single add button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 01:07:59 +02:00
Ethanfel 31406cb092 feat: resolution series editor in sequence card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 00:42:46 +02:00
Ethanfel 1ec3abf17a feat: replace Git-DAG timeline with flat snapshot browser
Replace HistoryTree (DAG with branches, Graphviz rendering) with a flat
chronological SnapshotTimeline. New UI features: split-view layout,
snapshot compare/diff, cherry-pick restore of individual sequences or
fields, auto-snapshots with debounce, and pin/filter support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 18:14:22 +01:00
Ethanfel 672b28e27f feat: split lora name and strength into separate keys
Reverses the previous merge migration. Lora data is now stored as
separate keys: 'lora 1 high' (STRING name) and 'lora 1 high strength'
(FLOAT). This allows ProjectKey relay nodes to output name and strength
as properly typed separate values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 16:18:33 +01:00
Ethanfel d3955c489b Write slim history tree to JSON when DB is enabled
The JSON file was storing full snapshot data in every history node,
causing files to grow to multi-GB. Now when DB is enabled:
- sync_to_db receives full tree (extracts snapshots to DB)
- save_json receives slim tree (no snapshot data)
- JSON file stays small, DB is authoritative for snapshots

When DB is disabled, JSON still stores full snapshots (only store).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 10:00:51 +01:00
Ethanfel a1a85ecc4d Fix bugs in snapshot stripping: auto-note, mass update, DB-off, fallback
- _auto_change_note now loads previous snapshot from DB instead of
  reading stripped in-memory node (was producing wrong commit messages)
- _render_mass_update now strips snapshots after save (was leaking RAM)
- Only strip snapshots when DB is enabled (preview/restore still works
  without DB via in-memory or disk fallback)
- _render_data_preview adds disk fallback matching _restore_node

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:52:46 +01:00
Ethanfel eac4e4f08b Fix RAM leak: strip history snapshots from memory, load on demand
History tree nodes stored full data snapshots in memory (5-50MB each),
accumulating with every save. Now:

- New `history_snapshots` DB table stores node data separately
- `save_history_tree` and `sync_to_db` extract snapshots before saving
- In-memory tree nodes only hold metadata (id, parent, note, timestamp)
- Restore and preview load snapshots from DB on demand
- `save_and_snap` uses json roundtrip instead of deepcopy (1 copy not 2)
- `_src_cache` moved to AppState, cleared on file switch
- `strip_snapshots()` method on HistoryTree for explicit cleanup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:48:47 +01:00
Ethanfel 79e1426036 Fix dict mutation race: snapshot data before background save/sync
json.dump in a background thread would crash with "dictionary changed
size during iteration" when the UI mutated data concurrently. Now all
save_json and sync_to_db calls receive a json.loads(json.dumps(data))
snapshot, isolating the serialization from live UI mutations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:36:46 +01:00
Ethanfel fac5013359 Load data from DB instead of parsing huge JSON files
- load_file and on_select try db.load_full_data first (~0.01s),
  fall back to load_json only when DB has no data
- Fix unawaited coroutine warning for auto-load (asyncio.ensure_future)
- Fix radio on_change to properly await async load_file
- Reuse current data cache when source file matches current file,
  eliminating a redundant 1.3s JSON parse during render

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 00:17:20 +01:00
Ethanfel a9197efacd Merge lora name+strength into single key to stay under 32 output limit
Combines separate lora name and strength keys into "name:strength" format,
removing 6 strength keys to free output slots for mode. Migration handles
legacy <lora:> wrapper, separate strength keys, and already-merged formats.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 23:58:32 +01:00
Ethanfel ecb5cdc13f Add comprehensive timing logs across all critical paths
Logs with perf_counter timing on: file load/save, DB sync, all
render functions, save & snap (with deepcopy/save/sync breakdown),
graphviz cache hit/miss, node restore, and API endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 23:53:30 +01:00
Ethanfel 589c84fd95 Fix remaining blocking I/O calls and N+1 project query
- tab_batch_ng.py: async create_batch with to_thread save/sync
- tab_raw_ng.py: async do_save with to_thread, replace deepcopy
  with dict comprehension for display data
- main.py: async create_new with to_thread save
- tab_projects_ng.py: replace per-project count_data_files with
  single list_projects_with_file_counts JOIN query
- db.py: add list_projects_with_file_counts method

Zero blocking I/O calls remain in UI callbacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 23:40:32 +01:00
Ethanfel be9c95ffbd Split LoRA keys into separate name and strength fields
Store lora name and strength as independent JSON keys instead of the
combined <lora:name:strength> format. Legacy format is auto-migrated
on load. Timeline preview updated to show all 6 LoRA slots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:21:48 +01:00
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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
Ethanfel 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