@hyperframes/studio 0.6.97 → 0.6.98
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-DgsMQSvV.js +418 -0
- package/dist/assets/index-B62bDCQv.css +1 -0
- package/dist/assets/index-Ce3pBm_I.js +252 -0
- package/dist/assets/{index-HveJ0MuV.js → index-D-ET9M0b.js} +1 -1
- package/dist/assets/index-D-bS9Dxx.js +1 -0
- package/dist/index.html +2 -2
- package/package.json +7 -5
- package/src/App.tsx +182 -177
- package/src/captions/store.ts +11 -11
- package/src/components/StudioHeader.tsx +4 -4
- package/src/components/StudioLeftSidebar.tsx +2 -2
- package/src/components/StudioPreviewArea.tsx +225 -183
- package/src/components/StudioRightPanel.tsx +3 -3
- package/src/components/TimelineToolbar.tsx +25 -0
- package/src/components/editor/DomEditOverlay.tsx +2 -5
- package/src/components/editor/EaseCurveSection.tsx +2 -3
- package/src/components/editor/GestureTrailOverlay.tsx +4 -3
- package/src/components/editor/LayersPanel.tsx +3 -9
- package/src/components/editor/PropertyPanel.tsx +20 -61
- package/src/components/editor/colorValue.ts +3 -1
- package/src/components/editor/domEditOverlayGestures.ts +54 -1
- package/src/components/editor/domEditOverlayStartGesture.ts +5 -2
- package/src/components/editor/gradientValue.ts +3 -3
- package/src/components/editor/keyframeMove.test.ts +101 -0
- package/src/components/editor/keyframeMove.ts +151 -0
- package/src/components/editor/manualEditsDom.ts +0 -12
- package/src/components/editor/propertyPanelHelpers.ts +10 -38
- package/src/components/editor/propertyPanelMediaSection.tsx +1 -5
- package/src/components/editor/propertyPanelTimingSection.tsx +1 -6
- package/src/components/editor/propertyPanelTransformCommit.ts +129 -0
- package/src/components/editor/studioMotionOps.test.ts +1 -1
- package/src/components/editor/studioMotionOps.ts +2 -1
- package/src/components/editor/useDomEditOverlayGestures.ts +1 -46
- package/src/components/nle/NLELayout.tsx +1 -24
- package/src/components/sidebar/BlocksTab.tsx +2 -2
- package/src/contexts/DomEditContext.tsx +134 -31
- package/src/contexts/StudioContext.tsx +90 -40
- package/src/contexts/TimelineEditContext.tsx +47 -0
- package/src/hooks/domEditCommitTypes.ts +14 -0
- package/src/hooks/gsapDragCommit.ts +9 -24
- package/src/hooks/gsapKeyframeCacheHelpers.ts +2 -1
- package/src/hooks/gsapKeyframeCommit.ts +5 -15
- package/src/hooks/gsapRuntimeBridge.ts +18 -52
- package/src/hooks/gsapRuntimeKeyframes.ts +8 -57
- package/src/hooks/gsapRuntimeReaders.ts +19 -26
- package/src/hooks/gsapScriptCommitHelpers.ts +1 -11
- package/src/hooks/gsapScriptCommitTypes.ts +58 -0
- package/src/hooks/gsapShared.ts +157 -0
- package/src/hooks/timelineEditingHelpers.ts +63 -2
- package/src/hooks/useAnimatedPropertyCommit.ts +3 -25
- package/src/hooks/useAppHotkeys.ts +299 -377
- package/src/hooks/useConsoleErrorCapture.ts +33 -5
- package/src/hooks/useDomEditCommits.ts +35 -293
- package/src/hooks/useDomEditPositionPatchCommit.ts +1 -1
- package/src/hooks/useDomEditSession.ts +78 -249
- package/src/hooks/useDomEditTextCommits.ts +1 -1
- package/src/hooks/useDomEditWiring.ts +255 -0
- package/src/hooks/useDomGeometryCommits.ts +181 -0
- package/src/hooks/useDomSelection.ts +10 -27
- package/src/hooks/useEditorSave.ts +82 -0
- package/src/hooks/useElementLifecycleOps.ts +177 -0
- package/src/hooks/useEnableKeyframes.ts +10 -15
- package/src/hooks/useFileManager.ts +32 -114
- package/src/hooks/useFileTree.ts +80 -0
- package/src/hooks/useGestureCommit.ts +7 -5
- package/src/hooks/useGestureRecording.ts +1 -1
- package/src/hooks/useGsapAnimationOps.ts +122 -0
- package/src/hooks/useGsapArcPathOps.ts +61 -0
- package/src/hooks/useGsapAwareEditing.ts +242 -0
- package/src/hooks/useGsapKeyframeOps.ts +167 -0
- package/src/hooks/useGsapPropertyDebounce.ts +135 -0
- package/src/hooks/useGsapScriptCommits.ts +58 -570
- package/src/hooks/useGsapSelectionHandlers.ts +22 -9
- package/src/hooks/useGsapTweenCache.ts +35 -29
- package/src/hooks/useLintModal.ts +7 -0
- package/src/hooks/useMusicBeatAnalysis.ts +152 -0
- package/src/hooks/useRazorSplit.ts +1 -1
- package/src/hooks/useRenderClipContent.ts +46 -21
- package/src/hooks/useTimelineEditing.ts +48 -4
- package/src/player/components/AudioWaveform.tsx +29 -4
- package/src/player/components/BeatStrip.tsx +166 -0
- package/src/player/components/Timeline.tsx +39 -18
- package/src/player/components/TimelineCanvas.tsx +52 -12
- package/src/player/components/TimelineClipDiamonds.tsx +130 -20
- package/src/player/components/TimelinePropertyRows.tsx +8 -2
- package/src/player/components/TimelineRuler.tsx +36 -2
- package/src/player/components/timelineEditing.ts +30 -5
- package/src/player/components/useTimelineClipDrag.ts +155 -4
- package/src/player/components/useTimelinePlayhead.ts +30 -1
- package/src/player/hooks/useTimelinePlayer.ts +47 -45
- package/src/player/lib/mediaProbe.ts +46 -3
- package/src/player/lib/playbackScrub.ts +16 -0
- package/src/player/lib/timelineDOM.ts +10 -2
- package/src/player/lib/timelineIframeHelpers.ts +89 -0
- package/src/player/store/playerStore.ts +92 -33
- package/src/utils/beatEditActions.ts +109 -0
- package/src/utils/beatEditing.ts +136 -0
- package/src/utils/clipboardPayload.ts +3 -2
- package/src/utils/compositionPatterns.ts +2 -0
- package/src/utils/keyframeSelection.test.ts +45 -0
- package/src/utils/keyframeSelection.ts +29 -0
- package/src/utils/rounding.ts +9 -0
- package/src/utils/studioHelpers.ts +5 -2
- package/src/utils/studioUrlState.ts +2 -1
- package/src/utils/timelineAssetDrop.ts +6 -5
- package/src/utils/timelineInspector.ts +15 -100
- package/dist/assets/hyperframes-player-Daj5djxa.js +0 -418
- package/dist/assets/index-B0twsRu0.css +0 -1
- package/dist/assets/index-Cfye9xzo.js +0 -251
- package/src/components/editor/DopesheetStrip.tsx +0 -141
- package/src/components/editor/StaggerControls.tsx +0 -61
- package/src/components/editor/TimelineLayerPanel.test.ts +0 -42
- package/src/components/editor/TimelineLayerPanel.tsx +0 -15
- package/src/components/nle/TimelineEditorNotice.tsx +0 -133
- package/src/hooks/gsapRuntimePreview.ts +0 -19
- package/src/player/components/timelineUtils.ts +0 -211
- package/src/utils/audioBeatDetection.ts +0 -58
- package/src/utils/keyframeSnapping.test.ts +0 -74
- package/src/utils/keyframeSnapping.ts +0 -63
- package/src/utils/timelineInspector.test.ts +0 -79
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{g as P}from"./index-Ce3pBm_I.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
|
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>HyperFrames Studio</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Ce3pBm_I.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B62bDCQv.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.98",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"types": "./src/index.ts",
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./src/index.ts",
|
|
19
|
-
"./tailwind-preset": "./src/styles/tailwind-preset.ts"
|
|
19
|
+
"./tailwind-preset": "./src/styles/tailwind-preset.ts",
|
|
20
|
+
"./package.json": "./package.json"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"@codemirror/autocomplete": "^6.20.1",
|
|
@@ -30,9 +31,10 @@
|
|
|
30
31
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
31
32
|
"@codemirror/view": "6.40.0",
|
|
32
33
|
"@phosphor-icons/react": "^2.1.10",
|
|
34
|
+
"bpm-detective": "^2.0.5",
|
|
33
35
|
"mediabunny": "^1.45.3",
|
|
34
|
-
"@hyperframes/core": "0.6.
|
|
35
|
-
"@hyperframes/player": "0.6.
|
|
36
|
+
"@hyperframes/core": "0.6.98",
|
|
37
|
+
"@hyperframes/player": "0.6.98"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
38
40
|
"@types/react": "19",
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
"vite": "^6.4.2",
|
|
47
49
|
"vitest": "^3.2.4",
|
|
48
50
|
"zustand": "^5.0.0",
|
|
49
|
-
"@hyperframes/producer": "0.6.
|
|
51
|
+
"@hyperframes/producer": "0.6.98"
|
|
50
52
|
},
|
|
51
53
|
"peerDependencies": {
|
|
52
54
|
"react": "19",
|
package/src/App.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useMemo, useEffect } from "react";
|
|
1
|
+
import { useState, useCallback, useRef, useMemo, useEffect, useLayoutEffect } from "react";
|
|
2
2
|
import type { LeftSidebarHandle, SidebarTab } from "./components/sidebar/LeftSidebar";
|
|
3
3
|
import { useRenderQueue } from "./components/renders/useRenderQueue";
|
|
4
4
|
import { usePlayerStore } from "./player";
|
|
@@ -17,6 +17,7 @@ import { useBlockHandlers } from "./hooks/useBlockHandlers";
|
|
|
17
17
|
import { useAppHotkeys } from "./hooks/useAppHotkeys";
|
|
18
18
|
import { useClipboard } from "./hooks/useClipboard";
|
|
19
19
|
import { readStudioUiPreferences, writeStudioUiPreferences } from "./utils/studioUiPreferences";
|
|
20
|
+
import { selectedKeyframePercentagesForElement } from "./utils/keyframeSelection";
|
|
20
21
|
import { useCaptionDetection } from "./hooks/useCaptionDetection";
|
|
21
22
|
import { useRenderClipContent } from "./hooks/useRenderClipContent";
|
|
22
23
|
import { useConsoleErrorCapture } from "./hooks/useConsoleErrorCapture";
|
|
@@ -37,13 +38,12 @@ import { StudioGlobalDragOverlay } from "./components/StudioGlobalDragOverlay";
|
|
|
37
38
|
import { StudioHeader } from "./components/StudioHeader";
|
|
38
39
|
import { useGestureCommit } from "./hooks/useGestureCommit";
|
|
39
40
|
import { STUDIO_KEYFRAMES_ENABLED } from "./components/editor/manualEditingAvailability";
|
|
40
|
-
|
|
41
41
|
import { GestureTrailOverlay } from "./components/editor/GestureTrailOverlay";
|
|
42
42
|
import { StudioLeftSidebar } from "./components/StudioLeftSidebar";
|
|
43
43
|
import { StudioPreviewArea } from "./components/StudioPreviewArea";
|
|
44
44
|
import { StudioRightPanel } from "./components/StudioRightPanel";
|
|
45
45
|
import { TimelineToolbar } from "./components/TimelineToolbar";
|
|
46
|
-
import {
|
|
46
|
+
import { StudioPlaybackProvider, StudioShellProvider } from "./contexts/StudioContext";
|
|
47
47
|
import { PanelLayoutProvider } from "./contexts/PanelLayoutContext";
|
|
48
48
|
import { FileManagerProvider } from "./contexts/FileManagerContext";
|
|
49
49
|
import { DomEditProvider } from "./contexts/DomEditContext";
|
|
@@ -57,15 +57,13 @@ import {
|
|
|
57
57
|
import { trackStudioSessionStart } from "./telemetry/events";
|
|
58
58
|
import { hasFiredSessionStart, markSessionStartFired } from "./telemetry/config";
|
|
59
59
|
|
|
60
|
+
type CanvasRect = { left: number; top: number; width: number; height: number };
|
|
60
61
|
// fallow-ignore-next-line complexity
|
|
61
62
|
export function StudioApp() {
|
|
62
63
|
const { projectId, resolving, waitingForServer } = useServerConnection();
|
|
63
64
|
const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
|
|
64
65
|
|
|
65
|
-
//
|
|
66
|
-
// remounts, route changes, and any future StudioApp remount within the
|
|
67
|
-
// same tab don't refire `studio_session_start`. `has_project` lets us
|
|
68
|
-
// tell scratch-open from project-context-open.
|
|
66
|
+
// sessionStorage-backed: fires once per tab, survives HMR remounts
|
|
69
67
|
useEffect(() => {
|
|
70
68
|
if (resolving || waitingForServer) return;
|
|
71
69
|
if (hasFiredSessionStart()) return;
|
|
@@ -83,7 +81,6 @@ export function StudioApp() {
|
|
|
83
81
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
84
82
|
const [, setPreviewDocumentVersion] = useState(0);
|
|
85
83
|
const [blockPreview, setBlockPreview] = useState<BlockPreviewInfo | null>(null);
|
|
86
|
-
|
|
87
84
|
const previewIframeRef = useRef<HTMLIFrameElement | null>(null);
|
|
88
85
|
const activeCompPathRef = useRef(activeCompPath);
|
|
89
86
|
activeCompPathRef.current = activeCompPath;
|
|
@@ -107,12 +104,22 @@ export function StudioApp() {
|
|
|
107
104
|
: 0;
|
|
108
105
|
return Math.max(timelineDuration, maxEnd);
|
|
109
106
|
}, [timelineDuration, timelineElements]);
|
|
107
|
+
const refreshTimersRef = useRef<number[]>([]);
|
|
110
108
|
const refreshPreviewDocumentVersion = useCallback(() => {
|
|
109
|
+
for (const id of refreshTimersRef.current) clearTimeout(id);
|
|
110
|
+
refreshTimersRef.current = [];
|
|
111
111
|
setPreviewDocumentVersion((v) => v + 1);
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
refreshTimersRef.current.push(
|
|
113
|
+
window.setTimeout(() => setPreviewDocumentVersion((v) => v + 1), 80),
|
|
114
|
+
window.setTimeout(() => setPreviewDocumentVersion((v) => v + 1), 300),
|
|
115
|
+
);
|
|
114
116
|
}, []);
|
|
115
|
-
|
|
117
|
+
useEffect(
|
|
118
|
+
() => () => {
|
|
119
|
+
for (const id of refreshTimersRef.current) clearTimeout(id);
|
|
120
|
+
},
|
|
121
|
+
[],
|
|
122
|
+
);
|
|
116
123
|
const [timelineVisible, setTimelineVisible] = useState(
|
|
117
124
|
() =>
|
|
118
125
|
initialUrlStateRef.current.timelineVisible ??
|
|
@@ -137,7 +144,6 @@ export function StudioApp() {
|
|
|
137
144
|
const reloadPreview = useCallback(() => {
|
|
138
145
|
setRefreshKey((k) => k + 1);
|
|
139
146
|
}, []);
|
|
140
|
-
|
|
141
147
|
const fileManager = useFileManager({
|
|
142
148
|
projectId,
|
|
143
149
|
showToast,
|
|
@@ -145,11 +151,9 @@ export function StudioApp() {
|
|
|
145
151
|
domEditSaveTimestampRef,
|
|
146
152
|
setRefreshKey,
|
|
147
153
|
});
|
|
148
|
-
|
|
149
154
|
useEffect(() => {
|
|
150
155
|
if (activeCompPathHydrated) return;
|
|
151
156
|
if (!fileManager.fileTreeLoaded) return;
|
|
152
|
-
|
|
153
157
|
const nextCompPath = normalizeStudioCompositionPath(
|
|
154
158
|
initialUrlStateRef.current.activeCompPath,
|
|
155
159
|
fileManager.fileTree,
|
|
@@ -157,7 +161,6 @@ export function StudioApp() {
|
|
|
157
161
|
setActiveCompPath((current) => (current === nextCompPath ? current : nextCompPath));
|
|
158
162
|
setActiveCompPathHydrated(true);
|
|
159
163
|
}, [activeCompPathHydrated, fileManager.fileTree, fileManager.fileTreeLoaded]);
|
|
160
|
-
|
|
161
164
|
const previewPersistence = usePreviewPersistence({
|
|
162
165
|
projectId,
|
|
163
166
|
showToast,
|
|
@@ -170,7 +173,6 @@ export function StudioApp() {
|
|
|
170
173
|
reloadPreview: () => setRefreshKey((k) => k + 1),
|
|
171
174
|
pendingTimelineEditPathRef,
|
|
172
175
|
});
|
|
173
|
-
|
|
174
176
|
const timelineEditing = useTimelineEditing({
|
|
175
177
|
projectId,
|
|
176
178
|
activeCompPath,
|
|
@@ -185,7 +187,6 @@ export function StudioApp() {
|
|
|
185
187
|
uploadProjectFiles: fileManager.uploadProjectFiles,
|
|
186
188
|
isRecordingRef: isGestureRecordingRef,
|
|
187
189
|
});
|
|
188
|
-
|
|
189
190
|
const {
|
|
190
191
|
activeBlockParams,
|
|
191
192
|
setActiveBlockParams,
|
|
@@ -208,7 +209,6 @@ export function StudioApp() {
|
|
|
208
209
|
setRightCollapsed: panelLayout.setRightCollapsed,
|
|
209
210
|
setRightPanelTab: panelLayout.setRightPanelTab,
|
|
210
211
|
});
|
|
211
|
-
|
|
212
212
|
const clearDomSelectionRef = useRef<() => void>(() => {});
|
|
213
213
|
const domEditSelectionBridgeRef = useRef<DomEditSelection | null>(null);
|
|
214
214
|
const handleDomEditElementDeleteRef = useRef<(s: DomEditSelection) => Promise<void>>(
|
|
@@ -265,7 +265,6 @@ export function StudioApp() {
|
|
|
265
265
|
() => leftSidebarRef.current?.getTab() ?? "compositions",
|
|
266
266
|
[],
|
|
267
267
|
);
|
|
268
|
-
|
|
269
268
|
const domEditSession = useDomEditSession({
|
|
270
269
|
projectId,
|
|
271
270
|
activeCompPath,
|
|
@@ -307,13 +306,13 @@ export function StudioApp() {
|
|
|
307
306
|
resetKeyframesRef.current = domEditSession.handleResetSelectedElementKeyframes;
|
|
308
307
|
invalidateGsapCacheRef.current = domEditSession.invalidateGsapCache;
|
|
309
308
|
deleteSelectedKeyframesRef.current = () => {
|
|
310
|
-
const
|
|
309
|
+
const { selectedKeyframes, selectedElementId } = usePlayerStore.getState();
|
|
311
310
|
const a = domEditSession.selectedGsapAnimations.find((x) => x.keyframes);
|
|
312
|
-
if (!a
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
311
|
+
if (!a) return;
|
|
312
|
+
// Only the active element's keyframes; a stale cross-element selection must not delete here.
|
|
313
|
+
for (const p of selectedKeyframePercentagesForElement(selectedKeyframes, selectedElementId)) {
|
|
314
|
+
domEditSession.handleGsapRemoveKeyframe(a.id, p);
|
|
315
|
+
}
|
|
317
316
|
};
|
|
318
317
|
useCaptionDetection({
|
|
319
318
|
projectId,
|
|
@@ -325,20 +324,17 @@ export function StudioApp() {
|
|
|
325
324
|
captionSync,
|
|
326
325
|
setRightCollapsed: panelLayout.setRightCollapsed,
|
|
327
326
|
});
|
|
328
|
-
|
|
329
327
|
const renderClipContent = useRenderClipContent({
|
|
330
328
|
projectIdRef: fileManager.projectIdRef,
|
|
331
329
|
compIdToSrc,
|
|
332
330
|
activePreviewUrl,
|
|
333
331
|
effectiveTimelineDuration,
|
|
334
332
|
});
|
|
335
|
-
|
|
336
333
|
const compositionDimensions = useCompositionDimensions();
|
|
337
|
-
const { lintModal, linting, handleLint, closeLintModal,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}, [findingsByElement]);
|
|
334
|
+
const { lintModal, linting, handleLint, closeLintModal, findingsByFile } = useLintModal(
|
|
335
|
+
projectId,
|
|
336
|
+
refreshKey,
|
|
337
|
+
);
|
|
342
338
|
const frameCapture = useFrameCapture({
|
|
343
339
|
projectId,
|
|
344
340
|
activeCompPath,
|
|
@@ -350,14 +346,12 @@ export function StudioApp() {
|
|
|
350
346
|
setConsoleErrors,
|
|
351
347
|
resetErrors: resetConsoleErrors,
|
|
352
348
|
} = useConsoleErrorCapture(previewIframe);
|
|
353
|
-
|
|
354
349
|
const dragOverlay = useDragOverlay(fileManager.handleImportFiles);
|
|
355
350
|
|
|
356
351
|
// Gesture recording
|
|
357
352
|
const handleToggleRecordingRef = useRef<() => void>(() => {});
|
|
358
353
|
const domEditSessionRef = useRef(domEditSession);
|
|
359
354
|
domEditSessionRef.current = domEditSession;
|
|
360
|
-
|
|
361
355
|
const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
|
|
362
356
|
domEditSessionRef,
|
|
363
357
|
previewIframeRef,
|
|
@@ -365,6 +359,15 @@ export function StudioApp() {
|
|
|
365
359
|
isGestureRecordingRef,
|
|
366
360
|
});
|
|
367
361
|
handleToggleRecordingRef.current = handleToggleRecording;
|
|
362
|
+
const canvasRectRef = useRef<CanvasRect | null>(null);
|
|
363
|
+
useLayoutEffect(() => {
|
|
364
|
+
if (gestureState !== "recording" || !previewIframe) {
|
|
365
|
+
canvasRectRef.current = null;
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const r = previewIframe.getBoundingClientRect();
|
|
369
|
+
canvasRectRef.current = { left: r.left, top: r.top, width: r.width, height: r.height };
|
|
370
|
+
}, [gestureState, previewIframe]);
|
|
368
371
|
|
|
369
372
|
const handlePreviewIframeRef = useCallback(
|
|
370
373
|
(iframe: HTMLIFrameElement | null) => {
|
|
@@ -388,7 +391,6 @@ export function StudioApp() {
|
|
|
388
391
|
},
|
|
389
392
|
[projectId, fileManager],
|
|
390
393
|
);
|
|
391
|
-
|
|
392
394
|
const {
|
|
393
395
|
designPanelActive,
|
|
394
396
|
inspectorPanelActive,
|
|
@@ -400,7 +402,6 @@ export function StudioApp() {
|
|
|
400
402
|
isPlaying,
|
|
401
403
|
gestureState === "recording",
|
|
402
404
|
);
|
|
403
|
-
|
|
404
405
|
useStudioUrlState({
|
|
405
406
|
projectId,
|
|
406
407
|
activeCompPath,
|
|
@@ -418,7 +419,17 @@ export function StudioApp() {
|
|
|
418
419
|
applyDomSelection: domEditSession.applyDomSelection,
|
|
419
420
|
initialState: initialUrlStateRef.current,
|
|
420
421
|
});
|
|
421
|
-
|
|
422
|
+
const { jobs, isRendering, deleteRender, clearCompleted, startRender } = renderQueue;
|
|
423
|
+
const stableRenderQueue = useMemo(
|
|
424
|
+
() => ({
|
|
425
|
+
jobs,
|
|
426
|
+
isRendering,
|
|
427
|
+
deleteRender,
|
|
428
|
+
clearCompleted,
|
|
429
|
+
startRender: startRender as (options: unknown) => Promise<void>,
|
|
430
|
+
}),
|
|
431
|
+
[jobs, isRendering, deleteRender, clearCompleted, startRender],
|
|
432
|
+
);
|
|
422
433
|
const studioCtxValue = buildStudioContextValue({
|
|
423
434
|
projectId: projectId!,
|
|
424
435
|
activeCompPath,
|
|
@@ -434,13 +445,7 @@ export function StudioApp() {
|
|
|
434
445
|
editHistory,
|
|
435
446
|
handleUndo: appHotkeys.handleUndo,
|
|
436
447
|
handleRedo: appHotkeys.handleRedo,
|
|
437
|
-
renderQueue:
|
|
438
|
-
jobs: renderQueue.jobs,
|
|
439
|
-
isRendering: renderQueue.isRendering,
|
|
440
|
-
deleteRender: renderQueue.deleteRender,
|
|
441
|
-
clearCompleted: renderQueue.clearCompleted,
|
|
442
|
-
startRender: renderQueue.startRender as (options: unknown) => Promise<void>,
|
|
443
|
-
},
|
|
448
|
+
renderQueue: stableRenderQueue,
|
|
444
449
|
compositionDimensions,
|
|
445
450
|
waitForPendingDomEditSaves: previewPersistence.waitForPendingDomEditSaves,
|
|
446
451
|
handlePreviewIframeRef,
|
|
@@ -448,147 +453,147 @@ export function StudioApp() {
|
|
|
448
453
|
timelineVisible,
|
|
449
454
|
toggleTimelineVisibility,
|
|
450
455
|
});
|
|
456
|
+
const timelineToolbar = useMemo(
|
|
457
|
+
() => (
|
|
458
|
+
<TimelineToolbar
|
|
459
|
+
toggleTimelineVisibility={toggleTimelineVisibility}
|
|
460
|
+
domEditSession={domEditSession}
|
|
461
|
+
onSplitElement={timelineEditing.handleTimelineElementSplit}
|
|
462
|
+
/>
|
|
463
|
+
),
|
|
464
|
+
[toggleTimelineVisibility, domEditSession, timelineEditing.handleTimelineElementSplit],
|
|
465
|
+
);
|
|
451
466
|
if (resolving || waitingForServer || !projectId)
|
|
452
467
|
return <StudioSplash waiting={waitingForServer} />;
|
|
453
|
-
const timelineToolbar = (
|
|
454
|
-
<TimelineToolbar
|
|
455
|
-
toggleTimelineVisibility={toggleTimelineVisibility}
|
|
456
|
-
domEditSession={domEditSession}
|
|
457
|
-
onSplitElement={timelineEditing.handleTimelineElementSplit}
|
|
458
|
-
/>
|
|
459
|
-
);
|
|
460
468
|
return (
|
|
461
|
-
<
|
|
462
|
-
<
|
|
463
|
-
<
|
|
464
|
-
<
|
|
465
|
-
<
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
{previewPersistence.domEditSaveQueuePaused && (
|
|
483
|
-
<SaveQueuePausedBanner
|
|
484
|
-
message={previewPersistence.domEditSaveQueuePaused}
|
|
485
|
-
onDismiss={previewPersistence.resetDomEditSaveQueueBreaker}
|
|
486
|
-
/>
|
|
487
|
-
)}
|
|
488
|
-
|
|
489
|
-
<div className="flex flex-1 min-h-0">
|
|
490
|
-
<StudioLeftSidebar
|
|
491
|
-
leftSidebarRef={leftSidebarRef}
|
|
492
|
-
onSelectComposition={handleSelectComposition}
|
|
493
|
-
onAddBlock={handleAddBlock}
|
|
494
|
-
onPreviewBlock={setBlockPreview}
|
|
495
|
-
onLint={handleLint}
|
|
496
|
-
linting={linting}
|
|
497
|
-
lintFindingCount={lintModal?.length ?? findingsByFile.size}
|
|
498
|
-
lintFindingsByFile={findingsByFile}
|
|
499
|
-
/>
|
|
500
|
-
<StudioPreviewArea
|
|
501
|
-
timelineToolbar={timelineToolbar}
|
|
502
|
-
renderClipContent={renderClipContent}
|
|
503
|
-
handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
|
|
504
|
-
handleTimelineAssetDrop={timelineEditing.handleTimelineAssetDrop}
|
|
505
|
-
handleTimelineBlockDrop={handleTimelineBlockDrop}
|
|
506
|
-
handlePreviewBlockDrop={handlePreviewBlockDrop}
|
|
507
|
-
handleTimelineFileDrop={timelineEditing.handleTimelineFileDrop}
|
|
508
|
-
handleTimelineElementMove={timelineEditing.handleTimelineElementMove}
|
|
509
|
-
handleTimelineElementResize={timelineEditing.handleTimelineElementResize}
|
|
510
|
-
handleBlockedTimelineEdit={timelineEditing.handleBlockedTimelineEdit}
|
|
511
|
-
handleTimelineElementSplit={timelineEditing.handleTimelineElementSplit}
|
|
512
|
-
handleRazorSplit={timelineEditing.handleRazorSplit}
|
|
513
|
-
handleRazorSplitAll={timelineEditing.handleRazorSplitAll}
|
|
514
|
-
setCompIdToSrc={setCompIdToSrc}
|
|
515
|
-
setCompositionLoading={setCompositionLoading}
|
|
516
|
-
shouldShowSelectedDomBounds={shouldShowSelectedDomBounds}
|
|
517
|
-
isGestureRecording={gestureState === "recording"}
|
|
518
|
-
recordingState={gestureState}
|
|
519
|
-
onToggleRecording={STUDIO_KEYFRAMES_ENABLED ? handleToggleRecording : undefined}
|
|
520
|
-
blockPreview={blockPreview}
|
|
521
|
-
gestureOverlay={
|
|
522
|
-
gestureState === "recording" && previewIframe ? (
|
|
523
|
-
<GestureTrailOverlay
|
|
524
|
-
samples={gestureRecording.samplesRef.current}
|
|
525
|
-
sampleCount={gestureRecording.samplesRef.current.length}
|
|
526
|
-
trail={gestureRecording.trailRef.current}
|
|
527
|
-
canvasRect={(() => {
|
|
528
|
-
const r = previewIframe.getBoundingClientRect();
|
|
529
|
-
return { left: r.left, top: r.top, width: r.width, height: r.height };
|
|
530
|
-
})()}
|
|
531
|
-
compositionSize={compositionDimensions ?? undefined}
|
|
532
|
-
mode="recording"
|
|
533
|
-
/>
|
|
534
|
-
) : undefined
|
|
535
|
-
}
|
|
469
|
+
<StudioShellProvider value={studioCtxValue}>
|
|
470
|
+
<StudioPlaybackProvider value={studioCtxValue}>
|
|
471
|
+
<PanelLayoutProvider value={panelLayout}>
|
|
472
|
+
<FileManagerProvider value={fileManager}>
|
|
473
|
+
<DomEditProvider value={domEditSession}>
|
|
474
|
+
<div
|
|
475
|
+
className="flex flex-col h-full w-full bg-neutral-950 relative"
|
|
476
|
+
onDragOver={dragOverlay.onDragOver}
|
|
477
|
+
onDragEnter={dragOverlay.onDragEnter}
|
|
478
|
+
onDragLeave={dragOverlay.onDragLeave}
|
|
479
|
+
onDrop={dragOverlay.onDrop}
|
|
480
|
+
>
|
|
481
|
+
<StudioHeader
|
|
482
|
+
captureFrameHref={frameCapture.captureFrameHref}
|
|
483
|
+
captureFrameFilename={frameCapture.captureFrameFilename}
|
|
484
|
+
handleCaptureFrameClick={frameCapture.handleCaptureFrameClick}
|
|
485
|
+
refreshCaptureFrameTime={frameCapture.refreshCaptureFrameTime}
|
|
486
|
+
inspectorButtonActive={inspectorButtonActive}
|
|
487
|
+
inspectorPanelActive={inspectorPanelActive}
|
|
488
|
+
onExport={() => void renderQueue.startRender()}
|
|
536
489
|
/>
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
490
|
+
{previewPersistence.domEditSaveQueuePaused && (
|
|
491
|
+
<SaveQueuePausedBanner
|
|
492
|
+
message={previewPersistence.domEditSaveQueuePaused}
|
|
493
|
+
onDismiss={previewPersistence.resetDomEditSaveQueueBreaker}
|
|
494
|
+
/>
|
|
495
|
+
)}
|
|
496
|
+
<div className="flex flex-1 min-h-0">
|
|
497
|
+
<StudioLeftSidebar
|
|
498
|
+
leftSidebarRef={leftSidebarRef}
|
|
499
|
+
onSelectComposition={handleSelectComposition}
|
|
500
|
+
onAddBlock={handleAddBlock}
|
|
501
|
+
onPreviewBlock={setBlockPreview}
|
|
502
|
+
onLint={handleLint}
|
|
503
|
+
linting={linting}
|
|
504
|
+
lintFindingCount={lintModal?.length ?? findingsByFile.size}
|
|
505
|
+
lintFindingsByFile={findingsByFile}
|
|
506
|
+
/>
|
|
507
|
+
<StudioPreviewArea
|
|
508
|
+
timelineToolbar={timelineToolbar}
|
|
509
|
+
renderClipContent={renderClipContent}
|
|
510
|
+
handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
|
|
511
|
+
handleTimelineAssetDrop={timelineEditing.handleTimelineAssetDrop}
|
|
512
|
+
handleTimelineBlockDrop={handleTimelineBlockDrop}
|
|
513
|
+
handlePreviewBlockDrop={handlePreviewBlockDrop}
|
|
514
|
+
handleTimelineFileDrop={timelineEditing.handleTimelineFileDrop}
|
|
515
|
+
handleTimelineElementMove={timelineEditing.handleTimelineElementMove}
|
|
516
|
+
handleTimelineElementResize={timelineEditing.handleTimelineElementResize}
|
|
517
|
+
handleBlockedTimelineEdit={timelineEditing.handleBlockedTimelineEdit}
|
|
518
|
+
handleTimelineElementSplit={timelineEditing.handleTimelineElementSplit}
|
|
519
|
+
handleRazorSplit={timelineEditing.handleRazorSplit}
|
|
520
|
+
handleRazorSplitAll={timelineEditing.handleRazorSplitAll}
|
|
521
|
+
setCompIdToSrc={setCompIdToSrc}
|
|
522
|
+
setCompositionLoading={setCompositionLoading}
|
|
523
|
+
shouldShowSelectedDomBounds={shouldShowSelectedDomBounds}
|
|
524
|
+
isGestureRecording={gestureState === "recording"}
|
|
546
525
|
recordingState={gestureState}
|
|
547
|
-
recordingDuration={gestureRecording.recordingDuration}
|
|
548
526
|
onToggleRecording={STUDIO_KEYFRAMES_ENABLED ? handleToggleRecording : undefined}
|
|
527
|
+
blockPreview={blockPreview}
|
|
528
|
+
gestureOverlay={
|
|
529
|
+
gestureState === "recording" && previewIframe ? (
|
|
530
|
+
<GestureTrailOverlay
|
|
531
|
+
samples={gestureRecording.samplesRef.current}
|
|
532
|
+
sampleCount={gestureRecording.samplesRef.current.length}
|
|
533
|
+
trail={gestureRecording.trailRef.current}
|
|
534
|
+
canvasRect={canvasRectRef.current!}
|
|
535
|
+
compositionSize={compositionDimensions ?? undefined}
|
|
536
|
+
mode="recording"
|
|
537
|
+
/>
|
|
538
|
+
) : undefined
|
|
539
|
+
}
|
|
549
540
|
/>
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
<AskAgentModal
|
|
565
|
-
selectionLabel={domEditSession.domEditSelection.label}
|
|
566
|
-
contextPreview={buildAgentContextPreview(
|
|
567
|
-
domEditSession.domEditSelection,
|
|
568
|
-
activeCompPath,
|
|
541
|
+
{!panelLayout.rightCollapsed && (
|
|
542
|
+
<StudioRightPanel
|
|
543
|
+
designPanelActive={designPanelActive}
|
|
544
|
+
activeBlockParams={activeBlockParams}
|
|
545
|
+
onCloseBlockParams={() => {
|
|
546
|
+
setActiveBlockParams(null);
|
|
547
|
+
panelLayout.setRightPanelTab("design");
|
|
548
|
+
}}
|
|
549
|
+
recordingState={gestureState}
|
|
550
|
+
recordingDuration={gestureRecording.recordingDuration}
|
|
551
|
+
onToggleRecording={
|
|
552
|
+
STUDIO_KEYFRAMES_ENABLED ? handleToggleRecording : undefined
|
|
553
|
+
}
|
|
554
|
+
/>
|
|
569
555
|
)}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
556
|
+
</div>
|
|
557
|
+
{lintModal !== null && (
|
|
558
|
+
<LintModal findings={lintModal} projectId={projectId} onClose={closeLintModal} />
|
|
559
|
+
)}
|
|
560
|
+
{consoleErrors !== null && consoleErrors.length > 0 && (
|
|
561
|
+
<LintModal
|
|
562
|
+
findings={consoleErrors}
|
|
563
|
+
projectId={projectId}
|
|
564
|
+
onClose={() => setConsoleErrors(null)}
|
|
565
|
+
/>
|
|
566
|
+
)}
|
|
567
|
+
{domEditSession.agentModalOpen && domEditSession.domEditSelection && (
|
|
568
|
+
<AskAgentModal
|
|
569
|
+
selectionLabel={domEditSession.domEditSelection.label}
|
|
570
|
+
contextPreview={buildAgentContextPreview(
|
|
571
|
+
domEditSession.domEditSelection,
|
|
572
|
+
activeCompPath,
|
|
573
|
+
)}
|
|
574
|
+
anchorPoint={domEditSession.agentModalAnchorPoint}
|
|
575
|
+
onSubmit={domEditSession.handleAgentModalSubmit}
|
|
576
|
+
onClose={() => {
|
|
577
|
+
domEditSession.setAgentModalOpen(false);
|
|
578
|
+
domEditSession.setAgentPromptSelectionContext(undefined);
|
|
579
|
+
domEditSession.setAgentModalAnchorPoint(null);
|
|
580
|
+
}}
|
|
581
|
+
/>
|
|
582
|
+
)}
|
|
579
583
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
584
|
+
{dragOverlay.active && <StudioGlobalDragOverlay />}
|
|
585
|
+
{appToast && (
|
|
586
|
+
<StudioToast
|
|
587
|
+
message={appToast.message}
|
|
588
|
+
tone={appToast.tone}
|
|
589
|
+
onDismiss={dismissToast}
|
|
590
|
+
/>
|
|
591
|
+
)}
|
|
592
|
+
</div>
|
|
593
|
+
</DomEditProvider>
|
|
594
|
+
</FileManagerProvider>
|
|
595
|
+
</PanelLayoutProvider>
|
|
596
|
+
</StudioPlaybackProvider>
|
|
597
|
+
</StudioShellProvider>
|
|
593
598
|
);
|
|
594
599
|
}
|
package/src/captions/store.ts
CHANGED
|
@@ -59,7 +59,7 @@ const initialState = {
|
|
|
59
59
|
sourceFilePath: null,
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
export const useCaptionStore = create<CaptionState>((set) => ({
|
|
62
|
+
export const useCaptionStore = create<CaptionState>((set, get) => ({
|
|
63
63
|
...initialState,
|
|
64
64
|
|
|
65
65
|
// Basic
|
|
@@ -82,15 +82,11 @@ export const useCaptionStore = create<CaptionState>((set) => ({
|
|
|
82
82
|
return { selectedSegmentIds: new Set([id]), selectedGroupId: null };
|
|
83
83
|
}),
|
|
84
84
|
|
|
85
|
-
selectGroup: (id) =>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
selectedSegmentIds: new Set(group.segmentIds),
|
|
91
|
-
selectedGroupId: id,
|
|
92
|
-
};
|
|
93
|
-
}),
|
|
85
|
+
selectGroup: (id) => {
|
|
86
|
+
const group = get().model?.groups.get(id);
|
|
87
|
+
if (!group) return;
|
|
88
|
+
set({ selectedSegmentIds: new Set(group.segmentIds), selectedGroupId: id });
|
|
89
|
+
},
|
|
94
90
|
|
|
95
91
|
selectAll: () =>
|
|
96
92
|
set((state) => {
|
|
@@ -101,7 +97,11 @@ export const useCaptionStore = create<CaptionState>((set) => ({
|
|
|
101
97
|
};
|
|
102
98
|
}),
|
|
103
99
|
|
|
104
|
-
clearSelection: () =>
|
|
100
|
+
clearSelection: () => {
|
|
101
|
+
const { selectedSegmentIds, selectedGroupId } = get();
|
|
102
|
+
if (selectedSegmentIds.size === 0 && selectedGroupId === null) return;
|
|
103
|
+
set({ selectedSegmentIds: new Set(), selectedGroupId: null });
|
|
104
|
+
},
|
|
105
105
|
|
|
106
106
|
// Segment mutations
|
|
107
107
|
updateSegmentStyle: (segmentId, style) =>
|