Commit Graph
1039 Commits
Author SHA1 Message Date
digital-garbageandClaude Opus 4.8 4f562cf0ff Movie Editor: timeline clip shows what it was generated with, not live plan text
Completes untying plan edits from the rendered result on the timeline. The clip
is the RESULT, so it now labels itself from the frozen render prompt
(renderPromptForScene) instead of the live scene text — editing a scene's prompt
in the plan no longer mutates the already-generated clip's identity here (it just
shows 'edited' until you regenerate). Falls back to live plan text only when the
scene isn't generated yet. Video clips unchanged.

With the earlier immutable-length fix, a plan edit (prompt/length) now leaves an
existing generated video's content, duration, AND label on the timeline alone;
only regeneration updates it. Editorial props (effects/trim/audio) stay live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:53:41 +03:00
digital-garbageandClaude Opus 4.8 af07b7bb25 Movie Editor: add Plan strip (scene recipes above the result timeline)
New plan_strip.js band (#plan-zone) above the timeline: compact recipe cards
(anchor thumb + prompt + status: not generated / in sync / edited), click to
select, chevron opens a full-width inline editor (prompt, source, anchor picker,
Generate this scene, Remove, Advanced -> inspector). Collapsible. Placed above
#split-bottom so the timeline splitter / --timeline-h math is untouched; it's a
bounded, additive band (dock flex:1 absorbs it).

Plan = 'what will generate'; the timeline below is the immutable rendered result.
Edits are quiet/debounced and the band won't rebuild while a field is focused
(no input disruption). Selection syncs with the timeline.

Phase 1 / additive: the inspector's per-scene panel is intentionally kept as the
full editor + fallback (the plan can be Hidden) — retiring/merging it and removing
ghosts are the next steps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:00:46 +03:00
digital-garbageandClaude Opus 4.8 4fb7e66759 Movie Editor: remove 'Convert to video/scene' buttons from the UI
The convert verbs don't fit the revamped plan/result direction and need a
rethink (see deferred notes). Remove the toolbar buttons now and the two
inspector hints that pointed at them, so nothing references a control that's
gone. The convertToVideo/convertToScene store functions are left intact and
callable — only the UI entry points are removed; the rework comes later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 13:48:51 +03:00
digital-garbageandClaude Opus 4.8 0c7fe14b79 Movie Editor: generated renders are immutable, full-length (no truncation by plan changes)
A render never recorded the length it was actually made at — sceneDurationSec
recomputed from the LIVE plan every time, so changing project/scene length
retroactively truncated already-generated videos and made their tail unreachable
in the preview, player, and final export alike.

Now each render freezes its real length at generation time (durationSec). A
generated scene in project (default) frames mode plays/lays-out/exports at that
frozen length; the project/scene default only drives the NEXT generation.
Explicit per-scene length (timeline/custom), slip-trim, and splits still win
(a split clears the frozen length so both halves revert to plan layout).

- store: renderDurationSec + _planDurationSec; sceneDurationSec is render-aware;
  _recordSegment freezes durationSec on scene renders and ghosts; trim/resize
  measure from the shown length; segmentDurationSec delegates ghost length to
  _ghostDurationSec (de-dupes the inline calc).
- inspector: scene duration hint shows 'Rendered ~Xs - plan Ys - Regenerate to
  apply' when a render is frozen at an older plan length.

Centralized in sceneDurationSec so timeline, player, audio offsets/durations and
the final stitch all agree. No-op in the common case (plan == render). Renders
made BEFORE this change have no frozen length and stay plan-driven until
regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 13:33:57 +03:00
digital-garbageandClaude Opus 4.8 6d25c8c1c5 Movie Editor: real min/max/step on exposed controls + stop autosave from eating input
Exposed node controls (e.g. a LoRA strength in project settings) now carry the
node's actual min/max/step/default from object_info instead of step=1/no bounds:
- capture numeric widget meta at expose time and on link creation (models.js)
- apply it in the Models node editor and the inspector's Exposed controls
- backfill meta onto pre-existing exposures when Models is opened / refreshed

Typing an exposed value no longer gets disrupted: the field now carries a data-k
so the edit guard protects it, and setModelInput/setModelLink update in place +
debounce the server save (was one saveModels POST per keystroke, notifying twice
and rebuilding the field mid-type). Pending model saves flush on blur and before
generate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 12:38:22 +03:00
digital-garbageandClaude Opus 4.8 94ffbb3cda Movie Editor: fix inspector input/label/control UX papercuts
- Rebuild guard now protects only typed/dragged fields (text/number/range),
  not selects/checkboxes — so dependent controls (custom length input, Ken
  Burns knobs, Steer mode 'absolute', Embed/Mid-scene guide) reveal on the
  toggle instead of only after blur (inspector.js + engine_settings.js).
- Tag blend-length and video 'Source in' fields so non-quiet patchScene no
  longer yanks them mid-type.
- Blur % and Zoom amount % slider labels update live while dragging.
- Surface Width/Height in the Project Output section (out of the collapsed
  Advanced fold); leave Max scenes in Advanced.
- Custom scene length reflects its snapped value back into the field on commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 12:28:31 +03:00
digital-garbageandClaude Sonnet 4.6 0d909f4b18 Fix: single-transition 'silent' split leaked the trigger word into conditioning
With exactly one transition trigger in the prompt (one "cut", one "qcut", etc.),
split_scenes produced anchor + 1 scene — so len(canon_scenes) == 1. The
previous guard `> 1` skipped the clean-text encode path, leaving
output_conditioning built from the raw prompt_to_encode which still contained
the trigger word. The CLIP encoder received "cut" and the model generated it
in audio.

Changing to `>= 1` ensures any split with at least one clean scene uses the
properly stripped scene text, even when there's only one scene to encode.
The chain sampler receiving a single conditioning entry is fine — it just
renders a single pass with no chaining.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 20:18:21 +03:00
digital-garbageandClaude Sonnet 4.6 ec61f169fe Remove FunPackNormalizingSampler (redundant)
Normalization is now available in both Hybrid Euler 2S and Distilled Flow
samplers, making the standalone Normalizing sampler redundant. Removes the
node class, its sampler function, two private helpers (_parse_norm_factors,
_apply_av_norm_factors), the Studio/Movie Editor "Normalizing" type option,
and the defaultNormalizing() factory. Stale "same as the Normalizing sampler"
tooltip references in both sampler nodes are updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 19:05:11 +03:00
digital-garbage cb25c14e8d Movie Editor: coerce stale LoRA/combo slot values to live choices
A renamed or removed file (e.g. an old distilled LoRA) left a slot's saved
combo value out of sync with object_info's current choices. The Models menu
select then showed the live first choice (browser default for "no matching
option") while the saved config still held the stale string, which the
builder passed straight through to ComfyUI and failed validation.

Coerce in both places: the builder falls back unknown combo values to the
node's current default/first choice, and the Models menu select persists
what it actually displays instead of leaving the stale value on disk.
2026-06-16 00:23:31 +03:00
digital-garbage cddb928608 Merge branch 'cutting_edge' into dev 2026-06-16 00:15:52 +03:00
digital-garbage 0bf8cbc6b9 Movie Editor: fix timeline not re-rendering after prompt edits
onStore marked the data fingerprint as handled before checking whether
render() actually redrew. render() can decline (focus/drag/scroll), so
post-generation prompt edits left the timeline showing stale text and
the "Generated with" badge. Only commit the fingerprint when the render
succeeds, both in timeline.js's onStore and view_bus.js's subscribeZone.
2026-06-16 00:13:42 +03:00
digital-garbageandClaude Opus 4.8 c2c9be6ce8 Remove the Scene Builder (Studio/Refiner) entirely
Deletes the FunPack Scene Builder node and the Studio Scene Builder feature
— the per-key scene database (scene_db / universal_memory / saved scenes),
the Studio "Scene" tab, scene-phrase authority over classification, and the
scene-database wildcard cleanup. It was redundant: helpful on paper but
effectively unused, undrivable from the Cutting Room, and a risk to shortcuts.

Removed:
- node registration (__init__.py, funpack.py) + web/funpack_scene_builder.js
- templates.py: scene-DB storage layer (load/save/normalize/empty_scene_db,
  SCENE_DB_VERSION/SCENE_STATE_FIELD/SCENE_CATEGORY_SOURCES, scene_store_path),
  all scene_* helpers, resolve_scene_database_wildcards, the FunPackSceneBuilder
  class, and the /funpack/scenes* routes
- conditioning.py: _v2_scene_builder_db / _v2_scene_memory_item /
  _v2_apply_scene_builder_authority / _v2_scene_builder_category_for_entry /
  _v2_sync_scene_builder_memory / _v2_resolve_scene_builder_wildcards (+ wildcard
  helpers + _v2_scene_key), the scene_db threading through classify/learning,
  the scene_category_locked phrase fields + their training branch, the
  state["scene_builder"] preservation, and the Studio scene_builder prompt mode
- web/funpack_studio.js: Scene tab, SB_MODES, scene_builder settings, renderScene,
  fetch/saveScene, the Session Scene-Builder section, and the Refiner banner/notice
- tests/test_scene_builder.py (whole file) + scene-builder tests in
  test_v2_category_alignment.py; the SceneBuilder example workflow + its doc;
  README link; Scene Builder mentions in docs/FunPackStudio.md

Kept intact: shortcuts (apply_prompt_shortcuts / shortcut DB), transitions and
the canonical split_scenes splitter, and FunPackRefinementKeyLoader. Studio now
always uses the connected positive_prompt. No new test failures (the 16/97
remaining in tests/ are pre-existing env/sampler failures); movie_editor 140 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 22:31:31 +03:00
digital-garbageandClaude Opus 4.8 648a7401c6 Movie Editor: don't prune a just-finished ghost on generation end
A scene removed mid-generation becomes a ghost; when that in-flight run
completes, _recordSegment attaches the finished media to the ghost
(pendingGen:false) and deliberately does NOT push its id to
recordedSceneIds. But _pruneGhostsAfterRegen dropped any ghost whose
afterSceneId was in recordedSceneIds — and a ghost anchored after a sibling
scene in the same run matches that, so the freshly-finished ghost was
removed from the timeline the instant generation ended.

Collect the ids of ghosts that received media this pass (completedGhostIds)
and protect them in _pruneGhostsAfterRegen. Genuinely stale ghosts (anchored
after a recorded scene, not updated this run) still get pruned as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:49:00 +03:00
digital-garbageandClaude Opus 4.8 df011b927f Stage 2 Part B: "Wrong appearance" → consistency anchor
Reworks the "Wrong appearance" rating from a (now-removed) prompt-repair
signal into a conditioning-level consistency anchor (option b). "Wrong
appearance" now means "character consistency wasn't preserved" and steers
the next generation back toward the established character.

Capture (_v2_update_appearance_anchor, wired into both the single-run
learning path and the shared per-scene suite _v2_learn_scene_into_state):
  - good rating (like/nailed_it/loved_it/loved) → store the rated run's
    conditioning as the per-key "blessed appearance" (overwrite = last good)
  - wrong_appearance → store the rejected run's conditioning as the "drift"
global_state is already per-refinement-key, so both live with that key.

Apply (_v2_apply_conditioning_memory_impl, after the intent-family delta):
when the user rated the previous gen wrong_appearance, gently pull this gen
toward the blessed appearance (lerp 0.05) and repel from the drift (0.04),
reusing _v2_apply_conditioning_payload / _v2_repel_conditioning_payload. No
ramping; the existing 7.5% delta cap bounds the net effect. No-op when
nothing is stored or shapes mismatch.

Drops the dead wrong_categories field (zero consumers after Part A) and
keeps skip_value_function (the anchor is a conditioning nudge, not a reward).

Tests: 7 new unit tests (storage + overwrite + skip + pull/repel + idle +
non-trigger); updated test_wrong_appearance_rating_is_available for the new
semantics. Zero new failures vs HEAD (the 10/31 remaining are pre-existing
env failures from missing server/comfy modules).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:44:55 +03:00
digital-garbageandClaude Opus 4.8 52fbb5b461 README: document cutting_edge branch under Dev and Cutting Edge branches
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:27:35 +03:00
digital-garbageandClaude Opus 4.8 61e7097416 README: document cutting_edge branch under Dev and Cutting Edge branches
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:27:15 +03:00
digital-garbageandClaude Opus 4.8 e39a64de57 Stage 2 (2/3): delete dead prompt-repair methods + their tests
Removes the now-unreferenced repair-only machinery left behind after
Stage 2 (1/3) neutralized the application:
  - _v2_prompt_repair_text_allowed
  - _v2_emphasized_prompt
  - _v2_repair_prompt_for_missing_axes and the repair-candidate helpers
  - perfect_repairs / rejected_repairs storage + perfect-repair eviction
    note in the intent-family learn path
Shared helpers still used by intent-alignment (e.g. _v2_repair_intent_roots,
_v2_repair_candidate_matches_intent, _v2_prompt_contains_text) are kept, as
is the repair_feedback / active_repair_axes "what's missing" signal the
advisor reads.

Deletes the 11 category-alignment tests that exercised the removed methods
directly. No new test failures (the 10/31 remaining are pre-existing env
failures from missing server/comfy modules).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:25:44 +03:00
digital-garbageandClaude Opus 4.8 65816e353a Stage 2 (1/3): remove the prompt-repair application + param
Neutralize the repair application in refine(): the intent-aligned prompt is
encoded as-is — perfect-repair phrase injection, missing-axis repair, and the
emphasis pass are gone. The advisor remains the only prompt-rewriting path, and
repair_feedback/active_repair_axes is KEPT (it's the 'what's missing' signal the
advisor still reads). Drop the prompt_repair param from refine(), INPUT_TYPES,
FunPackStudio, and both UIs (web + engine_settings).

The dead repair-only methods (_v2_repair_prompt_for_missing_axes, perfect-repair
phrases, etc.) come out in the next commit; shared helpers like
_v2_repair_intent_roots (used by intent alignment) stay.

Deleted the now-obsolete test_repair_candidates_saved_as_json_lists (tested the
removed injection). No new test failures vs dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:15:28 +03:00
digital-garbageandClaude Opus 4.8 91affc5359 Stage 1 cleanup: delete dead splitter method; preview uses split_scenes
Generation now reads split_scenes, so the duplicate _v2_split_prompt_by_transitions
method has zero callers — deleted. bridge.parse_timeline (the 'what Studio sees'
preview) now derives from split_scenes too, so the preview's scene count/boundaries
match generation exactly (no spurious 'split produced N but timeline has M' warnings).

The lossless RAW editing split (parse_timeline_verbatim / split_timeline_verbatim)
stays separate — it has a genuinely different lossless requirement (round-trips the
exact typed text). It still uses the old offset-projection path; converting it to a
lossless piece-walker is the one remaining Stage-1 loose end.

Tests: 33 + 140 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:20:28 +03:00
digital-garbage 9df7c654ac Reapply "Stage 1: generation reads split_scenes too — guard removed"
This reverts commit 1b98baaa3fbcfd4e4cd6f834401b30e36afe1e1e.
2026-06-15 13:16:46 +03:00
digital-garbageandClaude Opus 4.8 d79463a5ca Editor: materialize a just-typed global prompt before generate (screen = truth)
Root of the 'same video despite a different prompt' bug: editing the global
prompt is debounced 500ms (scheduleGlobalPromptApply) before it distributes
into scene.text. Hit Generate within that window ('thought runs faster than
quicksave') and _flushSaveForGenerate saved the OLD scene text while the screen
showed the new prompt — so generation (which reads the saved scenes) used stale
text, persisted, and survived restart.

Track the latest typed global prompt and add flushGlobalPromptApply(); call it at
the top of _flushSaveForGenerate so any pending global-prompt edit is distributed
into scenes (which also reconciles ghosts via _alignScenesFromParsed) BEFORE save
and generate. The file stays a faithful snapshot of the screen at generate time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:03:46 +03:00
digital-garbageandClaude Opus 4.8 2fae920839 Restore generic 'scene N' split — but literal-only (Movie Editor needs it)
Dropping the generic scene-N pattern broke the Movie Editor: build_combined_prompt
(for_generation) INJECTS 'scene N' markers into the generation prompt to force a
split for any unit whose text lacks a leading transition trigger. Without that
recognition the second unit never split -> 'scene 2 exists only on the timeline,
not generated'.

split_scenes now matches the generic scene-N label again, but ONLY in its
literal-text scanner (never over shortcut expansions). So an editor-injected /
user-typed 'scene 2' cuts, while a 'scene 2' buried inside a shortcut expansion
does not — fixing both the user's phantom split AND the broken montage.

For the reported prompt the piece-walker now returns 2 scenes (was 4), matching
the old generation splitter's 2 -> the guard should pass and keys attribute while
the chain/guides keep working.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:39:52 +03:00
digital-garbage 38509ea0d3 Revert "Stage 1: generation reads split_scenes too — guard removed"
This reverts commit 8fa840fd1305585b50bcc095477953a3bb9e9263.
2026-06-15 10:38:31 +03:00
digital-garbageandClaude Opus 4.8 b1e646efba Stage 1: generation reads split_scenes too — guard removed
Generation now builds its per-scene encode texts AND its per-scene keys from
the SAME split_scenes(raw) call (expanded scene text via the existing
_v2_transition_scene_texts anchor-prepend). Attribution, preview, reset and
generation are now one split, so scene count/boundaries can't disagree — the
SCENE SPLIT MISMATCH guard + diagnostic are deleted.

This also fixes WHERE generation cuts: it used to split the EXPANDED prompt
(boundaries from whatever trigger words survived expansion — e.g. the 'cut'
in 'Scene cut.'), not at your qcut/cut markers. Now it cuts at the real raw
triggers, so scene 1 = qcut..(keyA,keyB), scene 2 = ..(cs), as intended.

Behavior-changing on the generation path -> needs an in-app smoke test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 10:30:29 +03:00
digital-garbageandClaude Opus 4.8 02b2657324 Stage 1: rewrite split_scenes as a piece-walker (no offset projection)
The verbatim/offset-projection splitter mangled real prompts: long shortcut
expansions desynced the raw<->expanded offset map (empty anchor, qcut+cut
lumped), the hard-coded generic 'scene N' pattern split on incidental text
('scene 2' inside an expansion), and re-scanning expansions re-triggered on
words like the 'cut' inside 'Scene cut.' — 4 phantom chunks instead of 2.

split_scenes now walks _expand_with_map's ordered pieces ONCE: a boundary is
created only by a real transition trigger (a shortcut whose trigger word is in
the transition DB, or a literal typed trigger); expansion CONTENT is never
re-scanned; the generic scene-N pattern is dropped. Each keyed shortcut adds
its key to the current scene; the leading run is the anchor (keys fold into
all). Returns raw + expanded text + keys + effect per scene.

For the reported prompt this yields exactly 2 scenes: [keyA,keyB] and [cs],
with 'scene 2'/'Scene cut.' no longer splitting. Tests updated to use real
triggers + a regression test for the expansion-trap. 21 + 140 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 10:28:54 +03:00
digital-garbageandClaude Opus 4.8 95c97123dd Stage 1 (diag): dump both splits on SCENE SPLIT MISMATCH
The mismatch is structural, not a transform: generation splits the EXPANDED
prompt (qcut/cut already replaced by their expansions) while attribution
splits the RAW prompt (qcut/cut still live as triggers). Dump both splits'
scene texts + keys so we can see exactly which words cause the raw split to
over-count, before building the unified raw-first splitter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 10:05:50 +03:00
digital-garbageandClaude Opus 4.8 d1458eb681 Stage 1: repoint attribution + reset onto canonical split; remove band-aids
resolve_scene_refinement_keys now derives purely from split_scenes — drops
the scene_count parameter AND both divergence fallbacks (the band-aids).
Generation reads per-scene keys from the canonical raw split and GUARDS that
its text-split count (prompt_to_encode) matches; on mismatch it screams and
falls back to default-key attribution rather than land a key on the wrong
scene. bridge.scene_refinement_keys + the /preview callers drop scene_count.

Reset (_v2_reset_prompt_keys) now wipes refinement_key_pool_for(prompt), so
the pool is one canonical answer shared with the reset-confirmation modal.
That removes the last user of prompt_scene_shortcut_keys, so its entire
internal split is deleted (and its 3 tests, covered by split_scenes tests).

Tests: 20 refinement-key + 140 movie_editor pass. Generation path needs an
in-app smoke test (no model here) — the guard makes a desync loud, not silent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:52:52 +03:00
digital-garbageandClaude Opus 4.8 58a4f9b52b Stage 1a: canonical split_scenes (additive, not yet repointed)
One rich scene model {anchor, scenes:[{index,raw,keys,effect}]} that every
consumer will derive from — editing (raw), preview (raw->expanded),
attribution (keys), reset (pool = union of keys), generation. Built on the
existing shortcut-aware verbatim splitter + refinement_keys_in_text, so it
can't disagree with itself. Nothing repointed yet; consumers still on their
own splitters until the staged repointing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:41:09 +03:00
digital-garbageandClaude Opus 4.8 0b72037c5b Editor: reset confirmation must list every key the reset wipes
The reset confirmation read per-scene scene_refinement_keys (which drops
keys via its divergence fallback), but the backend _v2_reset_prompt_keys
wipes the scene-count-independent pool (prompt_scene_shortcut_keys
all_keys). So a second key being trained could be reset without ever
being listed in the confirmation — a silent wipe.

Expose refinement_key_pool on the preview (= refinement_keys_in_text over
the whole prompt, which equals the backend's all_keys) and list the reset
confirmation from it. Verified pool == backend reset set, including the
divergence case where the per-scene list showed nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:28:43 +03:00
digital-garbageandClaude Opus 4.8 ec8b5005b9 Release 3.0.2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v3.0.2
2026-06-15 08:44:44 +03:00
digital-garbageandClaude Opus 4.8 3565ac6877 Editor: arm session reset off a fresh preview so key list is current
sessionResetKeys() read scene_refinement_keys from the cached preview,
which can lag the prompt (debounced refresh; _distributeGlobalPrompt
carries the old keys forward until a follow-up preview). A just-added
key-bound shortcut was therefore absent from the reset list, so the modal
only offered to wipe "default". resetStudioSession() now awaits
refreshPreview(true) before reading the keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 08:33:25 +03:00
digital-garbageandClaude Opus 4.8 f7d83f9279 Editor: refresh exposed project-settings dropdowns with the model list
A node input exposed to the main editor snapshotted its combo choices at
expose time, so refreshing the model list updated the live spec in the
Models menu but left the exposed project-settings dropdown stale (a new
LoRA showed up in node settings but not in the exposed control).

Add refreshExposedChoices(): after a refresh reloads specs, re-pull each
exposed control's and shared link's combo choices from the fresh spec and
persist (so the inspector re-renders via funpack-models-changed). Route
the modal button, ModelsModal.refresh and the menubar item through one
shared doFullRefresh().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 08:22:55 +03:00
digital-garbageandClaude Opus 4.8 6338c39048 Refinement keys: read keys per scene, drop the count-compare guesswork
Attribution was re-splitting the raw prompt independently and comparing
scene counts, then guessing on mismatch (all-keys union -> cross-training,
or empty -> keys not detected, incl. single-scene-with-keys). Both the
80MB cross-training and Darki's "custom uploaded keys not detected" trace
to that.

resolve_scene_refinement_keys now reads each scene directly: split with
the shortcut-aware split_timeline_verbatim (same scenes generation uses)
and collect, via new refinement_keys_in_text(), the keys whose shortcuts
fired in each scene (anchor keys apply to all). No scene-count compare, no
union/empty fallback except a genuine multi-scene misalignment. Single
scene with a custom key is detected; each scene gets exactly its keys.

Reverted the original-text scan added to prompt_scene_shortcut_keys (it
desynced that splitter from parse_timeline/generation and caused the
detection regression); it's now used only for session-reset all_keys.

Training law: a scene owned by custom key(s) trains ONLY them; the project
default key is left untouched and learns solely from scenes with no custom
key. Tests added for all three behaviors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:47:50 +03:00
digital-garbageandClaude Opus 4.8 f23e8cd32e Stop prompt-repair ratings from poisoning the reward assets
Wrong-* ratings (appearance/details/action/combos) are prompt-REPAIR
signals — "good gen, wrong words/identity" — not quality rewards. They
were feeding their 0.0/low reward into both value-function training and
the keyless Absolute taste store, stamping visually-good conditioning as
a low-value valley. The VF ascent (now always the default key, applied to
every scene) then pushed conditioning away from good regions into drift /
wrong-character / distorted gens; Absolute stored good gens as global
bad-taste.

Wrong-* now carry skip_value_function: still drive prompt repair + the
relative per-key direction/category memory, but train no reward asset.
Quality ratings (Missing-*/Perfect/Nailed/Awful) unchanged. Gated all 3
relative VF call sites + _v2_learn_absolute. Preventative only — keys
poisoned before this fix still need reset/retrain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 23:52:07 +03:00
digital-garbageandClaude Opus 4.8 ab4f78695d Multi-key: stop training every refinement key on every clip
Per-scene key attribution diverged from the generation split whenever the
advisor rewrote prompt_to_encode away from the raw prompt, so
resolve_scene_refinement_keys hit its all-keys UNION fallback and assigned
every key to every scene. That union got persisted into the run and read
back at rating time, training every key on every clip (value_fn bloat).

- prompt_scene_shortcut_keys now scans both expanded and original text
  (mirrors split_timeline_verbatim), so identical structure -> matching
  scene count -> precise attribution far more often.
- Divergence fallback is now default-key only (empty sets), not the
  all-keys union; a diverged run under-trains instead of cross-training.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 23:22:12 +03:00
digital-garbageandClaude Opus 4.8 5ed5fcd59a Editor: clip volume slider takes effect live (fix mute that won't un-mute)
The timeline clip volume slider writes audio_volume via patchSceneQuiet, which
updates state WITHOUT a re-render, so the player's cached clip.vol stayed stale.
Dropping to 0% only "stuck" when some later action happened to rebuild the
clips; dragging back above 0 never rebuilt, so the clip stayed muted. _applyFx
now reads the scene's audio_volume (and audio_separated) LIVE from the store; it
runs every frame during playback, so the slider tracks immediately both ways.
Final-render volume already read live, so only preview was affected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 21:49:09 +03:00
digital-garbageandClaude Opus 4.8 189b1e598b Editor: drop an image on the timeline to create a default-length clip
Dropping an image on empty timeline did nothing — images only set an existing
clip's i2v anchor. The track already had drop-to-create for video; extend it to
images: a dropped image now becomes a new GENERATIVE scene at the project default
length, anchored to that image (i2v), with an empty prompt to fill in. New
store.addImageClip mirrors addScene (server assigns the id) plus the anchor.

Also stop the clip-level media drop from bubbling to the track drop, so dropping
an image ON a clip sets that clip's anchor without ALSO spawning a new clip (the
same latent double-fire existed for video). Media tooltip updated for discovery.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:22:12 +03:00
digital-garbageandClaude Opus 4.8 3d19555389 Clarify rating descriptions + display names (camera-aware), display-layer only
Reworks the rating picker wording in both the Studio node and the Movie Editor so
testers pick the right rating. Renames are DISPLAY-ONLY — the canonical label sent
to the backend is unchanged, so node validation and previously-saved ratings keep
working with no aliases/migration:
  Missing quality -> "Bad quality / artifacts"   (body horror lives here)
  Wrong details   -> "Not-asked details"
  Wrong appearance-> "Wrong character/subject"    (NOT body horror)
Descriptions now state that "action" covers CAMERA moves and "details" covers
appearance / setting / camera framing, matching _v2_axis_categories
(action->{action,camera}; details->{details,appearance,environment,camera,action}).
Stored values render via the new display map in formatLabel / getLabel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:17:05 +03:00
digital-garbageandClaude Opus 4.8 643ca447c0 Run VF ascend+search once on the merged scene, not per key
Per-scene multi-key steering ran the value-function ascend+search inside the
per-key loop, then averaged. That injected independent search noise per key and
let a poorly-fit fresh key's VF ascend toward a bad optimum, which the averaging
baked in — a body-horror path. Now each key contributes only its learned
directions (per-prompt conditioning memory, already delta-capped); the merge is
formed and magnitude-disciplined, then a single ascend+search runs on the merged
result using the always-on project default key (the most-trained, most stable VF).
Keyed single-key scenes also now ascend with the default VF rather than that one
key's, for the same stability reason.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:58:27 +03:00
digital-garbageandClaude Opus 4.8 bc0d035125 Bound multi-key scene merge to the single-key magnitude regime
Per-scene multi-key steering averaged each key's independently steered (and
VF-ascended/searched) conditioning with no magnitude discipline. Averaging
tensors that point different directions shrinks per-token magnitude — and
collapses it toward zero when keys disagree — leaving multi-key scenes weaker and
further-drifted than the validated single-key path, which free-ran into warped /
body-horror output (worst on simple prompts, fresh keys).

Re-impose the single-key tail on the merged result: cap the delta vs the raw
scene encode to 8%, then renormalize to its per-token norm. Capping before renorm
is what keeps a near-zero (disagreeing-keys) mean from being amplified into noise;
single-key scenes are unchanged. Does not address the separate question of
ambiguous "wrong appearance" ratings poisoning a key's value function.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:47:53 +03:00
digital-garbageandClaude Opus 4.8 2934afec7e Store refinement keys as readable <key>.json (no hashing); overwrite-or-cancel on import
The key name is now the filename: a key "123" is refinements/123.json, listed by
stem and resolved directly by name everywhere (Studio, loader, Editor, sampler).
This fixes shortcuts spawning ghost keys — a dropped/loaded <key>.json is found
by the refiner's own path lookup with no scanning or adoption needed. Sidecars
keep a <key>.<mode> suffix (value function, sampler context, latents); only keys
that aren't a valid filename fall back to the old <prefix>_<md5> hash.

Clean break: legacy hashed files are not migrated (and are hidden from the key
list rather than shown as hash names). Exports now download as <key>.json so a
re-dropped export is already canonical — no tolerant filename/inner-field
reconciliation layer needed.

Import now prompts overwrite-or-cancel on a name collision (409 -> confirm ->
retry with overwrite), in both the Editor and the Refinement Key Loader node.
413 chunked upload (Vast.ai / Runpod proxy body caps) retained.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:05:53 +03:00
digital-garbageandClaude Opus 4.8 c331fc21c7 Make refinement keys cross-compatible: detect any-named drops, chunk uploads past proxy limits
Detection: refinement_key_names() now scans every .json in the refinements
folder (not just the canonical refine_v2_<hash>.json name) and tolerates the
{"state": ...} export wrapper, so manually-placed keys and HuggingFace downloads
are listed whatever they're named. On detection/load they self-heal: the file is
migrated to the canonical hashed path so loads (which resolve by hash) succeed
everywhere — fixing keys that listed but wouldn't load. Sidecars (sampler_ctx
etc.) are excluded by the clip-namespace check.

Upload 413: large keys POSTed in one shot were rejected by the reverse proxies
on Vast.ai / Runpod (body cap well under ComfyUI's 100 MB). importRefinementKey
now streams big keys in 256 KB byte-accurate chunks reassembled server-side via
new /refinement_keys/import_chunk + /import_finalize routes; small keys keep the
fast single POST with an automatic chunked fallback on 413.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 15:41:59 +03:00
digital-garbageandClaude Opus 4.8 81c67f99e4 Release 3.0.1: refinement keys + Cutting Room fixes
Per-shortcut refinement keys (multi-key per-scene steering, timeline key
preview, session-reset wipes all trained keys) plus Cutting Room fixes
(comma-split replacements, i2v anchor prompt re-render, trigger-that-is-also
-a-shortcut timeline split). Restore the dropped [3.0.0] CHANGELOG header and
bump pyproject + README to 3.0.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v3.0.1
2026-06-14 11:05:58 +03:00
digital-garbageandClaude Opus 4.8 5f784285d0 Stop splitting shortcut replacements on commas
A replacement is a prose phrase and may contain commas (e.g. "The video
rapidly cuts, showing the next view."). Both the Movie Editor's splitLines
and the backend _shortcut_replacements string fallback split on commas, so
one phrase became several variants: the editor showed it across lines,
expansion picked one half at random (never matching the comma-form transition
trigger), and a comma edit reverted on save because it re-split every time.

Replacements are now one-per-line only (commas preserved); triggers still
accept comma-separation per their UI. Existing torn replacements self-heal on
the next edit + save.

- mediabrowser.js: separate splitTriggers (\n or ,) and splitReplacements (\n).
- templates.py: _shortcut_replacements string fallback splits on \n only.
- tests: comma phrase stays one replacement (list + string + normalize paths).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 10:56:16 +03:00
digital-garbageandClaude Opus 4.8 025b0213ef Re-render timeline after i2v anchor change so prompt text doesn't vanish
Replacing a scene's i2v anchor is a source-only change: it doesn't change
the prompt-preview key or selection, so commit() replaced state.project with
the saved project WITHOUT a notify(), leaving the stale optimistic DOM. The
clip's prompt text label could read blank until a reload/regenerate forced a
full re-render. Anchor/source changes now set _renderAfterSave so commit()
re-renders from the authoritative saved state once the save completes (the
flag survives commit's requeue/reentrancy and only clears on an actual
render). Backend round-trip already preserved text/gen_unit_id/source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 10:38:52 +03:00
digital-garbageandClaude Opus 4.8 2fde2e8506 Fix editor timeline losing scenes when a trigger is also a shortcut
split_timeline_verbatim expanded shortcuts before detecting transitions, so
a cut marker that is ALSO a shortcut (e.g. qcut/cut, common now that cut
markers carry refinement keys) had its trigger word replaced before
detection -> the split was lost or misplaced ("scene 2 removed, scene 1
shows scene 2's prompt"). It now ALSO scans the original verbatim text for
triggers and merges with the expanded scan; trigger-shortcuts are caught at
their true offset, shortcut-driven splits and plain triggers are unchanged,
and the dedup collapses duplicates. Lossless round-trip preserved.

- conditioning.py: original-text trigger scan in split_timeline_verbatim.
- tests: user's qcut/cut case (2 scenes, actions 10|20,30, lossless) +
  shortcut-driven split still works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 10:27:57 +03:00
digital-garbageandClaude Opus 4.8 3b719d098e Revert over-aggressive verbatim-split guard
The shortcut-internal-cut guard disabled the supported 'shortcut whose
expansion resolves to a transition' splitting (see project prompt model),
so scenes stopped splitting at all. Reverts split_timeline_verbatim to the
prior behavior; the real drop/shift bug needs a targeted fix with the
user's actual prompt + shortcut definition.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 10:17:55 +03:00
digital-garbageandClaude Opus 4.8 209466995b Fix editor timeline dropping scenes on shortcut-internal cuts
split_timeline_verbatim detected scene cuts in EXPANDED text and projected
them back onto the original. A cut living inside a shortcut's replacement
got projected onto the shortcut's edge; colliding/early boundaries were then
deduped away, dropping a scene and shifting the rest ("scene 2 removed,
scene 1 shows scene 2's prompt") until reload. This surfaced more once
shortcuts could carry refinement keys (more key-bound shortcuts in prompts).

The editor treats shortcuts as opaque (they only expand for preview/
generation), so the verbatim splitter now ignores any transition match that
starts inside a shortcut expansion. Literal user-typed markers still split;
generation still expands and splits unchanged. Result round-trips losslessly.

- conditioning.py: _starts_in_shortcut guard in split_timeline_verbatim.
- tests: shortcut-internal cut is ignored + lossless; literal cuts still split.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 10:11:51 +03:00
digital-garbageandClaude Opus 4.8 c8ec0e3d55 Session Reset wipes all prompt keys; warn with the key list
Per-scene multi-key learning trains every refinement key whose shortcut
fired in the prompt, so a reset must clear them all — not just the
project/default key, which left stale per-key state behind.

- conditioning.py: _v2_reset_prompt_keys() — on reset_session, union the
  non-default keys from the raw positive + intent prompts and reset/save
  each (primary key excluded; main flow already handles it). Reported in
  the state status string.
- store.js: "Reset Studio session" now confirms before arming, listing
  every key it will wipe (default + non-default keys in the prompt, from
  the preview's scene_refinement_keys). Disarming does not re-prompt.
- tests: union/primary-exclusion + no-op cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 09:53:40 +03:00
digital-garbageandClaude Opus 4.8 b78de23ccd Show per-scene refinement keys in the Movie Editor preview
Surface, before generation, which refinement key(s) each scene will steer
with — exactly matching the generation-time resolution.

- conditioning.py: extract resolve_scene_refinement_keys() as the single
  source of truth; FunPackVideoRefinerV2._v2_scene_refinement_keys now
  delegates to it (no behavior change).
- bridge.scene_refinement_keys(): preview payload per scene — explicit
  non-default keys, or the project default key when none fired.
- server /preview: attach scene_refinement_keys (both gen and non-gen
  branches), resolved against the project's refinement_key.
- inspector.js + styles.css: per-scene key chip in the timeline preview
  (avg when >1; greyed "(default)" fallback).
- tests: resolver/Studio parity + bridge preview payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 09:46:21 +03:00