Skip to content

feat(studio): app shell swap — mount EditorShell, retire the legacy engine#2213

Open
ukimsanov wants to merge 1 commit into
studio-dnd/pr21-timeline-glue-swapfrom
studio-dnd/pr22-app-shell-swap
Open

feat(studio): app shell swap — mount EditorShell, retire the legacy engine#2213
ukimsanov wants to merge 1 commit into
studio-dnd/pr21-timeline-glue-swapfrom
studio-dnd/pr22-app-shell-swap

Conversation

@ukimsanov

@ukimsanov ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

What

the final swap (43 files): App mounts EditorShell; contexts, hotkeys, sidebar (AssetsTab→AssetCard, toolbar, audio row), useTimelineEditing and the store drop the legacy single-select API and trimmed callbacks; deletes the 7 files this orphans (StudioPreviewArea, NLELayout, useTimelineGroupEditing, timelineLayerDrag + suites, legacy hook test); .fallowrc.jsonc lands at final content, dropping every TEMP(studio-dnd) suppression — everything shipped unwired is now reachable.

Why

last step of the NLE integration; after this the tree is byte-identical to the previously-green integration checkpoint.

How

modified files to final content + gate-forced deletions + suppression cleanup. Tree parity with checkpoint 86dd2a32b verified.

Test plan

tsc --noEmit in studio, studio-server, core; bun run --filter @hyperframes/studio build; bunx vitest run (full suite); fallow audit clean with zero remaining TEMP entries; tree-diff vs 86dd2a32b empty.


Stack position 22/25 — studio NLE overhaul (CapCut-parity timeline + canvas). Graphite manages bases; merge from #2192 upward. Temporary TEMP(studio-dnd) fallow entries (unwired-component windows) are all removed by #2213 (app-shell swap), whose tree is byte-identical to the fully-verified integration branch.

ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed as part of the 25-PR NLE overhaul stack. Full stack review on #2205 (the keystone). This is one of the critical swap PRs — reviewed with extra scrutiny. No blockers. — Miga

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R1 — Batch E · app-shell swap + legacy engine retirement

Reviewed as PR 5 of 6 in Batch E — the highest-blast-radius PR in the 25-PR NLE stack. Head SHA 6f9c592917d2dbc5efb6660066b6b793bc50d25b. -5,796 deletions across 44 files, including the .fallowrc.jsonc TEMP(studio-dnd) block, both legacy shells, all group-editing helpers, and a small cross-package trim into packages/core/.

Verdict: 🟢 LGTM

The cutover collapses cleanly. Zero dangling imports into deleted modules. No cross-package consumers of the removed core symbols. All TEMP(studio-dnd) fallow entries — added across the whole stack — are removed here. EditorShell mounts at the swap site with all callbacks wired.

Deletion audit — 8 removed source files

At tip ce89d8e (Batch E merged-tree), grepped from "..." imports across packages/ for each truly-removed module:

Removed module Dangling imports
StudioPreviewArea (-500) 0
NLELayout (-591) 0
useTimelineGroupEditing (-370) 0
timelineLayerDrag (-383) 0
useTimelineEditing.test.tsx (-963) 0
timelineLayerDrag.test.ts (-200) 0
NLELayout.test.ts (-12) 0
timelineDiscovery.test.ts (-90) 0

Every deleted source has its test deleted alongside it. timelineDiscovery.ts is trimmed (-17) rather than removed — only isEditableTarget remains; its test file was deleted since the surface it covered is gone. That's an intentional shrink, not an orphan.

.fallowrc.jsonc — TEMP(studio-dnd) sweep

Net -278 / +1 (the +1 is a comment rewrite for the TimelineEditorNotice.tsx entry). 57 path entries removed from the TEMP(studio-dnd) block. Enumerated for Phase-3 cross-check against Batches C/D additions:

  • EditorShell.tsx, StudioPreviewArea.tsx, CanvasContextMenu.tsx, DomEditOverlay.tsx, DomEditSelectionChrome.tsx, LayersPanel.test.ts, canvasContextMenuZOrder.{ts,test.ts}, domEditNudge.ts, domEditOverlayGeometry.ts, domEditOverlayGestures.ts, manualOffsetDrag.ts, useDomEditNudge.ts
  • AssetPreviewOverlay.tsx, NLEContext.{tsx,test.ts}, NLELayout.{tsx,test.ts}, PreviewOverlays.tsx, PreviewPane.tsx, TimelinePane.tsx, useTimelineEditCallbacks.ts
  • AssetCard.tsx, AssetContextMenu.tsx, assetHelpers.ts
  • DomEditContext.tsx, StudioContext.tsx
  • timelineEditingHelpers.ts, timelineElementsMove.ts, useDomEditWiring.ts, useGsapSelectionHandlers.ts, useTimelineEditing.ts, useTimelineEditingDrops.ts
  • Timeline.tsx, TimelineCanvas.tsx, TimelineLanes.tsx, TimelineOverlays.tsx, timelineClipChildren.tsx, timelineClipDragPreview.ts, timelineClipDragTypes.ts, timelineCollision.test.ts, timelineEditing.ts, timelineLayout.ts, timelineStackingSync.test.ts
  • useTimelineClipDrag.ts, useTimelineEditPinning.ts, useTimelineGeometry.ts, useTimelineMarqueeSelection.ts, useTimelineStackingSync.ts
  • useTimelinePlayer.ts, useTimelinePlayerLoop.ts, useTimelineSyncCallbacks.ts
  • timelineElementHelpers.ts, timelineIframeHelpers.ts
  • assetPreviewStore.ts, studioHelpers.ts, timelineAssetDrop.ts

Total: 57 distinct paths removed. Phase-3 aggregate will diff against Batches C/D's additions.

Cross-package concern — packages/core/src/runtime/timeline.ts (+6/-59)

Removed: file-local helper normalizeTrackAssignments and its lookup table KIND_ORDER (both const/function, not exported). Behavior change: collectRuntimeTimelinePayload no longer splits mixed-kind tracks onto separate row indices — it honors data-track-index verbatim.

Blast radius check:

  • Grepped normalizeTrackAssignments and KIND_ORDER across all packages/**/*.{ts,tsx} at tip → 0 external references. Both symbols were private to this file.
  • runtime/timeline is NOT exported from the @hyperframes/core barrel (packages/core/src/index.ts — no runtime/timeline re-export). External surface untouched.
  • Internal caller: packages/core/src/runtime/init.ts:1884 calls collectRuntimeTimelinePayload and posts the result as hf-preview payload / stores at window.__clipManifest. Consumers of clip.track downstream:
    • timeline.ts itself derives gsapTrack = max(clips.map(c => c.track)) + 1 and overlayTrack the same way for elements without an authored data-track-index. Under the removed normalizer these were derived over contiguous indices; now they're derived over authored indices. In both regimes GSAP/overlay clips still sort strictly above authored clips (it's max + 1), just at higher absolute values. No functional break.
    • timeline.test.ts has a single track assertion (expect(result.clips[0].track).toBe(0)) with data-track-index="0" — still holds under authored-track semantics.

The PR comment justifies this as the correct NLE behavior ("Track index is display-only; render never reads it"). Verified — RuntimeTimelineClip.track isn't consumed for rendering, only for display sort order.

App-shell swap verification

App.tsx (+89/-116) replaces StudioPreviewArea with EditorShell at the same slot inside viewModeValue.viewMode !== "storyboard" gate. All drag/drop/gesture/toolbar callbacks are threaded through (handleTimelineFileDrop, handleTimelineAssetDrop, handleTimelineBlockDrop, handleTimelineElementMove/Resize/Delete/Split, handleRazorSplit(All), handleToggleTrackHidden, renderClipContent, timelineToolbar, blockPreview, gestureOverlay). Left/right slots (StudioLeftSidebar, StudioRightPanel) are passed as slot props.

New: handleTimelineElementsMove (from Batch D multi-select) is now wired at the shell — good. Removed: handleTimelineGroupMove/GroupResize — the group-editing surface is being retired with useTimelineGroupEditing.ts, so this is consistent.

CI signal on tip

Check Conclusion
Preflight (lint + format) ✅ success (×3)
Detect changes ✅ success (×3)
Preview parity ✅ success
Perf: load / fps / drift / parity / scrub ✅ success (all 5)
player-perf ✅ success
preview-regression ✅ success
regression-shards 4/8 shards complete (all successful), 4/8 in progress
WIP 🟡 in progress (not a merge blocker)

Zero failures. Regression-shard tail + WIP are non-blocking on progress. All completed required checks green on the batch merge tip.

Nits

  • App.tsx has a small inline anonymous fn for deleteSelectedKeyframesRef.current that used to call deleteSelectedKeyframes(domEditSession). The inlined version calls usePlayerStore.getState() and pulls selectedKeyframePercentagesForElement. Semantics preserved but the helper import (deleteSelectedKeyframes from timelineEditingHelpers) is dropped because timelineEditingHelpers.ts is being gutted (466 → 91 lines). Fine — the extraction moved responsibility to the store.

R1 by Via

@ukimsanov ukimsanov force-pushed the studio-dnd/pr22-app-shell-swap branch from 6f9c592 to bc7ce78 Compare July 11, 2026 02:56
@ukimsanov ukimsanov force-pushed the studio-dnd/pr21-timeline-glue-swap branch from 26f858b to 5872d45 Compare July 11, 2026 08:18
@ukimsanov ukimsanov force-pushed the studio-dnd/pr22-app-shell-swap branch from bc7ce78 to 2700f84 Compare July 11, 2026 08:18
@ukimsanov ukimsanov marked this pull request as ready for review July 11, 2026 09:06

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — docs will 404 on merge + integration coverage regression

Two NEW findings from the deletion audit:

Docs / skills files still reference the removed export

packages/studio/src/index.ts at this PR's head no longer exports NLELayout, but three docs/skills references still target it:

  1. docs/packages/studio.mdx:64,68,70 imports NLELayout from @hyperframes/studio and uses <NLELayout> in the code example. Users copy-pasting this snippet after merge get a "NLELayout is not exported" TypeScript/runtime error.
  2. docs/contributing/studio-manual-dom-editing.mdx:277 references the deleted path packages/studio/src/components/nle/NLELayout.tsx.
  3. skills/website-to-video/references/capabilities.md:557 describes NLELayout as an active component.

Fix: update all three references in this same PR (docs example should either be removed or repointed at the new shell export; the skill capability description should be corrected).

963→290 line test coverage regression on undo/persist invariants

Deleted from hooks/useTimelineEditing.test.tsx (963 lines):

  • Root-duration extension via fallback path (SDK-move + SDK-resize)
  • DOM/store rollback on save-fail
  • Wait-for-lifecycle before resolve
  • Timing-after-z-index ordering on diagonal drag
  • Same-file group move as single write
  • Group move partitioned by source file with one undo entry
  • Wait for z-commit before group timing write
  • Single-clip-group-move ≡ single-clip-move

Added:

  • timelineGroupEditing.test.ts (126 lines: pure resize math)
  • useTimelineClipDrag.resize.test.tsx (164 lines: resize preview/commit/lock-degrade)

Different surface — no replacement for the persist-ordering / undo-atomicity / DOM-rollback invariants. Miga + Via + Abhai all flagged this; noting here so the follow-up integration-test PR gets scoped correctly (batched-move / multi-delete / content-driven duration / persist-ordering are the concrete invariants to restore).

Confirmed clean

  • 0 runtime dangling references at #2213 head (verified across packages/studio/src/index.ts, App.tsx, timelineCallbacks.ts, useTimelineEditing.ts, useTimelineClipDrag.ts, timelineEditing.ts).
  • .fallowrc.jsonc cleanly drops all TEMP(studio-dnd) entries — the 37-symbol superset invariant with #2205 holds.
  • Multi-select resize IS present (restored from main 36413da7f, tested at useTimelineClipDrag.resize.test.tsx). Abhai's "doesn't exist in the new NLE" was over-called; documented cut is atomic undo (per-member persist instead of atomic multi-file commit) — same tax as multi-select move.

Additional nits

  • Dead import at useTimelineEditCallbacks.ts:63: buildDomSelectionForTimelineElement destructured from useDomEditActionsContext(), appears in the deps array at :205, but never referenced in the returned callback body. Will trip lint.
  • Multi-select resize atomic-undo asymmetry: useTimelineEditCallbacks.ts:87 wires onMoveElements: handleTimelineElementsMove (atomic multi-move). No matching onResizeElements — the callback bag in timelineCallbacks.ts:26-57 doesn't even declare it. Move is atomic (single undo), resize is per-member (N undos). Undocumented in changelog. Either wire an onResizeElements batch handler for symmetry or explicitly note the asymmetry.
  • TimelineEditProvider no longer refreshes on visibility toggle from Timeline: TimelineEditContext.tsx drops value.onToggleElementHidden from the memo deps. Handler still exists but is routed only via App.tsx:503StudioRightPanelPropertyPanel. Verified Timeline.tsx and TimelineCanvas.tsx don't reference it — intentional scope narrowing, should be called out in the PR body.

— Rames Jusso

@ukimsanov ukimsanov force-pushed the studio-dnd/pr22-app-shell-swap branch from 2700f84 to d5242de Compare July 11, 2026 10:49
…ngine

What: the final swap (43 files): App mounts EditorShell; contexts, hotkeys,
sidebar (AssetsTab→AssetCard, toolbar, audio row), useTimelineEditing and
the store drop the legacy single-select API and trimmed callbacks; deletes
the 7 files this orphans (StudioPreviewArea, NLELayout,
useTimelineGroupEditing, timelineLayerDrag + suites, legacy hook test);
.fallowrc.jsonc lands at final content, dropping every TEMP(studio-dnd)
suppression — everything shipped unwired is now reachable.

Why: last step of the NLE integration; after this the tree is byte-identical
to the previously-green integration checkpoint.

How: modified files to final content + gate-forced deletions + suppression
cleanup. Tree parity with checkpoint 86dd2a32b verified.

Test plan: tsc --noEmit in studio, studio-server, core; bun run --filter
@hyperframes/studio build; bunx vitest run (full suite); fallow audit clean
with zero remaining TEMP entries; tree-diff vs 86dd2a32b empty.
@ukimsanov ukimsanov force-pushed the studio-dnd/pr22-app-shell-swap branch from d5242de to 2e1046f Compare July 11, 2026 12:51
@ukimsanov ukimsanov force-pushed the studio-dnd/pr21-timeline-glue-swap branch from 4a1b7a2 to 3e2c01f Compare July 11, 2026 12:51
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.

4 participants