@hyperframes/studio 0.7.37 → 0.7.39
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-BBrKzTGd.js +459 -0
- package/dist/assets/index-0P10SwC_.css +1 -0
- package/dist/assets/{index-BXYJbIax.js → index-312a3Ceu.js} +1 -1
- package/dist/assets/index-B9YvRJz1.js +396 -0
- package/dist/assets/{index-Ct-X2wlf.js → index-CQHiecE7.js} +1 -1
- package/dist/{chunk-JND3XUJL.js → chunk-RCLGSZ7C.js} +1 -1
- package/dist/chunk-RCLGSZ7C.js.map +1 -0
- package/dist/{domEditingLayers-UIQZJCOA.js → domEditingLayers-S6YOLVRG.js} +2 -2
- package/dist/index.d.ts +48 -10
- package/dist/index.html +2 -2
- package/dist/index.js +6045 -3507
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +26 -24
- package/src/components/AskAgentModal.tsx +23 -6
- package/src/components/LintModal.tsx +40 -10
- package/src/components/MediaPreview.tsx +36 -1
- package/src/components/SaveQueuePausedBanner.tsx +7 -6
- package/src/components/StudioErrorBoundary.tsx +19 -31
- package/src/components/StudioFeedbackBar.tsx +11 -2
- package/src/components/StudioGlobalDragOverlay.tsx +1 -1
- package/src/components/StudioHeader.tsx +184 -106
- package/src/components/StudioLeftSidebar.tsx +24 -3
- package/src/components/StudioOverlays.tsx +34 -8
- package/src/components/StudioPreviewArea.tsx +8 -0
- package/src/components/StudioRightPanel.tsx +160 -9
- package/src/components/StudioSplash.tsx +6 -3
- package/src/components/StudioToast.tsx +12 -11
- package/src/components/TimelineToolbar.test.tsx +9 -3
- package/src/components/TimelineToolbar.tsx +63 -29
- package/src/components/editor/DomEditCropHandles.tsx +238 -0
- package/src/components/editor/DomEditOverlay.tsx +111 -108
- package/src/components/editor/DomEditRotateHandle.tsx +41 -0
- package/src/components/editor/PropertyPanel.test.ts +48 -0
- package/src/components/editor/PropertyPanel.tsx +27 -47
- package/src/components/editor/PropertyPanelEmptyState.tsx +33 -0
- package/src/components/editor/SnapToolbar.tsx +20 -1
- package/src/components/editor/clipPathHelpers.ts +113 -0
- package/src/components/editor/colorGradingScopePatch.test.ts +74 -0
- package/src/components/editor/colorGradingScopePatch.ts +57 -0
- package/src/components/editor/domEditOverlayCrop.test.ts +106 -0
- package/src/components/editor/domEditOverlayCrop.ts +115 -0
- package/src/components/editor/domEditOverlayGeometry.ts +14 -0
- package/src/components/editor/domEditOverlayShape.ts +39 -0
- package/src/components/editor/domEditing.test.ts +2 -0
- package/src/components/editor/domEditingTypes.ts +3 -0
- package/src/components/editor/manualEditingAvailability.test.ts +0 -12
- package/src/components/editor/manualEditingAvailability.ts +0 -6
- package/src/components/editor/marqueeCommit.ts +3 -2
- package/src/components/editor/propertyPanelColorGradingControls.tsx +371 -329
- package/src/components/editor/propertyPanelColorGradingSection.tsx +274 -55
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +275 -0
- package/src/components/editor/propertyPanelHelpers.ts +32 -125
- package/src/components/editor/propertyPanelMediaSection.tsx +253 -99
- package/src/components/editor/propertyPanelStyleSections.tsx +75 -1
- package/src/components/editor/propertyPanelTypes.ts +113 -0
- package/src/components/editor/snapTargetCollection.ts +2 -3
- package/src/components/editor/useDomEditCompositionRect.ts +68 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +13 -6
- package/src/components/editor/useDomEditOverlayRects.ts +5 -3
- package/src/components/nle/CompositionBreadcrumb.tsx +3 -2
- package/src/components/nle/NLELayout.tsx +54 -51
- package/src/components/nle/NLEPreview.tsx +41 -5
- package/src/components/nle/TimelineResizeDivider.tsx +97 -0
- package/src/components/nle/usePreviewBlockDrop.ts +31 -7
- package/src/components/renders/RenderQueue.tsx +125 -24
- package/src/components/renders/RenderQueueItem.tsx +132 -64
- package/src/components/renders/useRenderQueue.ts +136 -24
- package/src/components/sidebar/AssetsTab.test.ts +87 -0
- package/src/components/sidebar/AssetsTab.tsx +113 -15
- package/src/components/sidebar/BlocksTab.tsx +2 -1
- package/src/components/sidebar/GlobalAssetsView.tsx +91 -0
- package/src/components/storyboard/StoryboardFrameFocus.tsx +39 -16
- package/src/components/storyboard/StoryboardLoaded.tsx +19 -2
- package/src/components/storyboard/StoryboardView.tsx +6 -0
- package/src/components/studioColorGradingScope.ts +124 -0
- package/src/components/studioMediaJobs.ts +123 -0
- package/src/components/ui/Button.tsx +20 -11
- package/src/components/ui/HyperframesLoader.tsx +9 -2
- package/src/components/ui/SearchInput.tsx +53 -0
- package/src/components/ui/Tooltip.tsx +52 -6
- package/src/components/ui/VideoFrameThumbnail.tsx +26 -3
- package/src/components/ui/useDialogBehavior.ts +83 -0
- package/src/contexts/PanelLayoutContext.tsx +3 -0
- package/src/contexts/StudioContext.tsx +7 -0
- package/src/hooks/domSelectionTestHarness.ts +1 -0
- package/src/hooks/useCompositionContentLoader.ts +40 -0
- package/src/hooks/useCropMode.ts +91 -0
- package/src/hooks/useDomEditCommits.test.tsx +2 -0
- package/src/hooks/useEditorSave.ts +14 -0
- package/src/hooks/useFileManager.ts +39 -25
- package/src/hooks/useFrameCapture.ts +12 -1
- package/src/hooks/usePanelLayout.ts +1 -0
- package/src/hooks/usePreviewPersistence.ts +24 -15
- package/src/hooks/useStudioContextValue.ts +5 -0
- package/src/hooks/useToast.ts +66 -13
- package/src/player/components/ShortcutsPanel.tsx +9 -0
- package/src/player/store/playerStore.ts +14 -0
- package/src/styles/studio.css +61 -0
- package/src/utils/sdkResolverShadow.test.ts +53 -3
- package/src/utils/sdkResolverShadow.ts +41 -1
- package/src/utils/studioPendingEdits.test.ts +43 -0
- package/src/utils/studioPendingEdits.ts +45 -0
- package/src/utils/studioUiPreferences.ts +4 -0
- package/dist/assets/hyperframes-player-BGuumSiM.js +0 -459
- package/dist/assets/index-DmkOvZns.css +0 -1
- package/dist/assets/index-SYSbQyK4.js +0 -396
- package/dist/chunk-JND3XUJL.js.map +0 -1
- /package/dist/{domEditingLayers-UIQZJCOA.js.map → domEditingLayers-S6YOLVRG.js.map} +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Minus, Plus, RotateCcw, Settings } from "../../icons/SystemIcons";
|
|
3
|
+
import { LABEL } from "./propertyPanelHelpers";
|
|
4
|
+
|
|
5
|
+
const SLIDER_THUMB_SIZE = 10;
|
|
6
|
+
const SLIDER_THUMB_RADIUS = SLIDER_THUMB_SIZE / 2;
|
|
7
|
+
|
|
8
|
+
function clampNumber(value: number, min: number, max: number): number {
|
|
9
|
+
if (!Number.isFinite(value)) return min;
|
|
10
|
+
return Math.min(max, Math.max(min, value));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function formatNumericInput(value: number, scale: number): string {
|
|
14
|
+
const scaled = value / scale;
|
|
15
|
+
return scale === 100 ? scaled.toFixed(2) : String(Math.round(scaled));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseNumericInput(value: string, scale: number): number | null {
|
|
19
|
+
const parsed = Number(value);
|
|
20
|
+
if (!Number.isFinite(parsed)) return null;
|
|
21
|
+
return parsed * scale;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function tickPercent(value: number, min: number, max: number): number {
|
|
25
|
+
if (max <= min) return 0;
|
|
26
|
+
return ((value - min) / (max - min)) * 100;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function ColorGradingSliderControl({
|
|
30
|
+
label,
|
|
31
|
+
value,
|
|
32
|
+
min,
|
|
33
|
+
max,
|
|
34
|
+
step,
|
|
35
|
+
neutral = min,
|
|
36
|
+
scale = 1,
|
|
37
|
+
suffix = "",
|
|
38
|
+
displayValue,
|
|
39
|
+
disabled,
|
|
40
|
+
onCommit,
|
|
41
|
+
onReset,
|
|
42
|
+
settings,
|
|
43
|
+
}: {
|
|
44
|
+
label: string;
|
|
45
|
+
value: number;
|
|
46
|
+
min: number;
|
|
47
|
+
max: number;
|
|
48
|
+
step: number;
|
|
49
|
+
neutral?: number;
|
|
50
|
+
scale?: number;
|
|
51
|
+
suffix?: string;
|
|
52
|
+
displayValue: string;
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
onCommit: (nextValue: number) => void;
|
|
55
|
+
onReset?: () => void;
|
|
56
|
+
settings?: {
|
|
57
|
+
active?: boolean;
|
|
58
|
+
label: string;
|
|
59
|
+
onClick: () => void;
|
|
60
|
+
};
|
|
61
|
+
}) {
|
|
62
|
+
const [draftState, setDraftState] = useState<{ value: number; source: number } | null>(null);
|
|
63
|
+
const [inputDraft, setInputDraft] = useState<{ value: string; source: number } | null>(null);
|
|
64
|
+
const commitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
65
|
+
const valueRef = useRef(value);
|
|
66
|
+
valueRef.current = value;
|
|
67
|
+
|
|
68
|
+
useEffect(
|
|
69
|
+
() => () => {
|
|
70
|
+
if (commitTimerRef.current) clearTimeout(commitTimerRef.current);
|
|
71
|
+
},
|
|
72
|
+
[],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const clampDraft = useCallback(
|
|
76
|
+
(nextValue: number) => clampNumber(nextValue, min, max),
|
|
77
|
+
[max, min],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const setLocalDraft = useCallback(
|
|
81
|
+
(nextValue: number) => {
|
|
82
|
+
const clamped = clampDraft(nextValue);
|
|
83
|
+
const source = valueRef.current;
|
|
84
|
+
setDraftState({ value: clamped, source });
|
|
85
|
+
setInputDraft({ value: formatNumericInput(clamped, scale), source });
|
|
86
|
+
return clamped;
|
|
87
|
+
},
|
|
88
|
+
[clampDraft, scale],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const commitDraft = useCallback(
|
|
92
|
+
(nextValue: number) => {
|
|
93
|
+
const clamped = setLocalDraft(nextValue);
|
|
94
|
+
if (commitTimerRef.current) clearTimeout(commitTimerRef.current);
|
|
95
|
+
if (clamped !== valueRef.current) onCommit(clamped);
|
|
96
|
+
},
|
|
97
|
+
[onCommit, setLocalDraft],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const scheduleCommit = useCallback(
|
|
101
|
+
(nextValue: number) => {
|
|
102
|
+
const clamped = setLocalDraft(nextValue);
|
|
103
|
+
if (commitTimerRef.current) clearTimeout(commitTimerRef.current);
|
|
104
|
+
commitTimerRef.current = setTimeout(() => {
|
|
105
|
+
if (clamped !== valueRef.current) onCommit(clamped);
|
|
106
|
+
}, 40);
|
|
107
|
+
},
|
|
108
|
+
[onCommit, setLocalDraft],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const draft = draftState?.source === value ? draftState.value : value;
|
|
112
|
+
const inputValue =
|
|
113
|
+
inputDraft?.source === value ? inputDraft.value : formatNumericInput(draft, scale);
|
|
114
|
+
|
|
115
|
+
const commitInputDraft = useCallback(() => {
|
|
116
|
+
const parsed = parseNumericInput(inputValue, scale);
|
|
117
|
+
if (parsed === null) {
|
|
118
|
+
setInputDraft(null);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
commitDraft(parsed);
|
|
122
|
+
}, [commitDraft, inputValue, scale]);
|
|
123
|
+
|
|
124
|
+
const nudge = useCallback(
|
|
125
|
+
(direction: -1 | 1) => {
|
|
126
|
+
commitDraft(draft + step * direction);
|
|
127
|
+
},
|
|
128
|
+
[commitDraft, draft, step],
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const range = max - min;
|
|
132
|
+
const valuePercent = range === 0 ? 0 : ((draft - min) / range) * 100;
|
|
133
|
+
const neutralPercent = range === 0 ? 0 : ((neutral - min) / range) * 100;
|
|
134
|
+
const fillLeft = Math.min(valuePercent, neutralPercent);
|
|
135
|
+
const fillWidth = Math.abs(valuePercent - neutralPercent);
|
|
136
|
+
const ticks = Array.from(new Set([min, neutral, max])).sort((a, b) => a - b);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className="grid min-w-0 gap-0.5 rounded-md bg-panel-input/30 px-1.5 py-1">
|
|
140
|
+
<div className="flex min-w-0 items-center gap-1">
|
|
141
|
+
<span className={`${LABEL} min-w-0 flex-1 truncate`}>{label}</span>
|
|
142
|
+
{settings && (
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
disabled={disabled}
|
|
146
|
+
aria-label={settings.label}
|
|
147
|
+
onClick={(event) => {
|
|
148
|
+
event.stopPropagation();
|
|
149
|
+
settings.onClick();
|
|
150
|
+
}}
|
|
151
|
+
className={`relative flex h-5 w-5 flex-shrink-0 items-center justify-center rounded transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
152
|
+
settings.active ? "text-studio-accent" : "text-panel-text-5"
|
|
153
|
+
}`}
|
|
154
|
+
title={settings.label}
|
|
155
|
+
>
|
|
156
|
+
<Settings size={11} />
|
|
157
|
+
{settings.active && (
|
|
158
|
+
<span className="absolute right-0.5 top-0.5 h-1 w-1 rounded-full bg-studio-accent" />
|
|
159
|
+
)}
|
|
160
|
+
</button>
|
|
161
|
+
)}
|
|
162
|
+
{onReset && (
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
disabled={disabled}
|
|
166
|
+
aria-label={`Reset ${label}`}
|
|
167
|
+
onClick={(event) => {
|
|
168
|
+
event.stopPropagation();
|
|
169
|
+
onReset();
|
|
170
|
+
}}
|
|
171
|
+
className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
|
172
|
+
title={`Reset ${label}`}
|
|
173
|
+
>
|
|
174
|
+
<RotateCcw size={11} />
|
|
175
|
+
</button>
|
|
176
|
+
)}
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<div className="relative h-5 min-w-0">
|
|
180
|
+
<div
|
|
181
|
+
data-color-grading-slider-track="true"
|
|
182
|
+
className="pointer-events-none absolute inset-y-0 z-0"
|
|
183
|
+
style={{ left: SLIDER_THUMB_RADIUS, right: SLIDER_THUMB_RADIUS }}
|
|
184
|
+
>
|
|
185
|
+
{ticks.map((tick) => (
|
|
186
|
+
<div
|
|
187
|
+
key={tick}
|
|
188
|
+
data-color-grading-slider-tick="true"
|
|
189
|
+
className="absolute top-1/2 h-3 w-px -translate-y-1/2 bg-panel-text-3"
|
|
190
|
+
style={{ left: `${tickPercent(tick, min, max)}%` }}
|
|
191
|
+
title={String(tick / scale)}
|
|
192
|
+
/>
|
|
193
|
+
))}
|
|
194
|
+
<div className="absolute left-0 right-0 top-1/2 z-10 h-0.5 -translate-y-1/2 rounded-full bg-panel-border" />
|
|
195
|
+
<div
|
|
196
|
+
className="absolute top-1/2 z-20 h-0.5 -translate-y-1/2 rounded-full bg-studio-accent"
|
|
197
|
+
style={{ left: `${fillLeft}%`, width: `${fillWidth}%` }}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
<input
|
|
201
|
+
type="range"
|
|
202
|
+
min={min}
|
|
203
|
+
max={max}
|
|
204
|
+
step={step}
|
|
205
|
+
value={draft}
|
|
206
|
+
disabled={disabled}
|
|
207
|
+
aria-label={label}
|
|
208
|
+
onChange={(event) => scheduleCommit(Number(event.currentTarget.value))}
|
|
209
|
+
onMouseUp={() => commitDraft(draft)}
|
|
210
|
+
onTouchEnd={() => commitDraft(draft)}
|
|
211
|
+
onBlur={() => commitDraft(draft)}
|
|
212
|
+
className="hf-color-grading-range absolute left-0 right-0 top-1/2 z-30 min-w-0 w-full -translate-y-1/2"
|
|
213
|
+
title={displayValue}
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<div className="flex min-w-0 items-center justify-end gap-1">
|
|
218
|
+
<div className="flex flex-shrink-0 items-center rounded-md bg-panel-input px-1.5 py-px">
|
|
219
|
+
<input
|
|
220
|
+
type="number"
|
|
221
|
+
value={inputValue}
|
|
222
|
+
min={min / scale}
|
|
223
|
+
max={max / scale}
|
|
224
|
+
step={step / scale}
|
|
225
|
+
disabled={disabled}
|
|
226
|
+
onChange={(event) =>
|
|
227
|
+
setInputDraft({ value: event.currentTarget.value, source: valueRef.current })
|
|
228
|
+
}
|
|
229
|
+
onBlur={commitInputDraft}
|
|
230
|
+
onKeyDown={(event) => {
|
|
231
|
+
if (event.key === "Enter") {
|
|
232
|
+
event.currentTarget.blur();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (event.key === "ArrowUp") {
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
nudge(1);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (event.key === "ArrowDown") {
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
nudge(-1);
|
|
243
|
+
}
|
|
244
|
+
}}
|
|
245
|
+
className="hf-color-grading-number h-4 w-[36px] bg-transparent text-right text-[10px] font-medium tabular-nums text-panel-text-1 outline-none disabled:cursor-not-allowed"
|
|
246
|
+
title={displayValue}
|
|
247
|
+
/>
|
|
248
|
+
{suffix && <span className="ml-0.5 text-[10px] text-panel-text-5">{suffix}</span>}
|
|
249
|
+
</div>
|
|
250
|
+
<div className="flex flex-shrink-0 overflow-hidden rounded-md bg-panel-input">
|
|
251
|
+
<button
|
|
252
|
+
type="button"
|
|
253
|
+
disabled={disabled}
|
|
254
|
+
aria-label={`Decrease ${label}`}
|
|
255
|
+
onClick={() => nudge(-1)}
|
|
256
|
+
className="flex h-5 w-5 items-center justify-center text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
|
257
|
+
title={`Decrease ${label}`}
|
|
258
|
+
>
|
|
259
|
+
<Minus size={11} />
|
|
260
|
+
</button>
|
|
261
|
+
<button
|
|
262
|
+
type="button"
|
|
263
|
+
disabled={disabled}
|
|
264
|
+
aria-label={`Increase ${label}`}
|
|
265
|
+
onClick={() => nudge(1)}
|
|
266
|
+
className="flex h-5 w-5 items-center justify-center border-l border-panel-border text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
|
267
|
+
title={`Increase ${label}`}
|
|
268
|
+
>
|
|
269
|
+
<Plus size={11} />
|
|
270
|
+
</button>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
@@ -1,91 +1,21 @@
|
|
|
1
1
|
import { parseCssColor, type ParsedColor } from "./colorValue";
|
|
2
2
|
import { COMMON_LOCAL_FONT_FAMILIES } from "./fontCatalog";
|
|
3
3
|
import type { DomEditSelection } from "./domEditing";
|
|
4
|
-
import type { ImportedFontAsset } from "./fontAssets";
|
|
5
4
|
import type { GsapAnimation } from "@hyperframes/parsers/gsap-parser";
|
|
6
5
|
import { roundToCenti } from "../../utils/rounding";
|
|
7
6
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
onSetAttributeLive: (attr: string, value: string | null) => void | Promise<void>;
|
|
21
|
-
onSetHtmlAttribute: (attr: string, value: string | null) => void | Promise<void>;
|
|
22
|
-
onSetManualOffset: (element: DomEditSelection, next: { x: number; y: number }) => void;
|
|
23
|
-
onSetManualSize: (element: DomEditSelection, next: { width: number; height: number }) => void;
|
|
24
|
-
onSetManualRotation: (element: DomEditSelection, next: { angle: number }) => void;
|
|
25
|
-
onSetText: (value: string, fieldKey?: string) => void;
|
|
26
|
-
onSetTextFieldStyle: (fieldKey: string, property: string, value: string) => void;
|
|
27
|
-
onAddTextField: (afterFieldKey?: string) => string | Promise<string | null> | null;
|
|
28
|
-
onRemoveTextField: (fieldKey: string) => void;
|
|
29
|
-
onAskAgent: () => void;
|
|
30
|
-
onImportAssets?: (files: FileList, dir?: string) => Promise<string[]>;
|
|
31
|
-
fontAssets?: ImportedFontAsset[];
|
|
32
|
-
onImportFonts?: (files: FileList | File[]) => Promise<ImportedFontAsset[]>;
|
|
33
|
-
previewIframeRef?: React.RefObject<HTMLIFrameElement | null>;
|
|
34
|
-
gsapAnimations?: import("@hyperframes/parsers/gsap-parser").GsapAnimation[];
|
|
35
|
-
gsapMultipleTimelines?: boolean;
|
|
36
|
-
gsapUnsupportedTimelinePattern?: boolean;
|
|
37
|
-
onUpdateGsapProperty?: (animId: string, prop: string, value: number | string) => void;
|
|
38
|
-
onUpdateGsapMeta?: (
|
|
39
|
-
animId: string,
|
|
40
|
-
updates: { duration?: number; ease?: string; position?: number },
|
|
41
|
-
) => void;
|
|
42
|
-
onDeleteGsapAnimation?: (animId: string) => void;
|
|
43
|
-
onAddGsapProperty?: (animId: string, prop: string) => void;
|
|
44
|
-
onRemoveGsapProperty?: (animId: string, prop: string) => void;
|
|
45
|
-
onUpdateGsapFromProperty?: (animId: string, prop: string, value: number | string) => void;
|
|
46
|
-
onAddGsapFromProperty?: (animId: string, prop: string) => void;
|
|
47
|
-
onRemoveGsapFromProperty?: (animId: string, prop: string) => void;
|
|
48
|
-
onAddGsapAnimation?: (method: "to" | "from" | "set" | "fromTo") => void;
|
|
49
|
-
onSetArcPath?: (
|
|
50
|
-
animId: string,
|
|
51
|
-
config: {
|
|
52
|
-
enabled: boolean;
|
|
53
|
-
autoRotate?: boolean | number;
|
|
54
|
-
segments?: import("@hyperframes/parsers/gsap-parser").ArcPathSegment[];
|
|
55
|
-
},
|
|
56
|
-
) => void;
|
|
57
|
-
onUpdateArcSegment?: (
|
|
58
|
-
animId: string,
|
|
59
|
-
segmentIndex: number,
|
|
60
|
-
update: Partial<import("@hyperframes/parsers/gsap-parser").ArcPathSegment>,
|
|
61
|
-
) => void;
|
|
62
|
-
/** Unroll computed (helper/loop) tweens into literal tweens for direct editing. */
|
|
63
|
-
onUnroll?: (animationId: string) => void;
|
|
64
|
-
onAddKeyframe?: (
|
|
65
|
-
animationId: string,
|
|
66
|
-
percentage: number,
|
|
67
|
-
property: string,
|
|
68
|
-
value: number | string,
|
|
69
|
-
) => void;
|
|
70
|
-
onRemoveKeyframe?: (animationId: string, percentage: number) => void;
|
|
71
|
-
onUpdateKeyframeEase?: (animationId: string, percentage: number, ease: string) => void;
|
|
72
|
-
onSetAllKeyframeEases?: (animationId: string, ease: string) => void;
|
|
73
|
-
onConvertToKeyframes?: (animationId: string, duration?: number) => void;
|
|
74
|
-
onCommitAnimatedProperty?: (
|
|
75
|
-
selection: DomEditSelection,
|
|
76
|
-
property: string,
|
|
77
|
-
value: number | string,
|
|
78
|
-
) => Promise<void>;
|
|
79
|
-
/** Batched variant: commit several props into ONE keyframe (e.g. the 3D cube's
|
|
80
|
-
* rotationX/Y/Z) so multi-axis edits don't race into adjacent duplicates. */
|
|
81
|
-
onCommitAnimatedProperties?: (
|
|
82
|
-
selection: DomEditSelection,
|
|
83
|
-
props: Record<string, number | string>,
|
|
84
|
-
) => Promise<void>;
|
|
85
|
-
onSeekToTime?: (time: number) => void;
|
|
86
|
-
recordingState?: "idle" | "recording" | "preview";
|
|
87
|
-
recordingDuration?: number;
|
|
88
|
-
onToggleRecording?: () => void;
|
|
7
|
+
export type {
|
|
8
|
+
BackgroundRemovalProgress,
|
|
9
|
+
BackgroundRemovalResult,
|
|
10
|
+
PropertyPanelProps,
|
|
11
|
+
} from "./propertyPanelTypes";
|
|
12
|
+
|
|
13
|
+
export function stripQueryAndHash(value: string): string {
|
|
14
|
+
const queryIndex = value.indexOf("?");
|
|
15
|
+
const hashIndex = value.indexOf("#");
|
|
16
|
+
if (queryIndex < 0) return hashIndex < 0 ? value : value.slice(0, hashIndex);
|
|
17
|
+
if (hashIndex < 0) return value.slice(0, queryIndex);
|
|
18
|
+
return value.slice(0, Math.min(queryIndex, hashIndex));
|
|
89
19
|
}
|
|
90
20
|
|
|
91
21
|
/* ------------------------------------------------------------------ */
|
|
@@ -230,6 +160,16 @@ const BOX_SHADOW_PRESETS = {
|
|
|
230
160
|
|
|
231
161
|
export type BoxShadowPreset = keyof typeof BOX_SHADOW_PRESETS | "custom";
|
|
232
162
|
|
|
163
|
+
export {
|
|
164
|
+
buildClipPathValue,
|
|
165
|
+
buildInsetClipPathSides,
|
|
166
|
+
buildInsetClipPathValue,
|
|
167
|
+
getClipPathInsetPx,
|
|
168
|
+
inferClipPathPreset,
|
|
169
|
+
parseInsetClipPathSides,
|
|
170
|
+
type ClipPathInsetSides,
|
|
171
|
+
} from "./clipPathHelpers";
|
|
172
|
+
|
|
233
173
|
/* ------------------------------------------------------------------ */
|
|
234
174
|
/* Shared types */
|
|
235
175
|
/* ------------------------------------------------------------------ */
|
|
@@ -322,23 +262,24 @@ export function normalizeTextMetricValue(
|
|
|
322
262
|
}
|
|
323
263
|
|
|
324
264
|
function splitCssFunctions(value: string): string[] {
|
|
265
|
+
const source = value.trim();
|
|
325
266
|
const functions: string[] = [];
|
|
326
|
-
let current = "";
|
|
327
|
-
// fallow-ignore-next-line code-duplication -- pre-existing; surfaced in this file's diff by an unrelated line shift
|
|
328
267
|
let depth = 0;
|
|
268
|
+
let start = 0;
|
|
329
269
|
|
|
330
|
-
for (
|
|
270
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
271
|
+
const char = source[index];
|
|
331
272
|
if (char === "(") depth += 1;
|
|
332
273
|
if (char === ")") depth = Math.max(0, depth - 1);
|
|
333
274
|
if (/\s/.test(char) && depth === 0) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
275
|
+
const part = source.slice(start, index).trim();
|
|
276
|
+
if (part) functions.push(part);
|
|
277
|
+
start = index + 1;
|
|
337
278
|
}
|
|
338
|
-
current += char;
|
|
339
279
|
}
|
|
340
280
|
|
|
341
|
-
|
|
281
|
+
const lastPart = source.slice(start).trim();
|
|
282
|
+
if (lastPart) functions.push(lastPart);
|
|
342
283
|
return functions;
|
|
343
284
|
}
|
|
344
285
|
|
|
@@ -383,23 +324,6 @@ export function buildBoxShadowPresetValue(
|
|
|
383
324
|
return BOX_SHADOW_PRESETS[preset];
|
|
384
325
|
}
|
|
385
326
|
|
|
386
|
-
export function inferClipPathPreset(
|
|
387
|
-
value: string | undefined,
|
|
388
|
-
): "none" | "inset" | "circle" | "custom" {
|
|
389
|
-
const normalized = value?.trim();
|
|
390
|
-
if (!normalized || normalized === "none") return "none";
|
|
391
|
-
if (/^inset\(/i.test(normalized)) return "inset";
|
|
392
|
-
if (/^circle\(/i.test(normalized)) return "circle";
|
|
393
|
-
return "custom";
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
export function getClipPathInsetPx(value: string | undefined): number {
|
|
397
|
-
const match = /^inset\(\s*(-?\d+(?:\.\d+)?)px\b/i.exec(value?.trim() ?? "");
|
|
398
|
-
if (!match) return 0;
|
|
399
|
-
const parsed = Number.parseFloat(match[1]);
|
|
400
|
-
return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
327
|
export function buildStrokeWidthStyleUpdates(
|
|
404
328
|
nextWidth: string,
|
|
405
329
|
currentBorderStyle: string | undefined,
|
|
@@ -428,23 +352,6 @@ export function buildStrokeStyleUpdates(
|
|
|
428
352
|
return updates;
|
|
429
353
|
}
|
|
430
354
|
|
|
431
|
-
export function buildClipPathValue(
|
|
432
|
-
preset: "none" | "inset" | "circle" | "custom",
|
|
433
|
-
radiusValue: number,
|
|
434
|
-
fallback: string | undefined,
|
|
435
|
-
) {
|
|
436
|
-
if (preset === "custom") return fallback?.trim() || "none";
|
|
437
|
-
if (preset === "circle") return "circle(50% at 50% 50%)";
|
|
438
|
-
if (preset === "inset") {
|
|
439
|
-
return `inset(0 round ${formatNumericValue(Math.max(0, radiusValue))}px)`;
|
|
440
|
-
}
|
|
441
|
-
return "none";
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
export function buildInsetClipPathValue(insetPx: number, radiusValue: number): string {
|
|
445
|
-
return `inset(${formatNumericValue(Math.max(0, insetPx))}px round ${formatNumericValue(Math.max(0, radiusValue))}px)`;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
355
|
export function adjustNumericToken(
|
|
449
356
|
value: string,
|
|
450
357
|
direction: 1 | -1,
|
|
@@ -490,11 +397,11 @@ export function extractBackgroundImageUrl(value: string | undefined): string {
|
|
|
490
397
|
|
|
491
398
|
// ── GSAP runtime value readers (used by PropertyPanel) ────────────────────
|
|
492
399
|
|
|
493
|
-
// fallow-ignore-next-line complexity -- pre-existing; surfaced in this file's diff by an unrelated line shift
|
|
494
400
|
// Core transform channels the panel ALWAYS reads live — even before a just-set
|
|
495
401
|
// value (e.g. rotationX) has re-parsed into `gsapAnimations`. Without this the
|
|
496
402
|
// cube + fields drop the prop and flicker to 0 on every commit; gsap.getProperty
|
|
497
403
|
// reflects the in-place instant patch, so it's the true current value.
|
|
404
|
+
// fallow-ignore-next-line complexity
|
|
498
405
|
const ALWAYS_READ_CHANNELS = [
|
|
499
406
|
"x",
|
|
500
407
|
"y",
|