Skip to content

Commit 06fa518

Browse files
committed
fix(studio): keep preview state synchronized
1 parent f731659 commit 06fa518

24 files changed

Lines changed: 821 additions & 305 deletions

packages/studio/src/components/nle/NLEContext.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
import {
2-
createContext,
3-
useContext,
4-
useState,
5-
useCallback,
6-
useRef,
7-
useEffect,
8-
type ReactNode,
9-
} from "react";
1+
import { useContext, useState, useCallback, useRef, useEffect, type ReactNode } from "react";
102
import { useTimelinePlayer, usePlayerStore } from "../../player";
113
import type { TimelineElement } from "../../player";
124
import type { CompositionLevel } from "./CompositionBreadcrumb";
@@ -16,6 +8,7 @@ import { setCompositionSourceMap } from "../editor/domEditingDom";
168
import { ensureMotionPathPluginLoaded } from "../../utils/gsapSoftReload";
179
import { readStudioUiPreferences, writeStudioUiPreferences } from "../../utils/studioUiPreferences";
1810
import { useAssetPreviewStore } from "../../utils/assetPreviewStore";
11+
import { createStableContext } from "../../utils/hmrStableContext";
1912

2013
// Timeline gets a generous default height so the preview isn't oversized and the
2114
// tracks have room to breathe (CapCut-style). Users can still drag the divider.
@@ -55,7 +48,7 @@ export interface NLEContextValue {
5548
setPreviewCompositionSize: (size: { width: number; height: number } | null) => void;
5649
}
5750

58-
const NLEContext = createContext<NLEContextValue | null>(null);
51+
const NLEContext = createStableContext<NLEContextValue | null>("NLEContext", null);
5952

6053
export function useNLEContext(): NLEContextValue {
6154
const ctx = useContext(NLEContext);

packages/studio/src/components/nle/PreviewPane.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ export function PreviewPane({
156156
disabled={timelineDisabled}
157157
isFullscreen={isFullscreen}
158158
onToggleFullscreen={toggleFullscreen}
159-
previewIframeRef={iframeRef}
160159
/>
161160
</div>
162161
</div>

packages/studio/src/components/nle/useTimelineEditCallbacks.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ export interface TimelineEditCallbackDeps {
4747
handleRazorSplit: (element: TimelineElement, splitTime: number) => Promise<void> | void;
4848
handleRazorSplitAll: (splitTime: number) => Promise<void> | void;
4949
/** C1's ungrouped-track FX pointer — same auto-grouping write B6's carve uses. */
50-
handleGroupClips?: (clipIds: readonly string[], groupId: string) => Promise<void>;
50+
handleGroupClips?: (
51+
clipIds: readonly string[],
52+
groupId: string,
53+
groupLabel?: string,
54+
) => Promise<void>;
5155
/** C1's single-clip FX write, addressed by the clip itself. */
5256
setElementFxAttribute?: {
5357
setLive: (element: TimelineElement, attr: string, value: string | null) => void;

packages/studio/src/contexts/DesignPanelInputContext.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { createContext, useCallback, useContext, useMemo, type ReactNode } from "react";
1+
import { useCallback, useContext, useMemo, type ReactNode } from "react";
2+
import { createStableContext } from "../utils/hmrStableContext";
23
import { trackDesignInput, type DesignInputUi } from "../utils/designInputTracking";
34

45
// Carries which inspector UI and which section the currently-rendered design-panel
@@ -11,10 +12,10 @@ interface DesignPanelInputContextValue {
1112
section: string;
1213
}
1314

14-
const DesignPanelInputContext = createContext<DesignPanelInputContextValue>({
15-
ui: "classic",
16-
section: "unknown",
17-
});
15+
const DesignPanelInputContext = createStableContext<DesignPanelInputContextValue>(
16+
"DesignPanelInputContext",
17+
{ ui: "classic", section: "unknown" },
18+
);
1819

1920
export function DesignPanelInputProvider({
2021
ui,

packages/studio/src/contexts/DomEditContext.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// fallow-ignore-file code-duplication
2-
import { createContext, useCallback, useContext, useMemo, useRef, type ReactNode } from "react";
32
import type { useDomEditSession } from "../hooks/useDomEditSession";
3+
import { useCallback, useContext, useMemo, useRef, type ReactNode } from "react";
4+
import { createStableContext } from "../utils/hmrStableContext";
45

56
type DomEditValue = ReturnType<typeof useDomEditSession>;
67

@@ -93,8 +94,14 @@ export interface DomEditSelectionValue extends Pick<
9394
| "agentPromptSelectionContext"
9495
> {}
9596

96-
const DomEditActionsContext = createContext<DomEditActionsValue | null>(null);
97-
const DomEditSelectionContext = createContext<DomEditSelectionValue | null>(null);
97+
const DomEditActionsContext = createStableContext<DomEditActionsValue | null>(
98+
"DomEditActionsContext",
99+
null,
100+
);
101+
const DomEditSelectionContext = createStableContext<DomEditSelectionValue | null>(
102+
"DomEditSelectionContext",
103+
null,
104+
);
98105

99106
export function useDomEditActionsContext(): DomEditActionsValue {
100107
const ctx = useContext(DomEditActionsContext);

packages/studio/src/contexts/FileManagerContext.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { createContext, useContext, useMemo, type ReactNode } from "react";
21
import type { useFileManager } from "../hooks/useFileManager";
2+
import { useContext, useMemo, type ReactNode } from "react";
3+
import { createStableContext } from "../utils/hmrStableContext";
34

45
type FileManagerValue = ReturnType<typeof useFileManager>;
56

6-
const FileManagerContext = createContext<FileManagerValue | null>(null);
7+
const FileManagerContext = createStableContext<FileManagerValue | null>("FileManagerContext", null);
78

89
export function useFileManagerContext(): FileManagerValue {
910
const ctx = useContext(FileManagerContext);

packages/studio/src/contexts/PanelLayoutContext.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { createContext, useContext, useMemo, type ReactNode } from "react";
21
import type { usePanelLayout } from "../hooks/usePanelLayout";
2+
import { useContext, useMemo, type ReactNode } from "react";
3+
import { createStableContext } from "../utils/hmrStableContext";
34

45
type PanelLayoutValue = ReturnType<typeof usePanelLayout>;
56

6-
const PanelLayoutContext = createContext<PanelLayoutValue | null>(null);
7+
const PanelLayoutContext = createStableContext<PanelLayoutValue | null>("PanelLayoutContext", null);
78

89
export function usePanelLayoutContext(): PanelLayoutValue {
910
const ctx = useContext(PanelLayoutContext);

packages/studio/src/contexts/StudioContext.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { createContext, useContext, useMemo, type ReactNode } from "react";
21
import type { TimelineElement } from "../player";
32
import type { CompositionDimensions } from "../components/renders/RenderQueue";
43
import type { FfmpegStatus } from "../components/renders/useFfmpegStatus";
4+
import { useContext, useMemo, type ReactNode } from "react";
5+
import { createStableContext } from "../utils/hmrStableContext";
56

67
export interface StudioShellValue {
78
projectId: string;
@@ -52,8 +53,11 @@ export interface StudioPlaybackValue {
5253

5354
export type StudioContextValue = StudioShellValue & StudioPlaybackValue;
5455

55-
const StudioShellContext = createContext<StudioShellValue | null>(null);
56-
const StudioPlaybackContext = createContext<StudioPlaybackValue | null>(null);
56+
const StudioShellContext = createStableContext<StudioShellValue | null>("StudioShellContext", null);
57+
const StudioPlaybackContext = createStableContext<StudioPlaybackValue | null>(
58+
"StudioPlaybackContext",
59+
null,
60+
);
5761

5862
export function useStudioShellContext(): StudioShellValue {
5963
const ctx = useContext(StudioShellContext);

packages/studio/src/contexts/TimelineEditContext.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import { createContext, useContext, useMemo, type ReactNode } from "react";
1+
import { useContext, useMemo, type ReactNode } from "react";
2+
import { createStableContext } from "../utils/hmrStableContext";
23
import type { TimelineEditCallbacks } from "../player/components/timelineCallbacks";
34

4-
const TimelineEditContext = createContext<TimelineEditCallbacks | null>(null);
5+
const TimelineEditContext = createStableContext<TimelineEditCallbacks | null>(
6+
"TimelineEditContext",
7+
null,
8+
);
59

610
export function useTimelineEditContext(): TimelineEditCallbacks {
711
const ctx = useContext(TimelineEditContext);

packages/studio/src/contexts/VariablePromoteContext.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* and callbacks to promote or to edit the bound variable's default in place.
88
*/
99

10-
import { createContext, useContext, useEffect, useMemo, useState } from "react";
1110
import type { Composition, CompositionVariable } from "@hyperframes/sdk";
1211
import type { DomEditSelection } from "../components/editor/domEditingTypes";
1312
import {
@@ -22,6 +21,8 @@ import {
2221
uniqueId,
2322
type PromoteChannel,
2423
} from "./variablePromoteHelpers";
24+
import { useContext, useEffect, useMemo, useState } from "react";
25+
import { createStableContext } from "../utils/hmrStableContext";
2526

2627
export type { PromoteChannel };
2728

@@ -47,7 +48,10 @@ interface VariablePromoteContextValue {
4748
onPersistError: (error: unknown) => void;
4849
}
4950

50-
const VariablePromoteContext = createContext<VariablePromoteContextValue | null>(null);
51+
const VariablePromoteContext = createStableContext<VariablePromoteContextValue | null>(
52+
"VariablePromoteContext",
53+
null,
54+
);
5155

5256
function readBinding(session: Composition, hfId: string, channel: PromoteChannel): string | null {
5357
const snapshot = session.getElement(hfId);

0 commit comments

Comments
 (0)