@bendyline/squisq 2.11.7 → 2.11.8

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 (38) hide show
  1. package/dist/{Doc-BkxT7G80.d.ts → Doc-CynsGqL4.d.ts} +208 -1
  2. package/dist/{ImageEditDoc-C8ZB54Vv.d.ts → ImageEditDoc-BVZe_5y4.d.ts} +1 -1
  3. package/dist/{chunk-SKKBRLLM.js → chunk-5FBI3ZW4.js} +7 -42
  4. package/dist/{chunk-YGGYYBJS.js → chunk-JJO6JFNZ.js} +1 -1
  5. package/dist/{chunk-SOIV7KDO.js → chunk-JM2QARY2.js} +42 -10
  6. package/dist/chunk-JSGIWW35.js +70 -0
  7. package/dist/{chunk-IYZTF4VO.js → chunk-KFZOCWPK.js} +2 -2
  8. package/dist/chunk-L7QZORLB.js +230 -0
  9. package/dist/chunk-NEWNNLWN.js +64 -0
  10. package/dist/{chunk-6ECMNUIP.js → chunk-OU5FYBOB.js} +170 -4
  11. package/dist/{chunk-3YNFMFNK.js → chunk-RMCUZDW5.js} +17 -8
  12. package/dist/{chunk-XJXBI3N5.js → chunk-XZVBR5BP.js} +1 -1
  13. package/dist/{chunk-GAZKTT4R.js → chunk-YNO6JV36.js} +96 -33
  14. package/dist/{chunk-EF353KGH.js → chunk-ZEONJCP3.js} +1 -1
  15. package/dist/doc/index.d.ts +5 -5
  16. package/dist/doc/index.js +22 -20
  17. package/dist/fence/index.d.ts +1 -1
  18. package/dist/generate/index.d.ts +1 -1
  19. package/dist/imageEdit/index.d.ts +47 -4
  20. package/dist/imageEdit/index.js +5 -1
  21. package/dist/index.d.ts +5 -5
  22. package/dist/index.js +48 -39
  23. package/dist/jsonForm/index.d.ts +1 -1
  24. package/dist/jsonForm/index.js +4 -2
  25. package/dist/markdown/index.js +3 -3
  26. package/dist/{materializePageSection-Dp2aHFbc.d.ts → materializePageSection-CAJoa8fh.d.ts} +1 -1
  27. package/dist/mediaEdit/index.d.ts +467 -0
  28. package/dist/mediaEdit/index.js +990 -0
  29. package/dist/narration/index.d.ts +20 -12
  30. package/dist/narration/index.js +6 -4
  31. package/dist/schemas/index.d.ts +4 -4
  32. package/dist/schemas/index.js +7 -3
  33. package/dist/{themeLibrary-BbSjnGmd.d.ts → themeLibrary-z7t3x5CU.d.ts} +1 -1
  34. package/dist/timing/index.d.ts +1 -1
  35. package/dist/transform/index.d.ts +2 -2
  36. package/dist/transform/index.js +6 -5
  37. package/package.json +6 -1
  38. package/dist/{chunk-CW24CH7I.js → chunk-HUXK7EOT.js} +3 -3
@@ -0,0 +1,230 @@
1
+ // src/mediaEdit/recipe.ts
2
+ var MEDIA_FX_ORDER = ["highpass", "denoise", "debreath", "loudness"];
3
+ var MEDIA_FX_SPECS = Object.freeze({
4
+ highpass: { defaultValue: 80, min: 20, max: 300 },
5
+ denoise: { defaultValue: 0.8, min: 0, max: 1 },
6
+ debreath: { defaultValue: -15, min: -40, max: 0 },
7
+ loudness: { defaultValue: -16, min: -31, max: -5 }
8
+ });
9
+ var MEDIA_EDIT_PARAMS = [
10
+ { key: "fx", htmlSuffix: "fx" },
11
+ { key: "cuts", htmlSuffix: "cuts" },
12
+ { key: "gain", htmlSuffix: "gain" },
13
+ { key: "fadeIn", htmlSuffix: "fade-in" },
14
+ { key: "fadeOut", htmlSuffix: "fade-out" },
15
+ { key: "crop", htmlSuffix: "crop" },
16
+ { key: "group", htmlSuffix: "group" }
17
+ ];
18
+ function mediaEditHtmlAttribute(kind, key) {
19
+ const param = MEDIA_EDIT_PARAMS.find((p) => p.key === key);
20
+ return `data-squisq-${kind}-${param?.htmlSuffix ?? key}`;
21
+ }
22
+ function formatMediaNumber(value, digits = 3) {
23
+ const rounded = Number(value.toFixed(digits));
24
+ return Object.is(rounded, -0) ? "0" : String(rounded);
25
+ }
26
+ function finiteNumber(raw) {
27
+ if (raw == null) return null;
28
+ const trimmed = raw.trim();
29
+ if (!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(trimmed)) return null;
30
+ const n = Number(trimmed);
31
+ return Number.isFinite(n) ? n : null;
32
+ }
33
+ function clamp(value, min, max) {
34
+ return Math.min(max, Math.max(min, value));
35
+ }
36
+ function parseSeconds(raw) {
37
+ const trimmed = raw.trim();
38
+ const bare = finiteNumber(trimmed);
39
+ if (bare != null) return bare >= 0 ? bare : null;
40
+ const ms = trimmed.match(/^(\d+(?:\.\d+)?)ms$/i);
41
+ if (ms) return Number(ms[1]) / 1e3;
42
+ const mmss = trimmed.match(/^(\d+):([0-5]?\d)(?:\.(\d+))?$/);
43
+ if (mmss) return Number(mmss[1]) * 60 + Number(mmss[2]) + Number(`0.${mmss[3] ?? "0"}`);
44
+ return null;
45
+ }
46
+ function isFxOpId(name) {
47
+ return MEDIA_FX_ORDER.includes(name);
48
+ }
49
+ function parseMediaFx(raw) {
50
+ if (raw == null) return null;
51
+ const tokens = raw.split(/[\s,]+/).filter(Boolean);
52
+ if (tokens.length === 0) return null;
53
+ const byId = /* @__PURE__ */ new Map();
54
+ const unknown = [];
55
+ for (const token of tokens) {
56
+ const colon = token.indexOf(":");
57
+ const name = (colon === -1 ? token : token.slice(0, colon)).toLowerCase();
58
+ if (!isFxOpId(name)) {
59
+ unknown.push(token);
60
+ continue;
61
+ }
62
+ const spec = MEDIA_FX_SPECS[name];
63
+ let value = spec.defaultValue;
64
+ if (colon !== -1) {
65
+ value = finiteNumber(token.slice(colon + 1));
66
+ if (value != null && name === "debreath") value = -Math.abs(value);
67
+ }
68
+ if (value == null) {
69
+ unknown.push(token);
70
+ continue;
71
+ }
72
+ byId.set(name, clamp(value, spec.min, spec.max));
73
+ }
74
+ const ops = MEDIA_FX_ORDER.filter((id) => byId.has(id)).map((id) => ({
75
+ id,
76
+ value: byId.get(id)
77
+ }));
78
+ return { ops, unknown };
79
+ }
80
+ function serializeMediaFx(chain) {
81
+ return [
82
+ ...chain.ops.map((op) => `${op.id}:${formatMediaNumber(op.value)}`),
83
+ ...chain.unknown
84
+ ].join(" ");
85
+ }
86
+ function hasRenderableFx(chain) {
87
+ return chain != null && chain.ops.length > 0;
88
+ }
89
+ var CUT_EPSILON = 1e-3;
90
+ function normalizeMediaCuts(cuts, windowStart = 0, windowEnd = Number.POSITIVE_INFINITY) {
91
+ const clamped = cuts.filter((c) => Number.isFinite(c.start) && Number.isFinite(c.end)).map((c) => ({
92
+ start: Math.max(windowStart, Math.min(c.start, c.end)),
93
+ end: Math.min(windowEnd, Math.max(c.start, c.end))
94
+ })).filter((c) => c.end - c.start >= CUT_EPSILON).sort((a, b) => a.start - b.start);
95
+ const merged = [];
96
+ for (const cut of clamped) {
97
+ const last = merged[merged.length - 1];
98
+ if (last && cut.start <= last.end + CUT_EPSILON) {
99
+ last.end = Math.max(last.end, cut.end);
100
+ } else {
101
+ merged.push({ ...cut });
102
+ }
103
+ }
104
+ return merged;
105
+ }
106
+ function parseMediaCuts(raw) {
107
+ if (raw == null) return null;
108
+ const cuts = [];
109
+ for (const token of raw.split(/[\s,]+/).filter(Boolean)) {
110
+ const dash = token.indexOf("-", 1);
111
+ if (dash === -1) continue;
112
+ const start = parseSeconds(token.slice(0, dash));
113
+ const end = parseSeconds(token.slice(dash + 1));
114
+ if (start == null || end == null || end <= start) continue;
115
+ cuts.push({ start, end });
116
+ }
117
+ const normalized = normalizeMediaCuts(cuts);
118
+ return normalized.length > 0 ? normalized : null;
119
+ }
120
+ function serializeMediaCuts(cuts) {
121
+ return normalizeMediaCuts(cuts).map((c) => `${formatMediaNumber(c.start)}-${formatMediaNumber(c.end)}`).join(" ");
122
+ }
123
+ var MIN_CROP_EDGE = 0.02;
124
+ function parseMediaCrop(raw) {
125
+ if (raw == null) return null;
126
+ const parts = raw.split(/[\s,]+/).filter(Boolean).map((p) => finiteNumber(p));
127
+ if (parts.length !== 4 || parts.some((p) => p == null)) return null;
128
+ return normalizeMediaCrop({
129
+ x: parts[0],
130
+ y: parts[1],
131
+ w: parts[2],
132
+ h: parts[3]
133
+ });
134
+ }
135
+ function normalizeMediaCrop(crop) {
136
+ const x = clamp(crop.x, 0, 1 - MIN_CROP_EDGE);
137
+ const y = clamp(crop.y, 0, 1 - MIN_CROP_EDGE);
138
+ const w = clamp(crop.w, MIN_CROP_EDGE, 1 - x);
139
+ const h = clamp(crop.h, MIN_CROP_EDGE, 1 - y);
140
+ const full = x < 1e-4 && y < 1e-4 && w > 1 - 1e-4 && h > 1 - 1e-4;
141
+ return full ? null : { x, y, w, h };
142
+ }
143
+ function serializeMediaCrop(crop) {
144
+ return [crop.x, crop.y, crop.w, crop.h].map((n) => formatMediaNumber(n, 4)).join(" ");
145
+ }
146
+ var GAIN_RANGE = { min: -60, max: 24 };
147
+ var GROUP_RE = /^[A-Za-z0-9_-]{1,64}$/;
148
+ function parseMediaEdits(values) {
149
+ const edits = {};
150
+ const fx = parseMediaFx(values.fx);
151
+ if (fx && (fx.ops.length > 0 || fx.unknown.length > 0)) edits.fx = fx;
152
+ const cuts = parseMediaCuts(values.cuts);
153
+ if (cuts) edits.cuts = cuts;
154
+ const gain = finiteNumber(values.gain);
155
+ if (gain != null && gain !== 0) edits.gain = clamp(gain, GAIN_RANGE.min, GAIN_RANGE.max);
156
+ const fadeIn = values.fadeIn == null ? null : parseSeconds(values.fadeIn);
157
+ if (fadeIn != null && fadeIn > 0) edits.fadeIn = fadeIn;
158
+ const fadeOut = values.fadeOut == null ? null : parseSeconds(values.fadeOut);
159
+ if (fadeOut != null && fadeOut > 0) edits.fadeOut = fadeOut;
160
+ const crop = parseMediaCrop(values.crop);
161
+ if (crop) edits.crop = crop;
162
+ const group = values.group?.trim();
163
+ if (group && GROUP_RE.test(group)) edits.group = group;
164
+ return Object.keys(edits).length > 0 ? edits : void 0;
165
+ }
166
+ function serializeMediaEdits(edits) {
167
+ const fx = edits?.fx && (edits.fx.ops.length > 0 || edits.fx.unknown.length > 0);
168
+ return {
169
+ fx: fx && edits?.fx ? serializeMediaFx(edits.fx) : null,
170
+ cuts: edits?.cuts && edits.cuts.length > 0 ? serializeMediaCuts(edits.cuts) : null,
171
+ gain: edits?.gain ? formatMediaNumber(edits.gain, 2) : null,
172
+ fadeIn: edits?.fadeIn ? formatMediaNumber(edits.fadeIn) : null,
173
+ fadeOut: edits?.fadeOut ? formatMediaNumber(edits.fadeOut) : null,
174
+ crop: edits?.crop ? serializeMediaCrop(edits.crop) : null,
175
+ group: edits?.group ?? null
176
+ };
177
+ }
178
+ function mediaEditValuesFromParams(params) {
179
+ const out = {};
180
+ for (const { key } of MEDIA_EDIT_PARAMS) {
181
+ if (params[key] != null) out[key] = params[key];
182
+ }
183
+ return out;
184
+ }
185
+ function mediaEditValuesFromAttributes(kind, attributes) {
186
+ const out = {};
187
+ for (const { key } of MEDIA_EDIT_PARAMS) {
188
+ const value = attributes[mediaEditHtmlAttribute(kind, key)];
189
+ if (value != null) out[key] = value;
190
+ }
191
+ return out;
192
+ }
193
+ function fnv1a64Hex(text) {
194
+ let h1 = 2166136261;
195
+ let h2 = 16777619 ^ 1540483477;
196
+ for (let i = 0; i < text.length; i++) {
197
+ const c = text.charCodeAt(i);
198
+ h1 = Math.imul(h1 ^ c, 16777619);
199
+ h2 = Math.imul(h2 ^ c, 1540483477);
200
+ h2 ^= h2 >>> 15;
201
+ }
202
+ return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0");
203
+ }
204
+ function mediaRenderKey(src, fx) {
205
+ const canonical = fx.ops.map((op) => `${op.id}:${formatMediaNumber(op.value)}`).join(" ");
206
+ return fnv1a64Hex(`${src}
207
+ ${canonical}`).slice(0, 12);
208
+ }
209
+
210
+ export {
211
+ MEDIA_FX_ORDER,
212
+ MEDIA_FX_SPECS,
213
+ MEDIA_EDIT_PARAMS,
214
+ mediaEditHtmlAttribute,
215
+ formatMediaNumber,
216
+ parseMediaFx,
217
+ serializeMediaFx,
218
+ hasRenderableFx,
219
+ normalizeMediaCuts,
220
+ parseMediaCuts,
221
+ serializeMediaCuts,
222
+ parseMediaCrop,
223
+ normalizeMediaCrop,
224
+ serializeMediaCrop,
225
+ parseMediaEdits,
226
+ serializeMediaEdits,
227
+ mediaEditValuesFromParams,
228
+ mediaEditValuesFromAttributes,
229
+ mediaRenderKey
230
+ };
@@ -0,0 +1,64 @@
1
+ // src/mediaEdit/dsp/biquad.ts
2
+ var BUTTERWORTH_Q = Math.SQRT1_2;
3
+ function createBiquadState() {
4
+ return { x1: 0, x2: 0, y1: 0, y2: 0 };
5
+ }
6
+ function omega(sampleRate, freqHz) {
7
+ const w0 = 2 * Math.PI * freqHz / sampleRate;
8
+ return { cosW0: Math.cos(w0), sinW0: Math.sin(w0) };
9
+ }
10
+ function highpassCoeffs(sampleRate, freqHz, q = BUTTERWORTH_Q) {
11
+ const { cosW0, sinW0 } = omega(sampleRate, freqHz);
12
+ const alpha = sinW0 / (2 * q);
13
+ const a0 = 1 + alpha;
14
+ return {
15
+ b0: (1 + cosW0) / 2 / a0,
16
+ b1: -(1 + cosW0) / a0,
17
+ b2: (1 + cosW0) / 2 / a0,
18
+ a1: -2 * cosW0 / a0,
19
+ a2: (1 - alpha) / a0
20
+ };
21
+ }
22
+ function lowpassCoeffs(sampleRate, freqHz, q = BUTTERWORTH_Q) {
23
+ const { cosW0, sinW0 } = omega(sampleRate, freqHz);
24
+ const alpha = sinW0 / (2 * q);
25
+ const a0 = 1 + alpha;
26
+ return {
27
+ b0: (1 - cosW0) / 2 / a0,
28
+ b1: (1 - cosW0) / a0,
29
+ b2: (1 - cosW0) / 2 / a0,
30
+ a1: -2 * cosW0 / a0,
31
+ a2: (1 - alpha) / a0
32
+ };
33
+ }
34
+ function biquadRun(coeffs, state, input, output) {
35
+ let { x1, x2, y1, y2 } = state;
36
+ const { b0, b1, b2, a1, a2 } = coeffs;
37
+ for (let i = 0; i < input.length; i++) {
38
+ const x0 = input[i];
39
+ const y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
40
+ output[i] = y0;
41
+ x2 = x1;
42
+ x1 = x0;
43
+ y2 = y1;
44
+ y1 = y0;
45
+ }
46
+ return { x1, x2, y1, y2 };
47
+ }
48
+ function createBiquadCascade(stages) {
49
+ const states = stages.map(() => createBiquadState());
50
+ return {
51
+ process(block) {
52
+ for (let s = 0; s < stages.length; s++) {
53
+ states[s] = biquadRun(stages[s], states[s], block, block);
54
+ }
55
+ }
56
+ };
57
+ }
58
+
59
+ export {
60
+ highpassCoeffs,
61
+ lowpassCoeffs,
62
+ biquadRun,
63
+ createBiquadCascade
64
+ };
@@ -216,11 +216,11 @@ async function revertToImageEditVersion(container, version, options = {}) {
216
216
  if (options.snapshotCurrent !== false) {
217
217
  const saveOptions = { stateFilename: options.stateFilename };
218
218
  if (options.doc !== void 0) saveOptions.doc = options.doc;
219
- const result = await saveImageEditVersion(container, saveOptions);
220
- if (!result.saved && result.reason !== "unchanged") {
219
+ const result2 = await saveImageEditVersion(container, saveOptions);
220
+ if (!result2.saved && result2.reason !== "unchanged") {
221
221
  return { reverted: false, snapshotted: null, reason: "snapshot-failed" };
222
222
  }
223
- snapshotted = result.version;
223
+ snapshotted = result2.version;
224
224
  }
225
225
  await writeImageEditDoc(container, doc, options.stateFilename);
226
226
  return { reverted: true, snapshotted };
@@ -482,6 +482,170 @@ function escapeText(s) {
482
482
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
483
483
  }
484
484
 
485
+ // src/imageEdit/animatedImage.ts
486
+ var MIN_FRAME_DELAY_MS = 10;
487
+ var CLAMPED_DELAY_MS = 100;
488
+ function inspectAnimatedImage(input) {
489
+ const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
490
+ return inspectGif(bytes) ?? inspectPng(bytes) ?? inspectWebp(bytes);
491
+ }
492
+ var ANIMATABLE_EXTENSIONS = /* @__PURE__ */ new Set(["gif", "webp", "apng"]);
493
+ var ANIMATABLE_MIME = /^image\/(?:gif|webp|apng)(?=$|[;,\s])/i;
494
+ function mayBeAnimatedImage(srcOrMime) {
495
+ const value = srcOrMime.trim();
496
+ if (!value) return false;
497
+ if (/^data:/i.test(value)) return ANIMATABLE_MIME.test(value.slice(5));
498
+ if (ANIMATABLE_MIME.test(value)) return true;
499
+ const path = value.split(/[?#]/, 1)[0] ?? "";
500
+ const dot = path.lastIndexOf(".");
501
+ if (dot < 0 || dot < path.lastIndexOf("/")) return false;
502
+ return ANIMATABLE_EXTENSIONS.has(path.slice(dot + 1).toLowerCase());
503
+ }
504
+ function inspectGif(b) {
505
+ if (b.length < 13) return null;
506
+ if (b[0] !== 71 || b[1] !== 73 || b[2] !== 70 || b[3] !== 56) return null;
507
+ if (b[4] !== 55 && b[4] !== 57 || b[5] !== 97) return null;
508
+ let pos = 13 + colorTableSize(b[10]);
509
+ let frames = 0;
510
+ let duration = 0;
511
+ let pendingDelayMs = 0;
512
+ let playCount = 1;
513
+ while (pos < b.length) {
514
+ const marker = b[pos++];
515
+ if (marker === 59) break;
516
+ if (marker === 33) {
517
+ if (pos >= b.length) break;
518
+ const label = b[pos++];
519
+ if (label === 249 && pos + 3 < b.length && b[pos] >= 4) {
520
+ pendingDelayMs = (b[pos + 2] | b[pos + 3] << 8) * 10;
521
+ } else if (label === 255 && b[pos] === 11 && pos + 12 <= b.length) {
522
+ const id = ascii(b, pos + 1, 11);
523
+ const sub = pos + 12;
524
+ if ((id === "NETSCAPE2.0" || id === "ANIMEXTS1.0") && sub + 3 < b.length && b[sub] >= 3 && b[sub + 1] === 1) {
525
+ const loops = b[sub + 2] | b[sub + 3] << 8;
526
+ playCount = loops === 0 ? 0 : loops + 1;
527
+ }
528
+ }
529
+ pos = skipSubBlocks(b, pos);
530
+ if (pos < 0) break;
531
+ continue;
532
+ }
533
+ if (marker === 44) {
534
+ if (pos + 9 > b.length) break;
535
+ const packed = b[pos + 8];
536
+ pos += 9 + colorTableSize(packed);
537
+ pos += 1;
538
+ frames++;
539
+ duration += playedDelay(pendingDelayMs);
540
+ pendingDelayMs = 0;
541
+ pos = skipSubBlocks(b, pos);
542
+ if (pos < 0) break;
543
+ continue;
544
+ }
545
+ break;
546
+ }
547
+ return result("gif", frames, duration, playCount);
548
+ }
549
+ function colorTableSize(packed) {
550
+ return packed & 128 ? 3 * (1 << (packed & 7) + 1) : 0;
551
+ }
552
+ function skipSubBlocks(b, start) {
553
+ let pos = start;
554
+ while (pos < b.length) {
555
+ const size = b[pos];
556
+ pos += 1;
557
+ if (size === 0) return pos;
558
+ pos += size;
559
+ }
560
+ return -1;
561
+ }
562
+ var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
563
+ function inspectPng(b) {
564
+ if (b.length < PNG_SIGNATURE.length) return null;
565
+ for (let i = 0; i < PNG_SIGNATURE.length; i++) {
566
+ if (b[i] !== PNG_SIGNATURE[i]) return null;
567
+ }
568
+ let pos = PNG_SIGNATURE.length;
569
+ let hasAnimationControl = false;
570
+ let frames = 1;
571
+ let playCount = 1;
572
+ let duration = 0;
573
+ while (pos + 8 <= b.length) {
574
+ const length = readU32BE(b, pos);
575
+ const type = ascii(b, pos + 4, 4);
576
+ const data = pos + 8;
577
+ if (type === "acTL" && length >= 8 && data + 8 <= b.length) {
578
+ hasAnimationControl = true;
579
+ frames = readU32BE(b, data);
580
+ playCount = readU32BE(b, data + 4);
581
+ } else if (type === "fcTL" && length >= 26 && data + 26 <= b.length) {
582
+ const num = readU16BE(b, data + 20);
583
+ const den = readU16BE(b, data + 22) || 100;
584
+ duration += playedDelay(num * 1e3 / den);
585
+ } else if (type === "IDAT" && !hasAnimationControl) {
586
+ break;
587
+ } else if (type === "IEND") {
588
+ break;
589
+ }
590
+ pos = data + length + 4;
591
+ }
592
+ return result("png", hasAnimationControl ? frames : 1, duration, playCount);
593
+ }
594
+ function inspectWebp(b) {
595
+ if (b.length < 12 || ascii(b, 0, 4) !== "RIFF" || ascii(b, 8, 4) !== "WEBP") return null;
596
+ let pos = 12;
597
+ let animationFlag = false;
598
+ let frames = 0;
599
+ let duration = 0;
600
+ let loopCount = null;
601
+ while (pos + 8 <= b.length) {
602
+ const type = ascii(b, pos, 4);
603
+ const size = readU32LE(b, pos + 4);
604
+ const data = pos + 8;
605
+ if (type === "VP8X" && size >= 10 && data < b.length) {
606
+ animationFlag = (b[data] & 2) !== 0;
607
+ if (!animationFlag) break;
608
+ } else if (type === "ANIM" && size >= 6 && data + 6 <= b.length) {
609
+ loopCount = b[data + 4] | b[data + 5] << 8;
610
+ } else if (type === "ANMF" && size >= 16 && data + 15 <= b.length) {
611
+ frames++;
612
+ duration += playedDelay(b[data + 12] | b[data + 13] << 8 | b[data + 14] << 16);
613
+ } else if ((type === "VP8 " || type === "VP8L") && !animationFlag) {
614
+ break;
615
+ }
616
+ pos = data + size + (size & 1);
617
+ }
618
+ const frameCount = animationFlag ? frames : 1;
619
+ return result("webp", frameCount, duration, loopCount ?? 0);
620
+ }
621
+ function result(format, frameCount, durationMs, playCount) {
622
+ const animated = frameCount > 1;
623
+ return {
624
+ format,
625
+ animated,
626
+ frameCount: Math.max(frameCount, 1),
627
+ durationMs: animated ? Math.round(durationMs) : 0,
628
+ playCount: animated ? playCount : 1
629
+ };
630
+ }
631
+ function playedDelay(ms) {
632
+ return ms <= MIN_FRAME_DELAY_MS ? CLAMPED_DELAY_MS : ms;
633
+ }
634
+ function ascii(b, start, length) {
635
+ let out = "";
636
+ for (let i = start; i < start + length && i < b.length; i++) out += String.fromCharCode(b[i]);
637
+ return out;
638
+ }
639
+ function readU16BE(b, pos) {
640
+ return b[pos] << 8 | b[pos + 1];
641
+ }
642
+ function readU32BE(b, pos) {
643
+ return (b[pos] << 24 | b[pos + 1] << 16 | b[pos + 2] << 8 | b[pos + 3]) >>> 0;
644
+ }
645
+ function readU32LE(b, pos) {
646
+ return (b[pos] | b[pos + 1] << 8 | b[pos + 2] << 16 | b[pos + 3] << 24) >>> 0;
647
+ }
648
+
485
649
  export {
486
650
  IMAGE_EDIT_STATE_FILENAME,
487
651
  IMAGE_EDIT_ASSETS_PREFIX,
@@ -507,5 +671,7 @@ export {
507
671
  coalesceImageEditVersions,
508
672
  ImageEditVersionManager,
509
673
  exportImageEditDoc,
510
- buildSvgString
674
+ buildSvgString,
675
+ inspectAnimatedImage,
676
+ mayBeAnimatedImage
511
677
  };
@@ -1,11 +1,3 @@
1
- import {
2
- anchorPoint,
3
- base64ToUtf8,
4
- connectorPath,
5
- lineStyleDasharray,
6
- shapePath,
7
- snapEndpoints
8
- } from "./chunk-PUS54YU6.js";
9
1
  import {
10
2
  detectAsciiDiagram,
11
3
  detectAsciiTimeline,
@@ -22,6 +14,14 @@ import {
22
14
  pickAutoTemplate,
23
15
  profileBlockContents
24
16
  } from "./chunk-C3NAY5YP.js";
17
+ import {
18
+ anchorPoint,
19
+ base64ToUtf8,
20
+ connectorPath,
21
+ lineStyleDasharray,
22
+ shapePath,
23
+ snapEndpoints
24
+ } from "./chunk-PUS54YU6.js";
25
25
  import {
26
26
  DEFAULT_THEME,
27
27
  FRONTMATTER_CUSTOM_TEMPLATES_KEY,
@@ -42,6 +42,11 @@ import {
42
42
  validateTheme,
43
43
  withAlpha
44
44
  } from "./chunk-Y4D6TDZT.js";
45
+ import {
46
+ mediaEditValuesFromAttributes,
47
+ mediaEditValuesFromParams,
48
+ parseMediaEdits
49
+ } from "./chunk-L7QZORLB.js";
45
50
  import {
46
51
  KNOWN_BLOCK_META_KEYS,
47
52
  TABLE_FED_TEMPLATES,
@@ -11133,6 +11138,8 @@ function toMediaClip(parsed, id) {
11133
11138
  if (clipEnd != null) clip.clipEnd = clipEnd;
11134
11139
  if (clip.lockToBlock === true) delete clip.clipEnd;
11135
11140
  if (params.spillover === "true" || params.spillover === "") clip.spillover = true;
11141
+ const edits = parseMediaEdits(mediaEditValuesFromParams(params));
11142
+ if (edits) clip.edits = edits;
11136
11143
  return clip;
11137
11144
  }
11138
11145
  function firstVideoElement(children) {
@@ -11195,6 +11202,8 @@ function htmlVideoClip(node, id) {
11195
11202
  const clipEnd = time(element.attributes["data-squisq-video-clip-end"]);
11196
11203
  if (clipStart != null) clip.clipStart = clipStart;
11197
11204
  if (!locked && clipEnd != null) clip.clipEnd = clipEnd;
11205
+ const edits = parseMediaEdits(mediaEditValuesFromAttributes("video", element.attributes));
11206
+ if (edits) clip.edits = edits;
11198
11207
  return clip;
11199
11208
  }
11200
11209
  function extractMediaFromContents(contents, makeId, blockId) {
@@ -2,7 +2,7 @@ import {
2
2
  assertMarkdownDocumentWithinLimits,
3
3
  parseMarkdown,
4
4
  toMdast
5
- } from "./chunk-CW24CH7I.js";
5
+ } from "./chunk-HUXK7EOT.js";
6
6
  import {
7
7
  formatFrontmatterYaml,
8
8
  getChildren,