Skip to content

Commit 9b2a1a7

Browse files
committed
feat(studio): add media treatment inspector
1 parent f02ecc6 commit 9b2a1a7

21 files changed

Lines changed: 2441 additions & 402 deletions

packages/studio/src/App.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ import { useFileManager } from "./hooks/useFileManager";
1212
import { usePreviewPersistence } from "./hooks/usePreviewPersistence";
1313
import { usePreviewDocumentVersion } from "./hooks/usePreviewDocumentVersion";
1414
import { useTimelineEditing } from "./hooks/useTimelineEditing";
15-
import {
16-
persistTimelineMoveEditsAtomically,
17-
type TimelineMoveOperation,
18-
} from "./hooks/timelineMoveAdapter";
15+
import { persistTimelineMoveEditsAtomically } from "./hooks/timelineMoveAdapter";
1916
import type { TimelineZIndexReorderCommit } from "./hooks/useTimelineEditingTypes";
2017
import type { TimelineStackingReorderIntent } from "./player/components/timelineStacking";
2118
import type { BlockPreviewInfo } from "./components/sidebar/BlocksTab";
@@ -65,6 +62,7 @@ import {
6562
} from "./utils/studioUrlState";
6663
import { trackStudioSessionStart } from "./telemetry/events";
6764
import { hasFiredSessionStart, markSessionStartFired } from "./telemetry/config";
65+
type TimelineMoveOperation = Parameters<typeof persistTimelineMoveEditsAtomically>[2];
6866
// fallow-ignore-next-line complexity
6967
export function StudioApp() {
7068
const { projectId, resolving, waitingForServer } = useServerConnection();
@@ -204,6 +202,7 @@ export function StudioApp() {
204202
setActiveBlockParams,
205203
handleAddBlock,
206204
handleTimelineBlockDrop,
205+
handleAddMediaOverlay,
207206
handlePreviewBlockDrop,
208207
} = useBlockHandlers({
209208
projectId,
@@ -536,6 +535,7 @@ export function StudioApp() {
536535
domEditSaveTimestampRef={domEditSaveTimestampRef}
537536
recordEdit={editHistory.recordEdit}
538537
onToggleElementHidden={timelineEditing.handleToggleElementHidden}
538+
onAddMediaOverlay={handleAddMediaOverlay}
539539
/>
540540
)
541541
}

packages/studio/src/components/StudioRightPanel.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import {
3131
EMPTY_COLOR_GRADING_SCOPE_RESULT,
3232
type ColorGradingScope,
3333
} from "./studioColorGradingScope";
34-
import type { BackgroundRemovalProgress } from "./editor/propertyPanelTypes";
34+
import type {
35+
AddMediaOverlayHandler,
36+
BackgroundRemovalProgress,
37+
} from "./editor/propertyPanelTypes";
3538
import { timelineKeysForSelections, type ToggleHiddenHandler } from "../utils/studioHelpers";
3639
import { useInspectorSplitResize } from "../hooks/useInspectorSplitResize";
3740

@@ -71,6 +74,7 @@ export interface StudioRightPanelProps extends StudioEditPersistenceProps {
7174
files: Record<string, { before: string; after: string }>;
7275
}) => Promise<void>;
7376
onToggleElementHidden?: ToggleHiddenHandler;
77+
onAddMediaOverlay?: AddMediaOverlayHandler;
7478
}
7579

7680
// fallow-ignore-next-line complexity
@@ -88,6 +92,7 @@ export function StudioRightPanel({
8892
domEditSaveTimestampRef,
8993
recordEdit,
9094
onToggleElementHidden,
95+
onAddMediaOverlay,
9196
}: StudioRightPanelProps) {
9297
const {
9398
rightWidth,
@@ -372,6 +377,7 @@ export function StudioRightPanel({
372377
onRemoveTextField={handleDomRemoveTextField}
373378
onAskAgent={handleAskAgent}
374379
onImportAssets={handleImportFiles}
380+
onAddMediaOverlay={onAddMediaOverlay}
375381
fontAssets={fontAssets}
376382
onImportFonts={handleImportFonts}
377383
previewIframeRef={previewIframeRef}

packages/studio/src/components/editor/PropertyPanel.test.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -812,10 +812,10 @@ describe("PropertyPanel — Media group (Plan 4)", () => {
812812
// design_handoff scrollable-open-section: collapsed headers before/after the
813813
// open group render in normal document flow and never move (no sticky, no
814814
// stacking offsets) — only the open group's own body content scrolls, in a
815-
// dedicated region between the two fixed header stacks. Worked example: 6
816-
// groups [text, style, layout, motion, grade, media], motion open (index 3)
815+
// dedicated region between the two fixed header stacks. Worked example: 7
816+
// groups [text, style, layout, motion, grade, effects, media], motion open (index 3)
817817
// -> text/style/layout render as fixed collapsed headers before it, motion
818-
// renders as an open header + scrollable body, grade/media render as fixed
818+
// renders as an open header + scrollable body, grade/effects/media render as fixed
819819
// collapsed headers after it — in exactly that DOM order, nothing sticky.
820820
describe("PropertyPanel — fixed headers + scrollable open section (Plan 11)", () => {
821821
it(
@@ -839,13 +839,14 @@ describe("PropertyPanel — fixed headers + scrollable open section (Plan 11)",
839839
});
840840
// Filter to just the group entries (drop any non-group nulls).
841841
const groupTitles = titles.filter((t): t is string => t !== null);
842-
expect(groupTitles).toHaveLength(6);
842+
expect(groupTitles).toHaveLength(7);
843843
expect(groupTitles[0]).toContain("Text");
844844
expect(groupTitles[1]).toContain("Style");
845845
expect(groupTitles[2]).toContain("Layout");
846846
expect(groupTitles[3]).toContain("Motion");
847847
expect(groupTitles[4]).toContain("Grade");
848-
expect(groupTitles[5]).toContain("Media");
848+
expect(groupTitles[5]).toContain("Effects");
849+
expect(groupTitles[6]).toContain("Media");
849850

850851
// The open group (Motion, index 3) is the one wrapped in
851852
// data-flat-group-open, sitting between the before/after collapsed
@@ -880,14 +881,15 @@ describe("PropertyPanel — fixed headers + scrollable open section (Plan 11)",
880881
const collapsedRows = Array.from(
881882
host.querySelectorAll<HTMLButtonElement>('[data-flat-group-collapsed="true"]'),
882883
);
883-
expect(collapsedRows).toHaveLength(6);
884+
expect(collapsedRows).toHaveLength(7);
884885
const titlesInOrder = collapsedRows.map((el) => el.textContent ?? "");
885886
expect(titlesInOrder[0]).toContain("Text");
886887
expect(titlesInOrder[1]).toContain("Style");
887888
expect(titlesInOrder[2]).toContain("Layout");
888889
expect(titlesInOrder[3]).toContain("Motion");
889890
expect(titlesInOrder[4]).toContain("Grade");
890-
expect(titlesInOrder[5]).toContain("Media");
891+
expect(titlesInOrder[5]).toContain("Effects");
892+
expect(titlesInOrder[6]).toContain("Media");
891893

892894
const body = host.querySelector('[data-flat-panel-body="true"]');
893895
expect(body?.querySelector(".overflow-y-auto")).toBeNull();

0 commit comments

Comments
 (0)