@hyperframes/studio 0.8.113 → 0.8.115
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-DzJbtQVO.js → hyperframes-player-C-9N8nk0.js} +1 -1
- package/dist/assets/{index-n5FUn1xv.js → index-BoUM6Xqb.js} +1 -1
- package/dist/assets/{index-DRhF3v4Z.js → index-CAvZ0AA-.js} +204 -204
- package/dist/assets/{index-DgwDQK5G.js → index-CVuorIMv.js} +1 -1
- package/dist/assets/index-t1LH6H5H.css +1 -0
- package/dist/{chunk-K47E2ZJA.js → chunk-5EKWJZLM.js} +504 -17
- package/dist/chunk-5EKWJZLM.js.map +1 -0
- package/dist/{chunk-VWT7STY3.js → chunk-J6Q572FA.js} +2 -2
- package/dist/{chunk-BIUXP3PG.js → chunk-TXZMMOMA.js} +43 -276
- package/dist/chunk-TXZMMOMA.js.map +1 -0
- package/dist/{domEditingLayers-V67MS4WP.js → domEditingLayers-D7VYFJJD.js} +3 -5
- package/dist/index.d.ts +46 -22
- package/dist/index.html +2 -2
- package/dist/index.js +3899 -3487
- package/dist/index.js.map +1 -1
- package/dist/player.js +2 -2
- package/dist/styles.css +1 -1
- package/package.json +10 -8
- package/src/App.tsx +84 -86
- package/src/components/EditorShell.tsx +3 -3
- package/src/components/LinkedSelectionToggle.tsx +3 -2
- package/src/components/StudioHeader.tsx +4 -4
- package/src/components/StudioLeftPanels.tsx +3 -3
- package/src/components/StudioOverlays.tsx +4 -4
- package/src/components/StudioRightPanels.tsx +14 -9
- package/src/components/StudioToast.tsx +1 -1
- package/src/components/TimelineToolbar.tsx +3 -3
- package/src/components/dock/Dock.tsx +5 -7
- package/src/components/editor/CanvasContextMenu.tsx +3 -2
- package/src/components/editor/DomEditCropHandles.test.tsx +27 -0
- package/src/components/editor/DomEditCropHandles.tsx +7 -4
- package/src/components/editor/DomEditOverlay.tsx +5 -1
- package/src/components/editor/FileTreeNodes.tsx +13 -10
- package/src/components/editor/InlineTextToolbar.tsx +11 -27
- package/src/components/editor/MotionPathOverlay.tsx +7 -2
- package/src/components/editor/PropertyPanelEmptyState.tsx +12 -2
- package/src/components/editor/SnapToolbar.test.tsx +100 -1
- package/src/components/editor/SnapToolbar.tsx +43 -5
- package/src/components/editor/anchoredResizeCommitFeedsOffset.test.ts +48 -1
- package/src/components/editor/audioFxTelemetry.ts +2 -3
- package/src/components/editor/colorBrowserCanvas.test.ts +116 -0
- package/src/components/editor/colorValue.test.ts +16 -0
- package/src/components/editor/colorValue.ts +100 -24
- package/src/components/editor/commitPreflightHandles.test.tsx +2 -2
- package/src/components/editor/cropResize.test.ts +11 -0
- package/src/components/editor/cropResize.ts +6 -6
- package/src/components/editor/domEditingDom.ts +4 -0
- package/src/components/editor/domEditingLayers.ts +1 -5
- package/src/components/editor/gradientValue.ts +28 -36
- package/src/components/editor/gsapAnimatesProperty.ts +0 -54
- package/src/components/editor/inlineTextStyleRange.test.ts +31 -1
- package/src/components/editor/inlineTextStyleRange.ts +5 -2
- package/src/components/editor/inlineTextStyleRead.ts +9 -0
- package/src/components/editor/manualEdits.ts +1 -0
- package/src/components/editor/manualEditsDom.ts +2 -2
- package/src/components/editor/manualEditsParsing.ts +7 -0
- package/src/components/editor/manualEditsSeekReapply.ts +2 -2
- package/src/components/editor/marqueeCommit.test.tsx +106 -37
- package/src/components/editor/marqueeCommit.ts +24 -8
- package/src/components/editor/motionPathCommit.test.ts +29 -1
- package/src/components/editor/motionPathCommit.ts +21 -9
- package/src/components/editor/probeSourceElement.test.ts +45 -0
- package/src/components/editor/probeSourceElement.ts +39 -15
- package/src/components/editor/propertyPanelColor.tsx +1 -1
- package/src/components/editor/propertyPanelFlatTextSection.tsx +10 -7
- package/src/components/editor/propertyPanelFxAddMenu.tsx +11 -2
- package/src/components/editor/propertyPanelFxCarveModule.tsx +15 -5
- package/src/components/editor/propertyPanelFxNodeOpenBody.tsx +3 -0
- package/src/components/editor/propertyPanelFxNodeParams.tsx +15 -2
- package/src/components/editor/propertyPanelFxNodeRow.tsx +3 -0
- package/src/components/editor/propertyPanelFxPresetRun.tsx +10 -2
- package/src/components/editor/propertyPanelFxSection.test.tsx +56 -0
- package/src/components/editor/propertyPanelFxSection.tsx +30 -28
- package/src/components/editor/propertyPanelTypes.ts +6 -3
- package/src/components/editor/reapplyPositionEditsAfterSeek.test.ts +25 -0
- package/src/components/editor/useDomEditNudge.test.tsx +37 -0
- package/src/components/editor/useDomEditNudge.ts +2 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +5 -0
- package/src/components/editor/useDomEditZOrder.ts +12 -2
- package/src/components/nle/useTimelineEditCallbacks.test.tsx +1 -0
- package/src/components/nle/useTimelineEditCallbacks.ts +7 -1
- package/src/components/sidebar/AssetContextMenu.tsx +4 -4
- package/src/components/ui/menuStyle.test.ts +61 -0
- package/src/components/ui/menuStyle.ts +10 -0
- package/src/contexts/DomEditContext.tsx +1 -1
- package/src/contexts/TimelineEditContext.tsx +1 -0
- package/src/domEditExports.test.tsx +9 -1
- package/src/hooks/domEditCommitRunner.ts +1 -1
- package/src/hooks/elementOffsetStager.ts +5 -4
- package/src/hooks/gsapDragCommit.ts +2 -20
- package/src/hooks/gsapDragPositionCommit.ts +18 -3
- package/src/hooks/gsapGestureOutcome.test.ts +161 -0
- package/src/hooks/gsapGestureOutcome.ts +64 -0
- package/src/hooks/gsapKeyframeCommit.ts +3 -12
- package/src/hooks/gsapResizeIntercept.test.ts +33 -0
- package/src/hooks/gsapResizeIntercept.ts +1 -0
- package/src/hooks/gsapRuntimeKeyframes.ts +1 -1
- package/src/hooks/gsapRuntimeReaders.test.ts +48 -0
- package/src/hooks/gsapRuntimeReaders.ts +3 -90
- package/src/hooks/gsapScriptCommitTypes.ts +5 -0
- package/src/hooks/gsapShared.writeTarget.test.ts +20 -0
- package/src/hooks/keyframeCacheAstLoad.ts +6 -5
- package/src/hooks/plainRotation.ts +3 -2
- package/src/hooks/previewPromotionSelection.test.tsx +1 -1
- package/src/hooks/refreshTimelineRowText.test.ts +65 -0
- package/src/hooks/refreshTimelineRowText.ts +29 -0
- package/src/hooks/useAnimatedPropertyCommit.test.tsx +46 -5
- package/src/hooks/useAnimatedPropertyCommit.ts +2 -2
- package/src/hooks/useAppHotkeys.ts +11 -5
- package/src/hooks/useBlockedTimelineEditToast.ts +12 -3
- package/src/hooks/useDomEditPositionPatchCommit.ts +18 -23
- package/src/hooks/useDomEditTextCommits.test.tsx +18 -0
- package/src/hooks/useDomEditTextCommits.ts +5 -1
- package/src/hooks/useDomEditWiring.ts +4 -2
- package/src/hooks/useDomGeometryCommit.test.tsx +25 -6
- package/src/hooks/useDomGeometryCommit.ts +7 -4
- package/src/hooks/useDomGeometryCommits.test.tsx +1 -0
- package/src/hooks/useDomGeometryCommits.ts +3 -2
- package/src/hooks/useDomSelection.test.ts +26 -0
- package/src/hooks/useDomSelection.ts +10 -3
- package/src/hooks/useDomSelectionTypes.ts +6 -1
- package/src/hooks/useEnableKeyframes.test.ts +2 -2
- package/src/hooks/useEnableKeyframes.ts +5 -3
- package/src/hooks/useExternalFileChangeCoordinator.test.tsx +102 -0
- package/src/hooks/useExternalFileChangeCoordinator.ts +31 -6
- package/src/hooks/useFrameCapture.ts +15 -8
- package/src/hooks/useGestureCommit.test.tsx +225 -55
- package/src/hooks/useGestureCommit.ts +90 -56
- package/src/hooks/useGestureTrailOverlay.tsx +33 -0
- package/src/hooks/useGsapAwareEditing.test.tsx +11 -6
- package/src/hooks/useGsapAwareEditing.ts +96 -195
- package/src/hooks/useGsapAwareGroupMove.ts +179 -0
- package/src/hooks/useGsapKeyframeOps.sdkUsage.test.tsx +114 -0
- package/src/hooks/useGsapKeyframeOps.ts +36 -10
- package/src/hooks/useGsapScriptCommits.test.tsx +57 -0
- package/src/hooks/useGsapScriptCommits.ts +3 -0
- package/src/hooks/useGsapSelectionHandlers.test.tsx +1 -1
- package/src/hooks/useGsapSelectionHandlers.ts +24 -18
- package/src/hooks/useGsapTweenCache.ts +18 -12
- package/src/hooks/usePanelLayout.ts +5 -1
- package/src/hooks/usePopulateKeyframeCacheForFile.test.tsx +76 -2
- package/src/hooks/usePreviewInteraction.test.ts +21 -0
- package/src/hooks/usePreviewInteraction.ts +2 -0
- package/src/hooks/useRenderClipContent.test.ts +18 -0
- package/src/hooks/useRenderClipContent.ts +4 -5
- package/src/hooks/useShellSlices.ts +53 -0
- package/src/hooks/useStableHandlers.test.tsx +77 -0
- package/src/hooks/useStableHandlers.ts +44 -0
- package/src/hooks/useStudioSdkSessions.ts +2 -1
- package/src/hooks/useStudioSidePanels.test.tsx +44 -0
- package/src/hooks/useStudioSidePanels.tsx +23 -0
- package/src/hooks/useTimelineAddAtPlayhead.ts +16 -8
- package/src/hooks/useTimelineDeleteOps.test.tsx +30 -22
- package/src/hooks/useTimelineDeleteOps.ts +21 -27
- package/src/hooks/useTimelineEditing.test.tsx +1 -1
- package/src/hooks/useTimelineEditing.ts +33 -27
- package/src/player/components/AutomationSelectionMenu.tsx +3 -2
- package/src/player/components/ClipContextMenu.test.tsx +73 -0
- package/src/player/components/ClipContextMenu.tsx +33 -31
- package/src/player/components/KeyframeDiamondContextMenu.tsx +8 -9
- package/src/player/components/OutOfSyncBadge.tsx +5 -5
- package/src/player/components/TextClipContent.test.ts +19 -0
- package/src/player/components/TextClipContent.tsx +27 -0
- package/src/player/components/TrackGapContextMenu.tsx +3 -6
- package/src/player/components/clipMenuToolItems.test.tsx +23 -0
- package/src/player/components/clipMenuToolItems.tsx +13 -9
- package/src/player/components/timelineCallbacks.ts +1 -0
- package/src/player/components/timelineClipDragGestureLifecycle.ts +1 -1
- package/src/player/components/timelineClipGestureHandlers.test.ts +94 -0
- package/src/player/components/timelineClipGestureHandlers.ts +20 -2
- package/src/player/components/timelineEditing.ts +3 -1
- package/src/player/components/timelineLinkSelection.ts +35 -1
- package/src/player/components/useTimelineClipDrag.keyboard.test.tsx +14 -0
- package/src/player/components/useTimelineClipDrag.ts +6 -7
- package/src/player/hooks/timelinePlayerSync.test.ts +6 -0
- package/src/player/hooks/timelinePlayerSync.ts +6 -1
- package/src/player/lib/timelineElementHelpers.test.ts +87 -1
- package/src/player/lib/timelineElementHelpers.ts +6 -15
- package/src/player/lib/timelineText.ts +40 -0
- package/src/player/store/timelineElement.ts +12 -0
- package/src/styles/theme.css +3 -0
- package/src/utils/frameCapture.ts +3 -1
- package/src/utils/gsapUndoRestore.browser.test.ts +4 -2
- package/src/utils/keyframeUsage.ts +79 -0
- package/src/utils/previewFeatureUsage.test.ts +32 -0
- package/src/utils/previewFeatureUsage.ts +46 -0
- package/src/utils/studioTelemetry.test.ts +107 -0
- package/src/utils/studioTelemetry.ts +49 -4
- package/src/webmcp/StudioAgentTools.tsx +2 -2
- package/dist/assets/index-DbfKVTId.css +0 -1
- package/dist/chunk-BIUXP3PG.js.map +0 -1
- package/dist/chunk-K47E2ZJA.js.map +0 -1
- /package/dist/{chunk-VWT7STY3.js.map → chunk-J6Q572FA.js.map} +0 -0
- /package/dist/{domEditingLayers-V67MS4WP.js.map → domEditingLayers-D7VYFJJD.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.115",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"dockview-react": "8.3.1",
|
|
61
61
|
"gsap": "^3.13.0",
|
|
62
62
|
"mediabunny": "^1.45.3",
|
|
63
|
-
"@hyperframes/core": "0.8.
|
|
64
|
-
"@hyperframes/player": "0.8.
|
|
65
|
-
"@hyperframes/
|
|
66
|
-
"@hyperframes/
|
|
67
|
-
"@hyperframes/
|
|
68
|
-
"@hyperframes/
|
|
63
|
+
"@hyperframes/core": "0.8.115",
|
|
64
|
+
"@hyperframes/player": "0.8.115",
|
|
65
|
+
"@hyperframes/studio-server": "0.8.115",
|
|
66
|
+
"@hyperframes/sdk": "0.8.115",
|
|
67
|
+
"@hyperframes/shader-transitions": "0.8.115",
|
|
68
|
+
"@hyperframes/parsers": "0.8.115"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@tailwindcss/vite": "^4.3.3",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"vite": "^6.4.2",
|
|
82
82
|
"vitest": "^4.1.11",
|
|
83
83
|
"zustand": "^5.0.0",
|
|
84
|
-
"@hyperframes/producer": "0.8.
|
|
84
|
+
"@hyperframes/producer": "0.8.115"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"react": "19",
|
|
@@ -97,8 +97,10 @@
|
|
|
97
97
|
"test:filmstrip-controls": "node tests/e2e/filmstrip-controls.mjs",
|
|
98
98
|
"test:webmcp-edit-loop": "node tests/e2e/webmcp-edit-loop.mjs",
|
|
99
99
|
"test:thumbnail-save": "node tests/e2e/thumbnail-save.mjs",
|
|
100
|
+
"bench:timeline-bulk-actions": "node tests/e2e/timeline-bulk-actions.mjs",
|
|
100
101
|
"test:timeline-virtualization": "TIMELINE_ROW_VIRTUALIZATION=on TIMELINE_ELEMENT_COUNT=50000 node tests/e2e/timeline-virtualization.mjs",
|
|
101
102
|
"test:watch": "vitest",
|
|
103
|
+
"test:colors:browser": "node tests/e2e/native-css-colors.mjs",
|
|
102
104
|
"report:sdk-cutover": "bun src/utils/sdkCutoverPolicy.report.ts",
|
|
103
105
|
"test:timeline-default": "bun run test:timeline-virtualization"
|
|
104
106
|
}
|
package/src/App.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useOwnPreviewIframe, usePreviewIframeStore } from "./player/store/previewIframeStore";
|
|
2
2
|
import { buildProjectApiPath } from "./utils/projectRouting";
|
|
3
|
-
import { useState, useCallback, useRef, useMemo
|
|
3
|
+
import { useState, useCallback, useRef, useMemo } from "react";
|
|
4
|
+
import { useStableHandlers } from "./hooks/useStableHandlers";
|
|
5
|
+
import { useHistoryFlags, useToolbarSession } from "./hooks/useShellSlices";
|
|
4
6
|
import { useDismissingTabSetter, useRightPanelIntent } from "./hooks/useRightPanelIntents";
|
|
5
7
|
import { useRenderQueue } from "./components/renders/useRenderQueue";
|
|
6
8
|
import { usePlayerStore } from "./player";
|
|
@@ -46,10 +48,9 @@ import {
|
|
|
46
48
|
import type { DomEditSelection } from "./components/editor/domEditing";
|
|
47
49
|
import { StudioHeader } from "./components/StudioHeader";
|
|
48
50
|
import { useGestureCommit } from "./hooks/useGestureCommit";
|
|
49
|
-
import {
|
|
50
|
-
import { StudioLeftPanels } from "./components/StudioLeftPanels";
|
|
51
|
+
import { useGestureTrailOverlay } from "./hooks/useGestureTrailOverlay";
|
|
51
52
|
import { EditorShell } from "./components/EditorShell";
|
|
52
|
-
import {
|
|
53
|
+
import { useStudioSidePanels } from "./hooks/useStudioSidePanels";
|
|
53
54
|
import { TimelineToolbar } from "./components/TimelineToolbar";
|
|
54
55
|
import { StudioPlaybackProvider, StudioShellProvider } from "./contexts/StudioContext";
|
|
55
56
|
import { PanelLayoutProvider } from "./contexts/PanelLayoutContext";
|
|
@@ -99,17 +100,18 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
99
100
|
rightCollapsed: initialUrlStateRef.current.rightCollapsed,
|
|
100
101
|
rightPanelTab: initialUrlStateRef.current.rightPanelTab,
|
|
101
102
|
});
|
|
102
|
-
const editHistory = usePersistentEditHistory({ projectId });
|
|
103
|
+
const editHistory = useStableHandlers(usePersistentEditHistory({ projectId }), projectId);
|
|
103
104
|
const handleDomZIndexReorderCommitRef = useRef<TimelineZIndexReorderCommit | null>(null);
|
|
104
105
|
const pendingTimelineEditPathRef = useRef(new Set<string>());
|
|
105
106
|
const isGestureRecordingRef = useRef(false);
|
|
106
107
|
const reloadPreview = useCallback(() => setRefreshKey((k) => k + 1), []);
|
|
107
|
-
const
|
|
108
|
+
const fileManagerResult = useFileManager({
|
|
108
109
|
projectId,
|
|
109
110
|
showToast,
|
|
110
111
|
recordEdit: editHistory.recordEdit,
|
|
111
112
|
setRefreshKey,
|
|
112
113
|
});
|
|
114
|
+
const fileManager = useStableHandlers(fileManagerResult, projectId);
|
|
113
115
|
const masterCompPath = useMemo(
|
|
114
116
|
() => resolveMasterCompositionPath(fileManager.compositions),
|
|
115
117
|
[fileManager.compositions],
|
|
@@ -134,8 +136,8 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
134
136
|
);
|
|
135
137
|
const activeCompPathRef = useRef(activeCompPath);
|
|
136
138
|
activeCompPathRef.current = activeCompPath;
|
|
137
|
-
const renderQueue = useRenderQueue(projectId, activeCompPathRef);
|
|
138
|
-
const
|
|
139
|
+
const renderQueue = useStableHandlers(useRenderQueue(projectId, activeCompPathRef), projectId);
|
|
140
|
+
const previewPersistenceResult = usePreviewPersistence({
|
|
139
141
|
showToast,
|
|
140
142
|
readOptionalProjectFile: fileManager.readOptionalProjectFile,
|
|
141
143
|
writeProjectFile: fileManager.writeProjectFile,
|
|
@@ -144,6 +146,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
144
146
|
activeCompPathRef,
|
|
145
147
|
reloadPreview: () => setRefreshKey((k) => k + 1),
|
|
146
148
|
});
|
|
149
|
+
const previewPersistence = useStableHandlers(previewPersistenceResult, projectId);
|
|
147
150
|
const externalFileChanges = useStudioExternalFileChanges({
|
|
148
151
|
projectId,
|
|
149
152
|
activeCompPath,
|
|
@@ -184,15 +187,9 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
184
187
|
} = useTimelineAddAtPlayhead(
|
|
185
188
|
timelineEditing.handleTimelineAssetDrop,
|
|
186
189
|
timelineEditing.handleTimelineCompositionDrop,
|
|
190
|
+
projectId,
|
|
187
191
|
);
|
|
188
|
-
const {
|
|
189
|
-
activeBlockParams,
|
|
190
|
-
setActiveBlockParams,
|
|
191
|
-
handleAddBlock,
|
|
192
|
-
handleTimelineBlockDrop,
|
|
193
|
-
handleAddMediaOverlay,
|
|
194
|
-
handlePreviewBlockDrop,
|
|
195
|
-
} = useBlockHandlers({
|
|
192
|
+
const blockHandlersResult = useBlockHandlers({
|
|
196
193
|
projectId,
|
|
197
194
|
blockCtxDeps: {
|
|
198
195
|
activeCompPath,
|
|
@@ -209,6 +206,14 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
209
206
|
setRightCollapsed: panelLayout.setRightCollapsed,
|
|
210
207
|
setRightPanelTab: panelLayout.setRightPanelTab,
|
|
211
208
|
});
|
|
209
|
+
const {
|
|
210
|
+
activeBlockParams,
|
|
211
|
+
setActiveBlockParams,
|
|
212
|
+
handleAddBlock,
|
|
213
|
+
handleTimelineBlockDrop,
|
|
214
|
+
handleAddMediaOverlay,
|
|
215
|
+
handlePreviewBlockDrop,
|
|
216
|
+
} = useStableHandlers(blockHandlersResult, projectId);
|
|
212
217
|
const dismissBlockParams = useCallback(() => setActiveBlockParams(null), [setActiveBlockParams]);
|
|
213
218
|
const setRightPanelTab = useDismissingTabSetter(panelLayout.setRightPanelTab, dismissBlockParams);
|
|
214
219
|
const layout = useMemo(
|
|
@@ -237,6 +242,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
237
242
|
waitForPendingDomEditSaves: previewPersistence.waitForPendingDomEditSaves,
|
|
238
243
|
});
|
|
239
244
|
const appHotkeys = useAppHotkeys({
|
|
245
|
+
projectId,
|
|
240
246
|
handleTimelineElementsDelete: timelineEditing.handleTimelineElementsDelete,
|
|
241
247
|
handleLinkEdit: timelineEditing.handleLinkEdit,
|
|
242
248
|
handleTimelineElementDeleteOnly: timelineEditing.handleTimelineElementDeleteOnly,
|
|
@@ -266,10 +272,10 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
266
272
|
onUngroupSelection: () => domEditSessionRef.current.handleUngroupSelection(),
|
|
267
273
|
activeCompPath,
|
|
268
274
|
forceReloadSdkSession: sdkHandle.forceReload,
|
|
269
|
-
onToggleRecording: () => handleToggleRecordingRef.current(),
|
|
275
|
+
onToggleRecording: () => handleToggleRecordingRef.current("keyboard"),
|
|
270
276
|
readOnlyPreview,
|
|
271
277
|
});
|
|
272
|
-
const
|
|
278
|
+
const domEditSessionResult = useDomEditSession({
|
|
273
279
|
projectId,
|
|
274
280
|
activeCompPath,
|
|
275
281
|
compIdToSrc,
|
|
@@ -309,6 +315,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
309
315
|
handleTimelineElementsDelete: timelineEditing.handleTimelineElementsDelete,
|
|
310
316
|
readOnlyPreview,
|
|
311
317
|
});
|
|
318
|
+
const domEditSession = useStableHandlers(domEditSessionResult, projectId);
|
|
312
319
|
domEditSelectionBridgeRef.current = domEditSession.domEditSelection;
|
|
313
320
|
const { handleDomZIndexReorderCommit: zCommit, handleDomEditElementDelete: del } = domEditSession;
|
|
314
321
|
handleDomZIndexReorderCommitRef.current = trackedStudioEdit(zCommit);
|
|
@@ -342,23 +349,25 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
342
349
|
effectiveTimelineDuration,
|
|
343
350
|
});
|
|
344
351
|
const compositionDimensions = useCompositionDimensions(previewIframeRef);
|
|
345
|
-
const lint = useLintModal(projectId, refreshKey);
|
|
346
|
-
const
|
|
352
|
+
const lint = useStableHandlers(useLintModal(projectId, refreshKey), projectId);
|
|
353
|
+
const frameCaptureResult = useFrameCapture({
|
|
347
354
|
projectId,
|
|
348
355
|
activeCompPath,
|
|
349
356
|
showToast,
|
|
350
357
|
waitForPendingDomEditSaves: previewPersistence.waitForPendingDomEditSaves,
|
|
351
358
|
});
|
|
359
|
+
const frameCapture = useStableHandlers(frameCaptureResult, projectId);
|
|
352
360
|
const {
|
|
353
361
|
consoleErrors,
|
|
354
362
|
setConsoleErrors,
|
|
355
363
|
resetErrors: resetConsoleErrors,
|
|
356
364
|
} = useConsoleErrorCapture(previewIframe);
|
|
357
365
|
const fileDrop = useGlobalFileDrop(timelineEditing.handleTimelineFileDrop);
|
|
358
|
-
const handleToggleRecordingRef = useRef<() => void>(() => {});
|
|
366
|
+
const handleToggleRecordingRef = useRef<(method?: "button" | "keyboard") => void>(() => {});
|
|
359
367
|
const domEditSessionRef = useRef(domEditSession);
|
|
360
368
|
domEditSessionRef.current = domEditSession;
|
|
361
369
|
const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
|
|
370
|
+
projectId,
|
|
362
371
|
domEditSessionRef,
|
|
363
372
|
previewIframeRef,
|
|
364
373
|
showToast,
|
|
@@ -366,14 +375,11 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
366
375
|
readOnlyPreview,
|
|
367
376
|
});
|
|
368
377
|
handleToggleRecordingRef.current = handleToggleRecording;
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
canvasRectRef.current = previewIframe.getBoundingClientRect();
|
|
376
|
-
}, [gestureState, previewIframe]);
|
|
378
|
+
const gestureOverlay = useGestureTrailOverlay(
|
|
379
|
+
{ gestureState, gestureRecording },
|
|
380
|
+
previewIframe,
|
|
381
|
+
compositionDimensions,
|
|
382
|
+
);
|
|
377
383
|
const handlePreviewIframeRef = useCallback(
|
|
378
384
|
(iframe: HTMLIFrameElement | null) => {
|
|
379
385
|
previewIframeRef.current = iframe;
|
|
@@ -415,6 +421,8 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
415
421
|
setRightPanelTab,
|
|
416
422
|
initialState: initialUrlStateRef.current,
|
|
417
423
|
});
|
|
424
|
+
const historyFlags = useHistoryFlags(editHistory);
|
|
425
|
+
const toolbarSession = useToolbarSession(domEditSession);
|
|
418
426
|
const studioCtxValue = buildStudioContextValue({
|
|
419
427
|
projectId: projectId!,
|
|
420
428
|
activeCompPath,
|
|
@@ -427,7 +435,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
427
435
|
setRefreshKey,
|
|
428
436
|
timelineElements,
|
|
429
437
|
isPlaying,
|
|
430
|
-
editHistory,
|
|
438
|
+
editHistory: historyFlags,
|
|
431
439
|
handleUndo: appHotkeys.handleUndo,
|
|
432
440
|
handleRedo: appHotkeys.handleRedo,
|
|
433
441
|
renderQueue,
|
|
@@ -441,11 +449,51 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
441
449
|
const timelineToolbar = useMemo(
|
|
442
450
|
() => (
|
|
443
451
|
<TimelineToolbar
|
|
444
|
-
domEditSession={
|
|
452
|
+
domEditSession={toolbarSession}
|
|
445
453
|
onSplitElement={timelineEditing.handleTimelineElementSplit}
|
|
446
454
|
/>
|
|
447
455
|
),
|
|
448
|
-
[
|
|
456
|
+
[toolbarSession, timelineEditing.handleTimelineElementSplit],
|
|
457
|
+
);
|
|
458
|
+
const handleExport = useCallback(() => {
|
|
459
|
+
void previewPersistence
|
|
460
|
+
.waitForPendingDomEditSaves()
|
|
461
|
+
.then(() => renderQueue.startRender(undefined));
|
|
462
|
+
}, [previewPersistence, renderQueue]);
|
|
463
|
+
const clearConsoleErrors = useCallback(() => setConsoleErrors(null), [setConsoleErrors]);
|
|
464
|
+
const panels = useStudioSidePanels(
|
|
465
|
+
{
|
|
466
|
+
onSelectComposition: handleSelectComposition,
|
|
467
|
+
onAddBlock: handleAddBlock,
|
|
468
|
+
onPreviewBlock: setBlockPreview,
|
|
469
|
+
onLint: lint.handleLint,
|
|
470
|
+
linting: lint.linting,
|
|
471
|
+
lintFindingCount: lint.lintFindingCount,
|
|
472
|
+
lintFindingsByFile: lint.findingsByFile,
|
|
473
|
+
lintHasError: lint.hasLintError,
|
|
474
|
+
onAddAssetToTimeline: handleAddAssetAtPlayhead,
|
|
475
|
+
onAddCompositionToTimeline: handleAddCompositionAtPlayhead,
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
activeBlockParams,
|
|
479
|
+
onDismissBlockParams: dismissBlockParams,
|
|
480
|
+
onCloseBlockParams: () => {
|
|
481
|
+
setActiveBlockParams(null);
|
|
482
|
+
panelLayout.setRightPanelTab("design");
|
|
483
|
+
},
|
|
484
|
+
recordingState: gestureState,
|
|
485
|
+
recordingDuration: gestureRecording.recordingDuration,
|
|
486
|
+
onToggleRecording: handleToggleRecording,
|
|
487
|
+
sdkSession: sdkHandle.session,
|
|
488
|
+
publishSdkSession: sdkHandle.publish,
|
|
489
|
+
forceReloadSdkSession: sdkHandle.forceReload,
|
|
490
|
+
reloadPreview,
|
|
491
|
+
recordEdit: editHistory.recordEdit,
|
|
492
|
+
onToggleElementHidden: timelineEditing.handleToggleElementHidden,
|
|
493
|
+
onAutoGroupCarveSources: timelineEditing.handleAutoGroupCarveSources,
|
|
494
|
+
onAddMediaOverlay: handleAddMediaOverlay,
|
|
495
|
+
},
|
|
496
|
+
projectId,
|
|
449
497
|
);
|
|
450
498
|
if (resolving || waitingForServer || !projectId)
|
|
451
499
|
return <StudioSplash waiting={waitingForServer} />;
|
|
@@ -468,12 +516,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
468
516
|
capturing={frameCapture.capturing}
|
|
469
517
|
inspectorButtonActive={inspectorButtonActive}
|
|
470
518
|
inspectorPanelActive={inspectorPanelActive}
|
|
471
|
-
onExport={
|
|
472
|
-
void (async () => {
|
|
473
|
-
await previewPersistence.waitForPendingDomEditSaves();
|
|
474
|
-
await renderQueue.startRender(undefined);
|
|
475
|
-
})();
|
|
476
|
-
}}
|
|
519
|
+
onExport={handleExport}
|
|
477
520
|
/>
|
|
478
521
|
{previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && (
|
|
479
522
|
<SaveQueuePausedBanner
|
|
@@ -491,41 +534,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
491
534
|
<EditorShell
|
|
492
535
|
readOnlyPreview={readOnlyPreview}
|
|
493
536
|
readOnlyPreviewReason={readOnlyPreviewReason}
|
|
494
|
-
panels={
|
|
495
|
-
<>
|
|
496
|
-
<StudioLeftPanels
|
|
497
|
-
onSelectComposition={handleSelectComposition}
|
|
498
|
-
onAddBlock={handleAddBlock}
|
|
499
|
-
onPreviewBlock={setBlockPreview}
|
|
500
|
-
onLint={lint.handleLint}
|
|
501
|
-
linting={lint.linting}
|
|
502
|
-
lintFindingCount={lint.lintFindingCount}
|
|
503
|
-
lintFindingsByFile={lint.findingsByFile}
|
|
504
|
-
lintHasError={lint.hasLintError}
|
|
505
|
-
onAddAssetToTimeline={handleAddAssetAtPlayhead}
|
|
506
|
-
onAddCompositionToTimeline={handleAddCompositionAtPlayhead}
|
|
507
|
-
/>
|
|
508
|
-
<StudioRightPanels
|
|
509
|
-
activeBlockParams={activeBlockParams}
|
|
510
|
-
onDismissBlockParams={dismissBlockParams}
|
|
511
|
-
onCloseBlockParams={() => {
|
|
512
|
-
setActiveBlockParams(null);
|
|
513
|
-
panelLayout.setRightPanelTab("design");
|
|
514
|
-
}}
|
|
515
|
-
recordingState={gestureState}
|
|
516
|
-
recordingDuration={gestureRecording.recordingDuration}
|
|
517
|
-
onToggleRecording={handleToggleRecording}
|
|
518
|
-
sdkSession={sdkHandle.session}
|
|
519
|
-
publishSdkSession={sdkHandle.publish}
|
|
520
|
-
forceReloadSdkSession={sdkHandle.forceReload}
|
|
521
|
-
reloadPreview={reloadPreview}
|
|
522
|
-
recordEdit={editHistory.recordEdit}
|
|
523
|
-
onToggleElementHidden={timelineEditing.handleToggleElementHidden}
|
|
524
|
-
onAutoGroupCarveSources={timelineEditing.handleAutoGroupCarveSources}
|
|
525
|
-
onAddMediaOverlay={handleAddMediaOverlay}
|
|
526
|
-
/>
|
|
527
|
-
</>
|
|
528
|
-
}
|
|
537
|
+
panels={panels}
|
|
529
538
|
timelineToolbar={timelineToolbar}
|
|
530
539
|
renderClipContent={renderClipContent}
|
|
531
540
|
handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
|
|
@@ -561,18 +570,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
561
570
|
recordingState={gestureState}
|
|
562
571
|
onToggleRecording={handleToggleRecording}
|
|
563
572
|
blockPreview={blockPreview}
|
|
564
|
-
gestureOverlay={
|
|
565
|
-
gestureState === "recording" && previewIframe ? (
|
|
566
|
-
<GestureTrailOverlay
|
|
567
|
-
samples={gestureRecording.samplesRef.current}
|
|
568
|
-
sampleCount={gestureRecording.samplesRef.current.length}
|
|
569
|
-
trail={gestureRecording.trailRef.current}
|
|
570
|
-
canvasRect={canvasRectRef.current!}
|
|
571
|
-
compositionSize={compositionDimensions ?? undefined}
|
|
572
|
-
mode="recording"
|
|
573
|
-
/>
|
|
574
|
-
) : undefined
|
|
575
|
-
}
|
|
573
|
+
gestureOverlay={gestureOverlay}
|
|
576
574
|
/>
|
|
577
575
|
<StudioOverlays
|
|
578
576
|
projectId={projectId}
|
|
@@ -580,7 +578,7 @@ export function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason }: St
|
|
|
580
578
|
lintModal={lint.lintModal}
|
|
581
579
|
closeLintModal={lint.closeLintModal}
|
|
582
580
|
consoleErrors={consoleErrors}
|
|
583
|
-
clearConsoleErrors={
|
|
581
|
+
clearConsoleErrors={clearConsoleErrors}
|
|
584
582
|
domEditSession={domEditSession}
|
|
585
583
|
activeCompPath={activeCompPath}
|
|
586
584
|
toasts={toasts}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, type ReactNode } from "react";
|
|
1
|
+
import { memo, useCallback, type ReactNode } from "react";
|
|
2
2
|
import { PreviewPane } from "./nle/PreviewPane";
|
|
3
3
|
import { TimelinePane } from "./nle/TimelinePane";
|
|
4
4
|
import { PreviewOverlays } from "./nle/PreviewOverlays";
|
|
@@ -81,7 +81,7 @@ export interface EditorShellProps extends TimelineEditCallbackDeps {
|
|
|
81
81
|
// The dockable shell: every panel lives in one Dock, arranged by the user's
|
|
82
82
|
// saved layout. Owns the shared player + composition-stack state via
|
|
83
83
|
// NLEProvider so every panel shares one player.
|
|
84
|
-
export function EditorShell({
|
|
84
|
+
export const EditorShell = memo(function EditorShell({
|
|
85
85
|
panels,
|
|
86
86
|
timelineToolbar,
|
|
87
87
|
renderClipContent,
|
|
@@ -228,7 +228,7 @@ export function EditorShell({
|
|
|
228
228
|
</div>
|
|
229
229
|
</PreviewReadOnlyProvider>
|
|
230
230
|
);
|
|
231
|
-
}
|
|
231
|
+
});
|
|
232
232
|
|
|
233
233
|
interface EditorShellBodyProps {
|
|
234
234
|
panels: ReactNode;
|
|
@@ -6,6 +6,7 @@ import { useMenuKeyboardNav } from "../player/components/menuKeyboardNav";
|
|
|
6
6
|
import { useLinkedClipPreferences } from "../utils/linkedClipPreferences";
|
|
7
7
|
import { Tooltip } from "./ui";
|
|
8
8
|
import { flatActive, flatIdle } from "./timelineToolbarStyles";
|
|
9
|
+
import { menuClasses } from "./ui/menuStyle";
|
|
9
10
|
|
|
10
11
|
function SyncIndicatorMenu({ x, y, onClose }: { x: number; y: number; onClose: () => void }) {
|
|
11
12
|
const menuRef = useContextMenuDismiss(onClose);
|
|
@@ -17,14 +18,14 @@ function SyncIndicatorMenu({ x, y, onClose }: { x: number; y: number; onClose: (
|
|
|
17
18
|
ref={menuRef}
|
|
18
19
|
role="menu"
|
|
19
20
|
aria-label="Linked clips"
|
|
20
|
-
className=
|
|
21
|
+
className={`${menuClasses.panel} fixed z-200 min-w-[200px]`}
|
|
21
22
|
style={{ left: x, top: y }}
|
|
22
23
|
>
|
|
23
24
|
<button
|
|
24
25
|
type="button"
|
|
25
26
|
role="menuitemcheckbox"
|
|
26
27
|
aria-checked={visible}
|
|
27
|
-
className=
|
|
28
|
+
className={`${menuClasses.row} ${menuClasses.rowEnabled} flex items-center gap-2`}
|
|
28
29
|
onClick={() => {
|
|
29
30
|
setVisible(!visible);
|
|
30
31
|
onClose();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext, type MouseEvent } from "react";
|
|
1
|
+
import { memo, useContext, type MouseEvent } from "react";
|
|
2
2
|
import { Camera } from "../icons/SystemIcons";
|
|
3
3
|
import { useStudioShellContext } from "../contexts/StudioContext";
|
|
4
4
|
import { usePanelLayoutContext } from "../contexts/PanelLayoutContext";
|
|
@@ -9,7 +9,7 @@ import { InspectorIcon } from "./icons/InspectorIcon";
|
|
|
9
9
|
import { HyperframesLogo } from "./ui/HyperframesLogo";
|
|
10
10
|
import { ShowThemeToggle, ThemeToggle } from "./ThemeToggle";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
interface StudioHeaderProps {
|
|
13
13
|
captureFrameHref: string;
|
|
14
14
|
captureFrameFilename: string;
|
|
15
15
|
handleCaptureFrameClick: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
@@ -35,7 +35,7 @@ export function shouldOpenInspector(
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// fallow-ignore-next-line complexity
|
|
38
|
-
export function StudioHeader({
|
|
38
|
+
export const StudioHeader = memo(function StudioHeader({
|
|
39
39
|
captureFrameHref,
|
|
40
40
|
captureFrameFilename,
|
|
41
41
|
handleCaptureFrameClick,
|
|
@@ -184,4 +184,4 @@ export function StudioHeader({
|
|
|
184
184
|
</div>
|
|
185
185
|
</div>
|
|
186
186
|
);
|
|
187
|
-
}
|
|
187
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, type ReactNode } from "react";
|
|
1
|
+
import { memo, useCallback, type ReactNode } from "react";
|
|
2
2
|
import { SourceEditor } from "./editor/SourceEditor";
|
|
3
3
|
import { FileTree } from "./editor/FileTree";
|
|
4
4
|
import { MediaPreview } from "./MediaPreview";
|
|
@@ -36,7 +36,7 @@ function PanelColumn({ footer, children }: { footer: ReactNode; children: ReactN
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// fallow-ignore-next-line complexity
|
|
39
|
-
export function StudioLeftPanels({
|
|
39
|
+
export const StudioLeftPanels = memo(function StudioLeftPanels({
|
|
40
40
|
onSelectComposition,
|
|
41
41
|
onAddBlock,
|
|
42
42
|
onPreviewBlock,
|
|
@@ -159,7 +159,7 @@ export function StudioLeftPanels({
|
|
|
159
159
|
</Dock.Panel>
|
|
160
160
|
</>
|
|
161
161
|
);
|
|
162
|
-
}
|
|
162
|
+
});
|
|
163
163
|
|
|
164
164
|
function CodeBody({
|
|
165
165
|
projectId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { memo, type ComponentProps } from "react";
|
|
2
2
|
import { LintModal } from "./LintModal";
|
|
3
3
|
import { AskAgentModal } from "./AskAgentModal";
|
|
4
4
|
import { StudioToast } from "./StudioToast";
|
|
@@ -9,7 +9,7 @@ import type { useToast } from "../hooks/useToast";
|
|
|
9
9
|
|
|
10
10
|
type LintFindings = ComponentProps<typeof LintModal>["findings"];
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
interface StudioOverlaysProps {
|
|
13
13
|
projectId: string;
|
|
14
14
|
projectDir?: string | null;
|
|
15
15
|
lintModal: LintFindings | null;
|
|
@@ -28,7 +28,7 @@ export interface StudioOverlaysProps {
|
|
|
28
28
|
* `App.tsx` to keep the shell within the studio's 600-line decomposition budget.
|
|
29
29
|
*/
|
|
30
30
|
// fallow-ignore-next-line complexity
|
|
31
|
-
export function StudioOverlays({
|
|
31
|
+
export const StudioOverlays = memo(function StudioOverlays({
|
|
32
32
|
projectId,
|
|
33
33
|
projectDir,
|
|
34
34
|
lintModal,
|
|
@@ -91,4 +91,4 @@ export function StudioOverlays({
|
|
|
91
91
|
</div>
|
|
92
92
|
</>
|
|
93
93
|
);
|
|
94
|
-
}
|
|
94
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
1
|
+
import { memo, useCallback } from "react";
|
|
2
2
|
import type { StudioRightPanelsProps } from "./StudioRightPanels.types";
|
|
3
3
|
|
|
4
4
|
import { PropertyPanel } from "./editor/PropertyPanel";
|
|
@@ -29,7 +29,9 @@ import { useRemoveBackground } from "../hooks/useRemoveBackground";
|
|
|
29
29
|
import { useApplyColorGradingScope } from "../hooks/useApplyColorGradingScope";
|
|
30
30
|
|
|
31
31
|
// fallow-ignore-next-line complexity
|
|
32
|
-
|
|
32
|
+
const seekToTime = (t: number) => usePlayerStore.getState().requestSeek(t);
|
|
33
|
+
|
|
34
|
+
export const StudioRightPanels = memo(function StudioRightPanels({
|
|
33
35
|
activeBlockParams,
|
|
34
36
|
onCloseBlockParams,
|
|
35
37
|
onDismissBlockParams,
|
|
@@ -166,7 +168,7 @@ export function StudioRightPanels({
|
|
|
166
168
|
handleDomAttributeLiveCommit(attr, value, undefined, { previewOnly: true }),
|
|
167
169
|
[handleDomAttributeLiveCommit],
|
|
168
170
|
);
|
|
169
|
-
const handleHideAllSelected = () => {
|
|
171
|
+
const handleHideAllSelected = useCallback(() => {
|
|
170
172
|
// Audio has no visual to hide, and `data-hidden` on an audio element is what
|
|
171
173
|
// MUTES it — preview silences it and the render drops it from the mix. The
|
|
172
174
|
// timeline withholds the eye on an audio track for that reason
|
|
@@ -181,7 +183,12 @@ export function StudioRightPanels({
|
|
|
181
183
|
const { elements } = usePlayerStore.getState();
|
|
182
184
|
const keys = timelineKeysForSelections(domEditGroupSelections, elements, activeCompPath);
|
|
183
185
|
if (keys.length > 0) void onToggleElementHidden?.(keys, true);
|
|
184
|
-
};
|
|
186
|
+
}, [domEditGroupSelections, showToast, activeCompPath, onToggleElementHidden]);
|
|
187
|
+
const convertToKeyframes = useCallback(
|
|
188
|
+
(animId: string, duration?: number) =>
|
|
189
|
+
handleGsapConvertToKeyframes(animId, undefined, duration),
|
|
190
|
+
[handleGsapConvertToKeyframes],
|
|
191
|
+
);
|
|
185
192
|
const propertyPanel = (
|
|
186
193
|
<DesignPanelPromoteProvider
|
|
187
194
|
selection={domEditGroupSelections.length > 1 ? null : domEditSelection}
|
|
@@ -246,10 +253,8 @@ export function StudioRightPanels({
|
|
|
246
253
|
onCommitAnimatedProperties={commitAnimatedProperties}
|
|
247
254
|
onAddKeyframe={handleGsapAddKeyframe}
|
|
248
255
|
onRemoveKeyframe={handleGsapRemoveKeyframe}
|
|
249
|
-
onConvertToKeyframes={
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
onSeekToTime={(t) => usePlayerStore.getState().requestSeek(t)}
|
|
256
|
+
onConvertToKeyframes={convertToKeyframes}
|
|
257
|
+
onSeekToTime={seekToTime}
|
|
253
258
|
onSetArcPath={handleSetArcPath}
|
|
254
259
|
onUpdateArcSegment={handleUpdateArcSegment}
|
|
255
260
|
onUnroll={handleUnroll}
|
|
@@ -307,4 +312,4 @@ export function StudioRightPanels({
|
|
|
307
312
|
</Dock.Panel>
|
|
308
313
|
</>
|
|
309
314
|
);
|
|
310
|
-
}
|
|
315
|
+
});
|
|
@@ -46,5 +46,5 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function toastSurface(isError: boolean): string {
|
|
49
|
-
return `border shadow-popover
|
|
49
|
+
return `border shadow-popover bg-raised ${isError ? "border-danger/40" : "border-border"}`;
|
|
50
50
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
1
|
+
import { memo, useEffect, useRef } from "react";
|
|
2
2
|
import {
|
|
3
3
|
ArrowsOutLineHorizontal,
|
|
4
4
|
Image,
|
|
@@ -141,7 +141,7 @@ function useKeyframeToggle(session?: DomEditSessionSlice) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// fallow-ignore-next-line complexity
|
|
144
|
-
export function TimelineToolbar({
|
|
144
|
+
export const TimelineToolbar = memo(function TimelineToolbar({
|
|
145
145
|
domEditSession,
|
|
146
146
|
onSplitElement,
|
|
147
147
|
history,
|
|
@@ -558,4 +558,4 @@ export function TimelineToolbar({
|
|
|
558
558
|
</div>
|
|
559
559
|
</div>
|
|
560
560
|
);
|
|
561
|
-
}
|
|
561
|
+
});
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
type PanelId,
|
|
32
32
|
} from "./panelRegistry";
|
|
33
33
|
import "./dock.css";
|
|
34
|
+
import { menuClasses } from "../ui/menuStyle";
|
|
34
35
|
|
|
35
36
|
const PERSIST_DEBOUNCE_MS = 250;
|
|
36
37
|
|
|
@@ -288,10 +289,7 @@ function WindowMenu() {
|
|
|
288
289
|
Window
|
|
289
290
|
</button>
|
|
290
291
|
{open && (
|
|
291
|
-
<div
|
|
292
|
-
role="menu"
|
|
293
|
-
className="absolute right-0 top-8 z-50 w-44 rounded-md border border-neutral-800 bg-neutral-900 py-1 shadow-lg"
|
|
294
|
-
>
|
|
292
|
+
<div role="menu" className={`${menuClasses.panel} absolute right-0 top-8 z-50 w-44`}>
|
|
295
293
|
{panels.map((id) => (
|
|
296
294
|
<button
|
|
297
295
|
key={id}
|
|
@@ -299,13 +297,13 @@ function WindowMenu() {
|
|
|
299
297
|
role="menuitemcheckbox"
|
|
300
298
|
aria-checked={openPanels.has(id)}
|
|
301
299
|
onClick={() => togglePanel(id)}
|
|
302
|
-
className=
|
|
300
|
+
className={`${menuClasses.row} ${menuClasses.rowEnabled} flex items-center gap-2`}
|
|
303
301
|
>
|
|
304
302
|
<span className="w-3 text-accent-ink">{openPanels.has(id) ? "✓" : ""}</span>
|
|
305
303
|
{PANEL_DEFINITIONS[id].title}
|
|
306
304
|
</button>
|
|
307
305
|
))}
|
|
308
|
-
<div className=
|
|
306
|
+
<div className={menuClasses.divider} />
|
|
309
307
|
<button
|
|
310
308
|
type="button"
|
|
311
309
|
role="menuitem"
|
|
@@ -313,7 +311,7 @@ function WindowMenu() {
|
|
|
313
311
|
resetLayout();
|
|
314
312
|
setOpen(false);
|
|
315
313
|
}}
|
|
316
|
-
className=
|
|
314
|
+
className={`${menuClasses.row} ${menuClasses.rowEnabled}`}
|
|
317
315
|
>
|
|
318
316
|
Reset layout
|
|
319
317
|
</button>
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
type ZOrderAction,
|
|
34
34
|
type ZOrderPatch,
|
|
35
35
|
} from "./canvasContextMenuZOrder";
|
|
36
|
+
import { menuClasses } from "../ui/menuStyle";
|
|
36
37
|
|
|
37
38
|
interface CanvasContextMenuProps {
|
|
38
39
|
/** Viewport x of the right-click event. */
|
|
@@ -209,7 +210,7 @@ export const CanvasContextMenu = memo(function CanvasContextMenu({
|
|
|
209
210
|
return createPortal(
|
|
210
211
|
<div
|
|
211
212
|
ref={menuRef}
|
|
212
|
-
className=
|
|
213
|
+
className={`${menuClasses.panel} fixed z-200 min-w-[180px]`}
|
|
213
214
|
style={{ left: adjustedX, top: adjustedY }}
|
|
214
215
|
onPointerDown={stopBubble}
|
|
215
216
|
onMouseDown={stopBubble}
|
|
@@ -252,7 +253,7 @@ export const CanvasContextMenu = memo(function CanvasContextMenu({
|
|
|
252
253
|
);
|
|
253
254
|
})}
|
|
254
255
|
|
|
255
|
-
{hasDivider && <div className=
|
|
256
|
+
{hasDivider && <div className={menuClasses.divider} />}
|
|
256
257
|
|
|
257
258
|
{hasDelete && (
|
|
258
259
|
<button
|