@pixerate/flair 0.24.0 → 0.26.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +1 -0
  3. package/dist/catalog/index.js +2 -2
  4. package/dist/{chunk-WAMA45G3.js → chunk-5USSDEQL.js} +77 -21
  5. package/dist/chunk-5USSDEQL.js.map +1 -0
  6. package/dist/{chunk-GTIAOYOK.js → chunk-74ZLCZOG.js} +13 -10
  7. package/dist/chunk-74ZLCZOG.js.map +1 -0
  8. package/dist/{chunk-NGKNI3QX.js → chunk-CK6EV37I.js} +19 -495
  9. package/dist/chunk-CK6EV37I.js.map +1 -0
  10. package/dist/chunk-DBTLCXKY.js +332 -0
  11. package/dist/chunk-DBTLCXKY.js.map +1 -0
  12. package/dist/chunk-G2B5L234.js +414 -0
  13. package/dist/chunk-G2B5L234.js.map +1 -0
  14. package/dist/{chunk-VP6IEWIK.js → chunk-H6L67KHI.js} +1 -1
  15. package/dist/chunk-H6VDFBIO.js +498 -0
  16. package/dist/chunk-H6VDFBIO.js.map +1 -0
  17. package/dist/{chunk-OQ4EWZ3B.js → chunk-HVXWNGTW.js} +83 -6
  18. package/dist/{chunk-OQ4EWZ3B.js.map → chunk-HVXWNGTW.js.map} +1 -1
  19. package/dist/chunk-JL6TI6II.js +724 -0
  20. package/dist/chunk-JL6TI6II.js.map +1 -0
  21. package/dist/{chunk-OHW7UG6D.js → chunk-ORAFCHX3.js} +26 -2
  22. package/dist/{chunk-OHW7UG6D.js.map → chunk-ORAFCHX3.js.map} +1 -1
  23. package/dist/{chunk-ARKC7LDW.js → chunk-RDU63CDO.js} +2 -331
  24. package/dist/chunk-RDU63CDO.js.map +1 -0
  25. package/dist/{chunk-LVPIIWPP.js → chunk-STKEKXCK.js} +18 -8
  26. package/dist/chunk-STKEKXCK.js.map +1 -0
  27. package/dist/chunk-UCZ43ARE.js +902 -0
  28. package/dist/chunk-UCZ43ARE.js.map +1 -0
  29. package/dist/chunk-YBGRXVAE.js +407 -0
  30. package/dist/chunk-YBGRXVAE.js.map +1 -0
  31. package/dist/cli.js +74 -11
  32. package/dist/cli.js.map +1 -1
  33. package/dist/editor/index.d.ts +1 -1
  34. package/dist/editor/index.js +14 -12
  35. package/dist/editor/index.js.map +1 -1
  36. package/dist/effect-GGH3OSOT.js +47 -0
  37. package/dist/embed/index.d.ts +215 -0
  38. package/dist/embed/index.js +21 -0
  39. package/dist/{export-DH2HMACO.js → export-QVW474KM.js} +3 -3
  40. package/dist/export-QVW474KM.js.map +1 -0
  41. package/dist/frames-VEcqa40g.d.ts +161 -0
  42. package/dist/index.d.ts +47 -164
  43. package/dist/index.js +102 -80
  44. package/dist/index.js.map +1 -1
  45. package/dist/node.d.ts +167 -7
  46. package/dist/node.js +25 -3
  47. package/dist/react/index.d.ts +33 -2
  48. package/dist/react/index.js +54 -9
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/recorder-CDPPTJMG.js +31 -0
  51. package/dist/recorder-CDPPTJMG.js.map +1 -0
  52. package/dist/{renderer-C2P1qGzP.d.ts → renderer-teaa98RE.d.ts} +1 -1
  53. package/dist/svelte/index.d.ts +33 -4
  54. package/dist/svelte/index.js +51 -9
  55. package/dist/svelte/index.js.map +1 -1
  56. package/dist/vector/index.d.ts +52 -1
  57. package/dist/vector/index.js +8 -4
  58. package/package.json +1 -1
  59. package/dist/chunk-ARKC7LDW.js.map +0 -1
  60. package/dist/chunk-GTIAOYOK.js.map +0 -1
  61. package/dist/chunk-LVPIIWPP.js.map +0 -1
  62. package/dist/chunk-NGKNI3QX.js.map +0 -1
  63. package/dist/chunk-UP6QQVRS.js +0 -331
  64. package/dist/chunk-UP6QQVRS.js.map +0 -1
  65. package/dist/chunk-WAMA45G3.js.map +0 -1
  66. package/dist/recorder-HKFFHQES.js +0 -10
  67. /package/dist/{chunk-VP6IEWIK.js.map → chunk-H6L67KHI.js.map} +0 -0
  68. /package/dist/{export-DH2HMACO.js.map → effect-GGH3OSOT.js.map} +0 -0
  69. /package/dist/{recorder-HKFFHQES.js.map → embed/index.js.map} +0 -0
@@ -0,0 +1,902 @@
1
+ import {
2
+ ShaderFrames,
3
+ ShaderRenderer
4
+ } from "./chunk-H6VDFBIO.js";
5
+ import {
6
+ seekVideo
7
+ } from "./chunk-RDU63CDO.js";
8
+ import {
9
+ effectDefaults,
10
+ effectInputs,
11
+ getEffect,
12
+ renderVectorEffect,
13
+ textImage
14
+ } from "./chunk-HVXWNGTW.js";
15
+ import {
16
+ parseColor
17
+ } from "./chunk-VYOMXE6I.js";
18
+ import {
19
+ SvgPlayer,
20
+ svgDuration,
21
+ svgView
22
+ } from "./chunk-PFGGPIMT.js";
23
+
24
+ // src/export/mp4mux.ts
25
+ var MOVIE_TIMESCALE = 1e3;
26
+ var UINT32 = 4294967296;
27
+ var ascii = (s) => Uint8Array.from(s, (c) => c.charCodeAt(0));
28
+ var Writer = class {
29
+ parts = [];
30
+ size = 0;
31
+ bytes(b) {
32
+ this.parts.push(b);
33
+ this.size += b.length;
34
+ return this;
35
+ }
36
+ u8(n) {
37
+ return this.bytes(Uint8Array.of(n & 255));
38
+ }
39
+ u16(n) {
40
+ return this.bytes(Uint8Array.of(n >>> 8 & 255, n & 255));
41
+ }
42
+ u32(n) {
43
+ const b = new Uint8Array(4);
44
+ new DataView(b.buffer).setUint32(0, n >>> 0);
45
+ return this.bytes(b);
46
+ }
47
+ u64(n) {
48
+ return this.u32(Math.floor(n / UINT32)).u32(n % UINT32);
49
+ }
50
+ zeros(n) {
51
+ return this.bytes(new Uint8Array(n));
52
+ }
53
+ done() {
54
+ const out = new Uint8Array(this.size);
55
+ let at = 0;
56
+ for (const p of this.parts) {
57
+ out.set(p, at);
58
+ at += p.length;
59
+ }
60
+ return out;
61
+ }
62
+ };
63
+ function box(type, ...content) {
64
+ const w = new Writer();
65
+ const size = 8 + content.reduce((n, c) => n + c.length, 0);
66
+ w.u32(size).bytes(ascii(type));
67
+ for (const c of content) w.bytes(c);
68
+ return w.done();
69
+ }
70
+ function fullBox(type, version, flags, ...content) {
71
+ return box(type, Uint8Array.of(version, flags >> 16 & 255, flags >> 8 & 255, flags & 255), ...content);
72
+ }
73
+ var fields = (fill) => {
74
+ const w = new Writer();
75
+ fill(w);
76
+ return w.done();
77
+ };
78
+ var MATRIX = fields((w) => [65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824].forEach((n) => w.u32(n)));
79
+ function sampleTicks(samples, timescale) {
80
+ const tick = (us) => Math.round(us * timescale / 1e6);
81
+ return samples.map((s, i) => {
82
+ const next = samples[i + 1];
83
+ if (next) return Math.max(1, tick(next.timestamp) - tick(s.timestamp));
84
+ if (s.duration) return Math.max(1, tick(s.duration));
85
+ const prev = samples[i - 1];
86
+ return prev ? Math.max(1, tick(s.timestamp) - tick(prev.timestamp)) : Math.max(1, tick(1e6 / 30));
87
+ });
88
+ }
89
+ function stts(ticks) {
90
+ const runs = [];
91
+ for (const t of ticks) {
92
+ const last = runs[runs.length - 1];
93
+ if (last && last[1] === t) last[0]++;
94
+ else runs.push([1, t]);
95
+ }
96
+ return fullBox("stts", 0, 0, fields((w) => {
97
+ w.u32(runs.length);
98
+ for (const [count, delta] of runs) w.u32(count).u32(delta);
99
+ }));
100
+ }
101
+ function moov(options, ticks, dataOffset, wide) {
102
+ const { width, height, avcC, samples } = options;
103
+ const timescale = options.timescale ?? 9e4;
104
+ const trackTicks = ticks.reduce((a, b) => a + b, 0);
105
+ const movieTicks = Math.round(trackTicks * MOVIE_TIMESCALE / timescale);
106
+ const mvhd = fullBox("mvhd", 0, 0, fields(
107
+ (w) => w.u32(0).u32(0).u32(MOVIE_TIMESCALE).u32(movieTicks).u32(65536).u16(256).zeros(10).bytes(MATRIX).zeros(24).u32(2)
108
+ // pre_defined, next track id
109
+ ));
110
+ const tkhd = fullBox("tkhd", 0, 3, fields(
111
+ (w) => (
112
+ // enabled, in the movie
113
+ w.u32(0).u32(0).u32(1).u32(0).u32(movieTicks).zeros(8).u16(0).u16(0).u16(0).u16(0).bytes(MATRIX).u32(width * 65536).u32(height * 65536)
114
+ )
115
+ ));
116
+ const mdhd = fullBox("mdhd", 0, 0, fields((w) => w.u32(0).u32(0).u32(timescale).u32(trackTicks).u16(21956).u16(0)));
117
+ const hdlr = fullBox("hdlr", 0, 0, fields((w) => w.u32(0).bytes(ascii("vide")).zeros(12).bytes(ascii("VideoHandler\0"))));
118
+ const vmhd = fullBox("vmhd", 0, 1, new Uint8Array(8));
119
+ const dinf = box("dinf", fullBox("dref", 0, 0, fields((w) => w.u32(1)), fullBox("url ", 0, 1)));
120
+ const avc1 = box(
121
+ "avc1",
122
+ fields(
123
+ (w) => w.zeros(6).u16(1).zeros(16).u16(width).u16(height).u32(4718592).u32(4718592).u32(0).u16(1).zeros(32).u16(24).u16(65535)
124
+ // compressor name, depth, pre_defined
125
+ ),
126
+ box("avcC", avcC)
127
+ );
128
+ const stsd = fullBox("stsd", 0, 0, fields((w) => w.u32(1)), avc1);
129
+ const keys = samples.flatMap((s, i) => s.key ? [i + 1] : []);
130
+ const stss = keys.length === samples.length ? [] : [fullBox("stss", 0, 0, fields((w) => {
131
+ w.u32(keys.length);
132
+ for (const k of keys) w.u32(k);
133
+ }))];
134
+ const stsz = fullBox("stsz", 0, 0, fields((w) => {
135
+ w.u32(0).u32(samples.length);
136
+ for (const s of samples) w.u32(s.data.length);
137
+ }));
138
+ const stsc = fullBox("stsc", 0, 0, fields((w) => samples.length ? w.u32(1).u32(1).u32(samples.length).u32(1) : w.u32(0)));
139
+ const stco = wide ? fullBox("co64", 0, 0, fields((w) => w.u32(1).u64(dataOffset))) : fullBox("stco", 0, 0, fields((w) => w.u32(1).u32(dataOffset)));
140
+ const stbl = box("stbl", stsd, stts(ticks), ...stss, stsz, stsc, stco);
141
+ const minf = box("minf", vmhd, dinf, stbl);
142
+ const trak = box("trak", tkhd, box("mdia", mdhd, hdlr, minf));
143
+ return box("moov", mvhd, trak);
144
+ }
145
+ function muxMp4(options) {
146
+ const { samples } = options;
147
+ if (!samples.length) throw new Error("An MP4 needs at least one frame.");
148
+ if (!samples[0].key) throw new Error("An MP4's first frame must be a keyframe.");
149
+ const ftyp = box("ftyp", ascii("isom"), fields((w) => w.u32(512)), ascii("isomiso2avc1mp41"));
150
+ const ticks = sampleTicks(samples, options.timescale ?? 9e4);
151
+ const payload = samples.reduce((n, s) => n + s.data.length, 0);
152
+ const large = payload + 8 >= UINT32;
153
+ const mdatHeader = large ? 16 : 8;
154
+ const wide = ftyp.length + moov(options, ticks, 0, large).length + mdatHeader + payload >= UINT32;
155
+ const index = moov(options, ticks, 0, wide);
156
+ const dataOffset = ftyp.length + index.length + mdatHeader;
157
+ const head = fields((w) => {
158
+ w.bytes(ftyp).bytes(moov(options, ticks, dataOffset, wide));
159
+ if (large) w.u32(1).bytes(ascii("mdat")).u64(payload + 16);
160
+ else w.u32(payload + 8).bytes(ascii("mdat"));
161
+ });
162
+ const out = new Uint8Array(head.length + payload);
163
+ out.set(head);
164
+ let at = head.length;
165
+ for (const s of samples) {
166
+ out.set(s.data, at);
167
+ at += s.data.length;
168
+ }
169
+ return out;
170
+ }
171
+
172
+ // src/export/webmmux.ts
173
+ var ID = {
174
+ EBML: 440786851,
175
+ EBMLVersion: 17030,
176
+ EBMLReadVersion: 17143,
177
+ EBMLMaxIDLength: 17138,
178
+ EBMLMaxSizeLength: 17139,
179
+ DocType: 17026,
180
+ DocTypeVersion: 17031,
181
+ DocTypeReadVersion: 17029,
182
+ Segment: 408125543,
183
+ Info: 357149030,
184
+ TimestampScale: 2807729,
185
+ Duration: 17545,
186
+ MuxingApp: 19840,
187
+ WritingApp: 22337,
188
+ Tracks: 374648427,
189
+ TrackEntry: 174,
190
+ TrackNumber: 215,
191
+ TrackUID: 29637,
192
+ TrackType: 131,
193
+ FlagLacing: 156,
194
+ CodecID: 134,
195
+ DefaultDuration: 2352003,
196
+ MaxBlockAdditionID: 21998,
197
+ Video: 224,
198
+ PixelWidth: 176,
199
+ PixelHeight: 186,
200
+ AlphaMode: 21440,
201
+ Cluster: 524531317,
202
+ Timestamp: 231,
203
+ SimpleBlock: 163,
204
+ BlockGroup: 160,
205
+ Block: 161,
206
+ ReferenceBlock: 251,
207
+ BlockAdditions: 30113,
208
+ BlockMore: 166,
209
+ BlockAddID: 238,
210
+ BlockAdditional: 165
211
+ };
212
+ var SCALE_NS = 1e6;
213
+ var MAX_OFFSET = 32767;
214
+ var concat = (parts) => {
215
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
216
+ let at = 0;
217
+ for (const p of parts) {
218
+ out.set(p, at);
219
+ at += p.length;
220
+ }
221
+ return out;
222
+ };
223
+ function idBytes(id) {
224
+ const out = [];
225
+ for (let n = id; n > 0; n = Math.floor(n / 256)) out.unshift(n & 255);
226
+ return Uint8Array.from(out);
227
+ }
228
+ function vint(n) {
229
+ for (let len = 1; len <= 8; len++) {
230
+ if (n < 2 ** (7 * len) - 1) {
231
+ const out = new Uint8Array(len);
232
+ let v = n;
233
+ for (let i = len - 1; i >= 0; i--) {
234
+ out[i] = v % 256;
235
+ v = Math.floor(v / 256);
236
+ }
237
+ out[0] |= 1 << 8 - len;
238
+ return out;
239
+ }
240
+ }
241
+ throw new RangeError(`An EBML size cannot be ${n}.`);
242
+ }
243
+ var el = (id, ...content) => {
244
+ const body = concat(content);
245
+ return concat([idBytes(id), vint(body.length), body]);
246
+ };
247
+ var uint = (id, n) => {
248
+ const out = [];
249
+ for (let v = n; v > 0 || out.length === 0; v = Math.floor(v / 256)) out.unshift(v % 256);
250
+ return el(id, Uint8Array.from(out));
251
+ };
252
+ var float = (id, n) => {
253
+ const b = new Uint8Array(8);
254
+ new DataView(b.buffer).setFloat64(0, n);
255
+ return el(id, b);
256
+ };
257
+ var str = (id, s) => el(id, new TextEncoder().encode(s));
258
+ function blockHeader(offset, flags) {
259
+ const b = new Uint8Array(4);
260
+ b[0] = 129;
261
+ new DataView(b.buffer).setInt16(1, offset);
262
+ b[3] = flags;
263
+ return b;
264
+ }
265
+ function muxWebm(options) {
266
+ const { codec, width, height, frames, fps } = options;
267
+ if (!frames.length) throw new Error("A WebM needs at least one frame.");
268
+ if (!frames[0].key) throw new Error("A WebM's first frame must be a keyframe.");
269
+ const alpha = frames.some((f) => f.alpha);
270
+ const ms = (us) => Math.round(us / 1e3);
271
+ const last = frames[frames.length - 1];
272
+ const frameUs = fps ? 1e6 / fps : frames.length > 1 ? (last.timestamp - frames[0].timestamp) / (frames.length - 1) : 1e6 / 30;
273
+ const durationUs = options.duration ?? last.timestamp + frameUs;
274
+ const header = el(
275
+ ID.EBML,
276
+ uint(ID.EBMLVersion, 1),
277
+ uint(ID.EBMLReadVersion, 1),
278
+ uint(ID.EBMLMaxIDLength, 4),
279
+ uint(ID.EBMLMaxSizeLength, 8),
280
+ str(ID.DocType, "webm"),
281
+ // BlockAdditions came in version 4.
282
+ uint(ID.DocTypeVersion, alpha ? 4 : 2),
283
+ uint(ID.DocTypeReadVersion, 2)
284
+ );
285
+ const info = el(
286
+ ID.Info,
287
+ uint(ID.TimestampScale, SCALE_NS),
288
+ float(ID.Duration, durationUs / 1e3),
289
+ str(ID.MuxingApp, "flair"),
290
+ str(ID.WritingApp, options.app ?? "flair")
291
+ );
292
+ const tracks = el(
293
+ ID.Tracks,
294
+ el(
295
+ ID.TrackEntry,
296
+ uint(ID.TrackNumber, 1),
297
+ uint(ID.TrackUID, 1),
298
+ uint(ID.TrackType, 1),
299
+ // video
300
+ uint(ID.FlagLacing, 0),
301
+ str(ID.CodecID, codec === "vp9" ? "V_VP9" : "V_VP8"),
302
+ ...fps ? [uint(ID.DefaultDuration, Math.round(1e9 / fps))] : [],
303
+ ...alpha ? [uint(ID.MaxBlockAdditionID, 1)] : [],
304
+ el(ID.Video, uint(ID.PixelWidth, width), uint(ID.PixelHeight, height), ...alpha ? [uint(ID.AlphaMode, 1)] : [])
305
+ )
306
+ );
307
+ const clusters = [];
308
+ let blocks = [];
309
+ let clusterMs = 0;
310
+ let previousMs = 0;
311
+ const close = () => {
312
+ if (blocks.length) clusters.push(el(ID.Cluster, uint(ID.Timestamp, clusterMs), ...blocks));
313
+ blocks = [];
314
+ };
315
+ frames.forEach((frame, i) => {
316
+ const at = ms(frame.timestamp);
317
+ if (i === 0 || frame.key || at - clusterMs > MAX_OFFSET) {
318
+ close();
319
+ clusterMs = at;
320
+ }
321
+ const offset = at - clusterMs;
322
+ if (frame.alpha) {
323
+ blocks.push(
324
+ el(
325
+ ID.BlockGroup,
326
+ el(ID.Block, blockHeader(offset, 0), frame.data),
327
+ el(ID.BlockAdditions, el(ID.BlockMore, uint(ID.BlockAddID, 1), el(ID.BlockAdditional, frame.alpha))),
328
+ ...frame.key ? [] : [el(ID.ReferenceBlock, signed(previousMs - at))]
329
+ )
330
+ );
331
+ } else {
332
+ blocks.push(el(ID.SimpleBlock, blockHeader(offset, frame.key ? 128 : 0), frame.data));
333
+ }
334
+ previousMs = at;
335
+ });
336
+ close();
337
+ const segment = el(ID.Segment, info, tracks, ...clusters);
338
+ return concat([header, segment]);
339
+ }
340
+ function signed(n) {
341
+ for (let len = 1; len <= 8; len++) {
342
+ if (n >= -(2 ** (8 * len - 1)) && n < 2 ** (8 * len - 1)) {
343
+ const out = new Uint8Array(len);
344
+ let v = n < 0 ? 2 ** (8 * len) + n : n;
345
+ for (let i = len - 1; i >= 0; i--) {
346
+ out[i] = v % 256;
347
+ v = Math.floor(v / 256);
348
+ }
349
+ return out;
350
+ }
351
+ }
352
+ throw new RangeError(`${n} is out of range.`);
353
+ }
354
+
355
+ // src/export/zip.ts
356
+ var table;
357
+ function crc32(data) {
358
+ table ??= Uint32Array.from({ length: 256 }, (_, n) => {
359
+ let c = n;
360
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
361
+ return c >>> 0;
362
+ });
363
+ let crc = 4294967295;
364
+ for (let i = 0; i < data.length; i++) crc = table[(crc ^ data[i]) & 255] ^ crc >>> 8;
365
+ return (crc ^ 4294967295) >>> 0;
366
+ }
367
+ var DOS_DATE = 0 << 9 | 1 << 5 | 1;
368
+ function zipStore(entries) {
369
+ const encoder = new TextEncoder();
370
+ const locals = [];
371
+ const centrals = [];
372
+ let offset = 0;
373
+ for (const { name, data } of entries) {
374
+ const nameBytes = encoder.encode(name);
375
+ const crc = crc32(data);
376
+ const local = new Uint8Array(30 + nameBytes.length);
377
+ const l = new DataView(local.buffer);
378
+ l.setUint32(0, 67324752, true);
379
+ l.setUint16(4, 20, true);
380
+ l.setUint16(6, 2048, true);
381
+ l.setUint16(8, 0, true);
382
+ l.setUint16(10, 0, true);
383
+ l.setUint16(12, DOS_DATE, true);
384
+ l.setUint32(14, crc, true);
385
+ l.setUint32(18, data.length, true);
386
+ l.setUint32(22, data.length, true);
387
+ l.setUint16(26, nameBytes.length, true);
388
+ local.set(nameBytes, 30);
389
+ const central = new Uint8Array(46 + nameBytes.length);
390
+ const c = new DataView(central.buffer);
391
+ c.setUint32(0, 33639248, true);
392
+ c.setUint16(4, 20, true);
393
+ c.setUint16(6, 20, true);
394
+ c.setUint16(8, 2048, true);
395
+ c.setUint16(10, 0, true);
396
+ c.setUint16(12, 0, true);
397
+ c.setUint16(14, DOS_DATE, true);
398
+ c.setUint32(16, crc, true);
399
+ c.setUint32(20, data.length, true);
400
+ c.setUint32(24, data.length, true);
401
+ c.setUint16(28, nameBytes.length, true);
402
+ c.setUint32(42, offset, true);
403
+ central.set(nameBytes, 46);
404
+ locals.push(local, data);
405
+ centrals.push(central);
406
+ offset += local.length + data.length;
407
+ }
408
+ const centralSize = centrals.reduce((n, c) => n + c.length, 0);
409
+ const end = new Uint8Array(22);
410
+ const e = new DataView(end.buffer);
411
+ e.setUint32(0, 101010256, true);
412
+ e.setUint16(8, entries.length, true);
413
+ e.setUint16(10, entries.length, true);
414
+ e.setUint32(12, centralSize, true);
415
+ e.setUint32(16, offset, true);
416
+ const out = new Uint8Array(offset + centralSize + 22);
417
+ let at = 0;
418
+ for (const part of [...locals, ...centrals, end]) {
419
+ out.set(part, at);
420
+ at += part.length;
421
+ }
422
+ return out;
423
+ }
424
+
425
+ // src/embed/record.ts
426
+ var DEFAULT_SECONDS = 4;
427
+ var yieldToBrowser = () => new Promise((resolve) => setTimeout(resolve, 0));
428
+ var formats = (list) => list.map((f) => `"${f}"`).join(", ").replace(/, ([^,]*)$/, " or $1");
429
+ var AVC_LEVELS = [
430
+ [31, 3600, 108e3],
431
+ // 3.1
432
+ [40, 8192, 245760],
433
+ // 4.0
434
+ [42, 8704, 522240],
435
+ // 4.2
436
+ [50, 22080, 589824],
437
+ // 5.0
438
+ [51, 36864, 983040],
439
+ // 5.1
440
+ [52, 36864, 2073600],
441
+ // 5.2
442
+ [60, 139264, 4177920]
443
+ // 6.0
444
+ ];
445
+ function avcCodecs(width, height, fps) {
446
+ const mbs = Math.ceil(width / 16) * Math.ceil(height / 16);
447
+ const level = (AVC_LEVELS.find(([, f, s]) => mbs <= f && mbs * fps <= s) ?? AVC_LEVELS[AVC_LEVELS.length - 1])[0];
448
+ const hex = level.toString(16).toUpperCase().padStart(2, "0");
449
+ return ["avc1.6400", "avc1.4D00", "avc1.42E0"].map((p) => p + hex);
450
+ }
451
+ async function supported(codecs, base) {
452
+ for (const codec of codecs) {
453
+ const config = { ...base, codec };
454
+ try {
455
+ const answer = await VideoEncoder.isConfigSupported(config);
456
+ if (answer.supported) return config;
457
+ } catch {
458
+ }
459
+ }
460
+ return void 0;
461
+ }
462
+ var hasWebCodecs = () => typeof VideoEncoder !== "undefined" && typeof VideoFrame !== "undefined";
463
+ async function videoConfig(format, width, height, fps, bitrate) {
464
+ const base = {
465
+ width,
466
+ height,
467
+ bitrate,
468
+ framerate: fps,
469
+ latencyMode: "quality",
470
+ ...format === "mp4" ? { avc: { format: "avc" } } : {}
471
+ };
472
+ return supported(format === "mp4" ? avcCodecs(width, height, fps) : ["vp09.00.10.08", "vp8"], base);
473
+ }
474
+ var resolveEffect = (effect) => {
475
+ const def = typeof effect === "string" ? getEffect(effect) : effect;
476
+ if (!def) throw new Error(`There is no effect "${String(effect)}" in the catalog; findEffects() lists them.`);
477
+ return def;
478
+ };
479
+ function positive(name, value, fallback) {
480
+ const n = value ?? fallback;
481
+ if (!Number.isFinite(n) || n <= 0) throw new Error(`${name} must be a number above 0 (it was ${String(value)}).`);
482
+ return n;
483
+ }
484
+ var even = (n) => Math.max(2, Math.ceil(n / 2) * 2);
485
+ async function recordingPlan(effect, options = {}) {
486
+ const def = resolveEffect(effect);
487
+ if (options.preset && !def.presets?.some((p) => p.name === options.preset)) {
488
+ const names = def.presets?.map((p) => `"${p.name}"`).join(", ");
489
+ throw new Error(`"${def.id}" has no preset "${options.preset}"${names ? `; it has ${names}` : ""}.`);
490
+ }
491
+ const fps = positive("fps", options.fps, 30);
492
+ const start = options.start ?? 0;
493
+ if (!Number.isFinite(start) || start < 0) throw new Error(`start must be 0 or more seconds (it was ${String(options.start)}).`);
494
+ const duration = positive("duration", options.duration, defaultDuration(def, options));
495
+ let width = Math.round(positive("width", options.width, 1280));
496
+ let height = Math.round(positive("height", options.height, 720));
497
+ const explicitAlpha = options.alpha === true;
498
+ let alpha = options.alpha ?? (def.kind === "shader" && Boolean(def.transparent));
499
+ let format = options.format;
500
+ if (format && !["mp4", "webm", "gif", "png"].includes(format)) throw new Error(`format is one of "mp4", "webm", "gif" or "png" (it was "${format}").`);
501
+ if (format === "mp4" && alpha) {
502
+ if (explicitAlpha) throw new Error(`An mp4 (H.264) cannot keep transparency; record ${formats(["webm", "png", "gif"])} to keep it, or pass alpha: false.`);
503
+ alpha = false;
504
+ }
505
+ let encoder;
506
+ if (format !== "gif" && format !== "png") {
507
+ if (!hasWebCodecs())
508
+ throw new Error(`This browser cannot encode video (it has no WebCodecs VideoEncoder), so it cannot record ${format ?? "a video"}; record ${formats(["gif", "png"])} instead.`);
509
+ const w = even(width);
510
+ const h = even(height);
511
+ const bitrate = Math.round(positive("bitrate", options.bitrate, w * h * fps * 0.15));
512
+ const tryFormat = (f) => videoConfig(f, w, h, fps, bitrate);
513
+ const choices = format ? [format] : alpha ? ["webm"] : ["mp4", "webm"];
514
+ for (const f of choices) {
515
+ encoder = await tryFormat(f);
516
+ if (encoder) {
517
+ format = f;
518
+ break;
519
+ }
520
+ }
521
+ if (!encoder && !format && alpha && !explicitAlpha) {
522
+ encoder = await tryFormat("mp4");
523
+ if (encoder) {
524
+ format = "mp4";
525
+ alpha = false;
526
+ }
527
+ }
528
+ if (!encoder) {
529
+ const asked = format ?? (alpha ? "webm" : "mp4");
530
+ const other = asked === "mp4" ? "webm" : "mp4";
531
+ if (alpha && asked === "webm") throw new Error(`This browser cannot encode VP8 or VP9, so it cannot record a webm with transparency; record ${formats(["png", "gif"])} to keep it, or pass alpha: false.`);
532
+ const otherWorks = format && await tryFormat(other);
533
+ const codec = asked === "mp4" ? "H.264" : "VP8 or VP9";
534
+ throw new Error(
535
+ format ? `This browser cannot encode ${codec} at ${w} \xD7 ${h}, so it cannot record ${asked}; record ${formats(otherWorks ? [other, "gif", "png"] : ["gif", "png"])} instead.` : `This browser cannot encode H.264, VP8 or VP9 at ${w} \xD7 ${h}; record ${formats(["gif", "png"])} instead.`
536
+ );
537
+ }
538
+ width = w;
539
+ height = h;
540
+ }
541
+ return { effect: def, format, width, height, fps, duration, start, frames: Math.max(1, Math.round(duration * fps)), alpha, ...encoder ? { encoder } : {} };
542
+ }
543
+ function defaultDuration(def, options) {
544
+ if (def.kind === "shader") return def.loop ?? DEFAULT_SECONDS;
545
+ const motion = renderVectorEffect(def, effectValues(def, options).values);
546
+ return (svgDuration(motion, options.steps ?? []) + (def.hold ?? 600)) / 1e3;
547
+ }
548
+ function effectValues(def, options) {
549
+ const values = effectDefaults(def, { ...options.preset ? { preset: options.preset } : {}, ...options.values ? { values: options.values } : {} });
550
+ if (!options.subject) return { values, live: {} };
551
+ const s = subjectValues(def, options.subject);
552
+ return { values: { ...values, ...s.values }, live: s.live };
553
+ }
554
+ function shaderSource(plan, values, live, options) {
555
+ const def = plan.effect;
556
+ const frames = new ShaderFrames({ source: def.source, values, width: plan.width, height: plan.height, alpha: plan.alpha || Boolean(def.transparent) });
557
+ const videos = [];
558
+ for (const [name, source] of Object.entries(live)) {
559
+ frames.setLiveSource(name, source);
560
+ if (typeof HTMLVideoElement !== "undefined" && source instanceof HTMLVideoElement) {
561
+ videos.push({ video: source, time: source.currentTime, paused: source.paused });
562
+ source.pause();
563
+ }
564
+ }
565
+ const script = options.pointer;
566
+ return {
567
+ async frame(time, index, out) {
568
+ for (const { video } of videos) await seekVideo(video, time);
569
+ await options.beforeFrame?.(time, index);
570
+ await frames.ready();
571
+ const pointer = typeof script === "function" ? script(time) : script;
572
+ frames.render(time, { frame: index, delta: 1 / plan.fps, ...pointer });
573
+ frames.readPixels(out);
574
+ },
575
+ dispose() {
576
+ frames.dispose();
577
+ for (const { video, time, paused } of videos) {
578
+ video.currentTime = time;
579
+ if (!paused) void video.play().catch(() => void 0);
580
+ }
581
+ }
582
+ };
583
+ }
584
+ function vectorSource(plan, values, options) {
585
+ const def = plan.effect;
586
+ const view = svgView(renderVectorEffect(def, values));
587
+ const host = document.createElement("div");
588
+ host.setAttribute("aria-hidden", "true");
589
+ host.style.cssText = `position:fixed;left:-100000px;top:0;width:${plan.width}px;height:${plan.height}px;pointer-events:none;`;
590
+ host.appendChild(view.element);
591
+ document.body.appendChild(host);
592
+ const steps = options.steps ?? [];
593
+ return {
594
+ async frame(time, index, out) {
595
+ await options.beforeFrame?.(time, index);
596
+ view.draw(time * 1e3, steps);
597
+ const canvas = await view.capture({ width: plan.width, height: plan.height });
598
+ if (!canvas) throw new Error(`"${def.id}" has no picture to record: its SVG does not parse.`);
599
+ out.set(canvas.getContext("2d").getImageData(0, 0, plan.width, plan.height).data);
600
+ },
601
+ dispose: () => host.remove()
602
+ };
603
+ }
604
+ function flatten(rgba, background) {
605
+ const [br, bg, bb] = background;
606
+ for (let i = 0; i < rgba.length; i += 4) {
607
+ const a = rgba[i + 3];
608
+ if (a === 255) continue;
609
+ const k = a / 255;
610
+ rgba[i] = rgba[i] * k + br * (1 - k);
611
+ rgba[i + 1] = rgba[i + 1] * k + bg * (1 - k);
612
+ rgba[i + 2] = rgba[i + 2] * k + bb * (1 - k);
613
+ rgba[i + 3] = 255;
614
+ }
615
+ }
616
+ async function gifEncoder(plan) {
617
+ const { GIFEncoder, quantize, applyPalette } = await import("gifenc");
618
+ const gif = GIFEncoder();
619
+ const delay = 1e3 / plan.fps;
620
+ return {
621
+ async add(rgba, i) {
622
+ const repeat = i === 0 ? { repeat: 0 } : {};
623
+ if (!plan.alpha) {
624
+ const palette2 = quantize(rgba, 256);
625
+ gif.writeFrame(applyPalette(rgba, palette2), plan.width, plan.height, { palette: palette2, delay, ...repeat });
626
+ return;
627
+ }
628
+ const palette = quantize(rgba, 256, { format: "rgba4444", oneBitAlpha: true });
629
+ const transparentIndex = palette.findIndex((c) => c[3] === 0);
630
+ gif.writeFrame(applyPalette(rgba, palette, "rgba4444"), plan.width, plan.height, {
631
+ palette,
632
+ delay,
633
+ dispose: 2,
634
+ ...transparentIndex >= 0 ? { transparent: true, transparentIndex } : {},
635
+ ...repeat
636
+ });
637
+ },
638
+ async finish() {
639
+ gif.finish();
640
+ return new Blob([gif.bytes()], { type: "image/gif" });
641
+ },
642
+ close() {
643
+ }
644
+ };
645
+ }
646
+ function pngEncoder(plan) {
647
+ const canvas = document.createElement("canvas");
648
+ canvas.width = plan.width;
649
+ canvas.height = plan.height;
650
+ const context = canvas.getContext("2d");
651
+ if (!context) throw new Error("This browser cannot draw on a canvas, so it cannot make PNG frames.");
652
+ const entries = [];
653
+ const digits = Math.max(4, String(plan.frames).length);
654
+ return {
655
+ async add(rgba, i) {
656
+ context.putImageData(new ImageData(rgba, plan.width, plan.height), 0, 0);
657
+ const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
658
+ if (!blob) throw new Error("The browser could not encode a PNG frame.");
659
+ entries.push({ name: `frame-${String(i + 1).padStart(digits, "0")}.png`, data: new Uint8Array(await blob.arrayBuffer()) });
660
+ },
661
+ async finish() {
662
+ return new Blob([zipStore(entries)], { type: "application/zip" });
663
+ },
664
+ close() {
665
+ }
666
+ };
667
+ }
668
+ var bytesOf = (source) => source instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && source instanceof SharedArrayBuffer ? new Uint8Array(source.slice(0)) : new Uint8Array(source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength));
669
+ function collector(config, fail) {
670
+ const chunks = [];
671
+ const out = { chunks, description: void 0, encoder: void 0 };
672
+ out.encoder = new VideoEncoder({
673
+ output(chunk, metadata) {
674
+ const data = new Uint8Array(chunk.byteLength);
675
+ chunk.copyTo(data);
676
+ const desc = metadata?.decoderConfig?.description;
677
+ if (desc && !out.description) out.description = bytesOf(desc);
678
+ chunks.push({ data, timestamp: chunk.timestamp, ...chunk.duration ? { duration: chunk.duration } : {}, key: chunk.type === "key" });
679
+ },
680
+ error: (e) => fail(new Error(`The video encoder failed: ${e.message}`, { cause: e }))
681
+ });
682
+ out.encoder.configure(config);
683
+ return out;
684
+ }
685
+ function videoEncoder(plan) {
686
+ const config = plan.encoder;
687
+ let failure;
688
+ const fail = (e) => failure ??= e;
689
+ const color = collector(config, fail);
690
+ const alpha = plan.alpha ? collector(config, fail) : void 0;
691
+ const encoders = [color.encoder, ...alpha ? [alpha.encoder] : []];
692
+ const frameUs = 1e6 / plan.fps;
693
+ const keyEvery = Math.max(1, Math.round(plan.fps * 2));
694
+ const { width, height } = plan;
695
+ const plane = alpha ? new Uint8Array(width * height * 1.5).fill(128) : void 0;
696
+ const check = () => {
697
+ if (failure) throw failure;
698
+ };
699
+ const encode = (encoder, frame, key) => {
700
+ try {
701
+ encoder.encode(frame, { keyFrame: key });
702
+ } finally {
703
+ frame.close();
704
+ }
705
+ };
706
+ return {
707
+ async add(rgba, i) {
708
+ check();
709
+ const timing = { codedWidth: width, codedHeight: height, timestamp: Math.round(i * frameUs), duration: Math.round(frameUs) };
710
+ const key = i % keyEvery === 0;
711
+ encode(color.encoder, new VideoFrame(rgba, { format: "RGBX", ...timing }), key);
712
+ if (alpha && plane) {
713
+ for (let p = 0, q = 3; p < width * height; p++, q += 4) plane[p] = rgba[q];
714
+ encode(alpha.encoder, new VideoFrame(plane, { format: "I420", ...timing }), key);
715
+ }
716
+ while (encoders.some((e) => e.encodeQueueSize > 4) && !failure) await new Promise((resolve) => setTimeout(resolve, 1));
717
+ },
718
+ async finish() {
719
+ await Promise.all(encoders.map((e) => e.flush()));
720
+ check();
721
+ for (const e of encoders) e.close();
722
+ const byTime = (a, b) => a.timestamp - b.timestamp;
723
+ const chunks = color.chunks.sort(byTime);
724
+ if (plan.format === "mp4") {
725
+ if (!color.description) throw new Error("The H.264 encoder gave no decoder configuration (avcC), so the mp4 cannot be written.");
726
+ const file2 = muxMp4({ width, height, avcC: color.description, samples: chunks });
727
+ return new Blob([file2], { type: "video/mp4" });
728
+ }
729
+ const alphas = new Map(alpha?.chunks.map((c) => [c.timestamp, c.data]));
730
+ const frames = chunks.map((c) => {
731
+ const a = alphas.get(c.timestamp);
732
+ if (alpha && !a) throw new Error(`The alpha encoder dropped the frame at ${c.timestamp / 1e6} s, so the webm cannot keep transparency; record "png" or "gif" instead.`);
733
+ return { ...c, ...a ? { alpha: a } : {} };
734
+ });
735
+ const codec = config.codec.startsWith("vp8") ? "vp8" : "vp9";
736
+ const file = muxWebm({ codec, width, height, frames, duration: plan.frames * frameUs, fps: plan.fps });
737
+ return new Blob([file], { type: "video/webm" });
738
+ },
739
+ close() {
740
+ for (const e of encoders) if (e.state !== "closed") e.close();
741
+ }
742
+ };
743
+ }
744
+ async function recordEffect(effect, options = {}) {
745
+ const plan = await recordingPlan(effect, options);
746
+ const { signal, onProgress } = options;
747
+ const { values, live } = effectValues(plan.effect, options);
748
+ const background = (parseColor(options.background ?? (plan.effect.kind === "shader" ? "#000000" : "#ffffff")) ?? [0, 0, 0]).map((c) => Math.round(c * 255));
749
+ const source = plan.effect.kind === "shader" ? shaderSource(plan, values, live, options) : vectorSource(plan, values, options);
750
+ let encoder;
751
+ try {
752
+ encoder = plan.format === "gif" ? await gifEncoder(plan) : plan.format === "png" ? pngEncoder(plan) : videoEncoder(plan);
753
+ const rgba = new Uint8ClampedArray(plan.width * plan.height * 4);
754
+ for (let i = 0; i < plan.frames; i++) {
755
+ if (signal?.aborted) throw new DOMException("Cancelled", "AbortError");
756
+ await source.frame(plan.start + i / plan.fps, i, rgba);
757
+ if (!plan.alpha) flatten(rgba, background);
758
+ await encoder.add(rgba, i);
759
+ onProgress?.((i + 1) / plan.frames);
760
+ if (i % 2 === 1) await yieldToBrowser();
761
+ }
762
+ if (signal?.aborted) throw new DOMException("Cancelled", "AbortError");
763
+ return await encoder.finish();
764
+ } finally {
765
+ encoder?.close();
766
+ source.dispose();
767
+ }
768
+ }
769
+
770
+ // src/embed/index.ts
771
+ var kindOf = (subject) => Object.keys(subject).find((k) => k !== "font" && k !== "weight");
772
+ function svgUrl(svg) {
773
+ const markup = svg.trim();
774
+ if (!markup.startsWith("<")) return svg;
775
+ const withNs = /\sxmlns\s*=/.test(markup.slice(0, markup.indexOf(">") + 1)) ? markup : markup.replace(/^<svg/i, '<svg xmlns="http://www.w3.org/2000/svg"');
776
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(withNs)}`;
777
+ }
778
+ function pick(slots, wanted, name, effect, what) {
779
+ const slot = name ? slots.find((s) => s.name === name) : slots.find(wanted);
780
+ if (!slot) {
781
+ const names = slots.map((s) => s.name).join(", ");
782
+ throw new Error(
783
+ name ? `"${effect.id}" has no control "${name}" for ${what}${names ? `; it has ${names}` : ""}.` : `"${effect.id}" takes no ${what}; see effectInputs(effect).accepts, or findEffects({ accepts: [...] }) for effects that do.`
784
+ );
785
+ }
786
+ return slot;
787
+ }
788
+ function subjectValues(effect, subject, slot) {
789
+ const out = { values: {}, live: {} };
790
+ const inputs = effectInputs(effect);
791
+ const used = /* @__PURE__ */ new Set();
792
+ for (const s of Array.isArray(subject) ? subject : [subject]) {
793
+ const kind = kindOf(s);
794
+ const free = (slots) => slots.filter((x) => !used.has(x.name));
795
+ if (kind === "path" || kind === "text" && effect.kind === "vector") {
796
+ const holds = kind === "path" ? "path" : "copy";
797
+ const text = pick(free(inputs.texts), (t) => t.holds === holds, slot, effect, kind === "path" ? "path data" : "text");
798
+ out.values[text.name] = kind === "path" ? s.path : s.text;
799
+ used.add(text.name);
800
+ continue;
801
+ }
802
+ if (effect.kind === "vector")
803
+ throw new Error(`"${effect.id}" is a vector effect: it draws its own picture and takes words or path data (${inputs.accepts.join(", ") || "nothing"}), not ${{ svg: "an SVG", image: "an image", video: "a video" }[kind]}.`);
804
+ const wanted = (x) => kind === "text" ? x.wants !== "picture" : kind === "video" ? x.wants === "picture" : true;
805
+ const image = pick(free(inputs.images), wanted, slot, effect, kind === "text" ? "text" : `${kind === "svg" ? "SVG" : kind}`);
806
+ used.add(image.name);
807
+ if (kind === "video") {
808
+ if (!inputs.accepts.includes("video")) throw new Error(`"${effect.id}" is not made for video (video: "${inputs.video}"); give it a picture.`);
809
+ out.live[image.name] = s.video;
810
+ } else if (kind === "text") {
811
+ const t = s;
812
+ out.values[image.name] = textImage(t.text, { ...t.font ? { font: t.font } : {}, ...t.weight !== void 0 ? { weight: t.weight } : {} });
813
+ } else out.values[image.name] = kind === "svg" ? svgUrl(s.svg) : s.image;
814
+ }
815
+ return out;
816
+ }
817
+ function mountEffect(host, effect, options = {}) {
818
+ const def = typeof effect === "string" ? getEffect(effect) : effect;
819
+ if (!def) throw new Error(`There is no effect "${String(effect)}" in the catalog; findEffects() lists them.`);
820
+ let values = { ...effectDefaults(def, { ...options.preset ? { preset: options.preset } : {}, ...options.values ? { values: options.values } : {} }) };
821
+ let live = {};
822
+ const apply = (subject) => {
823
+ if (!subject) return;
824
+ const s = subjectValues(def, subject);
825
+ values = { ...values, ...s.values };
826
+ live = s.live;
827
+ };
828
+ apply(options.subject);
829
+ if (def.kind === "shader") {
830
+ const renderer = new ShaderRenderer(host, {
831
+ alpha: Boolean(def.transparent),
832
+ autoplay: options.autoplay ?? true,
833
+ pauseWhenHidden: options.pauseWhenHidden ?? true,
834
+ ...options.className ? { className: options.className } : {}
835
+ });
836
+ renderer.setSource(def.source);
837
+ renderer.setValues(values);
838
+ const setLive = (previous) => {
839
+ for (const name of Object.keys(previous)) if (!(name in live)) renderer.setLiveSource(name, null);
840
+ for (const [name, source] of Object.entries(live)) renderer.setLiveSource(name, source);
841
+ };
842
+ setLive({});
843
+ return {
844
+ effect: def,
845
+ element: renderer.canvas,
846
+ renderer,
847
+ setSubject(subject) {
848
+ const before = live;
849
+ apply(subject);
850
+ renderer.setValues(values);
851
+ setLive(before);
852
+ },
853
+ setValues(next) {
854
+ values = { ...values, ...next };
855
+ renderer.setValues(values);
856
+ },
857
+ play: () => renderer.play(),
858
+ pause: () => renderer.pause(),
859
+ dispose: () => renderer.dispose()
860
+ };
861
+ }
862
+ let player;
863
+ const build = () => {
864
+ player?.dispose();
865
+ player = new SvgPlayer(host, renderVectorEffect(def, values), {
866
+ hold: def.hold,
867
+ ...def.trigger ? { trigger: def.trigger } : {},
868
+ autoplay: options.autoplay ?? true,
869
+ pauseWhenHidden: options.pauseWhenHidden ?? true
870
+ });
871
+ if (options.className) player.view.element.className = options.className;
872
+ };
873
+ build();
874
+ return {
875
+ effect: def,
876
+ get element() {
877
+ return player.view.element;
878
+ },
879
+ get player() {
880
+ return player;
881
+ },
882
+ setSubject(subject) {
883
+ apply(subject);
884
+ build();
885
+ },
886
+ setValues(next) {
887
+ values = { ...values, ...next };
888
+ build();
889
+ },
890
+ play: () => player.play(),
891
+ pause: () => player.pause(),
892
+ dispose: () => player.dispose()
893
+ };
894
+ }
895
+
896
+ export {
897
+ recordingPlan,
898
+ recordEffect,
899
+ subjectValues,
900
+ mountEffect
901
+ };
902
+ //# sourceMappingURL=chunk-UCZ43ARE.js.map