@hyperframes/studio 0.7.58 → 0.7.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{hyperframes-player-CtTDO63S.js → hyperframes-player-Bm07FLkl.js} +1 -1
- package/dist/assets/{index-C47jAC3Q.js → index-B995FG46.js} +1 -1
- package/dist/assets/index-CrkAdJkb.js +426 -0
- package/dist/assets/index-Dj5p8U_A.css +1 -0
- package/dist/assets/{index-DeQPzqwH.js → index-FvzmPhfG.js} +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.html +2 -2
- package/dist/index.js +13514 -8528
- package/dist/index.js.map +1 -1
- package/dist/styles/tailwind-preset.d.ts +5 -0
- package/dist/styles/tailwind-preset.js +8 -1
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +3 -3
- package/src/components/DesignPanelPromoteProvider.tsx +15 -2
- package/src/components/StudioRightPanel.tsx +29 -51
- package/src/components/editor/AnimationCard.test.tsx +134 -0
- package/src/components/editor/AnimationCard.tsx +19 -4
- package/src/components/editor/ArcPathControls.tsx +1 -0
- package/src/components/editor/GestureRecordControl.tsx +7 -1
- package/src/components/editor/GsapAnimationSection.tsx +109 -14
- package/src/components/editor/InspectorHeaderActions.tsx +42 -4
- package/src/components/editor/PropertyPanel.test.tsx +948 -0
- package/src/components/editor/PropertyPanel.tsx +144 -148
- package/src/components/editor/PropertyPanelEmptyState.test.tsx +74 -0
- package/src/components/editor/PropertyPanelEmptyState.tsx +179 -1
- package/src/components/editor/PropertyPanelFlat.tsx +580 -0
- package/src/components/editor/PropertyPanelFlatFooter.test.tsx +83 -0
- package/src/components/editor/PropertyPanelFlatFooter.tsx +73 -0
- package/src/components/editor/PropertyPanelFlatHeader.test.tsx +79 -0
- package/src/components/editor/PropertyPanelFlatHeader.tsx +112 -0
- package/src/components/editor/gsapAnimationCallbacks.ts +27 -0
- package/src/components/editor/gsapLivePreview.ts +34 -0
- package/src/components/editor/manualEditingAvailability.test.ts +10 -0
- package/src/components/editor/manualEditingAvailability.ts +9 -0
- package/src/components/editor/propertyPanel3dTransform.tsx +5 -0
- package/src/components/editor/propertyPanelColor.test.tsx +28 -0
- package/src/components/editor/propertyPanelColor.tsx +31 -1
- package/src/components/editor/propertyPanelColorGradingControls.tsx +11 -2
- package/src/components/editor/propertyPanelColorGradingSection.tsx +45 -365
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +19 -5
- package/src/components/editor/propertyPanelFill.tsx +22 -5
- package/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +622 -0
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +570 -0
- package/src/components/editor/propertyPanelFlatLayoutSection.test.tsx +293 -0
- package/src/components/editor/propertyPanelFlatLayoutSection.tsx +383 -0
- package/src/components/editor/propertyPanelFlatMaskInsetRows.tsx +102 -0
- package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +436 -0
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +286 -0
- package/src/components/editor/propertyPanelFlatMotionSection.test.tsx +272 -0
- package/src/components/editor/propertyPanelFlatMotionSection.tsx +315 -0
- package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +1174 -0
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +563 -0
- package/src/components/editor/propertyPanelFlatSelectRow.tsx +101 -0
- package/src/components/editor/propertyPanelFlatStyleHelpers.test.ts +23 -0
- package/src/components/editor/propertyPanelFlatStyleHelpers.ts +27 -0
- package/src/components/editor/propertyPanelFlatStyleSections.test.tsx +598 -0
- package/src/components/editor/propertyPanelFlatStyleSections.tsx +516 -0
- package/src/components/editor/propertyPanelFlatTextSection.test.tsx +465 -0
- package/src/components/editor/propertyPanelFlatTextSection.tsx +407 -0
- package/src/components/editor/propertyPanelFlatTimingDerivation.test.ts +71 -0
- package/src/components/editor/propertyPanelFlatTimingDerivation.ts +59 -0
- package/src/components/editor/propertyPanelFlatToggle.test.tsx +63 -0
- package/src/components/editor/propertyPanelFlatToggle.tsx +53 -0
- package/src/components/editor/propertyPanelFont.test.tsx +30 -0
- package/src/components/editor/propertyPanelFont.tsx +136 -95
- package/src/components/editor/propertyPanelHelpers.ts +73 -0
- package/src/components/editor/propertyPanelInputCoverage.test.tsx +587 -0
- package/src/components/editor/propertyPanelMediaSection.tsx +10 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +60 -29
- package/src/components/editor/propertyPanelSections.test.tsx +161 -0
- package/src/components/editor/propertyPanelSections.tsx +154 -90
- package/src/components/editor/propertyPanelStyleSections.tsx +12 -1
- package/src/components/editor/propertyPanelTimingSection.tsx +1 -1
- package/src/components/editor/propertyPanelTypes.ts +14 -1
- package/src/components/editor/propertyPanelValueTier.test.ts +32 -0
- package/src/components/editor/propertyPanelValueTier.ts +28 -0
- package/src/components/editor/useColorGradingController.test.ts +418 -0
- package/src/components/editor/useColorGradingController.ts +596 -0
- package/src/components/panels/VariablesPanel.tsx +4 -0
- package/src/components/sidebar/CompositionsTab.test.ts +28 -2
- package/src/components/sidebar/CompositionsTab.tsx +3 -1
- package/src/components/storyboard/FramePoster.tsx +24 -4
- package/src/components/storyboard/StoryboardFrameFocus.tsx +4 -0
- package/src/components/storyboard/StoryboardFrameTile.tsx +30 -1
- package/src/components/storyboard/StoryboardGrid.tsx +23 -1
- package/src/components/storyboard/StoryboardLoaded.tsx +58 -2
- package/src/components/storyboard/StoryboardView.tsx +9 -2
- package/src/components/storyboard/frameComments.test.ts +95 -0
- package/src/components/storyboard/frameComments.ts +115 -0
- package/src/components/storyboard/useFrameComments.ts +82 -0
- package/src/contexts/DesignPanelInputContext.test.tsx +81 -0
- package/src/contexts/DesignPanelInputContext.tsx +48 -0
- package/src/contexts/DomEditContext.tsx +4 -0
- package/src/contexts/VariablePromoteContext.test.tsx +67 -0
- package/src/contexts/VariablePromoteContext.tsx +10 -5
- package/src/hooks/domEditCommitRunner.ts +11 -0
- package/src/hooks/gsapScriptCommitTypes.ts +6 -4
- package/src/hooks/timelineTrackVisibility.test.ts +52 -0
- package/src/hooks/timelineTrackVisibility.ts +27 -10
- package/src/hooks/useAppHotkeys.ts +9 -0
- package/src/hooks/useDomEditAttributeCommits.ts +123 -1
- package/src/hooks/useDomEditCommits.ts +29 -175
- package/src/hooks/useDomEditCommitsHelpers.ts +158 -0
- package/src/hooks/useDomEditSession.ts +17 -5
- package/src/hooks/useDomEditTextCommits.ts +14 -9
- package/src/hooks/useDomEditWiring.ts +10 -9
- package/src/hooks/useElementLifecycleOps.ts +8 -3
- package/src/hooks/useFileManager.projectOwnership.test.tsx +98 -0
- package/src/hooks/useFileManager.ts +82 -60
- package/src/hooks/useGsapAnimationOps.ts +7 -6
- package/src/hooks/useGsapKeyframeOps.ts +6 -5
- package/src/hooks/useGsapPropertyDebounce.ts +43 -28
- package/src/hooks/useGsapPropertyDebounceFlush.test.ts +35 -0
- package/src/hooks/useGsapScriptCommits.test.tsx +48 -2
- package/src/hooks/useGsapScriptCommits.ts +150 -50
- package/src/hooks/useGsapSelectionHandlers.test.tsx +116 -0
- package/src/hooks/useGsapSelectionHandlers.ts +94 -31
- package/src/hooks/useInspectorSplitResize.ts +51 -0
- package/src/hooks/usePersistentEditHistory.projectOwnership.test.tsx +60 -0
- package/src/hooks/usePersistentEditHistory.test.ts +49 -1
- package/src/hooks/usePersistentEditHistory.ts +71 -31
- package/src/hooks/useProjectCompositionVariables.ts +10 -10
- package/src/hooks/useProjectSignaturePoll.test.tsx +135 -0
- package/src/hooks/useProjectSignaturePoll.ts +68 -0
- package/src/hooks/useSdkSession.lifecycle.test.tsx +137 -0
- package/src/hooks/useSdkSession.ts +155 -14
- package/src/hooks/useSlideshowPersist.ts +7 -1
- package/src/hooks/useStoryboard.ts +23 -4
- package/src/hooks/useTimelineEditing.test.tsx +4 -1
- package/src/hooks/useTimelineEditing.ts +16 -10
- package/src/hooks/useTimelineEditingTypes.ts +3 -0
- package/src/hooks/useTimelineGroupEditing.ts +16 -2
- package/src/hooks/useVariablesPersist.ts +9 -7
- package/src/icons/SystemIcons.tsx +2 -0
- package/src/styles/studio.css +22 -0
- package/src/styles/tailwind-preset.shared.js +7 -0
- package/src/utils/designInputTracking.test.ts +97 -0
- package/src/utils/designInputTracking.ts +80 -0
- package/src/utils/sdkCutover.gate.test.ts +7 -7
- package/src/utils/sdkCutover.test.ts +490 -57
- package/src/utils/sdkCutover.ts +181 -219
- package/src/utils/sdkEditTransaction.ts +292 -0
- package/src/utils/sdkResolverAttempts.ts +94 -0
- package/src/utils/sdkResolverShadow.test.ts +148 -0
- package/src/utils/sdkResolverShadow.ts +139 -106
- package/src/utils/setSlideshowManifest.test.ts +0 -9
- package/src/utils/setSlideshowManifest.ts +20 -27
- package/src/utils/studioFileHistory.test.ts +41 -0
- package/src/utils/studioFileHistory.ts +30 -27
- package/src/utils/studioFileMutationCoordinator.ts +45 -0
- package/src/utils/studioHelpers.test.ts +45 -0
- package/src/utils/studioHelpers.ts +45 -14
- package/src/utils/studioSaveDiagnostics.ts +17 -0
- package/dist/assets/index-B_UvTX3E.js +0 -423
- package/dist/assets/index-uahwWkgw.css +0 -1
|
@@ -12,11 +12,14 @@ declare namespace studioPreset {
|
|
|
12
12
|
}
|
|
13
13
|
let panel: {
|
|
14
14
|
bg: string;
|
|
15
|
+
"bg-inset": string;
|
|
15
16
|
input: string;
|
|
16
17
|
surface: string;
|
|
17
18
|
hover: string;
|
|
18
19
|
border: string;
|
|
19
20
|
"border-input": string;
|
|
21
|
+
hairline: string;
|
|
22
|
+
"text-0": string;
|
|
20
23
|
"text-1": string;
|
|
21
24
|
"text-2": string;
|
|
22
25
|
"text-3": string;
|
|
@@ -24,6 +27,8 @@ declare namespace studioPreset {
|
|
|
24
27
|
"text-5": string;
|
|
25
28
|
accent: string;
|
|
26
29
|
danger: string;
|
|
30
|
+
media: string;
|
|
31
|
+
container: string;
|
|
27
32
|
};
|
|
28
33
|
}
|
|
29
34
|
}
|
|
@@ -13,18 +13,25 @@ var studioPreset = {
|
|
|
13
13
|
},
|
|
14
14
|
panel: {
|
|
15
15
|
bg: "#0C0C0E",
|
|
16
|
+
// Open inspector-section body — slightly lighter than headers (bg)
|
|
17
|
+
// so the recessed scrollable region reads distinct.
|
|
18
|
+
"bg-inset": "#121214",
|
|
16
19
|
input: "#161618",
|
|
17
20
|
surface: "#18181B",
|
|
18
21
|
hover: "#27272A",
|
|
19
22
|
border: "#1E1E1E",
|
|
20
23
|
"border-input": "#27272A",
|
|
24
|
+
hairline: "#1A1A1C",
|
|
25
|
+
"text-0": "#FAFAFA",
|
|
21
26
|
"text-1": "#E4E4E7",
|
|
22
27
|
"text-2": "#A1A1AA",
|
|
23
28
|
"text-3": "#71717A",
|
|
24
29
|
"text-4": "#52525B",
|
|
25
30
|
"text-5": "#3F3F46",
|
|
26
31
|
accent: "#3CE6AC",
|
|
27
|
-
danger: "#EF4444"
|
|
32
|
+
danger: "#EF4444",
|
|
33
|
+
media: "#00E3FF",
|
|
34
|
+
container: "#F5A623"
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/styles/tailwind-preset.shared.js","../../src/styles/tailwind-preset.ts"],"sourcesContent":["const studioPreset = {\n theme: {\n extend: {\n colors: {\n studio: {\n bg: \"#0a0a0a\",\n surface: \"#141414\",\n border: \"#262626\",\n text: \"#e5e5e5\",\n muted: \"#737373\",\n accent: \"#3CE6AC\",\n },\n panel: {\n bg: \"#0C0C0E\",\n input: \"#161618\",\n surface: \"#18181B\",\n hover: \"#27272A\",\n border: \"#1E1E1E\",\n \"border-input\": \"#27272A\",\n \"text-1\": \"#E4E4E7\",\n \"text-2\": \"#A1A1AA\",\n \"text-3\": \"#71717A\",\n \"text-4\": \"#52525B\",\n \"text-5\": \"#3F3F46\",\n accent: \"#3CE6AC\",\n danger: \"#EF4444\",\n },\n },\n },\n },\n plugins: [],\n};\n\nexport default studioPreset;\n","import studioPreset from \"./tailwind-preset.shared.js\";\n\nexport default studioPreset;\n"],"mappings":";AAAA,IAAM,eAAe;AAAA,EACnB,OAAO;AAAA,IACL,QAAQ;AAAA,MACN,QAAQ;AAAA,QACN,QAAQ;AAAA,UACN,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,QACA,OAAO;AAAA,UACL,IAAI;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/styles/tailwind-preset.shared.js","../../src/styles/tailwind-preset.ts"],"sourcesContent":["const studioPreset = {\n theme: {\n extend: {\n colors: {\n studio: {\n bg: \"#0a0a0a\",\n surface: \"#141414\",\n border: \"#262626\",\n text: \"#e5e5e5\",\n muted: \"#737373\",\n accent: \"#3CE6AC\",\n },\n panel: {\n bg: \"#0C0C0E\",\n // Open inspector-section body — slightly lighter than headers (bg)\n // so the recessed scrollable region reads distinct.\n \"bg-inset\": \"#121214\",\n input: \"#161618\",\n surface: \"#18181B\",\n hover: \"#27272A\",\n border: \"#1E1E1E\",\n \"border-input\": \"#27272A\",\n hairline: \"#1A1A1C\",\n \"text-0\": \"#FAFAFA\",\n \"text-1\": \"#E4E4E7\",\n \"text-2\": \"#A1A1AA\",\n \"text-3\": \"#71717A\",\n \"text-4\": \"#52525B\",\n \"text-5\": \"#3F3F46\",\n accent: \"#3CE6AC\",\n danger: \"#EF4444\",\n media: \"#00E3FF\",\n container: \"#F5A623\",\n },\n },\n },\n },\n plugins: [],\n};\n\nexport default studioPreset;\n","import studioPreset from \"./tailwind-preset.shared.js\";\n\nexport default studioPreset;\n"],"mappings":";AAAA,IAAM,eAAe;AAAA,EACnB,OAAO;AAAA,IACL,QAAQ;AAAA,MACN,QAAQ;AAAA,QACN,QAAQ;AAAA,UACN,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,QACA,OAAO;AAAA,UACL,IAAI;AAAA;AAAA;AAAA,UAGJ,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,gBAAgB;AAAA,UAChB,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,WAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS,CAAC;AACZ;AAEA,IAAO,iCAAQ;;;ACtCf,IAAO,0BAAQ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.59",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"gsap": "^3.13.0",
|
|
47
47
|
"marked": "^14.1.4",
|
|
48
48
|
"mediabunny": "^1.45.3",
|
|
49
|
-
"@hyperframes/core": "0.7.
|
|
50
|
-
"@hyperframes/
|
|
51
|
-
"@hyperframes/
|
|
52
|
-
"@hyperframes/
|
|
53
|
-
"@hyperframes/
|
|
49
|
+
"@hyperframes/core": "0.7.59",
|
|
50
|
+
"@hyperframes/studio-server": "0.7.59",
|
|
51
|
+
"@hyperframes/sdk": "0.7.59",
|
|
52
|
+
"@hyperframes/parsers": "0.7.59",
|
|
53
|
+
"@hyperframes/player": "0.7.59"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/react": "19",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"vite": "^6.4.2",
|
|
66
66
|
"vitest": "^3.2.4",
|
|
67
67
|
"zustand": "^5.0.0",
|
|
68
|
-
"@hyperframes/producer": "0.7.
|
|
68
|
+
"@hyperframes/producer": "0.7.59"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"react": "19",
|
package/src/App.tsx
CHANGED
|
@@ -71,14 +71,12 @@ export function StudioApp() {
|
|
|
71
71
|
const { projectId, resolving, waitingForServer } = useServerConnection();
|
|
72
72
|
const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
|
|
73
73
|
const viewModeValue = useViewModeState();
|
|
74
|
-
|
|
75
74
|
useEffect(() => {
|
|
76
75
|
if (resolving || waitingForServer) return;
|
|
77
76
|
if (hasFiredSessionStart()) return;
|
|
78
77
|
markSessionStartFired();
|
|
79
78
|
trackStudioSessionStart({ has_project: projectId != null });
|
|
80
79
|
}, [projectId, resolving, waitingForServer]);
|
|
81
|
-
|
|
82
80
|
const [activeCompPath, setActiveCompPath] = useState<string | null>(null);
|
|
83
81
|
const [activeCompPathHydrated, setActiveCompPathHydrated] = useState(
|
|
84
82
|
() => initialUrlStateRef.current.activeCompPath == null,
|
|
@@ -176,6 +174,7 @@ export function StudioApp() {
|
|
|
176
174
|
uploadProjectFiles: fileManager.uploadProjectFiles,
|
|
177
175
|
isRecordingRef: isGestureRecordingRef,
|
|
178
176
|
sdkSession: editFlowSdkSession,
|
|
177
|
+
publishSdkSession: sdkHandle.publish,
|
|
179
178
|
forceReloadSdkSession: sdkHandle.forceReload,
|
|
180
179
|
handleDomZIndexReorderCommitRef,
|
|
181
180
|
});
|
|
@@ -317,6 +316,7 @@ export function StudioApp() {
|
|
|
317
316
|
selectSidebarTab: sidebarTabRef.current.select,
|
|
318
317
|
getSidebarTab: sidebarTabRef.current.get,
|
|
319
318
|
sdkSession: editFlowSdkSession,
|
|
319
|
+
publishSdkSession: sdkHandle.publish,
|
|
320
320
|
forceReloadSdkSession: sdkHandle.forceReload,
|
|
321
321
|
});
|
|
322
322
|
domEditSelectionBridgeRef.current = domEditSession.domEditSelection;
|
|
@@ -331,7 +331,6 @@ export function StudioApp() {
|
|
|
331
331
|
domEditSession.domEditSelection,
|
|
332
332
|
domEditSession.domEditGroupSelections,
|
|
333
333
|
);
|
|
334
|
-
|
|
335
334
|
useCaptionDetection({
|
|
336
335
|
projectId,
|
|
337
336
|
activeCompPath,
|
|
@@ -533,6 +532,7 @@ export function StudioApp() {
|
|
|
533
532
|
recordingDuration={gestureRecording.recordingDuration}
|
|
534
533
|
onToggleRecording={recordingToggle}
|
|
535
534
|
sdkSession={sdkHandle.session}
|
|
535
|
+
publishSdkSession={sdkHandle.publish}
|
|
536
536
|
reloadPreview={reloadPreview}
|
|
537
537
|
domEditSaveTimestampRef={domEditSaveTimestampRef}
|
|
538
538
|
recordEdit={editHistory.recordEdit}
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
* composition, so behavior there is unchanged.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type
|
|
12
|
+
import { useCallback, type ReactNode } from "react";
|
|
13
13
|
import type { DomEditSelection } from "./editor/domEditingTypes";
|
|
14
14
|
import { useSdkSession } from "../hooks/useSdkSession";
|
|
15
15
|
import { useVariablesPersist, type UseVariablesPersistParams } from "../hooks/useVariablesPersist";
|
|
16
16
|
import { VariablePromoteProvider } from "../contexts/VariablePromoteContext";
|
|
17
|
+
import { getStudioSaveErrorMessage } from "../utils/studioSaveDiagnostics";
|
|
17
18
|
|
|
18
19
|
/** Persist wiring minus the target — this provider derives the target from the selection. */
|
|
19
20
|
type PersistDeps = Omit<UseVariablesPersistParams, "sdkSession" | "activeCompPath">;
|
|
@@ -22,12 +23,14 @@ export function DesignPanelPromoteProvider({
|
|
|
22
23
|
selection,
|
|
23
24
|
projectId,
|
|
24
25
|
activeCompPath,
|
|
26
|
+
showToast,
|
|
25
27
|
children,
|
|
26
28
|
...persistDeps
|
|
27
29
|
}: PersistDeps & {
|
|
28
30
|
selection: DomEditSelection | null;
|
|
29
31
|
projectId: string | null;
|
|
30
32
|
activeCompPath: string | null;
|
|
33
|
+
showToast: (message: string, tone?: "error" | "info") => void;
|
|
31
34
|
children: ReactNode;
|
|
32
35
|
}) {
|
|
33
36
|
const targetPath = selection?.sourceFile || activeCompPath || "index.html";
|
|
@@ -35,10 +38,20 @@ export function DesignPanelPromoteProvider({
|
|
|
35
38
|
const persist = useVariablesPersist({
|
|
36
39
|
...persistDeps,
|
|
37
40
|
sdkSession: handle.session,
|
|
41
|
+
publishSdkSession: handle.publish,
|
|
38
42
|
activeCompPath: targetPath,
|
|
39
43
|
});
|
|
44
|
+
const handlePersistError = useCallback(
|
|
45
|
+
(error: unknown) => showToast(getStudioSaveErrorMessage(error), "error"),
|
|
46
|
+
[showToast],
|
|
47
|
+
);
|
|
40
48
|
return (
|
|
41
|
-
<VariablePromoteProvider
|
|
49
|
+
<VariablePromoteProvider
|
|
50
|
+
session={handle.session}
|
|
51
|
+
selection={selection}
|
|
52
|
+
persist={persist}
|
|
53
|
+
onPersistError={handlePersistError}
|
|
54
|
+
>
|
|
42
55
|
{children}
|
|
43
56
|
</VariablePromoteProvider>
|
|
44
57
|
);
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useCallback,
|
|
3
|
-
useEffect,
|
|
4
|
-
useMemo,
|
|
5
|
-
useRef,
|
|
6
|
-
useState,
|
|
7
|
-
type MutableRefObject,
|
|
8
|
-
type PointerEvent as ReactPointerEvent,
|
|
9
|
-
} from "react";
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, type MutableRefObject } from "react";
|
|
10
2
|
import { PropertyPanel } from "./editor/PropertyPanel";
|
|
11
3
|
import { LayersPanel } from "./editor/LayersPanel";
|
|
12
4
|
import { CaptionPropertyPanel } from "../captions/components/CaptionPropertyPanel";
|
|
@@ -23,7 +15,7 @@ import type { IframeWindow } from "../player/lib/playbackTypes";
|
|
|
23
15
|
import { STUDIO_INSPECTOR_PANELS_ENABLED } from "./editor/manualEditingAvailability";
|
|
24
16
|
import type { Composition } from "@hyperframes/sdk";
|
|
25
17
|
import type { EditHistoryKind } from "../utils/editHistory";
|
|
26
|
-
import { useSlideshowPersist } from "../hooks/useSlideshowPersist";
|
|
18
|
+
import { useSlideshowPersist, type UseSlideshowPersistParams } from "../hooks/useSlideshowPersist";
|
|
27
19
|
import { DesignPanelPromoteProvider } from "./DesignPanelPromoteProvider";
|
|
28
20
|
|
|
29
21
|
import { useStudioPlaybackContext, useStudioShellContext } from "../contexts/StudioContext";
|
|
@@ -38,9 +30,8 @@ import {
|
|
|
38
30
|
type ColorGradingScope,
|
|
39
31
|
} from "./studioColorGradingScope";
|
|
40
32
|
import type { BackgroundRemovalProgress } from "./editor/propertyPanelTypes";
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const MAX_INSPECTOR_SPLIT_PERCENT = 75;
|
|
33
|
+
import { timelineKeysForSelections, type ToggleHiddenHandler } from "../utils/studioHelpers";
|
|
34
|
+
import { useInspectorSplitResize } from "../hooks/useInspectorSplitResize";
|
|
44
35
|
|
|
45
36
|
export interface StudioRightPanelProps {
|
|
46
37
|
designPanelActive: boolean;
|
|
@@ -56,6 +47,7 @@ export interface StudioRightPanelProps {
|
|
|
56
47
|
onToggleRecording?: () => void;
|
|
57
48
|
/** Dependencies for the Slideshow persist callback, threaded from App.tsx. */
|
|
58
49
|
sdkSession: Composition | null;
|
|
50
|
+
publishSdkSession: NonNullable<UseSlideshowPersistParams["publishSdkSession"]>;
|
|
59
51
|
reloadPreview: () => void;
|
|
60
52
|
domEditSaveTimestampRef: MutableRefObject<number>;
|
|
61
53
|
recordEdit: (entry: {
|
|
@@ -63,7 +55,7 @@ export interface StudioRightPanelProps {
|
|
|
63
55
|
kind: EditHistoryKind;
|
|
64
56
|
files: Record<string, { before: string; after: string }>;
|
|
65
57
|
}) => Promise<void>;
|
|
66
|
-
onToggleElementHidden?:
|
|
58
|
+
onToggleElementHidden?: ToggleHiddenHandler;
|
|
67
59
|
}
|
|
68
60
|
|
|
69
61
|
// fallow-ignore-next-line complexity
|
|
@@ -75,6 +67,7 @@ export function StudioRightPanel({
|
|
|
75
67
|
recordingDuration,
|
|
76
68
|
onToggleRecording,
|
|
77
69
|
sdkSession,
|
|
70
|
+
publishSdkSession,
|
|
78
71
|
reloadPreview,
|
|
79
72
|
domEditSaveTimestampRef,
|
|
80
73
|
recordEdit,
|
|
@@ -109,10 +102,12 @@ export function StudioRightPanel({
|
|
|
109
102
|
copiedAgentPrompt,
|
|
110
103
|
clearDomSelection,
|
|
111
104
|
handleUngroupSelection,
|
|
105
|
+
handleGroupSelection,
|
|
112
106
|
handleDomStyleCommit,
|
|
113
107
|
handleDomAttributeCommit,
|
|
114
108
|
handleDomAttributeLiveCommit,
|
|
115
109
|
handleDomHtmlAttributeCommit,
|
|
110
|
+
handleDomAttributesCommit,
|
|
116
111
|
handleDomPathOffsetCommit,
|
|
117
112
|
handleDomBoxSizeCommit,
|
|
118
113
|
handleDomRotationCommit,
|
|
@@ -167,6 +162,7 @@ export function StudioRightPanel({
|
|
|
167
162
|
recordEdit,
|
|
168
163
|
reloadPreview,
|
|
169
164
|
domEditSaveTimestampRef,
|
|
165
|
+
publishSdkSession,
|
|
170
166
|
});
|
|
171
167
|
|
|
172
168
|
// Notes path: persists are debounced in SlideshowPanel; coalesceKey ensures
|
|
@@ -179,16 +175,17 @@ export function StudioRightPanel({
|
|
|
179
175
|
recordEdit,
|
|
180
176
|
reloadPreview,
|
|
181
177
|
domEditSaveTimestampRef,
|
|
178
|
+
publishSdkSession,
|
|
182
179
|
coalesceKey: activeCompPath ? `slideshow-notes:${activeCompPath}` : "slideshow-notes",
|
|
183
180
|
});
|
|
184
181
|
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
182
|
+
const {
|
|
183
|
+
layersPanePercent,
|
|
184
|
+
splitContainerRef,
|
|
185
|
+
handleInspectorSplitResizeStart,
|
|
186
|
+
handleInspectorSplitResizeMove,
|
|
187
|
+
handleInspectorSplitResizeEnd,
|
|
188
|
+
} = useInspectorSplitResize();
|
|
192
189
|
const backgroundRemovalAbortRef = useRef<AbortController | null>(null);
|
|
193
190
|
|
|
194
191
|
useEffect(
|
|
@@ -229,35 +226,6 @@ export function StudioRightPanel({
|
|
|
229
226
|
toggleRightInspectorPane(pane);
|
|
230
227
|
};
|
|
231
228
|
|
|
232
|
-
const handleInspectorSplitResizeStart = useCallback(
|
|
233
|
-
(event: ReactPointerEvent<HTMLDivElement>) => {
|
|
234
|
-
event.preventDefault();
|
|
235
|
-
event.currentTarget.setPointerCapture(event.pointerId);
|
|
236
|
-
const height = splitContainerRef.current?.getBoundingClientRect().height ?? 0;
|
|
237
|
-
splitDragRef.current = {
|
|
238
|
-
startY: event.clientY,
|
|
239
|
-
startPercent: layersPanePercent,
|
|
240
|
-
height,
|
|
241
|
-
};
|
|
242
|
-
},
|
|
243
|
-
[layersPanePercent],
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
const handleInspectorSplitResizeMove = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {
|
|
247
|
-
const drag = splitDragRef.current;
|
|
248
|
-
if (!drag || drag.height <= 0) return;
|
|
249
|
-
const deltaPercent = ((event.clientY - drag.startY) / drag.height) * 100;
|
|
250
|
-
const next = Math.min(
|
|
251
|
-
MAX_INSPECTOR_SPLIT_PERCENT,
|
|
252
|
-
Math.max(MIN_INSPECTOR_SPLIT_PERCENT, drag.startPercent + deltaPercent),
|
|
253
|
-
);
|
|
254
|
-
setLayersPanePercent(next);
|
|
255
|
-
}, []);
|
|
256
|
-
|
|
257
|
-
const handleInspectorSplitResizeEnd = useCallback(() => {
|
|
258
|
-
splitDragRef.current = null;
|
|
259
|
-
}, []);
|
|
260
|
-
|
|
261
229
|
const handleApplyColorGradingScope = useCallback(
|
|
262
230
|
async (scope: ColorGradingScope, value: string | null) =>
|
|
263
231
|
applyColorGradingScopeUpdate({
|
|
@@ -341,12 +309,17 @@ export function StudioRightPanel({
|
|
|
341
309
|
},
|
|
342
310
|
[projectId, refreshFileTree, showToast],
|
|
343
311
|
);
|
|
344
|
-
|
|
312
|
+
const handleHideAllSelected = () => {
|
|
313
|
+
const { elements } = usePlayerStore.getState();
|
|
314
|
+
const keys = timelineKeysForSelections(domEditGroupSelections, elements, activeCompPath);
|
|
315
|
+
if (keys.length > 0) void onToggleElementHidden?.(keys, true);
|
|
316
|
+
};
|
|
345
317
|
const propertyPanel = (
|
|
346
318
|
<DesignPanelPromoteProvider
|
|
347
319
|
selection={domEditGroupSelections.length > 1 ? null : domEditSelection}
|
|
348
320
|
projectId={projectId}
|
|
349
321
|
activeCompPath={activeCompPath}
|
|
322
|
+
showToast={showToast}
|
|
350
323
|
readProjectFile={readProjectFile}
|
|
351
324
|
writeProjectFile={writeProjectFile}
|
|
352
325
|
recordEdit={recordEdit}
|
|
@@ -359,12 +332,16 @@ export function StudioRightPanel({
|
|
|
359
332
|
assets={assets}
|
|
360
333
|
element={domEditGroupSelections.length > 1 ? null : domEditSelection}
|
|
361
334
|
multiSelectCount={domEditGroupSelections.length}
|
|
335
|
+
multiSelectedElements={domEditGroupSelections}
|
|
336
|
+
onGroupSelection={handleGroupSelection}
|
|
337
|
+
onHideAllSelected={handleHideAllSelected}
|
|
362
338
|
copiedAgentPrompt={copiedAgentPrompt}
|
|
363
339
|
onClearSelection={clearDomSelection}
|
|
364
340
|
onToggleElementHidden={onToggleElementHidden}
|
|
365
341
|
onUngroup={handleUngroupSelection}
|
|
366
342
|
onSetStyle={handleDomStyleCommit}
|
|
367
343
|
onSetAttribute={handleDomAttributeCommit}
|
|
344
|
+
onSetAttributes={handleDomAttributesCommit}
|
|
368
345
|
onSetAttributeLive={handleDomAttributeLiveCommit}
|
|
369
346
|
onApplyColorGradingScope={handleApplyColorGradingScope}
|
|
370
347
|
onSetHtmlAttribute={handleDomHtmlAttributeCommit}
|
|
@@ -534,6 +511,7 @@ export function StudioRightPanel({
|
|
|
534
511
|
) : rightPanelTab === "variables" ? (
|
|
535
512
|
<VariablesPanel
|
|
536
513
|
sdkSession={sdkSession}
|
|
514
|
+
publishSdkSession={publishSdkSession}
|
|
537
515
|
reloadPreview={reloadPreview}
|
|
538
516
|
domEditSaveTimestampRef={domEditSaveTimestampRef}
|
|
539
517
|
recordEdit={recordEdit}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import React, { act } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { AnimationCard } from "./AnimationCard";
|
|
7
|
+
import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
|
8
|
+
|
|
9
|
+
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
document.body.innerHTML = "";
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function baseAnimation(overrides: Partial<GsapAnimation> = {}): GsapAnimation {
|
|
16
|
+
return {
|
|
17
|
+
id: "anim-1",
|
|
18
|
+
method: "to",
|
|
19
|
+
position: 0.8,
|
|
20
|
+
duration: 1.2,
|
|
21
|
+
ease: "power2.out",
|
|
22
|
+
properties: { opacity: 1 },
|
|
23
|
+
...overrides,
|
|
24
|
+
} as GsapAnimation;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const noop = () => {};
|
|
28
|
+
|
|
29
|
+
describe("AnimationCard flat branch", () => {
|
|
30
|
+
it("renders a mint border-left and panel-token colors when flat", () => {
|
|
31
|
+
const host = document.createElement("div");
|
|
32
|
+
document.body.append(host);
|
|
33
|
+
const root = createRoot(host);
|
|
34
|
+
act(() => {
|
|
35
|
+
root.render(
|
|
36
|
+
<AnimationCard
|
|
37
|
+
animation={baseAnimation()}
|
|
38
|
+
defaultExpanded={false}
|
|
39
|
+
flat
|
|
40
|
+
onUpdateProperty={noop}
|
|
41
|
+
onUpdateMeta={noop}
|
|
42
|
+
onDeleteAnimation={noop}
|
|
43
|
+
onAddProperty={noop}
|
|
44
|
+
onRemoveProperty={noop}
|
|
45
|
+
/>,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
const card = host.querySelector('[data-flat-effect-card="true"]');
|
|
49
|
+
expect(card).not.toBeNull();
|
|
50
|
+
expect(card?.className).toContain("border-panel-accent");
|
|
51
|
+
act(() => root.unmount());
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("still renders the legacy (non-flat) appearance when flat is omitted", () => {
|
|
55
|
+
const host = document.createElement("div");
|
|
56
|
+
document.body.append(host);
|
|
57
|
+
const root = createRoot(host);
|
|
58
|
+
act(() => {
|
|
59
|
+
root.render(
|
|
60
|
+
<AnimationCard
|
|
61
|
+
animation={baseAnimation()}
|
|
62
|
+
defaultExpanded={false}
|
|
63
|
+
onUpdateProperty={noop}
|
|
64
|
+
onUpdateMeta={noop}
|
|
65
|
+
onDeleteAnimation={noop}
|
|
66
|
+
onAddProperty={noop}
|
|
67
|
+
onRemoveProperty={noop}
|
|
68
|
+
/>,
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
expect(host.querySelector('[data-flat-effect-card="true"]')).toBeNull();
|
|
72
|
+
expect(host.textContent).toContain("power2.out");
|
|
73
|
+
act(() => root.unmount());
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("toggles expanded state when the collapsed header button is clicked, in both modes", () => {
|
|
77
|
+
for (const flat of [false, true]) {
|
|
78
|
+
const host = document.createElement("div");
|
|
79
|
+
document.body.append(host);
|
|
80
|
+
const root = createRoot(host);
|
|
81
|
+
act(() => {
|
|
82
|
+
root.render(
|
|
83
|
+
<AnimationCard
|
|
84
|
+
animation={baseAnimation()}
|
|
85
|
+
defaultExpanded={false}
|
|
86
|
+
flat={flat || undefined}
|
|
87
|
+
onUpdateProperty={noop}
|
|
88
|
+
onUpdateMeta={noop}
|
|
89
|
+
onDeleteAnimation={noop}
|
|
90
|
+
onAddProperty={noop}
|
|
91
|
+
onRemoveProperty={noop}
|
|
92
|
+
/>,
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
expect(host.textContent).not.toContain("Remove");
|
|
96
|
+
const button = host.querySelector("button");
|
|
97
|
+
expect(button).not.toBeNull();
|
|
98
|
+
act(() => {
|
|
99
|
+
button?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
100
|
+
});
|
|
101
|
+
expect(host.textContent).toContain("Remove");
|
|
102
|
+
act(() => root.unmount());
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("invokes onDeleteAnimation with the animation id when Remove is clicked, in flat mode", () => {
|
|
107
|
+
const onDeleteAnimation = vi.fn();
|
|
108
|
+
const host = document.createElement("div");
|
|
109
|
+
document.body.append(host);
|
|
110
|
+
const root = createRoot(host);
|
|
111
|
+
act(() => {
|
|
112
|
+
root.render(
|
|
113
|
+
<AnimationCard
|
|
114
|
+
animation={baseAnimation()}
|
|
115
|
+
defaultExpanded={true}
|
|
116
|
+
flat
|
|
117
|
+
onUpdateProperty={noop}
|
|
118
|
+
onUpdateMeta={noop}
|
|
119
|
+
onDeleteAnimation={onDeleteAnimation}
|
|
120
|
+
onAddProperty={noop}
|
|
121
|
+
onRemoveProperty={noop}
|
|
122
|
+
/>,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
const buttons = Array.from(host.querySelectorAll("button"));
|
|
126
|
+
const removeButton = buttons.find((b) => b.textContent === "Remove");
|
|
127
|
+
expect(removeButton).not.toBeUndefined();
|
|
128
|
+
act(() => {
|
|
129
|
+
removeButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
130
|
+
});
|
|
131
|
+
expect(onDeleteAnimation).toHaveBeenCalledWith("anim-1");
|
|
132
|
+
act(() => root.unmount());
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -21,12 +21,14 @@ import {
|
|
|
21
21
|
interface AnimationCardProps extends GsapAnimationEditCallbacks {
|
|
22
22
|
animation: GsapAnimation;
|
|
23
23
|
defaultExpanded: boolean;
|
|
24
|
+
flat?: boolean;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
// fallow-ignore-next-line complexity
|
|
27
28
|
export const AnimationCard = memo(function AnimationCard({
|
|
28
29
|
animation,
|
|
29
30
|
defaultExpanded,
|
|
31
|
+
flat,
|
|
30
32
|
onUpdateProperty,
|
|
31
33
|
onUpdateMeta,
|
|
32
34
|
onDeleteAnimation,
|
|
@@ -150,7 +152,14 @@ export const AnimationCard = memo(function AnimationCard({
|
|
|
150
152
|
);
|
|
151
153
|
|
|
152
154
|
return (
|
|
153
|
-
<div
|
|
155
|
+
<div
|
|
156
|
+
data-flat-effect-card={flat ? "true" : undefined}
|
|
157
|
+
className={
|
|
158
|
+
flat
|
|
159
|
+
? "border-b border-l-2 border-panel-accent/40 border-b-panel-hairline pb-3 pl-2"
|
|
160
|
+
: "border-b border-neutral-800 pb-3"
|
|
161
|
+
}
|
|
162
|
+
>
|
|
154
163
|
<button
|
|
155
164
|
type="button"
|
|
156
165
|
onClick={() => setExpanded((v) => !v)}
|
|
@@ -162,13 +171,19 @@ export const AnimationCard = memo(function AnimationCard({
|
|
|
162
171
|
>
|
|
163
172
|
{methodLabel}
|
|
164
173
|
</span>
|
|
165
|
-
<span
|
|
174
|
+
<span
|
|
175
|
+
className={`text-[11px] font-medium ${flat ? "text-panel-text-3" : "text-neutral-400"}`}
|
|
176
|
+
title="When this effect plays"
|
|
177
|
+
>
|
|
166
178
|
{typeof animation.position === "number"
|
|
167
179
|
? `${parseFloat(animation.position.toFixed(3))}s`
|
|
168
180
|
: animation.position}{" "}
|
|
169
181
|
– {typeof endTime === "number" ? `${parseFloat(endTime.toFixed(3))}s` : endTime}
|
|
170
182
|
</span>
|
|
171
|
-
<span
|
|
183
|
+
<span
|
|
184
|
+
className={`ml-auto text-[10px] ${flat ? "text-panel-text-3" : "text-neutral-500"}`}
|
|
185
|
+
title={easeName}
|
|
186
|
+
>
|
|
172
187
|
{easeLabel}
|
|
173
188
|
</span>
|
|
174
189
|
<svg
|
|
@@ -176,7 +191,7 @@ export const AnimationCard = memo(function AnimationCard({
|
|
|
176
191
|
height="10"
|
|
177
192
|
viewBox="0 0 10 10"
|
|
178
193
|
fill="currentColor"
|
|
179
|
-
className={`flex-shrink-0 text-neutral-500
|
|
194
|
+
className={`flex-shrink-0 transition-transform ${flat ? "text-panel-text-5" : "text-neutral-500"} ${expanded ? "" : "-rotate-90"}`}
|
|
180
195
|
>
|
|
181
196
|
<path d="M2 3l3 4 3-4z" />
|
|
182
197
|
</svg>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useTrackDesignInput } from "../../contexts/DesignPanelInputContext";
|
|
2
|
+
|
|
1
3
|
export type GestureRecordingState = "idle" | "recording" | "preview";
|
|
2
4
|
|
|
3
5
|
interface GestureRecordIconProps {
|
|
@@ -28,13 +30,17 @@ export function GestureRecordPanelButton({
|
|
|
28
30
|
onToggleRecording,
|
|
29
31
|
}: GestureRecordPanelButtonProps) {
|
|
30
32
|
const recording = recordingState === "recording";
|
|
33
|
+
const track = useTrackDesignInput();
|
|
31
34
|
|
|
32
35
|
return (
|
|
33
36
|
<div className="px-4 pb-3">
|
|
34
37
|
<button
|
|
35
38
|
type="button"
|
|
36
39
|
onMouseDown={(e) => e.preventDefault()}
|
|
37
|
-
onClick={
|
|
40
|
+
onClick={() => {
|
|
41
|
+
track("button", "Gesture recording");
|
|
42
|
+
onToggleRecording();
|
|
43
|
+
}}
|
|
38
44
|
className={`w-full flex items-center justify-center gap-2 rounded-lg py-2 text-[11px] font-medium transition-colors ${
|
|
39
45
|
recording
|
|
40
46
|
? "bg-red-500/15 text-red-400 border border-red-500/30 animate-pulse"
|