@hyperframes/studio 0.6.0-alpha.1 → 0.6.0-alpha.4
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-CEnWY28J.js +417 -0
- package/dist/assets/index-BfnyZllX.js +106 -0
- package/dist/assets/index-pZvEUcY0.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +217 -98
- package/src/components/editor/PropertyPanel.test.ts +49 -0
- package/src/components/editor/PropertyPanel.tsx +258 -1276
- 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 +6 -4
- package/src/components/editor/manualEdits.ts +15 -3
- package/src/components/nle/NLELayout.test.ts +12 -0
- package/src/components/nle/NLELayout.tsx +49 -24
- package/src/components/nle/NLEPreview.tsx +3 -0
- package/src/components/renders/RenderQueue.tsx +66 -4
- package/src/components/renders/useRenderQueue.ts +30 -6
- package/src/player/components/Player.test.ts +58 -0
- package/src/player/components/Player.tsx +58 -4
- package/src/player/components/PlayerControls.tsx +20 -7
- package/src/player/components/Timeline.tsx +38 -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;
|
|
@@ -498,6 +502,19 @@ export const Timeline = memo(function Timeline({
|
|
|
498
502
|
if (shortcutHintRafRef.current) cancelAnimationFrame(shortcutHintRafRef.current);
|
|
499
503
|
});
|
|
500
504
|
|
|
505
|
+
useEffect(() => {
|
|
506
|
+
if (!disabled) return;
|
|
507
|
+
stopClipDragAutoScrollRef.current();
|
|
508
|
+
isDragging.current = false;
|
|
509
|
+
isRangeSelecting.current = false;
|
|
510
|
+
blockedClipRef.current = null;
|
|
511
|
+
setDraggedClip(null);
|
|
512
|
+
setResizingClip(null);
|
|
513
|
+
setRangeSelection(null);
|
|
514
|
+
setShowPopover(false);
|
|
515
|
+
setIsDragOver(false);
|
|
516
|
+
}, [disabled]);
|
|
517
|
+
|
|
501
518
|
// Effective duration: max of store duration and the furthest element end.
|
|
502
519
|
// processTimelineMessage updates elements but not duration, so elements can
|
|
503
520
|
// extend beyond the store's duration — this ensures fit mode shows everything.
|
|
@@ -724,6 +741,7 @@ export const Timeline = memo(function Timeline({
|
|
|
724
741
|
|
|
725
742
|
const seekFromX = useCallback(
|
|
726
743
|
(clientX: number) => {
|
|
744
|
+
if (disabledRef.current) return;
|
|
727
745
|
const el = scrollRef.current;
|
|
728
746
|
if (!el || effectiveDuration <= 0) return;
|
|
729
747
|
const rect = el.getBoundingClientRect();
|
|
@@ -781,6 +799,7 @@ export const Timeline = memo(function Timeline({
|
|
|
781
799
|
};
|
|
782
800
|
|
|
783
801
|
const handleWindowPointerMove = (e: PointerEvent) => {
|
|
802
|
+
if (disabledRef.current) return;
|
|
784
803
|
const drag = draggedClipRef.current;
|
|
785
804
|
const resize = resizingClipRef.current;
|
|
786
805
|
const blocked = blockedClipRef.current;
|
|
@@ -865,6 +884,7 @@ export const Timeline = memo(function Timeline({
|
|
|
865
884
|
|
|
866
885
|
const handleWindowPointerUp = () => {
|
|
867
886
|
stopClipDragAutoScrollRef.current();
|
|
887
|
+
if (disabledRef.current) return;
|
|
868
888
|
const resize = resizingClipRef.current;
|
|
869
889
|
if (resize) {
|
|
870
890
|
resizingClipRef.current = null;
|
|
@@ -958,6 +978,7 @@ export const Timeline = memo(function Timeline({
|
|
|
958
978
|
|
|
959
979
|
useMountEffect(() => {
|
|
960
980
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
981
|
+
if (disabledRef.current) return;
|
|
961
982
|
if (!shouldHandleTimelineDeleteKey(event)) return;
|
|
962
983
|
const selected = selectedElementRef.current;
|
|
963
984
|
const onDelete = onDeleteElementRef.current;
|
|
@@ -980,6 +1001,10 @@ export const Timeline = memo(function Timeline({
|
|
|
980
1001
|
|
|
981
1002
|
const handlePointerDown = useCallback(
|
|
982
1003
|
(e: React.PointerEvent) => {
|
|
1004
|
+
if (disabledRef.current) {
|
|
1005
|
+
e.preventDefault();
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
983
1008
|
if (e.button !== 0) return;
|
|
984
1009
|
|
|
985
1010
|
// Shift+click starts range selection — even on clips
|
|
@@ -1011,6 +1036,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1011
1036
|
);
|
|
1012
1037
|
const handlePointerMove = useCallback(
|
|
1013
1038
|
(e: React.PointerEvent) => {
|
|
1039
|
+
if (disabledRef.current) return;
|
|
1014
1040
|
if (isRangeSelecting.current) {
|
|
1015
1041
|
const rect = scrollRef.current?.getBoundingClientRect();
|
|
1016
1042
|
if (rect) {
|
|
@@ -1081,6 +1107,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1081
1107
|
|
|
1082
1108
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
1083
1109
|
const handleAssetDragOver = useCallback((e: React.DragEvent) => {
|
|
1110
|
+
if (disabledRef.current) return;
|
|
1084
1111
|
const hasFiles = e.dataTransfer.files.length > 0;
|
|
1085
1112
|
const hasAsset = Array.from(e.dataTransfer.types).includes(TIMELINE_ASSET_MIME);
|
|
1086
1113
|
if (!hasFiles && !hasAsset) return;
|
|
@@ -1095,6 +1122,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1095
1122
|
(e: React.DragEvent) => {
|
|
1096
1123
|
e.preventDefault();
|
|
1097
1124
|
setIsDragOver(false);
|
|
1125
|
+
if (disabledRef.current) return;
|
|
1098
1126
|
if (onFileDrop && e.dataTransfer.files.length > 0) {
|
|
1099
1127
|
const scroll = scrollRef.current;
|
|
1100
1128
|
const rect = scroll?.getBoundingClientRect();
|
|
@@ -1151,6 +1179,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1151
1179
|
|
|
1152
1180
|
const handlePinchWheel = useCallback(
|
|
1153
1181
|
(e: WheelEvent) => {
|
|
1182
|
+
if (disabledRef.current) return;
|
|
1154
1183
|
if (!e.ctrlKey) return;
|
|
1155
1184
|
const scroll = scrollRef.current;
|
|
1156
1185
|
if (!scroll || durationRef.current <= 0 || fitPpsRef.current <= 0 || ppsRef.current <= 0) {
|
|
@@ -1206,6 +1235,7 @@ export const Timeline = memo(function Timeline({
|
|
|
1206
1235
|
className={`h-full border-t bg-[#0a0a0b] flex flex-col select-none transition-colors duration-150 ${
|
|
1207
1236
|
isDragOver ? "border-studio-accent/50 bg-studio-accent/[0.03]" : "border-neutral-800/50"
|
|
1208
1237
|
}`}
|
|
1238
|
+
aria-disabled={disabled || undefined}
|
|
1209
1239
|
onDragOver={handleAssetDragOver}
|
|
1210
1240
|
onDragLeave={() => setIsDragOver(false)}
|
|
1211
1241
|
onDrop={handleAssetDrop}
|
|
@@ -1361,7 +1391,14 @@ export const Timeline = memo(function Timeline({
|
|
|
1361
1391
|
<div
|
|
1362
1392
|
ref={setContainerRef}
|
|
1363
1393
|
aria-label="Timeline"
|
|
1364
|
-
|
|
1394
|
+
aria-disabled={disabled || undefined}
|
|
1395
|
+
className={`relative border-t select-none h-full overflow-hidden transition-opacity ${
|
|
1396
|
+
disabled
|
|
1397
|
+
? "cursor-not-allowed opacity-45"
|
|
1398
|
+
: shiftHeld
|
|
1399
|
+
? "cursor-crosshair"
|
|
1400
|
+
: "cursor-default"
|
|
1401
|
+
}`}
|
|
1365
1402
|
style={{
|
|
1366
1403
|
touchAction: "pan-x pan-y",
|
|
1367
1404
|
background: theme.shellBackground,
|
|
@@ -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};
|