Skip to content

feat(core): structural montage editing — reorder + remove (0.6.40)#69

Merged
kiyeonjeon21 merged 1 commit into
mainfrom
feat/structural-montage
Jun 20, 2026
Merged

feat(core): structural montage editing — reorder + remove (0.6.40)#69
kiyeonjeon21 merged 1 commit into
mainfrom
feat/structural-montage

Conversation

@kiyeonjeon21

Copy link
Copy Markdown
Owner

What

Spine 2c — structural editing of montages, regen-surviving. Clip-ripple (0.6.39) made retiming survive AI regeneration via label-anchored video.start; this makes structure survive too. A human reorders or drops a montage shot in an overlay JSON and the edit re-applies after the base is regenerated.

How

1. Montage rewrite — self-contained named shot beats (packages/core/src/montage.ts)
Each shot is now a named beat shot-${i} that owns only its own layer's motion (fade-in ∥ Ken Burns ∥ fade-out). Every layer starts at opacity: 0; adjacent shots overlap by the crossfade via a negative gap in the seq. No shot references its neighbour, so it can move/drop as a unit. Stable shot-${i}/cross-${i} labels + node ids preserved → clip-ripple and anchored titles keep resolving.

2. New generic compose verb removeTimeline (packages/core/src/compose.ts)
Splices a beat/step out of its parent by label (parallel to removeNodes); the seq re-accumulates so later steps ripple up. Unknown label → orphan, never a silent drop.

3. The three edits, all overlay-time + regen-surviving

  • Reorder → existing beat order patch (no new code)
  • RemoveremoveTimeline: ["shot-2"] (the dropped layer just stays invisible)
  • Swap image → plain nodes.<id>.src patch

Verification

  • 386 tests pass, typecheck clean, npm build clean, goldens byte-identical
  • verify-overlay examples/scenes/photo-montage.ts examples/overlays/montage-restructure.json4 applied, 0 orphaned
  • reframe labels confirms each shot-i starts 0.70s (the transition) before the prior ends — true crossfade overlap
  • Rendered a crossfade frame: two photos cleanly blended, title + grade intact

Notes

  • Cosmetic limitation (documented in regen-contract.md): reordering a shot to the first slot drops its opening fade-up (the crossfade offset was baked for the original order).
  • A node still anchoring to a removed label (e.g. a video start: "shot-2") must also be neutralised (patch start to a number) or post-compose validation rejects the dangling anchor — documented.
  • Out of scope (separate increment): insert a shot (splice-into-parent + node-add + framing seed).
  • Version → 0.6.40 (PATCH); no plugin bump.

🤖 Generated with Claude Code

Spine 2c. Rewrites photoMontage/videoMontage so each shot is a
self-contained named beat `shot-${i}` (fade-in ∥ Ken Burns ∥ fade-out);
every layer starts at opacity 0 and adjacent shots overlap by the
crossfade via a negative `gap` in the seq. No shot references its
neighbour, so a shot is an independent unit you can edit by overlay and
survive AI regeneration:

- reorder via the existing beat `order` patch
- remove via a new generic compose verb `removeTimeline: ["shot-2"]`
  (splices a beat/step from its parent by label; the seq re-accumulates
  so later shots ripple up; unknown label → orphan, never silent)
- swap an image via a plain nodes.<id>.src patch

Stable addresses (shot-/cross- labels, node ids) preserved → clip-ripple
and anchored titles keep resolving. Montage opens on a fade-up / closes
on a fade-out (symmetric → edit-safe). Montage has no golden snapshot, so
the timing shift is a generator-output change, not a determinism break.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiyeonjeon21 kiyeonjeon21 merged commit 4d94f22 into main Jun 20, 2026
1 check passed
@kiyeonjeon21 kiyeonjeon21 deleted the feat/structural-montage branch June 20, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant