@hyperframes/studio 0.7.60 → 0.7.61
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-Xvx2hkrc.js +459 -0
- package/dist/assets/{index-cH6NfVV_.js → index-2mxh_HSy.js} +177 -177
- package/dist/assets/{index-D6etaey-.js → index-BCpoiv9S.js} +1 -1
- package/dist/assets/index-BhWig0mx.css +1 -0
- package/dist/assets/{index-Dh_WhagG.js → index-D-uFclFj.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/index.js +1202 -673
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +3 -8
- package/src/components/DesignPanelPromoteProvider.tsx +27 -1
- package/src/components/StudioHeader.tsx +2 -3
- package/src/components/StudioRightPanel.tsx +34 -26
- package/src/components/editor/PromotableControl.tsx +4 -2
- package/src/components/editor/PropertyPanel.tsx +151 -149
- package/src/components/editor/PropertyPanelFlat.tsx +44 -42
- package/src/components/editor/manualEditingAvailability.test.ts +6 -6
- package/src/components/editor/manualEditingAvailability.ts +5 -3
- package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +10 -0
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +1 -0
- package/src/components/editor/propertyPanelFlatStyleHelpers.ts +0 -13
- package/src/components/editor/propertyPanelFlatStyleSections.test.tsx +21 -9
- package/src/components/editor/propertyPanelFlatStyleSections.tsx +15 -32
- package/src/components/editor/propertyPanelFlatTextSection.tsx +2 -2
- package/src/components/editor/propertyPanelInputCoverage.test.tsx +14 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +9 -1
- package/src/components/storyboard/AgentChatMessageButton.test.tsx +47 -0
- package/src/components/storyboard/AgentChatMessageButton.tsx +45 -0
- package/src/components/storyboard/StoryboardFrameFocus.tsx +189 -66
- package/src/components/storyboard/StoryboardLoaded.tsx +121 -23
- package/src/components/storyboard/StoryboardReviewGuide.tsx +300 -0
- package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +170 -0
- package/src/components/storyboard/storyboardReviewStage.test.ts +40 -0
- package/src/components/storyboard/storyboardReviewStage.ts +45 -0
- package/src/components/storyboard/useFrameComments.ts +22 -6
- package/src/contexts/ViewModeContext.tsx +60 -7
- package/src/hooks/useAddAssetAtPlayhead.test.ts +44 -0
- package/src/hooks/useAddAssetAtPlayhead.ts +21 -0
- package/src/hooks/useSlideshowTabState.test.ts +96 -0
- package/src/hooks/useSlideshowTabState.ts +61 -0
- package/src/player/lib/playbackTypes.ts +7 -0
- package/src/player/lib/runtimeProtocol.test.ts +6 -1
- package/src/player/lib/timelineDOM.ts +8 -11
- package/src/player/lib/timelineIframeHelpers.ts +153 -107
- package/dist/assets/hyperframes-player-3XTTaVNf.js +0 -459
- package/dist/assets/index-DXbu6IPT.css +0 -1
- package/src/components/editor/propertyPanelFlatStyleHelpers.test.ts +0 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.61",
|
|
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/studio-server": "0.7.
|
|
49
|
+
"@hyperframes/core": "0.7.61",
|
|
50
|
+
"@hyperframes/parsers": "0.7.61",
|
|
51
|
+
"@hyperframes/player": "0.7.61",
|
|
52
|
+
"@hyperframes/sdk": "0.7.61",
|
|
53
|
+
"@hyperframes/studio-server": "0.7.61"
|
|
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.61"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"react": "19",
|
package/src/App.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import { useDomEditSession } from "./hooks/useDomEditSession";
|
|
|
23
23
|
import { useSdkSelectionSync } from "./hooks/useSdkSelectionSync";
|
|
24
24
|
import { useStudioSdkSessions } from "./hooks/useStudioSdkSessions";
|
|
25
25
|
import { useBlockHandlers } from "./hooks/useBlockHandlers";
|
|
26
|
+
import { useAddAssetAtPlayhead } from "./hooks/useAddAssetAtPlayhead";
|
|
26
27
|
import { useAppHotkeys } from "./hooks/useAppHotkeys";
|
|
27
28
|
import { useClipboard } from "./hooks/useClipboard";
|
|
28
29
|
import { deleteSelectedKeyframes } from "./hooks/timelineEditingHelpers";
|
|
@@ -195,14 +196,7 @@ export function StudioApp() {
|
|
|
195
196
|
},
|
|
196
197
|
[timelineEditing.handleTimelineGroupMove],
|
|
197
198
|
);
|
|
198
|
-
const handleAddAssetAtPlayhead =
|
|
199
|
-
(assetPath: string) =>
|
|
200
|
-
timelineEditing.handleTimelineAssetDrop(assetPath, {
|
|
201
|
-
start: usePlayerStore.getState().currentTime,
|
|
202
|
-
track: 0,
|
|
203
|
-
}),
|
|
204
|
-
[timelineEditing],
|
|
205
|
-
);
|
|
199
|
+
const handleAddAssetAtPlayhead = useAddAssetAtPlayhead(timelineEditing.handleTimelineAssetDrop);
|
|
206
200
|
const {
|
|
207
201
|
activeBlockParams,
|
|
208
202
|
setActiveBlockParams,
|
|
@@ -533,6 +527,7 @@ export function StudioApp() {
|
|
|
533
527
|
onToggleRecording={recordingToggle}
|
|
534
528
|
sdkSession={sdkHandle.session}
|
|
535
529
|
publishSdkSession={sdkHandle.publish}
|
|
530
|
+
forceReloadSdkSession={sdkHandle.forceReload}
|
|
536
531
|
reloadPreview={reloadPreview}
|
|
537
532
|
domEditSaveTimestampRef={domEditSaveTimestampRef}
|
|
538
533
|
recordEdit={editHistory.recordEdit}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { useCallback, type ReactNode } from "react";
|
|
13
|
+
import type { Composition } from "@hyperframes/sdk";
|
|
13
14
|
import type { DomEditSelection } from "./editor/domEditingTypes";
|
|
14
15
|
import { useSdkSession } from "../hooks/useSdkSession";
|
|
15
16
|
import { useVariablesPersist, type UseVariablesPersistParams } from "../hooks/useVariablesPersist";
|
|
@@ -24,6 +25,7 @@ export function DesignPanelPromoteProvider({
|
|
|
24
25
|
projectId,
|
|
25
26
|
activeCompPath,
|
|
26
27
|
showToast,
|
|
28
|
+
forceReloadSharedSdkSession,
|
|
27
29
|
children,
|
|
28
30
|
...persistDeps
|
|
29
31
|
}: PersistDeps & {
|
|
@@ -31,16 +33,40 @@ export function DesignPanelPromoteProvider({
|
|
|
31
33
|
projectId: string | null;
|
|
32
34
|
activeCompPath: string | null;
|
|
33
35
|
showToast: (message: string, tone?: "error" | "info") => void;
|
|
36
|
+
/**
|
|
37
|
+
* Forces the app's SHARED SDK session (Variables tab, Slideshow, etc.) to
|
|
38
|
+
* re-open from disk. This provider opens its OWN session, separate from
|
|
39
|
+
* that shared one, so a persist through it never fires the shared session's
|
|
40
|
+
* own "change" event. When the target happens to be the same file the
|
|
41
|
+
* shared session already has open, that session is left holding stale
|
|
42
|
+
* in-memory content after a successful persist — worse, the self-write-echo
|
|
43
|
+
* registry that would normally reload it on the next file-change
|
|
44
|
+
* notification is keyed by file path only (not by session instance), so it
|
|
45
|
+
* mistakes this provider's write for its own echo and stays stale
|
|
46
|
+
* indefinitely. Called unconditionally (not gated on targetPath matching
|
|
47
|
+
* activeCompPath): re-opening a file that didn't change is a harmless
|
|
48
|
+
* no-op re-parse, cheaper than the bug class a subtly-wrong path guard
|
|
49
|
+
* could reintroduce.
|
|
50
|
+
*/
|
|
51
|
+
forceReloadSharedSdkSession?: () => void;
|
|
34
52
|
children: ReactNode;
|
|
35
53
|
}) {
|
|
36
54
|
const targetPath = selection?.sourceFile || activeCompPath || "index.html";
|
|
37
55
|
const handle = useSdkSession(projectId, targetPath, persistDeps.domEditSaveTimestampRef);
|
|
38
|
-
const
|
|
56
|
+
const rawPersist = useVariablesPersist({
|
|
39
57
|
...persistDeps,
|
|
40
58
|
sdkSession: handle.session,
|
|
41
59
|
publishSdkSession: handle.publish,
|
|
42
60
|
activeCompPath: targetPath,
|
|
43
61
|
});
|
|
62
|
+
const persist = useCallback(
|
|
63
|
+
async (label: string, mutate: (session: Composition) => void) => {
|
|
64
|
+
const committed = await rawPersist(label, mutate);
|
|
65
|
+
if (committed) forceReloadSharedSdkSession?.();
|
|
66
|
+
return committed;
|
|
67
|
+
},
|
|
68
|
+
[rawPersist, forceReloadSharedSdkSession],
|
|
69
|
+
);
|
|
44
70
|
const handlePersistError = useCallback(
|
|
45
71
|
(error: unknown) => showToast(getStudioSaveErrorMessage(error), "error"),
|
|
46
72
|
[showToast],
|
|
@@ -149,14 +149,13 @@ const VIEW_MODE_OPTIONS: Array<{ mode: StudioViewMode; label: string }> = [
|
|
|
149
149
|
];
|
|
150
150
|
|
|
151
151
|
/** Segmented control switching the main stage between storyboard and preview. */
|
|
152
|
-
function ViewModeToggle() {
|
|
152
|
+
export function ViewModeToggle() {
|
|
153
153
|
const { viewMode, setViewMode } = useViewMode();
|
|
154
154
|
const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);
|
|
155
155
|
|
|
156
156
|
const selectMode = (mode: StudioViewMode) => {
|
|
157
157
|
if (mode === viewMode) return;
|
|
158
|
-
trackStudioEvent("view_mode_toggle", { mode });
|
|
159
|
-
setViewMode(mode);
|
|
158
|
+
if (setViewMode(mode)) trackStudioEvent("view_mode_toggle", { mode });
|
|
160
159
|
};
|
|
161
160
|
|
|
162
161
|
// Complete APG tabs pattern: roving tabIndex + arrow-key navigation.
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { useCallback, useEffect,
|
|
1
|
+
import { useCallback, useEffect, useRef, type MutableRefObject } from "react";
|
|
2
2
|
import { PropertyPanel } from "./editor/PropertyPanel";
|
|
3
3
|
import { LayersPanel } from "./editor/LayersPanel";
|
|
4
4
|
import { CaptionPropertyPanel } from "../captions/components/CaptionPropertyPanel";
|
|
5
5
|
import { BlockParamsPanel } from "./editor/BlockParamsPanel";
|
|
6
6
|
import { RenderQueue } from "./renders/RenderQueue";
|
|
7
7
|
import { SlideshowPanel } from "./panels/SlideshowPanel";
|
|
8
|
-
import type { SceneInfo } from "./panels/SlideshowPanel";
|
|
9
8
|
import { VariablesPanel } from "./panels/VariablesPanel";
|
|
10
9
|
import { PanelTabButton } from "./PanelTabButton";
|
|
11
10
|
import { usePreviewVariablesStore } from "../hooks/previewVariablesStore";
|
|
12
11
|
import type { RenderJob } from "./renders/useRenderQueue";
|
|
13
12
|
import type { BlockParam } from "@hyperframes/core/registry";
|
|
14
|
-
import type { IframeWindow } from "../player/lib/playbackTypes";
|
|
15
13
|
import {
|
|
16
14
|
STUDIO_FLAT_INSPECTOR_ENABLED,
|
|
17
15
|
STUDIO_INSPECTOR_PANELS_ENABLED,
|
|
@@ -19,6 +17,7 @@ import {
|
|
|
19
17
|
import type { Composition } from "@hyperframes/sdk";
|
|
20
18
|
import type { EditHistoryKind } from "../utils/editHistory";
|
|
21
19
|
import { useSlideshowPersist, type UseSlideshowPersistParams } from "../hooks/useSlideshowPersist";
|
|
20
|
+
import { useSlideshowTabState } from "../hooks/useSlideshowTabState";
|
|
22
21
|
import { DesignPanelPromoteProvider } from "./DesignPanelPromoteProvider";
|
|
23
22
|
|
|
24
23
|
import { useStudioPlaybackContext, useStudioShellContext } from "../contexts/StudioContext";
|
|
@@ -51,6 +50,19 @@ export interface StudioRightPanelProps {
|
|
|
51
50
|
/** Dependencies for the Slideshow persist callback, threaded from App.tsx. */
|
|
52
51
|
sdkSession: Composition | null;
|
|
53
52
|
publishSdkSession: NonNullable<UseSlideshowPersistParams["publishSdkSession"]>;
|
|
53
|
+
/**
|
|
54
|
+
* Forces THIS `sdkSession` to re-open from disk. DesignPanelPromoteProvider
|
|
55
|
+
* opens its own separate SDK session scoped to the selected element's own
|
|
56
|
+
* file (needed so promoting inside a sub-composition binds a variable there,
|
|
57
|
+
* not on the host) — for a top-level selection that's the SAME file this
|
|
58
|
+
* session already has open, so a write through that other session leaves
|
|
59
|
+
* this one holding stale in-memory content. The self-write-echo registry
|
|
60
|
+
* that normally suppresses redundant reloads is keyed by file path only, not
|
|
61
|
+
* by session instance, so it wrongly treats the sibling session's write as
|
|
62
|
+
* "our own echo" and never reloads on its own — this must be called
|
|
63
|
+
* explicitly after such a write.
|
|
64
|
+
*/
|
|
65
|
+
forceReloadSdkSession?: () => void;
|
|
54
66
|
reloadPreview: () => void;
|
|
55
67
|
domEditSaveTimestampRef: MutableRefObject<number>;
|
|
56
68
|
recordEdit: (entry: {
|
|
@@ -71,6 +83,7 @@ export function StudioRightPanel({
|
|
|
71
83
|
onToggleRecording,
|
|
72
84
|
sdkSession,
|
|
73
85
|
publishSdkSession,
|
|
86
|
+
forceReloadSdkSession,
|
|
74
87
|
reloadPreview,
|
|
75
88
|
domEditSaveTimestampRef,
|
|
76
89
|
recordEdit,
|
|
@@ -154,6 +167,7 @@ export function StudioRightPanel({
|
|
|
154
167
|
readProjectFile,
|
|
155
168
|
writeProjectFile,
|
|
156
169
|
fileTree,
|
|
170
|
+
editingFile,
|
|
157
171
|
} = useFileManagerContext();
|
|
158
172
|
|
|
159
173
|
// Discrete ops (toggle, reorder, add/delete, hotspot): persist immediately,
|
|
@@ -202,22 +216,13 @@ export function StudioRightPanel({
|
|
|
202
216
|
const renderJobs = renderQueue.jobs as RenderJob[];
|
|
203
217
|
const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
|
|
204
218
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
label: s.label,
|
|
213
|
-
start: s.start,
|
|
214
|
-
duration: s.duration,
|
|
215
|
-
}));
|
|
216
|
-
} catch {
|
|
217
|
-
return [];
|
|
218
|
-
}
|
|
219
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
220
|
-
}, [previewIframeRef, rightPanelTab, refreshKey]);
|
|
219
|
+
const { isSlideshowComposition, slideshowScenes } = useSlideshowTabState({
|
|
220
|
+
editingFileContent: editingFile?.content,
|
|
221
|
+
previewIframeRef,
|
|
222
|
+
refreshKey,
|
|
223
|
+
rightPanelTab,
|
|
224
|
+
setRightPanelTab,
|
|
225
|
+
});
|
|
221
226
|
const designPaneOpen = inspectorTabActive && rightInspectorPanes.design && designPanelActive;
|
|
222
227
|
const layersPaneOpen =
|
|
223
228
|
inspectorTabActive && rightInspectorPanes.layers && STUDIO_INSPECTOR_PANELS_ENABLED;
|
|
@@ -336,6 +341,7 @@ export function StudioRightPanel({
|
|
|
336
341
|
recordEdit={recordEdit}
|
|
337
342
|
reloadPreview={reloadPreview}
|
|
338
343
|
domEditSaveTimestampRef={domEditSaveTimestampRef}
|
|
344
|
+
forceReloadSharedSdkSession={forceReloadSdkSession}
|
|
339
345
|
>
|
|
340
346
|
<PropertyPanel
|
|
341
347
|
projectId={projectId}
|
|
@@ -491,12 +497,14 @@ export function StudioRightPanel({
|
|
|
491
497
|
active={rightPanelTab === "renders"}
|
|
492
498
|
onClick={() => setRightPanelTab("renders")}
|
|
493
499
|
/>
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
+
{isSlideshowComposition && (
|
|
501
|
+
<PanelTabButton
|
|
502
|
+
label="Slideshow"
|
|
503
|
+
tooltip="Slideshow branching editor"
|
|
504
|
+
active={rightPanelTab === "slideshow"}
|
|
505
|
+
onClick={() => setRightPanelTab("slideshow")}
|
|
506
|
+
/>
|
|
507
|
+
)}
|
|
500
508
|
<PanelTabButton
|
|
501
509
|
label="Variables"
|
|
502
510
|
tooltip="Template variables — declare, preview with values"
|
|
@@ -513,7 +521,7 @@ export function StudioRightPanel({
|
|
|
513
521
|
compositionPath={activeBlockParams.compositionPath}
|
|
514
522
|
onClose={onCloseBlockParams ?? (() => {})}
|
|
515
523
|
/>
|
|
516
|
-
) : rightPanelTab === "slideshow" ? (
|
|
524
|
+
) : rightPanelTab === "slideshow" && isSlideshowComposition ? (
|
|
517
525
|
<SlideshowPanel
|
|
518
526
|
scenes={slideshowScenes}
|
|
519
527
|
onPersist={onPersistSlideshow}
|
|
@@ -81,7 +81,9 @@ export function PromotableControl({
|
|
|
81
81
|
{rendered}
|
|
82
82
|
{bound && (
|
|
83
83
|
<span
|
|
84
|
-
|
|
84
|
+
// Sits above the row (not on top of top-0) so it clears a value that
|
|
85
|
+
// renders flush to the row's right edge, e.g. flat Font/Color rows.
|
|
86
|
+
className="pointer-events-none absolute -top-2 right-1.5 z-10 inline-flex max-w-[60%] items-center gap-1 truncate rounded bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-studio-accent"
|
|
85
87
|
title={`Bound to variable "${promote.boundId}"`}
|
|
86
88
|
>
|
|
87
89
|
◆ {promote.boundId}
|
|
@@ -95,7 +97,7 @@ export function PromotableControl({
|
|
|
95
97
|
e.stopPropagation();
|
|
96
98
|
promote.promote();
|
|
97
99
|
}}
|
|
98
|
-
className="absolute right-1.5
|
|
100
|
+
className="absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded bg-neutral-800/80 px-1 py-px font-mono text-[8px] font-medium text-neutral-400 opacity-70 transition-colors hover:bg-studio-accent/20 hover:text-studio-accent hover:opacity-100"
|
|
99
101
|
>
|
|
100
102
|
◇ var
|
|
101
103
|
</button>
|
|
@@ -197,11 +197,11 @@ export const PropertyPanel = memo(function PropertyPanel(props: PropertyPanelPro
|
|
|
197
197
|
const manualSizeEditingDisabled = !element.capabilities.canApplyManualSize;
|
|
198
198
|
const manualRotationEditingDisabled = !element.capabilities.canApplyManualRotation;
|
|
199
199
|
const sourceLabel = element.id ? `#${element.id}` : (element.selector ?? "");
|
|
200
|
-
const showEditableSections = element.capabilities.canEditStyles;
|
|
201
200
|
// Capabilities are already resolved on the selection; recompute only sections,
|
|
202
201
|
// feeding the live GSAP tween count (arrives on the gsapAnimations prop, not the
|
|
203
202
|
// selection) so the Timing section shows for pure-GSAP elements with no data-start.
|
|
204
203
|
const sections = resolveEditingSections(domEditSelectionToFacts(element, gsapAnimations.length));
|
|
204
|
+
const showEditableSections = element.capabilities.canEditStyles && sections.style;
|
|
205
205
|
const manualOffset = readStudioPathOffset(element.element);
|
|
206
206
|
const manualSize = readStudioBoxSize(element.element);
|
|
207
207
|
const resolvedWidth =
|
|
@@ -385,161 +385,163 @@ export const PropertyPanel = memo(function PropertyPanel(props: PropertyPanelPro
|
|
|
385
385
|
/>
|
|
386
386
|
)}
|
|
387
387
|
|
|
388
|
-
|
|
389
|
-
<
|
|
390
|
-
<div className=
|
|
391
|
-
<div className="flex-1">
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
388
|
+
{sections.layout && (
|
|
389
|
+
<Section title="Layout" icon={<Move size={15} />}>
|
|
390
|
+
<div className={RESPONSIVE_GRID}>
|
|
391
|
+
<div className="flex items-center gap-1">
|
|
392
|
+
<div className="flex-1">
|
|
393
|
+
<MetricField
|
|
394
|
+
label="X"
|
|
395
|
+
value={formatPxMetricValue(displayX)}
|
|
396
|
+
disabled={manualOffsetEditingDisabled}
|
|
397
|
+
scrub
|
|
398
|
+
onCommit={(next) => commitManualOffset("x", next)}
|
|
399
|
+
/>
|
|
400
|
+
</div>
|
|
401
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
402
|
+
<KeyframeNavigation
|
|
403
|
+
property="x"
|
|
404
|
+
keyframes={navKeyframes}
|
|
405
|
+
currentPercentage={currentPct}
|
|
406
|
+
onSeek={seekFromKfPct}
|
|
407
|
+
onAddKeyframe={() =>
|
|
408
|
+
onCommitAnimatedProperty &&
|
|
409
|
+
void onCommitAnimatedProperty(element, "x", displayX)
|
|
410
|
+
}
|
|
411
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("x"), pct)}
|
|
412
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("x"))}
|
|
413
|
+
/>
|
|
414
|
+
)}
|
|
399
415
|
</div>
|
|
400
|
-
|
|
401
|
-
<
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
416
|
+
<div className="flex items-center gap-1">
|
|
417
|
+
<div className="flex-1">
|
|
418
|
+
<MetricField
|
|
419
|
+
label="Y"
|
|
420
|
+
value={formatPxMetricValue(displayY)}
|
|
421
|
+
disabled={manualOffsetEditingDisabled}
|
|
422
|
+
scrub
|
|
423
|
+
onCommit={(next) => commitManualOffset("y", next)}
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
426
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
427
|
+
<KeyframeNavigation
|
|
428
|
+
property="y"
|
|
429
|
+
keyframes={navKeyframes}
|
|
430
|
+
currentPercentage={currentPct}
|
|
431
|
+
onSeek={seekFromKfPct}
|
|
432
|
+
onAddKeyframe={() =>
|
|
433
|
+
onCommitAnimatedProperty &&
|
|
434
|
+
void onCommitAnimatedProperty(element, "y", displayY)
|
|
435
|
+
}
|
|
436
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("y"), pct)}
|
|
437
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("y"))}
|
|
438
|
+
/>
|
|
439
|
+
)}
|
|
424
440
|
</div>
|
|
425
|
-
|
|
426
|
-
<
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
441
|
+
<div className="flex items-center gap-1">
|
|
442
|
+
<div className="flex-1">
|
|
443
|
+
<MetricField
|
|
444
|
+
label="W"
|
|
445
|
+
value={formatPxMetricValue(displayW)}
|
|
446
|
+
disabled={manualSizeEditingDisabled}
|
|
447
|
+
scrub
|
|
448
|
+
onCommit={(next) => commitManualSize("width", next)}
|
|
449
|
+
/>
|
|
450
|
+
</div>
|
|
451
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
452
|
+
<KeyframeNavigation
|
|
453
|
+
property="width"
|
|
454
|
+
keyframes={navKeyframes}
|
|
455
|
+
currentPercentage={currentPct}
|
|
456
|
+
onSeek={seekFromKfPct}
|
|
457
|
+
onAddKeyframe={() =>
|
|
458
|
+
onCommitAnimatedProperty &&
|
|
459
|
+
void onCommitAnimatedProperty(element, "width", displayW)
|
|
460
|
+
}
|
|
461
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("width"), pct)}
|
|
462
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("width"))}
|
|
463
|
+
/>
|
|
464
|
+
)}
|
|
449
465
|
</div>
|
|
450
|
-
|
|
451
|
-
<
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
466
|
+
<div className="flex items-center gap-1">
|
|
467
|
+
<div className="flex-1">
|
|
468
|
+
<MetricField
|
|
469
|
+
label="H"
|
|
470
|
+
value={formatPxMetricValue(displayH)}
|
|
471
|
+
disabled={manualSizeEditingDisabled}
|
|
472
|
+
scrub
|
|
473
|
+
onCommit={(next) => commitManualSize("height", next)}
|
|
474
|
+
/>
|
|
475
|
+
</div>
|
|
476
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
477
|
+
<KeyframeNavigation
|
|
478
|
+
property="height"
|
|
479
|
+
keyframes={navKeyframes}
|
|
480
|
+
currentPercentage={currentPct}
|
|
481
|
+
onSeek={seekFromKfPct}
|
|
482
|
+
onAddKeyframe={() =>
|
|
483
|
+
onCommitAnimatedProperty &&
|
|
484
|
+
void onCommitAnimatedProperty(element, "height", displayH)
|
|
485
|
+
}
|
|
486
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("height"), pct)}
|
|
487
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("height"))}
|
|
488
|
+
/>
|
|
489
|
+
)}
|
|
474
490
|
</div>
|
|
475
|
-
|
|
476
|
-
<
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
491
|
+
<div className="flex items-center gap-1">
|
|
492
|
+
<div className="flex-1">
|
|
493
|
+
<MetricField
|
|
494
|
+
label="R"
|
|
495
|
+
value={`${displayR}°`}
|
|
496
|
+
disabled={manualRotationEditingDisabled}
|
|
497
|
+
onCommit={(next) => commitManualRotation(next.replace("°", ""))}
|
|
498
|
+
/>
|
|
499
|
+
</div>
|
|
500
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
501
|
+
<KeyframeNavigation
|
|
502
|
+
property="rotation"
|
|
503
|
+
keyframes={navKeyframes}
|
|
504
|
+
currentPercentage={currentPct}
|
|
505
|
+
onSeek={seekFromKfPct}
|
|
506
|
+
onAddKeyframe={() =>
|
|
507
|
+
onCommitAnimatedProperty &&
|
|
508
|
+
void onCommitAnimatedProperty(element, "rotation", displayR)
|
|
509
|
+
}
|
|
510
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("rotation"), pct)}
|
|
511
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("rotation"))}
|
|
512
|
+
/>
|
|
513
|
+
)}
|
|
498
514
|
</div>
|
|
499
|
-
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
500
|
-
<KeyframeNavigation
|
|
501
|
-
property="rotation"
|
|
502
|
-
keyframes={navKeyframes}
|
|
503
|
-
currentPercentage={currentPct}
|
|
504
|
-
onSeek={seekFromKfPct}
|
|
505
|
-
onAddKeyframe={() =>
|
|
506
|
-
onCommitAnimatedProperty &&
|
|
507
|
-
void onCommitAnimatedProperty(element, "rotation", displayR)
|
|
508
|
-
}
|
|
509
|
-
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("rotation"), pct)}
|
|
510
|
-
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("rotation"))}
|
|
511
|
-
/>
|
|
512
|
-
)}
|
|
513
515
|
</div>
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
onLivePreviewProps={createGsapLivePreview(iframeRef)}
|
|
530
|
-
/>
|
|
531
|
-
<div className="mt-3">
|
|
532
|
-
<div className="mb-2 text-[10px] font-medium uppercase tracking-wider text-neutral-600">
|
|
533
|
-
Stacking
|
|
534
|
-
</div>
|
|
535
|
-
<MetricField
|
|
536
|
-
label="Z-index"
|
|
537
|
-
value={String(parseInt(styles["z-index"] || "auto", 10) || 0)}
|
|
538
|
-
scrub
|
|
539
|
-
onCommit={(next) => onSetStyle("z-index", next)}
|
|
516
|
+
<PropertyPanel3dTransform
|
|
517
|
+
gsapRuntimeValues={gsap3dValues}
|
|
518
|
+
gsapAnimId={gsapAnimId}
|
|
519
|
+
resolveAnimIdForProp={animIdForProp}
|
|
520
|
+
gsapKeyframes={navKeyframes}
|
|
521
|
+
currentPct={currentPct}
|
|
522
|
+
elStart={elStart}
|
|
523
|
+
elDuration={elDuration}
|
|
524
|
+
element={element}
|
|
525
|
+
onCommitAnimatedProperty={onCommitAnimatedProperty}
|
|
526
|
+
onCommitAnimatedProperties={onCommitAnimatedProperties}
|
|
527
|
+
onSeekToTime={onSeekToTime}
|
|
528
|
+
onRemoveKeyframe={onRemoveKeyframe}
|
|
529
|
+
onConvertToKeyframes={onConvertToKeyframes}
|
|
530
|
+
onLivePreviewProps={createGsapLivePreview(iframeRef)}
|
|
540
531
|
/>
|
|
541
|
-
|
|
542
|
-
|
|
532
|
+
<div className="mt-3">
|
|
533
|
+
<div className="mb-2 text-[10px] font-medium uppercase tracking-wider text-neutral-600">
|
|
534
|
+
Stacking
|
|
535
|
+
</div>
|
|
536
|
+
<MetricField
|
|
537
|
+
label="Z-index"
|
|
538
|
+
value={String(parseInt(styles["z-index"] || "auto", 10) || 0)}
|
|
539
|
+
scrub
|
|
540
|
+
onCommit={(next) => onSetStyle("z-index", next)}
|
|
541
|
+
/>
|
|
542
|
+
</div>
|
|
543
|
+
</Section>
|
|
544
|
+
)}
|
|
543
545
|
|
|
544
546
|
{STUDIO_GSAP_PANEL_ENABLED &&
|
|
545
547
|
onUpdateGsapProperty &&
|