@hyperframes/studio 0.6.0-alpha.9 → 0.6.1
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-CzwFysqv.js +418 -0
- package/dist/assets/index-D1JDq7Gg.css +1 -0
- package/dist/assets/index-hYc4aP7M.js +117 -0
- package/dist/favicon.svg +14 -0
- package/dist/index.html +3 -2
- package/package.json +9 -9
- package/src/App.tsx +421 -4303
- package/src/captions/components/CaptionOverlay.tsx +13 -246
- package/src/captions/components/CaptionOverlayUtils.ts +221 -0
- package/src/components/AskAgentModal.tsx +120 -0
- package/src/components/StudioHeader.tsx +133 -0
- package/src/components/StudioLeftSidebar.tsx +125 -0
- package/src/components/StudioPreviewArea.tsx +167 -0
- package/src/components/StudioRightPanel.tsx +198 -0
- package/src/components/TimelineToolbar.tsx +89 -0
- package/src/components/editor/DomEditOverlay.tsx +88 -993
- package/src/components/editor/EaseCurveEditor.tsx +221 -0
- package/src/components/editor/FileTree.tsx +13 -621
- package/src/components/editor/FileTreeIcons.tsx +128 -0
- package/src/components/editor/FileTreeNodes.tsx +496 -0
- package/src/components/editor/MotionPanel.tsx +16 -390
- package/src/components/editor/MotionPanelFields.tsx +185 -0
- package/src/components/editor/PropertyPanel.test.ts +0 -49
- package/src/components/editor/PropertyPanel.tsx +132 -2763
- package/src/components/editor/domEditOverlayGeometry.ts +211 -0
- package/src/components/editor/domEditOverlayGestures.ts +138 -0
- package/src/components/editor/domEditOverlayStartGesture.ts +155 -0
- package/src/components/editor/domEditing.ts +44 -1117
- package/src/components/editor/domEditingAgentPrompt.ts +97 -0
- package/src/components/editor/domEditingDom.ts +266 -0
- package/src/components/editor/domEditingElement.ts +329 -0
- package/src/components/editor/domEditingLayers.ts +460 -0
- package/src/components/editor/domEditingTypes.ts +125 -0
- package/src/components/editor/manualEditingAvailability.test.ts +2 -2
- package/src/components/editor/manualEditingAvailability.ts +1 -1
- package/src/components/editor/manualEdits.ts +84 -1049
- package/src/components/editor/manualEditsDom.ts +436 -0
- package/src/components/editor/manualEditsParsing.ts +280 -0
- package/src/components/editor/manualEditsSnapshot.ts +333 -0
- package/src/components/editor/manualEditsTypes.ts +141 -0
- package/src/components/editor/propertyPanelColor.tsx +371 -0
- package/src/components/editor/propertyPanelFill.tsx +421 -0
- package/src/components/editor/propertyPanelFont.tsx +455 -0
- package/src/components/editor/propertyPanelHelpers.ts +401 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +357 -0
- package/src/components/editor/propertyPanelSections.tsx +453 -0
- package/src/components/editor/propertyPanelStyleSections.tsx +411 -0
- package/src/components/editor/studioMotion.ts +47 -434
- package/src/components/editor/studioMotionOps.ts +299 -0
- package/src/components/editor/studioMotionTypes.ts +168 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +393 -0
- package/src/components/editor/useDomEditOverlayRects.ts +207 -0
- package/src/components/nle/NLELayout.tsx +68 -155
- package/src/components/nle/NLEPreview.tsx +3 -0
- package/src/components/nle/useCompositionStack.ts +126 -0
- package/src/components/renders/RenderQueue.tsx +102 -31
- package/src/components/renders/useRenderQueue.ts +8 -2
- package/src/components/sidebar/LeftSidebar.tsx +186 -186
- package/src/contexts/DomEditContext.tsx +137 -0
- package/src/contexts/FileManagerContext.tsx +110 -0
- package/src/contexts/PanelLayoutContext.tsx +68 -0
- package/src/contexts/StudioContext.tsx +135 -0
- package/src/hooks/useAppHotkeys.ts +326 -0
- package/src/hooks/useAskAgentModal.ts +162 -0
- package/src/hooks/useCaptionDetection.ts +132 -0
- package/src/hooks/useCompositionDimensions.ts +25 -0
- package/src/hooks/useConsoleErrorCapture.ts +60 -0
- package/src/hooks/useDomEditCommits.ts +437 -0
- package/src/hooks/useDomEditSession.ts +342 -0
- package/src/hooks/useDomEditTextCommits.ts +330 -0
- package/src/hooks/useDomSelection.ts +398 -0
- package/src/hooks/useFileManager.ts +431 -0
- package/src/hooks/useFrameCapture.ts +77 -0
- package/src/hooks/useLintModal.ts +35 -0
- package/src/hooks/useManifestPersistence.ts +492 -0
- package/src/hooks/usePanelLayout.ts +68 -0
- package/src/hooks/usePreviewInteraction.ts +153 -0
- package/src/hooks/useRenderClipContent.ts +124 -0
- package/src/hooks/useTimelineEditing.ts +472 -0
- package/src/hooks/useToast.ts +20 -0
- package/src/player/components/Player.tsx +33 -2
- package/src/player/components/Timeline.test.ts +0 -8
- package/src/player/components/Timeline.tsx +196 -1518
- package/src/player/components/TimelineCanvas.tsx +434 -0
- package/src/player/components/TimelineClip.tsx +9 -244
- package/src/player/components/TimelineEmptyState.tsx +102 -0
- package/src/player/components/TimelineRuler.tsx +90 -0
- package/src/player/components/timelineIcons.tsx +49 -0
- package/src/player/components/timelineLayout.ts +215 -0
- package/src/player/components/timelineUtils.ts +211 -0
- package/src/player/components/useTimelineClipDrag.ts +388 -0
- package/src/player/components/useTimelinePlayhead.ts +200 -0
- package/src/player/components/useTimelineRangeSelection.ts +135 -0
- package/src/player/hooks/usePlaybackKeyboard.ts +171 -0
- package/src/player/hooks/useTimelinePlayer.ts +105 -1371
- package/src/player/hooks/useTimelineSyncCallbacks.ts +288 -0
- package/src/player/lib/playbackAdapter.ts +145 -0
- package/src/player/lib/playbackShortcuts.ts +68 -0
- package/src/player/lib/playbackTypes.ts +60 -0
- package/src/player/lib/timelineDOM.ts +373 -0
- package/src/player/lib/timelineElementHelpers.ts +303 -0
- package/src/player/lib/timelineIframeHelpers.ts +269 -0
- package/src/utils/domEditHelpers.ts +50 -0
- package/src/utils/studioFontHelpers.ts +83 -0
- package/src/utils/studioHelpers.ts +214 -0
- package/src/utils/studioPreviewHelpers.ts +185 -0
- package/src/utils/timelineDiscovery.ts +1 -1
- package/dist/assets/hyperframes-player-DjsVzYFP.js +0 -418
- package/dist/assets/index-14zH9lqh.css +0 -1
- package/dist/assets/index-DYCiFGWQ.js +0 -108
- package/src/player/components/TimelineClip.test.ts +0 -92
|
@@ -1,437 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
updatedAt?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type StudioGsapMotionPreset = "fade-up" | "slide" | "pop";
|
|
52
|
-
export type StudioGsapMotionDirection = "up" | "down" | "left" | "right";
|
|
53
|
-
|
|
54
|
-
export const STUDIO_GSAP_EASE_OPTIONS = [
|
|
55
|
-
"none",
|
|
56
|
-
"power1.in",
|
|
57
|
-
"power1.out",
|
|
58
|
-
"power1.inOut",
|
|
59
|
-
"power2.in",
|
|
60
|
-
"power2.out",
|
|
61
|
-
"power2.inOut",
|
|
62
|
-
"power3.in",
|
|
63
|
-
"power3.out",
|
|
64
|
-
"power3.inOut",
|
|
65
|
-
"power4.in",
|
|
66
|
-
"power4.out",
|
|
67
|
-
"power4.inOut",
|
|
68
|
-
"sine.in",
|
|
69
|
-
"sine.out",
|
|
70
|
-
"sine.inOut",
|
|
71
|
-
"expo.in",
|
|
72
|
-
"expo.out",
|
|
73
|
-
"expo.inOut",
|
|
74
|
-
"circ.in",
|
|
75
|
-
"circ.out",
|
|
76
|
-
"circ.inOut",
|
|
77
|
-
"back.in(1.7)",
|
|
78
|
-
"back.out(1.7)",
|
|
79
|
-
"back.inOut(1.7)",
|
|
80
|
-
"elastic.out(1, 0.45)",
|
|
81
|
-
"bounce.out",
|
|
82
|
-
] as const;
|
|
83
|
-
|
|
84
|
-
const DEFAULT_CUSTOM_EASE_POINTS: StudioCustomEaseControlPoints = {
|
|
85
|
-
x1: 0.215,
|
|
86
|
-
y1: 0.61,
|
|
87
|
-
x2: 0.355,
|
|
88
|
-
y2: 1,
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const GSAP_EASE_CONTROL_POINTS: Record<string, StudioCustomEaseControlPoints> = {
|
|
92
|
-
none: { x1: 0, y1: 0, x2: 1, y2: 1 },
|
|
93
|
-
"power1.in": { x1: 0.55, y1: 0.085, x2: 0.68, y2: 0.53 },
|
|
94
|
-
"power1.out": { x1: 0.25, y1: 0.46, x2: 0.45, y2: 0.94 },
|
|
95
|
-
"power1.inOut": { x1: 0.455, y1: 0.03, x2: 0.515, y2: 0.955 },
|
|
96
|
-
"power2.in": { x1: 0.55, y1: 0.055, x2: 0.675, y2: 0.19 },
|
|
97
|
-
"power2.out": { x1: 0.215, y1: 0.61, x2: 0.355, y2: 1 },
|
|
98
|
-
"power2.inOut": { x1: 0.645, y1: 0.045, x2: 0.355, y2: 1 },
|
|
99
|
-
"power3.in": { x1: 0.895, y1: 0.03, x2: 0.685, y2: 0.22 },
|
|
100
|
-
"power3.out": { x1: 0.165, y1: 0.84, x2: 0.44, y2: 1 },
|
|
101
|
-
"power3.inOut": { x1: 0.77, y1: 0, x2: 0.175, y2: 1 },
|
|
102
|
-
"power4.in": { x1: 0.755, y1: 0.05, x2: 0.855, y2: 0.06 },
|
|
103
|
-
"power4.out": { x1: 0.23, y1: 1, x2: 0.32, y2: 1 },
|
|
104
|
-
"power4.inOut": { x1: 0.86, y1: 0, x2: 0.07, y2: 1 },
|
|
105
|
-
"sine.in": { x1: 0.47, y1: 0, x2: 0.745, y2: 0.715 },
|
|
106
|
-
"sine.out": { x1: 0.39, y1: 0.575, x2: 0.565, y2: 1 },
|
|
107
|
-
"sine.inOut": { x1: 0.445, y1: 0.05, x2: 0.55, y2: 0.95 },
|
|
108
|
-
"expo.in": { x1: 0.95, y1: 0.05, x2: 0.795, y2: 0.035 },
|
|
109
|
-
"expo.out": { x1: 0.19, y1: 1, x2: 0.22, y2: 1 },
|
|
110
|
-
"expo.inOut": { x1: 1, y1: 0, x2: 0, y2: 1 },
|
|
111
|
-
"circ.in": { x1: 0.6, y1: 0.04, x2: 0.98, y2: 0.335 },
|
|
112
|
-
"circ.out": { x1: 0.075, y1: 0.82, x2: 0.165, y2: 1 },
|
|
113
|
-
"circ.inOut": { x1: 0.785, y1: 0.135, x2: 0.15, y2: 0.86 },
|
|
114
|
-
"back.in(1.7)": { x1: 0.6, y1: -0.28, x2: 0.735, y2: 0.045 },
|
|
115
|
-
"back.out(1.7)": { x1: 0.175, y1: 0.885, x2: 0.32, y2: 1.275 },
|
|
116
|
-
"back.inOut(1.7)": { x1: 0.68, y1: -0.55, x2: 0.265, y2: 1.55 },
|
|
117
|
-
"elastic.out(1, 0.45)": { x1: 0.16, y1: 1.32, x2: 0.28, y2: 0.86 },
|
|
118
|
-
"bounce.out": { x1: 0.34, y1: 1.56, x2: 0.64, y2: 0.74 },
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const CUSTOM_EASE_DATA_PATTERN =
|
|
122
|
-
/^M\s*0\s*,\s*0\s*C\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s+1\s*,\s*1\s*$/i;
|
|
123
|
-
|
|
124
|
-
export interface StudioGsapPresetMotionOptions {
|
|
125
|
-
start: number;
|
|
126
|
-
duration: number;
|
|
127
|
-
distance: number;
|
|
128
|
-
ease: string;
|
|
129
|
-
direction?: StudioGsapMotionDirection;
|
|
130
|
-
customEase?: StudioGsapCustomEase;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface StudioMotionManifest {
|
|
134
|
-
version: 1;
|
|
135
|
-
motions: StudioGsapMotion[];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
interface StudioGsapTimeline {
|
|
139
|
-
fromTo?: (
|
|
140
|
-
target: HTMLElement,
|
|
141
|
-
from: Record<string, unknown>,
|
|
142
|
-
to: Record<string, unknown>,
|
|
143
|
-
at: number,
|
|
144
|
-
) => StudioGsapTimeline;
|
|
145
|
-
time?: (time: number) => StudioGsapTimeline;
|
|
146
|
-
totalTime?: (time: number, suppressEvents?: boolean) => StudioGsapTimeline;
|
|
147
|
-
pause?: () => StudioGsapTimeline;
|
|
148
|
-
kill?: () => void;
|
|
149
|
-
duration?: () => number;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
type StudioMotionWindow = Window & {
|
|
153
|
-
gsap?: {
|
|
154
|
-
timeline?: (vars?: Record<string, unknown>) => StudioGsapTimeline;
|
|
155
|
-
set?: (target: HTMLElement, vars: Record<string, unknown>) => void;
|
|
156
|
-
registerPlugin?: (...plugins: unknown[]) => void;
|
|
157
|
-
};
|
|
158
|
-
CustomEase?: { create?: (id: string, data: string) => void };
|
|
159
|
-
__player?: {
|
|
160
|
-
getTime?: () => number;
|
|
161
|
-
renderSeek?: (time: number) => void;
|
|
162
|
-
seek?: (time: number) => void;
|
|
163
|
-
};
|
|
164
|
-
__timeline?: { time?: () => number };
|
|
165
|
-
__timelines?: Record<string, StudioGsapTimeline | undefined>;
|
|
166
|
-
__hfStudioMotionApply?: () => number;
|
|
167
|
-
__hfStudioMotionWrapped?: boolean;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
export function emptyStudioMotionManifest(): StudioMotionManifest {
|
|
171
|
-
return { version: 1, motions: [] };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function clampPositiveNumber(value: number, fallback: number): number {
|
|
175
|
-
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function clampNonNegativeNumber(value: number, fallback: number): number {
|
|
179
|
-
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function sanitizeEase(value: string): string {
|
|
183
|
-
return value.trim() || "none";
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function roundEaseNumber(value: number): number {
|
|
187
|
-
return Math.round(value * 1000) / 1000;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function clampRange(value: number, min: number, max: number, fallback: number): number {
|
|
191
|
-
return Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function clampStudioCustomEasePoints(
|
|
195
|
-
points: Partial<StudioCustomEaseControlPoints>,
|
|
196
|
-
): StudioCustomEaseControlPoints {
|
|
197
|
-
return {
|
|
198
|
-
x1: roundEaseNumber(clampRange(points.x1 ?? DEFAULT_CUSTOM_EASE_POINTS.x1, 0, 1, 0.215)),
|
|
199
|
-
y1: roundEaseNumber(clampRange(points.y1 ?? DEFAULT_CUSTOM_EASE_POINTS.y1, -0.6, 1.6, 0.61)),
|
|
200
|
-
x2: roundEaseNumber(clampRange(points.x2 ?? DEFAULT_CUSTOM_EASE_POINTS.x2, 0, 1, 0.355)),
|
|
201
|
-
y2: roundEaseNumber(clampRange(points.y2 ?? DEFAULT_CUSTOM_EASE_POINTS.y2, -0.6, 1.6, 1)),
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function parseStudioCustomEaseData(
|
|
206
|
-
data: string | undefined,
|
|
207
|
-
): StudioCustomEaseControlPoints | null {
|
|
208
|
-
if (!data) return null;
|
|
209
|
-
const match = data.trim().match(CUSTOM_EASE_DATA_PATTERN);
|
|
210
|
-
if (!match) return null;
|
|
211
|
-
const points = {
|
|
212
|
-
x1: Number.parseFloat(match[1] ?? ""),
|
|
213
|
-
y1: Number.parseFloat(match[2] ?? ""),
|
|
214
|
-
x2: Number.parseFloat(match[3] ?? ""),
|
|
215
|
-
y2: Number.parseFloat(match[4] ?? ""),
|
|
216
|
-
};
|
|
217
|
-
if (!Object.values(points).every(Number.isFinite)) return null;
|
|
218
|
-
return clampStudioCustomEasePoints(points);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function formatEaseNumber(value: number): string {
|
|
222
|
-
const rounded = roundEaseNumber(value);
|
|
223
|
-
if (Object.is(rounded, -0)) return "0";
|
|
224
|
-
return `${rounded}`;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function serializeStudioCustomEaseData(points: StudioCustomEaseControlPoints): string {
|
|
228
|
-
const clamped = clampStudioCustomEasePoints(points);
|
|
229
|
-
return `M0,0 C${formatEaseNumber(clamped.x1)},${formatEaseNumber(clamped.y1)} ${formatEaseNumber(clamped.x2)},${formatEaseNumber(clamped.y2)} 1,1`;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export function controlPointsForGsapEase(ease: string): StudioCustomEaseControlPoints {
|
|
233
|
-
return GSAP_EASE_CONTROL_POINTS[ease] ?? DEFAULT_CUSTOM_EASE_POINTS;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export function buildStudioGsapPresetMotion(
|
|
237
|
-
preset: StudioGsapMotionPreset,
|
|
238
|
-
options: StudioGsapPresetMotionOptions,
|
|
239
|
-
): Omit<StudioGsapMotion, "kind" | "target" | "updatedAt"> {
|
|
240
|
-
const start = clampNonNegativeNumber(options.start, 0);
|
|
241
|
-
const duration = clampPositiveNumber(options.duration, 0.6);
|
|
242
|
-
const distance = clampPositiveNumber(options.distance, 32);
|
|
243
|
-
const ease = sanitizeEase(options.ease);
|
|
244
|
-
const direction = options.direction ?? "up";
|
|
245
|
-
const base = { start, duration, ease, customEase: options.customEase };
|
|
246
|
-
|
|
247
|
-
if (preset === "pop") {
|
|
248
|
-
return {
|
|
249
|
-
...base,
|
|
250
|
-
from: { scale: 0.88, autoAlpha: 0 },
|
|
251
|
-
to: { scale: 1, autoAlpha: 1 },
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (preset === "slide") {
|
|
256
|
-
const x = direction === "right" ? -distance : direction === "left" ? distance : 0;
|
|
257
|
-
const y = direction === "down" ? -distance : direction === "up" ? distance : 0;
|
|
258
|
-
return {
|
|
259
|
-
...base,
|
|
260
|
-
from: { x, y, autoAlpha: 0 },
|
|
261
|
-
to: { x: 0, y: 0, autoAlpha: 1 },
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
...base,
|
|
267
|
-
from: { y: direction === "down" ? -distance : distance, autoAlpha: 0 },
|
|
268
|
-
to: { y: 0, autoAlpha: 1 },
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function finiteNumber(value: unknown): number | null {
|
|
273
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function parseMotionValues(value: unknown): StudioGsapMotionValues | null {
|
|
277
|
-
if (!value || typeof value !== "object") return null;
|
|
278
|
-
const record = value as Record<string, unknown>;
|
|
279
|
-
const parsed: StudioGsapMotionValues = {};
|
|
280
|
-
for (const key of ["x", "y", "scale", "rotation", "opacity", "autoAlpha"] as const) {
|
|
281
|
-
const next = finiteNumber(record[key]);
|
|
282
|
-
if (next != null) parsed[key] = next;
|
|
283
|
-
}
|
|
284
|
-
return Object.keys(parsed).length > 0 ? parsed : null;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function parseTarget(value: unknown): StudioMotionTarget | null {
|
|
288
|
-
if (!value || typeof value !== "object") return null;
|
|
289
|
-
const record = value as Record<string, unknown>;
|
|
290
|
-
const sourceFile = typeof record.sourceFile === "string" ? record.sourceFile : "";
|
|
291
|
-
if (!sourceFile) return null;
|
|
292
|
-
const selector = typeof record.selector === "string" ? record.selector : undefined;
|
|
293
|
-
const id = typeof record.id === "string" ? record.id : undefined;
|
|
294
|
-
if (!selector && !id) return null;
|
|
295
|
-
return {
|
|
296
|
-
sourceFile,
|
|
297
|
-
selector,
|
|
298
|
-
selectorIndex: finiteNumber(record.selectorIndex) ?? undefined,
|
|
299
|
-
id,
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function parseCustomEase(value: unknown): StudioGsapCustomEase | undefined {
|
|
304
|
-
if (!value || typeof value !== "object") return undefined;
|
|
305
|
-
const record = value as Record<string, unknown>;
|
|
306
|
-
const id = typeof record.id === "string" ? record.id.trim() : "";
|
|
307
|
-
const data = typeof record.data === "string" ? record.data.trim() : "";
|
|
308
|
-
if (!id || !data) return undefined;
|
|
309
|
-
return { id, data };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function parseGsapMotion(value: unknown): StudioGsapMotion | null {
|
|
313
|
-
if (!value || typeof value !== "object") return null;
|
|
314
|
-
const record = value as Record<string, unknown>;
|
|
315
|
-
if (record.kind !== "gsap-motion") return null;
|
|
316
|
-
const target = parseTarget(record.target);
|
|
317
|
-
if (!target) return null;
|
|
318
|
-
const start = finiteNumber(record.start);
|
|
319
|
-
const duration = finiteNumber(record.duration);
|
|
320
|
-
if (start == null || duration == null || start < 0 || duration <= 0) return null;
|
|
321
|
-
const ease = typeof record.ease === "string" && record.ease.trim() ? record.ease.trim() : "none";
|
|
322
|
-
const from = parseMotionValues(record.from);
|
|
323
|
-
const to = parseMotionValues(record.to);
|
|
324
|
-
if (!from || !to) return null;
|
|
325
|
-
return {
|
|
326
|
-
kind: "gsap-motion",
|
|
327
|
-
target,
|
|
328
|
-
start,
|
|
329
|
-
duration,
|
|
330
|
-
ease,
|
|
331
|
-
customEase: parseCustomEase(record.customEase),
|
|
332
|
-
from,
|
|
333
|
-
to,
|
|
334
|
-
updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : undefined,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export function parseStudioMotionManifest(content: string): StudioMotionManifest {
|
|
339
|
-
if (!content.trim()) return emptyStudioMotionManifest();
|
|
340
|
-
try {
|
|
341
|
-
const parsed = JSON.parse(content) as unknown;
|
|
342
|
-
if (!parsed || typeof parsed !== "object") return emptyStudioMotionManifest();
|
|
343
|
-
const motions = (parsed as { motions?: unknown }).motions;
|
|
344
|
-
if (!Array.isArray(motions)) return emptyStudioMotionManifest();
|
|
345
|
-
return {
|
|
346
|
-
version: 1,
|
|
347
|
-
motions: motions
|
|
348
|
-
.map(parseGsapMotion)
|
|
349
|
-
.filter((motion): motion is StudioGsapMotion => motion !== null),
|
|
350
|
-
};
|
|
351
|
-
} catch {
|
|
352
|
-
return emptyStudioMotionManifest();
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
export function serializeStudioMotionManifest(manifest: StudioMotionManifest): string {
|
|
357
|
-
return `${JSON.stringify(manifest, null, 2)}\n`;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function normalizeStudioFileChangePath(path: string): string {
|
|
361
|
-
return path
|
|
362
|
-
.trim()
|
|
363
|
-
.replace(/\\/g, "/")
|
|
364
|
-
.replace(/^\.?\//, "");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export function isStudioMotionManifestPath(path: string | null): boolean {
|
|
368
|
-
if (!path) return false;
|
|
369
|
-
const normalized = normalizeStudioFileChangePath(path);
|
|
370
|
-
return normalized === STUDIO_MOTION_PATH || normalized.endsWith(`/${STUDIO_MOTION_PATH}`);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function selectionTarget(selection: DomEditSelection): StudioMotionTarget {
|
|
374
|
-
return {
|
|
375
|
-
sourceFile: selection.sourceFile || "index.html",
|
|
376
|
-
selector: selection.selector,
|
|
377
|
-
selectorIndex: selection.selectorIndex,
|
|
378
|
-
id: selection.id ?? undefined,
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function targetKey(target: StudioMotionTarget): string {
|
|
383
|
-
return [
|
|
384
|
-
target.sourceFile,
|
|
385
|
-
target.id ? `id:${target.id}` : "",
|
|
386
|
-
target.selector ? `selector:${target.selector}` : "",
|
|
387
|
-
target.selectorIndex != null ? `index:${target.selectorIndex}` : "",
|
|
388
|
-
].join("|");
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function sameSelectionTarget(motion: StudioGsapMotion, selection: DomEditSelection): boolean {
|
|
392
|
-
const target = selectionTarget(selection);
|
|
393
|
-
if (motion.target.sourceFile !== target.sourceFile) return false;
|
|
394
|
-
if (motion.target.id && target.id && motion.target.id === target.id) return true;
|
|
395
|
-
return targetKey(motion.target) === targetKey(target);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
export function upsertStudioGsapMotion(
|
|
399
|
-
manifest: StudioMotionManifest,
|
|
400
|
-
selection: DomEditSelection,
|
|
401
|
-
motion: Omit<StudioGsapMotion, "kind" | "target" | "updatedAt">,
|
|
402
|
-
): StudioMotionManifest {
|
|
403
|
-
const target = selectionTarget(selection);
|
|
404
|
-
const nextMotion: StudioGsapMotion = {
|
|
405
|
-
kind: "gsap-motion",
|
|
406
|
-
target,
|
|
407
|
-
...motion,
|
|
408
|
-
updatedAt: new Date().toISOString(),
|
|
409
|
-
};
|
|
410
|
-
return {
|
|
411
|
-
version: 1,
|
|
412
|
-
motions: [
|
|
413
|
-
...manifest.motions.filter((existing) => targetKey(existing.target) !== targetKey(target)),
|
|
414
|
-
nextMotion,
|
|
415
|
-
],
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
export function removeStudioMotionForSelection(
|
|
420
|
-
manifest: StudioMotionManifest,
|
|
421
|
-
selection: DomEditSelection,
|
|
422
|
-
): StudioMotionManifest {
|
|
423
|
-
return {
|
|
424
|
-
version: 1,
|
|
425
|
-
motions: manifest.motions.filter((motion) => !sameSelectionTarget(motion, selection)),
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
export function getStudioMotionForSelection(
|
|
430
|
-
manifest: StudioMotionManifest,
|
|
431
|
-
selection: DomEditSelection,
|
|
432
|
-
): StudioGsapMotion | null {
|
|
433
|
-
return manifest.motions.find((motion) => sameSelectionTarget(motion, selection)) ?? null;
|
|
434
|
-
}
|
|
1
|
+
// Public surface — re-exports types, constants, and ops; owns DOM application logic.
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
STUDIO_MOTION_PATH,
|
|
5
|
+
STUDIO_MOTION_TIMELINE_ID,
|
|
6
|
+
STUDIO_GSAP_EASE_OPTIONS,
|
|
7
|
+
type StudioMotionTarget,
|
|
8
|
+
type StudioGsapMotionValues,
|
|
9
|
+
type StudioGsapCustomEase,
|
|
10
|
+
type StudioCustomEaseControlPoints,
|
|
11
|
+
type StudioGsapMotion,
|
|
12
|
+
type StudioGsapMotionPreset,
|
|
13
|
+
type StudioGsapMotionDirection,
|
|
14
|
+
type StudioGsapPresetMotionOptions,
|
|
15
|
+
type StudioMotionManifest,
|
|
16
|
+
type StudioGsapTimeline,
|
|
17
|
+
type StudioMotionWindow,
|
|
18
|
+
} from "./studioMotionTypes";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
clampStudioCustomEasePoints,
|
|
22
|
+
parseStudioCustomEaseData,
|
|
23
|
+
serializeStudioCustomEaseData,
|
|
24
|
+
controlPointsForGsapEase,
|
|
25
|
+
buildStudioGsapPresetMotion,
|
|
26
|
+
emptyStudioMotionManifest,
|
|
27
|
+
parseStudioMotionManifest,
|
|
28
|
+
serializeStudioMotionManifest,
|
|
29
|
+
isStudioMotionManifestPath,
|
|
30
|
+
upsertStudioGsapMotion,
|
|
31
|
+
removeStudioMotionForSelection,
|
|
32
|
+
getStudioMotionForSelection,
|
|
33
|
+
} from "./studioMotionOps";
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
STUDIO_MOTION_ATTR,
|
|
37
|
+
STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR,
|
|
38
|
+
STUDIO_MOTION_ORIGINAL_OPACITY_ATTR,
|
|
39
|
+
STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR,
|
|
40
|
+
STUDIO_MOTION_TIMELINE_ID,
|
|
41
|
+
type StudioGsapMotion,
|
|
42
|
+
type StudioMotionManifest,
|
|
43
|
+
type StudioMotionTarget,
|
|
44
|
+
type StudioMotionWindow,
|
|
45
|
+
} from "./studioMotionTypes";
|
|
46
|
+
|
|
47
|
+
// ── DOM Helpers ──
|
|
435
48
|
|
|
436
49
|
function sourceFileForElement(element: HTMLElement, activeCompositionPath: string | null): string {
|
|
437
50
|
let current: HTMLElement | null = element;
|