From 6a03b3ca7c530a84e23894770beadd7b1844e2e3 Mon Sep 17 00:00:00 2001 From: ukimsanov Date: Sat, 11 Jul 2026 13:55:14 -0700 Subject: [PATCH] =?UTF-8?q?feat(studio):=20NLE=20shell=20components=20?= =?UTF-8?q?=E2=80=94=20context=20provider,=20panes,=20overlays=20(unwired)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What: the NLE shell layer, unwired: NLEContext (provider extracted from NLELayout), PreviewPane, AssetPreviewOverlay + assetPreviewStore, TimelineOverlays (menus/modal/hint extracted from Timeline), timelineClipChildren, timelineClipDragTypes, useTimelinePlayerLoop (playback loop extracted from useTimelinePlayer). Why: the extracted replacements for NLELayout/StudioPreviewArea and the oversized Timeline/useTimelinePlayer internals, reviewable standalone. How: new files only, tsc-clean against main. They intentionally duplicate blocks of their still-alive originals (duplication is warn-level in fallow; the swap PRs delete the originals). Dead-file findings covered by TEMP(studio-dnd) entry registrations, removed at the app-shell swap. Test plan: tsc --noEmit; bunx vitest run (suite unchanged); fallow audit clean. --- .fallowrc.jsonc | 13 + .../components/nle/AssetPreviewOverlay.tsx | 147 +++++++++ .../src/components/nle/NLEContext.test.ts | 88 +++++ .../studio/src/components/nle/NLEContext.tsx | 312 ++++++++++++++++++ .../studio/src/components/nle/PreviewPane.tsx | 163 +++++++++ .../player/components/TimelineOverlays.tsx | 122 +++++++ .../components/timelineClipChildren.tsx | 39 +++ .../components/timelineClipDragTypes.ts | 65 ++++ .../src/player/hooks/useTimelinePlayerLoop.ts | 85 +++++ .../studio/src/utils/assetPreviewStore.ts | 33 ++ 10 files changed, 1067 insertions(+) create mode 100644 packages/studio/src/components/nle/AssetPreviewOverlay.tsx create mode 100644 packages/studio/src/components/nle/NLEContext.test.ts create mode 100644 packages/studio/src/components/nle/NLEContext.tsx create mode 100644 packages/studio/src/components/nle/PreviewPane.tsx create mode 100644 packages/studio/src/player/components/TimelineOverlays.tsx create mode 100644 packages/studio/src/player/components/timelineClipChildren.tsx create mode 100644 packages/studio/src/player/components/timelineClipDragTypes.ts create mode 100644 packages/studio/src/player/hooks/useTimelinePlayerLoop.ts create mode 100644 packages/studio/src/utils/assetPreviewStore.ts diff --git a/.fallowrc.jsonc b/.fallowrc.jsonc index ccb30a1f32..ba82d3950b 100644 --- a/.fallowrc.jsonc +++ b/.fallowrc.jsonc @@ -55,6 +55,16 @@ "packages/studio/src/hooks/gsapRuntimePreview.ts", // TEMP(studio-dnd): shipped unwired ahead of the NLE integration; // the app-shell swap PR (studio-dnd/pr22) wires the consumers and removes this block. + "packages/studio/src/components/nle/NLEContext.tsx", + "packages/studio/src/components/nle/PreviewPane.tsx", + "packages/studio/src/components/nle/AssetPreviewOverlay.tsx", + "packages/studio/src/player/components/TimelineOverlays.tsx", + "packages/studio/src/player/components/timelineClipChildren.tsx", + "packages/studio/src/player/components/timelineClipDragTypes.ts", + "packages/studio/src/player/hooks/useTimelinePlayerLoop.ts", + "packages/studio/src/utils/assetPreviewStore.ts", + // TEMP(studio-dnd): shipped unwired ahead of the NLE integration; + // the app-shell swap PR (studio-dnd/pr22) wires the consumers and removes this block. "packages/studio/src/components/editor/CanvasContextMenu.tsx", ], "ignorePatterns": [ @@ -446,6 +456,9 @@ // complexity pre-dates the computed-timeline work. Exempted at file level // rather than refactored as scope creep. "ignore": [ + // TEMP(studio-dnd): coexistence-window complexity flare (inherited/CRAP-no-coverage); + // removed by studio-dnd/pr22 when the final config lands. + "packages/studio/src/player/components/TimelineOverlays.tsx", // TEMP(studio-dnd): coexistence-window complexity flare (inherited/CRAP-no-coverage); // removed by studio-dnd/pr22 when the final config lands. "packages/studio/src/player/hooks/useTimelineSyncCallbacks.ts", diff --git a/packages/studio/src/components/nle/AssetPreviewOverlay.tsx b/packages/studio/src/components/nle/AssetPreviewOverlay.tsx new file mode 100644 index 0000000000..5364289f15 --- /dev/null +++ b/packages/studio/src/components/nle/AssetPreviewOverlay.tsx @@ -0,0 +1,147 @@ +/** + * CapCut-style asset preview overlay rendered inside PreviewPane. + * + * Shown when the user clicks an asset card that has NOT yet been added to the + * timeline. Displays the media (image / video / audio) without modifying the + * composition — no undo entry, no file mutation. + * + * Dismiss: X button, Escape key, or click on the scrim. + * Switching to another not-added asset replaces the current preview. + */ +import { useEffect, useCallback } from "react"; +import { VIDEO_EXT, IMAGE_EXT } from "../../utils/mediaTypes"; +import { useAssetPreviewStore } from "../../utils/assetPreviewStore"; + +function basename(path: string): string { + return path.split("/").pop() ?? path; +} + +type AssetKind = "image" | "video" | "audio"; + +function resolveAssetKind(path: string): AssetKind { + if (VIDEO_EXT.test(path)) return "video"; + if (IMAGE_EXT.test(path)) return "image"; + return "audio"; +} + +/** The media element for a previewed asset, chosen by kind. */ +function AssetPreviewMedia({ + kind, + serveUrl, + name, +}: { + kind: AssetKind; + serveUrl: string; + name: string; +}) { + if (kind === "image") { + return ( + {name} + ); + } + if (kind === "video") { + return ( +