@hyperframes/studio 0.6.107 → 0.6.108
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 → hyperframes-player-67pq7USK.js} +2 -2
- package/dist/assets/index-BVqybwMG.css +1 -0
- package/dist/assets/index-BgbVhDYd.js +296 -0
- package/dist/assets/{index-zIK7PWvk.js → index-Btg-jqxS.js} +1 -1
- package/dist/assets/{index-P52mbTRb.js → index-tn9M43-U.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +5 -5
- package/src/App.tsx +1 -0
- package/src/components/StudioRightPanel.tsx +161 -75
- package/src/components/editor/PropertyPanel.tsx +26 -1
- package/src/components/editor/domEditOverlayGeometry.ts +2 -11
- package/src/components/editor/domEditingDom.ts +21 -0
- package/src/components/editor/domEditingElement.ts +2 -11
- package/src/components/editor/manualEditingAvailability.test.ts +12 -0
- package/src/components/editor/manualEditingAvailability.ts +6 -0
- package/src/components/editor/propertyPanelColorGradingControls.tsx +475 -0
- package/src/components/editor/propertyPanelColorGradingSection.tsx +355 -0
- package/src/components/editor/propertyPanelHelpers.ts +2 -1
- package/src/components/editor/propertyPanelPrimitives.tsx +32 -37
- package/src/contexts/DomEditContext.tsx +4 -0
- package/src/contexts/PanelLayoutContext.tsx +6 -0
- package/src/hooks/useDomEditCommits.ts +7 -0
- package/src/hooks/useDomEditSession.ts +2 -2
- package/src/hooks/useDomEditTextCommits.ts +82 -26
- package/src/hooks/useDomEditWiring.ts +1 -0
- package/src/hooks/useDomSelection.ts +2 -10
- package/src/hooks/usePanelLayout.ts +26 -1
- package/src/hooks/useStudioContextValue.ts +8 -3
- package/src/icons/SystemIcons.tsx +4 -0
- package/src/player/lib/timelineDOM.test.ts +36 -0
- package/src/player/lib/timelineDOM.ts +2 -0
- package/src/player/lib/timelineElementHelpers.ts +18 -1
- package/src/styles/studio.css +81 -0
- package/src/utils/mediaTypes.ts +1 -0
- package/src/utils/studioHelpers.ts +6 -0
- package/dist/assets/index-BrhJl2JY.css +0 -1
- package/dist/assets/index-CVHV-S-B.js +0 -296
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type PointerEvent as ReactPointerEvent,
|
|
8
|
+
type RefObject,
|
|
9
|
+
} from "react";
|
|
10
|
+
import {
|
|
11
|
+
HF_COLOR_GRADING_ATTR,
|
|
12
|
+
HF_COLOR_GRADING_COLOR_SPACE,
|
|
13
|
+
isHfColorGradingActive,
|
|
14
|
+
normalizeHfColorGrading,
|
|
15
|
+
serializeHfColorGrading,
|
|
16
|
+
type HfColorGradingAdjustKey,
|
|
17
|
+
type HfColorGradingTarget,
|
|
18
|
+
type NormalizedHfColorGrading,
|
|
19
|
+
} from "@hyperframes/core/color-grading";
|
|
20
|
+
import { Compare, Palette, RotateCcw } from "../../icons/SystemIcons";
|
|
21
|
+
import type { DomEditSelection } from "./domEditing";
|
|
22
|
+
import { ColorGradingControls } from "./propertyPanelColorGradingControls";
|
|
23
|
+
import { Section } from "./propertyPanelPrimitives";
|
|
24
|
+
|
|
25
|
+
const DEFAULT_ADJUST: Record<HfColorGradingAdjustKey, number> = {
|
|
26
|
+
exposure: 0,
|
|
27
|
+
contrast: 0,
|
|
28
|
+
highlights: 0,
|
|
29
|
+
shadows: 0,
|
|
30
|
+
whites: 0,
|
|
31
|
+
blacks: 0,
|
|
32
|
+
temperature: 0,
|
|
33
|
+
tint: 0,
|
|
34
|
+
saturation: 0,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const DEFAULT_COLOR_GRADING: NormalizedHfColorGrading = {
|
|
38
|
+
enabled: true,
|
|
39
|
+
preset: "neutral",
|
|
40
|
+
intensity: 1,
|
|
41
|
+
adjust: DEFAULT_ADJUST,
|
|
42
|
+
lut: null,
|
|
43
|
+
colorSpace: HF_COLOR_GRADING_COLOR_SPACE,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const COLOR_GRADING_DATA_KEY = HF_COLOR_GRADING_ATTR.replace(/^data-/, "");
|
|
47
|
+
|
|
48
|
+
interface RuntimeColorGradingStatus {
|
|
49
|
+
state: "missing" | "inactive" | "pending" | "active" | "unavailable";
|
|
50
|
+
message: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isColorGradingCapableElement(element: DomEditSelection): boolean {
|
|
54
|
+
return element.tagName === "video" || element.tagName === "img";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readColorGradingFromElement(element: DomEditSelection): NormalizedHfColorGrading {
|
|
58
|
+
const grading =
|
|
59
|
+
normalizeHfColorGrading(element.dataAttributes[COLOR_GRADING_DATA_KEY]) ??
|
|
60
|
+
DEFAULT_COLOR_GRADING;
|
|
61
|
+
return { ...grading, intensity: 1 };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toBridgeColorGrading(grading: NormalizedHfColorGrading): unknown {
|
|
65
|
+
if (!isHfColorGradingActive(grading)) return null;
|
|
66
|
+
const { enabled: _enabled, ...bridgeGrading } = grading;
|
|
67
|
+
return bridgeGrading;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function readRuntimeColorGradingStatus(
|
|
71
|
+
iframe: HTMLIFrameElement | null | undefined,
|
|
72
|
+
target: HfColorGradingTarget,
|
|
73
|
+
): RuntimeColorGradingStatus {
|
|
74
|
+
try {
|
|
75
|
+
const win = iframe?.contentWindow as
|
|
76
|
+
| (Window & {
|
|
77
|
+
__hf?: {
|
|
78
|
+
colorGrading?: {
|
|
79
|
+
getStatus?: (
|
|
80
|
+
target: HfColorGradingTarget | string | null | undefined,
|
|
81
|
+
) => RuntimeColorGradingStatus;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
})
|
|
85
|
+
| null
|
|
86
|
+
| undefined;
|
|
87
|
+
const status = win?.__hf?.colorGrading?.getStatus?.(target);
|
|
88
|
+
return status ?? { state: "pending", message: "Waiting for runtime" };
|
|
89
|
+
} catch {
|
|
90
|
+
return { state: "unavailable", message: "Preview unavailable" };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function StatusPill({ status }: { status: RuntimeColorGradingStatus }) {
|
|
95
|
+
const dotClass =
|
|
96
|
+
status.state === "active"
|
|
97
|
+
? "bg-emerald-400"
|
|
98
|
+
: status.state === "pending"
|
|
99
|
+
? "bg-amber-300"
|
|
100
|
+
: status.state === "unavailable"
|
|
101
|
+
? "bg-red-400"
|
|
102
|
+
: "bg-panel-text-5";
|
|
103
|
+
return (
|
|
104
|
+
<div className="flex min-w-0 items-center gap-1.5 rounded bg-panel-input px-2 py-1 text-[10px] font-medium text-panel-text-3">
|
|
105
|
+
<span className={`h-1.5 w-1.5 flex-shrink-0 rounded-full ${dotClass}`} />
|
|
106
|
+
<span className="truncate">{status.message}</span>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function HoldBeforeButton({
|
|
112
|
+
active,
|
|
113
|
+
disabled,
|
|
114
|
+
onHoldChange,
|
|
115
|
+
}: {
|
|
116
|
+
active: boolean;
|
|
117
|
+
disabled: boolean;
|
|
118
|
+
onHoldChange: (holding: boolean) => void;
|
|
119
|
+
}) {
|
|
120
|
+
const startHold = (event: ReactPointerEvent<HTMLButtonElement>) => {
|
|
121
|
+
if (disabled) return;
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
event.stopPropagation();
|
|
124
|
+
onHoldChange(true);
|
|
125
|
+
const release = () => {
|
|
126
|
+
onHoldChange(false);
|
|
127
|
+
window.removeEventListener("pointerup", release);
|
|
128
|
+
window.removeEventListener("pointercancel", release);
|
|
129
|
+
window.removeEventListener("blur", release);
|
|
130
|
+
};
|
|
131
|
+
window.addEventListener("pointerup", release);
|
|
132
|
+
window.addEventListener("pointercancel", release);
|
|
133
|
+
window.addEventListener("blur", release);
|
|
134
|
+
};
|
|
135
|
+
const stopHold = (event: ReactPointerEvent<HTMLButtonElement>) => {
|
|
136
|
+
if (disabled) return;
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
event.stopPropagation();
|
|
139
|
+
onHoldChange(false);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
disabled={disabled}
|
|
146
|
+
aria-pressed={active}
|
|
147
|
+
aria-label="Hold to show original"
|
|
148
|
+
onPointerDown={startHold}
|
|
149
|
+
onPointerUp={stopHold}
|
|
150
|
+
onPointerCancel={stopHold}
|
|
151
|
+
onBlur={() => {
|
|
152
|
+
if (active) onHoldChange(false);
|
|
153
|
+
}}
|
|
154
|
+
onKeyDown={(event) => {
|
|
155
|
+
if (disabled || (event.key !== " " && event.key !== "Enter")) return;
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
if (!active) onHoldChange(true);
|
|
158
|
+
}}
|
|
159
|
+
onKeyUp={(event) => {
|
|
160
|
+
if (disabled || (event.key !== " " && event.key !== "Enter")) return;
|
|
161
|
+
event.preventDefault();
|
|
162
|
+
onHoldChange(false);
|
|
163
|
+
}}
|
|
164
|
+
className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded transition-colors ${
|
|
165
|
+
active
|
|
166
|
+
? "bg-studio-accent text-black"
|
|
167
|
+
: "text-panel-text-4 hover:bg-panel-hover hover:text-panel-text-1"
|
|
168
|
+
} disabled:cursor-not-allowed disabled:opacity-40`}
|
|
169
|
+
title="Hold to show original"
|
|
170
|
+
>
|
|
171
|
+
<Compare size={13} />
|
|
172
|
+
</button>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function ColorGradingSection({
|
|
177
|
+
element,
|
|
178
|
+
assets,
|
|
179
|
+
previewIframeRef,
|
|
180
|
+
onImportAssets,
|
|
181
|
+
onSetAttributeLive,
|
|
182
|
+
}: {
|
|
183
|
+
element: DomEditSelection;
|
|
184
|
+
assets: string[];
|
|
185
|
+
previewIframeRef?: RefObject<HTMLIFrameElement | null>;
|
|
186
|
+
onImportAssets?: (files: FileList, dir?: string) => Promise<string[]>;
|
|
187
|
+
onSetAttributeLive: (attr: string, value: string | null) => void | Promise<void>;
|
|
188
|
+
}) {
|
|
189
|
+
const [grading, setGrading] = useState(() => readColorGradingFromElement(element));
|
|
190
|
+
const [compareEnabled, setCompareEnabled] = useState(false);
|
|
191
|
+
const [runtimeStatus, setRuntimeStatus] = useState<RuntimeColorGradingStatus>(() => ({
|
|
192
|
+
state: "pending",
|
|
193
|
+
message: "Waiting for runtime",
|
|
194
|
+
}));
|
|
195
|
+
const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
196
|
+
const pendingPersistValueRef = useRef<string | null | undefined>(undefined);
|
|
197
|
+
const onSetAttributeLiveRef = useRef(onSetAttributeLive);
|
|
198
|
+
const compareEnabledRef = useRef(compareEnabled);
|
|
199
|
+
onSetAttributeLiveRef.current = onSetAttributeLive;
|
|
200
|
+
compareEnabledRef.current = compareEnabled;
|
|
201
|
+
const target = useMemo(
|
|
202
|
+
(): HfColorGradingTarget => ({
|
|
203
|
+
id: element.id ?? null,
|
|
204
|
+
hfId: element.hfId ?? null,
|
|
205
|
+
selector: element.selector ?? null,
|
|
206
|
+
selectorIndex: element.selectorIndex ?? null,
|
|
207
|
+
}),
|
|
208
|
+
[element.hfId, element.id, element.selector, element.selectorIndex],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const refreshRuntimeStatus = useCallback(() => {
|
|
212
|
+
setRuntimeStatus(readRuntimeColorGradingStatus(previewIframeRef?.current, target));
|
|
213
|
+
}, [previewIframeRef, target]);
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
refreshRuntimeStatus();
|
|
217
|
+
}, [refreshRuntimeStatus]);
|
|
218
|
+
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
const iframe = previewIframeRef?.current;
|
|
221
|
+
if (!iframe) return;
|
|
222
|
+
const refresh = () => {
|
|
223
|
+
window.setTimeout(refreshRuntimeStatus, 50);
|
|
224
|
+
};
|
|
225
|
+
iframe.addEventListener("load", refresh);
|
|
226
|
+
const timer = window.setTimeout(refreshRuntimeStatus, 80);
|
|
227
|
+
return () => {
|
|
228
|
+
iframe.removeEventListener("load", refresh);
|
|
229
|
+
window.clearTimeout(timer);
|
|
230
|
+
};
|
|
231
|
+
}, [previewIframeRef, refreshRuntimeStatus]);
|
|
232
|
+
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
return () => {
|
|
235
|
+
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
236
|
+
if (pendingPersistValueRef.current !== undefined) {
|
|
237
|
+
void onSetAttributeLiveRef.current(COLOR_GRADING_DATA_KEY, pendingPersistValueRef.current);
|
|
238
|
+
pendingPersistValueRef.current = undefined;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}, []);
|
|
242
|
+
|
|
243
|
+
const postColorGrading = useCallback(
|
|
244
|
+
(nextGrading: NormalizedHfColorGrading) => {
|
|
245
|
+
previewIframeRef?.current?.contentWindow?.postMessage(
|
|
246
|
+
{
|
|
247
|
+
source: "hf-parent",
|
|
248
|
+
type: "control",
|
|
249
|
+
action: "set-color-grading",
|
|
250
|
+
target,
|
|
251
|
+
grading: toBridgeColorGrading(nextGrading),
|
|
252
|
+
},
|
|
253
|
+
"*",
|
|
254
|
+
);
|
|
255
|
+
},
|
|
256
|
+
[previewIframeRef, target],
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const postCompare = useCallback(
|
|
260
|
+
(enabled: boolean) => {
|
|
261
|
+
previewIframeRef?.current?.contentWindow?.postMessage(
|
|
262
|
+
{
|
|
263
|
+
source: "hf-parent",
|
|
264
|
+
type: "control",
|
|
265
|
+
action: "set-color-grading-compare",
|
|
266
|
+
target,
|
|
267
|
+
compare: {
|
|
268
|
+
enabled,
|
|
269
|
+
position: 1,
|
|
270
|
+
lineWidth: 0,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
"*",
|
|
274
|
+
);
|
|
275
|
+
},
|
|
276
|
+
[previewIframeRef, target],
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
useEffect(
|
|
280
|
+
() => () => {
|
|
281
|
+
postCompare(false);
|
|
282
|
+
},
|
|
283
|
+
[postCompare],
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const commitColorGrading = useCallback(
|
|
287
|
+
(nextGrading: NormalizedHfColorGrading) => {
|
|
288
|
+
setGrading(nextGrading);
|
|
289
|
+
setRuntimeStatus({ state: "pending", message: "Updating shader" });
|
|
290
|
+
postColorGrading(nextGrading);
|
|
291
|
+
const active = isHfColorGradingActive(nextGrading);
|
|
292
|
+
if (compareEnabledRef.current) {
|
|
293
|
+
postCompare(active);
|
|
294
|
+
if (!active) setCompareEnabled(false);
|
|
295
|
+
}
|
|
296
|
+
window.setTimeout(refreshRuntimeStatus, 50);
|
|
297
|
+
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
298
|
+
pendingPersistValueRef.current = isHfColorGradingActive(nextGrading)
|
|
299
|
+
? serializeHfColorGrading(nextGrading)
|
|
300
|
+
: null;
|
|
301
|
+
persistTimerRef.current = setTimeout(() => {
|
|
302
|
+
const value = pendingPersistValueRef.current;
|
|
303
|
+
pendingPersistValueRef.current = undefined;
|
|
304
|
+
void onSetAttributeLive(COLOR_GRADING_DATA_KEY, value ?? null);
|
|
305
|
+
}, 350);
|
|
306
|
+
},
|
|
307
|
+
[onSetAttributeLive, postColorGrading, postCompare, refreshRuntimeStatus],
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
const commitCompare = useCallback(
|
|
311
|
+
(enabled: boolean) => {
|
|
312
|
+
const nextEnabled = enabled && isHfColorGradingActive(grading);
|
|
313
|
+
setCompareEnabled(nextEnabled);
|
|
314
|
+
if (nextEnabled) postColorGrading(grading);
|
|
315
|
+
postCompare(nextEnabled);
|
|
316
|
+
window.setTimeout(refreshRuntimeStatus, 50);
|
|
317
|
+
},
|
|
318
|
+
[grading, postColorGrading, postCompare, refreshRuntimeStatus],
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<Section
|
|
323
|
+
title="Color Grading"
|
|
324
|
+
icon={<Palette size={15} />}
|
|
325
|
+
accessory={
|
|
326
|
+
<div className="flex min-w-0 items-center gap-1.5">
|
|
327
|
+
<HoldBeforeButton
|
|
328
|
+
active={compareEnabled}
|
|
329
|
+
disabled={!isHfColorGradingActive(grading)}
|
|
330
|
+
onHoldChange={commitCompare}
|
|
331
|
+
/>
|
|
332
|
+
<StatusPill status={runtimeStatus} />
|
|
333
|
+
<button
|
|
334
|
+
type="button"
|
|
335
|
+
onClick={(event) => {
|
|
336
|
+
event.stopPropagation();
|
|
337
|
+
commitColorGrading(DEFAULT_COLOR_GRADING);
|
|
338
|
+
}}
|
|
339
|
+
className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
|
|
340
|
+
title="Reset grading"
|
|
341
|
+
>
|
|
342
|
+
<RotateCcw size={12} />
|
|
343
|
+
</button>
|
|
344
|
+
</div>
|
|
345
|
+
}
|
|
346
|
+
>
|
|
347
|
+
<ColorGradingControls
|
|
348
|
+
grading={grading}
|
|
349
|
+
assets={assets}
|
|
350
|
+
onImportAssets={onImportAssets}
|
|
351
|
+
onCommitColorGrading={commitColorGrading}
|
|
352
|
+
/>
|
|
353
|
+
</Section>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
@@ -15,6 +15,7 @@ export interface PropertyPanelProps {
|
|
|
15
15
|
onClearSelection: () => void;
|
|
16
16
|
onSetStyle: (prop: string, value: string) => void | Promise<void>;
|
|
17
17
|
onSetAttribute: (attr: string, value: string) => void | Promise<void>;
|
|
18
|
+
onSetAttributeLive: (attr: string, value: string | null) => void | Promise<void>;
|
|
18
19
|
onSetHtmlAttribute: (attr: string, value: string | null) => void | Promise<void>;
|
|
19
20
|
onSetManualOffset: (element: DomEditSelection, next: { x: number; y: number }) => void;
|
|
20
21
|
onSetManualSize: (element: DomEditSelection, next: { width: number; height: number }) => void;
|
|
@@ -24,7 +25,7 @@ export interface PropertyPanelProps {
|
|
|
24
25
|
onAddTextField: (afterFieldKey?: string) => string | Promise<string | null> | null;
|
|
25
26
|
onRemoveTextField: (fieldKey: string) => void;
|
|
26
27
|
onAskAgent: () => void;
|
|
27
|
-
onImportAssets?: (files: FileList) => Promise<string[]>;
|
|
28
|
+
onImportAssets?: (files: FileList, dir?: string) => Promise<string[]>;
|
|
28
29
|
fontAssets?: ImportedFontAsset[];
|
|
29
30
|
onImportFonts?: (files: FileList | File[]) => Promise<ImportedFontAsset[]>;
|
|
30
31
|
previewIframeRef?: React.RefObject<HTMLIFrameElement | null>;
|
|
@@ -348,46 +348,41 @@ export function Section({
|
|
|
348
348
|
defaultCollapsed?: boolean;
|
|
349
349
|
}) {
|
|
350
350
|
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
351
|
+
const collapseIcon = collapsed ? (
|
|
352
|
+
<svg
|
|
353
|
+
width="12"
|
|
354
|
+
height="12"
|
|
355
|
+
viewBox="0 0 12 12"
|
|
356
|
+
fill="none"
|
|
357
|
+
className="flex-shrink-0 text-panel-text-5"
|
|
358
|
+
>
|
|
359
|
+
<path d="M6 2.5v7M2.5 6h7" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
|
|
360
|
+
</svg>
|
|
361
|
+
) : (
|
|
362
|
+
<svg
|
|
363
|
+
width="10"
|
|
364
|
+
height="10"
|
|
365
|
+
viewBox="0 0 10 10"
|
|
366
|
+
fill="currentColor"
|
|
367
|
+
className="flex-shrink-0 text-panel-text-5"
|
|
368
|
+
>
|
|
369
|
+
<path d="M2 3l3 4 3-4z" />
|
|
370
|
+
</svg>
|
|
371
|
+
);
|
|
351
372
|
|
|
352
373
|
return (
|
|
353
374
|
<section className="min-w-0 border-t border-panel-border">
|
|
354
|
-
<
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
{
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
height="12"
|
|
366
|
-
viewBox="0 0 12 12"
|
|
367
|
-
fill="none"
|
|
368
|
-
className="flex-shrink-0 text-panel-text-5"
|
|
369
|
-
>
|
|
370
|
-
<path
|
|
371
|
-
d="M6 2.5v7M2.5 6h7"
|
|
372
|
-
stroke="currentColor"
|
|
373
|
-
strokeWidth="1.2"
|
|
374
|
-
strokeLinecap="round"
|
|
375
|
-
/>
|
|
376
|
-
</svg>
|
|
377
|
-
)}
|
|
378
|
-
{!collapsed && (
|
|
379
|
-
<svg
|
|
380
|
-
width="10"
|
|
381
|
-
height="10"
|
|
382
|
-
viewBox="0 0 10 10"
|
|
383
|
-
fill="currentColor"
|
|
384
|
-
className="flex-shrink-0 text-panel-text-5"
|
|
385
|
-
>
|
|
386
|
-
<path d="M2 3l3 4 3-4z" />
|
|
387
|
-
</svg>
|
|
388
|
-
)}
|
|
389
|
-
</div>
|
|
390
|
-
</button>
|
|
375
|
+
<div className="flex w-full items-center gap-2 px-4 py-2.5">
|
|
376
|
+
<button
|
|
377
|
+
type="button"
|
|
378
|
+
onClick={() => setCollapsed((v) => !v)}
|
|
379
|
+
className="flex min-w-0 flex-1 items-center justify-between gap-2 text-left"
|
|
380
|
+
>
|
|
381
|
+
<h3 className="text-[12px] font-semibold text-panel-text-1">{title}</h3>
|
|
382
|
+
{collapseIcon}
|
|
383
|
+
</button>
|
|
384
|
+
{accessory && <div className="flex flex-shrink-0 items-center">{accessory}</div>}
|
|
385
|
+
</div>
|
|
391
386
|
{!collapsed && <div className="px-4 pb-3">{children}</div>}
|
|
392
387
|
</section>
|
|
393
388
|
);
|
|
@@ -14,6 +14,7 @@ export interface DomEditActionsValue extends Pick<
|
|
|
14
14
|
| "clearDomSelection"
|
|
15
15
|
| "handleDomStyleCommit"
|
|
16
16
|
| "handleDomAttributeCommit"
|
|
17
|
+
| "handleDomAttributeLiveCommit"
|
|
17
18
|
| "handleDomHtmlAttributeCommit"
|
|
18
19
|
| "handleDomPathOffsetCommit"
|
|
19
20
|
| "handleDomGroupPathOffsetCommit"
|
|
@@ -115,6 +116,7 @@ export function DomEditProvider({
|
|
|
115
116
|
clearDomSelection,
|
|
116
117
|
handleDomStyleCommit,
|
|
117
118
|
handleDomAttributeCommit,
|
|
119
|
+
handleDomAttributeLiveCommit,
|
|
118
120
|
handleDomHtmlAttributeCommit,
|
|
119
121
|
handleDomPathOffsetCommit,
|
|
120
122
|
handleDomGroupPathOffsetCommit,
|
|
@@ -189,6 +191,7 @@ export function DomEditProvider({
|
|
|
189
191
|
clearDomSelection,
|
|
190
192
|
handleDomStyleCommit,
|
|
191
193
|
handleDomAttributeCommit,
|
|
194
|
+
handleDomAttributeLiveCommit,
|
|
192
195
|
handleDomHtmlAttributeCommit,
|
|
193
196
|
handleDomPathOffsetCommit,
|
|
194
197
|
handleDomGroupPathOffsetCommit,
|
|
@@ -245,6 +248,7 @@ export function DomEditProvider({
|
|
|
245
248
|
clearDomSelection,
|
|
246
249
|
handleDomStyleCommit,
|
|
247
250
|
handleDomAttributeCommit,
|
|
251
|
+
handleDomAttributeLiveCommit,
|
|
248
252
|
handleDomHtmlAttributeCommit,
|
|
249
253
|
handleDomPathOffsetCommit,
|
|
250
254
|
handleDomGroupPathOffsetCommit,
|
|
@@ -22,6 +22,8 @@ export function PanelLayoutProvider({
|
|
|
22
22
|
setRightCollapsed,
|
|
23
23
|
rightPanelTab,
|
|
24
24
|
setRightPanelTab,
|
|
25
|
+
rightInspectorPanes,
|
|
26
|
+
toggleRightInspectorPane,
|
|
25
27
|
toggleLeftSidebar,
|
|
26
28
|
handlePanelResizeStart,
|
|
27
29
|
handlePanelResizeMove,
|
|
@@ -43,6 +45,8 @@ export function PanelLayoutProvider({
|
|
|
43
45
|
setRightCollapsed,
|
|
44
46
|
rightPanelTab,
|
|
45
47
|
setRightPanelTab,
|
|
48
|
+
rightInspectorPanes,
|
|
49
|
+
toggleRightInspectorPane,
|
|
46
50
|
toggleLeftSidebar,
|
|
47
51
|
handlePanelResizeStart,
|
|
48
52
|
handlePanelResizeMove,
|
|
@@ -58,6 +62,8 @@ export function PanelLayoutProvider({
|
|
|
58
62
|
setRightCollapsed,
|
|
59
63
|
rightPanelTab,
|
|
60
64
|
setRightPanelTab,
|
|
65
|
+
rightInspectorPanes,
|
|
66
|
+
toggleRightInspectorPane,
|
|
61
67
|
toggleLeftSidebar,
|
|
62
68
|
handlePanelResizeStart,
|
|
63
69
|
handlePanelResizeMove,
|
|
@@ -16,6 +16,9 @@ import { useDomGeometryCommits } from "./useDomGeometryCommits";
|
|
|
16
16
|
import { useElementLifecycleOps } from "./useElementLifecycleOps";
|
|
17
17
|
import { formatFieldsSuffix } from "./gsapScriptCommitHelpers";
|
|
18
18
|
|
|
19
|
+
// Re-export so existing consumers keep their import path
|
|
20
|
+
export { GSAP_CSS_FALLBACK_BLOCKED_MESSAGE } from "./useDomGeometryCommits";
|
|
21
|
+
|
|
19
22
|
// ── Helpers ──
|
|
20
23
|
|
|
21
24
|
function formatUnsafeFieldList(fields: Array<{ path: string }>): string {
|
|
@@ -42,6 +45,8 @@ interface RecordEditInput {
|
|
|
42
45
|
files: Record<string, { before: string; after: string }>;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
export type { PersistDomEditOperations } from "./domEditCommitTypes";
|
|
49
|
+
|
|
45
50
|
export interface UseDomEditCommitsParams {
|
|
46
51
|
activeCompPath: string | null;
|
|
47
52
|
previewIframeRef: React.MutableRefObject<HTMLIFrameElement | null>;
|
|
@@ -238,6 +243,7 @@ export function useDomEditCommits({
|
|
|
238
243
|
const {
|
|
239
244
|
handleDomStyleCommit,
|
|
240
245
|
handleDomAttributeCommit,
|
|
246
|
+
handleDomAttributeLiveCommit,
|
|
241
247
|
handleDomHtmlAttributeCommit,
|
|
242
248
|
handleDomTextCommit,
|
|
243
249
|
commitDomTextFields,
|
|
@@ -297,6 +303,7 @@ export function useDomEditCommits({
|
|
|
297
303
|
resolveImportedFontAsset,
|
|
298
304
|
handleDomStyleCommit,
|
|
299
305
|
handleDomAttributeCommit,
|
|
306
|
+
handleDomAttributeLiveCommit,
|
|
300
307
|
handleDomHtmlAttributeCommit,
|
|
301
308
|
handleDomTextCommit,
|
|
302
309
|
commitDomTextFields,
|
|
@@ -203,6 +203,7 @@ export function useDomEditSession({
|
|
|
203
203
|
resolveImportedFontAsset,
|
|
204
204
|
handleDomStyleCommit,
|
|
205
205
|
handleDomAttributeCommit,
|
|
206
|
+
handleDomAttributeLiveCommit,
|
|
206
207
|
handleDomHtmlAttributeCommit,
|
|
207
208
|
handleDomTextCommit,
|
|
208
209
|
handleDomTextFieldStyleCommit,
|
|
@@ -265,8 +266,6 @@ export function useDomEditSession({
|
|
|
265
266
|
handleGsapRemoveAllKeyframes,
|
|
266
267
|
handleResetSelectedElementKeyframes,
|
|
267
268
|
} = useDomEditWiring({
|
|
268
|
-
// Pre-existing prop-drilling clone (same param set forwarded to
|
|
269
|
-
// useDomEditWiring); surfaced by this PR's adjacent edits, not introduced.
|
|
270
269
|
// fallow-ignore-next-line code-duplication
|
|
271
270
|
projectId,
|
|
272
271
|
activeCompPath,
|
|
@@ -374,6 +373,7 @@ export function useDomEditSession({
|
|
|
374
373
|
clearDomSelection,
|
|
375
374
|
handleDomStyleCommit,
|
|
376
375
|
handleDomAttributeCommit,
|
|
376
|
+
handleDomAttributeLiveCommit,
|
|
377
377
|
handleDomHtmlAttributeCommit,
|
|
378
378
|
handleDomPathOffsetCommit: handleGsapAwarePathOffsetCommit,
|
|
379
379
|
handleDomGroupPathOffsetCommit,
|