@hyperframes/studio 0.5.3 → 0.5.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/index.html CHANGED
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
6
  <title>HyperFrames Studio</title>
7
- <script type="module" crossorigin src="/assets/index-BkBbJZGa.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BKjcNNNd.css">
7
+ <script type="module" crossorigin src="/assets/index-960mgQMI.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-04Mp2wOn.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,8 +32,8 @@
32
32
  "@phosphor-icons/react": "^2.1.10",
33
33
  "codemirror": "^6.0.1",
34
34
  "motion": "^12.38.0",
35
- "@hyperframes/core": "0.5.3",
36
- "@hyperframes/player": "0.5.3"
35
+ "@hyperframes/player": "0.5.4",
36
+ "@hyperframes/core": "0.5.4"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "^19.0.0",
@@ -47,7 +47,7 @@
47
47
  "vite": "^6.4.2",
48
48
  "vitest": "^3.2.4",
49
49
  "zustand": "^5.0.0",
50
- "@hyperframes/producer": "0.5.3"
50
+ "@hyperframes/producer": "0.5.4"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "react": "^18.0.0 || ^19.0.0",
package/src/App.tsx CHANGED
@@ -1684,7 +1684,9 @@ export function StudioApp() {
1684
1684
  projectId={projectId}
1685
1685
  onDelete={renderQueue.deleteRender}
1686
1686
  onClearCompleted={renderQueue.clearCompleted}
1687
- onStartRender={(format, quality) => renderQueue.startRender(30, quality, format)}
1687
+ onStartRender={(format, quality, resolution) =>
1688
+ renderQueue.startRender({ format, quality, resolution })
1689
+ }
1688
1690
  isRendering={renderQueue.isRendering}
1689
1691
  />
1690
1692
  )}
@@ -1,16 +1,50 @@
1
1
  import { memo, useState, useRef, useEffect } from "react";
2
2
  import { RenderQueueItem } from "./RenderQueueItem";
3
- import type { RenderJob } from "./useRenderQueue";
3
+ import type { RenderJob, ResolutionPreset } from "./useRenderQueue";
4
4
 
5
5
  interface RenderQueueProps {
6
6
  jobs: RenderJob[];
7
7
  projectId: string;
8
8
  onDelete: (jobId: string) => void;
9
9
  onClearCompleted: () => void;
10
- onStartRender: (format: "mp4" | "webm" | "mov", quality: "draft" | "standard" | "high") => void;
10
+ onStartRender: (
11
+ format: "mp4" | "webm" | "mov",
12
+ quality: "draft" | "standard" | "high",
13
+ resolution: ResolutionPreset | "auto",
14
+ ) => void;
11
15
  isRendering: boolean;
12
16
  }
13
17
 
18
+ // Indexing the table by `ResolutionPreset | "auto"` makes adding a new preset
19
+ // to `core.types` (e.g. an 8K row) a TypeScript error here instead of a
20
+ // silently missing dropdown entry. Order is fixed by the array below.
21
+ const RESOLUTION_LABELS: Record<ResolutionPreset | "auto", { label: string; title: string }> = {
22
+ auto: { label: "Auto", title: "Render at the composition's authored resolution" },
23
+ landscape: { label: "1080p ↔", title: "1920×1080 landscape" },
24
+ portrait: { label: "1080p ↕", title: "1080×1920 portrait" },
25
+ "landscape-4k": {
26
+ label: "4K ↔",
27
+ title: "3840×2160 — supersamples a 1080p composition via Chrome DPR. Slower, larger files.",
28
+ },
29
+ "portrait-4k": {
30
+ label: "4K ↕",
31
+ title: "2160×3840 — supersamples a 1080p portrait composition via Chrome DPR.",
32
+ },
33
+ };
34
+
35
+ const RESOLUTION_OPTION_ORDER: (ResolutionPreset | "auto")[] = [
36
+ "auto",
37
+ "landscape",
38
+ "portrait",
39
+ "landscape-4k",
40
+ "portrait-4k",
41
+ ];
42
+
43
+ const RESOLUTION_OPTIONS = RESOLUTION_OPTION_ORDER.map((value) => ({
44
+ value,
45
+ ...RESOLUTION_LABELS[value],
46
+ }));
47
+
14
48
  const FORMAT_INFO: Record<"mp4" | "webm" | "mov", { label: string; desc: string }> = {
15
49
  mp4: { label: "MP4", desc: "Best for general use. Smallest file, universal playback." },
16
50
  mov: {
@@ -91,11 +125,16 @@ function FormatExportButton({
91
125
  onStartRender,
92
126
  isRendering,
93
127
  }: {
94
- onStartRender: (format: "mp4" | "webm" | "mov", quality: "draft" | "standard" | "high") => void;
128
+ onStartRender: (
129
+ format: "mp4" | "webm" | "mov",
130
+ quality: "draft" | "standard" | "high",
131
+ resolution: ResolutionPreset | "auto",
132
+ ) => void;
95
133
  isRendering: boolean;
96
134
  }) {
97
135
  const [format, setFormat] = useState<"mp4" | "webm" | "mov">("mp4");
98
136
  const [quality, setQuality] = useState<"draft" | "standard" | "high">("standard");
137
+ const [resolution, setResolution] = useState<ResolutionPreset | "auto">("auto");
99
138
 
100
139
  // MOV (ProRes) is a fixed-quality codec — quality selector has no effect.
101
140
  const showQuality = format !== "mov";
@@ -103,13 +142,30 @@ function FormatExportButton({
103
142
  return (
104
143
  <div className="flex items-center gap-1">
105
144
  <FormatInfoTooltip format={format} />
145
+ {/* Resolution must remain the leftmost <select> in this row — it
146
+ carries `rounded-l` for the joined-button look. If you ever hide it
147
+ (feature-flag, etc.), move `rounded-l` to whichever element ends up
148
+ leftmost. */}
149
+ <select
150
+ value={resolution}
151
+ onChange={(e) => setResolution(e.target.value as ResolutionPreset | "auto")}
152
+ disabled={isRendering}
153
+ title={RESOLUTION_OPTIONS.find((r) => r.value === resolution)?.title}
154
+ className="h-5 px-1 text-[10px] rounded-l bg-neutral-800 border border-neutral-700 text-neutral-300 outline-none disabled:opacity-50"
155
+ >
156
+ {RESOLUTION_OPTIONS.map((r) => (
157
+ <option key={r.value} value={r.value} title={r.title}>
158
+ {r.label}
159
+ </option>
160
+ ))}
161
+ </select>
106
162
  {showQuality && (
107
163
  <select
108
164
  value={quality}
109
165
  onChange={(e) => setQuality(e.target.value as "draft" | "standard" | "high")}
110
166
  disabled={isRendering}
111
167
  title={QUALITY_OPTIONS.find((q) => q.value === quality)?.title}
112
- className="h-5 px-1 text-[10px] rounded-l bg-neutral-800 border border-neutral-700 text-neutral-300 outline-none disabled:opacity-50"
168
+ className="h-5 px-1 text-[10px] bg-neutral-800 border border-neutral-700 text-neutral-300 outline-none disabled:opacity-50"
113
169
  >
114
170
  {QUALITY_OPTIONS.map((q) => (
115
171
  <option key={q.value} value={q.value} title={q.title}>
@@ -122,14 +178,14 @@ function FormatExportButton({
122
178
  value={format}
123
179
  onChange={(e) => setFormat(e.target.value as "mp4" | "webm" | "mov")}
124
180
  disabled={isRendering}
125
- className={`h-5 px-1 text-[10px] bg-neutral-800 border border-neutral-700 text-neutral-300 outline-none disabled:opacity-50 ${showQuality ? "" : "rounded-l"}`}
181
+ className="h-5 px-1 text-[10px] bg-neutral-800 border border-neutral-700 text-neutral-300 outline-none disabled:opacity-50"
126
182
  >
127
183
  <option value="mp4">MP4</option>
128
184
  <option value="mov">MOV</option>
129
185
  <option value="webm">WebM</option>
130
186
  </select>
131
187
  <button
132
- onClick={() => onStartRender(format, quality)}
188
+ onClick={() => onStartRender(format, quality, resolution)}
133
189
  disabled={isRendering}
134
190
  className="flex items-center gap-1 px-2 py-0.5 text-[10px] font-semibold rounded-r bg-studio-accent text-[#09090B] hover:brightness-110 transition-colors disabled:opacity-50"
135
191
  >
@@ -11,6 +11,20 @@ export interface RenderJob {
11
11
  durationMs?: number;
12
12
  }
13
13
 
14
+ // Mirrors `CanvasResolution` from @hyperframes/core. Kept local because
15
+ // studio's tsconfig doesn't include node types, and the core barrel
16
+ // transitively pulls in modules with `node:fs` imports. Drift risk is
17
+ // low (4 string literals tied to a stable enum).
18
+ export type ResolutionPreset = "landscape" | "portrait" | "landscape-4k" | "portrait-4k";
19
+
20
+ export interface StartRenderOptions {
21
+ fps?: number;
22
+ quality?: "draft" | "standard" | "high";
23
+ format?: "mp4" | "webm" | "mov";
24
+ /** `"auto"` (default) renders at the composition's authored dimensions. */
25
+ resolution?: ResolutionPreset | "auto";
26
+ }
27
+
14
28
  export function useRenderQueue(projectId: string | null) {
15
29
  const [jobs, setJobs] = useState<RenderJob[]>([]);
16
30
  const eventSourceRef = useRef<EventSource | null>(null);
@@ -59,20 +73,30 @@ export function useRenderQueue(projectId: string | null) {
59
73
 
60
74
  // Start a render and track progress via SSE
61
75
  const startRender = useCallback(
62
- async (
63
- fps = 30,
64
- quality: "draft" | "standard" | "high" = "standard",
65
- format: "mp4" | "webm" | "mov" = "mp4",
66
- ) => {
76
+ async (opts: StartRenderOptions = {}) => {
67
77
  if (!projectId) return;
68
78
 
79
+ const fps = opts.fps ?? 30;
80
+ const quality = opts.quality ?? "standard";
81
+ const format = opts.format ?? "mp4";
82
+ const resolution = opts.resolution;
83
+
69
84
  const startTime = Date.now();
85
+ // "auto" / undefined means "render at the composition's authored size".
86
+ // Omit the field entirely — sending "auto" would trip the route's
87
+ // enum validation set.
88
+ const body: { fps: number; quality: string; format: string; resolution?: string } = {
89
+ fps,
90
+ quality,
91
+ format,
92
+ };
93
+ if (resolution && resolution !== "auto") body.resolution = resolution;
70
94
  let res: Response;
71
95
  try {
72
96
  res = await fetch(`/api/projects/${projectId}/render`, {
73
97
  method: "POST",
74
98
  headers: { "Content-Type": "application/json" },
75
- body: JSON.stringify({ fps, quality, format }),
99
+ body: JSON.stringify(body),
76
100
  });
77
101
  } catch {
78
102
  const failedJob: RenderJob = {
@@ -1,353 +0,0 @@
1
- var j=Object.defineProperty;var V=(a,d,e)=>d in a?j(a,d,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[d]=e;var c=(a,d,e)=>V(a,typeof d!="symbol"?d+"":d,e);const z=`
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
- .hfp-shader-loader {
35
- position: absolute;
36
- inset: 0;
37
- z-index: 20;
38
- display: grid;
39
- place-items: center;
40
- visibility: hidden;
41
- opacity: 0;
42
- pointer-events: none;
43
- background: #030504;
44
- color: #f4f7fb;
45
- cursor: default;
46
- user-select: none;
47
- -webkit-user-select: none;
48
- transition: opacity 420ms ease-out, visibility 420ms ease-out;
49
- }
50
-
51
- .hfp-shader-loader.hfp-visible,
52
- .hfp-shader-loader.hfp-hiding {
53
- visibility: visible;
54
- }
55
-
56
- .hfp-shader-loader.hfp-visible {
57
- opacity: 1;
58
- pointer-events: auto;
59
- }
60
-
61
- .hfp-shader-loader.hfp-hiding {
62
- opacity: 0;
63
- pointer-events: none;
64
- }
65
-
66
- .hfp-shader-loader-panel {
67
- display: grid;
68
- grid-template-rows: 86px 40px 26px 12px 44px;
69
- justify-items: center;
70
- align-items: center;
71
- gap: 8px;
72
- width: min(620px, 82%);
73
- text-align: center;
74
- font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
75
- }
76
-
77
- .hfp-shader-loader-mark {
78
- width: 86px;
79
- height: 86px;
80
- display: grid;
81
- place-items: center;
82
- overflow: visible;
83
- }
84
-
85
- .hfp-shader-loader-mark svg {
86
- display: block;
87
- overflow: visible;
88
- filter: drop-shadow(0 0 5px rgba(79, 219, 94, 0.16));
89
- pointer-events: none;
90
- }
91
-
92
- .hfp-shader-loader-title {
93
- width: 100%;
94
- height: 40px;
95
- overflow: hidden;
96
- white-space: nowrap;
97
- text-overflow: ellipsis;
98
- font-size: 26px;
99
- line-height: 40px;
100
- font-weight: 700;
101
- letter-spacing: 0;
102
- }
103
-
104
- .hfp-shader-loader-title-text {
105
- color: transparent;
106
- background: linear-gradient(
107
- 90deg,
108
- rgba(244, 247, 251, 0.84) 0%,
109
- #ffffff 42%,
110
- #80efe4 52%,
111
- #ffffff 62%,
112
- rgba(244, 247, 251, 0.84) 100%
113
- );
114
- background-size: 220% 100%;
115
- -webkit-background-clip: text;
116
- background-clip: text;
117
- animation: hfp-shader-loader-sheen 1.9s linear infinite;
118
- }
119
-
120
- .hfp-shader-loader-detail {
121
- width: 100%;
122
- height: 26px;
123
- overflow: hidden;
124
- white-space: nowrap;
125
- text-overflow: ellipsis;
126
- color: rgba(244, 247, 251, 0.62);
127
- font-size: 15px;
128
- line-height: 26px;
129
- font-weight: 500;
130
- }
131
-
132
- .hfp-shader-loader-track {
133
- width: min(360px, 100%);
134
- height: 8px;
135
- overflow: hidden;
136
- border-radius: 999px;
137
- background: rgba(255, 255, 255, 0.1);
138
- }
139
-
140
- .hfp-shader-loader-fill {
141
- width: 100%;
142
- height: 100%;
143
- border-radius: inherit;
144
- background: linear-gradient(90deg, #06e3fa, #4fdb5e);
145
- transform: scaleX(0);
146
- transform-origin: left center;
147
- transition: transform 160ms ease;
148
- }
149
-
150
- .hfp-shader-loader-progress {
151
- width: min(420px, 100%);
152
- height: 44px;
153
- display: grid;
154
- grid-template-rows: repeat(2, 22px);
155
- color: rgba(244, 247, 251, 0.48);
156
- font: 600 13px/22px "IBM Plex Mono", "SF Mono", "Fira Code", "Courier New", monospace;
157
- font-variant-numeric: tabular-nums;
158
- }
159
-
160
- .hfp-shader-loader-row {
161
- display: grid;
162
- grid-template-columns: minmax(0, 1fr) 74px;
163
- align-items: center;
164
- column-gap: 20px;
165
- width: 100%;
166
- white-space: nowrap;
167
- }
168
-
169
- .hfp-shader-loader-label {
170
- min-width: 0;
171
- overflow: hidden;
172
- text-align: left;
173
- text-overflow: ellipsis;
174
- }
175
-
176
- .hfp-shader-loader-value {
177
- text-align: right;
178
- }
179
-
180
- @keyframes hfp-shader-loader-sheen {
181
- from {
182
- background-position: 140% 0;
183
- }
184
- to {
185
- background-position: -140% 0;
186
- }
187
- }
188
-
189
- /* ── Theming via CSS custom properties ──
190
- *
191
- * Override from outside the shadow DOM:
192
- * hyperframes-player {
193
- * --hfp-controls-bg: linear-gradient(transparent, rgba(0,0,0,0.9));
194
- * --hfp-accent: #ff6b6b;
195
- * --hfp-font: "Inter", sans-serif;
196
- * }
197
- */
198
-
199
- .hfp-controls {
200
- position: absolute;
201
- bottom: 0;
202
- left: 0;
203
- right: 0;
204
- display: flex;
205
- align-items: center;
206
- gap: var(--hfp-controls-gap, 12px);
207
- padding: var(--hfp-controls-padding, 8px 16px);
208
- background: var(--hfp-controls-bg, linear-gradient(transparent, rgba(0, 0, 0, 0.7)));
209
- color: var(--hfp-color, #fff);
210
- font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);
211
- font-size: var(--hfp-font-size, 13px);
212
- z-index: 10;
213
- pointer-events: auto;
214
- opacity: 1;
215
- transition: opacity 0.3s ease;
216
- user-select: none;
217
- }
218
-
219
- .hfp-controls.hfp-hidden {
220
- opacity: 0;
221
- pointer-events: none;
222
- }
223
-
224
- .hfp-play-btn {
225
- background: none;
226
- border: none;
227
- color: var(--hfp-color, #fff);
228
- cursor: pointer;
229
- padding: 8px;
230
- display: flex;
231
- align-items: center;
232
- justify-content: center;
233
- width: 40px;
234
- height: 40px;
235
- flex-shrink: 0;
236
- z-index: 10;
237
- }
238
-
239
- .hfp-play-btn:hover {
240
- opacity: 0.8;
241
- }
242
-
243
- .hfp-play-btn svg,
244
- .hfp-play-btn svg * {
245
- pointer-events: none;
246
- }
247
-
248
- .hfp-scrubber {
249
- flex: 1;
250
- height: var(--hfp-scrubber-height, 4px);
251
- background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));
252
- border-radius: var(--hfp-scrubber-radius, 2px);
253
- cursor: pointer;
254
- position: relative;
255
- }
256
-
257
- .hfp-scrubber:hover {
258
- height: var(--hfp-scrubber-height-hover, 6px);
259
- }
260
-
261
- .hfp-progress {
262
- position: absolute;
263
- top: 0;
264
- left: 0;
265
- height: 100%;
266
- background: var(--hfp-accent, #fff);
267
- border-radius: var(--hfp-scrubber-radius, 2px);
268
- pointer-events: none;
269
- }
270
-
271
- .hfp-time {
272
- flex-shrink: 0;
273
- font-variant-numeric: tabular-nums;
274
- opacity: 0.9;
275
- }
276
-
277
- .hfp-speed-wrap {
278
- position: relative;
279
- flex-shrink: 0;
280
- }
281
-
282
- .hfp-speed-btn {
283
- background: var(--hfp-speed-btn-bg, rgba(255, 255, 255, 0.15));
284
- border: none;
285
- border-radius: var(--hfp-speed-btn-radius, 4px);
286
- color: var(--hfp-color, #fff);
287
- cursor: pointer;
288
- font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);
289
- font-size: 12px;
290
- font-variant-numeric: tabular-nums;
291
- font-weight: 600;
292
- padding: 4px 8px;
293
- min-width: 40px;
294
- text-align: center;
295
- transition: background 0.15s ease;
296
- }
297
-
298
- .hfp-speed-btn:hover {
299
- background: var(--hfp-speed-btn-bg-hover, rgba(255, 255, 255, 0.3));
300
- }
301
-
302
- .hfp-speed-menu {
303
- position: absolute;
304
- bottom: calc(100% + 8px);
305
- right: 0;
306
- background: var(--hfp-menu-bg, rgba(20, 20, 20, 0.95));
307
- backdrop-filter: blur(12px);
308
- -webkit-backdrop-filter: blur(12px);
309
- border: 1px solid var(--hfp-menu-border, rgba(255, 255, 255, 0.1));
310
- border-radius: var(--hfp-menu-radius, 8px);
311
- padding: 4px;
312
- display: flex;
313
- flex-direction: column;
314
- gap: 2px;
315
- min-width: 80px;
316
- opacity: 0;
317
- visibility: hidden;
318
- transform: translateY(4px);
319
- transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
320
- box-shadow: var(--hfp-menu-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
321
- }
322
-
323
- .hfp-speed-menu.hfp-open {
324
- opacity: 1;
325
- visibility: visible;
326
- transform: translateY(0);
327
- }
328
-
329
- .hfp-speed-option {
330
- background: none;
331
- border: none;
332
- border-radius: 4px;
333
- color: var(--hfp-menu-color, rgba(255, 255, 255, 0.7));
334
- cursor: pointer;
335
- font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);
336
- font-size: 13px;
337
- font-variant-numeric: tabular-nums;
338
- padding: 6px 12px;
339
- text-align: left;
340
- transition: background 0.1s ease, color 0.1s ease;
341
- white-space: nowrap;
342
- }
343
-
344
- .hfp-speed-option:hover {
345
- background: var(--hfp-menu-hover-bg, rgba(255, 255, 255, 0.1));
346
- color: var(--hfp-color, #fff);
347
- }
348
-
349
- .hfp-speed-option.hfp-active {
350
- color: var(--hfp-accent, #fff);
351
- font-weight: 600;
352
- }
353
- `,F='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>',q='<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>',B=[.25,.5,1,1.5,2,4];function C(a){return Number.isInteger(a)?`${a}x`:`${a}x`}function D(a){if(!Number.isFinite(a)||a<0)return"0:00";const d=Math.floor(a),e=Math.floor(d/60),t=d%60;return`${e}:${t.toString().padStart(2,"0")}`}function W(a,d,e={}){const t=e.speedPresets??B,r=document.createElement("div");r.className="hfp-controls",r.addEventListener("click",l=>{l.stopPropagation()});const i=document.createElement("button");i.className="hfp-play-btn",i.type="button",i.innerHTML=F,i.setAttribute("aria-label","Play");const s=document.createElement("div");s.className="hfp-scrubber";const n=document.createElement("div");n.className="hfp-progress",n.style.width="0%",s.appendChild(n);const o=document.createElement("span");o.className="hfp-time",o.textContent="0:00 / 0:00";const f=document.createElement("div");f.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 h=document.createElement("div");h.className="hfp-speed-menu",h.setAttribute("role","menu");for(const l of t){const p=document.createElement("button");p.className="hfp-speed-option",p.type="button",p.setAttribute("role","menuitem"),p.dataset.speed=String(l),p.textContent=C(l),l===1&&p.classList.add("hfp-active"),h.appendChild(p)}f.appendChild(h),f.appendChild(u),r.appendChild(i),r.appendChild(s),r.appendChild(o),r.appendChild(f),a.appendChild(r);let m=!1,g=null;t.indexOf(1),i.addEventListener("click",l=>{l.stopPropagation(),m?d.onPause():d.onPlay()});const b=l=>{for(const p of h.querySelectorAll(".hfp-speed-option"))p.classList.toggle("hfp-active",p.dataset.speed===String(l))};u.addEventListener("click",l=>{l.stopPropagation();const p=h.classList.toggle("hfp-open");u.setAttribute("aria-expanded",String(p))}),h.addEventListener("click",l=>{l.stopPropagation();const p=l.target.closest(".hfp-speed-option");if(!p)return;const E=parseFloat(p.dataset.speed);t.indexOf(E),u.textContent=C(E),b(E),h.classList.remove("hfp-open"),u.setAttribute("aria-expanded","false"),d.onSpeedChange(E)});const _=()=>{h.classList.remove("hfp-open"),u.setAttribute("aria-expanded","false")};document.addEventListener("click",_);const y=l=>{const p=s.getBoundingClientRect(),E=Math.max(0,Math.min(1,(l-p.left)/p.width));d.onSeek(E)};let v=!1;s.addEventListener("mousedown",l=>{l.stopPropagation(),v=!0,y(l.clientX)});const w=l=>{v&&y(l.clientX)},P=()=>{v=!1};document.addEventListener("mousemove",w),document.addEventListener("mouseup",P),s.addEventListener("touchstart",l=>{v=!0;const p=l.touches[0];p&&y(p.clientX)},{passive:!0});const I=l=>{if(v){const p=l.touches[0];p&&y(p.clientX)}},R=()=>{v=!1};document.addEventListener("touchmove",I,{passive:!0}),document.addEventListener("touchend",R);const O=()=>{g&&clearTimeout(g),g=setTimeout(()=>{m&&r.classList.add("hfp-hidden")},3e3)},N=a instanceof ShadowRoot?a.host:a;return N.addEventListener("mousemove",()=>{r.classList.remove("hfp-hidden"),O()}),N.addEventListener("mouseleave",()=>{m&&r.classList.add("hfp-hidden")}),{updateTime(l,p){const E=p>0?l/p*100:0;n.style.width=`${E}%`,o.textContent=`${D(l)} / ${D(p)}`},updatePlaying(l){m=l,i.innerHTML=l?q:F,i.setAttribute("aria-label",l?"Pause":"Play"),l?O():r.classList.remove("hfp-hidden")},updateSpeed(l){t.indexOf(l),u.textContent=C(l),b(l)},show(){r.style.display=""},hide(){r.style.display="none"},destroy(){document.removeEventListener("mousemove",w),document.removeEventListener("mouseup",P),document.removeEventListener("touchmove",I),document.removeEventListener("touchend",R),document.removeEventListener("click",_),g&&clearTimeout(g)}}}function G(a){return a.hasRuntime||a.runtimeInjected?!1:!!(a.hasNestedCompositions||a.hasTimelines&&a.attempts>=5)}let k=null;function X(){if(k)return k;if(typeof CSSStyleSheet>"u")return null;try{const a=new CSSStyleSheet;return a.replaceSync(z),k=a,a}catch{return null}}const T=30,Q="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js",S="shader-capture-scale",L="shader-loading",Y="__hf_shader_capture_scale",J="__hf_shader_loading",A=["Preparing scene transitions","Sampling outgoing scene motion","Sampling incoming scene motion","Caching transition frames","Finalizing transition preview"];function H(a){if(a===null)return null;const d=Number(a);return!Number.isFinite(d)||d<=0?null:String(Math.min(1,Math.max(.25,d)))}function $(a){if(a===null||a.trim()==="")return"composition";const d=a.trim().toLowerCase();return d==="none"||d==="false"||d==="0"||d==="off"?"none":d==="player"||d==="true"||d==="1"||d==="on"?"player":"composition"}function U(a,d,e){e===null?a.delete(d):a.set(d,e)}function Z(a,d,e){const t=a.indexOf("#"),r=t>=0?a.slice(0,t):a,i=t>=0?a.slice(t):"",s=r.indexOf("?"),n=s>=0?r.slice(0,s):r,o=s>=0?r.slice(s+1):"",f=new URLSearchParams(o);U(f,Y,d),U(f,J,e==="composition"?null:e);const u=f.toString();return`${n}${u?`?${u}`:""}${i}`}function K(a,d,e){if(d===null&&e==="composition")return a;const t=[];d!==null&&t.push(`window.__HF_SHADER_CAPTURE_SCALE=${JSON.stringify(d)};`),e!=="composition"&&t.push(`window.__HF_SHADER_LOADING=${JSON.stringify(e)};`);const r=`<script data-hyperframes-player-shader-options>${t.join("")}<\/script>`;return/<head\b[^>]*>/i.test(a)?a.replace(/<head\b[^>]*>/i,i=>`${i}${r}`):/<html\b[^>]*>/i.test(a)?a.replace(/<html\b[^>]*>/i,i=>`${i}${r}`):`${r}${a}`}const x=class x extends HTMLElement{constructor(){super();c(this,"shadow");c(this,"container");c(this,"iframe");c(this,"posterEl",null);c(this,"controlsApi",null);c(this,"resizeObserver");c(this,"shaderLoaderEl");c(this,"shaderLoaderFillEl");c(this,"shaderLoaderTitleEl");c(this,"shaderLoaderDetailEl");c(this,"shaderLoaderTransitionValueEl");c(this,"shaderLoaderFrameLabelEl");c(this,"shaderLoaderFrameValueEl");c(this,"shaderLoaderFrameRowEl");c(this,"shaderLoaderHideTimeout",null);c(this,"_ready",!1);c(this,"_duration",0);c(this,"_currentTime",0);c(this,"_paused",!0);c(this,"_compositionWidth",1920);c(this,"_compositionHeight",1080);c(this,"_probeInterval",null);c(this,"_lastUpdateMs",0);c(this,"_parentMedia",[]);c(this,"_audioOwner","runtime");c(this,"_mediaObserver");c(this,"_playbackErrorPosted",!1);c(this,"_runtimeInjected",!1);this.shadow=this.attachShadow({mode:"open"});const e=X();if(e)this.shadow.adoptedStyleSheets=[e];else{const r=document.createElement("style");r.textContent=z,this.shadow.appendChild(r)}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);const t=this._createShaderLoader();this.shaderLoaderEl=t.root,this.shaderLoaderFillEl=t.fill,this.shaderLoaderTitleEl=t.title,this.shaderLoaderDetailEl=t.detail,this.shaderLoaderTransitionValueEl=t.transitionValue,this.shaderLoaderFrameLabelEl=t.frameLabel,this.shaderLoaderFrameValueEl=t.frameValue,this.shaderLoaderFrameRowEl=t.frameRow,this.shadow.appendChild(this.shaderLoaderEl),this.addEventListener("click",r=>{this._isControlsClick(r)||(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",S,L]}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._prepareSrcdoc(this.getAttribute("srcdoc"))),this.hasAttribute("src")&&(this.iframe.src=this._prepareSrc(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.shaderLoaderHideTimeout&&clearTimeout(this.shaderLoaderHideTimeout),this.shaderLoaderHideTimeout=null,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,r){var i,s;switch(e){case"src":r&&(this._ready=!1,this.iframe.src=this._prepareSrc(r));break;case"srcdoc":this._ready=!1,r!==null?this.iframe.srcdoc=this._prepareSrcdoc(r):this.iframe.removeAttribute("srcdoc");break;case"width":this._compositionWidth=parseInt(r||"1920",10),this._updateScale();break;case"height":this._compositionHeight=parseInt(r||"1080",10),this._updateScale();break;case"controls":r!==null?this._setupControls():((i=this.controlsApi)==null||i.destroy(),this.controlsApi=null);break;case"poster":this._setupPoster();break;case"playback-rate":{const n=parseFloat(r||"1");for(const o of this._parentMedia)o.el.playbackRate=n;this._sendControl("set-playback-rate",{playbackRate:n}),(s=this.controlsApi)==null||s.updateSpeed(n),this.dispatchEvent(new Event("ratechange"));break}case"muted":for(const n of this._parentMedia)n.el.muted=r!==null;this._sendControl("set-muted",{muted:r!==null});break;case"audio-src":r&&this._setupParentAudioFromUrl(r);break;case S:case L:this._reloadShaderOptions();break}}get iframeElement(){return this.iframe}play(){var e;this._hidePoster(),this._duration>0&&this._currentTime>=this._duration&&this.seek(0),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,r;if(!this._trySyncSeek(e)){const i=Math.round(e*T);this._sendControl("seek",{frame:i})}if(this._currentTime=e,this._audioOwner==="parent")for(const i of this._parentMedia){const s=e-i.start;s>=0&&s<i.duration&&(i.el.currentTime=s)}this._paused=!0,(t=this.controlsApi)==null||t.updatePlaying(!1),(r=this.controlsApi)==null||r.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 shaderCaptureScale(){return Number(H(this.getAttribute(S))??"1")}set shaderCaptureScale(e){this.setAttribute(S,String(e))}get shaderLoading(){return $(this.getAttribute(L))}set shaderLoading(e){e==="composition"?this.removeAttribute(L):this.setAttribute(L,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 r;try{(r=this.iframe.contentWindow)==null||r.postMessage({source:"hf-parent",type:"control",action:e,...t},"*")}catch{}}_shaderCaptureScaleParam(){return H(this.getAttribute(S))}_shaderLoadingMode(){return $(this.getAttribute(L))}_prepareSrc(e){return Z(e,this._shaderCaptureScaleParam(),this._shaderLoadingMode())}_prepareSrcdoc(e){return K(e,this._shaderCaptureScaleParam(),this._shaderLoadingMode())}_reloadShaderOptions(){if(this._shaderLoadingMode()!=="player"&&this._resetShaderLoader(),this.hasAttribute("srcdoc")){this.iframe.srcdoc=this._prepareSrcdoc(this.getAttribute("srcdoc")||"");return}this.hasAttribute("src")&&(this.iframe.src=this._prepareSrc(this.getAttribute("src")||""))}_createShaderLoader(){const e=document.createElement("div");e.className="hfp-shader-loader",e.setAttribute("role","status"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-label","Preparing scene transitions"),e.setAttribute("data-hyperframes-ignore",""),e.draggable=!1;const t=_=>{_.preventDefault(),_.stopPropagation()};for(const _ of["selectstart","dragstart","pointerdown","mousedown","click","dblclick","contextmenu","touchstart"])e.addEventListener(_,t,{capture:!0});const r=document.createElement("div");r.className="hfp-shader-loader-panel",r.draggable=!1;const i=document.createElement("div");i.className="hfp-shader-loader-mark",i.draggable=!1,i.innerHTML=['<svg width="78" height="78" viewBox="0 0 100 100" fill="none" aria-hidden="true" draggable="false">','<path d="M10.1851 57.8021L33.1145 73.8313C36.2202 75.9978 41.5173 73.5433 42.4816 69.4984L51.7611 30.4271C52.7253 26.3822 48.5802 23.9277 44.4602 26.0942L13.917 42.1235C6.96677 45.7676 4.97564 54.1579 10.1851 57.8021Z" fill="url(#hfp-shader-loader-grad-left)"/>','<path d="M87.5129 57.5141L56.9696 73.5433C52.8371 75.7098 48.7046 73.2553 49.6688 69.2104L58.9483 30.1391C59.9125 26.0942 65.2097 23.6397 68.3154 25.8062L91.2447 41.8354C96.4668 45.4796 94.4631 53.8699 87.5129 57.5141Z" fill="url(#hfp-shader-loader-grad-right)"/>',"<defs>",'<linearGradient id="hfp-shader-loader-grad-left" x1="48.5676" y1="25" x2="44.7804" y2="71.9384" gradientUnits="userSpaceOnUse">','<stop stop-color="#06E3FA"/>','<stop offset="1" stop-color="#4FDB5E"/>',"</linearGradient>",'<linearGradient id="hfp-shader-loader-grad-right" x1="54.8282" y1="73.8392" x2="72.0989" y2="32.8932" gradientUnits="userSpaceOnUse">','<stop stop-color="#06E3FA"/>','<stop offset="1" stop-color="#4FDB5E"/>',"</linearGradient>","</defs>","</svg>"].join("");const s=document.createElement("div");s.className="hfp-shader-loader-title";const n=document.createElement("span");n.className="hfp-shader-loader-title-text",n.textContent=A[0],s.appendChild(n);const o=document.createElement("div");o.className="hfp-shader-loader-detail",o.textContent="Rendering animated scene samples for shader transitions.";const f=document.createElement("div");f.className="hfp-shader-loader-track",f.setAttribute("aria-hidden","true");const u=document.createElement("div");u.className="hfp-shader-loader-fill",f.appendChild(u);const h=document.createElement("div");h.className="hfp-shader-loader-progress";const m=_=>{const y=document.createElement("div");y.className="hfp-shader-loader-row";const v=document.createElement("span");v.className="hfp-shader-loader-label",v.textContent=_;const w=document.createElement("span");return w.className="hfp-shader-loader-value",y.appendChild(v),y.appendChild(w),h.appendChild(y),{row:y,label:v,value:w}},g=m("transition"),b=m("transition frame");return r.appendChild(i),r.appendChild(s),r.appendChild(o),r.appendChild(f),r.appendChild(h),e.appendChild(r),{root:e,fill:u,title:n,detail:o,transitionValue:g.value,frameLabel:b.label,frameValue:b.value,frameRow:b.row}}_showShaderLoader(){this.shaderLoaderHideTimeout&&(clearTimeout(this.shaderLoaderHideTimeout),this.shaderLoaderHideTimeout=null),this.shaderLoaderEl.classList.remove("hfp-hiding"),this.shaderLoaderEl.classList.add("hfp-visible")}_hideShaderLoader(){if(this.shaderLoaderEl.classList.contains("hfp-hiding")){this.shaderLoaderHideTimeout||this._scheduleShaderLoaderHideCleanup();return}this.shaderLoaderEl.classList.contains("hfp-visible")&&(this.shaderLoaderEl.classList.add("hfp-hiding"),this.shaderLoaderEl.classList.remove("hfp-visible"),this._scheduleShaderLoaderHideCleanup())}_scheduleShaderLoaderHideCleanup(){this.shaderLoaderHideTimeout&&clearTimeout(this.shaderLoaderHideTimeout),this.shaderLoaderHideTimeout=setTimeout(()=>{this.shaderLoaderEl.classList.remove("hfp-hiding"),this.shaderLoaderHideTimeout=null},420)}_resetShaderLoader(){this.shaderLoaderHideTimeout&&(clearTimeout(this.shaderLoaderHideTimeout),this.shaderLoaderHideTimeout=null),this.shaderLoaderEl.classList.remove("hfp-visible","hfp-hiding"),this.shaderLoaderFillEl.style.transform="scaleX(0)",this.shaderLoaderTransitionValueEl.textContent="",this.shaderLoaderFrameValueEl.textContent="",this.shaderLoaderFrameRowEl.style.visibility="hidden"}_updateShaderLoader(e){if(this._shaderLoadingMode()!=="player"){this._resetShaderLoader();return}if(e.ready||!e.loading){this._hideShaderLoader();return}const t=typeof e.progress=="number"&&Number.isFinite(e.progress)?e.progress:0,r=typeof e.total=="number"&&Number.isFinite(e.total)?e.total:0,i=r>0?Math.min(1,Math.max(0,t/r)):0,s=Math.min(A.length-1,Math.floor(i*A.length));this.shaderLoaderTitleEl.textContent=A[s]||"Preparing scene transitions",this.shaderLoaderDetailEl.textContent=e.phase==="cached"?"Loading cached transition frames before playback.":e.phase==="finalizing"?"Uploading transition textures for smooth playback.":"Rendering animated scene samples for shader transitions.",this.shaderLoaderFillEl.style.transform=`scaleX(${i})`,this.shaderLoaderTransitionValueEl.textContent=e.currentTransition!==void 0&&e.transitionTotal!==void 0?`${e.currentTransition}/${e.transitionTotal}`:r>0?`${t}/${r}`:"";const n=e.transitionFrame!==void 0&&e.transitionFrames!==void 0?`${e.transitionFrame}/${e.transitionFrames}`:"";this.shaderLoaderFrameLabelEl.textContent=e.phase==="cached"?"cached transition frames":e.phase==="finalizing"?"finalizing transition frames":"rendering transition frames",this.shaderLoaderFrameValueEl.textContent=n,this.shaderLoaderFrameRowEl.style.visibility=n?"visible":"hidden",this.shaderLoaderEl.setAttribute("aria-valuenow",String(Math.round(i*100))),this._showShaderLoader()}_trySyncSeek(e){try{const t=this.iframe.contentWindow,r=t==null?void 0:t.__player,i=r==null?void 0:r.seek;return typeof i!="function"?!1:(i.call(r,e),!0)}catch{return!1}}_isControlsClick(e){return e.composedPath().some(t=>t instanceof HTMLElement&&t.classList.contains("hfp-controls"))}_onMessage(e){var r,i,s,n;if(e.source!==this.iframe.contentWindow)return;const t=e.data;if(!(!t||t.source!=="hf-preview")){if(t.type==="shader-transition-state"){const o=t.state&&typeof t.state=="object"?t.state:{};this._updateShaderLoader(o),this.dispatchEvent(new CustomEvent("shadertransitionstate",{detail:{compositionId:t.compositionId,state:o}}));return}if(t.type==="state"){this._currentTime=(t.frame??0)/T;const o=!this._paused,f=!t.isPlaying,u=this._duration>0&&this._currentTime>=this._duration&&(o||t.isPlaying);if(u&&this.loop){this._audioOwner==="parent"&&this._pauseParentMedia(),this._paused=f,this.seek(0),this.play();return}this._paused=f,this._audioOwner==="parent"&&(o&&this._paused?this._pauseParentMedia():!o&&!this._paused&&this._playParentMedia(),this._mirrorParentMediaTime(this._currentTime));const h=performance.now();(h-this._lastUpdateMs>100||this._paused!==o)&&(this._lastUpdateMs=h,(r=this.controlsApi)==null||r.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,(s=this.controlsApi)==null||s.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/T,(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,this._resetShaderLoader();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 r,i;e++;try{const s=this.iframe.contentWindow;if(!s)return;const n=!!(s.__hf||s.__player),o=!!(s.__timelines&&Object.keys(s.__timelines).length>0),f=!!((r=this.iframe.contentDocument)!=null&&r.querySelector("[data-composition-src]"));if(G({hasRuntime:n,hasTimelines:o,hasNestedCompositions:f,runtimeInjected:this._runtimeInjected,attempts:e})){this._injectRuntime();return}if(this._runtimeInjected&&!n)return;const h=(()=>{var m,g;if(s.__player&&typeof s.__player.getDuration=="function")return s.__player;if(s.__timelines){const b=Object.keys(s.__timelines);if(b.length>0){const _=(g=(m=this.iframe.contentDocument)==null?void 0:m.querySelector("[data-composition-id]"))==null?void 0:g.getAttribute("data-composition-id"),y=_&&_ in s.__timelines?_:b[b.length-1],v=s.__timelines[y];return{getDuration:()=>v.duration()}}}return null})();if(h&&h.getDuration()>0){clearInterval(this._probeInterval),this._duration=h.getDuration(),this._ready=!0,(i=this.controlsApi)==null||i.updateTime(0,this._duration),this.dispatchEvent(new CustomEvent("ready",{detail:{duration:this._duration}}));const m=this.iframe.contentDocument,g=m==null?void 0:m.querySelector("[data-composition-id]");if(g){const b=parseInt(g.getAttribute("data-width")||"0",10),_=parseInt(g.getAttribute("data-height")||"0",10);b>0&&_>0&&(this._compositionWidth=b,this._compositionHeight=_,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=Q,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"),r=t?t.split(",").map(Number).filter(i=>!isNaN(i)&&i>0):void 0;this.controlsApi=W(this.shadow,e,{speedPresets:r})}_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 r=(t==null?void 0:t.force)===!0,i=x.MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES,s=x.MIRROR_DRIFT_THRESHOLD_SECONDS;for(const n of this._parentMedia){const o=e-n.start;if(o<0||o>=n.duration){n.driftSamples=0;continue}Math.abs(n.el.currentTime-o)>s?(n.driftSamples+=1,(r||n.driftSamples>=i)&&(n.el.currentTime=o,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,r,i){if(this._parentMedia.some(o=>o.el.src===e))return null;const s=t==="video"?document.createElement("video"):new Audio;s.preload="auto",s.src=e,s.load(),s.muted=this.muted,this.playbackRate!==1&&(s.playbackRate=this.playbackRate);const n={el:s,start:r,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 r of t)this._adoptIframeMedia(r);this._observeDynamicMedia(e)}catch{}}_adoptIframeMedia(e){var f;const t=e.getAttribute("src")||((f=e.querySelector("source"))==null?void 0:f.getAttribute("src"));if(!t)return;const r=new URL(t,e.ownerDocument.baseURI).href,i=parseFloat(e.getAttribute("data-start")||"0"),s=parseFloat(e.getAttribute("data-duration")||"Infinity"),n=e.tagName==="VIDEO"?"video":"audio",o=this._createParentMedia(r,n,i,s);o&&this._audioOwner==="parent"&&(this._mirrorParentMediaTime(this._currentTime,{force:!0}),!this._paused&&o.el.src&&o.el.play().catch(u=>this._reportPlaybackError(u)))}_observeDynamicMedia(e){if(this._teardownMediaObserver(),typeof MutationObserver>"u"||!e.body)return;const t=new MutationObserver(i=>{var s,n,o,f;for(const u of i){for(const h of u.addedNodes){if(!(h instanceof Element))continue;const m=[];(s=h.matches)!=null&&s.call(h,"audio[data-start], video[data-start]")&&m.push(h);const g=(n=h.querySelectorAll)==null?void 0:n.call(h,"audio[data-start], video[data-start]");if(g)for(const b of g)m.push(b);for(const b of m)this._adoptIframeMedia(b)}for(const h of u.removedNodes){if(!(h instanceof Element))continue;const m=[];(o=h.matches)!=null&&o.call(h,"audio[data-start], video[data-start]")&&m.push(h);const g=(f=h.querySelectorAll)==null?void 0:f.call(h,"audio[data-start], video[data-start]");if(g)for(const b of g)m.push(b);for(const b of m)this._detachIframeMedia(b)}}}),r=e.querySelectorAll("[data-composition-id]");if(r.length>0)for(const i of r)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 r=new URL(t,e.ownerDocument.baseURI).href,i=this._parentMedia.findIndex(o=>o.el.src===r);if(i===-1)return;const s=this._parentMedia[i];s.el.pause(),s.el.src="",this._parentMedia.splice(i,1)}_hidePoster(){var e;(e=this.posterEl)==null||e.remove(),this.posterEl=null}};c(x,"MIRROR_DRIFT_THRESHOLD_SECONDS",.05),c(x,"MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES",2);let M=x;customElements.get("hyperframes-player")||customElements.define("hyperframes-player",M);export{M as HyperframesPlayer,B as SPEED_PRESETS,C as formatSpeed,D as formatTime};