@vanillaskyai/video 0.4.0 → 0.5.0
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/CHANGELOG.md +89 -0
- package/PUBLIC-API.md +12 -5
- package/README.md +14 -17
- package/dist/{builtin-server-BRTZN4Q7.js → builtin-server-YHEZ2JRF.js} +2 -2
- package/dist/check-runtime.js +1 -1
- package/dist/{chunk-BKF3A357.js → chunk-34O5BY6X.js} +1 -1
- package/dist/{chunk-LHFADNWJ.js → chunk-3ROGEU5A.js} +179 -342
- package/dist/{chunk-XAMUOSX7.js → chunk-3RV4YKB3.js} +0 -1
- package/dist/{chunk-GHJEAP4O.js → chunk-66MNRUCR.js} +1 -1
- package/dist/{chunk-EE6PK6XC.js → chunk-AOWSXU2K.js} +1 -1
- package/dist/{chunk-GR4I3MN2.js → chunk-EFL34TXF.js} +3 -3
- package/dist/{chunk-I5YWVQ5R.js → chunk-FVTMYS6U.js} +1 -1
- package/dist/{chunk-STIFILQG.js → chunk-FZHMQFG3.js} +46 -38
- package/dist/{chunk-JW47XCRL.js → chunk-G66Z5CWR.js} +30 -72
- package/dist/{chunk-RBQN3VSY.js → chunk-HVCPEAQF.js} +83 -1265
- package/dist/{chunk-N3KUA7VP.js → chunk-K6YVQODK.js} +4 -1
- package/dist/{chunk-WHPZED7X.js → chunk-PNS52FL4.js} +2 -5
- package/dist/{chunk-S5ZU5WUU.js → chunk-ZQQQAAKP.js} +2 -2
- package/dist/cli.js +20 -14
- package/dist/compose-video-QJHKTOQ7.js +13 -0
- package/dist/{events-D_1F6gw5.d.ts → events-BBP30j3c.d.ts} +3 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{kit-DLeCr3d0.d.ts → kit-DEG3fcaL.d.ts} +1 -1
- package/dist/react.d.ts +7 -6
- package/dist/react.js +1000 -349
- package/dist/server.d.ts +3 -3
- package/dist/server.js +18 -79
- package/dist/{system-prompt-NMYHLAMJ.js → system-prompt-4I6Z5HK3.js} +1 -1
- package/dist/template-catalog.js +1 -1
- package/dist/templates.d.ts +3 -3
- package/dist/templates.js +87 -4
- package/dist/test.d.ts +3 -27
- package/dist/test.js +3 -3
- package/dist/{text-stream-UPGUD2TD.js → text-stream-J6EDDJP4.js} +3 -3
- package/dist/{types-Cg0Onj7l.d.ts → types-BV9IqExh.d.ts} +1 -16
- package/docs/agent-integration.md +4 -3
- package/docs/concepts.md +4 -4
- package/docs/custom-templates.md +2 -2
- package/docs/getting-started.md +13 -18
- package/docs/integrate-nextjs.md +12 -8
- package/docs/live-channels.md +5 -2
- package/docs/media-and-audio.md +7 -0
- package/docs/persistence.md +1 -1
- package/docs/prompt-and-input.md +2 -2
- package/docs/reference/protocol.md +9 -12
- package/docs/reference/provider-adapters.md +5 -5
- package/docs/testing.md +1 -1
- package/examples/nextjs-quickstart/.env.example +2 -2
- package/examples/nextjs-quickstart/README.md +4 -1
- package/examples/nextjs-quickstart/package.json +4 -4
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +4 -2
- package/package.json +8 -8
- package/dist/chunk-CR7FE7BW.js +0 -109
- package/dist/compose-video-6UQ33DOO.js +0 -13
package/dist/react.js
CHANGED
|
@@ -2,34 +2,29 @@ import {
|
|
|
2
2
|
VIDEO_STREAM_CONTENT_TYPE,
|
|
3
3
|
VIDEO_STREAM_HEADER,
|
|
4
4
|
decodeVideoSse
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZQQQAAKP.js";
|
|
6
6
|
import {
|
|
7
7
|
applyVideoEvent,
|
|
8
|
+
createReplayableStream,
|
|
8
9
|
createVideoState
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FZHMQFG3.js";
|
|
10
11
|
import {
|
|
11
12
|
safePublicDiagnostic
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-G66Z5CWR.js";
|
|
13
14
|
import {
|
|
14
15
|
parseVideo
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-66MNRUCR.js";
|
|
16
17
|
import {
|
|
17
18
|
VIDEO_PROTOCOL_VERSION,
|
|
18
19
|
createVideoRequest
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-AOWSXU2K.js";
|
|
20
21
|
import {
|
|
21
|
-
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import {
|
|
24
|
-
BUILTIN_TEMPLATE_CAPABILITIES,
|
|
25
|
-
BUILTIN_TEMPLATE_CATALOG,
|
|
26
|
-
getBuiltinTemplateMetadata
|
|
27
|
-
} from "./chunk-XAMUOSX7.js";
|
|
28
|
-
import "./chunk-2E6T633S.js";
|
|
22
|
+
assertTemplateTransitionMetadata
|
|
23
|
+
} from "./chunk-2E6T633S.js";
|
|
29
24
|
import {
|
|
30
25
|
VideoFrame,
|
|
31
26
|
getDimensions
|
|
32
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-K6YVQODK.js";
|
|
33
28
|
import {
|
|
34
29
|
fontStack
|
|
35
30
|
} from "./chunk-XWWLKRNU.js";
|
|
@@ -42,13 +37,41 @@ import "./chunk-73NTSFFI.js";
|
|
|
42
37
|
|
|
43
38
|
// src/player/video-player.tsx
|
|
44
39
|
import {
|
|
45
|
-
useEffect,
|
|
40
|
+
useEffect as useEffect2,
|
|
46
41
|
useMemo,
|
|
47
42
|
useRef,
|
|
48
43
|
useState
|
|
49
44
|
} from "react";
|
|
50
45
|
|
|
51
|
-
// src/visual-system/catalog/
|
|
46
|
+
// src/visual-system/catalog/player-kit.ts
|
|
47
|
+
function createPlayerTemplateRegistry(templates) {
|
|
48
|
+
const byId = /* @__PURE__ */ new Map();
|
|
49
|
+
for (const template of templates) {
|
|
50
|
+
assertTemplateTransitionMetadata(template);
|
|
51
|
+
if (!template.defaults && !template.schema) {
|
|
52
|
+
throw new Error(`${template.id} requires defaults or a schema`);
|
|
53
|
+
}
|
|
54
|
+
if (byId.has(template.id)) throw new Error(`Duplicate template id: ${template.id}`);
|
|
55
|
+
byId.set(template.id, template);
|
|
56
|
+
}
|
|
57
|
+
const frozenTemplates = Object.freeze([...templates]);
|
|
58
|
+
return Object.freeze({
|
|
59
|
+
templates: frozenTemplates,
|
|
60
|
+
capabilities: { templates: frozenTemplates.map(({ id }) => id) },
|
|
61
|
+
getTemplate: (id) => byId.get(id)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function overlayPlayerTemplateRegistry(defaults, customer) {
|
|
65
|
+
if (!customer) return defaults;
|
|
66
|
+
const customerTemplates = new Map(customer.templates.map((template) => [template.id, template]));
|
|
67
|
+
const defaultIds = new Set(defaults.templates.map(({ id }) => id));
|
|
68
|
+
return createPlayerTemplateRegistry([
|
|
69
|
+
...defaults.templates.map((template) => customerTemplates.get(template.id) ?? template),
|
|
70
|
+
...customer.templates.filter(({ id }) => !defaultIds.has(id))
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/visual-system/catalog/builtin-player.ts
|
|
52
75
|
import { createElement } from "react";
|
|
53
76
|
|
|
54
77
|
// src/visual-system/catalog/builtin-loaders.generated.ts
|
|
@@ -83,7 +106,551 @@ var GENERATED_BUILTIN_TEMPLATE_LOADERS = {
|
|
|
83
106
|
"ctaMedia": () => import("./cta-media-NY4ASBGG.js").then((module) => ({ default: module.CtaMediaTemplate }))
|
|
84
107
|
};
|
|
85
108
|
|
|
86
|
-
// src/visual-system/catalog/builtin.ts
|
|
109
|
+
// src/visual-system/catalog/builtin-player.generated.ts
|
|
110
|
+
var GENERATED_BUILTIN_PLAYER_TEMPLATES = [
|
|
111
|
+
{
|
|
112
|
+
"id": "media",
|
|
113
|
+
"defaults": {
|
|
114
|
+
"texts": "Make an impact.",
|
|
115
|
+
"mediaUrl": "",
|
|
116
|
+
"mediaKeyword": "",
|
|
117
|
+
"mediaType": "auto",
|
|
118
|
+
"mediaPoster": "",
|
|
119
|
+
"mediaPosition": "center",
|
|
120
|
+
"mediaTreatment": "cinematic",
|
|
121
|
+
"confetti": false
|
|
122
|
+
},
|
|
123
|
+
"usesGlobalTransition": true,
|
|
124
|
+
"transitionTiming": {
|
|
125
|
+
"entryReadyProgress": 0.2,
|
|
126
|
+
"holdProgress": 0.7
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "reaction",
|
|
131
|
+
"defaults": {
|
|
132
|
+
"texts": "Still editing manually?",
|
|
133
|
+
"reactionTag": "manual",
|
|
134
|
+
"mediaUrl": "",
|
|
135
|
+
"mediaType": "video",
|
|
136
|
+
"mediaPoster": ""
|
|
137
|
+
},
|
|
138
|
+
"usesGlobalTransition": true,
|
|
139
|
+
"transitionTiming": {
|
|
140
|
+
"entryReadyProgress": 0.2,
|
|
141
|
+
"holdProgress": 0.7
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "confetti",
|
|
146
|
+
"defaults": {
|
|
147
|
+
"texts": "Celebrate."
|
|
148
|
+
},
|
|
149
|
+
"usesGlobalTransition": true,
|
|
150
|
+
"transitionTiming": {
|
|
151
|
+
"entryReadyProgress": 0.2,
|
|
152
|
+
"holdProgress": 0.7
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "emojiBurst",
|
|
157
|
+
"defaults": {
|
|
158
|
+
"texts": "Let's go!"
|
|
159
|
+
},
|
|
160
|
+
"usesGlobalTransition": true,
|
|
161
|
+
"transitionTiming": {
|
|
162
|
+
"entryReadyProgress": 0.2,
|
|
163
|
+
"holdProgress": 0.7
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "bigNumber",
|
|
168
|
+
"defaults": {
|
|
169
|
+
"texts": "Our biggest milestone yet.",
|
|
170
|
+
"value": 1e3,
|
|
171
|
+
"confetti": false,
|
|
172
|
+
"label": "Total users",
|
|
173
|
+
"prefix": "",
|
|
174
|
+
"unit": "",
|
|
175
|
+
"mediaUrl": "",
|
|
176
|
+
"mediaKeyword": "",
|
|
177
|
+
"mediaType": "auto",
|
|
178
|
+
"mediaPoster": "",
|
|
179
|
+
"mediaPosition": "center",
|
|
180
|
+
"mediaTreatment": "cinematic"
|
|
181
|
+
},
|
|
182
|
+
"usesGlobalTransition": true,
|
|
183
|
+
"transitionTiming": {
|
|
184
|
+
"entryReadyProgress": 0.2,
|
|
185
|
+
"holdProgress": 0.7
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "barChart",
|
|
190
|
+
"defaults": {
|
|
191
|
+
"texts": "Revenue up 300%.",
|
|
192
|
+
"bars": [
|
|
193
|
+
{
|
|
194
|
+
"label": "Q1",
|
|
195
|
+
"value": 42
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"label": "Q2",
|
|
199
|
+
"value": 58
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"label": "Q3",
|
|
203
|
+
"value": 76
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"label": "Q4",
|
|
207
|
+
"value": 91
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"mediaUrl": "",
|
|
211
|
+
"mediaKeyword": "",
|
|
212
|
+
"mediaType": "auto",
|
|
213
|
+
"mediaPoster": "",
|
|
214
|
+
"mediaPosition": "center",
|
|
215
|
+
"mediaTreatment": "cinematic"
|
|
216
|
+
},
|
|
217
|
+
"usesGlobalTransition": true,
|
|
218
|
+
"transitionTiming": {
|
|
219
|
+
"entryReadyProgress": 0.2,
|
|
220
|
+
"holdProgress": 0.7
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"id": "progressRing",
|
|
225
|
+
"defaults": {
|
|
226
|
+
"texts": "Almost there.",
|
|
227
|
+
"value": 75,
|
|
228
|
+
"label": "Complete",
|
|
229
|
+
"unit": "%",
|
|
230
|
+
"mediaUrl": "",
|
|
231
|
+
"mediaKeyword": "",
|
|
232
|
+
"mediaType": "auto",
|
|
233
|
+
"mediaPoster": "",
|
|
234
|
+
"mediaPosition": "center",
|
|
235
|
+
"mediaTreatment": "cinematic"
|
|
236
|
+
},
|
|
237
|
+
"usesGlobalTransition": true,
|
|
238
|
+
"transitionTiming": {
|
|
239
|
+
"entryReadyProgress": 0.2,
|
|
240
|
+
"holdProgress": 0.7
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"id": "phoneMockup",
|
|
245
|
+
"defaults": {
|
|
246
|
+
"texts": "See it in action.",
|
|
247
|
+
"screenMediaUrl": "",
|
|
248
|
+
"screen1Url": "",
|
|
249
|
+
"screen2Url": "",
|
|
250
|
+
"screenFit": "cover",
|
|
251
|
+
"screenFocusX": 50,
|
|
252
|
+
"screenFocusY": 50,
|
|
253
|
+
"screenMotion": "pushIn",
|
|
254
|
+
"screenCalloutText": "",
|
|
255
|
+
"screenCalloutX": 70,
|
|
256
|
+
"screenCalloutY": 35,
|
|
257
|
+
"mediaUrl": "",
|
|
258
|
+
"mediaKeyword": "",
|
|
259
|
+
"mediaType": "auto",
|
|
260
|
+
"mediaPoster": "",
|
|
261
|
+
"mediaPosition": "center",
|
|
262
|
+
"mediaTreatment": "cinematic"
|
|
263
|
+
},
|
|
264
|
+
"usesGlobalTransition": true,
|
|
265
|
+
"transitionTiming": {
|
|
266
|
+
"entryReadyProgress": 0.2,
|
|
267
|
+
"holdProgress": 0.7
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"id": "webMockup",
|
|
272
|
+
"defaults": {
|
|
273
|
+
"frame": "browser",
|
|
274
|
+
"texts": "See it in action.",
|
|
275
|
+
"screenMediaUrl": "",
|
|
276
|
+
"screen1Url": "",
|
|
277
|
+
"screen2Url": "",
|
|
278
|
+
"screenFit": "cover",
|
|
279
|
+
"screenFocusX": 50,
|
|
280
|
+
"screenFocusY": 50,
|
|
281
|
+
"screenMotion": "pushIn",
|
|
282
|
+
"screenCalloutText": "",
|
|
283
|
+
"screenCalloutX": 70,
|
|
284
|
+
"screenCalloutY": 35,
|
|
285
|
+
"addressBarUrl": "",
|
|
286
|
+
"mediaUrl": "",
|
|
287
|
+
"mediaKeyword": "",
|
|
288
|
+
"mediaType": "auto",
|
|
289
|
+
"mediaPoster": "",
|
|
290
|
+
"mediaPosition": "center",
|
|
291
|
+
"mediaTreatment": "cinematic"
|
|
292
|
+
},
|
|
293
|
+
"usesGlobalTransition": true,
|
|
294
|
+
"transitionTiming": {
|
|
295
|
+
"entryReadyProgress": 0.2,
|
|
296
|
+
"holdProgress": 0.7
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"id": "codeEditor",
|
|
301
|
+
"defaults": {
|
|
302
|
+
"texts": "Simple as this.",
|
|
303
|
+
"code": 'import { getVideoDuration } from "@vanillaskyai/video";\nimport type { Video } from "@vanillaskyai/video";\n\nexport function duration(video: Video) {\n return getVideoDuration(video);\n}',
|
|
304
|
+
"filename": "app.ts",
|
|
305
|
+
"mediaUrl": "",
|
|
306
|
+
"mediaKeyword": "",
|
|
307
|
+
"mediaType": "auto",
|
|
308
|
+
"mediaPoster": "",
|
|
309
|
+
"mediaPosition": "center",
|
|
310
|
+
"mediaTreatment": "cinematic"
|
|
311
|
+
},
|
|
312
|
+
"usesGlobalTransition": true,
|
|
313
|
+
"transitionTiming": {
|
|
314
|
+
"entryReadyProgress": 0.2,
|
|
315
|
+
"holdProgress": 0.7
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"id": "terminal",
|
|
320
|
+
"defaults": {
|
|
321
|
+
"texts": "Ship it.",
|
|
322
|
+
"command": "npx vanillasky check",
|
|
323
|
+
"output": [],
|
|
324
|
+
"promptPrefix": "$",
|
|
325
|
+
"mediaUrl": "",
|
|
326
|
+
"mediaKeyword": "",
|
|
327
|
+
"mediaType": "auto",
|
|
328
|
+
"mediaPoster": "",
|
|
329
|
+
"mediaPosition": "center",
|
|
330
|
+
"mediaTreatment": "cinematic"
|
|
331
|
+
},
|
|
332
|
+
"usesGlobalTransition": true,
|
|
333
|
+
"transitionTiming": {
|
|
334
|
+
"entryReadyProgress": 0.2,
|
|
335
|
+
"holdProgress": 0.7
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"id": "tweet",
|
|
340
|
+
"defaults": {
|
|
341
|
+
"authorName": "Your brand",
|
|
342
|
+
"authorHandle": "",
|
|
343
|
+
"authorVerified": false,
|
|
344
|
+
"message": "Just shipped the new feature \u{1F680}",
|
|
345
|
+
"likes": 0,
|
|
346
|
+
"replies": 0,
|
|
347
|
+
"mediaUrl": "",
|
|
348
|
+
"mediaKeyword": "",
|
|
349
|
+
"mediaType": "auto",
|
|
350
|
+
"mediaPoster": "",
|
|
351
|
+
"mediaPosition": "center",
|
|
352
|
+
"mediaTreatment": "cinematic"
|
|
353
|
+
},
|
|
354
|
+
"usesGlobalTransition": true,
|
|
355
|
+
"transitionTiming": {
|
|
356
|
+
"entryReadyProgress": 0.2,
|
|
357
|
+
"holdProgress": 0.7
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"id": "notification",
|
|
362
|
+
"defaults": {
|
|
363
|
+
"appName": "Reminder",
|
|
364
|
+
"appIcon": "\u{1F514}",
|
|
365
|
+
"message": "Believe in your dreams, they will lead you.",
|
|
366
|
+
"mediaUrl": "",
|
|
367
|
+
"mediaKeyword": "",
|
|
368
|
+
"mediaType": "auto",
|
|
369
|
+
"mediaPoster": "",
|
|
370
|
+
"mediaPosition": "center",
|
|
371
|
+
"mediaTreatment": "cinematic"
|
|
372
|
+
},
|
|
373
|
+
"usesGlobalTransition": true,
|
|
374
|
+
"transitionTiming": {
|
|
375
|
+
"entryReadyProgress": 0.35,
|
|
376
|
+
"holdProgress": 0.7
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"id": "chatMessenger",
|
|
381
|
+
"defaults": {
|
|
382
|
+
"msg1": "Have you tried it yet?",
|
|
383
|
+
"msg2": "Yes \u2014 it saves me hours|out",
|
|
384
|
+
"msg3": "Okay, send me the link",
|
|
385
|
+
"msg4": "",
|
|
386
|
+
"dateChip1": "",
|
|
387
|
+
"dateChip2": "",
|
|
388
|
+
"msg5": "",
|
|
389
|
+
"theme": "messenger"
|
|
390
|
+
},
|
|
391
|
+
"usesGlobalTransition": false
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"id": "chatWhatsapp",
|
|
395
|
+
"defaults": {
|
|
396
|
+
"msg1": "Have you tried it yet?",
|
|
397
|
+
"msg2": "Yes \u2014 it saves me hours|out",
|
|
398
|
+
"msg3": "Okay, send me the link",
|
|
399
|
+
"msg4": "",
|
|
400
|
+
"dateChip1": "",
|
|
401
|
+
"dateChip2": "",
|
|
402
|
+
"msg5": "",
|
|
403
|
+
"theme": "whatsapp"
|
|
404
|
+
},
|
|
405
|
+
"usesGlobalTransition": false
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"id": "milestone",
|
|
409
|
+
"defaults": {
|
|
410
|
+
"label": "Followers",
|
|
411
|
+
"targetNumber": 1e4,
|
|
412
|
+
"startNumber": 0,
|
|
413
|
+
"badgeText": "",
|
|
414
|
+
"badgeEmoji": "\u{1F389}",
|
|
415
|
+
"mediaUrl": "",
|
|
416
|
+
"mediaKeyword": "",
|
|
417
|
+
"mediaType": "auto",
|
|
418
|
+
"mediaPoster": "",
|
|
419
|
+
"mediaPosition": "center",
|
|
420
|
+
"mediaTreatment": "cinematic"
|
|
421
|
+
},
|
|
422
|
+
"usesGlobalTransition": false
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"id": "reviewStack",
|
|
426
|
+
"defaults": {
|
|
427
|
+
"review1Title": "Life changing app",
|
|
428
|
+
"review1Body": "",
|
|
429
|
+
"review1Author": "",
|
|
430
|
+
"review2Title": "Best in class",
|
|
431
|
+
"review2Body": "",
|
|
432
|
+
"review2Author": "",
|
|
433
|
+
"review3Title": "Exceeded expectations",
|
|
434
|
+
"review3Body": "",
|
|
435
|
+
"review3Author": "",
|
|
436
|
+
"mediaUrl": "",
|
|
437
|
+
"mediaKeyword": "",
|
|
438
|
+
"mediaType": "auto",
|
|
439
|
+
"mediaPoster": "",
|
|
440
|
+
"mediaPosition": "center",
|
|
441
|
+
"mediaTreatment": "cinematic"
|
|
442
|
+
},
|
|
443
|
+
"usesGlobalTransition": false
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"id": "testimonial",
|
|
447
|
+
"defaults": {
|
|
448
|
+
"quote": "Best decision I made this year. Our conversion rate doubled in the first week.",
|
|
449
|
+
"authorName": "Jessica Torres",
|
|
450
|
+
"authorRole": "",
|
|
451
|
+
"mediaUrl": "",
|
|
452
|
+
"mediaKeyword": "",
|
|
453
|
+
"mediaType": "auto",
|
|
454
|
+
"mediaPoster": "",
|
|
455
|
+
"mediaPosition": "center",
|
|
456
|
+
"mediaTreatment": "cinematic"
|
|
457
|
+
},
|
|
458
|
+
"usesGlobalTransition": false
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
"id": "incomingCall",
|
|
462
|
+
"defaults": {
|
|
463
|
+
"callerName": "Your brand",
|
|
464
|
+
"subtitle": "is calling....",
|
|
465
|
+
"declineLabel": "Decline",
|
|
466
|
+
"acceptLabel": "Accept",
|
|
467
|
+
"mediaUrl": "",
|
|
468
|
+
"mediaKeyword": "",
|
|
469
|
+
"mediaType": "auto",
|
|
470
|
+
"mediaPoster": "",
|
|
471
|
+
"mediaPosition": "center",
|
|
472
|
+
"mediaTreatment": "cinematic"
|
|
473
|
+
},
|
|
474
|
+
"usesGlobalTransition": false
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"id": "brandMessage",
|
|
478
|
+
"defaults": {
|
|
479
|
+
"message": "We built this for you.",
|
|
480
|
+
"mediaUrl": "",
|
|
481
|
+
"mediaKeyword": "",
|
|
482
|
+
"mediaType": "auto",
|
|
483
|
+
"mediaPoster": "",
|
|
484
|
+
"mediaPosition": "center",
|
|
485
|
+
"mediaTreatment": "cinematic"
|
|
486
|
+
},
|
|
487
|
+
"usesGlobalTransition": false
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"id": "promptInput",
|
|
491
|
+
"defaults": {
|
|
492
|
+
"promptText": "Make a launch video for our app",
|
|
493
|
+
"mediaUrl": "",
|
|
494
|
+
"mediaKeyword": "",
|
|
495
|
+
"mediaType": "auto",
|
|
496
|
+
"mediaPoster": "",
|
|
497
|
+
"mediaPosition": "center",
|
|
498
|
+
"mediaTreatment": "cinematic"
|
|
499
|
+
},
|
|
500
|
+
"usesGlobalTransition": false
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
"id": "beforeAfter",
|
|
504
|
+
"defaults": {
|
|
505
|
+
"problemLabel": "BEFORE",
|
|
506
|
+
"problemHeadline": "Your calendar today.",
|
|
507
|
+
"solutionLabel": "AFTER",
|
|
508
|
+
"solutionHeadline": "Calmly organized.",
|
|
509
|
+
"problemEmojis": [
|
|
510
|
+
"\u{1F4C5}",
|
|
511
|
+
"\u{1F630}",
|
|
512
|
+
"\u{1F4BC}",
|
|
513
|
+
"\u{1F4CA}",
|
|
514
|
+
"\u23F0",
|
|
515
|
+
"\u{1F4AC}",
|
|
516
|
+
"\u{1F4DE}",
|
|
517
|
+
"\u{1F514}"
|
|
518
|
+
],
|
|
519
|
+
"solutionEmojis": [
|
|
520
|
+
"\u2728",
|
|
521
|
+
"\u{1F4CB}",
|
|
522
|
+
"\u2705",
|
|
523
|
+
"\u{1F3AF}"
|
|
524
|
+
],
|
|
525
|
+
"showEmojis": true
|
|
526
|
+
},
|
|
527
|
+
"usesGlobalTransition": false
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
"id": "tripleStats",
|
|
531
|
+
"defaults": {
|
|
532
|
+
"texts": "By the numbers.",
|
|
533
|
+
"stat1Value": "10K",
|
|
534
|
+
"stat1Label": "Users",
|
|
535
|
+
"stat2Value": "99.9%",
|
|
536
|
+
"stat2Label": "Uptime",
|
|
537
|
+
"stat3Value": "<50ms",
|
|
538
|
+
"stat3Label": "Latency",
|
|
539
|
+
"mediaUrl": "",
|
|
540
|
+
"mediaKeyword": "",
|
|
541
|
+
"mediaType": "auto",
|
|
542
|
+
"mediaPoster": "",
|
|
543
|
+
"mediaPosition": "center",
|
|
544
|
+
"mediaTreatment": "cinematic"
|
|
545
|
+
},
|
|
546
|
+
"usesGlobalTransition": true,
|
|
547
|
+
"transitionTiming": {
|
|
548
|
+
"entryReadyProgress": 0.2,
|
|
549
|
+
"holdProgress": 0.7
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
"id": "problemSolution",
|
|
554
|
+
"defaults": {
|
|
555
|
+
"problemLabel": "THE PROBLEM",
|
|
556
|
+
"problemText": "Teams waste 40% of time in meetings",
|
|
557
|
+
"solutionLabel": "THE SOLUTION",
|
|
558
|
+
"solutionText": "AI that summarizes in seconds",
|
|
559
|
+
"mediaUrl": "",
|
|
560
|
+
"mediaKeyword": "",
|
|
561
|
+
"mediaType": "auto",
|
|
562
|
+
"mediaPoster": "",
|
|
563
|
+
"mediaPosition": "center",
|
|
564
|
+
"mediaTreatment": "cinematic"
|
|
565
|
+
},
|
|
566
|
+
"usesGlobalTransition": false
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
"id": "cardList",
|
|
570
|
+
"defaults": {
|
|
571
|
+
"texts": "What you get.",
|
|
572
|
+
"items": [
|
|
573
|
+
"Automate your savings on energy bills",
|
|
574
|
+
"Cleaner energy without raising your bill",
|
|
575
|
+
"Same account and service guaranteed"
|
|
576
|
+
],
|
|
577
|
+
"itemEmojis": [],
|
|
578
|
+
"mediaUrl": "",
|
|
579
|
+
"mediaKeyword": "",
|
|
580
|
+
"mediaType": "auto",
|
|
581
|
+
"mediaPoster": "",
|
|
582
|
+
"mediaPosition": "center",
|
|
583
|
+
"mediaTreatment": "cinematic"
|
|
584
|
+
},
|
|
585
|
+
"usesGlobalTransition": true,
|
|
586
|
+
"transitionTiming": {
|
|
587
|
+
"entryReadyProgress": 0.2,
|
|
588
|
+
"holdProgress": 0.7
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
"id": "steps",
|
|
593
|
+
"defaults": {
|
|
594
|
+
"texts": "How it works.",
|
|
595
|
+
"steps": [
|
|
596
|
+
"Describe",
|
|
597
|
+
"Preview",
|
|
598
|
+
"Export"
|
|
599
|
+
],
|
|
600
|
+
"stepEmojis": [],
|
|
601
|
+
"mediaUrl": "",
|
|
602
|
+
"mediaKeyword": "",
|
|
603
|
+
"mediaType": "auto",
|
|
604
|
+
"mediaPoster": "",
|
|
605
|
+
"mediaPosition": "center",
|
|
606
|
+
"mediaTreatment": "cinematic"
|
|
607
|
+
},
|
|
608
|
+
"usesGlobalTransition": true,
|
|
609
|
+
"transitionTiming": {
|
|
610
|
+
"entryReadyProgress": 0.2,
|
|
611
|
+
"holdProgress": 0.7
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
"id": "ctaLogo",
|
|
616
|
+
"defaults": {
|
|
617
|
+
"url": "",
|
|
618
|
+
"cta": "",
|
|
619
|
+
"mediaUrl": "",
|
|
620
|
+
"mediaKeyword": "",
|
|
621
|
+
"mediaType": "auto",
|
|
622
|
+
"mediaPoster": "",
|
|
623
|
+
"mediaPosition": "center",
|
|
624
|
+
"mediaTreatment": "cinematic"
|
|
625
|
+
},
|
|
626
|
+
"usesGlobalTransition": true,
|
|
627
|
+
"transitionTiming": {
|
|
628
|
+
"entryReadyProgress": 0.2,
|
|
629
|
+
"holdProgress": 0.7
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"id": "ctaMedia",
|
|
634
|
+
"defaults": {
|
|
635
|
+
"headline": "Make every moment count",
|
|
636
|
+
"url": "",
|
|
637
|
+
"cta": "",
|
|
638
|
+
"mediaUrl": "",
|
|
639
|
+
"mediaKeyword": "",
|
|
640
|
+
"mediaType": "auto",
|
|
641
|
+
"mediaPoster": "",
|
|
642
|
+
"mediaPosition": "center",
|
|
643
|
+
"mediaTreatment": "cinematic"
|
|
644
|
+
},
|
|
645
|
+
"usesGlobalTransition": true,
|
|
646
|
+
"transitionTiming": {
|
|
647
|
+
"entryReadyProgress": 0.2,
|
|
648
|
+
"holdProgress": 0.7
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
// src/visual-system/catalog/builtin-player.ts
|
|
87
654
|
function createPreloadableBuiltinTemplate(loader) {
|
|
88
655
|
let loaded;
|
|
89
656
|
let failure;
|
|
@@ -109,35 +676,27 @@ function createPreloadableBuiltinTemplate(loader) {
|
|
|
109
676
|
};
|
|
110
677
|
return { component, preload };
|
|
111
678
|
}
|
|
112
|
-
function
|
|
679
|
+
function freezeValue(value) {
|
|
113
680
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
114
|
-
for (const nested of Object.values(value))
|
|
681
|
+
for (const nested of Object.values(value)) freezeValue(nested);
|
|
115
682
|
Object.freeze(value);
|
|
116
683
|
}
|
|
117
684
|
return value;
|
|
118
685
|
}
|
|
119
686
|
var preloadableById = new Map(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
687
|
+
GENERATED_BUILTIN_PLAYER_TEMPLATES.map(({ id }) => [
|
|
688
|
+
id,
|
|
689
|
+
createPreloadableBuiltinTemplate(GENERATED_BUILTIN_TEMPLATE_LOADERS[id])
|
|
690
|
+
])
|
|
124
691
|
);
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
...
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
).component
|
|
692
|
+
var builtinPlayerTemplates = Object.freeze(
|
|
693
|
+
GENERATED_BUILTIN_PLAYER_TEMPLATES.map((template) => Object.freeze({
|
|
694
|
+
...template,
|
|
695
|
+
defaults: freezeValue({ ...template.defaults }),
|
|
696
|
+
component: preloadableById.get(template.id).component
|
|
131
697
|
}))
|
|
132
698
|
);
|
|
133
|
-
var
|
|
134
|
-
var BUILTIN_TEMPLATE_KIT = Object.freeze({
|
|
135
|
-
templates: BUILTIN_TEMPLATES,
|
|
136
|
-
capabilities: BUILTIN_TEMPLATE_CAPABILITIES,
|
|
137
|
-
getTemplate: (id) => templateById.get(id),
|
|
138
|
-
getTemplateMetadata: getBuiltinTemplateMetadata,
|
|
139
|
-
listTemplateMetadata: () => BUILTIN_TEMPLATES.map(({ component: _component, ...metadata }) => metadata)
|
|
140
|
-
});
|
|
699
|
+
var BUILTIN_PLAYER_KIT = createPlayerTemplateRegistry(builtinPlayerTemplates);
|
|
141
700
|
function preloadBuiltinTemplate(id) {
|
|
142
701
|
return preloadableById.get(id)?.preload();
|
|
143
702
|
}
|
|
@@ -148,16 +707,26 @@ function warmSceneMedia(variables) {
|
|
|
148
707
|
});
|
|
149
708
|
}
|
|
150
709
|
|
|
151
|
-
// src/player/
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
710
|
+
// src/player/playback-policy.ts
|
|
711
|
+
function resolvePlaybackPolicy({
|
|
712
|
+
playbackMode,
|
|
713
|
+
autoPlay,
|
|
714
|
+
startMuted,
|
|
715
|
+
audioUnlocked,
|
|
716
|
+
reducedMotion,
|
|
717
|
+
hasStream
|
|
718
|
+
}) {
|
|
719
|
+
const resolvedStartMuted = playbackMode === "muted-autoplay" ? true : playbackMode ? false : startMuted;
|
|
720
|
+
const shouldAutoPlay = playbackMode === "manual" ? false : playbackMode === "autoplay-after-interaction" ? audioUnlocked : playbackMode === "muted-autoplay" || playbackMode === "autoplay-with-sound" ? true : autoPlay;
|
|
155
721
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
722
|
+
resolvedStartMuted,
|
|
723
|
+
shouldAutoPlay,
|
|
724
|
+
autoStartGeneration: Boolean(playbackMode && hasStream && shouldAutoPlay && !reducedMotion)
|
|
159
725
|
};
|
|
160
726
|
}
|
|
727
|
+
|
|
728
|
+
// src/player/player-controls.tsx
|
|
729
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
161
730
|
function PlayerIcon({ name, size = 22 }) {
|
|
162
731
|
const shared = {
|
|
163
732
|
width: size,
|
|
@@ -198,6 +767,312 @@ function PlayerIcon({ name, size = 22 }) {
|
|
|
198
767
|
}
|
|
199
768
|
return /* @__PURE__ */ jsx("svg", { ...shared, children: /* @__PURE__ */ jsx("path", { d: "M8 3H3v5M16 3h5v5M8 21H3v-5M16 21h5v-5" }) });
|
|
200
769
|
}
|
|
770
|
+
var startButtonStyle = {
|
|
771
|
+
display: "inline-flex",
|
|
772
|
+
alignItems: "center",
|
|
773
|
+
gap: 10,
|
|
774
|
+
minHeight: 60,
|
|
775
|
+
padding: "14px 22px",
|
|
776
|
+
border: "1px solid rgba(9, 7, 18, 0.08)",
|
|
777
|
+
borderRadius: 999,
|
|
778
|
+
backgroundColor: "#ffffff",
|
|
779
|
+
color: "#090712",
|
|
780
|
+
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.24)",
|
|
781
|
+
font: "700 16px/1 system-ui, sans-serif",
|
|
782
|
+
whiteSpace: "nowrap",
|
|
783
|
+
cursor: "pointer"
|
|
784
|
+
};
|
|
785
|
+
var startButtonPositionStyle = {
|
|
786
|
+
position: "absolute",
|
|
787
|
+
left: "50%",
|
|
788
|
+
top: "50%",
|
|
789
|
+
zIndex: 3,
|
|
790
|
+
transform: "translate(-50%, -50%)"
|
|
791
|
+
};
|
|
792
|
+
function playLabel(config, isMuted) {
|
|
793
|
+
return config?.audio && !isMuted ? "Play with sound" : "Play video";
|
|
794
|
+
}
|
|
795
|
+
function playAriaLabel(config, isMuted) {
|
|
796
|
+
return config?.audio && !isMuted ? "Play video with sound" : "Play video response";
|
|
797
|
+
}
|
|
798
|
+
function GenerationCover({
|
|
799
|
+
visible,
|
|
800
|
+
config,
|
|
801
|
+
isMuted,
|
|
802
|
+
startRequested,
|
|
803
|
+
isPlaying,
|
|
804
|
+
onStart
|
|
805
|
+
}) {
|
|
806
|
+
if (!visible) return null;
|
|
807
|
+
const background = config?.style.brand.background;
|
|
808
|
+
const backgroundCss = background?.type === "solid" ? background.color : `linear-gradient(135deg, ${background?.colors[0] ?? "#8711C1"}, ${background?.colors[1] ?? "#2167E3"})`;
|
|
809
|
+
return /* @__PURE__ */ jsxs(
|
|
810
|
+
"div",
|
|
811
|
+
{
|
|
812
|
+
"data-testid": "video-generation-cover",
|
|
813
|
+
role: "status",
|
|
814
|
+
"aria-live": "polite",
|
|
815
|
+
style: {
|
|
816
|
+
position: "absolute",
|
|
817
|
+
inset: 0,
|
|
818
|
+
display: "grid",
|
|
819
|
+
placeItems: "center",
|
|
820
|
+
padding: "12%",
|
|
821
|
+
textAlign: "center",
|
|
822
|
+
color: config?.style.brand.colors.foreground ?? "#ffffff",
|
|
823
|
+
background: backgroundCss,
|
|
824
|
+
fontFamily: fontStack(config?.style.brand.font)
|
|
825
|
+
},
|
|
826
|
+
children: [
|
|
827
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "absolute", top: "28%", left: "10%", right: "10%" }, children: [
|
|
828
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { fontSize: "clamp(28px, 8vw, 72px)", opacity: 0.9 }, children: "\u2726" }),
|
|
829
|
+
/* @__PURE__ */ jsx("strong", { style: { display: "block", marginTop: 18, fontSize: "clamp(24px, 5vw, 56px)", lineHeight: 1.08 }, children: "Creating your video\u2026" }),
|
|
830
|
+
/* @__PURE__ */ jsx("span", { style: { display: "block", marginTop: 14, fontSize: "clamp(14px, 2vw, 22px)", lineHeight: 1.4, opacity: 0.78 }, children: "Choosing the best scenes for your content." })
|
|
831
|
+
] }),
|
|
832
|
+
!startRequested && !isPlaying ? /* @__PURE__ */ jsxs(
|
|
833
|
+
"button",
|
|
834
|
+
{
|
|
835
|
+
type: "button",
|
|
836
|
+
"aria-label": playAriaLabel(config, isMuted),
|
|
837
|
+
onClick: onStart,
|
|
838
|
+
style: { ...startButtonStyle, ...startButtonPositionStyle },
|
|
839
|
+
children: [
|
|
840
|
+
/* @__PURE__ */ jsx(PlayerIcon, { name: "play", size: 20 }),
|
|
841
|
+
playLabel(config, isMuted)
|
|
842
|
+
]
|
|
843
|
+
}
|
|
844
|
+
) : null
|
|
845
|
+
]
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
function StartPosterButton({
|
|
850
|
+
visible,
|
|
851
|
+
config,
|
|
852
|
+
isMuted,
|
|
853
|
+
onStart
|
|
854
|
+
}) {
|
|
855
|
+
if (!visible) return null;
|
|
856
|
+
return /* @__PURE__ */ jsxs(
|
|
857
|
+
"button",
|
|
858
|
+
{
|
|
859
|
+
type: "button",
|
|
860
|
+
"data-testid": "video-start-button",
|
|
861
|
+
"aria-label": playAriaLabel(config, isMuted),
|
|
862
|
+
onClick: onStart,
|
|
863
|
+
style: { ...startButtonStyle, ...startButtonPositionStyle },
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsx(PlayerIcon, { name: "play", size: 20 }),
|
|
866
|
+
playLabel(config, isMuted)
|
|
867
|
+
]
|
|
868
|
+
}
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
function EndedOverlay({ ended, onReplay }) {
|
|
872
|
+
if (!ended) return null;
|
|
873
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
874
|
+
/* @__PURE__ */ jsx(
|
|
875
|
+
"div",
|
|
876
|
+
{
|
|
877
|
+
"data-testid": "video-ended-scrim",
|
|
878
|
+
"aria-hidden": "true",
|
|
879
|
+
style: {
|
|
880
|
+
position: "absolute",
|
|
881
|
+
inset: 0,
|
|
882
|
+
zIndex: 1,
|
|
883
|
+
pointerEvents: "none",
|
|
884
|
+
background: "rgba(4, 3, 18, 0.52)",
|
|
885
|
+
backdropFilter: "blur(4px)"
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
),
|
|
889
|
+
/* @__PURE__ */ jsxs(
|
|
890
|
+
"button",
|
|
891
|
+
{
|
|
892
|
+
type: "button",
|
|
893
|
+
"data-testid": "video-replay-button",
|
|
894
|
+
"aria-label": "Replay video response",
|
|
895
|
+
onClick: onReplay,
|
|
896
|
+
style: { ...startButtonStyle, ...startButtonPositionStyle, zIndex: 3 },
|
|
897
|
+
children: [
|
|
898
|
+
/* @__PURE__ */ jsx(PlayerIcon, { name: "replay", size: 21 }),
|
|
899
|
+
"Replay"
|
|
900
|
+
]
|
|
901
|
+
}
|
|
902
|
+
)
|
|
903
|
+
] });
|
|
904
|
+
}
|
|
905
|
+
function PlayerControls({
|
|
906
|
+
visible,
|
|
907
|
+
displayWidth,
|
|
908
|
+
ended,
|
|
909
|
+
isPlaying,
|
|
910
|
+
isMuted,
|
|
911
|
+
isFullscreen,
|
|
912
|
+
hasAudio,
|
|
913
|
+
onTogglePlayback,
|
|
914
|
+
onToggleMuted,
|
|
915
|
+
onToggleFullscreen
|
|
916
|
+
}) {
|
|
917
|
+
if (!visible) return null;
|
|
918
|
+
const size = Math.max(40, Math.min(52, Math.round(displayWidth * 0.15)));
|
|
919
|
+
const inset = Math.max(10, Math.min(20, Math.round(displayWidth * 0.056)));
|
|
920
|
+
const buttonStyle = {
|
|
921
|
+
display: "inline-grid",
|
|
922
|
+
placeItems: "center",
|
|
923
|
+
width: size,
|
|
924
|
+
height: size,
|
|
925
|
+
minWidth: size,
|
|
926
|
+
minHeight: size,
|
|
927
|
+
padding: 0,
|
|
928
|
+
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
929
|
+
borderRadius: 999,
|
|
930
|
+
backgroundColor: "rgba(255, 255, 255, 0.16)",
|
|
931
|
+
color: "#ffffff",
|
|
932
|
+
boxShadow: "0 5px 18px rgba(0, 0, 0, 0.16)",
|
|
933
|
+
backdropFilter: "blur(12px)",
|
|
934
|
+
cursor: "pointer"
|
|
935
|
+
};
|
|
936
|
+
return /* @__PURE__ */ jsxs(
|
|
937
|
+
"div",
|
|
938
|
+
{
|
|
939
|
+
"data-testid": "video-controls",
|
|
940
|
+
style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" },
|
|
941
|
+
children: [
|
|
942
|
+
/* @__PURE__ */ jsx(
|
|
943
|
+
"div",
|
|
944
|
+
{
|
|
945
|
+
"data-testid": "video-primary-controls",
|
|
946
|
+
style: { position: "absolute", left: inset, bottom: inset, display: "flex", pointerEvents: "auto" },
|
|
947
|
+
children: /* @__PURE__ */ jsx(
|
|
948
|
+
"button",
|
|
949
|
+
{
|
|
950
|
+
type: "button",
|
|
951
|
+
"aria-label": ended ? "Play video response from beginning" : isPlaying ? "Pause video response" : "Play video response",
|
|
952
|
+
onClick: onTogglePlayback,
|
|
953
|
+
style: buttonStyle,
|
|
954
|
+
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isPlaying ? "pause" : "play" })
|
|
955
|
+
}
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
),
|
|
959
|
+
/* @__PURE__ */ jsxs(
|
|
960
|
+
"div",
|
|
961
|
+
{
|
|
962
|
+
"data-testid": "video-secondary-controls",
|
|
963
|
+
style: { position: "absolute", right: inset, bottom: inset, display: "flex", gap: 10, pointerEvents: "auto" },
|
|
964
|
+
children: [
|
|
965
|
+
hasAudio ? /* @__PURE__ */ jsx(
|
|
966
|
+
"button",
|
|
967
|
+
{
|
|
968
|
+
type: "button",
|
|
969
|
+
"aria-label": isMuted ? "Unmute video response" : "Mute video response",
|
|
970
|
+
"aria-pressed": !isMuted,
|
|
971
|
+
onClick: onToggleMuted,
|
|
972
|
+
style: buttonStyle,
|
|
973
|
+
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isMuted ? "volume-off" : "volume" })
|
|
974
|
+
}
|
|
975
|
+
) : null,
|
|
976
|
+
/* @__PURE__ */ jsx(
|
|
977
|
+
"button",
|
|
978
|
+
{
|
|
979
|
+
type: "button",
|
|
980
|
+
"aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
981
|
+
onClick: onToggleFullscreen,
|
|
982
|
+
style: buttonStyle,
|
|
983
|
+
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isFullscreen ? "exit-fullscreen" : "enter-fullscreen" })
|
|
984
|
+
}
|
|
985
|
+
)
|
|
986
|
+
]
|
|
987
|
+
}
|
|
988
|
+
)
|
|
989
|
+
]
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/player/use-playback-clock.ts
|
|
995
|
+
import { useEffect } from "react";
|
|
996
|
+
function usePlaybackClock({
|
|
997
|
+
isPlaying,
|
|
998
|
+
stateRef,
|
|
999
|
+
timeRef,
|
|
1000
|
+
audioRef,
|
|
1001
|
+
loopRef,
|
|
1002
|
+
sceneIndexRef,
|
|
1003
|
+
callbacksRef,
|
|
1004
|
+
setCurrentTime,
|
|
1005
|
+
setIsPlaying
|
|
1006
|
+
}) {
|
|
1007
|
+
useEffect(() => {
|
|
1008
|
+
if (!isPlaying) return;
|
|
1009
|
+
let frame = 0;
|
|
1010
|
+
let previous = performance.now();
|
|
1011
|
+
const tick = (now) => {
|
|
1012
|
+
const current = stateRef.current;
|
|
1013
|
+
const config = current.config;
|
|
1014
|
+
const delta = Math.max(0, (now - previous) / 1e3);
|
|
1015
|
+
previous = now;
|
|
1016
|
+
const settled = current.status === "complete" || current.status === "error" || current.status === "aborted";
|
|
1017
|
+
const looping = loopRef.current && settled;
|
|
1018
|
+
let wrapped = false;
|
|
1019
|
+
if (config?.scenes.length) {
|
|
1020
|
+
const duration2 = getVideoDuration(config);
|
|
1021
|
+
const raw = timeRef.current + delta;
|
|
1022
|
+
let nextTime;
|
|
1023
|
+
if (looping && duration2 > 0 && raw >= duration2) {
|
|
1024
|
+
nextTime = raw % duration2;
|
|
1025
|
+
wrapped = true;
|
|
1026
|
+
} else {
|
|
1027
|
+
nextTime = Math.min(raw, duration2);
|
|
1028
|
+
}
|
|
1029
|
+
if (nextTime !== timeRef.current) {
|
|
1030
|
+
timeRef.current = nextTime;
|
|
1031
|
+
setCurrentTime(nextTime);
|
|
1032
|
+
}
|
|
1033
|
+
const audio = audioRef.current;
|
|
1034
|
+
if (audio && (current.status === "complete" || current.status === "error")) {
|
|
1035
|
+
if (wrapped) {
|
|
1036
|
+
audio.currentTime = 0;
|
|
1037
|
+
if (audio.paused) void audio.play().catch(Boolean);
|
|
1038
|
+
}
|
|
1039
|
+
const fadeSeconds = Math.max(0, (config.audio?.fadeOutMs ?? 3e3) / 1e3);
|
|
1040
|
+
const remaining = Math.max(0, duration2 - nextTime);
|
|
1041
|
+
const baseVolume = config.audio?.volume ?? 1;
|
|
1042
|
+
audio.volume = fadeSeconds > 0 ? baseVolume * Math.min(1, remaining / fadeSeconds) : baseVolume;
|
|
1043
|
+
if (remaining <= 0 && !looping) audio.pause();
|
|
1044
|
+
}
|
|
1045
|
+
const notify = callbacksRef.current.onSceneChange;
|
|
1046
|
+
if (notify) {
|
|
1047
|
+
if (wrapped) sceneIndexRef.current = -1;
|
|
1048
|
+
const ranges = resolveVideoTimeline(config);
|
|
1049
|
+
const index = ranges.findIndex((range) => nextTime >= range.start && nextTime < range.end);
|
|
1050
|
+
const resolved = index === -1 && nextTime >= duration2 ? ranges.length - 1 : index;
|
|
1051
|
+
if (resolved !== -1 && resolved !== sceneIndexRef.current) {
|
|
1052
|
+
sceneIndexRef.current = resolved;
|
|
1053
|
+
notify(ranges[resolved].scene, resolved);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
const duration = current.config ? getVideoDuration(current.config) : 0;
|
|
1058
|
+
if (!settled || looping || timeRef.current < duration) frame = requestAnimationFrame(tick);
|
|
1059
|
+
else setIsPlaying(false);
|
|
1060
|
+
};
|
|
1061
|
+
frame = requestAnimationFrame(tick);
|
|
1062
|
+
return () => cancelAnimationFrame(frame);
|
|
1063
|
+
}, [isPlaying]);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/player/video-player.tsx
|
|
1067
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1068
|
+
var MINIMUM_GENERATION_INTRO_MS = 3e3;
|
|
1069
|
+
function savedVideoState(video) {
|
|
1070
|
+
return {
|
|
1071
|
+
...createVideoState(),
|
|
1072
|
+
status: "complete",
|
|
1073
|
+
config: video
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
201
1076
|
function VideoPlayerRuntime({
|
|
202
1077
|
kit,
|
|
203
1078
|
stream,
|
|
@@ -221,9 +1096,14 @@ function VideoPlayerRuntime({
|
|
|
221
1096
|
() => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
222
1097
|
);
|
|
223
1098
|
const [audioUnlocked, setAudioUnlocked] = useState(false);
|
|
224
|
-
const resolvedStartMuted
|
|
225
|
-
|
|
226
|
-
|
|
1099
|
+
const { resolvedStartMuted, shouldAutoPlay, autoStartGeneration } = resolvePlaybackPolicy({
|
|
1100
|
+
playbackMode,
|
|
1101
|
+
autoPlay,
|
|
1102
|
+
startMuted,
|
|
1103
|
+
audioUnlocked,
|
|
1104
|
+
reducedMotion,
|
|
1105
|
+
hasStream: Boolean(stream)
|
|
1106
|
+
});
|
|
227
1107
|
const [isPlaying, setIsPlaying] = useState(() => shouldAutoPlay && !reducedMotion && !autoStartGeneration);
|
|
228
1108
|
const [isMuted, setIsMuted] = useState(resolvedStartMuted);
|
|
229
1109
|
const [fullscreenMode, setFullscreenMode] = useState("none");
|
|
@@ -276,7 +1156,7 @@ function VideoPlayerRuntime({
|
|
|
276
1156
|
setGenerationIntroComplete(!playbackMode || !stream);
|
|
277
1157
|
introStartedAtRef.current = autoStartReplacement ? performance.now() : null;
|
|
278
1158
|
}
|
|
279
|
-
|
|
1159
|
+
useEffect2(() => {
|
|
280
1160
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
281
1161
|
const preference = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
282
1162
|
const handleChange = (event) => {
|
|
@@ -291,11 +1171,11 @@ function VideoPlayerRuntime({
|
|
|
291
1171
|
preference.addEventListener?.("change", handleChange);
|
|
292
1172
|
return () => preference.removeEventListener?.("change", handleChange);
|
|
293
1173
|
}, []);
|
|
294
|
-
|
|
1174
|
+
useEffect2(() => {
|
|
295
1175
|
import("./control-visibility-NQOWTKOQ.js");
|
|
296
1176
|
}, []);
|
|
297
|
-
|
|
298
|
-
|
|
1177
|
+
useEffect2(() => setIsMuted(resolvedStartMuted), [resolvedStartMuted]);
|
|
1178
|
+
useEffect2(() => {
|
|
299
1179
|
let cancelled = false;
|
|
300
1180
|
let started = false;
|
|
301
1181
|
const reset = video ? savedVideoState(video) : createVideoState();
|
|
@@ -316,12 +1196,7 @@ function VideoPlayerRuntime({
|
|
|
316
1196
|
for await (const event of stream) {
|
|
317
1197
|
if (cancelled) return;
|
|
318
1198
|
const current = stateRef.current;
|
|
319
|
-
const
|
|
320
|
-
current.config ? resolveVideoTimeline(current.config).filter((range) => range.end > timeRef.current + 1e-3).map((range) => range.scene.id) : []
|
|
321
|
-
);
|
|
322
|
-
const next = applyVideoEvent(current, event, {
|
|
323
|
-
...current.config ? { mutableSceneIds } : {}
|
|
324
|
-
});
|
|
1199
|
+
const next = applyVideoEvent(current, event);
|
|
325
1200
|
stateRef.current = next;
|
|
326
1201
|
setReplacementPending(false);
|
|
327
1202
|
setState(next);
|
|
@@ -350,7 +1225,7 @@ function VideoPlayerRuntime({
|
|
|
350
1225
|
}
|
|
351
1226
|
};
|
|
352
1227
|
}, [stream, video]);
|
|
353
|
-
|
|
1228
|
+
useEffect2(() => {
|
|
354
1229
|
if (width != null) return;
|
|
355
1230
|
const container = containerRef.current;
|
|
356
1231
|
if (!container) return;
|
|
@@ -363,70 +1238,23 @@ function VideoPlayerRuntime({
|
|
|
363
1238
|
observer.observe(container);
|
|
364
1239
|
return () => observer.disconnect();
|
|
365
1240
|
}, [width]);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (config2?.scenes.length) {
|
|
379
|
-
const duration3 = getVideoDuration(config2);
|
|
380
|
-
const raw = timeRef.current + delta;
|
|
381
|
-
let nextTime;
|
|
382
|
-
if (looping && duration3 > 0 && raw >= duration3) {
|
|
383
|
-
nextTime = raw % duration3;
|
|
384
|
-
wrapped = true;
|
|
385
|
-
} else {
|
|
386
|
-
nextTime = Math.min(raw, duration3);
|
|
387
|
-
}
|
|
388
|
-
if (nextTime !== timeRef.current) {
|
|
389
|
-
timeRef.current = nextTime;
|
|
390
|
-
setCurrentTime(nextTime);
|
|
391
|
-
}
|
|
392
|
-
const audio = audioRef.current;
|
|
393
|
-
if (audio && (current.status === "complete" || current.status === "error")) {
|
|
394
|
-
if (wrapped) {
|
|
395
|
-
audio.currentTime = 0;
|
|
396
|
-
if (audio.paused) void audio.play().catch(Boolean);
|
|
397
|
-
}
|
|
398
|
-
const fadeSeconds = Math.max(0, (config2.audio?.fadeOutMs ?? 3e3) / 1e3);
|
|
399
|
-
const remaining = Math.max(0, duration3 - nextTime);
|
|
400
|
-
const baseVolume = config2.audio?.volume ?? 1;
|
|
401
|
-
audio.volume = fadeSeconds > 0 ? baseVolume * Math.min(1, remaining / fadeSeconds) : baseVolume;
|
|
402
|
-
if (remaining <= 0 && !looping) audio.pause();
|
|
403
|
-
}
|
|
404
|
-
const notify = callbacksRef.current.onSceneChange;
|
|
405
|
-
if (notify) {
|
|
406
|
-
if (wrapped) sceneIndexRef.current = -1;
|
|
407
|
-
const ranges = resolveVideoTimeline(config2);
|
|
408
|
-
const index = ranges.findIndex((range) => nextTime >= range.start && nextTime < range.end);
|
|
409
|
-
const resolved = index === -1 && nextTime >= duration3 ? ranges.length - 1 : index;
|
|
410
|
-
if (resolved !== -1 && resolved !== sceneIndexRef.current) {
|
|
411
|
-
sceneIndexRef.current = resolved;
|
|
412
|
-
notify(ranges[resolved].scene, resolved);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
const terminal2 = settled;
|
|
417
|
-
const duration2 = current.config ? getVideoDuration(current.config) : 0;
|
|
418
|
-
if (!terminal2 || looping || timeRef.current < duration2) frame = requestAnimationFrame(tick);
|
|
419
|
-
else setIsPlaying(false);
|
|
420
|
-
};
|
|
421
|
-
frame = requestAnimationFrame(tick);
|
|
422
|
-
return () => cancelAnimationFrame(frame);
|
|
423
|
-
}, [isPlaying]);
|
|
424
|
-
useEffect(() => {
|
|
1241
|
+
usePlaybackClock({
|
|
1242
|
+
isPlaying,
|
|
1243
|
+
stateRef,
|
|
1244
|
+
timeRef,
|
|
1245
|
+
audioRef,
|
|
1246
|
+
loopRef,
|
|
1247
|
+
sceneIndexRef,
|
|
1248
|
+
callbacksRef,
|
|
1249
|
+
setCurrentTime,
|
|
1250
|
+
setIsPlaying
|
|
1251
|
+
});
|
|
1252
|
+
useEffect2(() => {
|
|
425
1253
|
const audio = audioRef.current;
|
|
426
1254
|
if (!audio) return;
|
|
427
1255
|
audio.volume = state.config?.audio?.volume ?? 1;
|
|
428
1256
|
}, [state.config?.audio?.audioUrl, state.config?.audio?.volume]);
|
|
429
|
-
|
|
1257
|
+
useEffect2(() => {
|
|
430
1258
|
const audio = audioRef.current;
|
|
431
1259
|
if (!audio) return;
|
|
432
1260
|
if (isPlaying || introPlaying) {
|
|
@@ -444,7 +1272,7 @@ function VideoPlayerRuntime({
|
|
|
444
1272
|
audio.pause();
|
|
445
1273
|
}
|
|
446
1274
|
}, [introPlaying, isPlaying, state.config?.audio?.audioUrl]);
|
|
447
|
-
|
|
1275
|
+
useEffect2(() => {
|
|
448
1276
|
if (!state.config?.scenes.length) {
|
|
449
1277
|
const terminalWithoutVideo = state.status === "complete" || state.status === "error" || state.status === "aborted";
|
|
450
1278
|
if (terminalWithoutVideo) {
|
|
@@ -505,7 +1333,7 @@ function VideoPlayerRuntime({
|
|
|
505
1333
|
const firstSceneRange = config ? resolveVideoTimeline(config)[0] : void 0;
|
|
506
1334
|
const hasSuppliedOpening = firstSceneRange?.scene.id === "supplied-opening";
|
|
507
1335
|
const showStartPoster = (!generationIntroWaiting || hasSuppliedOpening) && !startRequested && !isPlaying && !ended && currentTime <= 1e-3 && Boolean(config?.scenes.length);
|
|
508
|
-
const firstSceneHoldProgress = firstSceneRange ? kit.
|
|
1336
|
+
const firstSceneHoldProgress = firstSceneRange ? kit.getTemplate(firstSceneRange.scene.templateId)?.transitionTiming?.holdProgress ?? 0.7 : 0;
|
|
509
1337
|
const posterTime = firstSceneRange ? firstSceneRange.start + Math.max(0, firstSceneRange.end - firstSceneRange.start) * firstSceneHoldProgress : currentTime;
|
|
510
1338
|
const startPlayback = () => {
|
|
511
1339
|
setGenerationIntroComplete(true);
|
|
@@ -565,53 +1393,10 @@ function VideoPlayerRuntime({
|
|
|
565
1393
|
if (!container) return;
|
|
566
1394
|
void import("./control-visibility-NQOWTKOQ.js").then((output) => output.togglePlayerFullscreen(container, setFullscreenMode));
|
|
567
1395
|
};
|
|
568
|
-
const controlSize = Math.max(40, Math.min(52, Math.round(displayWidth * 0.15)));
|
|
569
|
-
const controlInset = Math.max(10, Math.min(20, Math.round(displayWidth * 0.056)));
|
|
570
|
-
const controlButtonStyle = {
|
|
571
|
-
display: "inline-grid",
|
|
572
|
-
placeItems: "center",
|
|
573
|
-
width: controlSize,
|
|
574
|
-
height: controlSize,
|
|
575
|
-
minWidth: controlSize,
|
|
576
|
-
minHeight: controlSize,
|
|
577
|
-
padding: 0,
|
|
578
|
-
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
579
|
-
borderRadius: 999,
|
|
580
|
-
backgroundColor: "rgba(255, 255, 255, 0.16)",
|
|
581
|
-
color: "#ffffff",
|
|
582
|
-
boxShadow: "0 5px 18px rgba(0, 0, 0, 0.16)",
|
|
583
|
-
backdropFilter: "blur(12px)",
|
|
584
|
-
cursor: "pointer"
|
|
585
|
-
};
|
|
586
|
-
const startButtonStyle = {
|
|
587
|
-
display: "inline-flex",
|
|
588
|
-
alignItems: "center",
|
|
589
|
-
gap: 10,
|
|
590
|
-
minHeight: 60,
|
|
591
|
-
padding: "14px 22px",
|
|
592
|
-
border: "1px solid rgba(9, 7, 18, 0.08)",
|
|
593
|
-
borderRadius: 999,
|
|
594
|
-
backgroundColor: "#ffffff",
|
|
595
|
-
color: "#090712",
|
|
596
|
-
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.24)",
|
|
597
|
-
font: "700 16px/1 system-ui, sans-serif",
|
|
598
|
-
whiteSpace: "nowrap",
|
|
599
|
-
cursor: "pointer"
|
|
600
|
-
};
|
|
601
|
-
const startButtonPositionStyle = {
|
|
602
|
-
position: "absolute",
|
|
603
|
-
left: "50%",
|
|
604
|
-
top: "50%",
|
|
605
|
-
zIndex: 3,
|
|
606
|
-
transform: "translate(-50%, -50%)"
|
|
607
|
-
};
|
|
608
1396
|
const pendingReplacement = replacementPending && state.status === "idle";
|
|
609
1397
|
const displayedStatus = pendingReplacement ? "streaming" : state.status;
|
|
610
1398
|
const generationCoverVisible = generationIntroWaiting && !hasSuppliedOpening || displayedStatus === "streaming" && !config?.scenes.length;
|
|
611
|
-
|
|
612
|
-
const coverText = config?.style.brand.colors.foreground ?? "#ffffff";
|
|
613
|
-
const coverBackgroundCss = coverBackground?.type === "solid" ? coverBackground.color : `linear-gradient(135deg, ${coverBackground?.colors[0] ?? "#8711C1"}, ${coverBackground?.colors[1] ?? "#2167E3"})`;
|
|
614
|
-
return /* @__PURE__ */ jsxs(
|
|
1399
|
+
return /* @__PURE__ */ jsxs2(
|
|
615
1400
|
"div",
|
|
616
1401
|
{
|
|
617
1402
|
ref: containerRef,
|
|
@@ -651,45 +1436,18 @@ function VideoPlayerRuntime({
|
|
|
651
1436
|
...style
|
|
652
1437
|
},
|
|
653
1438
|
children: [
|
|
654
|
-
|
|
655
|
-
|
|
1439
|
+
/* @__PURE__ */ jsx2(
|
|
1440
|
+
GenerationCover,
|
|
656
1441
|
{
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
display: "grid",
|
|
664
|
-
placeItems: "center",
|
|
665
|
-
padding: "12%",
|
|
666
|
-
textAlign: "center",
|
|
667
|
-
color: coverText,
|
|
668
|
-
background: coverBackgroundCss,
|
|
669
|
-
fontFamily: fontStack(config?.style.brand.font)
|
|
670
|
-
},
|
|
671
|
-
children: [
|
|
672
|
-
/* @__PURE__ */ jsxs("div", { style: { position: "absolute", top: "28%", left: "10%", right: "10%" }, children: [
|
|
673
|
-
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { fontSize: "clamp(28px, 8vw, 72px)", opacity: 0.9 }, children: "\u2726" }),
|
|
674
|
-
/* @__PURE__ */ jsx("strong", { style: { display: "block", marginTop: 18, fontSize: "clamp(24px, 5vw, 56px)", lineHeight: 1.08 }, children: "Creating your video\u2026" }),
|
|
675
|
-
/* @__PURE__ */ jsx("span", { style: { display: "block", marginTop: 14, fontSize: "clamp(14px, 2vw, 22px)", lineHeight: 1.4, opacity: 0.78 }, children: "Choosing the best scenes for your content." })
|
|
676
|
-
] }),
|
|
677
|
-
!startRequested && !isPlaying ? /* @__PURE__ */ jsxs(
|
|
678
|
-
"button",
|
|
679
|
-
{
|
|
680
|
-
type: "button",
|
|
681
|
-
"aria-label": config?.audio && !isMuted ? "Play video with sound" : "Play video response",
|
|
682
|
-
onClick: armPlayback,
|
|
683
|
-
style: { ...startButtonStyle, ...startButtonPositionStyle },
|
|
684
|
-
children: [
|
|
685
|
-
/* @__PURE__ */ jsx(PlayerIcon, { name: "play", size: 20 }),
|
|
686
|
-
config?.audio && !isMuted ? "Play with sound" : "Play video"
|
|
687
|
-
]
|
|
688
|
-
}
|
|
689
|
-
) : null
|
|
690
|
-
]
|
|
1442
|
+
visible: generationCoverVisible,
|
|
1443
|
+
config,
|
|
1444
|
+
isMuted,
|
|
1445
|
+
startRequested,
|
|
1446
|
+
isPlaying,
|
|
1447
|
+
onStart: armPlayback
|
|
691
1448
|
}
|
|
692
|
-
)
|
|
1449
|
+
),
|
|
1450
|
+
!generationCoverVisible && config?.scenes.length ? /* @__PURE__ */ jsx2(
|
|
693
1451
|
VideoFrame,
|
|
694
1452
|
{
|
|
695
1453
|
kit,
|
|
@@ -707,24 +1465,16 @@ function VideoPlayerRuntime({
|
|
|
707
1465
|
}
|
|
708
1466
|
}
|
|
709
1467
|
) : null,
|
|
710
|
-
|
|
711
|
-
|
|
1468
|
+
/* @__PURE__ */ jsx2(
|
|
1469
|
+
StartPosterButton,
|
|
712
1470
|
{
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
style: {
|
|
718
|
-
...startButtonStyle,
|
|
719
|
-
...startButtonPositionStyle
|
|
720
|
-
},
|
|
721
|
-
children: [
|
|
722
|
-
/* @__PURE__ */ jsx(PlayerIcon, { name: "play", size: 20 }),
|
|
723
|
-
config?.audio && !isMuted ? "Play with sound" : "Play video"
|
|
724
|
-
]
|
|
1471
|
+
visible: showStartPoster,
|
|
1472
|
+
config,
|
|
1473
|
+
isMuted,
|
|
1474
|
+
onStart: startPlayback
|
|
725
1475
|
}
|
|
726
|
-
)
|
|
727
|
-
config?.audio ? /* @__PURE__ */
|
|
1476
|
+
),
|
|
1477
|
+
config?.audio ? /* @__PURE__ */ jsx2(
|
|
728
1478
|
"audio",
|
|
729
1479
|
{
|
|
730
1480
|
ref: audioRef,
|
|
@@ -733,107 +1483,29 @@ function VideoPlayerRuntime({
|
|
|
733
1483
|
autoPlay: isPlaying,
|
|
734
1484
|
muted: isMuted,
|
|
735
1485
|
preload: "auto",
|
|
736
|
-
loop: state.status === "streaming" || introPlaying
|
|
1486
|
+
loop: loop || state.status === "streaming" || introPlaying
|
|
737
1487
|
},
|
|
738
1488
|
config.audio.audioUrl
|
|
739
1489
|
) : null,
|
|
740
|
-
|
|
741
|
-
|
|
1490
|
+
/* @__PURE__ */ jsx2(EndedOverlay, { ended, onReplay: togglePlayback }),
|
|
1491
|
+
/* @__PURE__ */ jsx2(
|
|
1492
|
+
PlayerControls,
|
|
742
1493
|
{
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1494
|
+
visible: !generationCoverVisible && !showStartPoster && Boolean(config?.scenes.length),
|
|
1495
|
+
displayWidth,
|
|
1496
|
+
ended,
|
|
1497
|
+
isPlaying,
|
|
1498
|
+
isMuted,
|
|
1499
|
+
isFullscreen,
|
|
1500
|
+
hasAudio: Boolean(config?.audio),
|
|
1501
|
+
onTogglePlayback: togglePlayback,
|
|
1502
|
+
onToggleMuted: () => setIsMuted((muted) => {
|
|
1503
|
+
if (muted) setAudioUnlocked(true);
|
|
1504
|
+
return !muted;
|
|
1505
|
+
}),
|
|
1506
|
+
onToggleFullscreen: toggleFullscreen
|
|
753
1507
|
}
|
|
754
|
-
)
|
|
755
|
-
ended ? /* @__PURE__ */ jsxs(
|
|
756
|
-
"button",
|
|
757
|
-
{
|
|
758
|
-
type: "button",
|
|
759
|
-
"data-testid": "video-replay-button",
|
|
760
|
-
"aria-label": "Replay video response",
|
|
761
|
-
onClick: togglePlayback,
|
|
762
|
-
style: {
|
|
763
|
-
...startButtonStyle,
|
|
764
|
-
...startButtonPositionStyle,
|
|
765
|
-
zIndex: 3
|
|
766
|
-
},
|
|
767
|
-
children: [
|
|
768
|
-
/* @__PURE__ */ jsx(PlayerIcon, { name: "replay", size: 21 }),
|
|
769
|
-
"Replay"
|
|
770
|
-
]
|
|
771
|
-
}
|
|
772
|
-
) : null,
|
|
773
|
-
!generationCoverVisible && !showStartPoster && config?.scenes.length ? /* @__PURE__ */ jsxs(
|
|
774
|
-
"div",
|
|
775
|
-
{
|
|
776
|
-
"data-testid": "video-controls",
|
|
777
|
-
style: {
|
|
778
|
-
position: "absolute",
|
|
779
|
-
inset: 0,
|
|
780
|
-
zIndex: 4,
|
|
781
|
-
pointerEvents: "none"
|
|
782
|
-
},
|
|
783
|
-
children: [
|
|
784
|
-
/* @__PURE__ */ jsx(
|
|
785
|
-
"div",
|
|
786
|
-
{
|
|
787
|
-
"data-testid": "video-primary-controls",
|
|
788
|
-
style: { position: "absolute", left: controlInset, bottom: controlInset, display: "flex", pointerEvents: "auto" },
|
|
789
|
-
children: /* @__PURE__ */ jsx(
|
|
790
|
-
"button",
|
|
791
|
-
{
|
|
792
|
-
type: "button",
|
|
793
|
-
"aria-label": ended ? "Play video response from beginning" : isPlaying ? "Pause video response" : "Play video response",
|
|
794
|
-
onClick: togglePlayback,
|
|
795
|
-
style: controlButtonStyle,
|
|
796
|
-
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isPlaying ? "pause" : "play" })
|
|
797
|
-
}
|
|
798
|
-
)
|
|
799
|
-
}
|
|
800
|
-
),
|
|
801
|
-
/* @__PURE__ */ jsxs(
|
|
802
|
-
"div",
|
|
803
|
-
{
|
|
804
|
-
"data-testid": "video-secondary-controls",
|
|
805
|
-
style: { position: "absolute", right: controlInset, bottom: controlInset, display: "flex", gap: 10, pointerEvents: "auto" },
|
|
806
|
-
children: [
|
|
807
|
-
config?.audio ? /* @__PURE__ */ jsx(
|
|
808
|
-
"button",
|
|
809
|
-
{
|
|
810
|
-
type: "button",
|
|
811
|
-
"aria-label": isMuted ? "Unmute video response" : "Mute video response",
|
|
812
|
-
"aria-pressed": !isMuted,
|
|
813
|
-
onClick: () => setIsMuted((muted) => {
|
|
814
|
-
if (muted) setAudioUnlocked(true);
|
|
815
|
-
return !muted;
|
|
816
|
-
}),
|
|
817
|
-
style: controlButtonStyle,
|
|
818
|
-
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isMuted ? "volume-off" : "volume" })
|
|
819
|
-
}
|
|
820
|
-
) : null,
|
|
821
|
-
/* @__PURE__ */ jsx(
|
|
822
|
-
"button",
|
|
823
|
-
{
|
|
824
|
-
type: "button",
|
|
825
|
-
"aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
826
|
-
onClick: () => void toggleFullscreen(),
|
|
827
|
-
style: controlButtonStyle,
|
|
828
|
-
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isFullscreen ? "exit-fullscreen" : "enter-fullscreen" })
|
|
829
|
-
}
|
|
830
|
-
)
|
|
831
|
-
]
|
|
832
|
-
}
|
|
833
|
-
)
|
|
834
|
-
]
|
|
835
|
-
}
|
|
836
|
-
) : null
|
|
1508
|
+
)
|
|
837
1509
|
]
|
|
838
1510
|
}
|
|
839
1511
|
);
|
|
@@ -848,11 +1520,11 @@ function VideoPlayer({
|
|
|
848
1520
|
}) {
|
|
849
1521
|
const savedVideo = useMemo(() => video ? parseVideo(video) : void 0, [video]);
|
|
850
1522
|
const kit = useMemo(
|
|
851
|
-
() =>
|
|
1523
|
+
() => overlayPlayerTemplateRegistry(BUILTIN_PLAYER_KIT, templates),
|
|
852
1524
|
[templates]
|
|
853
1525
|
);
|
|
854
1526
|
if (!stream && !savedVideo) return null;
|
|
855
|
-
return /* @__PURE__ */
|
|
1527
|
+
return /* @__PURE__ */ jsx2(
|
|
856
1528
|
VideoPlayerRuntime,
|
|
857
1529
|
{
|
|
858
1530
|
...props,
|
|
@@ -886,7 +1558,7 @@ var VideoError = class extends Error {
|
|
|
886
1558
|
};
|
|
887
1559
|
|
|
888
1560
|
// src/player/use-video.ts
|
|
889
|
-
import { useCallback, useEffect as
|
|
1561
|
+
import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
890
1562
|
|
|
891
1563
|
// src/player/stream-video.ts
|
|
892
1564
|
var HTTP_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
@@ -956,15 +1628,7 @@ function streamVideo(options) {
|
|
|
956
1628
|
resolveResult = resolve;
|
|
957
1629
|
rejectResult = reject;
|
|
958
1630
|
});
|
|
959
|
-
const
|
|
960
|
-
const waiters = /* @__PURE__ */ new Set();
|
|
961
|
-
let streamDone = false;
|
|
962
|
-
let streamError;
|
|
963
|
-
const notify = () => {
|
|
964
|
-
for (const waiter of waiters) waiter();
|
|
965
|
-
waiters.clear();
|
|
966
|
-
};
|
|
967
|
-
void (async function consume() {
|
|
1631
|
+
const source = (async function* consume() {
|
|
968
1632
|
let state = options.resume?.state ?? createVideoState();
|
|
969
1633
|
try {
|
|
970
1634
|
const fetcher = options.fetcher ?? fetch;
|
|
@@ -995,8 +1659,6 @@ function streamVideo(options) {
|
|
|
995
1659
|
for await (const event of decodeVideoSse(response.body)) {
|
|
996
1660
|
state = applyVideoEvent(state, event);
|
|
997
1661
|
options.onEvent?.(event, state);
|
|
998
|
-
buffered.push(event);
|
|
999
|
-
notify();
|
|
1000
1662
|
if (state.status === "complete" && !settled) {
|
|
1001
1663
|
settled = true;
|
|
1002
1664
|
resolveResult(state);
|
|
@@ -1020,6 +1682,7 @@ function streamVideo(options) {
|
|
|
1020
1682
|
recoverable: false
|
|
1021
1683
|
}));
|
|
1022
1684
|
}
|
|
1685
|
+
yield event;
|
|
1023
1686
|
}
|
|
1024
1687
|
if (!["complete", "error", "aborted"].includes(state.status)) {
|
|
1025
1688
|
throw new Error("Video response stream ended without a terminal event");
|
|
@@ -1034,26 +1697,12 @@ function streamVideo(options) {
|
|
|
1034
1697
|
settled = true;
|
|
1035
1698
|
rejectResult(error);
|
|
1036
1699
|
}
|
|
1037
|
-
|
|
1700
|
+
throw error;
|
|
1038
1701
|
} finally {
|
|
1039
|
-
streamDone = true;
|
|
1040
|
-
notify();
|
|
1041
1702
|
options.signal?.removeEventListener("abort", forwardAbort);
|
|
1042
1703
|
}
|
|
1043
1704
|
})();
|
|
1044
|
-
const stream =
|
|
1045
|
-
async *[Symbol.asyncIterator]() {
|
|
1046
|
-
let index = 0;
|
|
1047
|
-
while (true) {
|
|
1048
|
-
while (index < buffered.length) yield buffered[index++];
|
|
1049
|
-
if (streamDone) {
|
|
1050
|
-
if (streamError) throw streamError;
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
await new Promise((resolve) => waiters.add(resolve));
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1705
|
+
const stream = createReplayableStream(source, () => void 0);
|
|
1057
1706
|
return {
|
|
1058
1707
|
request,
|
|
1059
1708
|
stream,
|
|
@@ -1076,13 +1725,13 @@ function errorFrom(cause) {
|
|
|
1076
1725
|
}
|
|
1077
1726
|
function useVideo(options = {}) {
|
|
1078
1727
|
const templates = useMemo2(
|
|
1079
|
-
() =>
|
|
1728
|
+
() => overlayPlayerTemplateRegistry(BUILTIN_PLAYER_KIT, options.templates),
|
|
1080
1729
|
[options.templates]
|
|
1081
1730
|
);
|
|
1082
1731
|
const [state, setState] = useState2(() => createVideoState());
|
|
1083
1732
|
const [error, setError] = useState2();
|
|
1084
1733
|
const [run, setRun] = useState2();
|
|
1085
|
-
const activeRun = useRef2();
|
|
1734
|
+
const activeRun = useRef2(void 0);
|
|
1086
1735
|
const generation = useRef2(0);
|
|
1087
1736
|
const mounted = useRef2(true);
|
|
1088
1737
|
const optionsRef = useRef2(options);
|
|
@@ -1124,9 +1773,8 @@ function useVideo(options = {}) {
|
|
|
1124
1773
|
if (!currentOptions.templates?.getTemplate(_event.data.scene.templateId)) {
|
|
1125
1774
|
preloadBuiltinTemplate(_event.data.scene.templateId);
|
|
1126
1775
|
}
|
|
1776
|
+
warmSceneMedia(_event.data.scene.variables);
|
|
1127
1777
|
}
|
|
1128
|
-
const backdrop = _event.type === "scene.add" ? _event.data.scene.variables : _event.type === "asset.patch" ? _event.data.variables : _event.type === "scene.patch" ? _event.data.patch.variables : void 0;
|
|
1129
|
-
if (backdrop) warmSceneMedia(backdrop);
|
|
1130
1778
|
if (mounted.current && generation.current === currentGeneration) setState(nextState);
|
|
1131
1779
|
}
|
|
1132
1780
|
});
|
|
@@ -1153,7 +1801,7 @@ function useVideo(options = {}) {
|
|
|
1153
1801
|
void result.catch(() => void 0);
|
|
1154
1802
|
return result;
|
|
1155
1803
|
}, []);
|
|
1156
|
-
|
|
1804
|
+
useEffect3(() => {
|
|
1157
1805
|
mounted.current = true;
|
|
1158
1806
|
return () => {
|
|
1159
1807
|
mounted.current = false;
|
|
@@ -1169,7 +1817,10 @@ function useVideo(options = {}) {
|
|
|
1169
1817
|
status: state.status,
|
|
1170
1818
|
error,
|
|
1171
1819
|
warnings: state.warnings,
|
|
1172
|
-
playerProps: {
|
|
1820
|
+
playerProps: {
|
|
1821
|
+
...options.templates ? { templates: options.templates } : {},
|
|
1822
|
+
...run ? { stream: run.stream } : {}
|
|
1823
|
+
}
|
|
1173
1824
|
};
|
|
1174
1825
|
}
|
|
1175
1826
|
export {
|