@bendyline/squisq 2.11.6 → 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-UUI35INI.js → chunk-5FBI3ZW4.js} +7 -42
  4. package/dist/{chunk-BUJ5RV2P.js → chunk-JJO6JFNZ.js} +3 -2
  5. package/dist/{chunk-GRGLZF4A.js → chunk-JM2QARY2.js} +42 -10
  6. package/dist/chunk-JSGIWW35.js +70 -0
  7. package/dist/{chunk-J2IPSQKP.js → chunk-KFZOCWPK.js} +256 -44
  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-WAGJK36Q.js → chunk-RMCUZDW5.js} +30 -9
  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-764WHWKM.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 +36 -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
@@ -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,
@@ -10494,6 +10499,7 @@ function expandDocBlocks(blocks, options = {}) {
10494
10499
  blocksBySegment.get(segmentIndex).push({ block, originalIndex: index });
10495
10500
  });
10496
10501
  const expandedBlocks = new Array(blocks.length);
10502
+ const splitParts = /* @__PURE__ */ new Map();
10497
10503
  for (const [segmentIndex, segmentBlocks] of blocksBySegment) {
10498
10504
  const audioSegment = audioSegments[segmentIndex];
10499
10505
  if (!audioSegment) {
@@ -10655,6 +10661,7 @@ function expandDocBlocks(blocks, options = {}) {
10655
10661
  const partDuration = block.duration / numParts;
10656
10662
  if (partDuration >= MIN_TRANSITION_GAP) {
10657
10663
  block.duration = partDuration;
10664
+ const parts = [];
10658
10665
  for (let p = 1; p < numParts; p++) {
10659
10666
  const splitBlock = {
10660
10667
  id: `${block.id}-split-${p}`,
@@ -10668,13 +10675,23 @@ function expandDocBlocks(blocks, options = {}) {
10668
10675
  transition: { type: "dissolve", duration: 1 },
10669
10676
  template: block.template
10670
10677
  };
10671
- expandedBlocks.push(splitBlock);
10678
+ parts.push(splitBlock);
10672
10679
  }
10680
+ splitParts.set(block, parts);
10673
10681
  }
10674
10682
  }
10675
10683
  }
10676
10684
  }
10677
10685
  }
10686
+ if (splitParts.size > 0) {
10687
+ const sequenced = [];
10688
+ for (const block of expandedBlocks) {
10689
+ sequenced.push(block);
10690
+ const parts = block ? splitParts.get(block) : void 0;
10691
+ if (parts) sequenced.push(...parts);
10692
+ }
10693
+ expandedBlocks.splice(0, expandedBlocks.length, ...sequenced);
10694
+ }
10678
10695
  return mergeShortBlocks ? expandedBlocks.filter((block) => block && block.duration > 0) : expandedBlocks.filter(Boolean);
10679
10696
  }
10680
10697
  function getAvailableTemplates() {
@@ -11121,6 +11138,8 @@ function toMediaClip(parsed, id) {
11121
11138
  if (clipEnd != null) clip.clipEnd = clipEnd;
11122
11139
  if (clip.lockToBlock === true) delete clip.clipEnd;
11123
11140
  if (params.spillover === "true" || params.spillover === "") clip.spillover = true;
11141
+ const edits = parseMediaEdits(mediaEditValuesFromParams(params));
11142
+ if (edits) clip.edits = edits;
11124
11143
  return clip;
11125
11144
  }
11126
11145
  function firstVideoElement(children) {
@@ -11183,6 +11202,8 @@ function htmlVideoClip(node, id) {
11183
11202
  const clipEnd = time(element.attributes["data-squisq-video-clip-end"]);
11184
11203
  if (clipStart != null) clip.clipStart = clipStart;
11185
11204
  if (!locked && clipEnd != null) clip.clipEnd = clipEnd;
11205
+ const edits = parseMediaEdits(mediaEditValuesFromAttributes("video", element.attributes));
11206
+ if (edits) clip.edits = edits;
11186
11207
  return clip;
11187
11208
  }
11188
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,
@@ -1,3 +1,7 @@
1
+ import {
2
+ createMediaTimeMap
3
+ } from "./chunk-JSGIWW35.js";
4
+
1
5
  // src/schemas/Media.ts
2
6
  function flatten(blocks, out = []) {
3
7
  for (const b of blocks) {
@@ -6,17 +10,104 @@ function flatten(blocks, out = []) {
6
10
  }
7
11
  return out;
8
12
  }
13
+ function clipTimeMap(clip, sourceDuration) {
14
+ return createMediaTimeMap({
15
+ clipStart: clip.clipStart,
16
+ clipEnd: clip.clipEnd,
17
+ cuts: clip.edits?.cuts,
18
+ sourceDuration
19
+ });
20
+ }
9
21
  function clipLength(clip) {
10
22
  if (clip.clipEnd == null) return null;
11
- return Math.max(0, clip.clipEnd - (clip.clipStart ?? 0));
23
+ if (!clip.edits?.cuts?.length) return Math.max(0, clip.clipEnd - (clip.clipStart ?? 0));
24
+ return clipTimeMap(clip).playedDuration;
12
25
  }
13
26
  function intrinsicPlayedLength(clip, opts) {
14
27
  if (clip.clipEnd != null || clip.lockToBlock === true) return null;
15
28
  const intrinsic = opts?.intrinsicDuration?.(clip);
16
29
  if (intrinsic == null || !Number.isFinite(intrinsic) || intrinsic <= 0) return null;
17
- const played = intrinsic - (clip.clipStart ?? 0);
30
+ const played = clip.edits?.cuts?.length ? clipTimeMap(clip, intrinsic).playedDuration : intrinsic - (clip.clipStart ?? 0);
18
31
  return played > 0 ? played : null;
19
32
  }
33
+ function emitClip(out, clip, start, end, anchor, blockId, opts) {
34
+ const edits = clip.edits;
35
+ const processed = edits?.fx ? opts?.processedAudio?.(clip) : void 0;
36
+ const base = {
37
+ src: clip.src,
38
+ kind: clip.kind,
39
+ ...clip.placement ? { placement: clip.placement } : {},
40
+ ...clip.pipSize ? { pipSize: clip.pipSize } : {},
41
+ ...clip.pipShape ? { pipShape: clip.pipShape } : {},
42
+ ...clip.pipPosition ? { pipPosition: clip.pipPosition } : {},
43
+ ...clip.lockToBlock != null ? { lockToBlock: clip.lockToBlock } : {},
44
+ anchor,
45
+ ...blockId !== void 0 ? { blockId } : {},
46
+ ...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {},
47
+ ...edits?.gain != null ? { gain: edits.gain } : {},
48
+ ...clip.kind === "video" && edits?.crop ? { crop: edits.crop } : {}
49
+ };
50
+ if (processed && clip.kind === "audio") {
51
+ base.src = processed;
52
+ base.originalSrc = clip.src;
53
+ }
54
+ if (processed && clip.kind === "video") base.audioMuted = true;
55
+ const safeEnd = Math.max(start, end);
56
+ const pieces = [];
57
+ if (!edits?.cuts?.length) {
58
+ pieces.push({ absoluteStart: start, absoluteEnd: safeEnd, sourceIn: clip.clipStart ?? 0 });
59
+ } else {
60
+ let offset = 0;
61
+ for (const range of clipTimeMap(clip).keptRanges()) {
62
+ const segStart = start + offset;
63
+ if (segStart >= safeEnd) break;
64
+ const segEnd = Math.min(safeEnd, segStart + (range.end - range.start));
65
+ pieces.push({ absoluteStart: segStart, absoluteEnd: segEnd, sourceIn: range.start });
66
+ offset += range.end - range.start;
67
+ }
68
+ if (pieces.length === 0) {
69
+ pieces.push({ absoluteStart: start, absoluteEnd: start, sourceIn: clip.clipStart ?? 0 });
70
+ }
71
+ }
72
+ const count = pieces.length;
73
+ pieces.forEach((piece, index) => {
74
+ const entry = {
75
+ ...base,
76
+ id: count > 1 ? `${clip.id}#${index}` : clip.id,
77
+ ...piece,
78
+ ...index === 0 && edits?.fadeIn ? { fadeIn: edits.fadeIn } : {},
79
+ ...index === count - 1 && edits?.fadeOut ? { fadeOut: edits.fadeOut } : {},
80
+ ...count > 1 ? { segment: { groupId: clip.id, index, count } } : {}
81
+ };
82
+ out.push(entry);
83
+ if (processed && clip.kind === "video") out.push(companionAudio(entry, clip, processed));
84
+ });
85
+ }
86
+ function scheduleMediaClip(clip, start, end, anchor, blockId, opts) {
87
+ const out = [];
88
+ emitClip(out, clip, start, end, anchor, blockId, opts);
89
+ return out;
90
+ }
91
+ function companionAudio(entry, clip, processed) {
92
+ const companion = {
93
+ ...entry,
94
+ id: `${entry.id}~audio`,
95
+ kind: "audio",
96
+ src: processed,
97
+ originalSrc: clip.src,
98
+ derivedFrom: clip.id
99
+ };
100
+ delete companion.crop;
101
+ delete companion.audioMuted;
102
+ delete companion.placement;
103
+ delete companion.pipSize;
104
+ delete companion.pipShape;
105
+ delete companion.pipPosition;
106
+ if (entry.segment) {
107
+ companion.segment = { ...entry.segment, groupId: `${clip.id}~audio` };
108
+ }
109
+ return companion;
110
+ }
20
111
  function baseTimelineEnd(doc) {
21
112
  const blockEnd = flatten(doc.blocks).reduce(
22
113
  (max, b) => Math.max(max, b.startTime + b.duration),
@@ -40,22 +131,7 @@ function resolveMediaSchedule(doc, opts) {
40
131
  }
41
132
  const natLen = intrinsicPlayedLength(clip, opts);
42
133
  if (natLen != null) end = Math.min(end, start + natLen);
43
- out.push({
44
- id: clip.id,
45
- src: clip.src,
46
- kind: clip.kind,
47
- ...clip.placement ? { placement: clip.placement } : {},
48
- ...clip.pipSize ? { pipSize: clip.pipSize } : {},
49
- ...clip.pipShape ? { pipShape: clip.pipShape } : {},
50
- ...clip.pipPosition ? { pipPosition: clip.pipPosition } : {},
51
- ...clip.lockToBlock != null ? { lockToBlock: clip.lockToBlock } : {},
52
- absoluteStart: start,
53
- absoluteEnd: Math.max(start, end),
54
- sourceIn: clip.clipStart ?? 0,
55
- anchor: "block",
56
- blockId: block.id,
57
- ...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}
58
- });
134
+ emitClip(out, clip, start, end, "block", block.id, opts);
59
135
  }
60
136
  }
61
137
  for (const clip of doc.documentMedia ?? []) {
@@ -64,21 +140,7 @@ function resolveMediaSchedule(doc, opts) {
64
140
  let end = len != null ? start + len : docEnd;
65
141
  const natLen = intrinsicPlayedLength(clip, opts);
66
142
  if (natLen != null) end = Math.min(end, start + natLen);
67
- out.push({
68
- id: clip.id,
69
- src: clip.src,
70
- kind: clip.kind,
71
- ...clip.placement ? { placement: clip.placement } : {},
72
- ...clip.pipSize ? { pipSize: clip.pipSize } : {},
73
- ...clip.pipShape ? { pipShape: clip.pipShape } : {},
74
- ...clip.pipPosition ? { pipPosition: clip.pipPosition } : {},
75
- ...clip.lockToBlock != null ? { lockToBlock: clip.lockToBlock } : {},
76
- absoluteStart: start,
77
- absoluteEnd: Math.max(start, end),
78
- sourceIn: clip.clipStart ?? 0,
79
- anchor: "document",
80
- ...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}
81
- });
143
+ emitClip(out, clip, start, end, "document", void 0, opts);
82
144
  }
83
145
  return out;
84
146
  }
@@ -232,6 +294,7 @@ function defaultPageStyle(theme) {
232
294
  }
233
295
 
234
296
  export {
297
+ scheduleMediaClip,
235
298
  resolveMediaSchedule,
236
299
  getDocPlaybackDuration,
237
300
  defaultPageStyle
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  flattenRenderableBlocks,
3
3
  getBlockBodyText
4
- } from "./chunk-WAGJK36Q.js";
4
+ } from "./chunk-RMCUZDW5.js";
5
5
  import {
6
6
  estimateSpokenWordCount
7
7
  } from "./chunk-GHOPHGOK.js";
@@ -1,12 +1,12 @@
1
- import { bb as TemplateBlock, bc as TemplateContext, a3 as Layer, w as CustomTemplateDefinition, be as TemplateRegistry, bh as Theme, bM as ViewportConfig, aL as PersistentLayerConfig, P as DocBlock, bN as ViewportOrientation, aK as PersistentLayer, bu as TitleBlockInput, b0 as SectionHeaderInput, j as BigTextInput, u as ContentBlockInput, b6 as StatHighlightInput, aW as QuoteBlockInput, V as FactCardInput, bC as TwoColumnInput, H as DateEventInput, a1 as ImageWithCaptionInput, a6 as LeftFeatureInput, aZ as RightFeatureInput, aa as MapBlockInput, b5 as StartBlockConfig, Y as FullBleedQuoteInput, a9 as ListBlockInput, aP as PhotoGridInput, J as DefinitionCardInput, t as ComparisonBarInput, aV as PullQuoteInput, bL as VideoWithCaptionInput, bK as VideoPullQuoteInput, G as DataTableInput, K as DiagramBlockInput, bx as TreeBlockInput, bq as TimelineBlockInput, k as Block, S as DrawingBlockInput, ae as MarkerStyle, A as AccentImage, a0 as ImageTreatment, a as AccentPosition, c as Animation, d as AnimationType, bj as ThemeColorScheme, O as Doc, bl as ThemeRegistry, aD as PageEmphasis, bk as ThemePageStyle, Q as DocDiagnostic, N as DiagramTemplateNode, M as DiagramTemplateEdge, bt as TimelineTemplateTrack, bs as TimelineTemplateLink } from '../Doc-BkxT7G80.js';
2
- export { T as FRONTMATTER_CUSTOM_TEMPLATES_KEY, U as FRONTMATTER_CUSTOM_THEMES_KEY, a5 as LayoutHints, aY as RenderStyle, bi as ThemeColorPalette, bo as ThemeStyle, bp as ThemeTypography, bD as VIEWPORT_PRESETS, bO as ViewportPreset, bT as createTemplateContext, b_ as getLayoutHints, c1 as getTwoColumnPositions, c2 as getViewport, c3 as getViewportOrientation, c6 as isTemplateBlock, c9 as scaledFontSize } from '../Doc-BkxT7G80.js';
1
+ import { be as TemplateBlock, bf as TemplateContext, a3 as Layer, w as CustomTemplateDefinition, bh as TemplateRegistry, bk as Theme, bP as ViewportConfig, aO as PersistentLayerConfig, P as DocBlock, bQ as ViewportOrientation, aN as PersistentLayer, bx as TitleBlockInput, b3 as SectionHeaderInput, j as BigTextInput, u as ContentBlockInput, b9 as StatHighlightInput, aZ as QuoteBlockInput, V as FactCardInput, bF as TwoColumnInput, H as DateEventInput, a1 as ImageWithCaptionInput, a6 as LeftFeatureInput, b0 as RightFeatureInput, aa as MapBlockInput, b8 as StartBlockConfig, Y as FullBleedQuoteInput, a9 as ListBlockInput, aS as PhotoGridInput, J as DefinitionCardInput, t as ComparisonBarInput, aY as PullQuoteInput, bO as VideoWithCaptionInput, bN as VideoPullQuoteInput, G as DataTableInput, K as DiagramBlockInput, bA as TreeBlockInput, bt as TimelineBlockInput, k as Block, S as DrawingBlockInput, ae as MarkerStyle, A as AccentImage, a0 as ImageTreatment, a as AccentPosition, c as Animation, d as AnimationType, bm as ThemeColorScheme, O as Doc, bo as ThemeRegistry, aG as PageEmphasis, bn as ThemePageStyle, Q as DocDiagnostic, N as DiagramTemplateNode, M as DiagramTemplateEdge, bw as TimelineTemplateTrack, bv as TimelineTemplateLink } from '../Doc-CynsGqL4.js';
2
+ export { T as FRONTMATTER_CUSTOM_TEMPLATES_KEY, U as FRONTMATTER_CUSTOM_THEMES_KEY, a5 as LayoutHints, a$ as RenderStyle, bl as ThemeColorPalette, br as ThemeStyle, bs as ThemeTypography, bG as VIEWPORT_PRESETS, bR as ViewportPreset, bW as createTemplateContext, c1 as getLayoutHints, c4 as getTwoColumnPositions, c5 as getViewport, c6 as getViewportOrientation, c9 as isTemplateBlock, cc as scaledFontSize } from '../Doc-CynsGqL4.js';
3
3
  import { L as MarkdownNode, a5 as TransitionType, a4 as TransitionDirection, M as MarkdownBlockNode, r as MarkdownHeading, n as MarkdownDocument, V as MarkdownTable, i as MarkdownCodeBlock, G as MarkdownList } from '../types-Be6sBUBx.js';
4
4
  import { C as CoercedBlockMeta } from '../annotationCoercion-Ckqpu33y.js';
5
- import { c as PageSection } from '../materializePageSection-Dp2aHFbc.js';
6
- export { M as MaterializePageSectionOptions, a as MaterializePageSectionsOptions, P as PageMedia, b as PageRichText, d as PageSectionDiagnostic, e as PageSectionItem, f as PageSectionMaterialization, g as PageSectionSource, h as PageSpatialKind, i as PageTransformHints, m as materializePageSection, j as materializePageSections, r as resolvePageStyle } from '../materializePageSection-Dp2aHFbc.js';
5
+ import { c as PageSection } from '../materializePageSection-CAJoa8fh.js';
6
+ export { M as MaterializePageSectionOptions, a as MaterializePageSectionsOptions, P as PageMedia, b as PageRichText, d as PageSectionDiagnostic, e as PageSectionItem, f as PageSectionMaterialization, g as PageSectionSource, h as PageSpatialKind, i as PageTransformHints, m as materializePageSection, j as materializePageSections, r as resolvePageStyle } from '../materializePageSection-CAJoa8fh.js';
7
7
  import { C as ContentContainer } from '../ContentContainer-B2w9sUoL.js';
8
8
  import { V as ViewIssue } from '../viewState-lTZS-hP_.js';
9
- export { D as DEFAULT_THEME, g as getAvailableThemes, b as getThemeSummaries, r as resolveTheme } from '../themeLibrary-BbSjnGmd.js';
9
+ export { D as DEFAULT_THEME, g as getAvailableThemes, b as getThemeSummaries, r as resolveTheme } from '../themeLibrary-z7t3x5CU.js';
10
10
 
11
11
  /** Runtime registry composition for built-in and document-scoped templates. */
12
12
 
package/dist/doc/index.js CHANGED
@@ -70,8 +70,8 @@ import {
70
70
  validateMarkdownDoc,
71
71
  validateMarkdownSource,
72
72
  writeDashboardLayoutsToFrontmatter
73
- } from "../chunk-GRGLZF4A.js";
74
- import "../chunk-764WHWKM.js";
73
+ } from "../chunk-JM2QARY2.js";
74
+ import "../chunk-ZEONJCP3.js";
75
75
  import {
76
76
  ASCII_CHAR_H,
77
77
  ASCII_CHAR_W,
@@ -193,21 +193,7 @@ import {
193
193
  wrapWithPersistentLayers,
194
194
  writeCustomTemplatesToFrontmatter,
195
195
  writeCustomThemesToFrontmatter
196
- } from "../chunk-WAGJK36Q.js";
197
- import {
198
- PATH_SHAPE_KINDS,
199
- anchorPoint,
200
- clipEndpoints,
201
- clipPoint,
202
- connectorPath,
203
- lineStyleDasharray,
204
- markerPath,
205
- nearestSnapPoint,
206
- shapePath,
207
- snapEndpoints,
208
- snapPoints
209
- } from "../chunk-PUS54YU6.js";
210
- import "../chunk-7ZAAICW4.js";
196
+ } from "../chunk-RMCUZDW5.js";
211
197
  import {
212
198
  ASCII_DIAGRAM_FENCE_LANGS,
213
199
  ASCII_TIMELINE_FENCE_LANGS,
@@ -231,7 +217,21 @@ import {
231
217
  parseTree,
232
218
  parseWrappedFlowTimeline
233
219
  } from "../chunk-C3NAY5YP.js";
234
- import "../chunk-GAZKTT4R.js";
220
+ import {
221
+ PATH_SHAPE_KINDS,
222
+ anchorPoint,
223
+ clipEndpoints,
224
+ clipPoint,
225
+ connectorPath,
226
+ lineStyleDasharray,
227
+ markerPath,
228
+ nearestSnapPoint,
229
+ shapePath,
230
+ snapEndpoints,
231
+ snapPoints
232
+ } from "../chunk-PUS54YU6.js";
233
+ import "../chunk-7ZAAICW4.js";
234
+ import "../chunk-YNO6JV36.js";
235
235
  import {
236
236
  DEFAULT_THEME,
237
237
  FRONTMATTER_CUSTOM_TEMPLATES_KEY,
@@ -248,15 +248,17 @@ import {
248
248
  resolveTheme,
249
249
  scaledFontSize2 as scaledFontSize
250
250
  } from "../chunk-Y4D6TDZT.js";
251
- import "../chunk-CW24CH7I.js";
251
+ import "../chunk-JSGIWW35.js";
252
+ import "../chunk-L7QZORLB.js";
253
+ import "../chunk-HUXK7EOT.js";
252
254
  import {
253
255
  CONTAINER_TEMPLATES,
254
256
  TABLE_FED_TEMPLATES,
255
257
  isContainerTemplate,
256
258
  resolveTemplateName
257
259
  } from "../chunk-TMOHAFYE.js";
258
- import "../chunk-7N4G32LG.js";
259
260
  import "../chunk-X7BNDUNI.js";
261
+ import "../chunk-7N4G32LG.js";
260
262
  import "../chunk-4VOD55SX.js";
261
263
  import "../chunk-GHOPHGOK.js";
262
264
  import "../chunk-XRW6P7IF.js";
@@ -1,4 +1,4 @@
1
- import { bh as Theme } from '../Doc-BkxT7G80.js';
1
+ import { bk as Theme } from '../Doc-CynsGqL4.js';
2
2
  import '../types-Be6sBUBx.js';
3
3
 
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  import { E as ExtractedElement } from '../contentExtractor-BNfVJV2U.js';
2
2
  export { C as ComparisonData, D as DateData, a as DefinitionData, b as ExtractionOptions, c as ExtractionResult, d as ExtractionType, F as FactData, I as ImpactLineData, L as ListData, Q as QuoteData, S as StatData, e as extractContent, s as stripMarkdown } from '../contentExtractor-BNfVJV2U.js';
3
- import { r as ColorScheme, A as AccentImage, bb as TemplateBlock } from '../Doc-BkxT7G80.js';
3
+ import { r as ColorScheme, A as AccentImage, be as TemplateBlock } from '../Doc-CynsGqL4.js';
4
4
  import '../types-Be6sBUBx.js';
5
5
 
6
6
  /**
@@ -1,8 +1,8 @@
1
- import { a as ImageEditDoc, b as ImageEditLayer } from '../ImageEditDoc-C8ZB54Vv.js';
2
- export { E as EditorLayerMeta, I as ImageEditCanvas, c as ImageEditLayerKind, d as ImageEditMeta } from '../ImageEditDoc-C8ZB54Vv.js';
1
+ import { a as ImageEditDoc, b as ImageEditLayer } from '../ImageEditDoc-BVZe_5y4.js';
2
+ export { E as EditorLayerMeta, I as ImageEditCanvas, c as ImageEditLayerKind, d as ImageEditMeta } from '../ImageEditDoc-BVZe_5y4.js';
3
3
  import { C as ContentContainer } from '../ContentContainer-B2w9sUoL.js';
4
4
  import { V as Version, C as CoalesceOptions, P as PrunePolicy } from '../types-8QjefM9J.js';
5
- import '../Doc-BkxT7G80.js';
5
+ import '../Doc-CynsGqL4.js';
6
6
  import '../types-Be6sBUBx.js';
7
7
 
8
8
  /**
@@ -253,4 +253,47 @@ declare function exportImageEditDoc(doc: ImageEditDoc, container: ContentContain
253
253
  */
254
254
  declare function buildSvgString(doc: ImageEditDoc, container: ContentContainer): Promise<string>;
255
255
 
256
- export { IMAGE_EDIT_ASSETS_PREFIX, IMAGE_EDIT_DEFAULT_BASENAME, IMAGE_EDIT_STATE_FILENAME, IMAGE_EDIT_VERSIONS_PREFIX, ImageEditDoc, type ImageEditExportFormat, type ImageEditExportOptions, ImageEditLayer, ImageEditVersionManager, type ImageEditVersionManagerOptions, type RevertImageEditFailureReason, type RevertImageEditOptions, type RevertImageEditResult, type SaveImageEditVersionOptions, type SaveImageEditVersionResult, addLayer, buildImageEditVersionPath, buildSvgString, coalesceImageEditVersions, createEmptyImageEditDoc, exportImageEditDoc, listImageEditVersions, parseImageEditVersionPath, pruneImageEditVersions, readImageEditDoc, readImageEditVersion, readImageEditVersionText, removeLayer, reorderLayer, revertToImageEditVersion, saveImageEditVersion, setCanvas, touch, updateLayer, writeImageEditDoc };
256
+ /**
257
+ * Animated-image inspection.
258
+ *
259
+ * Reads the container structure of a GIF, PNG (APNG) or WebP byte stream and
260
+ * reports whether it animates, how many frames it has, how long one pass
261
+ * takes, and how many passes the file asks for. It lets a renderer decide
262
+ * whether an image deserves playback controls, and lets the image editor
263
+ * refuse to flatten an animation into a still.
264
+ *
265
+ * Structure only: no pixel data is decoded. GIF image data is skipped by
266
+ * sub-block length and PNG/WebP chunks by their size fields, so a scan is
267
+ * linear in the number of blocks. Truncated or malformed input never throws;
268
+ * the scan stops and reports what it read.
269
+ */
270
+ type AnimatedImageFormat = 'gif' | 'png' | 'webp';
271
+ interface AnimatedImageInfo {
272
+ format: AnimatedImageFormat;
273
+ /** True when the image has more than one frame to play. */
274
+ animated: boolean;
275
+ /** Frames in one pass. 1 for a still image. */
276
+ frameCount: number;
277
+ /**
278
+ * Length of one pass in milliseconds, timed the way browsers play it: a
279
+ * frame delay of 10 ms or less plays at 100 ms. 0 for a still image.
280
+ */
281
+ durationMs: number;
282
+ /** Passes the file asks for; 0 means it loops forever. 1 for a still image. */
283
+ playCount: number;
284
+ }
285
+ /**
286
+ * Inspect an image's bytes. Returns `null` when the bytes are not a GIF, PNG
287
+ * or WebP (a JPEG, for instance, can never animate).
288
+ */
289
+ declare function inspectAnimatedImage(input: Uint8Array | ArrayBuffer): AnimatedImageInfo | null;
290
+ /**
291
+ * Whether a path, URL or MIME type names a format that can animate, i.e.
292
+ * whether reading its bytes is worthwhile. PNG is deliberately excluded: APNG
293
+ * is rare and nearly every screenshot is a PNG, so sniffing them all would
294
+ * cost far more than it finds. A caller already holding a PNG's bytes can
295
+ * still pass them to {@link inspectAnimatedImage}.
296
+ */
297
+ declare function mayBeAnimatedImage(srcOrMime: string): boolean;
298
+
299
+ export { type AnimatedImageFormat, type AnimatedImageInfo, IMAGE_EDIT_ASSETS_PREFIX, IMAGE_EDIT_DEFAULT_BASENAME, IMAGE_EDIT_STATE_FILENAME, IMAGE_EDIT_VERSIONS_PREFIX, ImageEditDoc, type ImageEditExportFormat, type ImageEditExportOptions, ImageEditLayer, ImageEditVersionManager, type ImageEditVersionManagerOptions, type RevertImageEditFailureReason, type RevertImageEditOptions, type RevertImageEditResult, type SaveImageEditVersionOptions, type SaveImageEditVersionResult, addLayer, buildImageEditVersionPath, buildSvgString, coalesceImageEditVersions, createEmptyImageEditDoc, exportImageEditDoc, inspectAnimatedImage, listImageEditVersions, mayBeAnimatedImage, parseImageEditVersionPath, pruneImageEditVersions, readImageEditDoc, readImageEditVersion, readImageEditVersionText, removeLayer, reorderLayer, revertToImageEditVersion, saveImageEditVersion, setCanvas, touch, updateLayer, writeImageEditDoc };