@bendyline/squisq 2.11.20 → 2.12.1

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 (45) hide show
  1. package/dist/{Doc-B6TYHYGc.d.ts → Doc-CHD4fxfm.d.ts} +28 -24
  2. package/dist/{ImageEditDoc-CN_3Ysj5.d.ts → ImageEditDoc-DSUM5MiA.d.ts} +1 -1
  3. package/dist/{annotationCoercion-BgL52343.d.ts → annotationCoercion-TxFucMtA.d.ts} +6 -15
  4. package/dist/{chunk-3XEABCT3.js → chunk-3H7SYCZS.js} +19 -0
  5. package/dist/{chunk-KCD3SH5Z.js → chunk-B2BQFF5B.js} +65 -8
  6. package/dist/{chunk-A5QJJZ5S.js → chunk-B3AFOXMA.js} +1 -1
  7. package/dist/{chunk-4KTLKEIY.js → chunk-FUBMT6SJ.js} +3 -3
  8. package/dist/{chunk-Y4OZ5DSG.js → chunk-H54A35NZ.js} +2 -2
  9. package/dist/{chunk-PFH3WV6J.js → chunk-I4L4GYDN.js} +1 -1
  10. package/dist/{chunk-5F7AO4S5.js → chunk-ISH4HDH7.js} +1 -1
  11. package/dist/{chunk-QFC4JZZH.js → chunk-LN72K54P.js} +38 -8
  12. package/dist/{chunk-4VOD55SX.js → chunk-M6YXFNGO.js} +37 -0
  13. package/dist/{chunk-7WN7DDMI.js → chunk-QHNO67JS.js} +1 -1
  14. package/dist/{chunk-UWLEDXSU.js → chunk-RKCARBWM.js} +4371 -4117
  15. package/dist/{chunk-K2Z7K65P.js → chunk-VTG7PNOL.js} +2 -2
  16. package/dist/{chunk-GB7OFYUK.js → chunk-WAX4QYHA.js} +6 -1
  17. package/dist/{chunk-PSC3KZEK.js → chunk-YA74HTAB.js} +1 -1
  18. package/dist/{chunk-WSFXV5BT.js → chunk-ZYWYSM6W.js} +105 -74
  19. package/dist/doc/index.d.ts +47 -30
  20. package/dist/doc/index.js +27 -11
  21. package/dist/fence/index.d.ts +2 -2
  22. package/dist/generate/index.d.ts +2 -2
  23. package/dist/imageEdit/index.d.ts +4 -4
  24. package/dist/imageEdit/index.js +2 -2
  25. package/dist/index.d.ts +7 -7
  26. package/dist/index.js +46 -16
  27. package/dist/jsonForm/index.d.ts +2 -2
  28. package/dist/jsonForm/index.js +4 -4
  29. package/dist/markdown/index.d.ts +3 -3
  30. package/dist/markdown/index.js +5 -5
  31. package/dist/{materializePageSection-IYXU3tDN.d.ts → materializePageSection-Dx1Sz0vK.d.ts} +2 -2
  32. package/dist/mediaEdit/index.d.ts +3 -3
  33. package/dist/narration/index.d.ts +4 -18
  34. package/dist/narration/index.js +9 -9
  35. package/dist/recommend/index.d.ts +1 -1
  36. package/dist/recommend/index.js +2 -2
  37. package/dist/schemas/index.d.ts +5 -5
  38. package/dist/schemas/index.js +17 -3
  39. package/dist/{themeLibrary-_46saKYO.d.ts → themeLibrary-DLmZ1Ct8.d.ts} +1 -1
  40. package/dist/timing/index.d.ts +2 -2
  41. package/dist/transform/index.d.ts +4 -5
  42. package/dist/transform/index.js +9 -9
  43. package/dist/{types-C6VvDDL2.d.ts → types-BmEHcL3l.d.ts} +42 -1
  44. package/package.json +1 -1
  45. package/dist/{chunk-Q6ELOAOQ.js → chunk-NNJA7ICB.js} +17 -17
@@ -1,4 +1,4 @@
1
- import { a3 as Transition, a5 as TransitionType, M as MarkdownBlockNode, r as MarkdownHeading } from './types-C6VvDDL2.js';
1
+ import { f as BuildSpec, h as BuildTiming, ac as Transition, ae as TransitionType, g as BuildStep, M as MarkdownBlockNode, z as MarkdownHeading, d as BuildAdvance } from './types-BmEHcL3l.js';
2
2
 
3
3
  /**
4
4
  * Media-edit recipes — the non-destructive edit list a media reference carries.
@@ -637,29 +637,6 @@ declare function resolveMotionForDoc(doc: {
637
637
  frontmatter?: Record<string, unknown>;
638
638
  } | null | undefined, theme?: Pick<Theme, 'renderStyle'> | null, explicit?: MotionSpec | null): MotionProfile;
639
639
 
640
- /**
641
- * Block Templates Schema
642
- *
643
- * Defines a template system for generated or programmatic doc blocks. Instead
644
- * of specifying low-level layers, positions, and animations, callers can use
645
- * high-level templates like "title" or "statHighlight" with simple
646
- * content parameters.
647
- *
648
- * Templates are expanded into full Block structures at render time,
649
- * ensuring consistent styling and reducing generation errors.
650
- *
651
- * Template Types:
652
- * - title: Doc intro with title + subtitle
653
- * - sectionHeader: Colored section divider
654
- * - content: Heading with the complete Markdown body
655
- * - statHighlight: Big number/stat with description
656
- * - quote: Large centered quote
657
- * - factCard: Key fact with explanation
658
- * - twoColumn: Side-by-side comparison
659
- * - dateEvent: Timeline-style date + description
660
- * - imageWithCaption: Image with text overlay
661
- */
662
-
663
640
  /**
664
641
  * Name of a color scheme defined in the active theme's `colorSchemes` map.
665
642
  * Templates reference schemes by name (e.g. 'blue', 'green') and the theme
@@ -703,6 +680,13 @@ interface AccentImage {
703
680
  * Base properties shared by all template blocks.
704
681
  */
705
682
  interface BaseTemplateBlock {
683
+ build?: BuildSpec;
684
+ buildTiming?: BuildTiming;
685
+ /** Projection-only source range for exact narration alignment. */
686
+ buildSource?: {
687
+ text: string;
688
+ charStart: number;
689
+ };
706
690
  /** Compiled presentation timing is authoritative; pacing must not merge or split it. */
707
691
  timelineLocked?: boolean;
708
692
  /** Unique block ID */
@@ -799,6 +783,12 @@ interface BigTextInput extends BaseTemplateBlock {
799
783
  imageAlt?: string;
800
784
  /** Ambient motion for the background image. */
801
785
  ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';
786
+ /**
787
+ * Where the title sits (default `center`, the whole frame). `upper` and
788
+ * `lower` fit it into the top or bottom half instead — `upper` keeps a
789
+ * vertical video's title clear of captions burned into the lower band.
790
+ */
791
+ placement?: 'center' | 'upper' | 'lower';
802
792
  }
803
793
  /**
804
794
  * Transcript block - one conversational beat: speaker (avatar + name)
@@ -2334,6 +2324,8 @@ interface DocDiagnostic {
2334
2324
  * A complete visual doc for an article.
2335
2325
  */
2336
2326
  interface Doc {
2327
+ /** Canonical pre-projection script used to validate build timing anchors. */
2328
+ buildSourceText?: string;
2337
2329
  /** Projection-only source/timing metadata from the document narration take. */
2338
2330
  presentationNarration?: {
2339
2331
  sourceText: string;
@@ -2341,6 +2333,7 @@ interface Doc {
2341
2333
  startTime: number;
2342
2334
  duration: number;
2343
2335
  bookmarks: AudioBookmark[];
2336
+ builds?: BuildAdvance[];
2344
2337
  };
2345
2338
  /** Projection-only guard: never compile an accepted plan twice. */
2346
2339
  presentationApplied?: boolean;
@@ -2477,6 +2470,15 @@ interface PromotedBodyAnnotation {
2477
2470
  };
2478
2471
  }
2479
2472
  interface Block {
2473
+ /** Optional progressive content reveal. */
2474
+ build?: BuildSpec;
2475
+ /** Projection-only timing, guarded against edits by the chunk text. */
2476
+ buildTiming?: BuildTiming;
2477
+ /** Projection-only source range for exact narration alignment. */
2478
+ buildSource?: {
2479
+ text: string;
2480
+ charStart: number;
2481
+ };
2480
2482
  /** Unique identifier for this block.
2481
2483
  * Defaults to a slug derived from the heading text. May be overridden by
2482
2484
  * a Pandoc-style `{#custom-id}` attribute on the heading. */
@@ -2704,6 +2706,8 @@ interface TextLayer extends BaseLayer {
2704
2706
  * `@bendyline/squisq-react`.
2705
2707
  */
2706
2708
  html?: string;
2709
+ /** Semantic steps targeted by generated data-squisq-build-step fragments. */
2710
+ buildSteps?: BuildStep[];
2707
2711
  /** Text styling */
2708
2712
  style: TextStyle;
2709
2713
  };
@@ -1,4 +1,4 @@
1
- import { a2 as ImageLayer, bu as TextLayer, bh as ShapeLayer, aX as PathLayer } from './Doc-B6TYHYGc.js';
1
+ import { a2 as ImageLayer, bu as TextLayer, bh as ShapeLayer, aX as PathLayer } from './Doc-CHD4fxfm.js';
2
2
 
3
3
  /**
4
4
  * ImageEditDoc — JSON document describing a layered, editable image.
@@ -1,18 +1,4 @@
1
- import { ae as BlockConnection, a5 as TransitionType, a4 as TransitionDirection } from './types-C6VvDDL2.js';
2
-
3
- /**
4
- * Heading-attribute value coercion.
5
- *
6
- * The Pandoc-style heading attribute block `{#id .class key=value}` parses
7
- * to a flat `Record<string, string>` of `key → raw value`. This module
8
- * coerces a known subset of keys to typed values (numbers, time-in-seconds,
9
- * `BlockConnection[]`) so downstream consumers don't have to re-parse them.
10
- *
11
- * Unknown keys are returned in `metadata` as raw strings. Malformed known-key
12
- * values are dropped from the typed result and reported via `warnings` (the
13
- * raw string is preserved in `params` upstream for lossless round-tripping —
14
- * coercion failures do not corrupt the source).
15
- */
1
+ import { f as BuildSpec, aq as BlockConnection, ae as TransitionType, ad as TransitionDirection } from './types-BmEHcL3l.js';
16
2
 
17
3
  /**
18
4
  * Registry of known block-meta keys and their coercion strategies.
@@ -22,6 +8,10 @@ import { ae as BlockConnection, a5 as TransitionType, a4 as TransitionDirection
22
8
  * in `markdownToDoc.ts`.
23
9
  */
24
10
  declare const KNOWN_BLOCK_META_KEYS: {
11
+ readonly build: "build";
12
+ readonly buildEffect: "buildEffect";
13
+ readonly buildDuration: "time";
14
+ readonly buildPrevious: "buildPrevious";
25
15
  readonly x: "number";
26
16
  readonly y: "number";
27
17
  readonly startTime: "time";
@@ -61,6 +51,7 @@ declare const BLOCK_META_KEY_DESCRIPTORS: readonly BlockMetaKeyDescriptor[];
61
51
  * `HeadingAttributes.blockMeta` in `types.ts`.
62
52
  */
63
53
  interface CoercedBlockMeta {
54
+ build?: BuildSpec;
64
55
  x?: number;
65
56
  y?: number;
66
57
  startTime?: number;
@@ -1,3 +1,7 @@
1
+ import {
2
+ recordBuildAdvance
3
+ } from "./chunk-M6YXFNGO.js";
4
+
1
5
  // src/narration/sidecar.ts
2
6
  function resolveNarrationTimingGenerator(options) {
3
7
  const baseWpm = options?.generator?.baseWpm ?? options?.baseWpm;
@@ -93,7 +97,22 @@ function parseNarrationTimingJson(data) {
93
97
  if (block.endSec < block.startSec) block.endSec = block.startSec;
94
98
  cursor = block.startSec;
95
99
  }
100
+ let builds = [];
101
+ if (Array.isArray(raw.builds))
102
+ for (const item of raw.builds) {
103
+ if (!item || typeof item !== "object") continue;
104
+ const event = item;
105
+ if (typeof event.blockId === "string" && typeof event.sourceText === "string" && typeof event.step === "number" && finiteNonNegative(event.time) && event.time <= raw.duration) {
106
+ builds = recordBuildAdvance(builds, {
107
+ blockId: event.blockId,
108
+ sourceText: event.sourceText,
109
+ step: event.step,
110
+ time: event.time
111
+ });
112
+ }
113
+ }
96
114
  return {
115
+ ...builds.length ? { builds } : {},
97
116
  version: 3,
98
117
  sourceText: raw.sourceText,
99
118
  duration: raw.duration,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseNarrationTimingJson
3
- } from "./chunk-3XEABCT3.js";
3
+ } from "./chunk-3H7SYCZS.js";
4
4
  import {
5
5
  applySavedPresentation
6
- } from "./chunk-QFC4JZZH.js";
6
+ } from "./chunk-LN72K54P.js";
7
7
  import {
8
8
  SHAPE_NAMES,
9
9
  buildChartData,
@@ -35,7 +35,7 @@ import {
35
35
  templateRegistry,
36
36
  writeCustomTemplatesToFrontmatter,
37
37
  writeCustomThemesToFrontmatter
38
- } from "./chunk-UWLEDXSU.js";
38
+ } from "./chunk-RKCARBWM.js";
39
39
  import {
40
40
  ASCII_TREE_VOCAB,
41
41
  ASCII_VOCAB,
@@ -52,7 +52,7 @@ import {
52
52
  toCells,
53
53
  toGrid,
54
54
  traceBoxes
55
- } from "./chunk-A5QJJZ5S.js";
55
+ } from "./chunk-B3AFOXMA.js";
56
56
  import {
57
57
  iconMarker
58
58
  } from "./chunk-7ZAAICW4.js";
@@ -76,7 +76,7 @@ import {
76
76
  resolveFontFamily,
77
77
  resolveTheme,
78
78
  withAlpha
79
- } from "./chunk-PFH3WV6J.js";
79
+ } from "./chunk-I4L4GYDN.js";
80
80
  import {
81
81
  createMediaTimeMap
82
82
  } from "./chunk-JSGIWW35.js";
@@ -85,7 +85,7 @@ import {
85
85
  } from "./chunk-L7QZORLB.js";
86
86
  import {
87
87
  parseMarkdown
88
- } from "./chunk-Y4OZ5DSG.js";
88
+ } from "./chunk-H54A35NZ.js";
89
89
  import {
90
90
  KNOWN_BLOCK_META_KEYS,
91
91
  TABLE_FED_TEMPLATES,
@@ -94,7 +94,7 @@ import {
94
94
  isContainerTemplate,
95
95
  resolveTemplateName,
96
96
  serializeAnnotation
97
- } from "./chunk-WSFXV5BT.js";
97
+ } from "./chunk-ZYWYSM6W.js";
98
98
  import {
99
99
  extractPlainText,
100
100
  findBlockVideoEmbed,
@@ -103,7 +103,11 @@ import {
103
103
  parseHtmlToNodes,
104
104
  parseVideoEmbedUrl,
105
105
  readFrontmatterThemeId
106
- } from "./chunk-Q6ELOAOQ.js";
106
+ } from "./chunk-NNJA7ICB.js";
107
+ import {
108
+ BUILD_PARAM_KEYS,
109
+ buildSpecParams
110
+ } from "./chunk-M6YXFNGO.js";
107
111
  import {
108
112
  buildCaptionTrack
109
113
  } from "./chunk-4OQFLZ6G.js";
@@ -586,6 +590,7 @@ function docToMarkdown(doc, options = {}) {
586
590
  children.push(synthesizeMediaParagraph(clip));
587
591
  }
588
592
  function emitBlock(block) {
593
+ block = reconcileBuildMetadata(block);
589
594
  const promoted = block.promotedBodyAnnotation;
590
595
  const promotedUnedited = promoted !== void 0 && !isPromotedBodyEdited(block, promoted);
591
596
  if (block.sourceHeading) {
@@ -788,6 +793,38 @@ function removeTransitionParams(attrs) {
788
793
  ...Object.keys(params).length > 0 ? { params } : { params: void 0 }
789
794
  };
790
795
  }
796
+ function reconcileBuildMetadata(block) {
797
+ const before = getPinnedBlockMeta(block).build;
798
+ if (paramsEqual(
799
+ before ? buildSpecParams(before) : {},
800
+ block.build ? buildSpecParams(block.build) : {}
801
+ ))
802
+ return block;
803
+ const params = { ...block.templateOverrides };
804
+ for (const key of BUILD_PARAM_KEYS) delete params[key];
805
+ if (block.build) Object.assign(params, buildSpecParams(block.build));
806
+ const heading = block.sourceHeading;
807
+ const attrs = heading?.attributes;
808
+ const attrParams = { ...attrs?.params };
809
+ const blockMeta = { ...attrs?.blockMeta };
810
+ for (const key of BUILD_PARAM_KEYS) delete attrParams[key];
811
+ delete blockMeta.build;
812
+ if (block.build) blockMeta.build = block.build;
813
+ return {
814
+ ...block,
815
+ templateOverrides: Object.keys(params).length ? params : void 0,
816
+ ...heading ? {
817
+ sourceHeading: {
818
+ ...heading,
819
+ attributes: {
820
+ ...attrs,
821
+ params: Object.keys(attrParams).length ? attrParams : void 0,
822
+ blockMeta
823
+ }
824
+ }
825
+ } : {}
826
+ };
827
+ }
791
828
 
792
829
  // src/doc/buildPreviewDoc.ts
793
830
  function extractRichText(node) {
@@ -1036,9 +1073,12 @@ function resolveDocumentTitle(doc, provided) {
1036
1073
  return provided?.trim() ?? "";
1037
1074
  }
1038
1075
  function buildPreviewDoc(doc, options) {
1076
+ const sourceDoc = doc;
1039
1077
  doc = applySavedPresentation(doc);
1040
1078
  if (doc.presentationApplied) options = { ...options, interleaveImages: false };
1041
1079
  const flat = flattenRenderableBlocks(doc.blocks);
1080
+ const buildScript = flat.some((block) => block.build && block.build.mode !== "off") ? buildNarrationScript(sourceDoc) : void 0;
1081
+ const sourceBlocks = buildScript ? flattenRenderableBlocks(sourceDoc.blocks) : [];
1042
1082
  const allImages = collectAllDocImages(doc.blocks);
1043
1083
  const usedImageSrcs = /* @__PURE__ */ new Set();
1044
1084
  const knownTemplates = doc.customTemplates ? new Set(doc.customTemplates.map((d) => d.name)) : void 0;
@@ -1049,6 +1089,18 @@ function buildPreviewDoc(doc, options) {
1049
1089
  const block = flat[i];
1050
1090
  const blockImages = extractBlockImages(block.contents);
1051
1091
  const slide = blockToSlide(block, i, knownTemplates, documentTitle);
1092
+ const range = buildScript?.blocks.find(
1093
+ (range2) => range2.blockId === (block.sourceBlockId ?? block.id)
1094
+ );
1095
+ if (range && buildScript && block.build) {
1096
+ const sourceBlock = sourceBlocks.find((source) => source.id === range.blockId);
1097
+ const body = sourceBlock ? getBlockBodyText(sourceBlock) : "";
1098
+ const charStart = body ? range.charEnd - body.length : range.charStart;
1099
+ slide.buildSource = {
1100
+ text: buildScript.sourceText.slice(charStart, range.charEnd),
1101
+ charStart
1102
+ };
1103
+ }
1052
1104
  if (block.summaryMode) {
1053
1105
  for (const img of blockImages) usedImageSrcs.add(img.src);
1054
1106
  } else if (blockImages.length > 0 && slide.template === "sectionHeader") {
@@ -1108,6 +1160,7 @@ function buildPreviewDoc(doc, options) {
1108
1160
  // Preview preparation should replace only the slide/timing projection.
1109
1161
  ...doc,
1110
1162
  duration: t,
1163
+ ...buildScript ? { buildSourceText: doc.buildSourceText ?? buildScript.sourceText } : {},
1111
1164
  blocks: slides,
1112
1165
  audio
1113
1166
  };
@@ -4428,6 +4481,10 @@ async function applyNarrationTiming(doc, container) {
4428
4481
  src: clip.src,
4429
4482
  startTime: clip.startAt,
4430
4483
  duration: playedDuration,
4484
+ builds: timing.builds?.flatMap((event) => {
4485
+ const time = timeMap ? timeMap.snapSourceToPlayed(event.time) : event.time;
4486
+ return time >= 0 && time <= playedDuration ? [{ ...event, time: clip.startAt + time }] : [];
4487
+ }),
4431
4488
  bookmarks: timing.bookmarks.flatMap((bookmark) => {
4432
4489
  const time = timeMap ? timeMap.sourceToPlayed(bookmark.time) : bookmark.time;
4433
4490
  return time !== null && time >= 0 && time <= playedDuration ? [{ ...bookmark, time: clip.startAt + time }] : [];
@@ -4,7 +4,7 @@ import {
4
4
  isVideoEmbedUrl,
5
5
  mediaKindForUrl,
6
6
  walkMarkdownTree
7
- } from "./chunk-Q6ELOAOQ.js";
7
+ } from "./chunk-NNJA7ICB.js";
8
8
 
9
9
  // src/doc/asciiDiagram/chars.ts
10
10
  function toCells(s) {
@@ -480,6 +480,8 @@ function base64ToUtf8(value) {
480
480
  }
481
481
 
482
482
  export {
483
+ bytesToBase64,
484
+ base64ToUtf8,
483
485
  PATH_SHAPE_KINDS,
484
486
  shapePath,
485
487
  markerPath,
@@ -490,7 +492,5 @@ export {
490
492
  nearestSnapPoint,
491
493
  snapEndpoints,
492
494
  connectorPath,
493
- lineStyleDasharray,
494
- bytesToBase64,
495
- base64ToUtf8
495
+ lineStyleDasharray
496
496
  };
@@ -6,11 +6,11 @@ import {
6
6
  quoteAttrValue,
7
7
  splitKeyValueToken,
8
8
  tokenizeAttrTokens
9
- } from "./chunk-WSFXV5BT.js";
9
+ } from "./chunk-ZYWYSM6W.js";
10
10
  import {
11
11
  parseFrontmatter,
12
12
  parseHtmlToNodes
13
- } from "./chunk-Q6ELOAOQ.js";
13
+ } from "./chunk-NNJA7ICB.js";
14
14
  import {
15
15
  resolveIcon
16
16
  } from "./chunk-CZ7AUSLI.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isTransitionType
3
- } from "./chunk-4VOD55SX.js";
3
+ } from "./chunk-M6YXFNGO.js";
4
4
 
5
5
  // src/schemas/Motion.ts
6
6
  var MOTION_PROFILE_NAMES = ["calm", "documentary", "vibrant"];
@@ -8,7 +8,7 @@ import {
8
8
  expectedSyllablesAt,
9
9
  flattenRenderableBlocks,
10
10
  wordPosAtExpectedSyllables
11
- } from "./chunk-UWLEDXSU.js";
11
+ } from "./chunk-RKCARBWM.js";
12
12
 
13
13
  // src/narration/types.ts
14
14
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -5,12 +5,12 @@ import {
5
5
  flattenRenderableBlocks,
6
6
  getPinnedBlockMeta,
7
7
  markdownToDoc
8
- } from "./chunk-UWLEDXSU.js";
8
+ } from "./chunk-RKCARBWM.js";
9
9
  import {
10
10
  isAsciiDiagramFence,
11
11
  isAsciiTimelineFence,
12
12
  isTreeFence
13
- } from "./chunk-A5QJJZ5S.js";
13
+ } from "./chunk-B3AFOXMA.js";
14
14
  import {
15
15
  extractContent,
16
16
  mapElementToBlock,
@@ -19,18 +19,21 @@ import {
19
19
  import {
20
20
  cloneAndFreezeData,
21
21
  isTemplateBlock
22
- } from "./chunk-PFH3WV6J.js";
22
+ } from "./chunk-I4L4GYDN.js";
23
23
  import {
24
24
  parseMarkdown
25
- } from "./chunk-Y4OZ5DSG.js";
25
+ } from "./chunk-H54A35NZ.js";
26
26
  import {
27
27
  isContainerTemplate
28
- } from "./chunk-WSFXV5BT.js";
28
+ } from "./chunk-ZYWYSM6W.js";
29
29
  import {
30
30
  extractPlainText,
31
31
  getChildren,
32
32
  stringifyHtmlNodes
33
- } from "./chunk-Q6ELOAOQ.js";
33
+ } from "./chunk-NNJA7ICB.js";
34
+ import {
35
+ parseBuildSpec
36
+ } from "./chunk-M6YXFNGO.js";
34
37
  import {
35
38
  estimateTimeFromText
36
39
  } from "./chunk-GHOPHGOK.js";
@@ -151,9 +154,14 @@ function parsePresentationHints(input) {
151
154
  const ids = /* @__PURE__ */ new Set();
152
155
  const blocks = [];
153
156
  for (const entry of value.blocks) {
154
- if (!record2(entry) || !exact(entry, ["blockId"], ["layout", "inbetweens", "wording", "ai"]) || !text2(entry.blockId, 1024) || ids.has(entry.blockId) || entry.layout !== void 0 && !PRESENTATION_LAYOUTS.includes(entry.layout) || entry.inbetweens !== void 0 && typeof entry.inbetweens !== "boolean")
157
+ if (!record2(entry) || !exact(entry, ["blockId"], ["layout", "inbetweens", "wording", "ai", "build"]) || !text2(entry.blockId, 1024) || ids.has(entry.blockId) || entry.layout !== void 0 && !PRESENTATION_LAYOUTS.includes(entry.layout) || entry.inbetweens !== void 0 && typeof entry.inbetweens !== "boolean")
155
158
  return null;
156
159
  const hint = { blockId: entry.blockId };
160
+ if (entry.build !== void 0) {
161
+ const build = parseBuildSpec(entry.build);
162
+ if (!build) return null;
163
+ hint.build = build;
164
+ }
157
165
  if (entry.layout !== void 0) hint.layout = entry.layout;
158
166
  if (entry.inbetweens !== void 0) hint.inbetweens = entry.inbetweens;
159
167
  if (entry.ai !== void 0) {
@@ -217,6 +225,21 @@ function usesDynamicPresentation(doc) {
217
225
  function contentsToPlainText(contents) {
218
226
  return contents.map((node) => extractPlainText(node)).join("\n");
219
227
  }
228
+ var PROSE_NODE_TYPES = /* @__PURE__ */ new Set(["paragraph", "blockquote", "heading"]);
229
+ function maskNonProse(stripped, contents) {
230
+ let masked = stripped;
231
+ let cursor = 0;
232
+ for (const node of contents) {
233
+ const text3 = stripMarkdown(extractPlainText(node));
234
+ if (!text3) continue;
235
+ const at = stripped.indexOf(text3, cursor);
236
+ if (at < 0) continue;
237
+ cursor = at + text3.length;
238
+ if (PROSE_NODE_TYPES.has(node.type)) continue;
239
+ masked = masked.slice(0, at) + text3.replace(/\S/g, " ") + masked.slice(cursor);
240
+ }
241
+ return masked;
242
+ }
220
243
  function collectImages(node) {
221
244
  const results = [];
222
245
  if ("type" in node && node.type === "image" && "url" in node) {
@@ -291,7 +314,10 @@ function analyzeFlatBlocks(flat, options) {
291
314
  }
292
315
  const stripped = stripMarkdown(plainText);
293
316
  const bodyWordCount = stripped.split(/\s+/).filter((w) => w.length > 0).length;
294
- const extraction = extractContent(stripped, options);
317
+ const extraction = extractContent(
318
+ block.contents && block.contents.length > 0 ? maskNonProse(stripped, block.contents) : stripped,
319
+ options
320
+ );
295
321
  const images = extractBlockImages(block, results.length);
296
322
  results.push({
297
323
  block,
@@ -1823,6 +1849,8 @@ function compilePresentationPlan(doc, plan) {
1823
1849
  previous = Math.min(duration, Math.max(previous, time));
1824
1850
  return previous;
1825
1851
  });
1852
+ const authoredBlocks = flattenRenderableBlocks(doc.blocks);
1853
+ const buildHints = parsePresentationHints(doc.frontmatter?.[PRESENTATION_HINTS_KEY]);
1826
1854
  const blocks = [];
1827
1855
  checked.beats.forEach((beat, index) => {
1828
1856
  const start = starts[index];
@@ -1831,7 +1859,9 @@ function compilePresentationPlan(doc, plan) {
1831
1859
  const source = script.blocks.find(
1832
1860
  (block2) => beat.sourceStart >= block2.charStart && beat.sourceStart < block2.charEnd
1833
1861
  );
1862
+ const build = buildHints?.blocks.find((hint) => hint.blockId === source?.blockId)?.build ?? authoredBlocks.find((block2) => block2.id === source?.blockId)?.build;
1834
1863
  const base = {
1864
+ ...build ? { build } : {},
1835
1865
  id: beat.id,
1836
1866
  duration: end - start,
1837
1867
  startTime: start,
@@ -1,3 +1,33 @@
1
+ // src/schemas/Build.ts
2
+ var BUILD_MODES = ["off", "items", "branches", "chunks"];
3
+ var BUILD_EFFECTS = ["appear", "fadeIn", "fadeInUp", "slideIn"];
4
+ var BUILD_PREVIOUS = ["keep", "dim"];
5
+ function recordBuildAdvance(log, event) {
6
+ if (!event.blockId || !event.sourceText || !Number.isInteger(event.step) || event.step < 0 || !Number.isFinite(event.time) || event.time < 0)
7
+ return log;
8
+ if (log.some(
9
+ (entry) => entry.blockId === event.blockId && entry.step === event.step && entry.sourceText === event.sourceText
10
+ ))
11
+ return log;
12
+ return [...log, { ...event, time: Math.max(log[log.length - 1]?.time ?? 0, event.time) }];
13
+ }
14
+ var BUILD_PARAM_KEYS = ["build", "buildEffect", "buildDuration", "buildPrevious"];
15
+ function parseBuildSpec(value) {
16
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
17
+ const spec = value;
18
+ if (Object.keys(spec).some((key) => !["mode", "effect", "duration", "previous"].includes(key)) || !BUILD_MODES.includes(spec.mode) || spec.effect !== void 0 && !BUILD_EFFECTS.includes(spec.effect) || spec.previous !== void 0 && !BUILD_PREVIOUS.includes(spec.previous) || spec.duration !== void 0 && (typeof spec.duration !== "number" || !Number.isFinite(spec.duration) || spec.duration < 0))
19
+ return void 0;
20
+ return { ...spec };
21
+ }
22
+ function buildSpecParams(build) {
23
+ return {
24
+ build: build.mode,
25
+ ...build.effect ? { buildEffect: build.effect } : {},
26
+ ...build.duration !== void 0 ? { buildDuration: String(build.duration) } : {},
27
+ ...build.previous ? { buildPrevious: build.previous } : {}
28
+ };
29
+ }
30
+
1
31
  // src/schemas/Transitions.ts
2
32
  var DEFAULT_TRANSITION_DURATION_SECONDS = 0.7;
3
33
  var TRANSITION_TYPES = [
@@ -166,6 +196,13 @@ function normalizeToken(value) {
166
196
  }
167
197
 
168
198
  export {
199
+ BUILD_MODES,
200
+ BUILD_EFFECTS,
201
+ BUILD_PREVIOUS,
202
+ recordBuildAdvance,
203
+ BUILD_PARAM_KEYS,
204
+ parseBuildSpec,
205
+ buildSpecParams,
169
206
  DEFAULT_TRANSITION_DURATION_SECONDS,
170
207
  TRANSITION_TYPES,
171
208
  TRANSITION_DIRECTIONS,
@@ -2,7 +2,7 @@ import {
2
2
  DEFAULT_THEME,
3
3
  applySurface,
4
4
  resolveFontFamily
5
- } from "./chunk-PFH3WV6J.js";
5
+ } from "./chunk-I4L4GYDN.js";
6
6
 
7
7
  // src/jsonForm/chooseControl.ts
8
8
  var ENUM_SEGMENTED_LIMIT = 4;