@hyperframes/studio 0.6.0-alpha.1 → 0.6.0-alpha.11
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-DjsVzYFP.js +418 -0
- package/dist/assets/index-FWg79aJz.css +1 -0
- package/dist/assets/index-xyVaWqe2.js +108 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +422 -71
- package/src/components/editor/PropertyPanel.test.ts +49 -0
- package/src/components/editor/PropertyPanel.tsx +277 -337
- package/src/components/editor/domEditing.test.ts +248 -0
- package/src/components/editor/domEditing.ts +126 -2
- package/src/components/editor/manualEditingAvailability.test.ts +15 -4
- package/src/components/editor/manualEditingAvailability.ts +4 -2
- package/src/components/editor/manualEdits.ts +15 -3
- package/src/components/nle/NLELayout.test.ts +12 -0
- package/src/components/nle/NLELayout.tsx +63 -24
- package/src/components/nle/NLEPreview.tsx +6 -0
- package/src/components/renders/RenderQueue.tsx +56 -4
- package/src/components/renders/useRenderQueue.ts +30 -6
- package/src/components/sidebar/LeftSidebar.tsx +186 -186
- package/src/player/components/Player.test.ts +58 -0
- package/src/player/components/Player.tsx +71 -4
- package/src/player/components/PlayerControls.tsx +20 -7
- package/src/player/components/Timeline.tsx +45 -20
- package/src/utils/timelineDiscovery.ts +1 -1
- package/dist/assets/hyperframes-player-Cd8vYWxP.js +0 -198
- package/dist/assets/index-D04_ZoMm.js +0 -107
- package/dist/assets/index-UWFaHilT.css +0 -1
|
@@ -26,11 +26,13 @@ export function resolveSeekPercent(clientX: number, rectLeft: number, rectWidth:
|
|
|
26
26
|
interface PlayerControlsProps {
|
|
27
27
|
onTogglePlay: () => void;
|
|
28
28
|
onSeek: (time: number) => void;
|
|
29
|
+
disabled?: boolean;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export const PlayerControls = memo(function PlayerControls({
|
|
32
33
|
onTogglePlay,
|
|
33
34
|
onSeek,
|
|
35
|
+
disabled = false,
|
|
34
36
|
}: PlayerControlsProps) {
|
|
35
37
|
// Subscribe to only the fields we render — each selector prevents cascading re-renders
|
|
36
38
|
const isPlaying = usePlayerStore((s) => s.isPlaying);
|
|
@@ -57,6 +59,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
57
59
|
|
|
58
60
|
const durationRef = useRef(duration);
|
|
59
61
|
durationRef.current = duration;
|
|
62
|
+
const controlsDisabled = disabled || !timelineReady;
|
|
60
63
|
useMountEffect(() => {
|
|
61
64
|
const updateProgress = (t: number) => {
|
|
62
65
|
currentTimeRef.current = t;
|
|
@@ -115,6 +118,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
115
118
|
|
|
116
119
|
const seekFromClientX = useCallback(
|
|
117
120
|
(clientX: number) => {
|
|
121
|
+
if (disabled) return;
|
|
118
122
|
const bar = seekBarRef.current;
|
|
119
123
|
if (!bar || duration <= 0) return;
|
|
120
124
|
const rect = bar.getBoundingClientRect();
|
|
@@ -125,7 +129,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
125
129
|
if (progressThumbRef.current) progressThumbRef.current.style.left = `${pct}%`;
|
|
126
130
|
onSeek(percent * duration);
|
|
127
131
|
},
|
|
128
|
-
[duration, onSeek],
|
|
132
|
+
[disabled, duration, onSeek],
|
|
129
133
|
);
|
|
130
134
|
|
|
131
135
|
const handlePointerDown = useCallback(
|
|
@@ -204,7 +208,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
204
208
|
|
|
205
209
|
const handleKeyDown = useCallback(
|
|
206
210
|
(e: React.KeyboardEvent) => {
|
|
207
|
-
if (!timelineReady || duration <= 0) return;
|
|
211
|
+
if (disabled || !timelineReady || duration <= 0) return;
|
|
208
212
|
const step = e.shiftKey ? 10 : 1;
|
|
209
213
|
if (e.key === "ArrowLeft") {
|
|
210
214
|
e.preventDefault();
|
|
@@ -214,14 +218,15 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
214
218
|
onSeek(Math.min(duration, stepFrameTime(currentTimeRef.current, step)));
|
|
215
219
|
}
|
|
216
220
|
},
|
|
217
|
-
[timelineReady, duration, onSeek],
|
|
221
|
+
[disabled, timelineReady, duration, onSeek],
|
|
218
222
|
);
|
|
219
223
|
|
|
220
224
|
const commitJumpFrame = useCallback(() => {
|
|
225
|
+
if (disabled) return;
|
|
221
226
|
const frame = Number.parseInt(jumpFrame, 10);
|
|
222
227
|
if (!Number.isFinite(frame) || duration <= 0) return;
|
|
223
228
|
onSeek(Math.min(duration, frameToSeconds(Math.max(0, frame))));
|
|
224
|
-
}, [duration, jumpFrame, onSeek]);
|
|
229
|
+
}, [disabled, duration, jumpFrame, onSeek]);
|
|
225
230
|
|
|
226
231
|
const handleJumpSubmit = useCallback(
|
|
227
232
|
(e: React.FormEvent) => {
|
|
@@ -243,6 +248,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
243
248
|
return (
|
|
244
249
|
<div
|
|
245
250
|
className="px-4 py-2 flex flex-wrap items-center gap-x-2 gap-y-1"
|
|
251
|
+
aria-disabled={disabled || undefined}
|
|
246
252
|
style={{
|
|
247
253
|
borderTop: "1px solid rgba(255,255,255,0.04)",
|
|
248
254
|
// Add iOS safe-area inset so Safari's bottom URL bar doesn't occlude
|
|
@@ -256,7 +262,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
256
262
|
type="button"
|
|
257
263
|
aria-label={isPlaying ? "Pause" : "Play"}
|
|
258
264
|
onClick={onTogglePlay}
|
|
259
|
-
disabled={
|
|
265
|
+
disabled={controlsDisabled}
|
|
260
266
|
className="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-lg disabled:opacity-30 disabled:pointer-events-none transition-colors"
|
|
261
267
|
style={{ background: "rgba(255,255,255,0.06)" }}
|
|
262
268
|
>
|
|
@@ -293,12 +299,15 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
293
299
|
(sliderRef as React.MutableRefObject<HTMLDivElement | null>).current = el;
|
|
294
300
|
}}
|
|
295
301
|
role="slider"
|
|
296
|
-
tabIndex={0}
|
|
302
|
+
tabIndex={disabled ? -1 : 0}
|
|
297
303
|
aria-label="Seek"
|
|
304
|
+
aria-disabled={disabled || undefined}
|
|
298
305
|
aria-valuemin={0}
|
|
299
306
|
aria-valuemax={Math.round(duration)}
|
|
300
307
|
aria-valuenow={0}
|
|
301
|
-
className=
|
|
308
|
+
className={`min-w-[96px] flex-1 h-6 flex items-center group ${
|
|
309
|
+
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
|
|
310
|
+
}`}
|
|
302
311
|
// `touch-action: none` tells the browser we're handling every
|
|
303
312
|
// pointer gesture on this element ourselves. Without it, iOS
|
|
304
313
|
// Safari consumes horizontal swipes for its own swipe-back-to-
|
|
@@ -334,6 +343,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
334
343
|
<button
|
|
335
344
|
type="button"
|
|
336
345
|
onClick={() => setShowSpeedMenu((v) => !v)}
|
|
346
|
+
disabled={disabled}
|
|
337
347
|
className="w-10 px-2 py-1 rounded-md text-[10px] font-mono tabular-nums transition-colors"
|
|
338
348
|
style={{ color: "#71717A", background: "rgba(255,255,255,0.04)" }}
|
|
339
349
|
>
|
|
@@ -374,6 +384,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
374
384
|
<button
|
|
375
385
|
type="button"
|
|
376
386
|
onClick={() => setLoopEnabled(!loopEnabled)}
|
|
387
|
+
disabled={disabled}
|
|
377
388
|
className={`h-7 w-14 rounded-md border px-2 text-[10px] font-medium transition-colors ${
|
|
378
389
|
loopEnabled
|
|
379
390
|
? "text-studio-accent bg-studio-accent/10 border-studio-accent/30"
|
|
@@ -389,6 +400,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
389
400
|
<button
|
|
390
401
|
type="button"
|
|
391
402
|
onClick={() => setTimeDisplayMode((mode) => (mode === "time" ? "frame" : "time"))}
|
|
403
|
+
disabled={disabled}
|
|
392
404
|
className="h-7 w-14 rounded-md border border-neutral-700 px-2 text-[10px] font-mono text-neutral-300 transition-colors hover:border-neutral-500 hover:bg-neutral-800"
|
|
393
405
|
title="Toggle time/frame display"
|
|
394
406
|
aria-label="Toggle time and frame display"
|
|
@@ -403,6 +415,7 @@ export const PlayerControls = memo(function PlayerControls({
|
|
|
403
415
|
<input
|
|
404
416
|
value={jumpFrame}
|
|
405
417
|
onChange={(e) => setJumpFrame(e.target.value)}
|
|
418
|
+
disabled={disabled}
|
|
406
419
|
inputMode="numeric"
|
|
407
420
|
pattern="[0-9]*"
|
|
408
421
|
aria-label="Jump to frame"
|
|
@@ -340,6 +340,7 @@ interface TimelineProps {
|
|
|
340
340
|
layerChildCounts?: ReadonlyMap<string, number>;
|
|
341
341
|
thumbnailedElementIds?: ReadonlySet<string>;
|
|
342
342
|
onToggleElementThumbnail?: (element: import("../store/playerStore").TimelineElement) => void;
|
|
343
|
+
disabled?: boolean;
|
|
343
344
|
theme?: Partial<TimelineTheme>;
|
|
344
345
|
}
|
|
345
346
|
|
|
@@ -393,6 +394,7 @@ export const Timeline = memo(function Timeline({
|
|
|
393
394
|
layerChildCounts,
|
|
394
395
|
thumbnailedElementIds,
|
|
395
396
|
onToggleElementThumbnail,
|
|
397
|
+
disabled = false,
|
|
396
398
|
theme: themeOverrides,
|
|
397
399
|
}: TimelineProps = {}) {
|
|
398
400
|
const theme = useMemo(() => ({ ...defaultTimelineTheme, ...themeOverrides }), [themeOverrides]);
|
|
@@ -412,6 +414,8 @@ export const Timeline = memo(function Timeline({
|
|
|
412
414
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
413
415
|
const [hoveredClip, setHoveredClip] = useState<string | null>(null);
|
|
414
416
|
const isDragging = useRef(false);
|
|
417
|
+
const disabledRef = useRef(disabled);
|
|
418
|
+
disabledRef.current = disabled;
|
|
415
419
|
const shiftClickClipRef = useRef<{
|
|
416
420
|
element: TimelineElement;
|
|
417
421
|
anchorX: number;
|
|
@@ -442,7 +446,6 @@ export const Timeline = memo(function Timeline({
|
|
|
442
446
|
const resizingClipRef = useRef<ResizingClipState | null>(null);
|
|
443
447
|
resizingClipRef.current = resizingClip;
|
|
444
448
|
const blockedClipRef = useRef<BlockedClipState | null>(null);
|
|
445
|
-
const deleteInFlightRef = useRef(false);
|
|
446
449
|
const onMoveElementRef = useRef(onMoveElement);
|
|
447
450
|
onMoveElementRef.current = onMoveElement;
|
|
448
451
|
const onResizeElementRef = useRef(onResizeElement);
|
|
@@ -498,6 +501,19 @@ export const Timeline = memo(function Timeline({
|
|
|
498
501
|
if (shortcutHintRafRef.current) cancelAnimationFrame(shortcutHintRafRef.current);
|
|
499
502
|
});
|
|
500
503
|
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
if (!disabled) return;
|
|
506
|
+
stopClipDragAutoScrollRef.current();
|
|
507
|
+
isDragging.current = false;
|
|
508
|
+
isRangeSelecting.current = false;
|
|
509
|
+
blockedClipRef.current = null;
|
|
510
|
+
setDraggedClip(null);
|
|
511
|
+
setResizingClip(null);
|
|
512
|
+
setRangeSelection(null);
|
|
513
|
+
setShowPopover(false);
|
|
514
|
+
setIsDragOver(false);
|
|
515
|
+
}, [disabled]);
|
|
516
|
+
|
|
501
517
|
// Effective duration: max of store duration and the furthest element end.
|
|
502
518
|
// processTimelineMessage updates elements but not duration, so elements can
|
|
503
519
|
// extend beyond the store's duration — this ensures fit mode shows everything.
|
|
@@ -724,6 +740,7 @@ export const Timeline = memo(function Timeline({
|
|
|
724
740
|
|
|
725
741
|
const seekFromX = useCallback(
|
|
726
742
|
(clientX: number) => {
|
|
743
|
+
if (disabledRef.current) return;
|
|
727
744
|
const el = scrollRef.current;
|
|
728
745
|
if (!el || effectiveDuration <= 0) return;
|
|
729
746
|
const rect = el.getBoundingClientRect();
|
|
@@ -781,6 +798,7 @@ export const Timeline = memo(function Timeline({
|
|
|
781
798
|
};
|
|
782
799
|
|
|
783
800
|
const handleWindowPointerMove = (e: PointerEvent) => {
|
|
801
|
+
if (disabledRef.current) return;
|
|
784
802
|
const drag = draggedClipRef.current;
|
|
785
803
|
const resize = resizingClipRef.current;
|
|
786
804
|
const blocked = blockedClipRef.current;
|
|
@@ -865,6 +883,7 @@ export const Timeline = memo(function Timeline({
|
|
|
865
883
|
|
|
866
884
|
const handleWindowPointerUp = () => {
|
|
867
885
|
stopClipDragAutoScrollRef.current();
|
|
886
|
+
if (disabledRef.current) return;
|
|
868
887
|
const resize = resizingClipRef.current;
|
|
869
888
|
if (resize) {
|
|
870
889
|
resizingClipRef.current = null;
|
|
@@ -956,30 +975,24 @@ export const Timeline = memo(function Timeline({
|
|
|
956
975
|
};
|
|
957
976
|
});
|
|
958
977
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
deleteInFlightRef.current = true;
|
|
967
|
-
suppressClickRef.current = true;
|
|
978
|
+
const prevSelectedRef = useRef(selectedElementRef.current);
|
|
979
|
+
// eslint-disable-next-line no-restricted-syntax, react-hooks/exhaustive-deps
|
|
980
|
+
useEffect(() => {
|
|
981
|
+
const prev = prevSelectedRef.current;
|
|
982
|
+
const curr = selectedElementRef.current;
|
|
983
|
+
prevSelectedRef.current = curr;
|
|
984
|
+
if (prev && !curr) {
|
|
968
985
|
setShowPopover(false);
|
|
969
986
|
setRangeSelection(null);
|
|
970
|
-
|
|
971
|
-
deleteInFlightRef.current = false;
|
|
972
|
-
requestAnimationFrame(() => {
|
|
973
|
-
suppressClickRef.current = false;
|
|
974
|
-
});
|
|
975
|
-
});
|
|
976
|
-
};
|
|
977
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
978
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
987
|
+
}
|
|
979
988
|
});
|
|
980
989
|
|
|
981
990
|
const handlePointerDown = useCallback(
|
|
982
991
|
(e: React.PointerEvent) => {
|
|
992
|
+
if (disabledRef.current) {
|
|
993
|
+
e.preventDefault();
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
983
996
|
if (e.button !== 0) return;
|
|
984
997
|
|
|
985
998
|
// Shift+click starts range selection — even on clips
|
|
@@ -1011,6 +1024,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1011
1024
|
);
|
|
1012
1025
|
const handlePointerMove = useCallback(
|
|
1013
1026
|
(e: React.PointerEvent) => {
|
|
1027
|
+
if (disabledRef.current) return;
|
|
1014
1028
|
if (isRangeSelecting.current) {
|
|
1015
1029
|
const rect = scrollRef.current?.getBoundingClientRect();
|
|
1016
1030
|
if (rect) {
|
|
@@ -1081,6 +1095,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1081
1095
|
|
|
1082
1096
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
1083
1097
|
const handleAssetDragOver = useCallback((e: React.DragEvent) => {
|
|
1098
|
+
if (disabledRef.current) return;
|
|
1084
1099
|
const hasFiles = e.dataTransfer.files.length > 0;
|
|
1085
1100
|
const hasAsset = Array.from(e.dataTransfer.types).includes(TIMELINE_ASSET_MIME);
|
|
1086
1101
|
if (!hasFiles && !hasAsset) return;
|
|
@@ -1095,6 +1110,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1095
1110
|
(e: React.DragEvent) => {
|
|
1096
1111
|
e.preventDefault();
|
|
1097
1112
|
setIsDragOver(false);
|
|
1113
|
+
if (disabledRef.current) return;
|
|
1098
1114
|
if (onFileDrop && e.dataTransfer.files.length > 0) {
|
|
1099
1115
|
const scroll = scrollRef.current;
|
|
1100
1116
|
const rect = scroll?.getBoundingClientRect();
|
|
@@ -1151,6 +1167,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1151
1167
|
|
|
1152
1168
|
const handlePinchWheel = useCallback(
|
|
1153
1169
|
(e: WheelEvent) => {
|
|
1170
|
+
if (disabledRef.current) return;
|
|
1154
1171
|
if (!e.ctrlKey) return;
|
|
1155
1172
|
const scroll = scrollRef.current;
|
|
1156
1173
|
if (!scroll || durationRef.current <= 0 || fitPpsRef.current <= 0 || ppsRef.current <= 0) {
|
|
@@ -1206,6 +1223,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1206
1223
|
className={`h-full border-t bg-[#0a0a0b] flex flex-col select-none transition-colors duration-150 ${
|
|
1207
1224
|
isDragOver ? "border-studio-accent/50 bg-studio-accent/[0.03]" : "border-neutral-800/50"
|
|
1208
1225
|
}`}
|
|
1226
|
+
aria-disabled={disabled || undefined}
|
|
1209
1227
|
onDragOver={handleAssetDragOver}
|
|
1210
1228
|
onDragLeave={() => setIsDragOver(false)}
|
|
1211
1229
|
onDrop={handleAssetDrop}
|
|
@@ -1361,7 +1379,14 @@ export const Timeline = memo(function Timeline({
|
|
|
1361
1379
|
<div
|
|
1362
1380
|
ref={setContainerRef}
|
|
1363
1381
|
aria-label="Timeline"
|
|
1364
|
-
|
|
1382
|
+
aria-disabled={disabled || undefined}
|
|
1383
|
+
className={`relative border-t select-none h-full overflow-hidden transition-opacity ${
|
|
1384
|
+
disabled
|
|
1385
|
+
? "cursor-not-allowed opacity-45"
|
|
1386
|
+
: shiftHeld
|
|
1387
|
+
? "cursor-crosshair"
|
|
1388
|
+
: "cursor-default"
|
|
1389
|
+
}`}
|
|
1365
1390
|
style={{
|
|
1366
1391
|
touchAction: "pan-x pan-y",
|
|
1367
1392
|
background: theme.shellBackground,
|
|
@@ -13,7 +13,7 @@ interface EditableTargetLike {
|
|
|
13
13
|
getAttribute?: (name: string) => string | null;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function isEditableTarget(target: EventTarget | null): boolean {
|
|
16
|
+
export function isEditableTarget(target: EventTarget | null): boolean {
|
|
17
17
|
if (!target || typeof target !== "object") return false;
|
|
18
18
|
|
|
19
19
|
const element = target as EditableTargetLike;
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
var D=Object.defineProperty;var F=(d,b,e)=>b in d?D(d,b,{enumerable:!0,configurable:!0,writable:!0,value:e}):d[b]=e;var p=(d,b,e)=>F(d,typeof b!="symbol"?b+"":b,e);const N=`
|
|
2
|
-
:host {
|
|
3
|
-
display: block;
|
|
4
|
-
position: relative;
|
|
5
|
-
overflow: hidden;
|
|
6
|
-
background: #000;
|
|
7
|
-
contain: layout style;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.hfp-container {
|
|
11
|
-
position: absolute;
|
|
12
|
-
inset: 0;
|
|
13
|
-
overflow: hidden;
|
|
14
|
-
pointer-events: none;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.hfp-iframe {
|
|
19
|
-
position: absolute;
|
|
20
|
-
top: 50%;
|
|
21
|
-
left: 50%;
|
|
22
|
-
border: none;
|
|
23
|
-
pointer-events: none;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.hfp-poster {
|
|
27
|
-
position: absolute;
|
|
28
|
-
inset: 0;
|
|
29
|
-
object-fit: contain;
|
|
30
|
-
z-index: 1;
|
|
31
|
-
pointer-events: none;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/* ── Theming via CSS custom properties ──
|
|
35
|
-
*
|
|
36
|
-
* Override from outside the shadow DOM:
|
|
37
|
-
* hyperframes-player {
|
|
38
|
-
* --hfp-controls-bg: linear-gradient(transparent, rgba(0,0,0,0.9));
|
|
39
|
-
* --hfp-accent: #ff6b6b;
|
|
40
|
-
* --hfp-font: "Inter", sans-serif;
|
|
41
|
-
* }
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
.hfp-controls {
|
|
45
|
-
position: absolute;
|
|
46
|
-
bottom: 0;
|
|
47
|
-
left: 0;
|
|
48
|
-
right: 0;
|
|
49
|
-
display: flex;
|
|
50
|
-
align-items: center;
|
|
51
|
-
gap: var(--hfp-controls-gap, 12px);
|
|
52
|
-
padding: var(--hfp-controls-padding, 8px 16px);
|
|
53
|
-
background: var(--hfp-controls-bg, linear-gradient(transparent, rgba(0, 0, 0, 0.7)));
|
|
54
|
-
color: var(--hfp-color, #fff);
|
|
55
|
-
font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);
|
|
56
|
-
font-size: var(--hfp-font-size, 13px);
|
|
57
|
-
z-index: 10;
|
|
58
|
-
pointer-events: auto;
|
|
59
|
-
opacity: 1;
|
|
60
|
-
transition: opacity 0.3s ease;
|
|
61
|
-
user-select: none;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.hfp-controls.hfp-hidden {
|
|
65
|
-
opacity: 0;
|
|
66
|
-
pointer-events: none;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.hfp-play-btn {
|
|
70
|
-
background: none;
|
|
71
|
-
border: none;
|
|
72
|
-
color: var(--hfp-color, #fff);
|
|
73
|
-
cursor: pointer;
|
|
74
|
-
padding: 8px;
|
|
75
|
-
display: flex;
|
|
76
|
-
align-items: center;
|
|
77
|
-
justify-content: center;
|
|
78
|
-
width: 40px;
|
|
79
|
-
height: 40px;
|
|
80
|
-
flex-shrink: 0;
|
|
81
|
-
z-index: 10;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.hfp-play-btn:hover {
|
|
85
|
-
opacity: 0.8;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.hfp-play-btn svg,
|
|
89
|
-
.hfp-play-btn svg * {
|
|
90
|
-
pointer-events: none;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.hfp-scrubber {
|
|
94
|
-
flex: 1;
|
|
95
|
-
height: var(--hfp-scrubber-height, 4px);
|
|
96
|
-
background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));
|
|
97
|
-
border-radius: var(--hfp-scrubber-radius, 2px);
|
|
98
|
-
cursor: pointer;
|
|
99
|
-
position: relative;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.hfp-scrubber:hover {
|
|
103
|
-
height: var(--hfp-scrubber-height-hover, 6px);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.hfp-progress {
|
|
107
|
-
position: absolute;
|
|
108
|
-
top: 0;
|
|
109
|
-
left: 0;
|
|
110
|
-
height: 100%;
|
|
111
|
-
background: var(--hfp-accent, #fff);
|
|
112
|
-
border-radius: var(--hfp-scrubber-radius, 2px);
|
|
113
|
-
pointer-events: none;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.hfp-time {
|
|
117
|
-
flex-shrink: 0;
|
|
118
|
-
font-variant-numeric: tabular-nums;
|
|
119
|
-
opacity: 0.9;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.hfp-speed-wrap {
|
|
123
|
-
position: relative;
|
|
124
|
-
flex-shrink: 0;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.hfp-speed-btn {
|
|
128
|
-
background: var(--hfp-speed-btn-bg, rgba(255, 255, 255, 0.15));
|
|
129
|
-
border: none;
|
|
130
|
-
border-radius: var(--hfp-speed-btn-radius, 4px);
|
|
131
|
-
color: var(--hfp-color, #fff);
|
|
132
|
-
cursor: pointer;
|
|
133
|
-
font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);
|
|
134
|
-
font-size: 12px;
|
|
135
|
-
font-variant-numeric: tabular-nums;
|
|
136
|
-
font-weight: 600;
|
|
137
|
-
padding: 4px 8px;
|
|
138
|
-
min-width: 40px;
|
|
139
|
-
text-align: center;
|
|
140
|
-
transition: background 0.15s ease;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.hfp-speed-btn:hover {
|
|
144
|
-
background: var(--hfp-speed-btn-bg-hover, rgba(255, 255, 255, 0.3));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.hfp-speed-menu {
|
|
148
|
-
position: absolute;
|
|
149
|
-
bottom: calc(100% + 8px);
|
|
150
|
-
right: 0;
|
|
151
|
-
background: var(--hfp-menu-bg, rgba(20, 20, 20, 0.95));
|
|
152
|
-
backdrop-filter: blur(12px);
|
|
153
|
-
-webkit-backdrop-filter: blur(12px);
|
|
154
|
-
border: 1px solid var(--hfp-menu-border, rgba(255, 255, 255, 0.1));
|
|
155
|
-
border-radius: var(--hfp-menu-radius, 8px);
|
|
156
|
-
padding: 4px;
|
|
157
|
-
display: flex;
|
|
158
|
-
flex-direction: column;
|
|
159
|
-
gap: 2px;
|
|
160
|
-
min-width: 80px;
|
|
161
|
-
opacity: 0;
|
|
162
|
-
visibility: hidden;
|
|
163
|
-
transform: translateY(4px);
|
|
164
|
-
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
|
|
165
|
-
box-shadow: var(--hfp-menu-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.hfp-speed-menu.hfp-open {
|
|
169
|
-
opacity: 1;
|
|
170
|
-
visibility: visible;
|
|
171
|
-
transform: translateY(0);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.hfp-speed-option {
|
|
175
|
-
background: none;
|
|
176
|
-
border: none;
|
|
177
|
-
border-radius: 4px;
|
|
178
|
-
color: var(--hfp-menu-color, rgba(255, 255, 255, 0.7));
|
|
179
|
-
cursor: pointer;
|
|
180
|
-
font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);
|
|
181
|
-
font-size: 13px;
|
|
182
|
-
font-variant-numeric: tabular-nums;
|
|
183
|
-
padding: 6px 12px;
|
|
184
|
-
text-align: left;
|
|
185
|
-
transition: background 0.1s ease, color 0.1s ease;
|
|
186
|
-
white-space: nowrap;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.hfp-speed-option:hover {
|
|
190
|
-
background: var(--hfp-menu-hover-bg, rgba(255, 255, 255, 0.1));
|
|
191
|
-
color: var(--hfp-color, #fff);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.hfp-speed-option.hfp-active {
|
|
195
|
-
color: var(--hfp-accent, #fff);
|
|
196
|
-
font-weight: 600;
|
|
197
|
-
}
|
|
198
|
-
`,O='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>',U='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><rect x="3" y="2" width="4" height="14"/><rect x="11" y="2" width="4" height="14"/></svg>',j=[.25,.5,1,1.5,2,4];function k(d){return Number.isInteger(d)?`${d}x`:`${d}x`}function R(d){if(!Number.isFinite(d)||d<0)return"0:00";const b=Math.floor(d),e=Math.floor(b/60),t=b%60;return`${e}:${t.toString().padStart(2,"0")}`}function z(d,b,e={}){const t=e.speedPresets??j,s=document.createElement("div");s.className="hfp-controls",s.addEventListener("click",o=>{o.stopPropagation()});const i=document.createElement("button");i.className="hfp-play-btn",i.type="button",i.innerHTML=O,i.setAttribute("aria-label","Play");const r=document.createElement("div");r.className="hfp-scrubber";const n=document.createElement("div");n.className="hfp-progress",n.style.width="0%",r.appendChild(n);const c=document.createElement("span");c.className="hfp-time",c.textContent="0:00 / 0:00";const _=document.createElement("div");_.className="hfp-speed-wrap";const u=document.createElement("button");u.className="hfp-speed-btn",u.type="button",u.textContent="1x",u.setAttribute("aria-label","Playback speed");const a=document.createElement("div");a.className="hfp-speed-menu",a.setAttribute("role","menu");for(const o of t){const h=document.createElement("button");h.className="hfp-speed-option",h.type="button",h.setAttribute("role","menuitem"),h.dataset.speed=String(o),h.textContent=k(o),o===1&&h.classList.add("hfp-active"),a.appendChild(h)}_.appendChild(a),_.appendChild(u),s.appendChild(i),s.appendChild(r),s.appendChild(c),s.appendChild(_),d.appendChild(s);let l=!1,f=null;t.indexOf(1),i.addEventListener("click",o=>{o.stopPropagation(),l?b.onPause():b.onPlay()});const m=o=>{for(const h of a.querySelectorAll(".hfp-speed-option"))h.classList.toggle("hfp-active",h.dataset.speed===String(o))};u.addEventListener("click",o=>{o.stopPropagation();const h=a.classList.toggle("hfp-open");u.setAttribute("aria-expanded",String(h))}),a.addEventListener("click",o=>{o.stopPropagation();const h=o.target.closest(".hfp-speed-option");if(!h)return;const y=parseFloat(h.dataset.speed);t.indexOf(y),u.textContent=k(y),m(y),a.classList.remove("hfp-open"),u.setAttribute("aria-expanded","false"),b.onSpeedChange(y)});const v=()=>{a.classList.remove("hfp-open"),u.setAttribute("aria-expanded","false")};document.addEventListener("click",v);const E=o=>{const h=r.getBoundingClientRect(),y=Math.max(0,Math.min(1,(o-h.left)/h.width));b.onSeek(y)};let g=!1;r.addEventListener("mousedown",o=>{o.stopPropagation(),g=!0,E(o.clientX)});const A=o=>{g&&E(o.clientX)},P=()=>{g=!1};document.addEventListener("mousemove",A),document.addEventListener("mouseup",P),r.addEventListener("touchstart",o=>{g=!0;const h=o.touches[0];h&&E(h.clientX)},{passive:!0});const C=o=>{if(g){const h=o.touches[0];h&&E(h.clientX)}},I=()=>{g=!1};document.addEventListener("touchmove",C,{passive:!0}),document.addEventListener("touchend",I);const T=()=>{f&&clearTimeout(f),f=setTimeout(()=>{l&&s.classList.add("hfp-hidden")},3e3)},L=d instanceof ShadowRoot?d.host:d;return L.addEventListener("mousemove",()=>{s.classList.remove("hfp-hidden"),T()}),L.addEventListener("mouseleave",()=>{l&&s.classList.add("hfp-hidden")}),{updateTime(o,h){const y=h>0?o/h*100:0;n.style.width=`${y}%`,c.textContent=`${R(o)} / ${R(h)}`},updatePlaying(o){l=o,i.innerHTML=o?U:O,i.setAttribute("aria-label",o?"Pause":"Play"),o?T():s.classList.remove("hfp-hidden")},updateSpeed(o){t.indexOf(o),u.textContent=k(o),m(o)},show(){s.style.display=""},hide(){s.style.display="none"},destroy(){document.removeEventListener("mousemove",A),document.removeEventListener("mouseup",P),document.removeEventListener("touchmove",C),document.removeEventListener("touchend",I),document.removeEventListener("click",v),f&&clearTimeout(f)}}}function H(d){return d.hasRuntime||d.runtimeInjected?!1:!!(d.hasNestedCompositions||d.hasTimelines&&d.attempts>=5)}let M=null;function q(){if(M)return M;if(typeof CSSStyleSheet>"u")return null;try{const d=new CSSStyleSheet;return d.replaceSync(N),M=d,d}catch{return null}}const S=30,W="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js",w=class w extends HTMLElement{constructor(){super();p(this,"shadow");p(this,"container");p(this,"iframe");p(this,"posterEl",null);p(this,"controlsApi",null);p(this,"resizeObserver");p(this,"_ready",!1);p(this,"_duration",0);p(this,"_currentTime",0);p(this,"_paused",!0);p(this,"_compositionWidth",1920);p(this,"_compositionHeight",1080);p(this,"_probeInterval",null);p(this,"_lastUpdateMs",0);p(this,"_parentMedia",[]);p(this,"_audioOwner","runtime");p(this,"_mediaObserver");p(this,"_playbackErrorPosted",!1);p(this,"_runtimeInjected",!1);this.shadow=this.attachShadow({mode:"open"});const e=q();if(e)this.shadow.adoptedStyleSheets=[e];else{const t=document.createElement("style");t.textContent=N,this.shadow.appendChild(t)}this.container=document.createElement("div"),this.container.className="hfp-container",this.iframe=document.createElement("iframe"),this.iframe.className="hfp-iframe",this.iframe.sandbox.add("allow-scripts","allow-same-origin"),this.iframe.allow="autoplay; fullscreen",this.iframe.referrerPolicy="no-referrer",this.iframe.title="HyperFrames Composition",this.container.appendChild(this.iframe),this.shadow.appendChild(this.container),this.addEventListener("click",t=>{this._isControlsClick(t)||(this._paused?this.play():this.pause())}),this.resizeObserver=new ResizeObserver(()=>this._updateScale()),this._onMessage=this._onMessage.bind(this),this._onIframeLoad=this._onIframeLoad.bind(this)}static get observedAttributes(){return["src","srcdoc","width","height","controls","muted","poster","playback-rate","audio-src"]}connectedCallback(){this.resizeObserver.observe(this),window.addEventListener("message",this._onMessage),this.iframe.addEventListener("load",this._onIframeLoad),this.hasAttribute("controls")&&this._setupControls(),this.hasAttribute("poster")&&this._setupPoster(),this.hasAttribute("audio-src")&&this._setupParentAudioFromUrl(this.getAttribute("audio-src")),this.hasAttribute("srcdoc")&&(this.iframe.srcdoc=this.getAttribute("srcdoc")),this.hasAttribute("src")&&(this.iframe.src=this.getAttribute("src"))}disconnectedCallback(){var e;this.resizeObserver.disconnect(),window.removeEventListener("message",this._onMessage),this.iframe.removeEventListener("load",this._onIframeLoad),this._probeInterval&&clearInterval(this._probeInterval),this._teardownMediaObserver(),(e=this.controlsApi)==null||e.destroy();for(const t of this._parentMedia)t.el.pause(),t.el.src="";this._parentMedia=[]}attributeChangedCallback(e,t,s){var i,r;switch(e){case"src":s&&(this._ready=!1,this.iframe.src=s);break;case"srcdoc":this._ready=!1,s!==null?this.iframe.srcdoc=s:this.iframe.removeAttribute("srcdoc");break;case"width":this._compositionWidth=parseInt(s||"1920",10),this._updateScale();break;case"height":this._compositionHeight=parseInt(s||"1080",10),this._updateScale();break;case"controls":s!==null?this._setupControls():((i=this.controlsApi)==null||i.destroy(),this.controlsApi=null);break;case"poster":this._setupPoster();break;case"playback-rate":{const n=parseFloat(s||"1");for(const c of this._parentMedia)c.el.playbackRate=n;this._sendControl("set-playback-rate",{playbackRate:n}),(r=this.controlsApi)==null||r.updateSpeed(n),this.dispatchEvent(new Event("ratechange"));break}case"muted":for(const n of this._parentMedia)n.el.muted=s!==null;this._sendControl("set-muted",{muted:s!==null});break;case"audio-src":s&&this._setupParentAudioFromUrl(s);break}}get iframeElement(){return this.iframe}play(){var e;this._hidePoster(),this._sendControl("play"),this._audioOwner==="parent"&&this._playParentMedia(),this._paused=!1,(e=this.controlsApi)==null||e.updatePlaying(!0),this.dispatchEvent(new Event("play"))}pause(){var e;this._sendControl("pause"),this._audioOwner==="parent"&&this._pauseParentMedia(),this._paused=!0,(e=this.controlsApi)==null||e.updatePlaying(!1),this.dispatchEvent(new Event("pause"))}seek(e){var t,s;if(!this._trySyncSeek(e)){const i=Math.round(e*S);this._sendControl("seek",{frame:i})}if(this._currentTime=e,this._audioOwner==="parent")for(const i of this._parentMedia){const r=e-i.start;r>=0&&r<i.duration&&(i.el.currentTime=r)}this._paused=!0,(t=this.controlsApi)==null||t.updatePlaying(!1),(s=this.controlsApi)==null||s.updateTime(this._currentTime,this._duration)}get currentTime(){return this._currentTime}set currentTime(e){this.seek(e)}get duration(){return this._duration}get paused(){return this._paused}get ready(){return this._ready}get playbackRate(){return parseFloat(this.getAttribute("playback-rate")||"1")}set playbackRate(e){this.setAttribute("playback-rate",String(e))}get muted(){return this.hasAttribute("muted")}set muted(e){e?this.setAttribute("muted",""):this.removeAttribute("muted")}get loop(){return this.hasAttribute("loop")}set loop(e){e?this.setAttribute("loop",""):this.removeAttribute("loop")}_sendControl(e,t={}){var s;try{(s=this.iframe.contentWindow)==null||s.postMessage({source:"hf-parent",type:"control",action:e,...t},"*")}catch{}}_trySyncSeek(e){try{const t=this.iframe.contentWindow,s=t==null?void 0:t.__player,i=s==null?void 0:s.seek;return typeof i!="function"?!1:(i.call(s,e),!0)}catch{return!1}}_isControlsClick(e){return e.composedPath().some(t=>t instanceof HTMLElement&&t.classList.contains("hfp-controls"))}_onMessage(e){var s,i,r,n;if(e.source!==this.iframe.contentWindow)return;const t=e.data;if(!(!t||t.source!=="hf-preview")){if(t.type==="state"){this._currentTime=(t.frame??0)/S;const c=!this._paused,_=!t.isPlaying,u=this._duration>0&&this._currentTime>=this._duration&&(c||t.isPlaying);if(u&&this.loop){this._audioOwner==="parent"&&this._pauseParentMedia(),this._paused=_,this.seek(0),this.play();return}this._paused=_,this._audioOwner==="parent"&&(c&&this._paused?this._pauseParentMedia():!c&&!this._paused&&this._playParentMedia(),this._mirrorParentMediaTime(this._currentTime));const a=performance.now();(a-this._lastUpdateMs>100||this._paused!==c)&&(this._lastUpdateMs=a,(s=this.controlsApi)==null||s.updateTime(this._currentTime,this._duration),(i=this.controlsApi)==null||i.updatePlaying(!this._paused),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{currentTime:this._currentTime}}))),u&&(this._audioOwner==="parent"&&this._pauseParentMedia(),this._paused=!0,(r=this.controlsApi)==null||r.updatePlaying(!1),this.dispatchEvent(new Event("ended")))}t.type==="media-autoplay-blocked"&&this._promoteToParentProxy(),t.type==="timeline"&&t.durationInFrames>0&&Number.isFinite(t.durationInFrames)&&(this._duration=t.durationInFrames/S,(n=this.controlsApi)==null||n.updateTime(this._currentTime,this._duration)),t.type==="stage-size"&&t.width>0&&t.height>0&&(this._compositionWidth=t.width,this._compositionHeight=t.height,this._updateScale())}}_onIframeLoad(){let e=0;this._runtimeInjected=!1;const t=this._audioOwner==="parent";this._audioOwner="runtime",this._playbackErrorPosted=!1,this._pauseParentMedia(),this._teardownMediaObserver(),t&&this.dispatchEvent(new CustomEvent("audioownershipchange",{detail:{owner:"runtime",reason:"iframe-reload"}})),this._probeInterval&&clearInterval(this._probeInterval),this._probeInterval=setInterval(()=>{var s,i;e++;try{const r=this.iframe.contentWindow;if(!r)return;const n=!!(r.__hf||r.__player),c=!!(r.__timelines&&Object.keys(r.__timelines).length>0),_=!!((s=this.iframe.contentDocument)!=null&&s.querySelector("[data-composition-src]"));if(H({hasRuntime:n,hasTimelines:c,hasNestedCompositions:_,runtimeInjected:this._runtimeInjected,attempts:e})){this._injectRuntime();return}if(this._runtimeInjected&&!n)return;const a=(()=>{var l,f;if(r.__player&&typeof r.__player.getDuration=="function")return r.__player;if(r.__timelines){const m=Object.keys(r.__timelines);if(m.length>0){const v=(f=(l=this.iframe.contentDocument)==null?void 0:l.querySelector("[data-composition-id]"))==null?void 0:f.getAttribute("data-composition-id"),E=v&&v in r.__timelines?v:m[m.length-1],g=r.__timelines[E];return{getDuration:()=>g.duration()}}}return null})();if(a&&a.getDuration()>0){clearInterval(this._probeInterval),this._duration=a.getDuration(),this._ready=!0,(i=this.controlsApi)==null||i.updateTime(0,this._duration),this.dispatchEvent(new CustomEvent("ready",{detail:{duration:this._duration}}));const l=this.iframe.contentDocument,f=l==null?void 0:l.querySelector("[data-composition-id]");if(f){const m=parseInt(f.getAttribute("data-width")||"0",10),v=parseInt(f.getAttribute("data-height")||"0",10);m>0&&v>0&&(this._compositionWidth=m,this._compositionHeight=v,this._updateScale())}this._setupParentMedia(),this.hasAttribute("autoplay")&&this.play();return}}catch{}e>=40&&(clearInterval(this._probeInterval),this.dispatchEvent(new CustomEvent("error",{detail:{message:"Composition timeline not found after 8s"}})))},200)}_injectRuntime(){this._runtimeInjected=!0;try{const e=this.iframe.contentDocument;if(!e)return;const t=e.createElement("script");t.src=W,t.onload=()=>{},t.onerror=()=>{},(e.head||e.documentElement).appendChild(t)}catch{}}_updateScale(){const e=this.getBoundingClientRect();if(e.width===0||e.height===0)return;const t=Math.min(e.width/this._compositionWidth,e.height/this._compositionHeight);this.iframe.style.width=`${this._compositionWidth}px`,this.iframe.style.height=`${this._compositionHeight}px`,this.iframe.style.transform=`translate(-50%, -50%) scale(${t})`}_setupControls(){if(this.controlsApi)return;const e={onPlay:()=>this.play(),onPause:()=>this.pause(),onSeek:i=>this.seek(i*this._duration),onSpeedChange:i=>{this.playbackRate=i}},t=this.getAttribute("speed-presets"),s=t?t.split(",").map(Number).filter(i=>!isNaN(i)&&i>0):void 0;this.controlsApi=z(this.shadow,e,{speedPresets:s})}_setupPoster(){var t;const e=this.getAttribute("poster");if(!e){(t=this.posterEl)==null||t.remove(),this.posterEl=null;return}this.posterEl||(this.posterEl=document.createElement("img"),this.posterEl.className="hfp-poster",this.shadow.appendChild(this.posterEl)),this.posterEl.src=e}_playParentMedia(){for(const e of this._parentMedia)e.el.src&&e.el.play().catch(t=>this._reportPlaybackError(t))}_reportPlaybackError(e){this._playbackErrorPosted||(this._playbackErrorPosted=!0,this.dispatchEvent(new CustomEvent("playbackerror",{detail:{source:"parent-proxy",error:e}})))}_pauseParentMedia(){for(const e of this._parentMedia)e.el.pause()}_mirrorParentMediaTime(e,t){const s=(t==null?void 0:t.force)===!0,i=w.MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES,r=w.MIRROR_DRIFT_THRESHOLD_SECONDS;for(const n of this._parentMedia){const c=e-n.start;if(c<0||c>=n.duration){n.driftSamples=0;continue}Math.abs(n.el.currentTime-c)>r?(n.driftSamples+=1,(s||n.driftSamples>=i)&&(n.el.currentTime=c,n.driftSamples=0)):n.driftSamples=0}}_promoteToParentProxy(){this._audioOwner!=="parent"&&(this._audioOwner="parent",this._sendControl("set-media-output-muted",{muted:!0}),this._mirrorParentMediaTime(this._currentTime,{force:!0}),this._paused||this._playParentMedia(),this.dispatchEvent(new CustomEvent("audioownershipchange",{detail:{owner:"parent",reason:"autoplay-blocked"}})))}_createParentMedia(e,t,s,i){if(this._parentMedia.some(c=>c.el.src===e))return null;const r=t==="video"?document.createElement("video"):new Audio;r.preload="auto",r.src=e,r.load(),r.muted=this.muted,this.playbackRate!==1&&(r.playbackRate=this.playbackRate);const n={el:r,start:s,duration:i,driftSamples:0};return this._parentMedia.push(n),n}_setupParentAudioFromUrl(e){this._createParentMedia(e,"audio",0,1/0)}_setupParentMedia(){try{const e=this.iframe.contentDocument;if(!e)return;const t=e.querySelectorAll("audio[data-start], video[data-start]");for(const s of t)this._adoptIframeMedia(s);this._observeDynamicMedia(e)}catch{}}_adoptIframeMedia(e){var _;const t=e.getAttribute("src")||((_=e.querySelector("source"))==null?void 0:_.getAttribute("src"));if(!t)return;const s=new URL(t,e.ownerDocument.baseURI).href,i=parseFloat(e.getAttribute("data-start")||"0"),r=parseFloat(e.getAttribute("data-duration")||"Infinity"),n=e.tagName==="VIDEO"?"video":"audio",c=this._createParentMedia(s,n,i,r);c&&this._audioOwner==="parent"&&(this._mirrorParentMediaTime(this._currentTime,{force:!0}),!this._paused&&c.el.src&&c.el.play().catch(u=>this._reportPlaybackError(u)))}_observeDynamicMedia(e){if(this._teardownMediaObserver(),typeof MutationObserver>"u"||!e.body)return;const t=new MutationObserver(i=>{var r,n,c,_;for(const u of i){for(const a of u.addedNodes){if(!(a instanceof Element))continue;const l=[];(r=a.matches)!=null&&r.call(a,"audio[data-start], video[data-start]")&&l.push(a);const f=(n=a.querySelectorAll)==null?void 0:n.call(a,"audio[data-start], video[data-start]");if(f)for(const m of f)l.push(m);for(const m of l)this._adoptIframeMedia(m)}for(const a of u.removedNodes){if(!(a instanceof Element))continue;const l=[];(c=a.matches)!=null&&c.call(a,"audio[data-start], video[data-start]")&&l.push(a);const f=(_=a.querySelectorAll)==null?void 0:_.call(a,"audio[data-start], video[data-start]");if(f)for(const m of f)l.push(m);for(const m of l)this._detachIframeMedia(m)}}}),s=e.querySelectorAll("[data-composition-id]");if(s.length>0)for(const i of s)t.observe(i,{childList:!0,subtree:!0});else t.observe(e.body,{childList:!0,subtree:!0});this._mediaObserver=t}_teardownMediaObserver(){var e;(e=this._mediaObserver)==null||e.disconnect(),this._mediaObserver=void 0}_detachIframeMedia(e){var n;const t=e.getAttribute("src")||((n=e.querySelector("source"))==null?void 0:n.getAttribute("src"));if(!t)return;const s=new URL(t,e.ownerDocument.baseURI).href,i=this._parentMedia.findIndex(c=>c.el.src===s);if(i===-1)return;const r=this._parentMedia[i];r.el.pause(),r.el.src="",this._parentMedia.splice(i,1)}_hidePoster(){var e;(e=this.posterEl)==null||e.remove(),this.posterEl=null}};p(w,"MIRROR_DRIFT_THRESHOLD_SECONDS",.05),p(w,"MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES",2);let x=w;customElements.get("hyperframes-player")||customElements.define("hyperframes-player",x);export{x as HyperframesPlayer,j as SPEED_PRESETS,k as formatSpeed,R as formatTime};
|