@bendyline/squisq 1.5.0 → 1.5.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 (124) hide show
  1. package/README.md +14 -1
  2. package/dist/{ContentContainer-DNx460y8.d.ts → ContentContainer-BXUlIf18.d.ts} +6 -2
  3. package/dist/{Doc-BsMkmzH8.d.ts → Doc-BBVGq1_9.d.ts} +94 -15
  4. package/dist/{ImageEditDoc-DvoeHMP9.d.ts → ImageEditDoc-FuyGtt6o.d.ts} +1 -1
  5. package/dist/{chunk-KZNXZ5SM.js → chunk-6ATE2PSM.js} +18 -7
  6. package/dist/chunk-6ATE2PSM.js.map +1 -0
  7. package/dist/{chunk-7MQGNR76.js → chunk-A4O7GIWE.js} +2 -27
  8. package/dist/chunk-A4O7GIWE.js.map +1 -0
  9. package/dist/chunk-ET53IIEP.js +1 -0
  10. package/dist/chunk-ET53IIEP.js.map +1 -0
  11. package/dist/{chunk-OKJOQXK4.js → chunk-HWVFJAAH.js} +38 -2
  12. package/dist/chunk-HWVFJAAH.js.map +1 -0
  13. package/dist/{chunk-2TMAWB4Y.js → chunk-IMSCRLLV.js} +1 -1
  14. package/dist/chunk-IMSCRLLV.js.map +1 -0
  15. package/dist/{chunk-4AKUQPEE.js → chunk-KKNUBQ6Y.js} +1 -1
  16. package/dist/chunk-KKNUBQ6Y.js.map +1 -0
  17. package/dist/{chunk-TVSGLUID.js → chunk-LDQ2HJIX.js} +3 -3
  18. package/dist/{chunk-4ZFRZBU2.js → chunk-LH7I6SH7.js} +777 -151
  19. package/dist/chunk-LH7I6SH7.js.map +1 -0
  20. package/dist/{chunk-K32VJONL.js → chunk-T5UK6YOB.js} +41 -2
  21. package/dist/chunk-T5UK6YOB.js.map +1 -0
  22. package/dist/{chunk-OH43RPTP.js → chunk-TEEEILMP.js} +12 -2
  23. package/dist/chunk-TEEEILMP.js.map +1 -0
  24. package/dist/{chunk-FR2RBTKO.js → chunk-TQWLI6S2.js} +93 -27
  25. package/dist/chunk-TQWLI6S2.js.map +1 -0
  26. package/dist/{chunk-LRCV7N7F.js → chunk-UY7KGQ5R.js} +2 -2
  27. package/dist/{chunk-3E5F2XMR.js → chunk-VWUFZ6ZG.js} +29 -2
  28. package/dist/chunk-VWUFZ6ZG.js.map +1 -0
  29. package/dist/doc/index.d.ts +4 -4
  30. package/dist/doc/index.js +16 -7
  31. package/dist/generate/index.d.ts +2 -80
  32. package/dist/generate/index.js +2 -7
  33. package/dist/imageEdit/index.d.ts +4 -4
  34. package/dist/index.d.ts +8 -8
  35. package/dist/index.js +32 -23
  36. package/dist/jsonForm/index.d.ts +42 -1
  37. package/dist/jsonForm/index.js +9 -1
  38. package/dist/markdown/index.d.ts +18 -2
  39. package/dist/markdown/index.js +5 -3
  40. package/dist/recommend/index.js +2 -2
  41. package/dist/schemas/index.d.ts +4 -4
  42. package/dist/schemas/index.js +3 -5
  43. package/dist/storage/index.d.ts +2 -2
  44. package/dist/storage/index.js +1 -1
  45. package/dist/story/index.d.ts +125 -11
  46. package/dist/story/index.js +16 -7
  47. package/dist/{themeLibrary-DQQKuRMx.d.ts → themeLibrary-CehcJhzz.d.ts} +1 -1
  48. package/dist/timing/index.js +3 -5
  49. package/dist/transform/index.d.ts +2 -2
  50. package/dist/transform/index.js +3 -3
  51. package/dist/versions/index.d.ts +1 -1
  52. package/package.json +1 -1
  53. package/src/__tests__/contentExtractor.test.ts +60 -0
  54. package/src/__tests__/coverBlock.test.ts +46 -1
  55. package/src/__tests__/customTemplateSample.test.ts +63 -2
  56. package/src/__tests__/customTemplatesFrontmatter.test.ts +31 -0
  57. package/src/__tests__/customThemesFrontmatter.test.ts +27 -0
  58. package/src/__tests__/inputDescriptors.test.ts +162 -0
  59. package/src/__tests__/jsonForm.tokens.test.ts +84 -0
  60. package/src/__tests__/markdown.test.ts +64 -0
  61. package/src/__tests__/markdownSanitize.test.ts +15 -0
  62. package/src/__tests__/markdownToDoc.test.ts +168 -0
  63. package/src/__tests__/mediaAnnotations.test.ts +27 -0
  64. package/src/__tests__/structuredData.test.ts +73 -3
  65. package/src/__tests__/templateMetadata.test.ts +11 -1
  66. package/src/__tests__/templates.test.ts +112 -0
  67. package/src/__tests__/validateDoc.test.ts +160 -0
  68. package/src/doc/annotationBlocks.ts +117 -0
  69. package/src/doc/customTemplatesFrontmatter.ts +7 -1
  70. package/src/doc/customThemesFrontmatter.ts +6 -1
  71. package/src/doc/docToMarkdown.ts +31 -1
  72. package/src/doc/getLayers.ts +11 -2
  73. package/src/doc/index.ts +12 -0
  74. package/src/doc/markdownToDoc.ts +80 -3
  75. package/src/doc/mediaAnnotations.ts +15 -33
  76. package/src/doc/standaloneAnnotation.ts +58 -0
  77. package/src/doc/structuredData.ts +92 -21
  78. package/src/doc/templates/__tests__/customTemplate.test.ts +21 -2
  79. package/src/doc/templates/comparisonBar.ts +8 -1
  80. package/src/doc/templates/coverBlock.ts +29 -2
  81. package/src/doc/templates/customTemplate.ts +10 -4
  82. package/src/doc/templates/factCard.ts +4 -2
  83. package/src/doc/templates/imageWithCaption.ts +10 -8
  84. package/src/doc/templates/index.ts +12 -2
  85. package/src/doc/templates/inputDescriptors.ts +432 -0
  86. package/src/doc/templates/photoGrid.ts +16 -39
  87. package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +267 -1
  88. package/src/doc/templates/tokens/resolveTokens.ts +0 -0
  89. package/src/doc/templates/videoWithCaption.ts +15 -14
  90. package/src/doc/utils/nearest.ts +59 -0
  91. package/src/doc/validate.ts +153 -54
  92. package/src/generate/contentExtractor.ts +148 -35
  93. package/src/generate/index.ts +0 -3
  94. package/src/jsonForm/index.ts +2 -0
  95. package/src/jsonForm/tokens.ts +78 -0
  96. package/src/markdown/annotationCoercion.ts +1 -1
  97. package/src/markdown/attrTokens.ts +19 -0
  98. package/src/markdown/index.ts +2 -1
  99. package/src/markdown/sanitize.ts +22 -2
  100. package/src/markdown/stringify.ts +12 -3
  101. package/src/markdown/utils.ts +62 -1
  102. package/src/schemas/CustomTemplates.ts +56 -4
  103. package/src/schemas/Doc.ts +39 -41
  104. package/src/schemas/themeCompile.ts +1 -1
  105. package/src/storage/ContentContainer.ts +6 -2
  106. package/src/transform/types.ts +1 -1
  107. package/dist/chunk-2TMAWB4Y.js.map +0 -1
  108. package/dist/chunk-3E5F2XMR.js.map +0 -1
  109. package/dist/chunk-4AKUQPEE.js.map +0 -1
  110. package/dist/chunk-4X3JQXNM.js +0 -35
  111. package/dist/chunk-4X3JQXNM.js.map +0 -1
  112. package/dist/chunk-4ZFRZBU2.js.map +0 -1
  113. package/dist/chunk-7MQGNR76.js.map +0 -1
  114. package/dist/chunk-FBKP5CAO.js +0 -145
  115. package/dist/chunk-FBKP5CAO.js.map +0 -1
  116. package/dist/chunk-FR2RBTKO.js.map +0 -1
  117. package/dist/chunk-K32VJONL.js.map +0 -1
  118. package/dist/chunk-KZNXZ5SM.js.map +0 -1
  119. package/dist/chunk-OH43RPTP.js.map +0 -1
  120. package/dist/chunk-OKJOQXK4.js.map +0 -1
  121. package/src/__tests__/slideshowGenerator.test.ts +0 -128
  122. package/src/generate/slideshowGenerator.ts +0 -280
  123. /package/dist/{chunk-TVSGLUID.js.map → chunk-LDQ2HJIX.js.map} +0 -0
  124. /package/dist/{chunk-LRCV7N7F.js.map → chunk-UY7KGQ5R.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/doc/templates/persistentLayers.ts","../src/doc/utils/applyRenderStyle.ts","../src/doc/templates/tokens/resolveTokens.ts","../src/doc/templates/customTemplate.ts","../src/doc/utils/nearest.ts","../src/doc/templates/inputDescriptors.ts","../src/doc/templates/metadata.ts","../src/doc/utils/themeUtils.ts","../src/doc/templates/captionUtils.ts","../src/doc/templates/titleBlock.ts","../src/doc/templates/sectionHeader.ts","../src/doc/templates/accentImage.ts","../src/doc/templates/statHighlight.ts","../src/doc/templates/quoteBlock.ts","../src/doc/templates/factCard.ts","../src/doc/templates/twoColumn.ts","../src/doc/templates/dateEvent.ts","../src/doc/templates/imageWithCaption.ts","../src/doc/templates/featureBlock.ts","../src/doc/templates/mapBlock.ts","../src/doc/templates/fullBleedQuote.ts","../src/doc/templates/listBlock.ts","../src/doc/templates/photoGrid.ts","../src/doc/templates/definitionCard.ts","../src/doc/templates/comparisonBar.ts","../src/doc/templates/pullQuote.ts","../src/doc/templates/videoWithCaption.ts","../src/doc/templates/videoPullQuote.ts","../src/doc/templates/dataTable.ts","../src/doc/templates/diagramLayout.ts","../src/doc/templates/diagramBlock.ts","../src/doc/templates/drawingLayout.ts","../src/doc/templates/drawingBlock.ts","../src/doc/templates/layoutLayout.ts","../src/doc/templates/layoutBlock.ts","../src/doc/templates/coverBlock.ts","../src/doc/templates/index.ts","../src/doc/utils/animationUtils.ts","../src/doc/utils/imageTreatment.ts","../src/doc/templateInputs.ts","../src/doc/customTemplatesFrontmatter.ts","../src/doc/customThemesFrontmatter.ts","../src/doc/structuredData.ts","../src/doc/standaloneAnnotation.ts","../src/doc/mediaAnnotations.ts","../src/doc/annotationBlocks.ts","../src/doc/markdownToDoc.ts","../src/doc/docToMarkdown.ts","../src/doc/resolveDocTheme.ts","../src/doc/templates/fallbackBlock.ts","../src/doc/getLayers.ts","../src/doc/audioMapping.ts","../src/doc/validate.ts"],"sourcesContent":["/**\n * Persistent Layers Expansion\n *\n * Expands persistent layer templates (solid backgrounds, gradients, overlays)\n * into raw Layer arrays. These layers are injected into each block based on\n * per-block flags (useBottomLayer, useTopLayer).\n *\n * This is shared code used by both site and efb-app doc renderers.\n *\n * Related Files:\n * - schemas/BlockTemplates.ts - PersistentLayerConfig types\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type {\n PersistentLayer,\n PersistentLayerTemplate,\n PersistentLayerConfig,\n SolidBackgroundConfig,\n GradientBackgroundConfig,\n ImageBackgroundConfig,\n PatternBackgroundConfig,\n VignetteConfig,\n AmbientGradientConfig,\n TitleCaptionConfig,\n CornerBrandingConfig,\n ProgressIndicatorConfig,\n} from '../../schemas/BlockTemplates.js';\nimport type { Theme } from '../../schemas/Theme.js';\nimport { isPersistentLayerTemplate } from '../../schemas/BlockTemplates.js';\nimport { oklchDarken, withAlpha } from '../../schemas/colorUtils.js';\n\n// ============================================\n// Gradient Presets\n// ============================================\n\nconst GRADIENT_PRESETS: Record<string, string> = {\n 'dark-vignette':\n 'radial-gradient(ellipse at center, rgba(26,32,44,0.8) 0%, rgba(0,0,0,0.95) 100%)',\n 'radial-dark': 'radial-gradient(ellipse at center, #1a202c 0%, #000000 100%)',\n // Warm presets deepen within their own hue — ending in the shared navy\n // made warm themes drift cold halfway down the frame.\n 'warm-sunset': 'linear-gradient(135deg, rgba(124,58,48,0.9) 0%, rgba(46,20,16,0.95) 100%)',\n 'cool-blue': 'linear-gradient(135deg, rgba(26,54,93,0.9) 0%, rgba(26,32,44,0.95) 100%)',\n 'earth-tones': 'linear-gradient(135deg, rgba(68,51,34,0.9) 0%, rgba(30,22,15,0.95) 100%)',\n};\n\n// ============================================\n// Background Layer Expansion\n// ============================================\n\n/**\n * Expand a solid background config to a Layer.\n */\nfunction expandSolidBackground(config: SolidBackgroundConfig): Layer {\n return {\n type: 'shape',\n id: 'persistent-bg-solid',\n content: {\n shape: 'rect',\n fill: config.color,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n}\n\n/**\n * Expand a gradient background config to a Layer.\n */\nfunction expandGradientBackground(config: GradientBackgroundConfig): Layer {\n const gradient = config.gradient ?? GRADIENT_PRESETS[config.preset ?? 'dark-vignette'];\n\n return {\n type: 'shape',\n id: 'persistent-bg-gradient',\n content: {\n shape: 'rect',\n fill: gradient,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n}\n\n/**\n * Expand an image background config to Layers.\n * Returns multiple layers: base image + dark overlay for readability.\n */\nfunction expandImageBackground(config: ImageBackgroundConfig): Layer[] {\n const layers: Layer[] = [];\n const opacity = config.opacity ?? 0.4;\n\n // Base image layer. `blur` softens the photo into an atmosphere layer\n // (the ImageLayer renderer over-scans blurred images so soft edges never\n // reveal the frame).\n const imageLayer: Layer = {\n type: 'image',\n id: 'persistent-bg-image',\n content: {\n src: config.src,\n alt: 'Background',\n fit: 'cover',\n ...(config.blur != null && config.blur > 0 ? { blur: config.blur } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: config.ambientMotion\n ? {\n type: config.ambientMotion,\n duration: 30, // Long ambient motion\n }\n : undefined,\n };\n\n layers.push(imageLayer);\n\n // Add dark overlay for text readability\n layers.push({\n type: 'shape',\n id: 'persistent-bg-overlay',\n content: {\n shape: 'rect',\n fill: `rgba(0,0,0,${0.5 + (1 - opacity) * 0.3})`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n });\n\n return layers;\n}\n\n/**\n * Expand a pattern background config to a Layer.\n */\nfunction expandPatternBackground(config: PatternBackgroundConfig): Layer {\n // 'noise' is film grain: a full-bleed rect whose paint is replaced by a\n // static feTurbulence field clipped to the rect (see ShapeFilter).\n if (config.pattern === 'noise') {\n return {\n type: 'shape',\n id: 'persistent-grain',\n content: {\n shape: 'rect',\n fill: '#000000',\n filter: { type: 'noise', opacity: config.opacity ?? 0.05 },\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n }\n\n // dots / grid / diagonal render as native SVG <pattern> fills.\n const opacity = config.opacity ?? 0.06;\n return {\n type: 'shape',\n id: 'persistent-bg-pattern',\n content: {\n shape: 'rect',\n pattern: {\n kind: config.pattern,\n color: config.color ?? '#ffffff',\n size: Math.round(24 * (config.scale ?? 1)),\n opacity,\n },\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n}\n\n/**\n * Expand a vignette config: transparent center darkening toward the frame\n * edges. Rides the CSS-gradient rect path in the renderer.\n */\nfunction expandVignette(config: VignetteConfig): Layer {\n const strength = Math.max(0, Math.min(1, config.strength ?? 0.3));\n const color = config.color ?? '#000000';\n const edge = withAlpha(color, strength);\n const clear = withAlpha(color, 0);\n return {\n type: 'shape',\n id: 'persistent-vignette',\n content: {\n shape: 'rect',\n fill: `radial-gradient(ellipse at center, ${clear} 55%, ${edge} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n}\n\n/**\n * Expand an ambient drifting gradient: an oversized surface gradient with\n * a very slow Ken Burns loop. Deterministic (no randomness).\n */\nfunction expandAmbientGradient(config: AmbientGradientConfig, theme?: Theme): Layer {\n const from = config.from ?? theme?.colors.backgroundLight ?? '#1a202c';\n const to = config.to ?? (theme ? oklchDarken(theme.colors.background, 0.04) : '#0f141d');\n return {\n type: 'shape',\n id: 'persistent-ambient-gradient',\n content: {\n shape: 'rect',\n fill: `linear-gradient(150deg, ${from} 0%, ${to} 100%)`,\n },\n // Oversized so the slow pan never reveals the frame edge.\n position: { x: '-5%', y: '-5%', width: '110%', height: '110%' },\n animation: { type: 'slowZoom', panDirection: 'left', duration: config.duration ?? 40 },\n };\n}\n\n// ============================================\n// Overlay Layer Expansion\n// ============================================\n\n/**\n * Expand a title caption config to Layers.\n */\nfunction expandTitleCaption(config: TitleCaptionConfig): Layer[] {\n const layers: Layer[] = [];\n const fontSize = config.fontSize ?? 18;\n const hasSubtitle = !!config.subtitle;\n\n // Position calculations\n const isBottom = config.position.includes('bottom');\n const isLeft = config.position.includes('left');\n\n // Even padding around the pill (3% from edges)\n const pad = 3; // percent\n\n // Pill dimensions — taller when subtitle present\n const pillHeight = hasSubtitle ? '15%' : '7%';\n // Position above player controls\n const bgYPos = isBottom ? (hasSubtitle ? '78%' : '84%') : '2%';\n const bgXPos = isLeft ? `${pad}%` : '68%';\n\n // Thumbnail sizing — use different width/height to appear square in 16:9 viewport\n // In 16:9, 6% of width ≈ 10.7% of height in pixels, so use ~6%w x 10%h for visual square\n const thumbW = hasSubtitle ? '6%' : '5%';\n const thumbH = hasSubtitle ? '10%' : '5%';\n const thumbPad = 1.5; // padding inside pill around thumbnail (percent)\n\n // Text X offset: after thumbnail + padding, or just pill padding\n const textX =\n config.showThumbnail && isLeft\n ? `${pad + thumbPad + 6 + thumbPad}%` // pill edge + thumb padding + thumb width + gap\n : `${pad + thumbPad}%`;\n // Text area width: pill width minus thumbnail area minus padding\n const textWidth = config.showThumbnail\n ? '19%' // narrower when thumbnail takes space\n : '24%';\n\n // Title Y: top of text area inside pill\n const titleYPos = isBottom ? (hasSubtitle ? '80%' : '86%') : '4%';\n\n // Background pill for readability\n layers.push({\n type: 'shape',\n id: 'persistent-caption-bg',\n content: {\n shape: 'rect',\n fill: 'rgba(0,0,0,0.6)',\n borderRadius: 8,\n },\n position: {\n x: bgXPos,\n y: bgYPos,\n width: config.showThumbnail ? '30%' : '28%',\n height: pillHeight,\n },\n });\n\n // Thumbnail if configured\n if (config.showThumbnail && config.thumbnailSrc) {\n const thumbX = isLeft ? `${pad + thumbPad}%` : '93%';\n // Center thumbnail vertically in pill\n const thumbY = isBottom ? (hasSubtitle ? `${78 + thumbPad}%` : '85%') : '3%';\n\n layers.push({\n type: 'image',\n id: 'persistent-caption-thumb',\n content: {\n src: config.thumbnailSrc,\n alt: 'Article thumbnail',\n fit: 'cover',\n },\n position: {\n x: thumbX,\n y: thumbY,\n width: thumbW,\n height: thumbH,\n },\n });\n }\n\n // Title text — single line, cropped with ellipsis\n layers.push({\n type: 'text',\n id: 'persistent-caption-title',\n content: {\n text: config.title,\n style: {\n fontSize,\n fontWeight: 'bold',\n color: 'rgba(255,255,255,0.9)',\n textAlign: isLeft ? 'left' : 'right',\n maxLines: 1,\n },\n },\n position: {\n x: textX,\n y: titleYPos,\n width: textWidth,\n },\n });\n\n // Subtitle text (e.g., short URL)\n if (hasSubtitle) {\n const subtitleYPos = isBottom ? '85.5%' : '8%';\n\n layers.push({\n type: 'text',\n id: 'persistent-caption-subtitle',\n content: {\n text: config.subtitle!,\n style: {\n fontSize: 18,\n fontWeight: 'normal',\n color: 'rgba(255,255,255,0.55)',\n textAlign: isLeft ? 'left' : 'right',\n },\n },\n position: {\n x: textX,\n y: subtitleYPos,\n width: textWidth,\n },\n });\n }\n\n return layers;\n}\n\n/**\n * Expand a corner branding config to a Layer.\n */\nfunction expandCornerBranding(config: CornerBrandingConfig): Layer {\n const isBottom = config.position.includes('bottom');\n const isLeft = config.position.includes('left');\n\n const xPos = isLeft ? '3%' : '90%';\n const yPos = isBottom ? '94%' : '3%';\n\n if (config.isImage) {\n return {\n type: 'image',\n id: 'persistent-branding',\n content: {\n src: config.content,\n alt: 'Branding',\n fit: 'contain',\n },\n position: {\n x: xPos,\n y: yPos,\n width: '8%',\n height: '4%',\n },\n };\n }\n\n return {\n type: 'text',\n id: 'persistent-branding',\n content: {\n text: config.content,\n style: {\n fontSize: 14,\n fontWeight: 'normal',\n color: 'rgba(255,255,255,0.6)',\n },\n },\n position: {\n x: xPos,\n y: yPos,\n },\n };\n}\n\n/**\n * Expand a progress indicator config to a Layer.\n * Note: Progress requires block context, returns placeholder bar.\n */\nfunction expandProgressIndicator(config: ProgressIndicatorConfig): Layer {\n const color = config.color ?? 'rgba(255,255,255,0.3)';\n const y = config.position === 'top' ? '1%' : '98%';\n\n // Simple bar style - actual progress would need to be set per-block\n return {\n type: 'shape',\n id: 'persistent-progress',\n content: {\n shape: 'rect',\n fill: color,\n borderRadius: 2,\n },\n position: {\n x: '5%',\n y,\n width: '90%',\n height: '0.5%',\n },\n };\n}\n\n// ============================================\n// Main Expansion Function\n// ============================================\n\n/**\n * Expand a single persistent layer (template or raw) to raw Layer(s).\n */\nexport function expandPersistentLayer(layer: PersistentLayer, theme?: Theme): Layer[] {\n // If already a raw Layer, return as-is\n if (!isPersistentLayerTemplate(layer)) {\n return [layer as Layer];\n }\n\n const template = layer as PersistentLayerTemplate;\n const config = template.config;\n\n switch (config.type) {\n case 'solidBackground':\n return [expandSolidBackground(config)];\n case 'gradientBackground':\n return [expandGradientBackground(config)];\n case 'imageBackground':\n return expandImageBackground(config);\n case 'patternBackground':\n return [expandPatternBackground(config)];\n case 'vignette':\n return [expandVignette(config)];\n case 'ambientGradient':\n return [expandAmbientGradient(config, theme)];\n case 'titleCaption':\n return expandTitleCaption(config);\n case 'cornerBranding':\n return [expandCornerBranding(config)];\n case 'progressIndicator':\n return [expandProgressIndicator(config)];\n default:\n console.warn('Unknown persistent layer template:', (config as Record<string, unknown>).type);\n return [];\n }\n}\n\n/**\n * Expand all persistent layers in a config to raw Layer arrays.\n */\nexport function expandPersistentLayers(\n layers: PersistentLayer[] | undefined,\n theme?: Theme,\n): Layer[] {\n if (!layers || layers.length === 0) {\n return [];\n }\n\n // Use reduce+concat instead of flatMap for Coherent GT compatibility (ES2017)\n return layers.reduce<Layer[]>(\n (acc, layer) => acc.concat(expandPersistentLayer(layer, theme)),\n [],\n );\n}\n\n// ============================================\n// Style Presets (legacy helper — prefer theme.persistentLayers)\n// ============================================\n\n/** Legacy style preset names. Prefer using `Theme.persistentLayers` directly. */\nexport type DocStylePreset = 'minimal' | 'documentary' | 'branded' | 'cinematic' | 'clean';\n\n/**\n * Get a PersistentLayerConfig from a style preset.\n *\n * @param preset - Style preset name\n * @param articleTitle - Article title for title caption\n * @param heroSrc - Optional hero image for cinematic style\n */\nexport function getDocStyleConfig(\n preset: DocStylePreset,\n articleTitle: string,\n heroSrc?: string,\n subtitle?: string,\n): PersistentLayerConfig {\n switch (preset) {\n case 'minimal':\n return {};\n\n case 'documentary':\n return {\n bottomLayers: [\n {\n template: 'gradientBackground',\n config: { type: 'gradientBackground', preset: 'dark-vignette' },\n },\n ],\n topLayers: [\n {\n template: 'titleCaption',\n config: {\n type: 'titleCaption',\n title: articleTitle,\n subtitle,\n position: 'bottom-left',\n fontSize: 24,\n showThumbnail: !!heroSrc,\n thumbnailSrc: heroSrc,\n },\n },\n ],\n };\n\n case 'branded':\n return {\n bottomLayers: [\n {\n template: 'gradientBackground',\n config: { type: 'gradientBackground', preset: 'cool-blue' },\n },\n ],\n topLayers: [\n {\n template: 'titleCaption',\n config: {\n type: 'titleCaption',\n title: articleTitle,\n subtitle,\n position: 'bottom-left',\n fontSize: 26,\n showThumbnail: !!heroSrc,\n thumbnailSrc: heroSrc,\n },\n },\n ],\n };\n\n case 'cinematic':\n return {\n bottomLayers: heroSrc\n ? [\n {\n template: 'imageBackground',\n config: {\n type: 'imageBackground',\n src: heroSrc,\n blur: 12,\n opacity: 0.3,\n ambientMotion: 'zoomIn',\n },\n },\n ]\n : [\n {\n template: 'gradientBackground',\n config: { type: 'gradientBackground', preset: 'radial-dark' },\n },\n ],\n topLayers: [\n {\n template: 'titleCaption',\n config: {\n type: 'titleCaption',\n title: articleTitle,\n subtitle,\n position: 'bottom-right',\n fontSize: 22,\n },\n },\n ],\n };\n\n case 'clean':\n return {\n bottomLayers: [\n {\n template: 'solidBackground',\n config: { type: 'solidBackground', color: '#1a202c' },\n },\n ],\n };\n\n default:\n return {};\n }\n}\n\n/**\n * Get persistent layers from a Theme. Returns the theme's baked-in\n * persistentLayers config, or an empty config when the theme has none.\n */\nexport function getPersistentLayersFromTheme(theme: Theme): PersistentLayerConfig {\n return theme.persistentLayers ?? {};\n}\n\n/**\n * Resolve the effective persistent-layer config for a doc: the doc's own\n * config wins **wholesale** when present; only docs with no persistent\n * layers inherit the theme's.\n *\n * Doc-wins-wholesale (rather than merging) protects pre-baked documents\n * that carry their own background/branding layers — merging a theme's\n * atmosphere on top of those would double up backgrounds and stack\n * overlays the doc author never saw.\n */\nexport function resolvePersistentLayers(\n doc: { persistentLayers?: PersistentLayerConfig },\n theme: Theme,\n): PersistentLayerConfig | undefined {\n const docConfig = doc.persistentLayers;\n const hasDocLayers =\n !!docConfig &&\n ((docConfig.bottomLayers?.length ?? 0) > 0 || (docConfig.topLayers?.length ?? 0) > 0);\n if (hasDocLayers) return docConfig;\n const themeConfig = theme.persistentLayers;\n const hasThemeLayers =\n !!themeConfig &&\n ((themeConfig.bottomLayers?.length ?? 0) > 0 || (themeConfig.topLayers?.length ?? 0) > 0);\n return hasThemeLayers ? themeConfig : docConfig;\n}\n\n/**\n * Compose a block's layers between pre-expanded persistent bottom/top\n * layers, honoring the per-block `useBottomLayer` / `useTopLayer` opt-outs.\n * Single shared implementation for `expandDocBlocks` and `getLayers`.\n */\nexport function wrapWithPersistentLayers(\n layers: Layer[],\n block: { useBottomLayer?: boolean; useTopLayer?: boolean },\n bottomLayers: Layer[],\n topLayers: Layer[],\n): Layer[] {\n if (bottomLayers.length === 0 && topLayers.length === 0) return layers;\n const useBottom = block.useBottomLayer !== false;\n const useTop = block.useTopLayer !== false;\n return [...(useBottom ? bottomLayers : []), ...layers, ...(useTop ? topLayers : [])];\n}\n","/**\n * Render-style post-pass — applies a theme's mechanical motion defaults to\n * template-generated layers.\n *\n * This is the intent-free half of theme motion (see `themedEntrance` in\n * [themeUtils.ts](./themeUtils.ts) for the intent-bearing half): it never\n * decides *what kind* of entrance a layer gets, only\n *\n * 1. scales existing animation durations/delays by the theme's\n * `style.animationSpeed` multiplier (1.4 = calmer/slower, 0.7 = snappier,\n * 1.0 = no-op), and\n * 2. gives full-bleed cover imagery with no authored animation a gentle,\n * deterministic Ken Burns when the theme opts into ambient motion.\n *\n * Applied ONLY to template-generated layers — raw authored `block.layers`\n * are never touched, and an explicit `animation` (including\n * `{ type: 'none' }`) always wins.\n */\n\nimport type { Layer, Animation } from '../../schemas/Doc.js';\nimport type { Theme } from '../../schemas/Theme.js';\nimport { SeededRandom, hashString } from '../../random/index.js';\n\n/** The gentle Ken Burns variants ambient motion rotates between. */\nconst AMBIENT_VARIANTS: readonly Animation[] = [\n { type: 'slowZoom', direction: 'in' },\n { type: 'slowZoom', direction: 'out' },\n { type: 'slowZoom', panDirection: 'left' },\n { type: 'slowZoom', panDirection: 'right' },\n];\n\n/** Minimum ambient Ken Burns duration — short blocks shouldn't rush the pan. */\nconst MIN_AMBIENT_DURATION_SECONDS = 8;\n\nfunction isZeroish(v: number | string | undefined): boolean {\n return v === 0 || v === '0' || v === '0%';\n}\n\n/**\n * Full-bleed cover imagery is the only shape we can safely blanket-animate:\n * anything smaller (strips, insets, grid cells) was placed deliberately and\n * a zoom would fight the composition.\n */\nfunction isFullBleedCoverMedia(layer: Layer): boolean {\n if (layer.type !== 'image' && layer.type !== 'video') return false;\n const { position } = layer;\n if (!isZeroish(position.x) || !isZeroish(position.y)) return false;\n if (position.width !== '100%' || position.height !== '100%') return false;\n const fit = (layer.content as { fit?: string }).fit;\n return fit === undefined || fit === 'cover';\n}\n\n/**\n * Whether the theme asks for ambient Ken Burns on otherwise-static imagery.\n * `defaultImageAnimation: 'slowZoom'` is treated as an alias for the boolean\n * (that's what themes authoring it were reaching for).\n */\nexport function themeWantsAmbientMotion(theme: Theme): boolean {\n return (\n theme.renderStyle.ambientMotion === true ||\n theme.renderStyle.defaultImageAnimation === 'slowZoom'\n );\n}\n\n/**\n * Apply the theme's mechanical motion defaults to a block's\n * template-generated layers. Pure: returns new layer objects where a change\n * applies and the original objects where none does.\n *\n * @param layers Template-generated layers (never raw authored layers)\n * @param block The source block — supplies the ambient-motion seed and duration\n * @param theme Resolved theme\n */\nexport function applyRenderStyleToLayers(\n layers: Layer[],\n block: { id: string; duration?: number },\n theme: Theme,\n): Layer[] {\n const speed = theme.style.animationSpeed ?? 1.0;\n const ambient = themeWantsAmbientMotion(theme);\n if (speed === 1.0 && !ambient) return layers;\n\n return layers.map((layer) => {\n let animation = layer.animation;\n\n // Ambient Ken Burns for full-bleed cover imagery with no authored\n // animation. Deterministic per block+layer so re-renders, export\n // frames, and screenshots always agree.\n if (!animation && ambient && isFullBleedCoverMedia(layer)) {\n const rng = new SeededRandom(hashString(`${block.id}:${layer.id}`));\n const variant = rng.pickRequired(AMBIENT_VARIANTS as Animation[]);\n animation = {\n ...variant,\n duration: Math.max(MIN_AMBIENT_DURATION_SECONDS, block.duration ?? 0),\n };\n }\n\n if (!animation || animation.type === 'none') {\n return animation === layer.animation ? layer : { ...layer, animation };\n }\n\n // Speed scaling — single site so templates never double-scale.\n if (speed !== 1.0) {\n animation = {\n ...animation,\n ...(animation.duration != null ? { duration: animation.duration * speed } : {}),\n ...(animation.delay != null ? { delay: animation.delay * speed } : {}),\n };\n }\n\n return animation === layer.animation ? layer : { ...layer, animation };\n });\n}\n","/**\n * Token resolver for user-defined custom templates.\n *\n * Custom templates author their layers with placeholder strings — e.g.\n * a TextLayer whose `content.text` is `\"Welcome, {title}!\"` — and the\n * resolver substitutes those placeholders against the source Block's\n * actual data at render time. Built-in templates don't use the\n * resolver; they construct strings imperatively in their own\n * functions.\n *\n * Token grammar (v2):\n * {name} — bare name, e.g. `{title}`, `{content}`,\n * `{children}`\n * {name:arg} — name with a string argument, e.g. `{image:0}`\n * (Nth image in the block's body), `{attr:key}`\n * {name|default} — pipe default: used when the token resolves to\n * the empty string, e.g. `{title|Untitled}`\n * {name:arg|default}— combined, e.g. `{attr:subtitle|Untitled}` or\n * `{image:1|fallback.jpg}` (the default is applied\n * AFTER arg splitting, so it works in image src)\n * {{name}} — escaped — renders as the literal text `{name}`\n * (lets an author include a real `{title}` in body\n * text without it being interpreted as a token)\n *\n * Unknown tokens are left as literal text so the author can see what's\n * misspelled. Tokens with no value (e.g. `{title}` when block.title is\n * undefined) collapse to the empty string — or to the pipe default when\n * one is supplied.\n *\n * Token semantics:\n * - {title} — Block.title\n * - {content} — extractPlainText() of Block.contents, joined\n * with single spaces\n * - {children} — comma-separated list of Block.children[].title\n * - {attr:key} — Block.templateOverrides[key] ?? templateData[key]\n * ?? metadata[key], stringified\n * - {image:N} — In TextLayer: the Nth image's `alt` text.\n * In ImageLayer.content.src: the Nth image's URL\n * (if no Nth image exists and no default is given,\n * the layer is dropped).\n *\n * Per-item tokens (only meaningful inside a layer that carries a\n * `repeat` directive — see {@link LayerRepeat}):\n * - {item} — image alt / child title / list-item text\n * - {item:src} — image src (empty for children / list items)\n * - {item:label} — item label (same as {item} for children/list items)\n * - {index} — 1-based position of the item in its collection\n */\n\nimport type { Block, Layer, ImageLayer } from '../../../schemas/Doc.js';\nimport type { CustomTemplateLayer } from '../../../schemas/CustomTemplates.js';\nimport type { MarkdownBlockNode, MarkdownNode } from '../../../markdown/types.js';\nimport { extractPlainText, walkMarkdownTree } from '../../../markdown/utils.js';\n\ninterface ImageRef {\n url: string;\n alt: string;\n}\n\n/**\n * One resolved iteration item, produced by {@link collectRepeatItems}\n * and threaded into token resolution for a repeated layer's clone.\n */\ninterface RepeatItem {\n /** `{item}` — image alt / child title / list-item text. */\n text: string;\n /** `{item:src}` — image URL (undefined for children / list items). */\n src?: string;\n /** `{item:label}` — a display label for the item. */\n label?: string;\n /** `{index}` — 1-based position in the source collection. */\n index: number;\n}\n\n// Matches a single token: name, optional `:arg`, optional `|default`.\n// The arg stops at `|` so the default can be split off afterwards; the\n// default runs to the closing brace. Recreated per call site would be\n// wasteful, but a shared global is safe: String.replace resets a global\n// regex's lastIndex, and resolution is single-threaded/sequential.\nconst TOKEN_RE = /\\{(\\w+)(?::([^}|]+))?(?:\\|([^}]*))?\\}/g;\n// Matches an escaped `{{…}}` block (no nested braces).\nconst ESCAPE_RE = /\\{\\{([^{}]*)\\}\\}/g;\n\n// Sentinels chosen so they can't collide with real user content and\n// survive plain-text copy; safer than zero-width characters.\nconst ESCAPED_OPEN = ' ESC_OPEN ';\nconst ESCAPED_CLOSE = ' ESC_CLOSE ';\n\n/**\n * Walk the block's body content collecting every `image` node in\n * document order — including inline images nested inside paragraphs,\n * list items, etc. `findNodesByType` only knows the block-node union;\n * we use the more permissive `walkMarkdownTree` so inline image nodes\n * (`type: 'image'`, an inline type) are also picked up.\n *\n * Returns an empty array when the block has no `contents` or none of\n * the contents contain images.\n */\nfunction collectImages(block: Block): ImageRef[] {\n if (!block.contents) return [];\n const out: ImageRef[] = [];\n const visit = (node: MarkdownNode): void => {\n if (node.type === 'image') {\n const img = node as MarkdownNode & { url?: string; alt?: string };\n out.push({ url: img.url ?? '', alt: img.alt ?? '' });\n }\n };\n for (const node of block.contents as MarkdownBlockNode[]) {\n walkMarkdownTree(node, (n) => {\n visit(n);\n return;\n });\n }\n return out;\n}\n\n/**\n * Walk the block's body content collecting the plain text of every\n * top-level list item, in document order. Mirrors {@link collectImages}\n * but for `{ repeat: { source: 'listItems' } }`. Nested lists are not\n * descended into separately — a nested list's text is already folded\n * into its parent item's text, so we skip children of a `listItem` to\n * avoid double-counting.\n *\n * Exported for the same reason `resolveTokens` is: it's the collector\n * the repeat machinery relies on and is unit-tested directly.\n */\nexport function collectListItems(block: Block): string[] {\n if (!block.contents) return [];\n const out: string[] = [];\n for (const node of block.contents as MarkdownBlockNode[]) {\n walkMarkdownTree(node, (n) => {\n if (n.type === 'listItem') {\n const text = extractPlainText(n).replace(/\\s+/g, ' ').trim();\n if (text) out.push(text);\n // Skip descent: this item's nested lists are already part of\n // its plain text above; don't visit them as separate items.\n return true;\n }\n return;\n });\n }\n return out;\n}\n\n/**\n * Plain-text join of Block.contents. Block-level boundaries become\n * spaces so a `{content}` substitution reads as natural prose rather\n * than running paragraphs together.\n */\nfunction getBlockBodyText(block: Block): string {\n if (!block.contents) return '';\n return (block.contents as MarkdownBlockNode[])\n .map((node) => extractPlainText(node).trim())\n .filter((s) => s.length > 0)\n .join(' ');\n}\n\n/** Comma-joined child titles (omits children without a title). */\nfunction getChildrenSummary(block: Block): string {\n if (!block.children || block.children.length === 0) return '';\n return block.children\n .map((c) => c.title?.trim())\n .filter((t): t is string => Boolean(t))\n .join(', ');\n}\n\nfunction escapeBraces(text: string): string {\n return text.replace(ESCAPE_RE, (_match, inner: string) => ESCAPED_OPEN + inner + ESCAPED_CLOSE);\n}\n\nfunction unescapeBraces(text: string): string {\n return text\n .replace(new RegExp(ESCAPED_OPEN, 'g'), '{')\n .replace(new RegExp(ESCAPED_CLOSE, 'g'), '}');\n}\n\n/**\n * Substitute every `{token}` occurrence in `text` using `resolve`,\n * preserving `{{token}}` as a literal `{token}`.\n *\n * `resolve` returns `null` for an unknown token (the whole match,\n * including any `|default`, is left literal) or a string value. When the\n * value is the empty string and a `|default` was supplied, the default\n * is used. Escaped `{{…}}` blocks are shielded first so tokens inside\n * them are never interpreted.\n */\nfunction substitute(\n text: string,\n resolve: (name: string, arg: string | undefined) => string | null,\n): string {\n const escaped = escapeBraces(text);\n const tokenized = escaped.replace(\n TOKEN_RE,\n (match, name: string, arg: string | undefined, def: string | undefined) => {\n const value = resolve(name, arg);\n if (value == null) return match;\n if (value === '' && def !== undefined) return def;\n return value;\n },\n );\n return unescapeBraces(tokenized);\n}\n\n/**\n * Look up a single token as text. Returns null when the token is\n * unknown — caller treats null as \"leave the placeholder literal\".\n * `{item}` / `{index}` resolve to null when there is no per-item\n * context (i.e. the layer isn't inside a `repeat`).\n */\nfunction resolveToken(\n name: string,\n arg: string | undefined,\n block: Block,\n images: ImageRef[],\n item: RepeatItem | undefined,\n): string | null {\n switch (name) {\n case 'title':\n return block.title ?? '';\n case 'content':\n return getBlockBodyText(block);\n case 'children':\n return getChildrenSummary(block);\n case 'attr': {\n if (!arg) return '';\n const raw =\n block.templateOverrides?.[arg] ?? block.templateData?.[arg] ?? block.metadata?.[arg];\n return raw == null ? '' : String(raw);\n }\n case 'image': {\n const idx = arg ? parseInt(arg, 10) : 0;\n if (!Number.isFinite(idx) || idx < 0 || idx >= images.length) return '';\n return images[idx].alt;\n }\n case 'item': {\n if (!item) return null;\n if (!arg) return item.text;\n if (arg === 'src') return item.src ?? '';\n if (arg === 'label') return item.label ?? '';\n return '';\n }\n case 'index':\n return item ? String(item.index) : null;\n default:\n return null;\n }\n}\n\nfunction substituteText(\n text: string,\n block: Block,\n images: ImageRef[],\n item: RepeatItem | undefined,\n): string {\n return substitute(text, (name, arg) => resolveToken(name, arg, block, images, item));\n}\n\n/**\n * Resolve tokens inside an ImageLayer's `content.src`. `{image:N}`\n * resolves to the Nth image's URL (not its alt); `{item:src}` and\n * `{attr:key}` resolve as usual. When an `{image:N}` has no matching\n * image AND no `|default`, the whole layer is dropped (returns null) so\n * we never leave a broken URL. A supplied default suppresses the drop.\n */\nfunction resolveImageSrc(\n src: string,\n block: Block,\n images: ImageRef[],\n item: RepeatItem | undefined,\n): string | null {\n // Optimization: nothing to resolve.\n if (!src.includes('{')) return src;\n\n let dropped = false;\n const escaped = escapeBraces(src);\n const tokenized = escaped.replace(\n TOKEN_RE,\n (match, name: string, arg: string | undefined, def: string | undefined) => {\n if (name === 'image') {\n const idx = arg ? parseInt(arg, 10) : 0;\n const ok = Number.isFinite(idx) && idx >= 0 && idx < images.length;\n if (ok) return images[idx].url;\n if (def !== undefined) return def;\n dropped = true;\n return '';\n }\n const value = resolveToken(name, arg, block, images, item);\n if (value == null) return match;\n if (value === '' && def !== undefined) return def;\n return value;\n },\n );\n\n if (dropped) return null;\n return unescapeBraces(tokenized);\n}\n\n/**\n * Parse the leading numeric magnitude of a position field (px number or\n * `%`-string). Used to advance the cursor between repeated clones.\n */\nfunction parseMagnitude(value: number | string | undefined): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const m = value.match(/-?\\d*\\.?\\d+/);\n if (m) return parseFloat(m[0]);\n }\n return 0;\n}\n\n/**\n * Offset a position coordinate by `delta`, preserving its unit: a bare\n * number stays a number; a `%`-string (or any suffixed string) keeps\n * its suffix.\n */\nfunction offsetCoord(base: number | string | undefined, delta: number): number | string {\n if (base == null) return delta;\n if (typeof base === 'number') return base + delta;\n const m = base.match(/^(\\s*)(-?\\d*\\.?\\d+)(.*)$/);\n if (m) {\n const next = parseFloat(m[2]) + delta;\n return `${m[1]}${next}${m[3]}`;\n }\n return base;\n}\n\n/** Build the per-item list for a `repeat` source collection. */\nfunction collectRepeatItems(\n source: 'images' | 'children' | 'listItems',\n block: Block,\n images: ImageRef[],\n): RepeatItem[] {\n switch (source) {\n case 'images':\n return images.map((img, i) => ({\n text: img.alt,\n src: img.url,\n label: img.alt,\n index: i + 1,\n }));\n case 'children':\n return (block.children ?? []).map((child, i) => ({\n text: child.title ?? '',\n label: child.title ?? '',\n index: i + 1,\n }));\n case 'listItems':\n return collectListItems(block).map((text, i) => ({ text, label: text, index: i + 1 }));\n default:\n return [];\n }\n}\n\n/**\n * Expand a `repeat` layer into one clone per item of its source\n * collection. Each clone is offset along `direction` by the cumulative\n * `(width|height) + gap`, has `repeat` stripped, gets a per-clone id,\n * and resolves per-item tokens against its item.\n */\nfunction expandRepeat(layer: CustomTemplateLayer, block: Block, images: ImageRef[]): Layer[] {\n const repeat = layer.repeat;\n if (!repeat) {\n const resolved = resolveLayer(layer, block, images, undefined);\n return resolved ? [resolved] : [];\n }\n\n const items = collectRepeatItems(repeat.source, block, images);\n const direction = repeat.direction ?? 'column';\n const gap = repeat.gap ?? 24;\n const limit =\n repeat.max != null && repeat.max >= 0 ? Math.min(repeat.max, items.length) : items.length;\n\n const out: Layer[] = [];\n let cursor = 0;\n for (let i = 0; i < limit; i++) {\n const position = { ...layer.position };\n if (direction === 'row') {\n position.x = offsetCoord(position.x, cursor);\n } else {\n position.y = offsetCoord(position.y, cursor);\n }\n\n const clone = { ...layer, position, id: `${layer.id}-${i}` } as CustomTemplateLayer;\n delete clone.repeat;\n\n const resolved = resolveLayer(clone, block, images, items[i]);\n if (resolved) out.push(resolved);\n\n const dim = direction === 'row' ? layer.position.width : layer.position.height;\n cursor += parseMagnitude(dim) + gap;\n }\n return out;\n}\n\n/**\n * Walk the layer array and produce a new array with all placeholder\n * tokens resolved. Layers carrying a `repeat` directive are expanded\n * into one clone per source item. ImageLayers whose `{image:N}` token\n * doesn't match a real image (and has no default) are filtered out.\n * Other layer types pass through unchanged.\n *\n * Pure function — does not mutate the input.\n */\nexport function resolveTokens(layers: readonly CustomTemplateLayer[], block: Block): Layer[] {\n const images = collectImages(block);\n const out: Layer[] = [];\n for (const layer of layers) {\n if (layer.repeat) {\n out.push(...expandRepeat(layer, block, images));\n } else {\n const resolved = resolveLayer(layer, block, images, undefined);\n if (resolved) out.push(resolved);\n }\n }\n return out;\n}\n\nfunction resolveLayer(\n layer: CustomTemplateLayer,\n block: Block,\n images: ImageRef[],\n item: RepeatItem | undefined,\n): Layer | null {\n switch (layer.type) {\n case 'text':\n return {\n ...layer,\n content: {\n ...layer.content,\n text: substituteText(layer.content.text, block, images, item),\n },\n };\n case 'image': {\n const nextSrc = resolveImageSrc(layer.content.src, block, images, item);\n if (nextSrc == null) return null;\n const next: ImageLayer = {\n ...layer,\n content: {\n ...layer.content,\n src: nextSrc,\n },\n };\n // Also try to substitute the alt text if it carries a token.\n if (layer.content.alt && layer.content.alt.includes('{')) {\n next.content.alt = substituteText(layer.content.alt, block, images, item);\n }\n return next;\n }\n default:\n return layer;\n }\n}\n","/**\n * Custom template factory.\n *\n * Turns a `CustomTemplateDefinition` (user-authored visual design with\n * placeholder tokens) into a runtime `TemplateFunction` that can be\n * dropped into the template registry alongside built-ins.\n *\n * The generated function's job is small: read the source `Block` from\n * `context.block` and hand the definition's layers off to\n * `resolveTokens` for placeholder substitution. The hard work (drag-\n * to-position, token grammar, image lookup) happens in the designer\n * UI and the resolver, not here.\n */\n\nimport type { Block, Layer } from '../../schemas/Doc.js';\nimport type {\n RawLayersInput,\n TemplateContext,\n TemplateFunction,\n} from '../../schemas/BlockTemplates.js';\nimport type { CustomTemplateDefinition } from '../../schemas/CustomTemplates.js';\nimport { resolveTokens } from './tokens/resolveTokens.js';\n\n/**\n * Build a template function for a user-authored custom template.\n *\n * The returned function is registered under `def.name` in the merged\n * registry (see `buildRegistry` in `templates/index.ts`). At expansion\n * time it receives the standard `TemplateBlock` + `TemplateContext`\n * pair; the context's `block` field carries the source markdown-derived\n * block whose data drives token resolution.\n *\n * When `context.block` is missing (shouldn't happen in the normal\n * pipeline but defensively handled), the layers are returned with\n * tokens left unresolved — better visible placeholders than a thrown\n * error.\n */\nexport function makeCustomTemplateFn(\n def: CustomTemplateDefinition,\n): TemplateFunction<RawLayersInput> {\n return (input: RawLayersInput, context: TemplateContext): Layer[] => {\n // `{attr:…}` reads the source block's `templateOverrides` /\n // `templateData` / `metadata`. Prefer `context.block` (the raw\n // block the expander attaches); fall back to `input` (the merged\n // TemplateBlock, itself a Block superset) so attributes still\n // resolve if `context.block` is ever absent.\n const block = context.block ?? (input as unknown as Block | undefined);\n if (!block) return def.layers.slice() as Layer[];\n return resolveTokens(def.layers, block);\n };\n}\n","/**\n * Shared \"did-you-mean\" edit-distance helpers.\n *\n * A leaf module (no imports) so both `doc/validate.ts` and\n * `doc/templates/inputDescriptors.ts` can share one implementation without\n * risking an import cycle through `templates/index.ts`.\n */\n\n/** Levenshtein edit distance between two strings. */\nexport function levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = new Array<number>(n + 1);\n let curr = new Array<number>(n + 1);\n for (let j = 0; j <= n; j++) prev[j] = j;\n for (let i = 1; i <= m; i++) {\n curr[0] = i;\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);\n }\n [prev, curr] = [curr, prev];\n }\n return prev[n];\n}\n\n/**\n * Nearest candidate name by edit distance, or `undefined` when nothing is\n * plausibly close. \"Close\" means distance ≤ `maxDistance` (default 2) OR\n * ≤ 40% of the input length — the shared threshold both call sites use.\n *\n * @param options.map - Optional post-processor applied to the winning\n * candidate (e.g. `resolveTemplateName` to canonicalize an alias hit).\n * @param options.maxDistance - Absolute distance floor for \"close\" (default 2).\n */\nexport function nearestName(\n input: string,\n candidates: Iterable<string>,\n options?: { map?: (best: string) => string; maxDistance?: number },\n): string | undefined {\n const maxDistance = options?.maxDistance ?? 2;\n let best: string | undefined;\n let bestDist = Infinity;\n const lower = input.toLowerCase();\n for (const candidate of candidates) {\n const dist = levenshtein(lower, candidate.toLowerCase());\n if (dist < bestDist) {\n bestDist = dist;\n best = candidate;\n }\n }\n if (best && (bestDist <= maxDistance || bestDist <= Math.ceil(input.length * 0.4))) {\n return options?.map ? options.map(best) : best;\n }\n return undefined;\n}\n","/**\n * Template input descriptors — a typed catalog of the inline `{[…]}` params\n * each built-in template understands, plus the pure coercion + lint logic\n * that consumes it.\n *\n * Two jobs, one source of truth:\n *\n * - **Coercion (`coerceTemplateParams`)** turns a flat `key → raw string`\n * param map (from a `{[name key=value]}` annotation, always strings) into a\n * partial typed input the template functions can consume directly — e.g.\n * `center=\"47.6,-122.3\"` → `{ lat: 47.6, lng: -122.3 }`, `zoom=9` → `9`.\n * This is what makes `{[map center=\"47.6,-122.3\" zoom=9]}` render with no\n * changes to any template function. Unknown keys ALWAYS pass through\n * untouched (as strings), so coercion is never lossy; coercion failures are\n * returned as `warnings` and the raw string is preserved.\n *\n * - **Linting (`lintTemplateParams`)** checks a param map against the\n * descriptors and reports unknown keys (with a did-you-mean suggestion),\n * values outside a closed enum / that fail coercion, and missing required\n * inputs. The document validator surfaces these as diagnostics.\n *\n * This mirrors the proven block-meta descriptor pattern in\n * `markdown/annotationCoercion.ts` (`BLOCK_META_KEY_DESCRIPTORS`), extended\n * from block geometry/timing to per-template content inputs.\n *\n * The descriptors are the `{[…]}` counterpart of the template input\n * interfaces in `schemas/BlockTemplates.ts` — only keys/enums that provably\n * exist on those interfaces are declared. Keys that can't be meaningfully\n * expressed as a single inline string (e.g. a `dataTable`'s `rows`) are still\n * declared with `coerce: 'string'` so they aren't mis-flagged as unknown.\n */\n\nimport { KNOWN_BLOCK_META_KEYS, parseNumber } from '../../markdown/annotationCoercion.js';\nimport { resolveTemplateName } from './index.js';\nimport { nearestName } from '../utils/nearest.js';\n\n// ============================================\n// Types\n// ============================================\n\n/** How a raw `{[…]}` param string is coerced into a typed input value. */\nexport type InputCoercion =\n | 'string'\n | 'number'\n | 'boolean'\n | 'latLng'\n | 'labeledPair'\n | 'stringList';\n\n/**\n * Editor/validator-facing description of a single template input key: what it\n * does, how to coerce its raw string, and either its closed set of valid\n * values or a free-form format hint. `values`/`valueHint` are mutually\n * exclusive (a key has a closed enum OR a hint, not both).\n */\nexport interface TemplateInputDescriptor {\n /** The `{[name key=…]}` param key, matching the template input interface. */\n key: string;\n /** One-line description of the input. */\n description: string;\n /** Coercion strategy for the raw string (default `'string'`). */\n coerce?: InputCoercion;\n /** Closed set of valid values, for keys backed by an enum. */\n values?: readonly string[];\n /** Format hint shown when the value is free-form (no closed set). */\n valueHint?: string;\n /** When true, the validator warns if the key is absent and can't be derived. */\n required?: boolean;\n}\n\n/** A single lint finding produced by {@link lintTemplateParams}. */\nexport interface TemplateParamFinding {\n kind: 'unknown-input' | 'invalid-input-value' | 'missing-input';\n key: string;\n message: string;\n /** Did-you-mean candidate (for `unknown-input`). */\n suggestion?: string;\n}\n\n// ============================================\n// Shared enum vocabularies (verified against BlockTemplates.ts)\n// ============================================\n\n/** `ambientMotion` on image/video/map templates. */\nconst AMBIENT_MOTION_VALUES = ['zoomIn', 'zoomOut', 'panLeft', 'panRight'] as const;\n/** `captionPosition` on imageWithCaption / videoWithCaption. */\nconst CAPTION_POSITION_VALUES = ['bottom', 'top', 'center'] as const;\n/** `mapStyle` — mirrors `MapTileStyle` in `schemas/Doc.ts`. */\nconst MAP_STYLE_VALUES = ['terrain', 'satellite', 'road', 'toner', 'watercolor'] as const;\n/** `mood` on dateEvent. */\nconst DATE_EVENT_MOOD_VALUES = ['neutral', 'somber', 'celebratory'] as const;\n\n/** Shared free-form hint for `colorScheme`-style keys. */\nconst COLOR_SCHEME_HINT = \"theme color-scheme name — e.g. 'blue', 'green'\";\n\n// ============================================\n// Descriptor tables\n// ============================================\n\n/**\n * Inputs shared by every template via `BaseTemplateBlock`. Merged into each\n * template's known-key set for coercion + lint. Block-level timing/geometry\n * keys (`duration`, `x`, `y`, …) are NOT here — those stay owned by the\n * block-meta registry (`KNOWN_BLOCK_META_KEYS`) and are folded into the lint\n * known-key set separately.\n */\nexport const BASE_INPUT_DESCRIPTORS: readonly TemplateInputDescriptor[] = [\n {\n key: 'imageTreatment',\n description: \"Per-block photographic grade override ('none' opts out)\",\n values: ['none', 'mono', 'duotone', 'warm', 'cool'],\n },\n {\n key: 'useBottomLayer',\n description: \"Show the doc's bottom persistent layers\",\n coerce: 'boolean',\n },\n { key: 'useTopLayer', description: \"Show the doc's top persistent layers\", coerce: 'boolean' },\n];\n\n/**\n * Per-template input descriptors, keyed by canonical template id. A template\n * absent from this map has no descriptors and is exempt from lint (custom\n * templates and not-yet-described built-ins).\n */\nexport const TEMPLATE_INPUT_DESCRIPTORS: Readonly<\n Record<string, readonly TemplateInputDescriptor[]>\n> = {\n title: [\n { key: 'title', description: 'Main title text' },\n { key: 'subtitle', description: 'Subtitle or tagline' },\n { key: 'backgroundColor', description: 'Background color', valueHint: 'CSS color' },\n ],\n statHighlight: [\n { key: 'stat', description: 'The statistic (e.g. \"89%\", \"2x\")' },\n { key: 'description', description: 'What the stat means' },\n { key: 'detail', description: 'Additional detail or context' },\n { key: 'colorScheme', description: 'Color scheme for the stat', valueHint: COLOR_SCHEME_HINT },\n ],\n quote: [\n { key: 'quote', description: 'The quote text' },\n { key: 'attribution', description: 'Attribution (author, source)' },\n ],\n pullQuote: [\n { key: 'text', description: 'Quote text' },\n { key: 'attribution', description: 'Attribution' },\n ],\n twoColumn: [\n {\n key: 'left',\n description: 'Left column, as \"label|sublabel\"',\n coerce: 'labeledPair',\n required: true,\n },\n {\n key: 'right',\n description: 'Right column, as \"label|sublabel\"',\n coerce: 'labeledPair',\n required: true,\n },\n { key: 'header', description: 'Optional header above the columns' },\n { key: 'leftColor', description: 'Left column color scheme', valueHint: COLOR_SCHEME_HINT },\n { key: 'rightColor', description: 'Right column color scheme', valueHint: COLOR_SCHEME_HINT },\n ],\n dateEvent: [\n { key: 'date', description: 'The date (e.g. \"July 14, 1974\")' },\n { key: 'description', description: 'What happened' },\n { key: 'footer', description: 'Optional footer text' },\n { key: 'mood', description: 'Emotional tone', values: DATE_EVENT_MOOD_VALUES },\n ],\n imageWithCaption: [\n { key: 'imageSrc', description: 'Path to the image file', required: true },\n { key: 'imageAlt', description: 'Alt text for accessibility' },\n { key: 'caption', description: 'Caption text' },\n { key: 'captionPosition', description: 'Caption placement', values: CAPTION_POSITION_VALUES },\n { key: 'ambientMotion', description: 'Slow zoom/pan effect', values: AMBIENT_MOTION_VALUES },\n { key: 'isTitle', description: 'Style the caption as a title', coerce: 'boolean' },\n { key: 'subtitle', description: 'Subtitle (only when isTitle)' },\n { key: 'imageCredit', description: 'Photo credit / artist name' },\n { key: 'imageLicense', description: 'License identifier' },\n ],\n leftFeature: FEATURE_DESCRIPTORS(),\n rightFeature: FEATURE_DESCRIPTORS(),\n map: [\n {\n key: 'center',\n description: 'Map center as \"lat,lng\"',\n coerce: 'latLng',\n required: true,\n },\n { key: 'zoom', description: 'Zoom level (4-16)', coerce: 'number' },\n { key: 'mapStyle', description: 'Map tile style', values: MAP_STYLE_VALUES },\n { key: 'title', description: 'Optional title overlay' },\n { key: 'caption', description: 'Optional caption' },\n ],\n photoGrid: [\n { key: 'images', description: 'Comma-separated image paths', coerce: 'stringList' },\n { key: 'caption', description: 'Optional caption below the grid' },\n {\n key: 'ambientMotion',\n description: 'Slow zoom/pan on the largest image',\n values: AMBIENT_MOTION_VALUES,\n },\n ],\n comparisonBar: [\n { key: 'leftLabel', description: 'Left bar label', required: true },\n { key: 'leftValue', description: 'Left bar numeric value', coerce: 'number', required: true },\n { key: 'rightLabel', description: 'Right bar label', required: true },\n { key: 'rightValue', description: 'Right bar numeric value', coerce: 'number', required: true },\n { key: 'unit', description: 'Unit label (e.g. \"km\")' },\n { key: 'colorScheme', description: 'Color scheme', valueHint: COLOR_SCHEME_HINT },\n ],\n dataTable: [\n { key: 'title', description: 'Optional title above the table' },\n { key: 'headers', description: 'Comma-separated header cells', coerce: 'stringList' },\n // `rows`/`align` are structured (string[][] / alignment[]) and can't be\n // expressed as a single inline string — declared so they aren't flagged\n // as unknown when supplied via a data fence's overriding params.\n { key: 'rows', description: 'Table rows (supply via a data fence)' },\n { key: 'align', description: 'Per-column alignment (supply via a data fence)' },\n { key: 'colorScheme', description: 'Header color scheme', valueHint: COLOR_SCHEME_HINT },\n ],\n videoWithCaption: [\n { key: 'videoSrc', description: 'Path to the video file', required: true },\n { key: 'posterSrc', description: 'Poster frame path' },\n { key: 'videoAlt', description: 'Alt text for accessibility' },\n { key: 'clipStart', description: 'Clip start time (seconds)', coerce: 'number' },\n { key: 'clipEnd', description: 'Clip end time (seconds)', coerce: 'number' },\n { key: 'caption', description: 'Caption text' },\n { key: 'captionPosition', description: 'Caption placement', values: CAPTION_POSITION_VALUES },\n { key: 'videoCredit', description: 'Video credit / artist name' },\n { key: 'videoLicense', description: 'License identifier' },\n ],\n};\n\n/** Shared descriptor list for `leftFeature`/`rightFeature` (identical inputs). */\nfunction FEATURE_DESCRIPTORS(): readonly TemplateInputDescriptor[] {\n return [\n { key: 'imageSrc', description: 'Path to the feature image', required: true },\n { key: 'imageAlt', description: 'Alt text for accessibility' },\n { key: 'imageWidth', description: 'Explicit display width in px', coerce: 'number' },\n { key: 'imageHeight', description: 'Explicit display height in px', coerce: 'number' },\n { key: 'title', description: 'Heading text next to the image' },\n { key: 'body', description: 'Body text below the title' },\n ];\n}\n\n// ============================================\n// Descriptor lookup\n// ============================================\n\n/** All descriptors (template-specific + base) for a canonical template id. */\nfunction descriptorsFor(canonical: string): readonly TemplateInputDescriptor[] | undefined {\n const specific = TEMPLATE_INPUT_DESCRIPTORS[canonical];\n if (!specific) return undefined;\n return [...specific, ...BASE_INPUT_DESCRIPTORS];\n}\n\nfunction descriptorMap(\n descriptors: readonly TemplateInputDescriptor[],\n): Map<string, TemplateInputDescriptor> {\n return new Map(descriptors.map((d) => [d.key, d]));\n}\n\n// ============================================\n// Coercion (M2)\n// ============================================\n\n/**\n * Coerce a flat `{[…]}` param map into a partial typed template input.\n *\n * Known descriptor keys are coerced per their `coerce` strategy; unknown keys\n * (and `string`-typed keys) pass through unchanged. A coercion failure leaves\n * the raw string in place and adds a human-readable `warnings` entry — the\n * output is never lossy. Returns a fresh object; the caller must NOT feed\n * `templateOverrides` back in — only the ephemeral merged input is coerced.\n */\nexport function coerceTemplateParams(\n template: string | undefined,\n params: Record<string, string>,\n): { input: Record<string, unknown>; warnings: string[] } {\n const input: Record<string, unknown> = {};\n const warnings: string[] = [];\n const descriptors = template ? descriptorsFor(resolveTemplateName(template)) : undefined;\n const byKey = descriptors ? descriptorMap(descriptors) : undefined;\n\n for (const [key, raw] of Object.entries(params)) {\n const coerce = byKey?.get(key)?.coerce ?? 'string';\n const { value, warning } = coerceValue(coerce, raw);\n if (warning) {\n warnings.push(`\"${key}\": ${warning}`);\n input[key] = raw; // preserve the raw string on failure\n } else {\n input[key] = value;\n }\n }\n\n return { input, warnings };\n}\n\n/** Coerce one raw string per a coercion kind. `warning` set on failure. */\nfunction coerceValue(coerce: InputCoercion, raw: string): { value: unknown; warning?: string } {\n switch (coerce) {\n case 'number': {\n const n = parseNumber(raw);\n return n == null\n ? { value: raw, warning: `not a number (${JSON.stringify(raw)})` }\n : { value: n };\n }\n case 'boolean':\n return coerceBoolean(raw);\n case 'latLng':\n return coerceLatLng(raw);\n case 'labeledPair':\n return { value: coerceLabeledPair(raw) };\n case 'stringList':\n return { value: coerceStringList(raw) };\n case 'string':\n default:\n return { value: raw };\n }\n}\n\n/** `'true'`/`''` (bare flag) → true, `'false'` → false; anything else fails. */\nfunction coerceBoolean(raw: string): { value: unknown; warning?: string } {\n const t = raw.trim().toLowerCase();\n if (t === 'true' || t === '') return { value: true };\n if (t === 'false') return { value: false };\n return { value: raw, warning: `not a boolean (${JSON.stringify(raw)}) — expected true or false` };\n}\n\n/** `\"47.6,-122.3\"` → `{ lat, lng }`; malformed → failure (raw preserved). */\nfunction coerceLatLng(raw: string): { value: unknown; warning?: string } {\n const parts = raw.split(',');\n if (parts.length === 2) {\n const lat = parseNumber(parts[0]);\n const lng = parseNumber(parts[1]);\n if (lat != null && lng != null) return { value: { lat, lng } };\n }\n return { value: raw, warning: `not a \"lat,lng\" pair (${JSON.stringify(raw)})` };\n}\n\n/** `\"Label|Sub\"` → `{ label, sublabel? }`; sublabel omitted when empty. */\nfunction coerceLabeledPair(raw: string): { label: string; sublabel?: string } {\n const sep = raw.indexOf('|');\n if (sep < 0) return { label: raw.trim() };\n const label = raw.slice(0, sep).trim();\n const sublabel = raw.slice(sep + 1).trim();\n return sublabel ? { label, sublabel } : { label };\n}\n\n/** `\"a, b ,c\"` → `['a','b','c']` (trimmed, empties dropped). */\nfunction coerceStringList(raw: string): string[] {\n return raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n}\n\n// ============================================\n// Linting (M5)\n// ============================================\n\n/**\n * Lint a template's `{[…]}` params against its descriptors. Returns findings\n * for unknown keys, values outside a closed enum / that fail coercion, and\n * missing required inputs. Templates without descriptors (custom or\n * not-yet-described built-ins) return `[]`.\n */\nexport function lintTemplateParams(\n template: string,\n params: Record<string, string>,\n): TemplateParamFinding[] {\n const canonical = resolveTemplateName(template);\n const specific = TEMPLATE_INPUT_DESCRIPTORS[canonical];\n if (!specific) return [];\n\n const findings: TemplateParamFinding[] = [];\n const allDescriptors = [...specific, ...BASE_INPUT_DESCRIPTORS];\n const byKey = descriptorMap(allDescriptors);\n // Known keys for did-you-mean: descriptor keys ∪ block-meta keys (which are\n // honored on the same annotation, e.g. `duration`, `transition`).\n const knownKeys = new Set<string>([...byKey.keys(), ...Object.keys(KNOWN_BLOCK_META_KEYS)]);\n\n for (const [key, raw] of Object.entries(params)) {\n const descriptor = byKey.get(key);\n if (!descriptor) {\n if (knownKeys.has(key)) continue; // block-meta key — valid, not a template input\n const suggestion = nearestName(key, knownKeys);\n findings.push({\n kind: 'unknown-input',\n key,\n message:\n `Unknown input \"${key}\" for template \"${canonical}\"` +\n (suggestion ? `. Did you mean \"${suggestion}\"?` : ''),\n ...(suggestion ? { suggestion } : {}),\n });\n continue;\n }\n\n // Value validation: closed enum first, then coercion.\n if (descriptor.values) {\n const v = raw.trim();\n if (v !== '' && !descriptor.values.includes(v)) {\n findings.push({\n kind: 'invalid-input-value',\n key,\n message: `Invalid value ${JSON.stringify(raw)} for \"${key}\" — expected one of: ${descriptor.values.join(', ')}`,\n });\n }\n } else if (descriptor.coerce && descriptor.coerce !== 'string') {\n const { warning } = coerceValue(descriptor.coerce, raw);\n if (warning) {\n findings.push({ kind: 'invalid-input-value', key, message: `\"${key}\": ${warning}` });\n }\n }\n }\n\n // Missing required inputs (checked over template-specific descriptors only;\n // base inputs are never required).\n for (const descriptor of specific) {\n if (descriptor.required && params[descriptor.key] == null) {\n findings.push({\n kind: 'missing-input',\n key: descriptor.key,\n message: `Missing required input \"${descriptor.key}\" for template \"${canonical}\"`,\n });\n }\n }\n\n return findings;\n}\n","/**\n * Canonical, framework-free UI metadata for every built-in block template.\n *\n * This is the single source of truth for the human-readable label and the\n * one-sentence description of each template id in {@link templateRegistry}.\n * The editor's `TemplatePicker` renders its gallery from this map (joining\n * each id with a locally-defined SVG icon, which can't live in this\n * framework-free package), so a template only has to be added here and to\n * `templateRegistry` for it to surface in the editor.\n *\n * `templateMetadata.test.ts` asserts this map and `templateRegistry` stay\n * exactly 1:1 — adding a template to one without the other fails the build.\n *\n * Order matters: pickers iterate this object's insertion order, so it doubles\n * as the canonical gallery order.\n */\n\nexport interface TemplateMetadata {\n /** Human-readable label shown in pickers (e.g. \"Image with Caption\"). */\n label: string;\n /** One-sentence description of when to reach for this template. */\n description: string;\n}\n\nexport const TEMPLATE_METADATA: Record<string, TemplateMetadata> = {\n title: {\n label: 'Title',\n description: 'A bold opening slide with large title text, perfect for covers and chapters.',\n },\n sectionHeader: {\n label: 'Section Header',\n description: 'A clean section break with a prominent title and optional subtitle.',\n },\n statHighlight: {\n label: 'Stat Highlight',\n description: 'Showcases a single key number or metric with supporting context.',\n },\n quote: {\n label: 'Quote',\n description: 'Displays a stylized pull quote with decorative marks and attribution.',\n },\n factCard: {\n label: 'Fact Card',\n description: 'Presents a focused fact or insight with a labeled header and body text.',\n },\n twoColumn: {\n label: 'Two Column',\n description: 'Divides the slide into two equal side-by-side content columns.',\n },\n dateEvent: {\n label: 'Date Event',\n description: 'Highlights a date-based event or milestone with a prominent date display.',\n },\n imageWithCaption: {\n label: 'Image with Caption',\n description: 'Displays a full-bleed image with an optional caption below.',\n },\n leftFeature: {\n label: 'Left Feature',\n description: 'Image on the left, title and body text stacked on the right.',\n },\n rightFeature: {\n label: 'Right Feature',\n description: 'Image on the right, title and body text stacked on the left.',\n },\n map: {\n label: 'Map',\n description: 'Embeds an interactive map centered on a geographic location.',\n },\n fullBleedQuote: {\n label: 'Full Bleed Quote',\n description: 'A full-width quote that spans the entire slide for maximum impact.',\n },\n list: {\n label: 'List',\n description: 'Renders a bulleted or numbered list in a clean, card-style layout.',\n },\n photoGrid: {\n label: 'Photo Grid',\n description: 'Arranges multiple photos in a 2×2 or 3×3 mosaic grid.',\n },\n definitionCard: {\n label: 'Definition Card',\n description: 'Shows a term and its definition in a structured, dictionary-style card.',\n },\n comparisonBar: {\n label: 'Comparison Bar',\n description: 'Visualizes two or more values side-by-side with labeled horizontal bars.',\n },\n pullQuote: {\n label: 'Pull Quote',\n description: 'A stylized pull quote with large decorative marks and centered text.',\n },\n videoWithCaption: {\n label: 'Video with Caption',\n description: 'Embeds a video player with an optional caption below.',\n },\n videoPullQuote: {\n label: 'Video Pull Quote',\n description: 'Combines a video panel with a highlighted pull quote side-by-side.',\n },\n dataTable: {\n label: 'Data Table',\n description: 'Renders tabular data in a clean, styled table with a header row.',\n },\n diagram: {\n label: 'Diagram',\n description:\n 'Renders sub-headings as connected nodes — edit visually as a node-and-edge diagram.',\n },\n layout: {\n label: 'Layout',\n description:\n \"Free-form 2D canvas — drag layers into place. Use for one-off block layouts that don't fit a template.\",\n },\n drawing: {\n label: 'Drawing',\n description: 'Free-form sketches — draw shapes, paths, and text directly on a 2D surface.',\n },\n};\n","/**\n * Theme Utilities\n *\n * Template-facing helpers that read from the Theme object on the\n * TemplateContext. Templates call these instead of hard-coding style\n * decisions, so the same template code adapts to any theme.\n */\n\nimport type { TemplateContext } from '../../schemas/BlockTemplates.js';\nimport type { ThemeColorScheme, RenderStyle } from '../../schemas/Theme.js';\nimport type { Animation, AnimationType, ImageTreatment } from '../../schemas/Doc.js';\nimport { resolveFontFamily } from '../../schemas/fontStacks.js';\nimport { oklchDarken, withAlpha } from '../../schemas/colorUtils.js';\n\n// ============================================\n// Color Scheme Resolution\n// ============================================\n\n/** Fallback color scheme when a name is not found in the theme. */\nconst FALLBACK_COLOR_SCHEME: ThemeColorScheme = {\n bg: '#1a365d',\n text: '#63b3ed',\n accent: '#90cdf4',\n};\n\n/**\n * Resolve a named color scheme from the theme, with a safe fallback.\n * Templates call this instead of `COLOR_SCHEMES[name]`.\n */\nexport function resolveColorScheme(\n context: TemplateContext,\n name: string | undefined,\n): ThemeColorScheme {\n const schemes = context.theme.colorSchemes;\n if (name && name in schemes) return schemes[name];\n // Fall back to first defined scheme, then hard fallback\n const keys = Object.keys(schemes);\n if (keys.length > 0) return schemes[keys[0]];\n return FALLBACK_COLOR_SCHEME;\n}\n\n// ============================================\n// Font Family Resolution\n// ============================================\n\n/** Safe system font fallbacks. */\nconst FALLBACK_BODY = 'system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif';\nconst FALLBACK_TITLE = 'Georgia, \"Times New Roman\", serif';\nconst FALLBACK_MONO = 'Consolas, \"Courier New\", monospace';\n\n/**\n * Get the theme font family for a given role, resolved to a CSS string.\n * Templates call this to set `fontFamily` on text layers.\n *\n * Theme fields are structured `FontFamily` references (`{ stackId }` or\n * `{ custom: { name, fallback } }`); this helper resolves them to a\n * CSS font-family string with safe fallbacks.\n */\nexport function getThemeFont(context: TemplateContext, role: 'title' | 'body' | 'mono'): string {\n const { typography } = context.theme;\n switch (role) {\n case 'title':\n return resolveFontFamily(typography.titleFont, FALLBACK_TITLE);\n case 'body':\n return resolveFontFamily(typography.bodyFont, FALLBACK_BODY);\n case 'mono':\n return resolveFontFamily(typography.monoFont, FALLBACK_MONO);\n }\n}\n\n// ============================================\n// Font Sizing\n// ============================================\n\n/**\n * Calculate a theme-aware scaled font size.\n * Applies the theme's typography scale multipliers on top of the\n * viewport-based font scale from LayoutStrategy.\n *\n * @param basePx Base font size designed for 1920×1080\n * @param context Template context (includes fontScale, layout, theme)\n * @param isTitle Whether this is title text (uses titleScale) or body (bodyScale)\n */\nexport function themedFontSize(\n basePx: number,\n context: TemplateContext,\n isTitle: boolean = false,\n): number {\n const { fontScale, layout, theme } = context;\n\n // Base scale from viewport + orientation\n const layoutScale = isTitle ? layout.titleScale : layout.bodyScale;\n\n // Theme multiplier (defaults to 1.0 when not set)\n const themeScale = isTitle\n ? (theme.typography.titleScale ?? 1.0)\n : (theme.typography.bodyScale ?? 1.0);\n\n return Math.round(basePx * fontScale * layoutScale * themeScale);\n}\n\n// ============================================\n// Animation Helpers\n// ============================================\n\n/**\n * Scale an animation duration by the theme's animationSpeed multiplier.\n * A speed of 1.0 returns the base duration unchanged.\n */\nexport function scaleAnimationDuration(baseDuration: number, context: TemplateContext): number {\n const speed = context.theme.style.animationSpeed ?? 1.0;\n return baseDuration * speed;\n}\n\n/**\n * Get the theme's default animation type for a layer kind.\n * Returns `undefined` when the theme doesn't specify one (template picks its own).\n */\nexport function getDefaultAnimation(\n context: TemplateContext,\n layerType: 'text' | 'image',\n): AnimationType | undefined {\n const rs: RenderStyle = context.theme.renderStyle;\n return layerType === 'text' ? rs.defaultTextAnimation : rs.defaultImageAnimation;\n}\n\n/**\n * Theme-aware entrance animation for a template layer.\n *\n * The theme's `renderStyle.defaultTextAnimation` / `defaultImageAnimation`\n * overrides the entrance *type*; the template keeps its authored duration,\n * delay, and stagger. Templates opt in per layer:\n *\n * ```ts\n * animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 2 })\n * ```\n *\n * `slowZoom` is excluded — it's an ambient treatment handled by the\n * `applyRenderStyleToLayers` post-pass, not an entrance.\n */\nexport function themedEntrance(\n context: TemplateContext,\n layerType: 'text' | 'image',\n fallback: Animation,\n): Animation {\n const type = getDefaultAnimation(context, layerType);\n if (!type || type === 'slowZoom' || type === fallback.type) return fallback;\n return { ...fallback, type };\n}\n\n// ============================================\n// Style Helpers\n// ============================================\n\n/**\n * Whether the theme defaults to text shadows.\n * Templates should consult this when choosing `shadow: true/false`.\n */\nexport function shouldUseShadow(context: TemplateContext): boolean {\n return context.theme.style.textShadow ?? true;\n}\n\n/**\n * Get the theme's overlay opacity for image-backed blocks.\n */\nexport function getOverlayOpacity(context: TemplateContext): number {\n return context.theme.style.overlayOpacity ?? 0.5;\n}\n\n/**\n * Resolve the effective photographic treatment for a block's imagery.\n *\n * Precedence: the block's `imageTreatment` override ('none' opts out, a\n * type forces that grade) → the theme's `style.imageTreatment` → none.\n * Duotone tints default to the theme primary. Templates pass the result\n * straight into `ImageLayer.content.treatment`.\n */\nexport function themedImageTreatment(\n context: TemplateContext,\n override?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool',\n): ImageTreatment | undefined {\n if (override === 'none') return undefined;\n const themeTreatment = context.theme.style.imageTreatment;\n const base: ImageTreatment | undefined = override\n ? { type: override, strength: themeTreatment?.strength }\n : themeTreatment;\n if (!base || base.type === 'none') return undefined;\n if (base.type === 'duotone' && !base.color) {\n return { ...base, color: context.theme.colors.primary };\n }\n return base;\n}\n\n/**\n * Theme-derived surface gradient for text-first templates.\n *\n * Runs from the theme's alternate surface into a slightly deepened\n * background, staying inside the theme's own hue. Templates must use\n * this instead of hard-coding a dark endpoint (`#0f1520`, `#1e2030`, …):\n * hard-coded navy endpoints made light themes render dark panels and\n * warm themes drift cold halfway down the block.\n */\nexport function themedSurfaceGradient(context: TemplateContext, angleDeg = 160): string {\n const { background, backgroundLight } = context.theme.colors;\n return `linear-gradient(${angleDeg}deg, ${backgroundLight} 0%, ${oklchDarken(background, 0.04)} 100%)`;\n}\n\n/**\n * Translucent overlay tint derived from the theme background, for text\n * bands/scrims painted over imagery. Light themes get a light scrim with\n * dark theme text on top; dark themes get the familiar dark scrim —\n * `theme.colors.text` stays legible on it by construction.\n */\nexport function themedScrim(context: TemplateContext, alpha?: number): string {\n const a = alpha ?? Math.max(0.55, getOverlayOpacity(context));\n return withAlpha(context.theme.colors.background, a);\n}\n\n// ============================================\n// Template Hint Access\n// ============================================\n\n/**\n * Read a per-template hint from the theme's renderStyle.\n * Returns `fallback` when the hint isn't defined.\n *\n * @example\n * ```ts\n * const entrance = getTemplateHint(context, 'statHighlight', 'entrance', 'subtle');\n * ```\n */\nexport function getTemplateHint<T extends string | number | boolean>(\n context: TemplateContext,\n templateName: string,\n key: string,\n fallback: T,\n): T {\n const hints = context.theme.renderStyle.templateHints;\n if (!hints) return fallback;\n const tplHints = hints[templateName];\n if (!tplHints || !(key in tplHints)) return fallback;\n return tplHints[key] as T;\n}\n","/**\n * Template Utilities\n *\n * Shared helpers for doc block templates:\n * - Caption text cleaning\n * - Common layer construction patterns\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\n\nconst TRAILING_PUNCT_RE = /[.;:!?]+$/;\n\n/**\n * Strip trailing punctuation from a caption string.\n * Image captions on blocks look cleaner without terminal periods,\n * semicolons, or other sentence-ending punctuation.\n */\nexport function cleanCaption(text: string): string {\n return text.replace(TRAILING_PUNCT_RE, '').trim();\n}\n\n/**\n * Create a full-screen background shape layer.\n * Most text-based templates start with this as their first layer.\n */\nexport function createBackgroundLayer(id: string, fill: string): Layer {\n return {\n type: 'shape',\n id,\n content: { shape: 'rect', fill },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n}\n\n/**\n * Estimate the rendered height (px) of a wrapped text block.\n *\n * Templates are pure functions with no access to real text metrics, but\n * stacked lockups (quote + attribution, fact + explanation + source) need\n * to place each element relative to the previous one instead of at fixed\n * slots — fixed slots leave 200px voids for short content and collide for\n * long content. The 0.52 average-glyph-width factor is tuned for the\n * sans/serif faces the built-in themes use; it only needs to be right\n * within ~20% for spacing purposes.\n */\nexport function estimateTextHeight(\n text: string,\n fontSizePx: number,\n maxWidthPx: number,\n lineHeight: number,\n): number {\n const avgCharWidth = fontSizePx * 0.52;\n const charsPerLine = Math.max(8, Math.floor(maxWidthPx / avgCharWidth));\n const lines = Math.max(1, Math.ceil(text.length / charsPerLine));\n return lines * fontSizePx * lineHeight;\n}\n","/**\n * Title Block Template\n *\n * Large title with optional subtitle for doc intros.\n * Centered text with fade-in animations, composed as one lockup:\n * accent rule, title, and subtitle are spaced from the title's\n * estimated height instead of fixed slots that collide on wrap.\n * Adapts font sizes and positioning for different viewports.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { TitleBlockInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getTemplateHint,\n getThemeFont,\n shouldUseShadow,\n themedEntrance,\n themedFontSize,\n} from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { estimateTextHeight } from './captionUtils.js';\n\n/**\n * Hint schema published for the theme validator + future customizer hint UI.\n * Themes may set `templateHints.title` entries matching these keys.\n */\nexport const titleBlockHintSchema = {\n /** Whether to render a thin accent line above the title. Default: theme-dependent. */\n showAccentLine: { type: 'boolean' as const, default: true },\n} as const;\n\nexport function titleBlock(input: TitleBlockInput, context: TemplateContext): Layer[] {\n const { title, subtitle, backgroundColor } = input;\n const { theme, layout, viewport } = context;\n\n // Scale font sizes for viewport\n const titleFontSize = themedFontSize(96, context, true);\n const subtitleFontSize = themedFontSize(36, context, false);\n\n const baseBg = backgroundColor || theme.colors.primary;\n\n // Estimate the title's rendered height so the rule and subtitle key off\n // its real extent — fixed slots let the rule overlap the ascenders and\n // stranded the subtitle when the title wrapped.\n const maxWidthPx = (parseFloat(layout.maxTextWidth) / 100) * viewport.width;\n const titleH = estimateTextHeight(title, titleFontSize, maxWidthPx, 1.15);\n const titleYPct = subtitle ? 42 : 48;\n const px = (v: number) => (v / viewport.height) * 100;\n\n const layers: Layer[] = [\n // Background — theme surface with a soft radial accent tint. The tint\n // is translucent so the theme's text color keeps its contrast with the\n // surface; a full-strength `primary` hotspot behind the title made\n // dark-primary light themes unreadable.\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: theme.colors.background,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n {\n type: 'shape',\n id: 'bg-tint',\n content: {\n shape: 'rect',\n fill: `radial-gradient(ellipse at 50% 40%, ${withAlpha(baseBg, 0.32)} 0%, ${withAlpha(baseBg, 0)} 75%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n // Subtle decorative line, clear above the title's ascenders. Themes can\n // hide it via `templateHints.title.showAccentLine: false` (the line is\n // decorative; title/subtitle positions don't depend on it).\n if (getTemplateHint(context, 'title', 'showAccentLine', true)) {\n layers.push({\n type: 'shape',\n id: 'accent-line',\n content: {\n shape: 'rect',\n fill: withAlpha(theme.colors.text, 0.3),\n },\n position: {\n x: '44%',\n y: `${titleYPct - px(titleH / 2 + 56)}%`,\n width: '12%',\n height: '3px',\n },\n });\n }\n\n layers.push(\n // Title\n {\n type: 'text',\n id: 'title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: `${titleYPct}%`,\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 2 }),\n },\n );\n\n // Add subtitle if provided — hangs from the title's estimated bottom edge\n if (subtitle) {\n const subH = estimateTextHeight(subtitle, subtitleFontSize, maxWidthPx, 1.5);\n layers.push({\n type: 'text',\n id: 'subtitle',\n content: {\n text: subtitle,\n style: {\n fontSize: subtitleFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n },\n },\n position: {\n x: '50%',\n y: `${titleYPct + px(titleH / 2 + 56 + subH / 2)}%`,\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 1.5, delay: 1 },\n });\n }\n\n return layers;\n}\n","/**\n * Section Header Template\n *\n * Section title card with optional background image.\n * Used to introduce new sections of a story.\n * When an image is provided, displays like a title slide with the image as background.\n * Without an image, falls back to a colored background.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer, AnimationType } from '../../schemas/Doc.js';\nimport type { SectionHeaderInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n resolveColorScheme,\n getThemeFont,\n shouldUseShadow,\n themedEntrance,\n themedFontSize,\n themedScrim,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\n\nexport function sectionHeader(input: SectionHeaderInput, context: TemplateContext): Layer[] {\n const { title = '', colorScheme = 'blue', imageSrc, imageAlt, ambientMotion } = input;\n const { theme, layout } = context;\n\n const treatment = themedImageTreatment(context, input.imageTreatment);\n const colors = resolveColorScheme(context, colorScheme);\n\n // Scale font sizes for viewport. Section dividers are the loudest\n // interstitial in a doc, so the title sits a step above ordinary\n // content-block headings.\n const titleFontSize = themedFontSize(84, context, true);\n\n const layers: Layer[] = [];\n\n // Background - either image or solid color\n if (imageSrc) {\n // Image background with Ken Burns effect\n layers.push({\n type: 'image',\n id: 'bg-image',\n content: {\n src: imageSrc,\n alt: imageAlt || title,\n fit: 'cover',\n ...(treatment ? { treatment } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: ambientMotion\n ? { type: ambientMotion as AnimationType, duration: 8 }\n : { type: 'slowZoom', duration: 8, direction: 'in' },\n });\n\n // Theme-tinted overlay for text readability — light themes get a\n // light scrim with dark theme text, dark themes the familiar dark one.\n layers.push({\n type: 'shape',\n id: 'overlay',\n content: {\n shape: 'rect',\n fill: themedScrim(context),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n });\n } else {\n // Solid color background\n layers.push({\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: colors.bg,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n });\n\n // Decorative lines above and below title\n layers.push({\n type: 'shape',\n id: 'line-top',\n content: { shape: 'rect', fill: `${colors.text}33` },\n position: { x: '50%', y: '40%', width: '20%', height: '2px', anchor: 'center' },\n });\n layers.push({\n type: 'shape',\n id: 'line-bottom',\n content: { shape: 'rect', fill: `${colors.text}33` },\n position: { x: '50%', y: '60%', width: '20%', height: '2px', anchor: 'center' },\n });\n }\n\n // Section title — theme text over the theme-tinted scrim (readable by\n // construction), scheme text on the solid-color fallback\n layers.push({\n type: 'text',\n id: 'title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: imageSrc ? theme.colors.text : colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: '50%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1.5 }),\n });\n\n return layers;\n}\n","/**\n * Accent Image Utility\n *\n * Creates image layers and layout adjustments for accent images on text-based slides.\n * Accent images are tasteful additions that complement text without overwhelming it.\n *\n * Layout patterns:\n * - left-strip: 35% width vertical strip on left, text area shifted to right 65%\n * - right-strip: 35% width vertical strip on right, text area shifted to left 65%\n * - bottom-strip: 35% height horizontal strip at bottom, text area in upper 65%\n * - corner-inset: Small 25% corner image with gradient vignette\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type {\n Layer,\n ImageLayer,\n ImageTreatment,\n ShapeLayer,\n Animation,\n} from '../../schemas/Doc.js';\nimport type { AccentImage, AccentPosition } from '../../schemas/BlockTemplates.js';\n\nconst PERCENTAGE_RE = /^(\\d+(?:\\.\\d+)?)\\s*%?$/;\n\n/**\n * Layout adjustments when an accent image is present.\n * Templates use these values to reposition their text content.\n */\nexport interface AccentLayout {\n /** X offset for text center (e.g., '64%' for left-strip) */\n textCenterX: string;\n /** Width available for text content */\n textWidth: string;\n /** Y offset adjustment for vertical positioning */\n textYAdjust: number;\n /** Whether to adjust Y positions (for bottom-strip) */\n adjustY: boolean;\n}\n\n/**\n * Default layout when no accent image is present.\n */\nexport const DEFAULT_LAYOUT: AccentLayout = {\n textCenterX: '50%',\n textWidth: '85%',\n textYAdjust: 0,\n adjustY: false,\n};\n\n/**\n * Map ambientMotion values to proper Ken Burns animations.\n * Uses slowZoom variants which provide smooth pan/zoom without opacity changes.\n * Supports both standard values ('zoomIn', 'panLeft', etc.) and legacy\n * aliases ('in', 'out', 'left', 'right') for backward compatibility.\n *\n * @param motion - The ambient motion value (e.g., 'zoomIn', 'panLeft')\n * @param duration - Optional animation duration in seconds\n */\nexport function mapAmbientMotion(\n motion: string | undefined,\n duration?: number,\n): Animation | undefined {\n if (!motion) return undefined;\n const base = duration !== undefined ? { duration } : {};\n switch (motion) {\n case 'zoomIn':\n case 'in':\n return { type: 'slowZoom', direction: 'in', ...base };\n case 'zoomOut':\n case 'out':\n return { type: 'slowZoom', direction: 'out', ...base };\n case 'panLeft':\n case 'left':\n return { type: 'slowZoom', panDirection: 'left', ...base };\n case 'panRight':\n case 'right':\n return { type: 'slowZoom', panDirection: 'right', ...base };\n default:\n return undefined;\n }\n}\n\n/**\n * Accent strip width/height as percentage.\n * 35% gives the image enough presence to complement the text without overwhelming it.\n */\nconst STRIP_SIZE = 35;\nconst STRIP_SIZE_PCT = `${STRIP_SIZE}%`;\n\n/**\n * Get layout adjustments based on accent position.\n */\nexport function getAccentLayout(position: AccentPosition): AccentLayout {\n switch (position) {\n case 'left-strip':\n return {\n textCenterX: `${50 + STRIP_SIZE / 2}%`, // Shift center right\n textWidth: `${100 - STRIP_SIZE - 8}%`, // Leave margin\n textYAdjust: 0,\n adjustY: false,\n };\n case 'right-strip':\n return {\n textCenterX: `${50 - STRIP_SIZE / 2}%`, // Shift center left\n textWidth: `${100 - STRIP_SIZE - 8}%`,\n textYAdjust: 0,\n adjustY: false,\n };\n case 'bottom-strip':\n return {\n textCenterX: '50%',\n textWidth: '85%',\n textYAdjust: -10, // Shift content up by 10%\n adjustY: true,\n };\n case 'corner-inset':\n return {\n textCenterX: '45%', // Slight shift to avoid corner\n textWidth: '70%',\n textYAdjust: 0,\n adjustY: false,\n };\n default:\n return DEFAULT_LAYOUT;\n }\n}\n\n/**\n * Create layers for an accent image.\n * Returns the image layer and any overlay/gradient layers needed.\n */\nexport function createAccentLayers(\n accent: AccentImage,\n slideId: string,\n treatment?: ImageTreatment,\n): Layer[] {\n const layers: Layer[] = [];\n const { src, alt, position, ambientMotion, credit, license } = accent;\n\n switch (position) {\n case 'left-strip':\n layers.push(\n createStripImage(src, alt, 'left', slideId, ambientMotion, credit, license, treatment),\n );\n layers.push(createStripGradient('left', slideId));\n break;\n\n case 'right-strip':\n layers.push(\n createStripImage(src, alt, 'right', slideId, ambientMotion, credit, license, treatment),\n );\n layers.push(createStripGradient('right', slideId));\n break;\n\n case 'bottom-strip':\n layers.push(\n createBottomStripImage(src, alt, slideId, ambientMotion, credit, license, treatment),\n );\n layers.push(createBottomStripGradient(slideId));\n break;\n\n case 'corner-inset':\n layers.push(\n createCornerInsetImage(src, alt, slideId, ambientMotion, credit, license, treatment),\n );\n layers.push(createCornerVignette(slideId));\n break;\n }\n\n return layers;\n}\n\n/**\n * Create a vertical strip image layer.\n */\nfunction createStripImage(\n src: string,\n alt: string,\n side: 'left' | 'right',\n slideId: string,\n ambientMotion?: AccentImage['ambientMotion'],\n credit?: string,\n license?: string,\n treatment?: ImageTreatment,\n): ImageLayer {\n return {\n type: 'image',\n id: `${slideId}-accent-img`,\n content: {\n src,\n alt,\n fit: 'cover',\n credit,\n license,\n ...(treatment ? { treatment } : {}),\n },\n position: {\n x: side === 'left' ? 0 : `${100 - STRIP_SIZE}%`,\n y: 0,\n width: STRIP_SIZE_PCT,\n height: '100%',\n },\n animation: mapAmbientMotion(ambientMotion, 15),\n };\n}\n\n/**\n * Create a gradient overlay for strip images (fades to background).\n */\nfunction createStripGradient(side: 'left' | 'right', slideId: string): ShapeLayer {\n // Gradient from transparent to background color at the edge\n const gradientId = side === 'left' ? 'accent-gradient-left' : 'accent-gradient-right';\n\n return {\n type: 'shape',\n id: `${slideId}-accent-fade`,\n content: {\n shape: 'rect',\n // SVG gradient will be handled by the renderer\n // Using a semi-transparent overlay as fallback\n fill: `url(#${gradientId})`,\n },\n position: {\n x: side === 'left' ? `${STRIP_SIZE - 8}%` : `${100 - STRIP_SIZE}%`,\n y: 0,\n width: '10%',\n height: '100%',\n },\n };\n}\n\n/**\n * Create a horizontal bottom strip image layer.\n */\nfunction createBottomStripImage(\n src: string,\n alt: string,\n slideId: string,\n ambientMotion?: AccentImage['ambientMotion'],\n credit?: string,\n license?: string,\n treatment?: ImageTreatment,\n): ImageLayer {\n return {\n type: 'image',\n id: `${slideId}-accent-img`,\n content: {\n src,\n alt,\n fit: 'cover',\n credit,\n license,\n ...(treatment ? { treatment } : {}),\n },\n position: {\n x: 0,\n y: `${100 - STRIP_SIZE}%`,\n width: '100%',\n height: STRIP_SIZE_PCT,\n },\n animation: mapAmbientMotion(ambientMotion, 15),\n };\n}\n\n/**\n * Create a gradient overlay for bottom strip (fades up to background).\n */\nfunction createBottomStripGradient(slideId: string): ShapeLayer {\n return {\n type: 'shape',\n id: `${slideId}-accent-fade`,\n content: {\n shape: 'rect',\n fill: 'url(#accent-gradient-bottom)',\n },\n position: {\n x: 0,\n y: `${100 - STRIP_SIZE - 8}%`,\n width: '100%',\n height: '12%',\n },\n };\n}\n\n/**\n * Create a corner inset image layer.\n */\nfunction createCornerInsetImage(\n src: string,\n alt: string,\n slideId: string,\n ambientMotion?: AccentImage['ambientMotion'],\n credit?: string,\n license?: string,\n treatment?: ImageTreatment,\n): ImageLayer {\n return {\n type: 'image',\n id: `${slideId}-accent-img`,\n content: {\n src,\n alt,\n fit: 'cover',\n credit,\n license,\n ...(treatment ? { treatment } : {}),\n },\n position: {\n x: '70%',\n y: '60%',\n width: '28%',\n height: '38%',\n anchor: 'top-left',\n },\n animation: mapAmbientMotion(ambientMotion, 15) ?? { type: 'fadeIn', duration: 2, delay: 0.5 },\n };\n}\n\n/**\n * Create a vignette overlay around corner inset.\n */\nfunction createCornerVignette(slideId: string): ShapeLayer {\n return {\n type: 'shape',\n id: `${slideId}-accent-vignette`,\n content: {\n shape: 'rect',\n fill: 'url(#accent-vignette-corner)',\n borderRadius: 8,\n },\n position: {\n x: '69%',\n y: '59%',\n width: '30%',\n height: '40%',\n },\n };\n}\n\n/**\n * Adjust a Y position value based on accent layout.\n * Used by templates to shift content when bottom-strip is present.\n */\nexport function adjustY(originalY: string | number, layout: AccentLayout): string {\n if (!layout.adjustY || layout.textYAdjust === 0) {\n return typeof originalY === 'number' ? `${originalY}%` : originalY;\n }\n\n // Parse percentage value\n const yStr = typeof originalY === 'number' ? `${originalY}` : originalY;\n const match = yStr.match(PERCENTAGE_RE);\n\n if (match) {\n const yValue = parseFloat(match[1]);\n const adjusted = Math.max(5, yValue + layout.textYAdjust);\n return `${adjusted}%`;\n }\n\n return yStr;\n}\n","/**\n * Stat Highlight Template\n *\n * Large statistic/number with description and optional detail.\n * Great for emphasizing key data points.\n * Adapts font sizes and positioning for different viewports.\n *\n * Supports optional accent images that appear as tasteful side/bottom strips.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { StatHighlightInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n resolveColorScheme,\n getTemplateHint,\n getThemeFont,\n shouldUseShadow,\n themedFontSize,\n themedSurfaceGradient,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer } from './captionUtils.js';\n\n/**\n * Hint schema published for the theme validator + future customizer hint UI.\n * Themes may set `templateHints.statHighlight` entries matching these keys.\n */\nexport const statHighlightHintSchema = {\n /** Entrance style for the stat number. 'subtle' or 'dramatic'. */\n entrance: {\n type: 'string' as const,\n options: ['subtle', 'dramatic'] as const,\n default: 'subtle',\n },\n} as const;\n\nexport function statHighlight(input: StatHighlightInput, context: TemplateContext): Layer[] {\n const { stat, description, detail, colorScheme = 'blue', accentImage } = input;\n const { theme } = context;\n const colors = resolveColorScheme(context, colorScheme);\n\n // Get layout adjustments if accent image is present\n const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;\n\n // Scale font sizes — stat is dramatically large, description is understated\n const statFontSize = themedFontSize(148, context, true);\n const descFontSize = themedFontSize(32, context, false);\n const detailFontSize = themedFontSize(26, context, false);\n\n const layers: Layer[] = [createBackgroundLayer('bg', themedSurfaceGradient(context, 180))];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(\n ...createAccentLayers(\n accentImage,\n input.id,\n themedImageTreatment(context, input.imageTreatment),\n ),\n );\n }\n\n // Big stat — hero element, dominates the slide\n layers.push({\n type: 'text',\n id: 'stat',\n content: {\n text: stat,\n style: {\n fontSize: statFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: colors.text,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('36%', accentLayout),\n anchor: 'center',\n },\n animation:\n getTemplateHint<string>(context, 'statHighlight', 'entrance', 'subtle') === 'dramatic'\n ? { type: 'zoomIn', duration: 0.4 }\n : { type: 'zoomIn', duration: 0.6 },\n });\n\n // Description — smaller and understated beneath the stat. Sits close\n // under the stat (the ~26% fixed gap it used to get read as two\n // unrelated elements floating in the panel).\n layers.push({\n type: 'text',\n id: 'description',\n content: {\n text: description,\n style: {\n fontSize: descFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('54%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 0.3 },\n });\n\n // Add detail if provided\n if (detail) {\n layers.push({\n type: 'text',\n id: 'detail',\n content: {\n text: detail,\n style: {\n fontSize: detailFontSize,\n fontFamily: getThemeFont(context, 'body'),\n // Scheme *text* rather than *accent*: accents in the built-in\n // schemes are tuned as chart/fill colors and drop below legible\n // contrast as body copy on light surfaces.\n color: colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('66%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 1 },\n });\n }\n\n return layers;\n}\n","/**\n * Quote Block Template\n *\n * Large centered quote with optional attribution, composed as one\n * vertically-centered lockup: the attribution hangs a fixed distance\n * below the quote's estimated bottom edge instead of being pinned to a\n * far-away layout slot.\n * Adapts font sizes and positioning for different viewports.\n *\n * Supports optional accent images that appear as tasteful side/bottom strips.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { QuoteBlockInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n shouldUseShadow,\n themedEntrance,\n themedFontSize,\n themedSurfaceGradient,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer, estimateTextHeight } from './captionUtils.js';\n\nexport function quoteBlock(input: QuoteBlockInput, context: TemplateContext): Layer[] {\n const { quote, attribution, accentImage } = input;\n const { theme, viewport } = context;\n\n // Get layout adjustments if accent image is present\n const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;\n\n // Scale font sizes for viewport\n const quoteFontSize = themedFontSize(48, context, true);\n const attrFontSize = themedFontSize(26, context, false);\n const quoteLineHeight = Math.max(theme.typography.titleLineHeight ?? 1.4, 1.25);\n\n // Decorative quotation mark font size\n const decorativeQuoteFontSize = themedFontSize(280, context, true);\n\n const layers: Layer[] = [createBackgroundLayer('bg', themedSurfaceGradient(context, 160))];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(\n ...createAccentLayers(\n accentImage,\n input.id,\n themedImageTreatment(context, input.imageTreatment),\n ),\n );\n }\n\n // Decorative opening quotation mark — oversized, low-opacity behind the\n // quote. Derived from the theme text color so it stays a subtle ornament\n // on light and dark surfaces alike (a hard-coded white glyph was\n // invisible on both).\n layers.push({\n type: 'text',\n id: 'deco-quote',\n content: {\n text: '“',\n style: {\n fontSize: decorativeQuoteFontSize,\n fontFamily: getThemeFont(context, 'title'),\n color: withAlpha(theme.colors.text, 0.09),\n textAlign: 'center',\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('26%', accentLayout),\n anchor: 'center',\n },\n });\n\n // Quote text — optically centered, nudged up slightly when an\n // attribution hangs below it.\n const quoteYPct = attribution ? 45 : 50;\n layers.push({\n type: 'text',\n id: 'quote',\n content: {\n text: quote,\n style: {\n fontSize: quoteFontSize,\n fontFamily: getThemeFont(context, 'title'),\n color: theme.colors.text,\n textAlign: 'center',\n lineHeight: quoteLineHeight,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(`${quoteYPct}%`, accentLayout),\n anchor: 'center',\n width: accentLayout.textWidth,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 2 }),\n });\n\n // Attribution hangs just below the quote's estimated bottom edge, so\n // the two read as one lockup instead of the attribution stranding near\n // the bottom of the block.\n if (attribution) {\n const quoteWidthPx = (parseFloat(accentLayout.textWidth) / 100) * viewport.width;\n const quoteHeightPx = estimateTextHeight(quote, quoteFontSize, quoteWidthPx, quoteLineHeight);\n const gapPx = quoteFontSize * 1.7;\n const attrYPct = Math.min(\n 82,\n quoteYPct + ((quoteHeightPx / 2 + gapPx) / viewport.height) * 100,\n );\n\n layers.push({\n type: 'text',\n id: 'attribution',\n content: {\n text: `— ${attribution}`,\n style: {\n fontSize: attrFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(`${attrYPct}%`, accentLayout),\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 1.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Fact Card Template\n *\n * Key fact with explanation and optional source, composed as one\n * vertically-centered lockup: each element is placed relative to the\n * estimated height of the one above it, so short content doesn't leave\n * fixed-slot voids and long content doesn't collide.\n * Adapts font sizes and positioning for different viewports.\n *\n * Supports optional accent images that appear as tasteful side/bottom strips.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { FactCardInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n shouldUseShadow,\n themedEntrance,\n themedFontSize,\n themedSurfaceGradient,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer, estimateTextHeight } from './captionUtils.js';\n\nexport function factCard(input: FactCardInput, context: TemplateContext): Layer[] {\n const { fact, explanation, source, accentImage } = input;\n const { theme, viewport } = context;\n\n // Get layout adjustments if accent image is present\n const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;\n\n // Scale font sizes for viewport. The explanation sits at ~60% of the fact\n // size — large enough to read comfortably as the secondary line while still\n // clearly subordinate to the headline fact.\n const factFontSize = themedFontSize(56, context, true);\n const explainFontSize = themedFontSize(34, context, false);\n const sourceFontSize = themedFontSize(20, context, false);\n\n const layers: Layer[] = [createBackgroundLayer('bg', themedSurfaceGradient(context, 170))];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(\n ...createAccentLayers(\n accentImage,\n input.id,\n themedImageTreatment(context, input.imageTreatment),\n ),\n );\n }\n\n // Estimate each element's height so the stack reads as one lockup.\n const textWidthPx = (parseFloat(accentLayout.textWidth) / 100) * viewport.width;\n const factH = estimateTextHeight(fact, factFontSize, textWidthPx, 1.3);\n const explainH = estimateTextHeight(explanation, explainFontSize, textWidthPx, 1.5);\n const sourceH = source ? sourceFontSize * 1.4 : 0;\n const gap = factFontSize * 0.9;\n const sourceGap = source ? explainFontSize * 1.1 : 0;\n const totalH = factH + gap + explainH + sourceGap + sourceH;\n\n // Center the lockup slightly above the geometric middle (optical center).\n const groupTopPct = 47 - (totalH / 2 / viewport.height) * 100;\n const pct = (px: number) => groupTopPct + (px / viewport.height) * 100;\n\n const factY = pct(factH / 2);\n const explainY = pct(factH + gap + explainH / 2);\n const sourceY = pct(factH + gap + explainH + sourceGap + sourceH / 2);\n\n // Fact (main statement)\n layers.push({\n type: 'text',\n id: 'fact',\n content: {\n text: fact,\n style: {\n fontSize: factFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n lineHeight: 1.3,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(`${factY}%`, accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1.5 }),\n });\n\n // Explanation\n layers.push({\n type: 'text',\n id: 'explanation',\n content: {\n text: explanation,\n style: {\n fontSize: explainFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(`${explainY}%`, accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1, delay: 0.8 }),\n });\n\n // Add source if provided\n if (source) {\n layers.push({\n type: 'text',\n id: 'source',\n content: {\n text: source,\n style: {\n fontSize: sourceFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(`${sourceY}%`, accentLayout),\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 1.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Two Column Template\n *\n * Side-by-side comparison of two items.\n * Each column has a label and optional sublabel.\n * Adapts font sizes and positioning for different viewports.\n *\n * Portrait mode: Stacks columns vertically (top/bottom) instead of side-by-side.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { TwoColumnInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n resolveColorScheme,\n getThemeFont,\n themedEntrance,\n themedFontSize,\n themedSurfaceGradient,\n} from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { getTwoColumnPositions } from '../../schemas/LayoutStrategy.js';\n\nexport function twoColumn(input: TwoColumnInput, context: TemplateContext): Layer[] {\n const { left, right, header, leftColor = 'green', rightColor = 'blue' } = input;\n\n // Guard: required fields may be absent when generated from sparse markdown\n if (!left?.label || !right?.label) return [];\n\n const { theme, layout, orientation } = context;\n const leftColors = resolveColorScheme(context, leftColor);\n const rightColors = resolveColorScheme(context, rightColor);\n\n // Get column positions based on orientation\n const positions = getTwoColumnPositions(orientation);\n const isStacked = layout.stackColumns;\n\n // Column width ~42% for side-by-side, ~85% for stacked\n const columnWidth = isStacked ? '85%' : '42%';\n\n // Calculate font sizes based on viewport. The header is the block's\n // title, so it sits a step ABOVE the column labels in the hierarchy —\n // it used to render smaller and dimmer than the labels, which read as\n // an inverted hierarchy.\n const headerFontSize = themedFontSize(36, context, true);\n const labelFontSize = themedFontSize(44, context, true);\n const sublabelFontSize = themedFontSize(22, context, false);\n\n // Panels drop below the header band so the title never overlaps them.\n const panelTop = header && !isStacked ? 22 : 15;\n const panelHeight = header && !isStacked ? 64 : 70;\n\n const layers: Layer[] = [\n // Background — subtle gradient to add depth\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: themedSurfaceGradient(context, 135),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n if (!isStacked) {\n // Column background panels — tinted from the theme text color so\n // they stay visible on light and dark surfaces alike.\n const panelFill = withAlpha(theme.colors.text, 0.05);\n layers.push({\n type: 'shape',\n id: 'left-panel',\n content: {\n shape: 'rect',\n fill: panelFill,\n borderRadius: 12,\n },\n position: { x: '3%', y: `${panelTop}%`, width: '44%', height: `${panelHeight}%` },\n });\n\n layers.push({\n type: 'shape',\n id: 'right-panel',\n content: {\n shape: 'rect',\n fill: panelFill,\n borderRadius: 12,\n },\n position: { x: '53%', y: `${panelTop}%`, width: '44%', height: `${panelHeight}%` },\n });\n }\n\n // Header position adjusts based on layout — always clear of the panels.\n const headerY = isStacked ? '8%' : '12%';\n\n // Add header if provided\n if (header) {\n layers.push({\n type: 'text',\n id: 'header',\n content: {\n text: header,\n style: {\n fontSize: headerFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: headerY, anchor: 'center' },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 0.8 }),\n });\n }\n\n // Calculate Y positions based on layout mode and header presence\n let leftY: string;\n let leftSublabelY: string;\n let rightY: string;\n let rightSublabelY: string;\n\n if (isStacked) {\n // Portrait: stacked vertically\n leftY = header ? '30%' : '25%';\n leftSublabelY = header ? '40%' : '35%';\n rightY = header ? '60%' : '55%';\n rightSublabelY = header ? '70%' : '65%';\n } else {\n // Landscape/square: side by side — vertically centered in the panels.\n const panelCenter = panelTop + panelHeight / 2;\n leftY = `${panelCenter - 4}%`;\n leftSublabelY = `${panelCenter + 7}%`;\n rightY = `${panelCenter - 4}%`;\n rightSublabelY = `${panelCenter + 7}%`;\n }\n\n // Left column (or top in portrait)\n layers.push({\n type: 'text',\n id: 'left-label',\n content: {\n text: left.label,\n style: {\n fontSize: labelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n fontWeight: 'bold',\n color: leftColors.text,\n textAlign: 'center',\n lineHeight: 1.3,\n },\n },\n position: {\n x: positions.left.x,\n y: leftY,\n anchor: 'center',\n width: columnWidth,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1 }),\n });\n\n if (left.sublabel) {\n layers.push({\n type: 'text',\n id: 'left-sublabel',\n content: {\n text: left.sublabel,\n style: {\n fontSize: sublabelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.4,\n },\n },\n position: {\n x: positions.left.x,\n y: leftSublabelY,\n anchor: 'center',\n width: columnWidth,\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.3 },\n });\n }\n\n // Visual connector between columns — sized to fit inside the gutter\n // between the panels (a 120px glyph used to cross both panel edges),\n // tinted from the theme text color so it shows on any surface.\n const connectorFontSize = themedFontSize(64, context, true);\n const connectorY = isStacked ? '48%' : `${panelTop + panelHeight / 2}%`;\n const connectorSymbol = isStacked ? '\\u2193' : '\\u2192'; // ↓ for stacked, → for side-by-side\n\n layers.push({\n type: 'text',\n id: 'connector',\n content: {\n text: connectorSymbol,\n style: {\n fontSize: connectorFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: withAlpha(theme.colors.text, 0.45),\n textAlign: 'center',\n },\n },\n position: {\n x: '50%',\n y: connectorY,\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 0.6, delay: 0.4 },\n });\n\n // Right column (or bottom in portrait)\n layers.push({\n type: 'text',\n id: 'right-label',\n content: {\n text: right.label,\n style: {\n fontSize: labelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n fontWeight: 'bold',\n color: rightColors.text,\n textAlign: 'center',\n lineHeight: 1.3,\n },\n },\n position: {\n x: positions.right.x,\n y: rightY,\n anchor: 'center',\n width: columnWidth,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1, delay: 0.5 }),\n });\n\n if (right.sublabel) {\n layers.push({\n type: 'text',\n id: 'right-sublabel',\n content: {\n text: right.sublabel,\n style: {\n fontSize: sublabelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.4,\n },\n },\n position: {\n x: positions.right.x,\n y: rightSublabelY,\n anchor: 'center',\n width: columnWidth,\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.8 },\n });\n }\n\n return layers;\n}\n","/**\n * Date Event Template\n *\n * Timeline-style slide with prominent date and description.\n * Supports different moods: neutral, somber, celebratory.\n * Adapts font sizes and positioning for different viewports.\n *\n * Supports optional accent images that appear as tasteful side/bottom strips.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { DateEventInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n shouldUseShadow,\n themedFontSize,\n themedSurfaceGradient,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { oklchDarken, relativeLuminance } from '../../schemas/colorUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\n\n/**\n * Mood accents tint the hero date, not the surface — the surface always\n * comes from the theme. (Hard-coded dark mood panels used to turn light\n * and warm themes into a cold navy slide.)\n */\nconst MOOD_ACCENTS = {\n neutral: '#63b3ed',\n somber: '#e53e3e',\n celebratory: '#68d391',\n};\n\nexport function dateEvent(input: DateEventInput, context: TemplateContext): Layer[] {\n const { date, description, footer, mood = 'neutral', accentImage } = input;\n const { theme } = context;\n // Deepen the mood accent on light surfaces so the hero date keeps contrast.\n const moodAccent = MOOD_ACCENTS[mood];\n const dateColor =\n relativeLuminance(theme.colors.background) > 0.5 ? oklchDarken(moodAccent, 0.25) : moodAccent;\n\n // Get layout adjustments if accent image is present\n const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;\n\n // Scale font sizes — date is the hero element\n const dateFontSize = themedFontSize(96, context, true);\n const descFontSize = themedFontSize(30, context, false);\n const footerFontSize = themedFontSize(26, context, false);\n\n const layers: Layer[] = [\n // Background — theme surface gradient (diagonal to differentiate\n // from other text templates)\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: themedSurfaceGradient(context, 135),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(\n ...createAccentLayers(\n accentImage,\n input.id,\n themedImageTreatment(context, input.imageTreatment),\n ),\n );\n }\n\n // Date — hero element, much larger\n layers.push({\n type: 'text',\n id: 'date',\n content: {\n text: date,\n style: {\n fontSize: dateFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: dateColor,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('35%', accentLayout),\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1.5 },\n });\n\n // Description - constrained width for proper text wrapping\n layers.push({\n type: 'text',\n id: 'description',\n content: {\n text: description,\n style: {\n fontSize: descFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'center',\n lineHeight: 1.6,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('58%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 2, delay: 1 },\n });\n\n // Add footer if provided\n if (footer) {\n layers.push({\n type: 'text',\n id: 'footer',\n content: {\n text: footer,\n style: {\n fontSize: footerFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('72%', accentLayout),\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 3 },\n });\n }\n\n return layers;\n}\n","/**\n * Image With Caption Template\n *\n * Full-screen background image with text overlay.\n * Supports Ken Burns animation effects (zoom, pan).\n * Adapts caption positioning and font sizes for different viewports.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { ImageWithCaptionInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n shouldUseShadow,\n themedFontSize,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { cleanCaption } from './captionUtils.js';\nimport { mapAmbientMotion } from './accentImage.js';\n\nexport function imageWithCaption(input: ImageWithCaptionInput, context: TemplateContext): Layer[] {\n const {\n imageSrc,\n imageAlt,\n caption: rawCaption,\n captionPosition: _captionPosition = 'bottom',\n ambientMotion,\n isTitle,\n subtitle,\n imageCredit,\n imageLicense,\n } = input;\n const caption = rawCaption ? cleanCaption(rawCaption) : rawCaption;\n const { theme, layout } = context;\n\n const treatment = themedImageTreatment(context, input.imageTreatment);\n\n // Scale font sizes for viewport\n const captionFontSize = themedFontSize(30, context, false);\n const titleFontSize = themedFontSize(96, context, true);\n const subtitleFontSize = themedFontSize(36, context, false);\n\n // Determine animation based on ambientMotion setting\n // Support both new 'ambientMotion' and legacy 'kenBurns' property\n const legacyKenBurns = (input as unknown as Record<string, unknown>).kenBurns as\n | string\n | undefined;\n const motion: string | undefined = ambientMotion || legacyKenBurns;\n const imageAnimation = mapAmbientMotion(motion);\n\n const layers: Layer[] = [\n // Background image\n {\n type: 'image',\n id: 'bg-image',\n content: {\n src: imageSrc,\n alt: imageAlt,\n fit: 'cover',\n credit: imageCredit,\n license: imageLicense,\n ...(treatment ? { treatment } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: imageAnimation,\n },\n ];\n\n // Scrim gradients are tinted from the theme background so the theme\n // text color painted on top stays readable by construction — a fixed\n // black scrim put dark theme text on a dark band in light themes.\n const bg = theme.colors.background;\n\n // TITLE MODE: Large centered title over hero image (like cover slide)\n if (isTitle && caption) {\n // Full gradient overlay for title readability (similar to cover slide)\n layers.push({\n type: 'shape',\n id: 'title-gradient',\n content: {\n shape: 'rect',\n fill: `linear-gradient(0deg, ${withAlpha(bg, 0.85)} 0%, ${withAlpha(bg, 0.45)} 40%, ${withAlpha(bg, 0.1)} 70%, ${withAlpha(bg, 0)} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n });\n\n // Title text - large and centered in lower portion\n layers.push({\n type: 'text',\n id: 'title',\n content: {\n text: caption,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: subtitle ? '70%' : '75%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 0.8 },\n });\n\n // Optional subtitle\n if (subtitle) {\n layers.push({\n type: 'text',\n id: 'subtitle',\n content: {\n text: subtitle,\n style: {\n fontSize: subtitleFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n },\n },\n position: {\n x: '50%',\n y: '82%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.2 },\n });\n }\n\n return layers;\n }\n\n // STANDARD MODE: Caption at bottom\n if (caption) {\n // Compact caption band raised above media controls. Long archival captions\n // are clamped so they don't sprawl across the image.\n layers.push({\n type: 'shape',\n id: 'caption-gradient',\n content: {\n shape: 'rect',\n fill: `linear-gradient(90deg, ${withAlpha(bg, 0)}, ${withAlpha(bg, 0.78)} 16%, ${withAlpha(bg, 0.78)} 84%, ${withAlpha(bg, 0)})`,\n },\n position: {\n x: 0,\n y: '68%',\n width: '100%',\n height: '16%',\n },\n });\n\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n lineHeight: 1.18,\n maxLines: 2,\n },\n },\n position: {\n x: '50%',\n y: '74%',\n anchor: 'center',\n width: '78%',\n },\n animation: { type: 'fadeIn', duration: 1.5, delay: 0.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Feature Block Templates — `leftFeature` and `rightFeature`\n *\n * Pairs a \"feature\" image on one side of the block with a title + body\n * paragraph on the other. The two templates are mirror images, so the\n * actual layer-builder lives here once and the per-side template files\n * just call it with a `side` parameter.\n *\n * Used for editorial layouts like product highlights, profile cards,\n * and section intros where a single image deserves to sit next to a\n * short text block rather than behind it (which is what\n * `imageWithCaption` does).\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type {\n LeftFeatureInput,\n RightFeatureInput,\n TemplateContext,\n} from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n themedEntrance,\n themedFontSize,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\n\ntype FeatureInput = LeftFeatureInput | RightFeatureInput;\n\n/**\n * Build the layer list for either feature template. Layers are in\n * back-to-front order: background, image, then text.\n */\nfunction buildFeatureLayers(\n input: FeatureInput,\n context: TemplateContext,\n side: 'left' | 'right',\n): Layer[] {\n const { imageSrc, imageAlt, imageWidth, imageHeight, title, body } = input;\n const { theme, layout } = context;\n\n const treatment = themedImageTreatment(context, input.imageTreatment);\n // Treat the image as \"sized\" when the host gave us an explicit width\n // or height — that's our cue that the user resized the image in the\n // editor and we should respect that as a sizing hint instead of\n // stretching the image to fill the whole half.\n const sized = (imageWidth ?? 0) > 0 || (imageHeight ?? 0) > 0;\n // In portrait the image stacks above the text rather than splitting\n // horizontally — same flag the twoColumn template uses, so the two\n // behave consistently on narrow viewports.\n const stack = layout.stackColumns;\n\n const titleFontSize = themedFontSize(48, context, true);\n const bodyFontSize = themedFontSize(24, context, false);\n\n // Image takes the full left or right half. The text column gets the\n // opposite half, with a comfortable inset so the text doesn't kiss\n // the image edge or the card border. All values are percentages of\n // the block viewport so they scale with the card size.\n //\n // When the image is \"sized\" we shrink it inside its half: the half\n // still claims the space (so the text column stays the same width\n // and the layout doesn't shift around when dimensions are toggled)\n // but the image itself sits centered with padding around it. The\n // sized box uses a square envelope sized to the smaller dimension of\n // its half so the image breathes regardless of viewport aspect.\n let imgX: string;\n let imgY: string;\n let imgW: string;\n let imgH: string;\n let imageFit: 'cover' | 'contain';\n if (stack) {\n imgX = '0';\n imgY = '0';\n imgW = '100%';\n imgH = '50%';\n imageFit = sized ? 'contain' : 'cover';\n } else if (sized) {\n // 90% of the half (with explicit aspect via fit='contain') so the\n // image keeps its natural proportions and never touches the card\n // edges. We don't try to honor the literal pixel value — block\n // viewports are designed in their own coordinate space — but the\n // *intent* (smaller image, padded, centered) is what comes through.\n const halfStart = side === 'left' ? 5 : 55; // %\n imgX = `${halfStart}%`;\n imgY = '5%';\n imgW = '40%';\n imgH = '90%';\n imageFit = 'contain';\n } else {\n imgX = side === 'left' ? '0' : '50%';\n imgY = '0';\n imgW = '50%';\n imgH = '100%';\n imageFit = 'cover';\n }\n\n // Text-column geometry. `textX` is the LEFT edge of the column when\n // the side is \"left\" (image left, text right); when the side is\n // \"right\" the column occupies the left half, inset from the card edge.\n // The mirror lives in the layout only — fully right-aligned\n // (ragged-left) running text on rightFeature was genuinely hard to read.\n const COLUMN_INSET = 4; // % of block width — padding from card edge / divider\n const textColumnWidth = stack ? 90 : 42;\n const textX = stack\n ? `${(100 - textColumnWidth) / 2}%`\n : side === 'left'\n ? `${50 + COLUMN_INSET}%` // text column starts just past the divider\n : `${COLUMN_INSET + 2}%`; // left edge of the left-half text column\n\n // Stack the title above the body, with the pair vertically centered\n // in the text column. The title sits 8% above center; body sits 2%\n // below — these offsets keep both lines visible at the small card\n // sizes used by the inline preview gutter without overflowing.\n const titleY = stack ? '60%' : body ? '42%' : '50%';\n const bodyY = stack ? '78%' : title ? '55%' : '50%';\n const textAnchor = 'top-left';\n const textAlign = 'left' as const;\n\n // Paint our own background so the text column has a theme-paired\n // surface to sit on. The host wrapper's surface isn't always theme-\n // aware (e.g. the InlinePreviewGutter card uses a fixed light SVG\n // background), and `theme.colors.text` is only guaranteed legible\n // against `theme.colors.background` — themes like Gezellig pair\n // cream text with a dark warm-brown background, which would\n // disappear on a light wrapper. Matching the convention every other\n // text-bearing template uses (see twoColumn / listBlock).\n const layers: Layer[] = [\n {\n type: 'shape',\n id: 'feature-bg',\n content: { shape: 'rect', fill: theme.colors.background },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n if (imageSrc) {\n layers.push({\n type: 'image',\n id: 'feature-image',\n content: {\n src: imageSrc,\n alt: imageAlt ?? title ?? '',\n fit: imageFit,\n ...(treatment ? { treatment } : {}),\n },\n position: { x: imgX, y: imgY, width: imgW, height: imgH },\n });\n }\n\n if (title) {\n layers.push({\n type: 'text',\n id: 'feature-title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign,\n lineHeight: 1.2,\n },\n },\n position: {\n x: textX,\n y: titleY,\n anchor: textAnchor,\n width: `${textColumnWidth}%`,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 0.8 }),\n });\n }\n\n if (body) {\n layers.push({\n type: 'text',\n id: 'feature-body',\n content: {\n text: body,\n style: {\n fontSize: bodyFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign,\n lineHeight: 1.5,\n },\n },\n position: {\n x: textX,\n y: bodyY,\n anchor: textAnchor,\n width: `${textColumnWidth}%`,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 0.8, delay: 0.2 }),\n });\n }\n\n return layers;\n}\n\nexport function leftFeature(input: LeftFeatureInput, context: TemplateContext): Layer[] {\n return buildFeatureLayers(input, context, 'left');\n}\n\nexport function rightFeature(input: RightFeatureInput, context: TemplateContext): Layer[] {\n return buildFeatureLayers(input, context, 'right');\n}\n","/**\n * Map Block Template\n *\n * Full-screen geographic map with optional title and caption overlays.\n * Great for establishing geographic context at the start of a doc\n * or when discussing location-specific content.\n * Adapts font sizes and positioning for different viewports.\n *\n * Map tiles are fetched from free/open-source providers.\n * See docs/MAP_TILES.md for available styles and attribution requirements.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { MapBlockInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { getThemeFont, shouldUseShadow, themedFontSize } from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { mapAmbientMotion } from './accentImage.js';\n\nexport function mapBlock(input: MapBlockInput, context: TemplateContext): Layer[] {\n const {\n center,\n zoom,\n mapStyle = 'terrain',\n title,\n caption,\n markers,\n ambientMotion,\n staticSrc,\n } = input;\n const { theme, layout } = context;\n\n // Scale font sizes for viewport\n const titleFontSize = themedFontSize(64, context, true);\n const captionFontSize = themedFontSize(32, context, false);\n\n // Determine animation based on ambientMotion setting\n const mapAnimation = mapAmbientMotion(ambientMotion);\n\n const layers: Layer[] = [\n // Map background\n {\n type: 'map',\n id: 'map-bg',\n content: {\n center,\n zoom,\n style: mapStyle,\n markers,\n showAttribution: true,\n staticSrc, // Use pre-rendered image if available\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: mapAnimation,\n },\n ];\n\n // Overlay bands are tinted from the theme background so the theme text\n // color on them stays readable in light and dark themes alike; they\n // fade out toward the map so the band doesn't read as a hard seam.\n const bg = theme.colors.background;\n\n // Add title overlay if provided\n if (title) {\n layers.push({\n type: 'shape',\n id: 'title-overlay',\n content: {\n shape: 'rect',\n fill: `linear-gradient(180deg, ${withAlpha(bg, 0.85)} 0%, ${withAlpha(bg, 0.6)} 70%, ${withAlpha(bg, 0)} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '20%' },\n });\n\n // Title text - positioned at top of slide\n layers.push({\n type: 'text',\n id: 'title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: '9%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 1, delay: 0.3 },\n });\n }\n\n // Add caption if provided\n if (caption) {\n layers.push({\n type: 'shape',\n id: 'caption-overlay',\n content: {\n shape: 'rect',\n fill: `linear-gradient(0deg, ${withAlpha(bg, 0.85)} 0%, ${withAlpha(bg, 0.6)} 70%, ${withAlpha(bg, 0)} 100%)`,\n },\n position: { x: 0, y: '80%', width: '100%', height: '20%' },\n });\n\n // Caption text\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: layout.captionY,\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 1, delay: 0.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Full Bleed Quote Template\n *\n * Short dramatic text filling the viewport like a movie title card.\n * Designed for punchy text under 60 characters. Uses massive font\n * centered on a dark vignette background.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { FullBleedQuoteInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n resolveColorScheme,\n getTemplateHint,\n getThemeFont,\n shouldUseShadow,\n themedFontSize,\n} from '../utils/themeUtils.js';\nimport { oklchDarken } from '../../schemas/colorUtils.js';\n\n/**\n * Hint schema published for the theme validator + future customizer hint UI.\n * Themes may set `templateHints.fullBleedQuote` entries matching these keys.\n */\nexport const fullBleedQuoteHintSchema = {\n /** Entrance style for the quote text. 'subtle' or 'dramatic'. */\n entrance: {\n type: 'string' as const,\n options: ['subtle', 'dramatic'] as const,\n default: 'subtle',\n },\n} as const;\n\nexport function fullBleedQuote(input: FullBleedQuoteInput, context: TemplateContext): Layer[] {\n const { text, colorScheme = 'blue' } = input;\n const colors = resolveColorScheme(context, colorScheme);\n\n // Massive font for dramatic impact\n const textFontSize = themedFontSize(120, context, true);\n\n return [\n // Background — radial vignette built from the color scheme's own\n // surface, so the card takes on the scheme (and the theme behind it)\n // instead of vignetting every theme into hard black.\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: `radial-gradient(ellipse at 50% 50%, ${colors.bg} 0%, ${oklchDarken(colors.bg, 0.12)} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n // The text — massive, centered\n {\n type: 'text',\n id: 'impact-text',\n content: {\n text,\n style: {\n fontSize: textFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: colors.text,\n textAlign: 'center',\n lineHeight: 1.2,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: '50%',\n anchor: 'center',\n width: '85%',\n },\n animation:\n getTemplateHint<string>(context, 'fullBleedQuote', 'entrance', 'subtle') === 'dramatic'\n ? { type: 'zoomIn', duration: 0.8 }\n : { type: 'fadeIn', duration: 1.5 },\n },\n ];\n}\n","/**\n * List Block Template\n *\n * Displays 3-5 items in a numbered vertical list with staggered animations.\n * Good for enumerations like \"things to see\", \"key features\", or \"tips\".\n * Supports optional accent images.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { ListBlockInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n shouldUseShadow,\n themedEntrance,\n themedFontSize,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\n\nexport function listBlock(input: ListBlockInput, context: TemplateContext): Layer[] {\n const { title, accentImage } = input;\n // `items` is required by the schema, but malformed / partially-authored\n // blocks (e.g. someone wrote `template: list` with no items yet) reach\n // this code path during live preview. Treat missing/non-array as empty\n // so we render the title-and-background frame instead of blowing up\n // every keystroke with a TypeError.\n const items: string[] = Array.isArray(input.items) ? input.items : [];\n const { theme } = context;\n\n // Get layout adjustments if accent image is present\n const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;\n\n const titleFontSize = themedFontSize(44, context, true);\n const itemFontSize = themedFontSize(34, context, false);\n\n const layers: Layer[] = [\n // Background — gradient\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: `linear-gradient(155deg, ${theme.colors.backgroundLight} 0%, ${theme.colors.background} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n // Add accent image layers\n if (accentImage) {\n layers.push(\n ...createAccentLayers(\n accentImage,\n input.id,\n themedImageTreatment(context, input.imageTreatment),\n ),\n );\n }\n\n // Left edge of the text column — derived from the center+width pair so\n // the title and items share one left axis instead of a centered title\n // floating over a left-aligned column. Falls back to a safe 8% if the\n // layout strings ever turn out unparseable (they're authored, but\n // defensive against future edits to the accent-layout types).\n const centerX = parseFloat(accentLayout.textCenterX);\n const widthPct = parseFloat(accentLayout.textWidth);\n const leftX =\n Number.isFinite(centerX) && Number.isFinite(widthPct) ? `${centerX - widthPct / 2}%` : '8%';\n\n // Title if provided\n const startY = title ? 34 : 26;\n if (title) {\n layers.push({\n type: 'text',\n id: 'list-title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'left',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: leftX,\n y: adjustY('20%', accentLayout),\n width: accentLayout.textWidth,\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1 }),\n });\n }\n\n // Stack items with a fixed compact gap rather than stretching them\n // across the available band. Distributing items across (startY → 80%)\n // left big vertical gaps for short lists and made the slide read as a\n // sparse menu instead of a tight enumeration; conversely a bare\n // line-height gap read as a cramped paragraph — 18px of air keeps each\n // entry its own line without breaking the group.\n //\n // Spacing = item line-height (34px base × 1.2) + 18px gap, expressed\n // as % of the 1080px design canvas (~5.4%). Wrapped items push the\n // next entry down via their own line-height, so this sets the\n // minimum baseline-to-baseline distance for unwrapped items.\n const LIST_ITEM_BASE_PX = 34;\n const LIST_ITEM_LINE_HEIGHT = 1.2;\n const LIST_ITEM_GAP_PX = 18;\n const DESIGN_HEIGHT_PX = 1080;\n const spacing =\n ((LIST_ITEM_BASE_PX * LIST_ITEM_LINE_HEIGHT + LIST_ITEM_GAP_PX) / DESIGN_HEIGHT_PX) * 100;\n\n // List items with staggered animation\n for (let i = 0; i < items.length; i++) {\n const y = startY + spacing * i;\n const itemText = `${i + 1}. ${items[i]}`;\n\n layers.push({\n type: 'text',\n id: `item-${i}`,\n content: {\n text: itemText,\n style: {\n fontSize: itemFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'left',\n lineHeight: 1.2,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: leftX,\n y: adjustY(`${y}%`, accentLayout),\n width: accentLayout.textWidth,\n },\n animation: themedEntrance(context, 'text', {\n type: 'fadeIn',\n duration: 0.8,\n delay: 0.3 + 0.3 * i,\n }),\n });\n }\n\n return layers;\n}\n","/**\n * Photo Grid Template\n *\n * Displays 2-4 images in a tiled layout for visual variety.\n * Layout adapts based on image count and viewport orientation:\n *\n * Landscape:\n * - 2 images: side-by-side 50/50\n * - 3 images: one large left (60%) + two stacked right\n * - 4 images: 2x2 grid\n *\n * Portrait (9:16): images stack horizontally (top/bottom) instead of\n * side-by-side, giving each image full width on narrow screens.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { PhotoGridInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { getThemeFont, themedFontSize, themedImageTreatment } from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { cleanCaption } from './captionUtils.js';\n\n/** Gap between images as percentage */\nconst GAP = 1.2;\n\nexport function photoGrid(input: PhotoGridInput, context: TemplateContext): Layer[] {\n const { images, caption: rawCaption, ambientMotion } = input;\n\n // Guard: images array is required\n if (!images || images.length === 0) return [];\n\n const caption = rawCaption ? cleanCaption(rawCaption) : rawCaption;\n const { theme, layout } = context;\n\n const treatment = themedImageTreatment(context, input.imageTreatment);\n\n const layers: Layer[] = [\n // Theme surface background (visible in gaps between images)\n {\n type: 'shape',\n id: 'bg',\n content: { shape: 'rect', fill: theme.colors.background },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n const gridHeight = 100;\n\n // Generate image positions based on count; stack vertically in portrait\n const positions = getGridPositions(images.length, gridHeight, layout.stackColumns);\n\n for (let i = 0; i < Math.min(images.length, 4); i++) {\n const img = images[i];\n const pos = positions[i];\n\n // Placeholder behind image (visible if image fails to load)\n layers.push({\n type: 'shape',\n id: `grid-placeholder-${i}`,\n content: { shape: 'rect', fill: theme.colors.background },\n position: { x: `${pos.x}%`, y: `${pos.y}%`, width: `${pos.w}%`, height: `${pos.h}%` },\n });\n\n // Image layer (covers placeholder when loaded)\n layers.push({\n type: 'image',\n id: `grid-img-${i}`,\n content: {\n src: img.src,\n alt: img.alt,\n fit: 'cover',\n credit: img.credit,\n license: img.license,\n ...(treatment ? { treatment } : {}),\n },\n position: {\n x: `${pos.x}%`,\n y: `${pos.y}%`,\n width: `${pos.w}%`,\n height: `${pos.h}%`,\n },\n // Apply ambient motion to the first (largest) image only\n animation:\n i === 0 && ambientMotion\n ? { type: ambientMotion, duration: 15 }\n : { type: 'fadeIn', duration: 1, delay: 0.2 * i },\n });\n }\n\n // Caption in a compact translucent chip above player controls. Long\n // archival/Wikimedia descriptions are intentionally clamped.\n if (caption) {\n const captionFontSize = themedFontSize(layout.stackColumns ? 24 : 26, context, false);\n const captionY = layout.stackColumns ? '78%' : '74%';\n const captionBgY = layout.stackColumns ? '72%' : '68%';\n const captionHeight = layout.stackColumns ? '13%' : '16%';\n const bg = theme.colors.background;\n\n layers.push({\n type: 'shape',\n id: 'caption-bg',\n content: {\n shape: 'rect',\n fill: `linear-gradient(90deg, ${withAlpha(bg, 0)}, ${withAlpha(bg, 0.78)} 16%, ${withAlpha(bg, 0.78)} 84%, ${withAlpha(bg, 0)})`,\n },\n position: { x: 0, y: captionBgY, width: '100%', height: captionHeight },\n });\n\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n lineHeight: 1.18,\n maxLines: layout.stackColumns ? 2 : 1,\n },\n },\n position: {\n x: '50%',\n y: captionY,\n anchor: 'center',\n width: '78%',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 0.5 },\n });\n }\n\n return layers;\n}\n\ninterface GridPos {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nfunction getGridPositions(count: number, gridHeight: number, stacked: boolean): GridPos[] {\n const halfW = 50 - GAP / 2;\n const halfH = gridHeight / 2 - GAP / 2;\n\n switch (count) {\n case 2:\n if (stacked) {\n // Portrait: stack top/bottom\n return [\n { x: 0, y: 0, w: 100, h: halfH },\n { x: 0, y: gridHeight / 2 + GAP / 2, w: 100, h: halfH },\n ];\n }\n return [\n { x: 0, y: 0, w: halfW, h: gridHeight },\n { x: 50 + GAP / 2, y: 0, w: halfW, h: gridHeight },\n ];\n case 3: {\n if (stacked) {\n // Portrait: stack all three vertically\n const thirdH = gridHeight / 3 - (GAP * 2) / 3;\n return [\n { x: 0, y: 0, w: 100, h: thirdH },\n { x: 0, y: gridHeight / 3 + GAP / 3, w: 100, h: thirdH },\n { x: 0, y: (gridHeight * 2) / 3 + (GAP * 2) / 3, w: 100, h: thirdH },\n ];\n }\n const leftW = 60 - GAP / 2;\n const rightW = 40 - GAP / 2;\n return [\n { x: 0, y: 0, w: leftW, h: gridHeight },\n { x: 60 + GAP / 2, y: 0, w: rightW, h: halfH },\n { x: 60 + GAP / 2, y: gridHeight / 2 + GAP / 2, w: rightW, h: halfH },\n ];\n }\n case 4:\n default:\n // 2x2 grid works well in both orientations\n return [\n { x: 0, y: 0, w: halfW, h: halfH },\n { x: 50 + GAP / 2, y: 0, w: halfW, h: halfH },\n { x: 0, y: gridHeight / 2 + GAP / 2, w: halfW, h: halfH },\n { x: 50 + GAP / 2, y: gridHeight / 2 + GAP / 2, w: halfW, h: halfH },\n ];\n }\n}\n","/**\n * Definition Card Template\n *\n * Dictionary-style slide with a large term and its definition.\n * Good for explaining local words, place names, or cultural concepts.\n * Supports optional accent images.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { DefinitionCardInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n resolveColorScheme,\n getThemeFont,\n shouldUseShadow,\n themedEntrance,\n themedFontSize,\n themedSurfaceGradient,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer } from './captionUtils.js';\n\nexport function definitionCard(input: DefinitionCardInput, context: TemplateContext): Layer[] {\n const { term, definition, origin, colorScheme = 'blue', accentImage } = input;\n const { theme } = context;\n const colors = resolveColorScheme(context, colorScheme);\n\n // Get layout adjustments if accent image is present\n const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;\n\n const termFontSize = themedFontSize(72, context, true);\n const defFontSize = themedFontSize(32, context, false);\n const originFontSize = themedFontSize(22, context, false);\n\n const layers: Layer[] = [createBackgroundLayer('bg', themedSurfaceGradient(context, 145))];\n\n // Add accent image layers\n if (accentImage) {\n layers.push(\n ...createAccentLayers(\n accentImage,\n input.id,\n themedImageTreatment(context, input.imageTreatment),\n ),\n );\n }\n\n // Term — large, accent-colored\n layers.push({\n type: 'text',\n id: 'term',\n content: {\n text: term,\n style: {\n fontSize: termFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('30%', accentLayout),\n anchor: 'center',\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1.5 }),\n });\n\n // Horizontal separator line\n layers.push({\n type: 'shape',\n id: 'separator',\n content: {\n shape: 'rect',\n fill: `${colors.text}33`, // accent color at 20% opacity\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('42%', accentLayout),\n width: '30%',\n height: '2px',\n anchor: 'center',\n },\n });\n\n // Definition text\n layers.push({\n type: 'text',\n id: 'definition',\n content: {\n text: definition,\n style: {\n fontSize: defFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'center',\n lineHeight: 1.6,\n maxLines: 4,\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('55%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 1, delay: 0.8 }),\n });\n\n // Origin if provided\n if (origin) {\n layers.push({\n type: 'text',\n id: 'origin',\n content: {\n text: origin,\n style: {\n fontSize: originFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('70%', accentLayout),\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 1.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Comparison Bar Template\n *\n * Two horizontal bars showing relative numeric values side by side.\n * Bar widths are proportional to the values for immediate visual comparison.\n * Good for population, distance, or measurement comparisons.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { ComparisonBarInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n resolveColorScheme,\n getThemeFont,\n themedFontSize,\n themedSurfaceGradient,\n} from '../utils/themeUtils.js';\n\nexport function comparisonBar(input: ComparisonBarInput, context: TemplateContext): Layer[] {\n const { leftLabel, rightLabel, unit, colorScheme = 'blue' } = input;\n const { theme, viewport } = context;\n const colors = resolveColorScheme(context, colorScheme);\n\n // Defensive: coerce non-finite values to 0. Callers should pass real numbers,\n // but a NaN would otherwise render as literal \"NaN\" text and collapse every\n // bar/label to a `NaN%` position, cramming the block against its left edge.\n const leftValue = Number.isFinite(input.leftValue) ? input.leftValue : 0;\n const rightValue = Number.isFinite(input.rightValue) ? input.rightValue : 0;\n\n const labelFontSize = themedFontSize(28, context, false);\n const valueFontSize = themedFontSize(48, context, true);\n\n // Format values for display\n const formatValue = (v: number): string => {\n if (!Number.isFinite(v)) return '—';\n if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`;\n if (v >= 1_000) return `${(v / 1_000).toFixed(v >= 10_000 ? 0 : 1)}K`;\n return v.toLocaleString();\n };\n\n const leftDisplay = unit ? `${formatValue(leftValue)} ${unit}` : formatValue(leftValue);\n const rightDisplay = unit ? `${formatValue(rightValue)} ${unit}` : formatValue(rightValue);\n\n // Bar positioning\n const barStartX = 15;\n const barHeight = 6; // % of viewport height\n const topBarY = 36;\n const bottomBarY = 58;\n\n // Calculate proportional bar widths. The widest bar must leave room\n // for its trailing value label inside a 96% safe area — otherwise a\n // long label (\"84 clarity score\") runs off the right edge of the block.\n const maxValue = Math.max(leftValue, rightValue, 1);\n const longestLabelPx = Math.max(leftDisplay.length, rightDisplay.length) * valueFontSize * 0.58;\n const labelWidthPct = (longestLabelPx / viewport.width) * 100;\n const maxBarWidth = Math.max(20, Math.min(65, 96 - barStartX - labelWidthPct - 2));\n const leftBarWidth = (leftValue / maxValue) * maxBarWidth;\n const rightBarWidth = (rightValue / maxValue) * maxBarWidth;\n\n return [\n // Background\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: themedSurfaceGradient(context, 180),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n\n // Top bar label\n {\n type: 'text',\n id: 'left-label',\n content: {\n text: leftLabel,\n style: {\n fontSize: labelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n },\n },\n position: { x: `${barStartX}%`, y: `${topBarY - 8}%` },\n },\n\n // Top bar value\n {\n type: 'text',\n id: 'left-value',\n content: {\n text: leftDisplay,\n style: {\n fontSize: valueFontSize,\n fontFamily: getThemeFont(context, 'body'),\n fontWeight: 'bold',\n color: colors.text,\n },\n },\n position: { x: `${barStartX + leftBarWidth + 2}%`, y: `${topBarY - 1}%` },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.3 },\n },\n\n // Top bar\n {\n type: 'shape',\n id: 'left-bar',\n content: {\n shape: 'rect',\n fill: colors.text,\n borderRadius: 4,\n },\n position: {\n x: `${barStartX}%`,\n y: `${topBarY}%`,\n width: `${leftBarWidth}%`,\n height: `${barHeight}%`,\n },\n animation: { type: 'fadeIn', duration: 1 },\n },\n\n // Bottom bar label\n {\n type: 'text',\n id: 'right-label',\n content: {\n text: rightLabel,\n style: {\n fontSize: labelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n },\n },\n position: { x: `${barStartX}%`, y: `${bottomBarY - 8}%` },\n },\n\n // Bottom bar value\n {\n type: 'text',\n id: 'right-value',\n content: {\n text: rightDisplay,\n style: {\n fontSize: valueFontSize,\n fontFamily: getThemeFont(context, 'body'),\n fontWeight: 'bold',\n color: colors.accent,\n },\n },\n position: { x: `${barStartX + rightBarWidth + 2}%`, y: `${bottomBarY - 1}%` },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.6 },\n },\n\n // Bottom bar\n {\n type: 'shape',\n id: 'right-bar',\n content: {\n shape: 'rect',\n fill: colors.accent,\n borderRadius: 4,\n },\n position: {\n x: `${barStartX}%`,\n y: `${bottomBarY}%`,\n width: `${rightBarWidth}%`,\n height: `${barHeight}%`,\n },\n animation: { type: 'fadeIn', duration: 1, delay: 0.3 },\n },\n ];\n}\n","/**\n * Pull Quote Template\n *\n * Quote text over a full-bleed background image with dark overlay.\n * Cinematic alternative to quoteBlock when a high-quality image is available.\n * Combines the visual impact of imageWithCaption with the text focus of quoteBlock.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { PullQuoteInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n themedEntrance,\n themedFontSize,\n themedImageTreatment,\n} from '../utils/themeUtils.js';\nimport { estimateTextHeight } from './captionUtils.js';\n\nexport function pullQuote(input: PullQuoteInput, context: TemplateContext): Layer[] {\n const { text, attribution, backgroundImage, ambientMotion } = input;\n const { viewport } = context;\n\n const treatment = themedImageTreatment(context, input.imageTreatment);\n\n // Guard: backgroundImage is required\n if (!backgroundImage?.src) return [];\n\n const quoteFontSize = themedFontSize(52, context, true);\n const attrFontSize = themedFontSize(26, context, false);\n const decoFontSize = themedFontSize(200, context, true);\n const quoteLineHeight = 1.4;\n const quoteYPct = attribution ? 45 : 50;\n\n const layers: Layer[] = [\n // Full-bleed background image\n {\n type: 'image',\n id: 'bg-image',\n content: {\n src: backgroundImage.src,\n alt: backgroundImage.alt,\n fit: 'cover',\n credit: backgroundImage.credit,\n license: backgroundImage.license,\n ...(treatment ? { treatment } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: ambientMotion ? { type: ambientMotion, duration: 15 } : undefined,\n },\n\n // Dark overlay for text readability\n {\n type: 'shape',\n id: 'overlay',\n content: {\n shape: 'rect',\n fill: 'rgba(0, 0, 0, 0.55)',\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n\n // Decorative quotation mark\n {\n type: 'text',\n id: 'deco-quote',\n content: {\n text: '\\u201C',\n style: {\n fontSize: decoFontSize,\n fontFamily: getThemeFont(context, 'title'),\n color: 'rgba(255, 255, 255, 0.08)',\n textAlign: 'center',\n },\n },\n position: {\n x: '50%',\n y: '20%',\n anchor: 'center',\n },\n },\n\n // Quote text\n {\n type: 'text',\n id: 'quote-text',\n content: {\n text,\n style: {\n fontSize: quoteFontSize,\n fontFamily: getThemeFont(context, 'title'),\n color: '#ffffff',\n textAlign: 'center',\n lineHeight: quoteLineHeight,\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: `${quoteYPct}%`,\n anchor: 'center',\n width: '72%',\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 2 }),\n },\n ];\n\n // Attribution \\u2014 hangs just below the quote's estimated bottom edge so\n // the two read as one lockup instead of stranding near the bottom.\n if (attribution) {\n const quoteWidthPx = 0.72 * viewport.width;\n const quoteHeightPx = estimateTextHeight(text, quoteFontSize, quoteWidthPx, quoteLineHeight);\n const attrYPct = Math.min(\n 82,\n quoteYPct + ((quoteHeightPx / 2 + quoteFontSize * 1.7) / viewport.height) * 100,\n );\n layers.push({\n type: 'text',\n id: 'attribution',\n content: {\n text: `\\u2014 ${attribution}`,\n style: {\n fontSize: attrFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: 'rgba(255, 255, 255, 0.85)',\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: `${attrYPct}%`,\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 1.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Video With Caption Template\n *\n * Full-screen background video clip with text overlay. Mirrors the structure of\n * imageWithCaption but uses a VideoLayer instead of an ImageLayer. The video\n * plays muted — narration audio is the only sound track.\n *\n * Adapts caption positioning and font sizes for different viewports.\n *\n * This is shared code used by both site and efb-app doc renderers.\n *\n * Related Files:\n * - shared/story/templates/imageWithCaption.ts — image equivalent\n * - schemas/StoryScript.ts — VideoLayer type\n * - site/src/components/story/layers/VideoLayer.tsx — rendering component\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { VideoWithCaptionInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { getThemeFont, shouldUseShadow, themedFontSize } from '../utils/themeUtils.js';\nimport { withAlpha } from '../../schemas/colorUtils.js';\nimport { cleanCaption } from './captionUtils.js';\n\nexport function videoWithCaption(input: VideoWithCaptionInput, context: TemplateContext): Layer[] {\n const {\n videoSrc,\n posterSrc,\n videoAlt,\n clipStart,\n clipEnd,\n sourceDuration,\n caption: rawCaption,\n captionPosition = 'bottom',\n videoCredit,\n videoLicense,\n } = input;\n const caption = rawCaption ? cleanCaption(rawCaption) : rawCaption;\n const { theme } = context;\n\n const captionFontSize = themedFontSize(30, context, false);\n const creditFontSize = themedFontSize(16, context, false);\n\n const layers: Layer[] = [\n // Background video clip\n {\n type: 'video',\n id: 'bg-video',\n content: {\n src: videoSrc,\n posterSrc,\n alt: videoAlt,\n fit: 'cover',\n clipStart,\n clipEnd,\n sourceDuration,\n credit: videoCredit,\n license: videoLicense,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n // Caption text with a compact translucent band. Long archival captions are\n // clamped so they don't collide with player controls or cover the frame.\n if (caption) {\n const bg = theme.colors.background;\n const captionBgY =\n captionPosition === 'top' ? '7%' : captionPosition === 'center' ? '42%' : '68%';\n const captionY =\n captionPosition === 'top' ? '13%' : captionPosition === 'center' ? '48%' : '74%';\n\n layers.push({\n type: 'shape',\n id: 'caption-gradient',\n content: {\n shape: 'rect',\n fill: `linear-gradient(90deg, ${withAlpha(bg, 0)}, ${withAlpha(bg, 0.78)} 16%, ${withAlpha(bg, 0.78)} 84%, ${withAlpha(bg, 0)})`,\n },\n position: {\n x: 0,\n y: captionBgY,\n width: '100%',\n height: '16%',\n },\n });\n\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n lineHeight: 1.18,\n maxLines: 2,\n },\n },\n position: {\n x: '50%',\n y: captionY,\n anchor: 'center',\n width: '78%',\n },\n animation: { type: 'fadeIn', duration: 1.5, delay: 0.5 },\n });\n }\n\n // Small credit text in bottom-right corner\n if (videoCredit) {\n layers.push({\n type: 'text',\n id: 'credit',\n content: {\n text: videoCredit,\n style: {\n fontSize: creditFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: withAlpha(theme.colors.text, 0.65),\n textAlign: 'right',\n },\n },\n position: {\n x: '96%',\n y: '96%',\n anchor: 'bottom-right',\n },\n });\n }\n\n return layers;\n}\n","/**\n * Video Pull Quote Template\n *\n * Quote text over a video clip background with dark overlay.\n * Cinematic alternative to pullQuote when a video clip is available.\n * Combines the visual dynamism of live video with the text focus of quoteBlock.\n *\n * The video plays muted — narration audio is the only sound track.\n *\n * This is shared code used by both site and efb-app doc renderers.\n *\n * Related Files:\n * - shared/story/templates/pullQuote.ts — image-based equivalent\n * - schemas/StoryScript.ts — VideoLayer type\n * - site/src/components/story/layers/VideoLayer.tsx — rendering component\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { VideoPullQuoteInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { getThemeFont, themedEntrance, themedFontSize } from '../utils/themeUtils.js';\nimport { estimateTextHeight } from './captionUtils.js';\n\nexport function videoPullQuote(input: VideoPullQuoteInput, context: TemplateContext): Layer[] {\n const { text, attribution, backgroundVideo } = input;\n const { viewport } = context;\n\n // Guard: backgroundVideo is required\n if (!backgroundVideo?.src) return [];\n\n const quoteFontSize = themedFontSize(52, context, true);\n const attrFontSize = themedFontSize(26, context, false);\n const decoFontSize = themedFontSize(200, context, true);\n const quoteLineHeight = 1.4;\n const quoteYPct = attribution ? 45 : 50;\n\n const layers: Layer[] = [\n // Full-bleed background video clip\n {\n type: 'video',\n id: 'bg-video',\n content: {\n src: backgroundVideo.src,\n posterSrc: backgroundVideo.posterSrc,\n alt: backgroundVideo.alt,\n fit: 'cover',\n clipStart: backgroundVideo.clipStart,\n clipEnd: backgroundVideo.clipEnd,\n credit: backgroundVideo.credit,\n license: backgroundVideo.license,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n\n // Dark overlay for text readability\n {\n type: 'shape',\n id: 'overlay',\n content: {\n shape: 'rect',\n fill: 'rgba(0, 0, 0, 0.55)',\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n\n // Decorative quotation mark\n {\n type: 'text',\n id: 'deco-quote',\n content: {\n text: '\\u201C',\n style: {\n fontSize: decoFontSize,\n fontFamily: getThemeFont(context, 'title'),\n color: 'rgba(255, 255, 255, 0.08)',\n textAlign: 'center',\n },\n },\n position: {\n x: '50%',\n y: '20%',\n anchor: 'center',\n },\n },\n\n // Quote text\n {\n type: 'text',\n id: 'quote-text',\n content: {\n text,\n style: {\n fontSize: quoteFontSize,\n fontFamily: getThemeFont(context, 'title'),\n color: '#ffffff',\n textAlign: 'center',\n lineHeight: quoteLineHeight,\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: `${quoteYPct}%`,\n anchor: 'center',\n width: '72%',\n },\n animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 2 }),\n },\n ];\n\n // Attribution \\u2014 hangs just below the quote's estimated bottom edge so\n // the two read as one lockup instead of stranding near the bottom.\n if (attribution) {\n const quoteWidthPx = 0.72 * viewport.width;\n const quoteHeightPx = estimateTextHeight(text, quoteFontSize, quoteWidthPx, quoteLineHeight);\n const attrYPct = Math.min(\n 82,\n quoteYPct + ((quoteHeightPx / 2 + quoteFontSize * 1.7) / viewport.height) * 100,\n );\n layers.push({\n type: 'text',\n id: 'attribution',\n content: {\n text: `\\u2014 ${attribution}`,\n style: {\n fontSize: attrFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: 'rgba(255, 255, 255, 0.85)',\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: `${attrYPct}%`,\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 1.5 },\n });\n }\n\n return layers;\n}\n","/**\n * Data Table Template\n *\n * Renders a themed table with header row and data rows.\n * Uses a TableLayer (foreignObject-based HTML table inside SVG)\n * for proper table layout within the viewport.\n *\n * Adapts font sizes for different viewports and uses theme colors\n * for header background, text, borders, and body cells.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { DataTableInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport {\n getThemeFont,\n resolveColorScheme,\n shouldUseShadow,\n themedFontSize,\n themedSurfaceGradient,\n} from '../utils/themeUtils.js';\nimport { pickContrastingText, withAlpha } from '../../schemas/colorUtils.js';\nimport { createBackgroundLayer } from './captionUtils.js';\n\nexport function dataTable(input: DataTableInput, context: TemplateContext): Layer[] {\n const { title, headers, rows, align, colorScheme } = input;\n const { theme, viewport } = context;\n\n const colors = resolveColorScheme(context, colorScheme);\n const titleFontSize = themedFontSize(48, context, true);\n const tableFontSize = themedFontSize(28, context, false);\n\n const layers: Layer[] = [createBackgroundLayer('bg', themedSurfaceGradient(context, 170))];\n\n // Size the table band from its natural content height (header + rows at\n // ~2.4× line height for cell padding) and center the title+table as one\n // group — a full-height band left a ~200px orphan gap under the title.\n // `rows` is required by the schema but may be missing on partially-\n // authored blocks in live preview; treat it as empty rather than throwing.\n const rowCount = (Array.isArray(rows) ? rows.length : 0) + 1;\n const naturalTableHPct = Math.min(74, ((rowCount * tableFontSize * 2.4) / viewport.height) * 100);\n const titleBandPct = title ? (titleFontSize * 2.2 * 100) / viewport.height : 0;\n const groupTopPct = Math.max(8, (100 - titleBandPct - naturalTableHPct) / 2);\n\n // Optional title above the table\n if (title) {\n layers.push({\n type: 'text',\n id: 'title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: shouldUseShadow(context),\n },\n },\n position: {\n x: '50%',\n y: `${groupTopPct + titleBandPct / 2}%`,\n width: '80%',\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 0.8 },\n });\n }\n\n // Table layer. Header text color is picked for contrast against the\n // header fill — pairing two mid-tones from the same scheme made the\n // header row read as a smudge in most themes.\n layers.push({\n type: 'table',\n id: 'table',\n content: {\n headers,\n rows,\n align,\n style: {\n headerBackground: colors.accent,\n headerColor: pickContrastingText(colors.accent),\n cellBackground: withAlpha(theme.colors.text, 0.04),\n cellColor: theme.colors.text,\n borderColor: withAlpha(theme.colors.text, 0.15),\n fontSize: tableFontSize,\n fontFamily: getThemeFont(context, 'body'),\n headerFontFamily: getThemeFont(context, 'title'),\n borderRadius: 8,\n },\n },\n position: {\n x: '10%',\n y: `${groupTopPct + titleBandPct}%`,\n width: '80%',\n height: `${naturalTableHPct}%`,\n },\n animation: { type: 'fadeIn', duration: 1, delay: title ? 0.4 : 0 },\n });\n\n return layers;\n}\n","/**\n * Diagram layout — shared between the read-only SVG render path\n * (`diagramBlock` template) and the interactive editor (React Flow in\n * `@bendyline/squisq-editor-react`). Same children + same options always\n * produce the same node positions and edge list, so previews and the\n * editor stay visually consistent.\n *\n * Layout rules:\n * 1. Children with both `x` and `y` set keep their authored coordinates.\n * 2. Children missing either coordinate are auto-placed in a square-ish\n * grid in declaration order, offset below the bounding box of the\n * explicitly-positioned children.\n * 3. Edges come from each child's `connectsTo`. Targets that don't match\n * any sibling id are dropped (and counted in `warnings`).\n */\n\nimport type { Block, BlockConnection } from '../../schemas/Doc.js';\n\nexport interface DiagramNodePosition {\n /** Block id (matches `Block.id`). */\n id: string;\n /** Display label (block title or fallback to id). */\n label: string;\n /** Pixel x coordinate (canvas-relative). */\n x: number;\n /** Pixel y coordinate (canvas-relative). */\n y: number;\n /** True when the position came from `x=`/`y=` attributes; false when auto-laid out. */\n pinned: boolean;\n /** Optional `.class` tokens carried over from the heading. */\n classes?: string[];\n}\n\nexport interface DiagramEdge {\n /** Synthetic stable edge id. */\n id: string;\n /** Source node id. */\n source: string;\n /** Target node id. */\n target: string;\n /** Connection type from `connectsTo=target:type` (used as edge label). */\n type?: string;\n}\n\nexport interface DiagramLayout {\n nodes: DiagramNodePosition[];\n edges: DiagramEdge[];\n warnings: string[];\n}\n\nexport interface DiagramLayoutOptions {\n /** Horizontal grid pitch between auto-laid-out nodes. Default: 260 (node width 180 + 80 air). */\n gapX?: number;\n /** Vertical grid pitch between auto-laid-out nodes. Default: 140. */\n gapY?: number;\n /** Padding around the explicit-positions bounding box before the grid starts. Default: 80. */\n gridPad?: number;\n /** Origin x for the grid when no explicit nodes anchor it. Default: 80. */\n gridOriginX?: number;\n /** Origin y for the grid when no explicit nodes anchor it. Default: 80. */\n gridOriginY?: number;\n}\n\nconst DEFAULT_OPTS: Required<DiagramLayoutOptions> = {\n gapX: 260,\n gapY: 140,\n gridPad: 80,\n gridOriginX: 80,\n gridOriginY: 80,\n};\n\n/**\n * Compute node positions and edges for a diagram from a parent block's\n * children. Pure function — same inputs always produce the same output.\n */\nexport function computeDiagramLayout(\n children: readonly Block[],\n options: DiagramLayoutOptions = {},\n): DiagramLayout {\n const opts = { ...DEFAULT_OPTS, ...options };\n const warnings: string[] = [];\n\n // First pass: extract explicit positions, record which children still need a grid slot.\n type Entry = { child: Block; pinned: boolean; x: number; y: number };\n const entries: Entry[] = [];\n const unpositionedIdx: number[] = [];\n\n for (let i = 0; i < children.length; i++) {\n const c = children[i];\n const hasX = typeof c.x === 'number';\n const hasY = typeof c.y === 'number';\n if (hasX && hasY) {\n entries.push({ child: c, pinned: true, x: c.x as number, y: c.y as number });\n } else {\n entries.push({ child: c, pinned: false, x: 0, y: 0 });\n unpositionedIdx.push(i);\n }\n }\n\n // Second pass: lay out unpositioned children in a square-ish grid.\n if (unpositionedIdx.length > 0) {\n const cols = Math.max(1, Math.ceil(Math.sqrt(unpositionedIdx.length)));\n // Find the bottom-right of the pinned bounding box so the grid sits below it.\n let baseX = opts.gridOriginX;\n let baseY = opts.gridOriginY;\n const pinned = entries.filter((e) => e.pinned);\n if (pinned.length > 0) {\n const minX = Math.min(...pinned.map((e) => e.x));\n const maxY = Math.max(...pinned.map((e) => e.y));\n baseX = minX;\n baseY = maxY + opts.gridPad + opts.gapY;\n }\n unpositionedIdx.forEach((entryIdx, k) => {\n const col = k % cols;\n const row = Math.floor(k / cols);\n entries[entryIdx].x = baseX + col * opts.gapX;\n entries[entryIdx].y = baseY + row * opts.gapY;\n });\n }\n\n // Third pass: emit nodes + the id-set used to validate edge targets.\n const nodes: DiagramNodePosition[] = entries.map((e) => ({\n id: e.child.id,\n label: e.child.title ?? e.child.id,\n x: e.x,\n y: e.y,\n pinned: e.pinned,\n ...(e.child.classes && e.child.classes.length > 0 ? { classes: e.child.classes } : {}),\n }));\n const nodeIds = new Set(nodes.map((n) => n.id));\n\n // Fourth pass: emit edges from each child's connectsTo, dropping ghosts.\n const edges: DiagramEdge[] = [];\n const seenEdgeIds = new Set<string>();\n for (const child of children) {\n if (!child.connectsTo) continue;\n for (const conn of child.connectsTo) {\n if (!nodeIds.has(conn.target)) {\n warnings.push(`Edge from \"${child.id}\" to \"${conn.target}\" dropped: no such sibling node`);\n continue;\n }\n const edgeId = makeEdgeId(child.id, conn);\n if (seenEdgeIds.has(edgeId)) continue;\n seenEdgeIds.add(edgeId);\n edges.push({\n id: edgeId,\n source: child.id,\n target: conn.target,\n ...(conn.type ? { type: conn.type } : {}),\n });\n }\n }\n\n return { nodes, edges, warnings };\n}\n\nfunction makeEdgeId(source: string, conn: BlockConnection): string {\n return conn.type ? `${source}->${conn.target}:${conn.type}` : `${source}->${conn.target}`;\n}\n","/**\n * Diagram block template.\n *\n * Renders the parent block's children as a node-and-edge diagram:\n * - Each child becomes a rounded-rect node card with its title as label.\n * - Each `child.connectsTo` entry becomes a path edge (cubic-bezier curve\n * with an arrowhead at the target end).\n * - Children with `x`/`y` set are placed at those coordinates; the rest\n * are auto-laid out below in a square-ish grid (see `diagramLayout.ts`).\n *\n * Layout coordinates from `x=`/`y=` are author-defined units. The\n * template computes a bounding box of all nodes and scales it to fit\n * the block's viewport with padding, so users don't have to think about\n * absolute pixel ranges.\n */\n\nimport type { Layer, ShapeLayer, TextLayer, PathLayer, MarkerStyle } from '../../schemas/Doc.js';\nimport type { DiagramBlockInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme, getThemeFont, themedFontSize } from '../utils/themeUtils.js';\nimport { clipEndpoints, connectorPath, lineStyleDasharray } from '../utils/shapeGeometry.js';\nimport { computeDiagramLayout, type DiagramNodePosition } from './diagramLayout.js';\n\nconst NODE_WIDTH = 180;\nconst NODE_HEIGHT = 64;\nconst PADDING = 80;\n\nexport function diagramBlock(input: DiagramBlockInput, context: TemplateContext): Layer[] {\n const { theme, viewport, children = [] } = context;\n const colors = resolveColorScheme(context, input.colorScheme ?? 'blue');\n\n const layout = computeDiagramLayout(children);\n if (layout.nodes.length === 0) {\n // Empty diagram — render a single hint label so the block has visible content.\n return [\n {\n type: 'text',\n id: 'diagram-empty',\n content: {\n text: input.title ?? 'Empty diagram',\n style: {\n fontSize: themedFontSize(36, context, true),\n fontFamily: getThemeFont(context, 'title'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: '50%', anchor: 'center' },\n },\n ];\n }\n\n // Compute the bounding box of all nodes in author coordinates.\n const minX = Math.min(...layout.nodes.map((n) => n.x));\n const maxX = Math.max(...layout.nodes.map((n) => n.x + NODE_WIDTH));\n const minY = Math.min(...layout.nodes.map((n) => n.y));\n const maxY = Math.max(...layout.nodes.map((n) => n.y + NODE_HEIGHT));\n\n // Available area in viewport pixels (after reserving space for the title, if any).\n const titleHeight = input.title ? 120 : 0;\n const availW = Math.max(1, viewport.width - PADDING * 2);\n const availH = Math.max(1, viewport.height - PADDING * 2 - titleHeight);\n const contentW = Math.max(1, maxX - minX);\n const contentH = Math.max(1, maxY - minY);\n\n // Uniform scale so the diagram fits both dimensions. Small diagrams are\n // allowed to grow (everything here is vector) up to 1.8× so a three-node\n // flow doesn't render as a tiny cluster in an empty canvas; label fonts\n // and strokes scale with the nodes, clamped so text never balloons.\n const scale = Math.min(availW / contentW, availH / contentH, 1.8);\n const fontAdj = Math.min(Math.max(scale, 1), 1.5);\n const strokeW = Math.round(2 * fontAdj);\n\n // Compute viewport-pixel coordinates of each node's top-left corner.\n // The title and the scaled diagram are centered together as one group —\n // a top-pinned title over a center-floated diagram split the block into\n // two stranded pieces.\n const scaledW = contentW * scale;\n const scaledH = contentH * scale;\n const groupTop = Math.max(PADDING / 2, (viewport.height - titleHeight - scaledH) / 2);\n const offsetX = PADDING + (availW - scaledW) / 2;\n const offsetY = groupTop + titleHeight;\n const transform = (n: DiagramNodePosition): { x: number; y: number; w: number; h: number } => ({\n x: offsetX + (n.x - minX) * scale,\n y: offsetY + (n.y - minY) * scale,\n w: NODE_WIDTH * scale,\n h: NODE_HEIGHT * scale,\n });\n\n const layers: Layer[] = [];\n\n // Optional title above the diagram.\n if (input.title) {\n layers.push({\n type: 'text',\n id: 'diagram-title',\n content: {\n text: input.title,\n style: {\n fontSize: themedFontSize(40, context, true),\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: groupTop + titleHeight / 2 - 16, anchor: 'center' },\n });\n }\n\n // Position lookup by id for edge routing.\n const positions = new Map<string, { cx: number; cy: number; rx: number; ry: number }>();\n for (const node of layout.nodes) {\n const t = transform(node);\n positions.set(node.id, { cx: t.x + t.w / 2, cy: t.y + t.h / 2, rx: t.w / 2, ry: t.h / 2 });\n }\n\n // Edge styling (applies to all edges; per-edge styling lives on the nodes).\n const startMarker: MarkerStyle = input.startStyle ?? 'none';\n const endMarker: MarkerStyle = input.endStyle ?? 'arrow';\n const edgeDash = lineStyleDasharray(input.lineStyle);\n\n // Edges first so they sit behind nodes.\n for (const edge of layout.edges) {\n const a = positions.get(edge.source);\n const b = positions.get(edge.target);\n if (!a || !b) continue;\n const { start, end } = clipEndpoints(a, b);\n const pathLayer: PathLayer = {\n type: 'path',\n id: `edge-${edge.id}`,\n content: {\n d: connectorPath(input.edgeStyle ?? 'curved', start, end),\n stroke: colors.text ?? theme.colors.primary,\n strokeWidth: strokeW,\n fill: 'none',\n ...(edgeDash ? { dasharray: edgeDash } : {}),\n ...(startMarker !== 'none' ? { startMarker } : {}),\n ...(endMarker !== 'none' ? { endMarker } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n layers.push(pathLayer);\n\n if (edge.type) {\n // Connection-type label, floated just above the edge midpoint so the\n // line never strikes through the text.\n const mx = (a.cx + b.cx) / 2;\n const my = (a.cy + b.cy) / 2;\n const labelFontSize = themedFontSize(Math.round(18 * fontAdj), context, false);\n const labelLayer: TextLayer = {\n type: 'text',\n id: `edge-label-${edge.id}`,\n content: {\n text: edge.type,\n style: {\n fontSize: labelFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n },\n },\n position: { x: mx, y: my - labelFontSize * 0.9, anchor: 'center' },\n };\n layers.push(labelLayer);\n }\n }\n\n // Nodes (card + label) on top of edges.\n for (const node of layout.nodes) {\n const t = transform(node);\n const card: ShapeLayer = {\n type: 'shape',\n id: `node-card-${node.id}`,\n content: {\n shape: 'rect',\n fill: colors.bg ?? theme.colors.backgroundLight,\n stroke: colors.text ?? theme.colors.primary,\n strokeWidth: strokeW,\n borderRadius: input.nodeShape === 'pill' ? t.h / 2 : 10,\n },\n position: { x: t.x, y: t.y, width: t.w, height: t.h },\n };\n layers.push(card);\n\n const label: TextLayer = {\n type: 'text',\n id: `node-label-${node.id}`,\n content: {\n text: node.label,\n style: {\n fontSize: themedFontSize(Math.round(22 * fontAdj), context, false),\n fontFamily: getThemeFont(context, 'body'),\n fontWeight: 'bold',\n color: colors.text ?? theme.colors.text,\n textAlign: 'center',\n },\n },\n position: { x: t.x + t.w / 2, y: t.y + t.h / 2, anchor: 'center', width: t.w },\n };\n layers.push(label);\n }\n\n return layers;\n}\n","/**\n * Drawing layout — shared, pure derivation of shapes + connectors from a\n * drawing block's child headings. Same children always produce the same\n * shapes, so SSR previews, exports, and (eventually) the editor stay\n * visually consistent. Mirrors `diagramLayout.ts` for the richer\n * free-form shape vocabulary.\n *\n * Each child heading is one shape. Its `{[shape …]}` annotation names the\n * primitive (`child.template`) and carries geometry/style as string params\n * (`child.templateOverrides`); `child.id` makes it referenceable;\n * `child.title` is the label.\n *\n * Layout rules (parallel to the diagram):\n * 1. Shapes with both `x` and `y` keep their authored coordinates.\n * 2. Shapes missing either are auto-placed in a square-ish grid below the\n * bounding box of the explicitly-positioned shapes.\n * 3. `line`/`arrow` shapes with `from`/`to` (or any shape's `connectsTo`)\n * become connectors; endpoints that match no sibling id are dropped\n * (and counted in `warnings`).\n */\n\nimport type { Block, MarkerStyle } from '../../schemas/Doc.js';\nimport { extractPlainText } from '../../markdown/utils.js';\nimport { lineStyleDasharray, type ConnectorRouting } from '../utils/shapeGeometry.js';\n\n/**\n * The shape kinds a drawing can render. `rect`/`circle`/`line` render as a\n * native `ShapeLayer`; everything else (and `path`/`arrow`) renders as a\n * computed `PathLayer` (see `shapeGeometry.ts`). `text` is a `TextLayer`.\n */\nexport type DrawingShapeKind =\n | 'rect'\n | 'circle'\n | 'line'\n | 'arrow'\n | 'path'\n | 'text'\n | 'triangle'\n | 'right-triangle'\n | 'diamond'\n | 'pentagon'\n | 'hexagon'\n | 'octagon'\n | 'star'\n | 'star4'\n | 'star6'\n | 'parallelogram'\n | 'trapezoid'\n | 'plus'\n | 'chevron'\n | 'arrow-right'\n | 'arrow-left'\n | 'arrow-up'\n | 'arrow-down'\n | 'double-arrow'\n | 'callout'\n | 'cylinder'\n | 'cloud'\n | 'heart'\n | 'lightning';\n\n/**\n * Author-facing shape annotation names accepted on a drawing's children\n * (the `{[…]}` token). Shared with validation so an unknown shape gets a\n * \"did you mean\" suggestion. Aliases (`rectangle`→rect, `ellipse`→circle)\n * are included.\n */\nexport const SHAPE_NAMES = [\n 'rectangle',\n 'rect',\n 'square',\n 'circle',\n 'ellipse',\n 'oval',\n 'line',\n 'arrow',\n 'path',\n 'text',\n 'triangle',\n 'right-triangle',\n 'diamond',\n 'rhombus',\n 'pentagon',\n 'hexagon',\n 'octagon',\n 'star',\n 'star4',\n 'star6',\n 'parallelogram',\n 'trapezoid',\n 'plus',\n 'cross',\n 'chevron',\n 'arrow-right',\n 'arrow-left',\n 'arrow-up',\n 'arrow-down',\n 'double-arrow',\n 'callout',\n 'speech',\n 'cylinder',\n 'cloud',\n 'heart',\n 'lightning',\n 'bolt',\n] as const;\n\nconst SHAPE_ALIASES: Readonly<Record<string, DrawingShapeKind>> = {\n rectangle: 'rect',\n rect: 'rect',\n square: 'rect',\n circle: 'circle',\n ellipse: 'circle',\n oval: 'circle',\n line: 'line',\n arrow: 'arrow',\n path: 'path',\n text: 'text',\n label: 'text',\n triangle: 'triangle',\n 'right-triangle': 'right-triangle',\n righttriangle: 'right-triangle',\n diamond: 'diamond',\n rhombus: 'diamond',\n pentagon: 'pentagon',\n hexagon: 'hexagon',\n octagon: 'octagon',\n star: 'star',\n star5: 'star',\n star4: 'star4',\n star6: 'star6',\n parallelogram: 'parallelogram',\n trapezoid: 'trapezoid',\n plus: 'plus',\n cross: 'plus',\n chevron: 'chevron',\n 'arrow-right': 'arrow-right',\n arrowright: 'arrow-right',\n rightarrow: 'arrow-right',\n 'arrow-left': 'arrow-left',\n arrowleft: 'arrow-left',\n leftarrow: 'arrow-left',\n 'arrow-up': 'arrow-up',\n arrowup: 'arrow-up',\n uparrow: 'arrow-up',\n 'arrow-down': 'arrow-down',\n arrowdown: 'arrow-down',\n downarrow: 'arrow-down',\n 'double-arrow': 'double-arrow',\n doublearrow: 'double-arrow',\n callout: 'callout',\n speech: 'callout',\n bubble: 'callout',\n cylinder: 'cylinder',\n can: 'cylinder',\n cloud: 'cloud',\n heart: 'heart',\n lightning: 'lightning',\n bolt: 'lightning',\n};\n\n/**\n * Resolve a child's `{[…]}` annotation name to a shape kind, or `null`\n * when it isn't a shape. Case-insensitive.\n */\nexport function normalizeShapeKind(name: string | undefined): DrawingShapeKind | null {\n if (!name) return null;\n return SHAPE_ALIASES[name.toLowerCase()] ?? null;\n}\n\n/** True when `name` is an accepted shape annotation (any alias). */\nexport function isShapeName(name: string | undefined): boolean {\n return normalizeShapeKind(name) != null;\n}\n\n/** A positioned shape in author-defined units (top-left origin). */\nexport interface DrawingShape {\n id: string;\n kind: DrawingShapeKind;\n x: number;\n y: number;\n width: number;\n height: number;\n /** True when position came from `x=`/`y=`; false when auto-laid out. */\n pinned: boolean;\n /** Heading text used as the shape's label ('' when none). */\n label: string;\n /** Body text under the heading, used as a sublabel (when present). */\n sublabel?: string;\n /** Text content for `text` shapes (`text=` param, else label, else body). */\n text?: string;\n /** Raw SVG path `d` for `path` shapes. */\n d?: string;\n fill?: string;\n stroke?: string;\n strokeWidth?: number;\n borderRadius?: number;\n dasharray?: string;\n}\n\n/** A connector joining two shapes by id. */\nexport interface DrawingConnector {\n id: string;\n kind: 'line' | 'arrow';\n /** Source shape id (resolved to an existing shape). */\n from: string;\n /** Target shape id (resolved to an existing shape). */\n to: string;\n /** Optional midpoint label (heading text, or `connectsTo` type). */\n label?: string;\n stroke?: string;\n strokeWidth?: number;\n dasharray?: string;\n /** End-of-line markers (default: `arrow` kind → filled arrow at `to`). */\n startMarker: MarkerStyle;\n endMarker: MarkerStyle;\n /** How the line is routed between the two shapes. Default: 'straight'. */\n routing: ConnectorRouting;\n}\n\nexport interface DrawingLayout {\n shapes: DrawingShape[];\n connectors: DrawingConnector[];\n warnings: string[];\n}\n\nexport interface DrawingLayoutOptions {\n /** Horizontal spacing between auto-laid-out shapes. Default: 180. */\n gapX?: number;\n /** Vertical spacing between auto-laid-out shapes. Default: 140. */\n gapY?: number;\n /** Padding below the explicit bounding box before the grid starts. Default: 80. */\n gridPad?: number;\n /** Grid origin when no explicit shapes anchor it. Default: 80, 80. */\n gridOriginX?: number;\n gridOriginY?: number;\n}\n\nconst DEFAULT_OPTS: Required<DrawingLayoutOptions> = {\n gapX: 180,\n gapY: 140,\n gridPad: 80,\n gridOriginX: 80,\n gridOriginY: 80,\n};\n\n/** Default size (author units) per shape kind when width/height are omitted. */\nconst DEFAULT_SIZE: Partial<Record<DrawingShapeKind, { w: number; h: number }>> = {\n rect: { w: 160, h: 90 },\n circle: { w: 120, h: 120 },\n line: { w: 160, h: 0 },\n arrow: { w: 160, h: 0 },\n path: { w: 160, h: 120 },\n text: { w: 200, h: 48 },\n};\nconst FALLBACK_SIZE = { w: 120, h: 120 };\n\nfunction defaultSize(kind: DrawingShapeKind): { w: number; h: number } {\n return DEFAULT_SIZE[kind] ?? FALLBACK_SIZE;\n}\n\n/**\n * Compute shapes + connectors for a drawing from its parent block's\n * children. Pure — same inputs always produce the same output.\n */\nexport function computeDrawingLayout(\n children: readonly Block[],\n options: DrawingLayoutOptions = {},\n): DrawingLayout {\n const opts = { ...DEFAULT_OPTS, ...options };\n const warnings: string[] = [];\n\n // First pass: turn each child into a shape descriptor (or a pending\n // connector). Track which shapes still need an auto-grid slot.\n interface Entry {\n shape: DrawingShape;\n hasX: boolean;\n hasY: boolean;\n }\n const entries: Entry[] = [];\n const unpositioned: number[] = [];\n\n // Connectors are resolved after all shape ids are known.\n interface PendingConnector {\n id: string;\n kind: 'line' | 'arrow';\n from: string;\n to: string;\n label?: string;\n stroke?: string;\n strokeWidth?: number;\n dasharray?: string;\n startMarker: MarkerStyle;\n endMarker: MarkerStyle;\n routing: ConnectorRouting;\n source: string; // child id, for warning messages\n }\n const pendingConnectors: PendingConnector[] = [];\n\n for (const child of children) {\n const kind = normalizeShapeKind(child.template);\n const params = child.templateOverrides ?? {};\n const label = child.title ?? '';\n const body = bodyText(child);\n\n if (!kind) {\n warnings.push(\n `Child \"${child.id}\" is not a shape (annotation \"${child.template ?? '—'}\") and was skipped`,\n );\n continue;\n }\n\n // line/arrow with from/to (or any shape's connectsTo) → connector.\n const from = strParam(params, 'from');\n const to = strParam(params, 'to');\n if ((kind === 'line' || kind === 'arrow') && (from || to)) {\n pendingConnectors.push({\n id: child.id,\n kind,\n from: from ?? '',\n to: to ?? '',\n ...(label ? { label } : {}),\n ...connectorSemantics(params, kind),\n source: child.id,\n });\n continue;\n }\n\n // Diagram-compatible: a shape's `connectsTo` also emits arrow connectors.\n for (const conn of child.connectsTo ?? []) {\n pendingConnectors.push({\n id: `${child.id}->${conn.target}${conn.type ? `:${conn.type}` : ''}`,\n kind: 'arrow',\n from: child.id,\n to: conn.target,\n ...(conn.type ? { label: conn.type } : {}),\n ...connectorSemantics(params, 'arrow'),\n source: child.id,\n });\n }\n\n const size = defaultSize(kind);\n // Prefer annotation params; fall back to Block.x/Block.y so children\n // authored the diagram way (coordinates on the block itself) place\n // identically here instead of silently dropping into the auto-grid.\n const x = numParam(params, 'x') ?? (typeof child.x === 'number' ? child.x : undefined);\n const y = numParam(params, 'y') ?? (typeof child.y === 'number' ? child.y : undefined);\n const shape: DrawingShape = {\n id: child.id,\n kind,\n x: x ?? 0,\n y: y ?? 0,\n width: numParam(params, 'width') ?? numParam(params, 'w') ?? size.w,\n height: numParam(params, 'height') ?? numParam(params, 'h') ?? size.h,\n pinned: x != null && y != null,\n label,\n ...(body ? { sublabel: body } : {}),\n ...styleParams(params),\n };\n if (kind === 'path') {\n const d = strParam(params, 'd');\n if (d) shape.d = d;\n }\n if (kind === 'text') {\n shape.text = strParam(params, 'text') ?? label ?? body ?? '';\n }\n if (numParam(params, 'borderRadius') != null) {\n shape.borderRadius = numParam(params, 'borderRadius');\n }\n\n entries.push({ shape, hasX: x != null, hasY: y != null });\n if (x == null || y == null) unpositioned.push(entries.length - 1);\n }\n\n // Second pass: auto-place shapes missing a coordinate in a grid below\n // the pinned bounding box (mirrors diagramLayout). Grid pitch accounts\n // for the largest unpositioned shape so wide/tall shapes never overlap\n // their grid neighbors.\n if (unpositioned.length > 0) {\n const cols = Math.max(1, Math.ceil(Math.sqrt(unpositioned.length)));\n const maxW = Math.max(...unpositioned.map((i) => entries[i].shape.width));\n const maxH = Math.max(...unpositioned.map((i) => entries[i].shape.height));\n const pitchX = Math.max(opts.gapX, maxW + 60);\n const pitchY = Math.max(opts.gapY, maxH + 50);\n let baseX = opts.gridOriginX;\n let baseY = opts.gridOriginY;\n const pinned = entries.filter((e) => e.shape.pinned);\n if (pinned.length > 0) {\n baseX = Math.min(...pinned.map((e) => e.shape.x));\n baseY = Math.max(...pinned.map((e) => e.shape.y + e.shape.height)) + opts.gridPad;\n }\n unpositioned.forEach((entryIdx, k) => {\n const col = k % cols;\n const row = Math.floor(k / cols);\n entries[entryIdx].shape.x = baseX + col * pitchX;\n entries[entryIdx].shape.y = baseY + row * pitchY;\n });\n }\n\n const shapes = entries.map((e) => e.shape);\n const shapeIds = new Set(shapes.map((s) => s.id));\n\n // Third pass: resolve connectors against known shape ids, dropping ghosts.\n const connectors: DrawingConnector[] = [];\n const seen = new Set<string>();\n for (const c of pendingConnectors) {\n if (!shapeIds.has(c.from) || !shapeIds.has(c.to)) {\n const missing = !shapeIds.has(c.from) ? c.from : c.to;\n warnings.push(\n `Connector \"${c.source}\" dropped: ${missing ? `no shape with id \"${missing}\"` : 'missing from/to'}`,\n );\n continue;\n }\n const key = `${c.kind}:${c.from}->${c.to}:${c.label ?? ''}`;\n if (seen.has(key)) continue;\n seen.add(key);\n const { source: _source, ...rest } = c;\n connectors.push(rest);\n }\n\n return { shapes, connectors, warnings };\n}\n\n// ============================================\n// Param coercion\n// ============================================\n\n/** Read a raw string param (trims a trailing comma — `x=21, y=25` is tolerated). */\nfunction strParam(params: Record<string, string>, key: string): string | undefined {\n const raw = params[key];\n if (raw == null) return undefined;\n const v = raw.replace(/,\\s*$/, '').trim();\n return v.length > 0 ? v : undefined;\n}\n\n/** Coerce a numeric param, tolerating a trailing comma. Returns undefined on failure. */\nfunction numParam(params: Record<string, string>, key: string): number | undefined {\n const v = strParam(params, key);\n if (v == null) return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/** Stroke style pass-throughs shared by shapes and connectors. */\nfunction connectorStyle(params: Record<string, string>): {\n stroke?: string;\n strokeWidth?: number;\n dasharray?: string;\n} {\n const out: { stroke?: string; strokeWidth?: number; dasharray?: string } = {};\n const stroke = strParam(params, 'stroke');\n const strokeWidth = numParam(params, 'strokeWidth');\n const dasharray = strParam(params, 'dasharray');\n if (stroke) out.stroke = stroke;\n if (strokeWidth != null) out.strokeWidth = strokeWidth;\n if (dasharray) out.dasharray = dasharray;\n return out;\n}\n\n/** Style pass-throughs for a fillable shape — connector stroke set plus `fill`. */\nfunction styleParams(params: Record<string, string>): {\n fill?: string;\n stroke?: string;\n strokeWidth?: number;\n dasharray?: string;\n} {\n const fill = strParam(params, 'fill');\n return { ...connectorStyle(params), ...(fill ? { fill } : {}) };\n}\n\nconst MARKER_ALIASES: Readonly<Record<string, MarkerStyle>> = {\n none: 'none',\n arrow: 'arrow',\n triangle: 'arrow',\n filled: 'arrow',\n open: 'open',\n v: 'open',\n diamond: 'diamond',\n circle: 'circle',\n dot: 'circle',\n square: 'square',\n box: 'square',\n};\n\nconst ROUTING_ALIASES: Readonly<Record<string, ConnectorRouting>> = {\n straight: 'straight',\n direct: 'straight',\n orthogonal: 'orthogonal',\n elbow: 'orthogonal',\n step: 'orthogonal',\n curved: 'curved',\n curve: 'curved',\n bezier: 'curved',\n};\n\nfunction parseMarker(raw: string | undefined): MarkerStyle | undefined {\n return raw ? MARKER_ALIASES[raw.toLowerCase()] : undefined;\n}\n\nfunction parseRouting(raw: string | undefined): ConnectorRouting | undefined {\n return raw ? ROUTING_ALIASES[raw.toLowerCase()] : undefined;\n}\n\n/**\n * Connector-only semantics: stroke style + end markers + line style + routing.\n * The default end marker is a filled `arrow` for `arrow` connectors, `none`\n * for plain `line`s; `endStyle`/`startStyle` override. `lineStyle` fills in a\n * dasharray when no explicit `dasharray` is given.\n */\nfunction connectorSemantics(\n params: Record<string, string>,\n kind: 'line' | 'arrow',\n): {\n stroke?: string;\n strokeWidth?: number;\n dasharray?: string;\n startMarker: MarkerStyle;\n endMarker: MarkerStyle;\n routing: ConnectorRouting;\n} {\n const out = connectorStyle(params);\n const dasharray = out.dasharray ?? lineStyleDasharray(strParam(params, 'lineStyle'));\n return {\n ...out,\n ...(dasharray ? { dasharray } : {}),\n startMarker: parseMarker(strParam(params, 'startStyle')) ?? 'none',\n endMarker: parseMarker(strParam(params, 'endStyle')) ?? (kind === 'arrow' ? 'arrow' : 'none'),\n routing: parseRouting(strParam(params, 'routing')) ?? 'straight',\n };\n}\n\n/** Plain text of a block's body content (sublabel source). */\nfunction bodyText(block: Block): string {\n if (!block.contents || block.contents.length === 0) return '';\n return block.contents\n .map((node) => extractPlainText(node))\n .join(' ')\n .trim();\n}\n","/**\n * Drawing block template.\n *\n * Renders the parent block's children as free-form shapes on a canvas:\n * - Each child becomes a shape (`rect`/`circle` → ShapeLayer, `line`/\n * `arrow`/`path` → PathLayer, `text` → TextLayer) with its title as a\n * centered label and its body text as an optional sublabel.\n * - `line`/`arrow` children with `from`/`to` (and any child's\n * `connectsTo`) become connectors clipped to the shapes they join.\n *\n * Coordinates from `x`/`y`/`width`/`height` are author-defined units; the\n * template fits the bounding box of all shapes to the viewport with\n * padding (same approach as `diagramBlock`), so authors don't think in\n * absolute pixels.\n */\n\nimport type { Layer, ShapeLayer, TextLayer, PathLayer } from '../../schemas/Doc.js';\nimport type { DrawingBlockInput, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme, getThemeFont, themedFontSize } from '../utils/themeUtils.js';\nimport { computeDrawingLayout, type DrawingShape, type DrawingConnector } from './drawingLayout.js';\nimport { shapePath, connectorPath, clipEndpoints } from '../utils/shapeGeometry.js';\n\nconst PADDING = 80;\n\nexport function drawingBlock(input: DrawingBlockInput, context: TemplateContext): Layer[] {\n const { theme, viewport, children = [] } = context;\n const colors = resolveColorScheme(context, input.colorScheme);\n const defaultStroke = input.stroke ?? colors.text ?? theme.colors.text;\n const defaultFill = input.fill ?? 'none';\n\n const layout = computeDrawingLayout(children);\n\n // No shapes authored as children → empty drawing hint.\n if (layout.shapes.length === 0 && layout.connectors.length === 0) {\n return [emptyHint(input, context)];\n }\n\n // Bounding box of all shapes in author coordinates.\n const minX = Math.min(...layout.shapes.map((s) => s.x));\n const maxX = Math.max(...layout.shapes.map((s) => s.x + s.width));\n const minY = Math.min(...layout.shapes.map((s) => s.y));\n const maxY = Math.max(...layout.shapes.map((s) => s.y + s.height));\n\n const titleHeight = input.title ? 120 : 0;\n const availW = Math.max(1, viewport.width - PADDING * 2);\n const availH = Math.max(1, viewport.height - PADDING * 2 - titleHeight);\n const contentW = Math.max(1, maxX - minX);\n const contentH = Math.max(1, maxY - minY);\n\n // Uniform scale that fits both dimensions. Small drawings may grow\n // (shapes are vector) up to 1.8× so a three-shape sketch doesn't render\n // as a tiny off-center cluster; the title and the scaled drawing are\n // centered together as one group.\n const scale = Math.min(availW / contentW, availH / contentH, 1.8);\n const scaledW = contentW * scale;\n const scaledH = contentH * scale;\n const groupTop = Math.max(PADDING / 2, (viewport.height - titleHeight - scaledH) / 2);\n const offsetX = PADDING + (availW - scaledW) / 2;\n const offsetY = groupTop + titleHeight;\n\n const tx = (x: number) => offsetX + (x - minX) * scale;\n const ty = (y: number) => offsetY + (y - minY) * scale;\n const fontAdj = Math.min(Math.max(scale, 1), 1.5);\n\n const layers: Layer[] = [];\n\n if (input.title) {\n layers.push({\n type: 'text',\n id: 'drawing-title',\n content: {\n text: input.title,\n style: {\n fontSize: themedFontSize(40, context, true),\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: groupTop + titleHeight / 2 - 16, anchor: 'center' },\n });\n }\n\n // Center lookup so connectors can clip to the shapes they join.\n const boxes = new Map<string, { cx: number; cy: number; rx: number; ry: number }>();\n for (const s of layout.shapes) {\n const x = tx(s.x);\n const y = ty(s.y);\n const w = s.width * scale;\n const h = s.height * scale;\n boxes.set(s.id, {\n cx: x + w / 2,\n cy: y + h / 2,\n rx: Math.max(1, w / 2),\n ry: Math.max(1, h / 2),\n });\n }\n\n // Connectors first so they sit behind shapes.\n for (const conn of layout.connectors) {\n const a = boxes.get(conn.from);\n const b = boxes.get(conn.to);\n if (!a || !b) continue;\n const { start, end } = clipEndpoints(a, b);\n const path: PathLayer = {\n type: 'path',\n id: `connector-${conn.id}`,\n content: {\n d: connectorPath(conn.routing, start, end),\n stroke: conn.stroke ?? defaultStroke,\n strokeWidth: conn.strokeWidth ?? Math.round(2 * fontAdj),\n fill: 'none',\n ...(conn.dasharray ? { dasharray: conn.dasharray } : {}),\n ...(conn.startMarker !== 'none' ? { startMarker: conn.startMarker } : {}),\n ...(conn.endMarker !== 'none' ? { endMarker: conn.endMarker } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n layers.push(path);\n if (conn.label) layers.push(connectorLabel(conn, a, b, context, fontAdj));\n }\n\n // Shapes (and their labels) on top.\n for (const s of layout.shapes) {\n const x = tx(s.x);\n const y = ty(s.y);\n const w = s.width * scale;\n const h = s.height * scale;\n layers.push(...shapeLayers(s, x, y, w, h, context, defaultStroke, defaultFill, fontAdj));\n }\n\n return layers;\n}\n\n// ============================================\n// Per-shape layer emission\n// ============================================\n\nfunction shapeLayers(\n s: DrawingShape,\n x: number,\n y: number,\n w: number,\n h: number,\n context: TemplateContext,\n defaultStroke: string,\n defaultFill: string,\n fontAdj: number,\n): Layer[] {\n const out: Layer[] = [];\n const stroke = s.stroke ?? defaultStroke;\n const fill = s.fill ?? defaultFill;\n\n if (s.kind === 'text') {\n out.push(\n textLayer(\n `shape-${s.id}`,\n s.text ?? s.label ?? '',\n x + w / 2,\n y + h / 2,\n w,\n context,\n s.stroke ?? context.theme.colors.text,\n true,\n fontAdj,\n ),\n );\n return out;\n }\n\n if (s.kind === 'rect' || s.kind === 'circle') {\n const shape: ShapeLayer = {\n type: 'shape',\n id: `shape-${s.id}`,\n content: {\n shape: s.kind,\n fill,\n stroke,\n strokeWidth: s.strokeWidth ?? Math.round(2 * fontAdj),\n ...(s.kind === 'rect' && s.borderRadius != null ? { borderRadius: s.borderRadius } : {}),\n },\n position: { x, y, width: w, height: h },\n };\n out.push(shape);\n } else {\n // line / arrow / path / polygon shapes → PathLayer.\n const diagonal = `M ${round(x)} ${round(y)} L ${round(x + w)} ${round(y + h)}`;\n let d = diagonal;\n let pathFill = 'none';\n let endMarker: 'none' | 'arrow' = 'none';\n if (s.kind === 'path') {\n d = s.d ?? diagonal;\n pathFill = fill;\n } else if (s.kind === 'arrow') {\n endMarker = 'arrow';\n } else if (s.kind !== 'line') {\n // Named polygon / curved shape → computed filled path.\n d = shapePath(s.kind, x, y, w, h) ?? diagonal;\n pathFill = fill;\n }\n const path: PathLayer = {\n type: 'path',\n id: `shape-${s.id}`,\n content: {\n d,\n stroke,\n strokeWidth: s.strokeWidth ?? Math.round(2 * fontAdj),\n fill: pathFill,\n ...(s.dasharray ? { dasharray: s.dasharray } : {}),\n ...(endMarker !== 'none' ? { endMarker } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n };\n out.push(path);\n }\n\n // Label (heading text) centered on the shape; sublabel (body) below it.\n if (s.label) {\n out.push(\n textLayer(\n `shape-label-${s.id}`,\n s.label,\n x + w / 2,\n y + h / 2,\n w,\n context,\n s.stroke ?? context.theme.colors.text,\n true,\n fontAdj,\n ),\n );\n }\n if (s.sublabel) {\n out.push(\n textLayer(\n `shape-sublabel-${s.id}`,\n s.sublabel,\n x + w / 2,\n y + h + 14 * fontAdj,\n w,\n context,\n context.theme.colors.textMuted,\n false,\n fontAdj,\n ),\n );\n }\n return out;\n}\n\nfunction textLayer(\n id: string,\n text: string,\n cx: number,\n cy: number,\n width: number,\n context: TemplateContext,\n color: string,\n bold: boolean,\n fontAdj: number,\n): TextLayer {\n return {\n type: 'text',\n id,\n content: {\n text,\n style: {\n fontSize: themedFontSize(Math.round((bold ? 22 : 18) * fontAdj), context, false),\n fontFamily: getThemeFont(context, 'body'),\n ...(bold ? { fontWeight: 'bold' as const } : {}),\n color,\n textAlign: 'center',\n },\n },\n position: { x: cx, y: cy, anchor: 'center', width },\n };\n}\n\nfunction connectorLabel(\n conn: DrawingConnector,\n a: { cx: number; cy: number },\n b: { cx: number; cy: number },\n context: TemplateContext,\n fontAdj: number,\n): TextLayer {\n const fontSize = themedFontSize(Math.round(18 * fontAdj), context, false);\n return {\n type: 'text',\n id: `connector-label-${conn.id}`,\n content: {\n text: conn.label ?? '',\n style: {\n fontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: context.theme.colors.textMuted,\n textAlign: 'center',\n },\n },\n // Floated just above the connector midpoint so the line never\n // strikes through the label text.\n position: { x: (a.cx + b.cx) / 2, y: (a.cy + b.cy) / 2 - fontSize * 0.9, anchor: 'center' },\n };\n}\n\nfunction emptyHint(input: DrawingBlockInput, context: TemplateContext): TextLayer {\n return {\n type: 'text',\n id: 'drawing-empty',\n content: {\n text: input.title ?? 'Empty drawing',\n style: {\n fontSize: themedFontSize(36, context, true),\n fontFamily: getThemeFont(context, 'title'),\n color: context.theme.colors.textMuted,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: '50%', anchor: 'center' },\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * Layout layout — pure derivation of positioned layers from a `layout`\n * block's child sub-blocks. The layout counterpart to `drawingLayout.ts`,\n * with two deliberate differences:\n *\n * - **Absolute** positioning. Each child's `x`/`y`/`width`/`height` are\n * used as-is in viewport units (no auto-grid, no fit-to-viewport\n * scaling). A layout is a free-form canvas; authors place things where\n * they want them.\n * - **Text content lives in the child body.** A `{[text …]}` child's\n * paragraphs/lists/marks under its heading become the text — readable\n * markdown rather than an encoded blob. (`content.html` is filled by the\n * editor for rich on-canvas rendering; SSR uses the plain `content.text`.)\n *\n * Each child heading is one layer: `### {#id} {[<type> x=.. y=.. …]}`.\n * Children are emitted in document order, so the first child is the\n * back-most layer (painters' order).\n */\n\nimport type { Layer, ShapeLayer, TextLayer, PathLayer, ImageLayer } from '../../schemas/Doc.js';\nimport { extractPlainText } from '../../markdown/utils.js';\nimport { normalizeShapeKind } from './drawingLayout.js';\nimport { shapePath } from '../utils/shapeGeometry.js';\n\nexport interface LayoutLayersResult {\n layers: Layer[];\n warnings: string[];\n}\n\nexport interface LayoutLayerDefaults {\n /**\n * Default ink — text color and path/line stroke — used when the author\n * omits `color=`/`stroke=`. `layoutBlock` passes the theme text color so\n * un-styled text stays legible on dark themes; the editor's scene canvas\n * omits it and gets the dark-slate default that suits its fixed light\n * surface.\n */\n ink?: string;\n}\n\n/** Fallback ink for callers that don't supply defaults (editor scene canvas). */\nconst DEFAULT_INK = '#1e293b';\n\n/** Minimal Block shape this derivation reads (subset of `Block`). */\ninterface LayoutChildBlock {\n id: string;\n template?: string;\n templateOverrides?: Record<string, string>;\n title?: string;\n contents?: import('../../markdown/types.js').MarkdownNode[];\n}\n\nconst DEFAULT_TEXT = { w: 300, h: 80 };\nconst DEFAULT_SHAPE = { w: 160, h: 90 };\nconst DEFAULT_IMAGE = { w: 320, h: 240 };\n\n/**\n * Map a layout block's children → positioned layers. Pure: same children\n * always produce the same layers.\n */\nexport function computeLayoutLayers(\n children: readonly LayoutChildBlock[],\n _viewport: { width: number; height: number },\n defaults: LayoutLayerDefaults = {},\n): LayoutLayersResult {\n const ink = defaults.ink ?? DEFAULT_INK;\n const layers: Layer[] = [];\n const warnings: string[] = [];\n\n for (const child of children) {\n const params = child.templateOverrides ?? {};\n const annotation = (child.template ?? '').toLowerCase();\n const x = numParam(params, 'x') ?? 0;\n const y = numParam(params, 'y') ?? 0;\n\n if (annotation === 'image' || annotation === 'img') {\n const src = strParam(params, 'src');\n if (!src) {\n warnings.push(`Layout image \"${child.id}\" has no src= and was skipped`);\n continue;\n }\n const layer: ImageLayer = {\n id: child.id,\n type: 'image',\n position: {\n x,\n y,\n width: numParam(params, 'width') ?? DEFAULT_IMAGE.w,\n height: numParam(params, 'height') ?? DEFAULT_IMAGE.h,\n },\n content: {\n src,\n alt: strParam(params, 'alt') ?? '',\n ...(strParam(params, 'fit')\n ? { fit: strParam(params, 'fit') as 'cover' | 'contain' | 'fill' }\n : {}),\n },\n };\n layers.push(layer);\n continue;\n }\n\n const kind = normalizeShapeKind(child.template);\n if (!kind) {\n warnings.push(\n `Layout child \"${child.id}\" is not a layer (annotation \"${child.template ?? '—'}\") and was skipped`,\n );\n continue;\n }\n\n if (kind === 'text') {\n const text = bodyText(child) || child.title || '';\n const layer: TextLayer = {\n id: child.id,\n type: 'text',\n position: {\n x,\n y,\n width: numParam(params, 'width') ?? DEFAULT_TEXT.w,\n height: numParam(params, 'height') ?? DEFAULT_TEXT.h,\n },\n content: { text, style: textStyleFromParams(params, ink) },\n };\n layers.push(layer);\n continue;\n }\n\n const width = numParam(params, 'width') ?? numParam(params, 'w') ?? DEFAULT_SHAPE.w;\n const height = numParam(params, 'height') ?? numParam(params, 'h') ?? DEFAULT_SHAPE.h;\n\n if (kind === 'rect' || kind === 'circle') {\n const shape: ShapeLayer = {\n id: child.id,\n type: 'shape',\n position: { x, y, width, height },\n content: {\n shape: kind,\n ...(strParam(params, 'fill') ? { fill: strParam(params, 'fill') } : {}),\n ...(strParam(params, 'stroke') ? { stroke: strParam(params, 'stroke') } : {}),\n ...(numParam(params, 'strokeWidth') != null\n ? { strokeWidth: numParam(params, 'strokeWidth') }\n : {}),\n ...(strParam(params, 'borderStyle')\n ? { borderStyle: strParam(params, 'borderStyle') as 'solid' | 'dashed' | 'dotted' }\n : {}),\n ...(kind === 'rect' && numParam(params, 'borderRadius') != null\n ? { borderRadius: numParam(params, 'borderRadius') }\n : {}),\n },\n };\n layers.push(shape);\n continue;\n }\n\n // line / arrow / path / named polygon → PathLayer (absolute geometry).\n const diagonal = `M ${round(x)} ${round(y)} L ${round(x + width)} ${round(y + height)}`;\n let d = diagonal;\n let pathFill = 'none';\n let endMarker: 'none' | 'arrow' = 'none';\n if (kind === 'path') {\n d = strParam(params, 'd') ?? diagonal;\n pathFill = strParam(params, 'fill') ?? 'none';\n } else if (kind === 'arrow') {\n endMarker = 'arrow';\n } else if (kind !== 'line') {\n d = shapePath(kind, x, y, width, height) ?? diagonal;\n pathFill = strParam(params, 'fill') ?? 'none';\n }\n const path: PathLayer = {\n id: child.id,\n type: 'path',\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n content: {\n d,\n stroke: strParam(params, 'stroke') ?? ink,\n strokeWidth: numParam(params, 'strokeWidth') ?? 2,\n fill: pathFill,\n ...(strParam(params, 'dasharray') ? { dasharray: strParam(params, 'dasharray') } : {}),\n ...(endMarker !== 'none' ? { endMarker } : {}),\n },\n };\n layers.push(path);\n }\n\n return { layers, warnings };\n}\n\n/** Build a TextStyle from a text layer's annotation params. */\nfunction textStyleFromParams(\n params: Record<string, string>,\n ink: string,\n): TextLayer['content']['style'] {\n const style: TextLayer['content']['style'] = {\n fontSize: numParam(params, 'fontSize') ?? 32,\n color: strParam(params, 'color') ?? ink,\n };\n const fontFamily = strParam(params, 'fontFamily');\n if (fontFamily) style.fontFamily = fontFamily;\n const fontWeight = strParam(params, 'fontWeight');\n if (fontWeight === 'bold' || fontWeight === 'normal') style.fontWeight = fontWeight;\n const fontStyle = strParam(params, 'fontStyle');\n if (fontStyle === 'italic' || fontStyle === 'normal') style.fontStyle = fontStyle;\n const align = strParam(params, 'align');\n if (align === 'left' || align === 'center' || align === 'right') style.textAlign = align;\n const valign = strParam(params, 'valign');\n if (valign === 'top' || valign === 'middle' || valign === 'bottom') style.verticalAlign = valign;\n const lineHeight = numParam(params, 'lineHeight');\n if (lineHeight != null) style.lineHeight = lineHeight;\n const padding = numParam(params, 'padding');\n if (padding != null) style.padding = padding;\n const background = strParam(params, 'background');\n if (background) style.background = background;\n return style;\n}\n\n/** Read a raw string param (trims a trailing comma — `x=21, y=25` tolerated). */\nfunction strParam(params: Record<string, string>, key: string): string | undefined {\n const raw = params[key];\n if (raw == null) return undefined;\n const v = raw.replace(/,\\s*$/, '').trim();\n return v.length > 0 ? v : undefined;\n}\n\n/** Coerce a numeric param, tolerating a trailing comma. Undefined on failure. */\nfunction numParam(params: Record<string, string>, key: string): number | undefined {\n const v = strParam(params, key);\n if (v == null) return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/** Plain text of a block's body content. */\nfunction bodyText(block: LayoutChildBlock): string {\n if (!block.contents || block.contents.length === 0) return '';\n return block.contents\n .map((node) => extractPlainText(node))\n .join('\\n')\n .trim();\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * Layout block template.\n *\n * Renders the parent block's children as free-form, **absolutely\n * positioned** layers on a canvas (text boxes, shapes, images). Unlike\n * `drawingBlock`, coordinates are used as-is — there is no fit-to-viewport\n * scaling — because a layout is a fixed-canvas composition.\n *\n * Each child is one layer: `### {#id} {[<type> x=.. y=.. …]}`. A text box's\n * content lives in its child body as markdown (see `computeLayoutLayers`).\n * Children render in document order (first child = back-most layer).\n */\n\nimport type { Layer, TextLayer } from '../../schemas/Doc.js';\nimport type { TemplateBlock, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { getThemeFont } from '../utils/themeUtils.js';\nimport { computeLayoutLayers } from './layoutLayout.js';\n\nexport function layoutBlock(input: TemplateBlock, context: TemplateContext): Layer[] {\n const { theme, viewport, children = [] } = context;\n // Theme ink for un-styled text and strokes — the fixed dark-slate\n // default disappeared on dark themes.\n const { layers } = computeLayoutLayers(children, viewport, { ink: theme.colors.text });\n return layers.length > 0 ? layers : [emptyHint(input, context)];\n}\n\nfunction emptyHint(input: TemplateBlock, context: TemplateContext): TextLayer {\n return {\n type: 'text',\n id: 'layout-empty',\n content: {\n text: (input as { title?: string }).title ?? 'Empty layout',\n style: {\n fontSize: scaledFontSize(36, context, true),\n fontFamily: getThemeFont(context, 'title'),\n color: context.theme.colors.textMuted,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: '50%', anchor: 'center' },\n };\n}\n","/**\n * Cover Block Template\n *\n * Full-screen hero image with title overlay, shown before playback starts.\n * This is the \"poster for the doc, displaying the\n * article's hero image with elegant title treatment.\n *\n * Features:\n * - Full-screen hero image with Ken Burns ambient motion\n * - Gradient overlay for text readability\n * - Large centered title with optional subtitle\n * - No animation delays (shown at rest, not during playback)\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { TemplateContext } from '../../schemas/BlockTemplates.js';\nimport type { StartBlockConfig } from '../../schemas/Doc.js';\nimport { getThemeFont, themedFontSize, themedImageTreatment } from '../utils/themeUtils.js';\nimport { mapAmbientMotion } from './accentImage.js';\n\n/**\n * Input for coverBlock template - matches StartBlockConfig\n */\nexport interface CoverBlockInput {\n /** Path to hero image (omit for theme-driven background) */\n heroSrc?: string;\n /** Alt text for the hero image */\n heroAlt?: string;\n /** Title to display over the hero */\n title: string;\n /** Optional subtitle */\n subtitle?: string;\n /** Ambient motion for the hero image */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n /** Photo credit / artist name */\n heroCredit?: string;\n /** License identifier */\n heroLicense?: string;\n /** Per-block override for the theme's photographic image grade. */\n imageTreatment?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool';\n}\n\n/** Base (pre-viewport-scale) font size for a full-size cover title. */\nconst COVER_TITLE_BASE_PX = 120;\n/** Title length (chars) that still reads well at the full base size. */\nconst COVER_TITLE_COMFORTABLE_CHARS = 26;\n/** Readability floor so very long titles never shrink to nothing. */\nconst COVER_TITLE_MIN_BASE_PX = 62;\n\n/**\n * Compute the base cover-title font size for a given title, shrinking long\n * titles so their wrapped block fits the frame. Returns the pre-viewport-scale\n * value expected by {@link themedFontSize}; short titles get the full base.\n */\nexport function fitCoverTitleSize(title: string): number {\n const len = title.trim().length;\n if (len <= COVER_TITLE_COMFORTABLE_CHARS) return COVER_TITLE_BASE_PX;\n const scaled = COVER_TITLE_BASE_PX * Math.sqrt(COVER_TITLE_COMFORTABLE_CHARS / len);\n return Math.max(COVER_TITLE_MIN_BASE_PX, Math.round(scaled));\n}\n\n/**\n * Generate cover block layers from StartBlockConfig.\n */\nexport function coverBlock(input: CoverBlockInput, context: TemplateContext): Layer[] {\n const treatment = themedImageTreatment(context, input.imageTreatment);\n const { heroSrc, heroAlt, title, subtitle, ambientMotion, heroCredit, heroLicense } = input;\n const { theme, layout } = context;\n\n // Scale font sizes for viewport - cover titles are larger than regular title blocks.\n // The cover title wraps within maxTextWidth but does not otherwise shrink, so a long\n // title (e.g. \"Seattle: The Emerald City That Rebuilt Itself on Top of Itself\") wrapped\n // to three 120px lines overflows the frame and is clipped by the player controls.\n // Auto-fit the base size to title length so long titles stay within the frame. The\n // wrapped title's total height grows with fontSize² × length (line count ∝ length ×\n // fontSize / width, line height ∝ fontSize), so scaling the base by √(threshold/length)\n // keeps the wrapped block's area roughly constant past the threshold. Short titles are\n // unaffected (scale clamps at 1).\n const titleFontSize = themedFontSize(fitCoverTitleSize(title), context, true);\n const subtitleFontSize = themedFontSize(40, context, false);\n\n const layers: Layer[] = [];\n\n if (heroSrc) {\n // Hero image path: full-screen image with gradient overlay for text readability\n const imageAnimation = mapAmbientMotion(ambientMotion);\n\n layers.push(\n {\n type: 'image',\n id: 'cover-hero',\n content: {\n src: heroSrc,\n alt: heroAlt || title,\n fit: 'cover',\n credit: heroCredit,\n license: heroLicense,\n ...(treatment ? { treatment } : {}),\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: imageAnimation,\n },\n {\n type: 'shape',\n id: 'cover-gradient',\n content: {\n shape: 'rect',\n fill: 'linear-gradient(0deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.4) 40%, rgba(0,0,0,0.1) 70%, transparent 100%)',\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n );\n } else {\n // No hero image: use a rich theme-driven background with radial gradient\n layers.push({\n type: 'shape',\n id: 'cover-bg',\n content: {\n shape: 'rect',\n fill: `radial-gradient(ellipse at 50% 40%, ${theme.colors.primary} 0%, ${theme.colors.background} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n });\n\n // Subtle decorative accent line below title\n layers.push({\n type: 'shape',\n id: 'cover-accent',\n content: {\n shape: 'rect',\n fill: 'rgba(255, 255, 255, 0.2)',\n },\n position: {\n x: '35%',\n y: subtitle ? '42%' : '58%',\n width: '30%',\n height: '2px',\n },\n });\n }\n\n // Title - positioned lower when over hero, centered when over solid background\n layers.push({\n type: 'text',\n id: 'cover-title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: heroSrc ? (subtitle ? '70%' : '75%') : subtitle ? layout.primaryY : '50%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n // No animation delay - shown immediately at rest\n animation: { type: 'fadeIn', duration: 0.8 },\n });\n\n // Add subtitle if provided\n if (subtitle) {\n layers.push({\n type: 'text',\n id: 'cover-subtitle',\n content: {\n text: subtitle,\n style: {\n fontSize: subtitleFontSize,\n fontFamily: getThemeFont(context, 'body'),\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n },\n },\n position: {\n x: '50%',\n y: heroSrc ? '82%' : layout.secondaryY,\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.2 },\n });\n }\n\n return layers;\n}\n\n/**\n * Convert StartBlockConfig to CoverBlockInput.\n */\nexport function startBlockToCoverInput(config: StartBlockConfig): CoverBlockInput {\n return {\n heroSrc: config.heroSrc,\n heroAlt: config.heroAlt,\n title: config.title,\n subtitle: config.subtitle,\n ambientMotion: config.ambientMotion,\n heroCredit: config.heroCredit,\n heroLicense: config.heroLicense,\n };\n}\n\n/**\n * Expand a StartBlockConfig into a renderable Block.\n * This is used by the player to render the cover block at rest.\n */\nexport function expandCoverBlock(config: StartBlockConfig, context: TemplateContext): Layer[] {\n const input = startBlockToCoverInput(config);\n return coverBlock(input, context);\n}\n","/**\n * Block Template Registry\n *\n * Central registry of all block templates. Provides functions to:\n * - Look up templates by name\n * - Expand template blocks into full Layer arrays\n * - Convert a template-based doc script into a renderable format\n *\n * Supports multiple viewport configurations for different aspect ratios.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type { Block, Layer } from '../../schemas/Doc.js';\nimport type {\n TemplateBlock,\n TemplateContext,\n TemplateRegistry,\n DocBlock,\n PersistentLayerConfig,\n} from '../../schemas/BlockTemplates.js';\nimport type { Theme } from '../../schemas/Theme.js';\nimport type { CustomTemplateDefinition } from '../../schemas/CustomTemplates.js';\nimport { isTemplateBlock, createTemplateContext } from '../../schemas/BlockTemplates.js';\nimport { DEFAULT_THEME as defaultTheme } from '../../schemas/themeLibrary.js';\nimport { expandPersistentLayers, wrapWithPersistentLayers } from './persistentLayers.js';\nimport { applyRenderStyleToLayers } from '../utils/applyRenderStyle.js';\nimport { resolveBlockTransition } from '../../schemas/Transitions.js';\nimport { makeCustomTemplateFn } from './customTemplate.js';\n// Coerces inline `{[…]}` string params into typed template input. inputDescriptors\n// imports `resolveTemplateName` from this module in return; the cycle is benign\n// because every cross-reference lives inside a function body, not module init.\nimport { coerceTemplateParams } from './inputDescriptors.js';\nimport type { ViewportConfig } from '../../schemas/Viewport.js';\nimport { VIEWPORT_PRESETS } from '../../schemas/Viewport.js';\n\nexport { TEMPLATE_METADATA } from './metadata.js';\nexport type { TemplateMetadata } from './metadata.js';\n\n// Import all template functions\nimport { titleBlock } from './titleBlock.js';\nimport { sectionHeader } from './sectionHeader.js';\nimport { statHighlight } from './statHighlight.js';\nimport { quoteBlock } from './quoteBlock.js';\nimport { factCard } from './factCard.js';\nimport { twoColumn } from './twoColumn.js';\nimport { dateEvent } from './dateEvent.js';\nimport { imageWithCaption } from './imageWithCaption.js';\nimport { leftFeature, rightFeature } from './featureBlock.js';\nimport { mapBlock } from './mapBlock.js';\nimport { fullBleedQuote } from './fullBleedQuote.js';\nimport { listBlock } from './listBlock.js';\nimport { photoGrid } from './photoGrid.js';\nimport { definitionCard } from './definitionCard.js';\nimport { comparisonBar } from './comparisonBar.js';\nimport { pullQuote } from './pullQuote.js';\nimport { videoWithCaption } from './videoWithCaption.js';\nimport { videoPullQuote } from './videoPullQuote.js';\nimport { dataTable } from './dataTable.js';\nimport { diagramBlock } from './diagramBlock.js';\nimport { drawingBlock } from './drawingBlock.js';\nimport { layoutBlock } from './layoutBlock.js';\n\n/**\n * Registry mapping template ids (the strings that appear in\n * `{[name]}` markdown annotations) to their implementation functions.\n *\n * Some implementation functions keep historical \"Block\" suffixes\n * (`titleBlock`, `quoteBlock`, `mapBlock`, `listBlock`) for source\n * readability — the registry surfaces them under the cleaner short\n * ids (`title`, `quote`, `map`, `list`). {@link TEMPLATE_ALIASES} keeps\n * the legacy long ids resolvable for documents written before the\n * rename.\n *\n * Note: coverBlock is not in the registry as it's used directly for\n * start blocks, not as a regular template in the block sequence.\n */\nexport const templateRegistry: TemplateRegistry = {\n title: titleBlock,\n sectionHeader,\n statHighlight,\n quote: quoteBlock,\n factCard,\n twoColumn,\n dateEvent,\n imageWithCaption,\n leftFeature,\n rightFeature,\n map: mapBlock,\n fullBleedQuote,\n list: listBlock,\n photoGrid,\n definitionCard,\n comparisonBar,\n pullQuote,\n videoWithCaption,\n videoPullQuote,\n dataTable,\n diagram: diagramBlock,\n layout: layoutBlock,\n drawing: drawingBlock,\n};\n\n/**\n * Back-compat: maps legacy template ids (kept around for documents\n * authored before the \"Block\" suffix was dropped) to the canonical\n * short ids. Resolved by {@link resolveTemplateName} so both the\n * registry lookup and `hasTemplate()` accept either form.\n */\nexport const TEMPLATE_ALIASES: Readonly<Record<string, string>> = {\n titleBlock: 'title',\n quoteBlock: 'quote',\n mapBlock: 'map',\n listBlock: 'list',\n diagramBlock: 'diagram',\n diagramNode: 'diagram',\n};\n\n/**\n * Resolve a template id through {@link TEMPLATE_ALIASES}. Returns the\n * input unchanged when no alias is registered.\n */\nexport function resolveTemplateName(name: string): string {\n return TEMPLATE_ALIASES[name] ?? name;\n}\n\n/**\n * Container templates render their parent block by consuming the block's\n * child headings (via `context.children`) — `diagram` draws them as nodes,\n * `drawing` as shapes, `layout` as absolutely-positioned layers. Those\n * children are therefore NOT independently renderable slides/sections;\n * render paths use {@link isContainerTemplate} to skip descending into\n * them (see `flattenRenderableBlocks`).\n */\nexport const CONTAINER_TEMPLATES: ReadonlySet<string> = new Set(['diagram', 'drawing', 'layout']);\n\n/** True when `name` (or its alias) is a children-consuming container template. */\nexport function isContainerTemplate(name: string | undefined): boolean {\n if (!name) return false;\n return CONTAINER_TEMPLATES.has(resolveTemplateName(name));\n}\n\n/**\n * Merge user-defined custom templates onto the built-in registry.\n * Built-in names take precedence on collision — a user can't shadow\n * `title` or `diagram` from a custom definition. Returns a frozen view\n * so callers don't accidentally mutate the global registry.\n */\nexport type RuntimeTemplateRegistry = Record<\n string,\n (input: TemplateBlock, ctx: TemplateContext) => Layer[]\n>;\n\nexport function buildRegistry(\n custom?: readonly CustomTemplateDefinition[],\n): RuntimeTemplateRegistry {\n // The exported `templateRegistry` is a discriminated-union mapped type;\n // we cast through `unknown` because each entry's `input` type is the\n // template-specific variant, not the broad `TemplateBlock` union.\n // The runtime contract — \"given any TemplateBlock that resolves to a\n // registered name, the function returns Layer[]\" — holds because the\n // expansion path looks up by `template` string before calling.\n const merged: RuntimeTemplateRegistry = {\n ...(templateRegistry as unknown as RuntimeTemplateRegistry),\n };\n if (custom) {\n for (const def of custom) {\n if (merged[def.name]) continue; // built-in wins\n merged[def.name] = makeCustomTemplateFn(def) as unknown as RuntimeTemplateRegistry[string];\n }\n }\n return merged;\n}\n\n/**\n * Expand a template block into a full Block with layers.\n *\n * `registry` lets callers swap in a registry that includes user-defined\n * custom templates. Defaults to the built-in registry when omitted.\n */\nexport function expandTemplateBlock(\n templateBlock: TemplateBlock,\n context: TemplateContext,\n registry: RuntimeTemplateRegistry = templateRegistry as unknown as RuntimeTemplateRegistry,\n): Block {\n const resolved = resolveTemplateName(templateBlock.template);\n const templateFn = registry[resolved];\n\n if (!templateFn) {\n console.warn(`Unknown template: ${templateBlock.template}`);\n // Return block with no layers\n return {\n id: templateBlock.id,\n startTime: 0,\n duration: templateBlock.duration,\n audioSegment: templateBlock.audioSegment,\n transition: templateBlock.transition,\n template: templateBlock.template, // Preserve for debugging\n };\n }\n\n // Effective template input: the block's own fields, then structured\n // body data (```json data fences / derived auto-template inputs), then\n // `{[…]}` string overrides — the same merge order getLayers uses. Inline\n // overrides are coerced to their typed shape (e.g. `center=\"47.6,-122.3\"` →\n // `{lat,lng}`); `templateOverrides` itself stays raw for round-tripping.\n const { templateData, templateOverrides } = templateBlock as Block;\n const input =\n templateData || templateOverrides\n ? ({\n ...templateBlock,\n ...templateData,\n ...coerceTemplateParams(templateBlock.template, templateOverrides ?? {}).input,\n } as TemplateBlock)\n : templateBlock;\n\n // Generate layers from template with error handling\n let layers: Layer[];\n try {\n layers = templateFn(input, context);\n if (!Array.isArray(layers)) {\n console.error(\n `Template ${templateBlock.template} did not return an array, got:`,\n typeof layers,\n );\n layers = [];\n }\n } catch (err: unknown) {\n console.error(`Error expanding template ${templateBlock.template}:`, err);\n layers = [];\n }\n\n return {\n id: templateBlock.id,\n startTime: 0, // Will be calculated later\n duration: templateBlock.duration,\n audioSegment: templateBlock.audioSegment,\n ...(layers.length > 0 ? { layers } : {}),\n transition: templateBlock.transition,\n template: templateBlock.template, // Preserve for debugging\n };\n}\n\n/**\n * Audio segment timing info for aligning blocks with audio.\n */\nexport interface AudioSegmentTiming {\n /** Start time of this segment in the overall timeline */\n startTime: number;\n /** Duration of this segment */\n duration: number;\n}\n\n/**\n * Options for expanding doc blocks.\n */\nexport interface ExpandDocBlocksOptions {\n /** Theme for template rendering (defaults to DEFAULT_THEME) */\n theme?: Theme;\n /** Viewport configuration (defaults to 16:9 landscape) */\n viewport?: ViewportConfig;\n /** Persistent layers for visual consistency across blocks */\n persistentLayers?: PersistentLayerConfig;\n /**\n * Audio segment timing information.\n * When provided, blocks are timed relative to their audio segment's start time,\n * ensuring proper synchronization with audio playback.\n */\n audioSegments?: AudioSegmentTiming[];\n /**\n * User-defined custom templates to merge onto the built-in registry\n * before expanding blocks. Typically passed straight from\n * `Doc.customTemplates`. Built-in names take precedence on collision.\n */\n customTemplates?: readonly CustomTemplateDefinition[];\n}\n\n/**\n * Expand all template blocks in a doc, calculating start times.\n * Injects persistent layers (bottom/top) based on per-block flags.\n *\n * When audioSegments is provided, blocks are timed relative to their audio segment's\n * start time, ensuring blocks appear when their audio is playing. Blocks within each\n * segment are distributed proportionally across the segment duration.\n *\n * @param blocks - Array of template or raw blocks\n * @param options - Expansion options including theme, viewport, and persistent layers\n */\n/**\n * Finalize an expanded block in place: apply the theme's motion post-pass\n * to template-generated layers, wrap with persistent bottom/top layers,\n * and fill the theme's default transition (never on block 0, never over an\n * authored transition). Shared by every expansion path in this module.\n */\nfunction finalizeExpandedBlock(\n expandedBlock: Block,\n sourceBlock: DocBlock,\n blockIndex: number,\n theme: Theme,\n bottomLayers: Layer[],\n topLayers: Layer[],\n): void {\n // Theme motion defaults apply only to template-generated layers —\n // raw authored block.layers are never restyled.\n if (isTemplateBlock(sourceBlock) && expandedBlock.layers && expandedBlock.layers.length > 0) {\n expandedBlock.layers = applyRenderStyleToLayers(expandedBlock.layers, expandedBlock, theme);\n }\n\n if (bottomLayers.length > 0 || topLayers.length > 0) {\n expandedBlock.layers = wrapWithPersistentLayers(\n expandedBlock.layers ?? [],\n sourceBlock as TemplateBlock,\n bottomLayers,\n topLayers,\n );\n }\n\n const transition = resolveBlockTransition(expandedBlock, theme, blockIndex);\n if (transition !== expandedBlock.transition) {\n expandedBlock.transition = transition;\n }\n}\n\nexport function expandDocBlocks(blocks: DocBlock[], options: ExpandDocBlocksOptions = {}): Block[] {\n const opts: ExpandDocBlocksOptions = options;\n\n const theme = opts.theme ?? defaultTheme;\n const viewport = opts.viewport ?? VIEWPORT_PRESETS.landscape;\n const { persistentLayers, audioSegments, customTemplates } = opts;\n const totalBlocks = blocks.length;\n // Merge user-defined templates onto the built-in registry once per\n // call; passed into every `expandTemplateBlock` invocation below.\n const registry: RuntimeTemplateRegistry =\n customTemplates && customTemplates.length > 0\n ? buildRegistry(customTemplates)\n : (templateRegistry as unknown as RuntimeTemplateRegistry);\n\n // Pre-expand persistent layers once. Callers that pass no config inherit\n // the theme's atmosphere (docs with their own layers pass them in and win\n // wholesale — see resolvePersistentLayers).\n const effectivePersistentLayers = persistentLayers ?? theme.persistentLayers;\n const bottomLayers = expandPersistentLayers(effectivePersistentLayers?.bottomLayers, theme);\n const topLayers = expandPersistentLayers(effectivePersistentLayers?.topLayers, theme);\n\n // If no audio segments provided, use simple cumulative timing\n if (!audioSegments || audioSegments.length === 0) {\n let currentTime = 0;\n return blocks.map((block, index) => {\n const context = createTemplateContext(theme, index, totalBlocks, viewport);\n const maybeChildren = (block as Block).children;\n if (maybeChildren && maybeChildren.length > 0) {\n context.children = maybeChildren;\n }\n // Expose the source block so custom templates' token resolver can\n // see the block's title / contents / children. Only set for\n // custom-templated blocks — built-ins ignore context.block but\n // exposing it changes object identity which we want to avoid for\n // the perf-sensitive built-in path.\n if (\n isTemplateBlock(block) &&\n customTemplates &&\n customTemplates.some((c) => c.name === block.template)\n ) {\n context.block = block as Block;\n }\n const expandedBlock = isTemplateBlock(block)\n ? expandTemplateBlock(block, context, registry)\n : (block as Block);\n\n finalizeExpandedBlock(expandedBlock, block, index, theme, bottomLayers, topLayers);\n\n expandedBlock.startTime = currentTime;\n currentTime += expandedBlock.duration;\n return expandedBlock;\n });\n }\n\n // Group blocks by their audioSegment index\n const blocksBySegment = new Map<number, { block: DocBlock; originalIndex: number }[]>();\n blocks.forEach((block, index) => {\n const segmentIndex = (block as TemplateBlock).audioSegment ?? 0;\n if (!blocksBySegment.has(segmentIndex)) {\n blocksBySegment.set(segmentIndex, []);\n }\n blocksBySegment.get(segmentIndex)!.push({ block, originalIndex: index });\n });\n\n // Expand blocks, timing them relative to their audio segment\n const expandedBlocks: Block[] = new Array(blocks.length);\n\n for (const [segmentIndex, segmentBlocks] of blocksBySegment) {\n const audioSegment = audioSegments[segmentIndex];\n if (!audioSegment) {\n // No audio segment info - use simple sequential timing within the segment\n let offsetTime = 0;\n for (const { block, originalIndex } of segmentBlocks) {\n const context = createTemplateContext(theme, originalIndex, totalBlocks, viewport);\n const maybeChildren = (block as Block).children;\n if (maybeChildren && maybeChildren.length > 0) {\n context.children = maybeChildren;\n }\n if (\n isTemplateBlock(block) &&\n customTemplates &&\n customTemplates.some((c) => c.name === block.template)\n ) {\n context.block = block as Block;\n }\n const expandedBlock = isTemplateBlock(block)\n ? expandTemplateBlock(block, context, registry)\n : (block as Block);\n\n finalizeExpandedBlock(expandedBlock, block, originalIndex, theme, bottomLayers, topLayers);\n\n expandedBlock.startTime = offsetTime;\n offsetTime += expandedBlock.duration;\n expandedBlocks[originalIndex] = expandedBlock;\n }\n continue;\n }\n\n // Section headers (sectionHeader template) get their original duration since\n // the title is spoken at the start of the segment. Other blocks are scaled\n // to fill the remaining time.\n const sectionHeaderBlocks: typeof segmentBlocks = [];\n const contentBlocks: typeof segmentBlocks = [];\n\n for (const item of segmentBlocks) {\n const templateBlock = item.block as TemplateBlock;\n if (templateBlock.template === 'sectionHeader') {\n sectionHeaderBlocks.push(item);\n } else {\n contentBlocks.push(item);\n }\n }\n\n // Calculate fixed duration (section headers) and scalable duration (content)\n const fixedDuration = sectionHeaderBlocks.reduce((sum, { block }) => {\n return sum + ((block as TemplateBlock).duration ?? 0);\n }, 0);\n\n const contentBlockDuration = contentBlocks.reduce((sum, { block }) => {\n return sum + ((block as TemplateBlock).duration ?? 0);\n }, 0);\n\n // Remaining time after section header(s) for content blocks\n const remainingDuration = audioSegment.duration - fixedDuration;\n\n // Scale factor only applies to content blocks\n const scaleFactor =\n contentBlockDuration > 0 && remainingDuration > 0\n ? remainingDuration / contentBlockDuration\n : 1;\n\n // First pass: expand all blocks and track which have source timing\n interface ExpandedSlideInfo {\n block: Block;\n originalIndex: number;\n templateBlock: TemplateBlock;\n hasSourceTiming: boolean;\n }\n const expandedInfos: ExpandedSlideInfo[] = [];\n\n for (const { block, originalIndex } of segmentBlocks) {\n const context = createTemplateContext(theme, originalIndex, totalBlocks, viewport);\n const maybeChildren = (block as Block).children;\n if (maybeChildren && maybeChildren.length > 0) {\n context.children = maybeChildren;\n }\n if (\n isTemplateBlock(block) &&\n customTemplates &&\n customTemplates.some((c) => c.name === block.template)\n ) {\n context.block = block as Block;\n }\n const expandedBlock = isTemplateBlock(block)\n ? expandTemplateBlock(block, context, registry)\n : (block as Block);\n\n finalizeExpandedBlock(expandedBlock, block, originalIndex, theme, bottomLayers, topLayers);\n\n const templateBlock = block as TemplateBlock;\n expandedInfos.push({\n block: expandedBlock,\n originalIndex,\n templateBlock,\n hasSourceTiming: typeof templateBlock.sourceStartTime === 'number',\n });\n }\n\n // Sort blocks within this segment.\n // When ANY block has source timing (from audio analysis), section headers\n // are placed first since they are intro cards for the segment. When there\n // is no source timing (e.g., preview/synthetic mode), keep original\n // document order so the slideshow matches what the author wrote.\n const hasAnySourceTiming = expandedInfos.some((info) => info.hasSourceTiming);\n\n expandedInfos.sort((a, b) => {\n if (hasAnySourceTiming) {\n const aIsHeader = a.templateBlock.template === 'sectionHeader';\n const bIsHeader = b.templateBlock.template === 'sectionHeader';\n\n // Section headers come first when we have audio-based timing\n if (aIsHeader && !bIsHeader) return -1;\n if (!aIsHeader && bIsHeader) return 1;\n\n // Both have source timing - sort by time\n if (a.hasSourceTiming && b.hasSourceTiming) {\n return a.templateBlock.sourceStartTime! - b.templateBlock.sourceStartTime!;\n }\n\n // One has timing, one doesn't - timing comes first\n if (a.hasSourceTiming && !b.hasSourceTiming) return -1;\n if (!a.hasSourceTiming && b.hasSourceTiming) return 1;\n }\n\n // Keep original document order\n return a.originalIndex - b.originalIndex;\n });\n\n // Second pass: assign start times\n // Slides with sourceStartTime use that; others fill gaps\n let offsetWithinSegment = 0;\n for (let i = 0; i < expandedInfos.length; i++) {\n const info = expandedInfos[i];\n const { block: expandedBlock, templateBlock } = info;\n\n // Use source timing if available, otherwise use sequential offset\n if (info.hasSourceTiming) {\n // Use the source timing - this is when the content is spoken\n const sourceStart = templateBlock.sourceStartTime!;\n expandedBlock.startTime = audioSegment.startTime + sourceStart;\n // Use source duration if available, otherwise use block duration\n if (typeof templateBlock.sourceDuration === 'number') {\n expandedBlock.duration = Math.max(5, templateBlock.sourceDuration);\n }\n // Update offset for next block\n offsetWithinSegment = sourceStart + expandedBlock.duration;\n } else if (templateBlock.template === 'sectionHeader') {\n // Section headers start at the beginning of the segment\n expandedBlock.startTime = audioSegment.startTime + offsetWithinSegment;\n offsetWithinSegment += expandedBlock.duration;\n } else {\n // No source timing - place after previous block\n expandedBlock.startTime = audioSegment.startTime + offsetWithinSegment;\n // Scale content block duration to fit\n const scaledDuration = expandedBlock.duration * scaleFactor;\n expandedBlock.duration = scaledDuration;\n offsetWithinSegment += scaledDuration;\n }\n\n expandedBlocks[info.originalIndex] = expandedBlock;\n }\n\n // Third pass: fix overlaps and gaps by adjusting durations\n // Sort by startTime for overlap detection\n const segmentExpandedBlocks = expandedInfos\n .map((info) => expandedBlocks[info.originalIndex])\n .sort((a, b) => a.startTime - b.startTime);\n\n for (let i = 0; i < segmentExpandedBlocks.length - 1; i++) {\n const current = segmentExpandedBlocks[i];\n const next = segmentExpandedBlocks[i + 1];\n const currentEnd = current.startTime + current.duration;\n\n if (currentEnd > next.startTime) {\n // Overlap - shorten current block to end when next begins\n current.duration = next.startTime - current.startTime;\n } else if (currentEnd < next.startTime - 0.5) {\n // Gap > 0.5s - extend current block to fill (visual continuity)\n // Same formula as overlap: snap to next block start\n current.duration = Math.max(0.1, next.startTime - current.startTime);\n }\n }\n\n // Minimum gap between any two transitions (including section boundaries).\n // No block should be visible for less than this before a transition occurs.\n const MIN_TRANSITION_GAP = 5;\n const segmentEnd = audioSegment.startTime + audioSegment.duration;\n\n // Fourth pass: enforce minimum transition gap at section boundary.\n // Eliminate blocks from the end that would start within MIN_TRANSITION_GAP\n // of the section end (creating a jarring transition right before the next\n // section header). Loop to handle cascading eliminations.\n while (segmentExpandedBlocks.length > 1) {\n const lastBlock = segmentExpandedBlocks[segmentExpandedBlocks.length - 1];\n const timeFromLastToEnd = segmentEnd - lastBlock.startTime;\n\n if (timeFromLastToEnd < MIN_TRANSITION_GAP && lastBlock.template !== 'sectionHeader') {\n const prevBlock = segmentExpandedBlocks[segmentExpandedBlocks.length - 2];\n prevBlock.duration = segmentEnd - prevBlock.startTime;\n lastBlock.duration = 0;\n lastBlock.startTime = segmentEnd;\n segmentExpandedBlocks.pop();\n } else {\n // Extend last block to fill to segment end\n if (lastBlock.startTime + lastBlock.duration < segmentEnd) {\n lastBlock.duration = segmentEnd - lastBlock.startTime;\n }\n break;\n }\n }\n\n // Handle single-block segment\n if (segmentExpandedBlocks.length === 1) {\n const onlyBlock = segmentExpandedBlocks[0];\n if (onlyBlock.startTime + onlyBlock.duration < segmentEnd) {\n onlyBlock.duration = segmentEnd - onlyBlock.startTime;\n }\n }\n\n // Fifth pass: eliminate any remaining blocks shorter than MIN_TRANSITION_GAP.\n // Walk backwards and merge short blocks into their predecessor.\n // Skip index 0 (first block, typically section header) to preserve segment start.\n let changed = true;\n while (changed) {\n changed = false;\n for (let i = segmentExpandedBlocks.length - 1; i >= 1; i--) {\n const block = segmentExpandedBlocks[i];\n if (block.duration > 0 && block.duration < MIN_TRANSITION_GAP) {\n const prev = segmentExpandedBlocks[i - 1];\n const blockEnd = block.startTime + block.duration;\n prev.duration = blockEnd - prev.startTime;\n block.duration = 0;\n block.startTime = segmentEnd;\n segmentExpandedBlocks.splice(i, 1);\n changed = true;\n break; // Restart loop after modification\n }\n }\n }\n\n // Sixth pass: split blocks that are too long (>20s)\n // This ensures no single block lingers for too long\n const MAX_BLOCK_DURATION = 20;\n\n for (let i = 0; i < segmentExpandedBlocks.length; i++) {\n const block = segmentExpandedBlocks[i];\n\n if (block.duration > MAX_BLOCK_DURATION) {\n // Calculate how many parts we need\n const numParts = Math.ceil(block.duration / MAX_BLOCK_DURATION);\n const partDuration = block.duration / numParts;\n\n // Only split if each part would meet minimum transition gap\n if (partDuration >= MIN_TRANSITION_GAP) {\n // Shorten original block to first part\n block.duration = partDuration;\n\n // Create additional blocks for remaining parts\n for (let p = 1; p < numParts; p++) {\n const splitBlock: Block = {\n id: `${block.id}-split-${p}`,\n startTime: block.startTime + p * partDuration,\n duration: partDuration,\n audioSegment: block.audioSegment,\n layers: (block.layers ?? []).map((layer) => ({\n ...layer,\n id: `${layer.id}-split-${p}`,\n })),\n transition: { type: 'dissolve', duration: 1.0 },\n template: block.template,\n };\n // Insert into expanded blocks array\n expandedBlocks.push(splitBlock);\n }\n }\n }\n }\n }\n\n // Filter out zero-duration blocks (eliminated in earlier passes)\n return expandedBlocks.filter((block) => block && block.duration > 0);\n}\n\n/**\n * Get list of available template names.\n */\nexport function getAvailableTemplates(): string[] {\n return Object.keys(templateRegistry);\n}\n\n/**\n * Check if a template exists. Accepts both the canonical short id\n * (`title`, `quote`, `map`, `list`) and legacy aliases\n * (`titleBlock`, `quoteBlock`, `mapBlock`, `listBlock`).\n */\nexport function hasTemplate(name: string): boolean {\n return resolveTemplateName(name) in templateRegistry;\n}\n\n// Re-export types and utilities from schemas\nexport {\n isTemplateBlock,\n createTemplateContext,\n scaledFontSize,\n} from '../../schemas/BlockTemplates.js';\nexport type {\n TemplateBlock,\n DocBlock,\n TemplateContext,\n PersistentLayerConfig,\n} from '../../schemas/BlockTemplates.js';\nexport {\n DEFAULT_THEME,\n resolveTheme,\n getAvailableThemes,\n getThemeSummaries,\n} from '../../schemas/themeLibrary.js';\nexport type {\n Theme,\n ThemeColorPalette,\n ThemeColorScheme,\n ThemeTypography,\n ThemeStyle,\n RenderStyle,\n} from '../../schemas/Theme.js';\nexport type { DocStylePreset } from './persistentLayers.js';\n// Re-export timing types (AudioSegmentTiming and ExpandDocBlocksOptions are already exported above)\nexport { VIEWPORT_PRESETS, getViewport, getViewportOrientation } from '../../schemas/Viewport.js';\nexport type {\n ViewportConfig,\n ViewportPreset,\n ViewportOrientation,\n} from '../../schemas/Viewport.js';\nexport { getLayoutHints, getTwoColumnPositions } from '../../schemas/LayoutStrategy.js';\nexport type { LayoutHints } from '../../schemas/LayoutStrategy.js';\nexport {\n expandPersistentLayers,\n getDocStyleConfig,\n getPersistentLayersFromTheme,\n resolvePersistentLayers,\n wrapWithPersistentLayers,\n} from './persistentLayers.js';\n\n// Re-export individual templates for direct access\nexport { titleBlock } from './titleBlock.js';\nexport { sectionHeader } from './sectionHeader.js';\nexport { statHighlight } from './statHighlight.js';\nexport { quoteBlock } from './quoteBlock.js';\nexport { factCard } from './factCard.js';\nexport { twoColumn } from './twoColumn.js';\nexport { dateEvent } from './dateEvent.js';\nexport { imageWithCaption } from './imageWithCaption.js';\nexport { leftFeature, rightFeature } from './featureBlock.js';\nexport { mapBlock } from './mapBlock.js';\nexport { coverBlock, expandCoverBlock } from './coverBlock.js';\nexport type { CoverBlockInput } from './coverBlock.js';\nexport { fullBleedQuote } from './fullBleedQuote.js';\nexport { listBlock } from './listBlock.js';\nexport { photoGrid } from './photoGrid.js';\nexport { definitionCard } from './definitionCard.js';\nexport { comparisonBar } from './comparisonBar.js';\nexport { pullQuote } from './pullQuote.js';\nexport { videoWithCaption } from './videoWithCaption.js';\nexport { videoPullQuote } from './videoPullQuote.js';\nexport { dataTable } from './dataTable.js';\nexport { diagramBlock } from './diagramBlock.js';\nexport { computeDiagramLayout } from './diagramLayout.js';\nexport type {\n DiagramLayout,\n DiagramNodePosition,\n DiagramEdge,\n DiagramLayoutOptions,\n} from './diagramLayout.js';\nexport { drawingBlock } from './drawingBlock.js';\nexport {\n computeDrawingLayout,\n normalizeShapeKind,\n isShapeName,\n SHAPE_NAMES,\n} from './drawingLayout.js';\nexport type {\n DrawingLayout,\n DrawingShape,\n DrawingShapeKind,\n DrawingConnector,\n DrawingLayoutOptions,\n} from './drawingLayout.js';\nexport { layoutBlock } from './layoutBlock.js';\nexport { computeLayoutLayers } from './layoutLayout.js';\nexport type { LayoutLayersResult, LayoutLayerDefaults } from './layoutLayout.js';\n\n// Re-export accent image utilities\nexport { getAccentLayout, createAccentLayers, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nexport type { AccentLayout } from './accentImage.js';\n","/**\n * Animation Utilities\n *\n * Helper functions for mapping Doc animations to CSS classes and styles.\n * Generates the appropriate animation class names and CSS custom properties\n * for duration, delay, and easing.\n *\n * This is shared code used by both site and efb-app doc renderers.\n */\n\nimport type {\n Animation,\n AnimationType,\n TransitionDirection,\n TransitionType,\n} from '../../schemas/Doc.js';\n\ninterface AnimationResult {\n /** CSS class name to apply */\n className: string;\n /** Inline styles for CSS custom properties */\n style: Record<string, string>;\n}\n\n/**\n * Get CSS class and styles for an animation.\n */\nexport function getAnimationStyle(\n animation: Animation | undefined,\n _currentTime: number = 0,\n): AnimationResult {\n if (!animation || animation.type === 'none') {\n return { className: '', style: {} };\n }\n\n const className = getAnimationClassName(animation);\n const style = getAnimationCSSVars(animation);\n\n return { className, style };\n}\n\n/**\n * Map animation type to CSS class name.\n */\nfunction getAnimationClassName(animation: Animation): string {\n const { type, direction, panDirection } = animation;\n\n switch (type) {\n case 'fadeIn':\n return 'anim-fadeIn';\n case 'fadeOut':\n return 'anim-fadeOut';\n case 'slowZoom':\n // Slow zoom (Ken Burns style) has variants based on direction and pan\n if (panDirection === 'left') return 'anim-slowZoom-panLeft';\n if (panDirection === 'right') return 'anim-slowZoom-panRight';\n if (direction === 'out') return 'anim-slowZoom-out';\n return 'anim-slowZoom-in';\n case 'zoomIn':\n return 'anim-zoomIn';\n case 'zoomOut':\n return 'anim-zoomOut';\n case 'panLeft':\n return 'anim-panLeft';\n case 'panRight':\n return 'anim-panRight';\n case 'typewriter':\n return 'anim-typewriter';\n default:\n return '';\n }\n}\n\n/**\n * Generate CSS custom properties for animation timing.\n */\nfunction getAnimationCSSVars(animation: Animation): Record<string, string> {\n const vars: Record<string, string> = {};\n\n if (animation.duration !== undefined) {\n vars['--anim-duration'] = `${animation.duration}s`;\n }\n\n if (animation.delay !== undefined) {\n vars['--anim-delay'] = `${animation.delay}s`;\n }\n\n if (animation.easing) {\n vars['--anim-easing'] = animation.easing;\n }\n\n return vars;\n}\n\n/**\n * Get default duration for an animation type.\n */\nexport function getDefaultAnimationDuration(type: AnimationType): number {\n switch (type) {\n case 'slowZoom':\n case 'panLeft':\n case 'panRight':\n return 8; // Long, slow animations\n case 'typewriter':\n return 3;\n case 'fadeIn':\n case 'fadeOut':\n return 1;\n case 'zoomIn':\n case 'zoomOut':\n return 0.5;\n default:\n return 1;\n }\n}\n\n/**\n * Get transition class for slide entry/exit.\n */\nexport function getTransitionClass(\n type: TransitionType,\n entering: boolean,\n direction?: TransitionDirection,\n): string {\n if (type === 'cut') return '';\n const mode = entering ? 'enter' : 'exit';\n return `transition-${getTransitionVisualClass(type, direction)}-${mode}`;\n}\n\ntype TransitionVisualClass =\n | 'cut'\n | 'fade'\n | 'dissolve'\n | 'pushLeft'\n | 'pushRight'\n | 'pushUp'\n | 'pushDown'\n | 'wipeLeft'\n | 'wipeRight'\n | 'wipeUp'\n | 'wipeDown'\n | 'splitHorizontal'\n | 'splitVertical'\n | 'revealLeft'\n | 'revealRight'\n | 'revealUp'\n | 'revealDown'\n | 'random'\n | 'randomBarsHorizontal'\n | 'randomBarsVertical'\n | 'shapeCircle'\n | 'shapeDiamond'\n | 'shapePlus'\n | 'uncoverLeft'\n | 'uncoverRight'\n | 'uncoverUp'\n | 'uncoverDown'\n | 'coverLeft'\n | 'coverRight'\n | 'coverUp'\n | 'coverDown'\n | 'zoom'\n | 'panLeft'\n | 'panRight'\n | 'panUp'\n | 'panDown'\n | 'flash'\n | 'newsflash'\n | 'fallOver'\n | 'drape'\n | 'curtains'\n | 'wind'\n | 'prestige'\n | 'fracture'\n | 'crush'\n | 'peelOff'\n | 'pageCurl'\n | 'pageCurlDouble'\n | 'pageCurlSingle'\n | 'airplane'\n | 'origami'\n | 'checkerboard'\n | 'blindsHorizontal'\n | 'blindsVertical'\n | 'clock'\n | 'ripple'\n | 'honeycomb'\n | 'glitter'\n | 'vortex'\n | 'shred'\n | 'switch'\n | 'flip'\n | 'gallery'\n | 'cube'\n | 'doors'\n | 'box'\n | 'ferrisWheel'\n | 'conveyor'\n | 'rotate'\n | 'window'\n | 'orbit'\n | 'flyThrough'\n | 'morph'\n | 'combHorizontal'\n | 'combVertical'\n | 'pullLeft'\n | 'pullRight'\n | 'pullUp'\n | 'pullDown'\n | 'stripsLeft'\n | 'stripsRight'\n | 'stripsUp'\n | 'stripsDown'\n | 'wedge'\n | 'wheel'\n | 'wheelReverse'\n | 'prism'\n | 'warp'\n | 'slideLeft'\n | 'slideRight'\n | 'slideUp'\n | 'slideDown';\n\nconst TRANSITION_VISUAL_CLASS_BY_TYPE: Record<TransitionType, TransitionVisualClass> = {\n airplane: 'airplane',\n blinds: 'blindsHorizontal',\n box: 'box',\n checker: 'checkerboard',\n checkerboard: 'checkerboard',\n circle: 'shapeCircle',\n clock: 'clock',\n comb: 'combHorizontal',\n conveyor: 'conveyor',\n cover: 'coverLeft',\n crush: 'crush',\n cube: 'cube',\n curtains: 'curtains',\n cut: 'cut',\n diamond: 'shapeDiamond',\n dissolve: 'dissolve',\n doors: 'doors',\n drape: 'drape',\n fade: 'fade',\n fallOver: 'fallOver',\n ferris: 'ferrisWheel',\n ferrisWheel: 'ferrisWheel',\n flash: 'flash',\n flip: 'flip',\n flyThrough: 'flyThrough',\n flythrough: 'flyThrough',\n fracture: 'fracture',\n gallery: 'gallery',\n glitter: 'glitter',\n honeycomb: 'honeycomb',\n morph: 'morph',\n newsflash: 'newsflash',\n orbit: 'orbit',\n origami: 'origami',\n pageCurl: 'pageCurl',\n pageCurlDouble: 'pageCurlDouble',\n pageCurlSingle: 'pageCurlSingle',\n pan: 'panLeft',\n peelOff: 'peelOff',\n plus: 'shapePlus',\n prestige: 'prestige',\n prism: 'prism',\n pull: 'pullLeft',\n push: 'pushLeft',\n random: 'random',\n randomBar: 'randomBarsHorizontal',\n randomBars: 'randomBarsVertical',\n reveal: 'revealLeft',\n ripple: 'ripple',\n rotate: 'rotate',\n shape: 'shapeCircle',\n shred: 'shred',\n slideDown: 'slideDown',\n slideLeft: 'slideLeft',\n slideRight: 'slideRight',\n slideUp: 'slideUp',\n split: 'splitHorizontal',\n strips: 'stripsLeft',\n switch: 'switch',\n uncover: 'uncoverLeft',\n vortex: 'vortex',\n warp: 'warp',\n wedge: 'wedge',\n wheel: 'wheel',\n wheelReverse: 'wheelReverse',\n wind: 'wind',\n window: 'window',\n wipe: 'wipeLeft',\n zoom: 'zoom',\n};\n\nfunction getTransitionVisualClass(\n type: TransitionType,\n direction?: TransitionDirection,\n): TransitionVisualClass {\n switch (type) {\n case 'randomBars':\n case 'randomBar':\n return axisClass('randomBars', direction, type === 'randomBar' ? 'Horizontal' : 'Vertical');\n case 'comb':\n return axisClass('comb', direction, 'Horizontal');\n case 'blinds':\n return axisClass('blinds', direction, 'Horizontal');\n case 'split':\n return axisClass('split', direction, 'Horizontal');\n case 'push':\n return directionalClass('push', direction, 'Left');\n case 'wipe':\n return directionalClass('wipe', direction, 'Left');\n case 'cover':\n return directionalClass('cover', direction, 'Left');\n case 'uncover':\n return directionalClass('uncover', direction, 'Left');\n case 'pull':\n return directionalClass('pull', direction, 'Left');\n case 'reveal':\n return directionalClass('reveal', direction, 'Left');\n case 'strips':\n return directionalClass('strips', direction, 'Left');\n case 'pan':\n return directionalClass('pan', direction, 'Left');\n default:\n return TRANSITION_VISUAL_CLASS_BY_TYPE[type];\n }\n}\n\nfunction directionalClass(\n prefix: 'cover' | 'pan' | 'pull' | 'push' | 'reveal' | 'strips' | 'uncover' | 'wipe',\n direction: TransitionDirection | undefined,\n fallback: 'Left' | 'Right' | 'Up' | 'Down',\n): TransitionVisualClass {\n const suffix = directionSuffix(direction, fallback);\n return `${prefix}${suffix}` as TransitionVisualClass;\n}\n\nfunction axisClass(\n prefix: 'blinds' | 'comb' | 'randomBars' | 'split',\n direction: TransitionDirection | undefined,\n fallback: 'Horizontal' | 'Vertical',\n): TransitionVisualClass {\n const suffix = axisSuffix(direction, fallback);\n return `${prefix}${suffix}` as TransitionVisualClass;\n}\n\nfunction directionSuffix(\n direction: TransitionDirection | undefined,\n fallback: 'Left' | 'Right' | 'Up' | 'Down',\n): 'Left' | 'Right' | 'Up' | 'Down' {\n switch (direction) {\n case 'right':\n return 'Right';\n case 'up':\n return 'Up';\n case 'down':\n return 'Down';\n case 'left':\n default:\n return fallback;\n }\n}\n\nfunction axisSuffix(\n direction: TransitionDirection | undefined,\n fallback: 'Horizontal' | 'Vertical',\n): 'Horizontal' | 'Vertical' {\n switch (direction) {\n case 'vertical':\n case 'up':\n case 'down':\n return 'Vertical';\n case 'horizontal':\n case 'left':\n case 'right':\n return 'Horizontal';\n default:\n return fallback;\n }\n}\n\n/**\n * Calculate animation progress (0-1) based on current time.\n */\nexport function getAnimationProgress(\n animation: Animation,\n currentTime: number,\n slideDuration: number,\n): number {\n const delay = animation.delay || 0;\n const duration = animation.duration || slideDuration;\n\n if (currentTime < delay) return 0;\n if (currentTime >= delay + duration) return 1;\n\n return (currentTime - delay) / duration;\n}\n","/**\n * Image treatment → CSS filter derivation.\n *\n * Treatments are theme-level photographic grades (see `ImageTreatment` in\n * [Doc.ts](../../schemas/Doc.ts)). They compile to plain CSS `filter`\n * functions so the same string works on a foreignObject `<img>`, an SVG\n * `<image>` element, and in headless frame capture — no SVG filter defs to\n * coordinate, no renderer-specific code paths.\n */\n\nimport type { ImageTreatment } from '../../schemas/Doc.js';\nimport { hexHueDegrees } from '../../schemas/colorUtils.js';\n\n/** Sepia's intrinsic tint hue (deg) — duotone rotates from here to the target. */\nconst SEPIA_HUE_DEGREES = 40;\n\nconst clamp01 = (v: number) => Math.max(0, Math.min(1, v));\nconst r2 = (v: number) => Math.round(v * 100) / 100;\n\n/**\n * Build the CSS `filter` value for a treatment and/or blur.\n * Returns `undefined` when there is nothing to apply.\n */\nexport function cssFilterForTreatment(\n treatment?: ImageTreatment,\n blur?: number,\n): string | undefined {\n const parts: string[] = [];\n\n if (blur != null && blur > 0) {\n parts.push(`blur(${Math.round(blur)}px)`);\n }\n\n if (treatment && treatment.type !== 'none') {\n const s = clamp01(treatment.strength ?? 0.6);\n switch (treatment.type) {\n case 'mono':\n // Archival black & white with a whisper of contrast to keep depth.\n parts.push(`grayscale(${r2(s)})`, `contrast(${r2(1 + 0.05 * s)})`);\n break;\n case 'duotone': {\n // Classic CSS duotone approximation: flatten to gray, tint through\n // sepia (intrinsic hue ~40°), rotate to the target hue, re-saturate.\n const hue = hexHueDegrees(treatment.color ?? '#3d5a80');\n parts.push(\n 'grayscale(1)',\n `sepia(${r2(Math.max(0.35, s))})`,\n `hue-rotate(${Math.round(hue - SEPIA_HUE_DEGREES)}deg)`,\n `saturate(${r2(1 + s)})`,\n );\n break;\n }\n case 'warm':\n // Partial sepia is a well-behaved warmer; slight saturation lift.\n parts.push(`sepia(${r2(0.4 * s)})`, `saturate(${r2(1 + 0.15 * s)})`);\n break;\n case 'cool':\n // Desaturate a touch and nudge warm tones toward magenta/blue —\n // small rotations read as a grade, large ones wreck hues.\n parts.push(\n `saturate(${r2(1 - 0.25 * s)})`,\n `hue-rotate(${Math.round(-12 * s)}deg)`,\n `contrast(${r2(1 + 0.06 * s)})`,\n );\n break;\n }\n }\n\n return parts.length > 0 ? parts.join(' ') : undefined;\n}\n","/**\n * Template input derivation — build a template's required inputs from a\n * block's heading + markdown body.\n *\n * Used by:\n * - `markdownToDoc`'s content-aware auto template picking, so an\n * auto-picked `quote`/`statHighlight`/`leftFeature` block carries the\n * inputs its template needs through every render path (player, linear\n * view, exports).\n * - `LinearDocView` / editor previews, to render annotated blocks whose\n * authors didn't spell out every template param.\n *\n * Two modes:\n * - strict (default): returns `null` when an essential input can't be\n * derived (no image for a feature block, no table for dataTable…) —\n * auto-picking uses this to fall back to the structural default.\n * - placeholders: returns visible placeholder values instead of failing —\n * preview surfaces use this so a half-authored block still renders.\n */\n\nimport type { MarkdownBlockNode, MarkdownList, MarkdownTable } from '../markdown/types.js';\nimport { extractPlainText } from '../markdown/utils.js';\n\n/** First image discovered in a block's body, with explicit dimensions when present. */\nexport interface FirstImage {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n}\n\n/** Plain text of a block's body contents (excluding the heading). */\nexport function extractBodyPlainText(contents?: MarkdownBlockNode[]): string {\n if (!contents || contents.length === 0) return '';\n return contents\n .map((n) => extractPlainText(n))\n .join('\\n')\n .trim();\n}\n\n/** Extract list items as plain text. */\nexport function extractListItems(contents?: MarkdownBlockNode[]): string[] {\n if (!contents) return [];\n const items: string[] = [];\n for (const node of contents) {\n if (node.type === 'list') {\n for (const item of (node as MarkdownList).children) {\n const text = extractPlainText(item).trim();\n if (text) items.push(text);\n }\n }\n }\n return items;\n}\n\n/** Parse a `width`/`height` HTML attribute to a positive number. */\nfunction parseDim(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined;\n const n = parseFloat(raw);\n return Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\n/**\n * Find images referenced anywhere in block contents — both markdown\n * shorthand `![alt](url)` (type `image`) and raw HTML `<img>` tags\n * (type `htmlBlock`/`htmlInline`). The WYSIWYG editor emits the HTML form\n * whenever a user resizes an image, so missing that path silently breaks\n * every resized image.\n *\n * @param limit Stop after this many images (default: all).\n */\nexport function extractImages(\n contents: MarkdownBlockNode[] | undefined,\n limit = Infinity,\n): FirstImage[] {\n if (!contents || contents.length === 0) return [];\n const found: FirstImage[] = [];\n\n function fromHtml(nodes: unknown[]): void {\n for (const node of nodes) {\n if (found.length >= limit) return;\n if (!node || typeof node !== 'object') continue;\n const n = node as Record<string, unknown>;\n if (n.type === 'htmlElement' && n.tagName === 'img') {\n const attrs = n.attributes as Record<string, string> | undefined;\n if (attrs && typeof attrs.src === 'string' && attrs.src) {\n found.push({\n src: attrs.src,\n alt: typeof attrs.alt === 'string' ? attrs.alt : '',\n width: parseDim(attrs.width),\n height: parseDim(attrs.height),\n });\n }\n }\n if (Array.isArray(n.children)) fromHtml(n.children);\n }\n }\n\n function walk(node: unknown): void {\n if (found.length >= limit) return;\n if (!node || typeof node !== 'object') return;\n const n = node as Record<string, unknown>;\n if (n.type === 'image' && typeof n.url === 'string' && n.url) {\n found.push({ src: n.url, alt: typeof n.alt === 'string' ? n.alt : '' });\n return;\n }\n if ((n.type === 'htmlBlock' || n.type === 'htmlInline') && Array.isArray(n.htmlChildren)) {\n fromHtml(n.htmlChildren);\n }\n if (Array.isArray(n.children)) {\n for (const child of n.children) walk(child);\n }\n }\n\n for (const node of contents) {\n if (found.length >= limit) break;\n walk(node);\n }\n return found;\n}\n\n/** First image in a block's body, or null. */\nexport function extractFirstImage(contents: MarkdownBlockNode[] | undefined): FirstImage | null {\n return extractImages(contents, 1)[0] ?? null;\n}\n\n/** Extract table data (headers, rows, alignment) from block contents. */\nexport function extractTableFromContents(contents?: MarkdownBlockNode[]): {\n headers: string[];\n rows: string[][];\n align?: (('left' | 'right' | 'center') | null)[];\n} | null {\n if (!contents) return null;\n for (const node of contents) {\n if (node.type === 'table') {\n const table = node as MarkdownTable;\n const [headerRow, ...bodyRows] = table.children;\n if (!headerRow) return null;\n const headers = headerRow.children.map((cell) => extractPlainText(cell).trim());\n const rows = bodyRows.map((row) => row.children.map((cell) => extractPlainText(cell).trim()));\n return { headers, rows, align: table.align };\n }\n }\n return null;\n}\n\n/** First blockquote's plain text, or ''. */\nexport function extractBlockquoteText(contents?: MarkdownBlockNode[]): string {\n if (!contents) return '';\n for (const node of contents) {\n if (node.type === 'blockquote') return extractPlainText(node).trim();\n }\n return '';\n}\n\nexport interface DeriveTemplateInputsOptions {\n /**\n * Return visible placeholder values instead of `null` when an essential\n * input can't be derived (preview surfaces). Default false (strict).\n */\n placeholders?: boolean;\n}\n\n/**\n * Derive a template's inputs from a block's heading text and body nodes.\n * Returns the input fields to merge onto the block, or `null` in strict\n * mode when an essential input is missing.\n */\nexport function deriveTemplateInputs(\n templateName: string,\n headingText: string,\n contents: MarkdownBlockNode[] | undefined,\n options: DeriveTemplateInputsOptions = {},\n): Record<string, unknown> | null {\n const placeholders = options.placeholders === true;\n const bodyText = extractBodyPlainText(contents);\n\n switch (templateName) {\n case 'statHighlight':\n return { stat: headingText, description: bodyText || headingText };\n case 'quote': {\n const quote = extractBlockquoteText(contents) || bodyText || headingText;\n return { quote };\n }\n case 'fullBleedQuote':\n case 'pullQuote': {\n // These templates take `text` (not `quote`).\n const text = extractBlockquoteText(contents) || bodyText || headingText;\n return { text };\n }\n case 'factCard':\n return { fact: headingText, explanation: bodyText || headingText };\n case 'comparisonBar':\n return placeholders\n ? { leftLabel: 'A', leftValue: 60, rightLabel: 'B', rightValue: 40 }\n : null;\n case 'list': {\n const items = extractListItems(contents);\n if (items.length > 0) return { items };\n return placeholders ? { items: ['Item 1', 'Item 2', 'Item 3'] } : null;\n }\n case 'definitionCard':\n return { term: headingText, definition: bodyText || headingText };\n case 'dateEvent':\n return { date: headingText, description: bodyText || headingText };\n case 'dataTable': {\n const tableData = extractTableFromContents(contents);\n if (tableData) return tableData;\n return placeholders ? { headers: ['Column'], rows: [['Data']] } : null;\n }\n case 'imageWithCaption': {\n const img = extractFirstImage(contents);\n if (!img) return placeholders ? { caption: headingText } : null;\n return { imageSrc: img.src, imageAlt: img.alt || headingText, caption: headingText };\n }\n case 'photoGrid': {\n const images = extractImages(contents, 4);\n if (images.length < 2) return placeholders ? {} : null;\n return {\n images: images.map((i) => ({ src: i.src, alt: i.alt })),\n caption: headingText,\n };\n }\n case 'leftFeature':\n case 'rightFeature': {\n const img = extractFirstImage(contents);\n if (!img && !placeholders) return null;\n return {\n imageSrc: img?.src ?? '',\n imageAlt: img?.alt || headingText,\n imageWidth: img?.width,\n imageHeight: img?.height,\n title: headingText,\n body: bodyText,\n };\n }\n default:\n return placeholders ? {} : null;\n }\n}\n","/**\n * Frontmatter serialization for user-defined custom templates (layouts).\n *\n * Custom template definitions live in the document's YAML frontmatter\n * under the key `squisq-custom-templates`. They're stored as a single\n * **compact JSON** object keyed by template name:\n *\n * ```yaml\n * squisq-custom-templates: {\"hero\":{\"lb\":\"Hero Section\",\"ly\":[{\"ty\":\"text\",…}]}}\n * ```\n *\n * Squisq's frontmatter parser is line-based; a JSON object literal\n * round-trips through it verbatim (no leading quote to strip, single\n * line), so the value is written **unquoted** and stays human-readable\n * and diffable — important for a codebase edited by people and agents.\n *\n * The JSON is kept small by:\n * - keying definitions by `name` (drops the `name` field + array wrapper),\n * - omitting the viewport when it's the 1920×1080 default,\n * - renaming well-known property names to two-letter codes via\n * {@link LONG_TO_SHORT} (e.g. `fontSize` → `fz`).\n *\n * The rename is a generic, recursive, bijective pass: **any property not\n * in the map passes through unchanged**, so the format is lossless even\n * as the Layer schema grows — new fields are simply stored under their\n * full name until (optionally) added to the map.\n *\n * Back-compat: the reader still accepts the historical base64-of-JSON\n * payload and a structured array, so older documents load unchanged.\n * The first save migrates them to the compact form.\n *\n * The `encodeLayersForFrontmatter` / `decodeLayersFromFrontmatter` pair\n * handles a single Layer array for the editor's `dataLayers=\"…\"` Pandoc\n * param path; that path stays base64 (it's a different, per-block\n * mechanism) and is re-exported here for locality.\n */\n\nimport type { Layer } from '../schemas/Doc.js';\nimport type { CustomTemplateDefinition } from '../schemas/CustomTemplates.js';\nimport { FRONTMATTER_CUSTOM_TEMPLATES_KEY } from '../schemas/CustomTemplates.js';\n\nconst DEFAULT_VIEWPORT = { width: 1920, height: 1080 };\n\n/** Re-export for callers that need the canonical key spelling. */\nexport { FRONTMATTER_CUSTOM_TEMPLATES_KEY };\n\n// ─── Compact key codec ──────────────────────────────────────────────\n\n/**\n * Long → short property-name map. Values must be unique and must not\n * collide with any real key that passes through unmapped (those are\n * 1-char — `x`/`y`/`d`/`id`/`to` — or full words; the codes here are\n * 2-letter combos, so they never clash). A field literally named like a\n * code (e.g. a real `fz` key) would be the one corruption case — none\n * exist in the schema, and the round-trip test guards against it.\n */\nconst LONG_TO_SHORT: Readonly<Record<string, string>> = {\n // definition\n label: 'lb',\n description: 'ds',\n viewport: 'vp',\n layers: 'ly',\n name: 'nm',\n // layer\n type: 'ty',\n position: 'po',\n content: 'ct',\n animation: 'am',\n // position\n width: 'wd',\n height: 'hg',\n anchor: 'an',\n // text\n text: 'tx',\n style: 'sy',\n fontSize: 'fz',\n fontFamily: 'ff',\n fontWeight: 'fw',\n color: 'cl',\n textAlign: 'al',\n verticalAlign: 'vl',\n lineHeight: 'lh',\n shadow: 'sd',\n background: 'bg',\n backgroundOpacity: 'bo',\n backgroundGradient: 'bj',\n borderColor: 'bc',\n borderWidth: 'bw',\n borderStyle: 'bs',\n padding: 'pd',\n maxLines: 'mx',\n // shape / path\n shape: 'sp',\n fill: 'fl',\n fillOpacity: 'fo',\n gradient: 'gr',\n stroke: 'sk',\n strokeWidth: 'sw',\n borderRadius: 'br',\n shapeKind: 'kd',\n dasharray: 'da',\n arrow: 'ar',\n startMarker: 'st',\n endMarker: 'em',\n // image\n src: 'sc',\n alt: 'at',\n fit: 'ft',\n credit: 'cr',\n license: 'lc',\n // gradient\n from: 'fr',\n angle: 'ag',\n};\n\nconst SHORT_TO_LONG: Readonly<Record<string, string>> = Object.fromEntries(\n Object.entries(LONG_TO_SHORT).map(([long, short]) => [short, long]),\n);\n\n/** Recursively rename an object's keys via `map`, leaving unmapped keys. */\nfunction renameKeys(value: unknown, map: Readonly<Record<string, string>>): unknown {\n if (Array.isArray(value)) return value.map((v) => renameKeys(v, map));\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[map[k] ?? k] = renameKeys(v, map);\n }\n return out;\n }\n return value;\n}\n\nfunction isDefaultViewport(v: { width: number; height: number } | undefined): boolean {\n return !v || (v.width === DEFAULT_VIEWPORT.width && v.height === DEFAULT_VIEWPORT.height);\n}\n\n// ─── Single-Layer base64 codec (legacy `dataLayers` path) ───────────\n\n/**\n * Base64-encode a Layer array as JSON, UTF-8 safe. Used by the editor's\n * per-block `dataLayers=\"…\"` Pandoc param (not the doc-level template\n * list, which uses the compact JSON above).\n *\n * `btoa` only accepts Latin1 strings — any character outside that range\n * (e.g. an em-dash in a description) throws — so we round through\n * `TextEncoder`. Works in Node ≥18 and browsers.\n */\nexport function encodeLayersForFrontmatter(layers: readonly Layer[]): string {\n return utf8ToBase64(JSON.stringify(layers));\n}\n\n/** Inverse of {@link encodeLayersForFrontmatter}. Returns [] on parse failure. */\nexport function decodeLayersFromFrontmatter(encoded: string): Layer[] {\n try {\n const json = base64ToUtf8(encoded);\n const parsed = JSON.parse(json);\n return Array.isArray(parsed) ? (parsed as Layer[]) : [];\n } catch {\n return [];\n }\n}\n\n/** UTF-8 safe `btoa` — uses TextEncoder so arbitrary Unicode round-trips. */\nfunction utf8ToBase64(str: string): string {\n const bytes = new TextEncoder().encode(str);\n if (typeof globalThis.btoa === 'function') {\n let binary = '';\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);\n return globalThis.btoa(binary);\n }\n return Buffer.from(bytes).toString('base64');\n}\n\n/** UTF-8 safe `atob`. */\nfunction base64ToUtf8(b64: string): string {\n if (typeof globalThis.atob === 'function') {\n const binary = globalThis.atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n return Buffer.from(b64, 'base64').toString('utf-8');\n}\n\n// ─── Definition list read / write ───────────────────────────────────\n\n/**\n * Read the `squisq-custom-templates` frontmatter key and decode it into\n * an array of CustomTemplateDefinitions. Returns undefined when the key\n * is absent or unparseable so callers can omit the field from the Doc.\n *\n * Accepts, in order: the compact JSON object (current), a base64-JSON\n * string (legacy), and an already-structured array/object (in case a\n * richer YAML parser delivers it). Malformed payloads return undefined\n * rather than failing the whole doc load.\n */\nexport function readCustomTemplatesFromFrontmatter(\n frontmatter: Record<string, unknown> | undefined,\n): CustomTemplateDefinition[] | undefined {\n if (!frontmatter) return undefined;\n const candidates = normalizeCandidates(frontmatter[FRONTMATTER_CUSTOM_TEMPLATES_KEY]);\n if (!candidates) return undefined;\n const out: CustomTemplateDefinition[] = [];\n for (const entry of candidates) {\n if (!entry || typeof entry !== 'object') continue;\n const e = entry as Record<string, unknown>;\n if (typeof e.name !== 'string' || typeof e.label !== 'string') continue;\n if (!Array.isArray(e.layers)) continue;\n const def: CustomTemplateDefinition = {\n name: e.name,\n label: e.label,\n viewport: readViewport(e.viewport),\n layers: e.layers as Layer[],\n };\n if (typeof e.description === 'string') def.description = e.description;\n out.push(def);\n }\n return out.length > 0 ? out : undefined;\n}\n\n/**\n * Encode a list of custom template definitions into the compact JSON\n * object described in the module header. Returns undefined when the\n * input list is empty so callers can leave the key off the output.\n */\nexport function writeCustomTemplatesToFrontmatter(\n templates: readonly CustomTemplateDefinition[] | undefined,\n options?: { pretty?: boolean },\n): string | undefined {\n if (!templates || templates.length === 0) return undefined;\n const map: Record<string, Record<string, unknown>> = {};\n for (const def of templates) {\n const entry: Record<string, unknown> = { lb: def.label };\n if (def.description) entry.ds = def.description;\n if (!isDefaultViewport(def.viewport)) entry.vp = def.viewport;\n entry.ly = renameKeys(def.layers, LONG_TO_SHORT);\n map[def.name] = entry;\n }\n // Pretty output is multi-line, so it rides out as a YAML literal block\n // scalar (see `formatBlockScalar` / stringify's frontmatter path) and\n // parses back via `JSON.parse`. Default stays compact (single line) so\n // existing docs and snapshots are byte-unchanged. The compact key codec\n // (LONG_TO_SHORT) is applied in both modes.\n return JSON.stringify(map, null, options?.pretty ? 2 : undefined);\n}\n\nfunction readViewport(raw: unknown): { width: number; height: number } {\n if (raw && typeof raw === 'object') {\n const v = raw as Record<string, unknown>;\n const w = typeof v.width === 'number' ? v.width : DEFAULT_VIEWPORT.width;\n const h = typeof v.height === 'number' ? v.height : DEFAULT_VIEWPORT.height;\n return { width: w, height: h };\n }\n return DEFAULT_VIEWPORT;\n}\n\n/**\n * Expand the compact, name-keyed map into full-keyed definition objects\n * that {@link readCustomTemplatesFromFrontmatter}'s validation loop\n * understands. The `name` comes from the map key.\n */\nfunction expandCompactMap(map: Record<string, unknown>): unknown[] {\n return Object.entries(map).map(([name, raw]) => {\n const e = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n const def: Record<string, unknown> = {\n name,\n label: e.lb,\n viewport: e.vp ?? { ...DEFAULT_VIEWPORT },\n layers: renameKeys(e.ly ?? [], SHORT_TO_LONG),\n };\n if (typeof e.ds === 'string') def.description = e.ds;\n return def;\n });\n}\n\n/** Turn a parsed value into a list of full-keyed definition objects. */\nfunction fromParsed(parsed: unknown): unknown[] | null {\n if (Array.isArray(parsed)) return parsed; // legacy structured array\n if (parsed && typeof parsed === 'object')\n return expandCompactMap(parsed as Record<string, unknown>);\n return null;\n}\n\nfunction tryJson(s: string): unknown | undefined {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Normalize any supported payload shape into a list of full-keyed\n * definition objects. Returns null when the value is none of them.\n */\nfunction normalizeCandidates(raw: unknown): unknown[] | null {\n if (raw == null) return null;\n if (Array.isArray(raw)) return raw;\n if (typeof raw === 'object') return expandCompactMap(raw as Record<string, unknown>);\n if (typeof raw !== 'string') return null;\n\n // Current format: a JSON object/array literal stored verbatim.\n const direct = tryJson(raw.trim());\n if (direct !== undefined) return fromParsed(direct);\n\n // Legacy format: base64-encoded JSON.\n try {\n const decoded = tryJson(base64ToUtf8(raw));\n if (decoded !== undefined) return fromParsed(decoded);\n } catch {\n // not valid base64 either\n }\n return null;\n}\n","/**\n * Frontmatter serialization for user-defined custom themes.\n *\n * The theme analog of {@link ./customTemplatesFrontmatter.ts}. Custom Theme\n * definitions live in the document's YAML frontmatter under the key\n * `squisq-custom-themes`, stored as a single **compact JSON** object keyed\n * by theme id:\n *\n * ```yaml\n * squisq-custom-themes: {\"my-brand\":{\"schemaVersion\":\"1\",\"id\":\"my-brand\",…}}\n * ```\n *\n * Squisq's frontmatter parser is line-based; a JSON object literal\n * round-trips through it verbatim (single line, no leading quote to strip),\n * so the value is written **unquoted** and stays human-readable and diffable\n * — the same mechanism the custom-templates codec relies on.\n *\n * Only one theme is *active* per doc (selected by id via the `squisq-theme`\n * frontmatter key / `Doc.themeId`), but the payload is a keyed map — parallel\n * to custom templates — so a doc can carry a small catalog and switch between\n * them, and `resolveThemeForDoc` can resolve any of them doc-scoped.\n *\n * Malformed entries are dropped (via `validateTheme`) rather than failing the\n * whole doc load, matching the tolerant behavior of the templates reader.\n */\n\nimport type { Theme } from '../schemas/Theme.js';\nimport { FRONTMATTER_CUSTOM_THEMES_KEY } from '../schemas/Theme.js';\nimport { validateTheme } from '../schemas/themeValidator.js';\n\n/** Re-export for callers that need the canonical key spelling. */\nexport { FRONTMATTER_CUSTOM_THEMES_KEY };\n\n/**\n * Normalize the raw frontmatter value into an array of candidate objects.\n * Accepts, in order: a JSON string (current, from the line-based parser),\n * an already-parsed object map (keyed by id), and an array (in case a richer\n * YAML parser delivers one). Returns undefined when absent/unparseable.\n */\nfunction normalizeCandidates(raw: unknown): unknown[] | undefined {\n if (raw === undefined || raw === null) return undefined;\n let value: unknown = raw;\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n try {\n value = JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n }\n if (Array.isArray(value)) return value;\n if (value && typeof value === 'object') {\n return Object.values(value as Record<string, unknown>);\n }\n return undefined;\n}\n\n/**\n * Read the custom themes inlined into a document's frontmatter. Returns\n * undefined when the key is absent or holds no valid theme, so callers can\n * omit the field from the Doc.\n */\nexport function readCustomThemesFromFrontmatter(\n frontmatter: Record<string, unknown> | undefined,\n): Theme[] | undefined {\n if (!frontmatter) return undefined;\n const candidates = normalizeCandidates(frontmatter[FRONTMATTER_CUSTOM_THEMES_KEY]);\n if (!candidates) return undefined;\n const out: Theme[] = [];\n for (const entry of candidates) {\n const result = validateTheme(entry);\n if (result.valid && result.theme) out.push(result.theme);\n }\n return out.length > 0 ? out : undefined;\n}\n\n/**\n * Encode a list of custom themes into the compact JSON object described in\n * the module header (keyed by theme id). Returns undefined when the input is\n * empty so callers can leave the key off the output.\n */\nexport function writeCustomThemesToFrontmatter(\n themes: readonly Theme[] | undefined,\n options?: { pretty?: boolean },\n): string | undefined {\n if (!themes || themes.length === 0) return undefined;\n const map: Record<string, Theme> = {};\n for (const theme of themes) map[theme.id] = theme;\n // Pretty output is multi-line, so it serializes as a YAML literal block\n // scalar (see the markdown stringify frontmatter path) and reads back via\n // `JSON.parse`. Default stays compact (single line) so existing docs and\n // snapshots are byte-unchanged.\n return JSON.stringify(map, null, options?.pretty ? 2 : undefined);\n}\n","/**\n * Structured template data from markdown body content.\n *\n * Attribute strings on a heading annotation are fine for scalar params,\n * but tabular or nested inputs (dataTable rows, map markers, chart data)\n * don't pack well into a single quoted line. This module supplies the two\n * structured channels instead:\n *\n * 1. **Data fences** — a fenced code block whose info string is\n * `json data` or `yaml data`, placed in a heading's body content:\n *\n * ````markdown\n * ## Quarterly numbers {[dataTable]}\n *\n * ```json data\n * { \"headers\": [\"Q\", \"Revenue\"], \"rows\": [[\"Q1\", \"1.2M\"], [\"Q2\", \"1.4M\"]] }\n * ```\n * ````\n *\n * The parsed object lands on `block.templateData` and merges into the\n * template input (after defaults, before `{[…]}` string overrides).\n * The `data` marker keeps ordinary ```json code samples — which should\n * render as code — out of the template input.\n *\n * 2. **GFM tables** — for the `dataTable` template, the first table in the\n * section body supplies `headers` / `rows` / `align` when the author\n * didn't provide them explicitly.\n *\n * YAML support is a documented subset (this package has no YAML\n * dependency): top-level `key: scalar`, `key: [inline, array]`, `key:`\n * followed by an indented `- item` block sequence (scalars or inline\n * arrays), and `key:` followed by *one level* of indented `subkey: scalar`\n * lines (a nested map, e.g. `center:` + indented `lat:` / `lng:`). Deeper\n * nesting, and mixing list items with mapping keys under one key, are\n * rejected with a clear error — use a `json data` fence for those.\n */\n\nimport type {\n MarkdownBlockNode,\n MarkdownCodeBlock,\n MarkdownTable,\n MarkdownTableRow,\n} from '../markdown/types.js';\nimport { extractPlainText } from '../markdown/utils.js';\n\n// ============================================\n// Data fences\n// ============================================\n\n/** Languages accepted for data fences. */\nconst DATA_FENCE_LANGS = new Set(['json', 'yaml', 'yml']);\n\n/**\n * Whether a code block is a structured-data fence: lang `json`/`yaml`/`yml`\n * with the word `data` in the fence meta (` ```json data `).\n */\nexport function isDataFence(node: MarkdownCodeBlock): boolean {\n if (!node.lang || !DATA_FENCE_LANGS.has(node.lang.toLowerCase())) return false;\n const meta = node.meta ?? '';\n return /\\bdata\\b/.test(meta);\n}\n\nexport interface DataFenceParseResult {\n /** Parsed key→value map. Undefined when parsing failed. */\n data?: Record<string, unknown>;\n /** Parse failure description. Undefined when parsing succeeded. */\n error?: string;\n}\n\n/**\n * Parse a data fence's content into a key→value map. JSON fences must\n * contain a top-level object (arrays have no key to merge under); YAML\n * fences are parsed with {@link parseYamlSubset}.\n */\nexport function parseDataFence(node: MarkdownCodeBlock): DataFenceParseResult {\n const lang = node.lang?.toLowerCase();\n if (lang === 'json') {\n try {\n const parsed: unknown = JSON.parse(node.value);\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return { error: 'JSON data fence must contain a top-level object ({\"key\": …})' };\n }\n return { data: parsed as Record<string, unknown> };\n } catch (err: unknown) {\n return { error: `invalid JSON: ${err instanceof Error ? err.message : String(err)}` };\n }\n }\n try {\n return { data: parseYamlSubset(node.value) };\n } catch (err: unknown) {\n return { error: `invalid YAML: ${err instanceof Error ? err.message : String(err)}` };\n }\n}\n\n/**\n * Return a new contents array with the first data fence's code content\n * replaced by `data` (pretty-printed JSON). The fence node keeps its\n * language and `data` marker; only `value` changes. When no data fence\n * exists, a new ` ```json data ` fence is appended instead.\n *\n * Pure: neither `contents` nor its nodes are mutated. This is the write\n * half of the data-fence channel — editors update `block.templateData`\n * and call this to reflect the change back into `block.contents`.\n */\nexport function replaceDataFence(\n contents: MarkdownBlockNode[],\n data: Record<string, unknown>,\n): MarkdownBlockNode[] {\n const value = JSON.stringify(data, null, 2);\n const index = contents.findIndex((node) => node.type === 'code' && isDataFence(node));\n if (index < 0) {\n const fence: MarkdownCodeBlock = { type: 'code', lang: 'json', meta: 'data', value };\n return [...contents, fence];\n }\n const next = [...contents];\n next[index] = { ...(contents[index] as MarkdownCodeBlock), value };\n return next;\n}\n\n// ============================================\n// YAML subset parser\n// ============================================\n\n/** Parse a scalar token: quoted string, boolean, null, number, or string. */\nfunction parseScalar(raw: string): unknown {\n const trimmed = raw.trim();\n if (trimmed.length >= 2) {\n const q = trimmed[0];\n if ((q === '\"' || q === \"'\") && trimmed.endsWith(q)) {\n return trimmed.slice(1, -1);\n }\n }\n if (trimmed === 'true') return true;\n if (trimmed === 'false') return false;\n if (trimmed === 'null' || trimmed === '~') return null;\n if (trimmed !== '' && !isNaN(Number(trimmed))) return Number(trimmed);\n return trimmed;\n}\n\n/**\n * Split an inline-array interior on top-level commas, respecting quotes.\n */\nfunction splitInlineArray(interior: string): string[] {\n const items: string[] = [];\n let current = '';\n let quote: string | null = null;\n for (const ch of interior) {\n if (quote) {\n current += ch;\n if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n current += ch;\n } else if (ch === ',') {\n items.push(current);\n current = '';\n } else {\n current += ch;\n }\n }\n items.push(current);\n // A trailing comma (or empty interior) leaves an empty final item — drop it.\n return items.map((s) => s.trim()).filter((s) => s !== '');\n}\n\n/** Parse a value that may be an inline array `[a, b]` or a scalar. */\nfunction parseInlineValue(raw: string): unknown {\n const trimmed = raw.trim();\n if (trimmed.startsWith('[') && trimmed.endsWith(']')) {\n return splitInlineArray(trimmed.slice(1, -1)).map(parseScalar);\n }\n return parseScalar(trimmed);\n}\n\n/**\n * Parse the supported YAML subset (see module doc) into a key→value map.\n * Throws an Error with a line-anchored message on anything outside the\n * subset, so callers can surface a precise diagnostic.\n */\nexport function parseYamlSubset(src: string): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const lines = src.split('\\n');\n\n // Open block under the most recent `key:` line: whether its children are a\n // block sequence (`- …`) or a nested map (`subkey: …`) is decided by the\n // first indented child; `childIndent` pins the one allowed child depth.\n let pendingKey: string | null = null;\n let pendingKind: 'list' | 'map' | null = null;\n let childIndent = -1;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const indent = line.length - line.trimStart().length;\n\n if (indent > 0) {\n if (!pendingKey) {\n throw new Error(`line ${i + 1}: unexpected indented line without a preceding \"key:\" line`);\n }\n if (childIndent < 0) childIndent = indent;\n if (indent > childIndent) {\n throw new Error(\n `line ${i + 1}: nesting deeper than one level is not supported — use a \\`\\`\\`json data fence for nested input`,\n );\n }\n\n if (trimmed.startsWith('- ')) {\n if (pendingKind === 'map') {\n throw new Error(\n `line ${i + 1}: cannot mix list items and mapping keys under \"${pendingKey}\"`,\n );\n }\n pendingKind = 'list';\n (result[pendingKey] as unknown[]).push(parseInlineValue(trimmed.slice(2)));\n continue;\n }\n\n // Otherwise this is a nested `subkey: value` mapping line.\n if (pendingKind === 'list') {\n throw new Error(\n `line ${i + 1}: cannot mix mapping keys and list items under \"${pendingKey}\"`,\n );\n }\n const subColon = trimmed.indexOf(':');\n if (subColon < 1) {\n throw new Error(`line ${i + 1}: expected \"key: value\"`);\n }\n const subValue = trimmed.slice(subColon + 1).trim();\n if (subValue === '') {\n // A nested `subkey:` opening its own block would be a second level.\n throw new Error(\n `line ${i + 1}: nesting deeper than one level is not supported — use a \\`\\`\\`json data fence for nested input`,\n );\n }\n if (pendingKind !== 'map') {\n pendingKind = 'map';\n result[pendingKey] = {};\n }\n (result[pendingKey] as Record<string, unknown>)[trimmed.slice(0, subColon).trim()] =\n parseInlineValue(subValue);\n continue;\n }\n\n pendingKey = null;\n pendingKind = null;\n childIndent = -1;\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx < 1) {\n throw new Error(`line ${i + 1}: expected \"key: value\"`);\n }\n const key = trimmed.slice(0, colonIdx).trim();\n const rawValue = trimmed.slice(colonIdx + 1).trim();\n\n if (rawValue === '') {\n // `key:` opens a block: an indented `- …` sequence or a `subkey: …`\n // map. Default to an empty list so a `key:` with no children stays\n // back-compatible (`{ key: [] }`); the map branch replaces it wholesale.\n result[key] = [];\n pendingKey = key;\n } else {\n result[key] = parseInlineValue(rawValue);\n }\n }\n\n return result;\n}\n\n// ============================================\n// GFM table extraction\n// ============================================\n\nexport interface ExtractedTableData {\n headers: string[];\n rows: string[][];\n align?: (('left' | 'right' | 'center') | null)[];\n}\n\n/** Find the first GFM table among a block's body contents. */\nexport function findFirstTable(\n contents: MarkdownBlockNode[] | undefined,\n): MarkdownTable | undefined {\n return contents?.find((n): n is MarkdownTable => n.type === 'table');\n}\n\n/**\n * Extract `headers` / `rows` / `align` (the `dataTable` input shape) from a\n * GFM table node. The first row is the header row.\n */\nexport function extractTableData(table: MarkdownTable): ExtractedTableData {\n const toCells = (row: MarkdownTableRow): string[] =>\n row.children.map((cell) => extractPlainText(cell).trim());\n\n const [headerRow, ...bodyRows] = table.children;\n return {\n headers: headerRow ? toCells(headerRow) : [],\n rows: bodyRows.map(toCells),\n ...(table.align && table.align.some((a) => a != null) ? { align: table.align } : {}),\n };\n}\n","/**\n * Standalone-paragraph annotation parser.\n *\n * A paragraph whose entire trimmed text is exactly a `{[…]}` annotation\n * (nothing before or after) is a *standalone annotation*. Two features share\n * this shape:\n *\n * - **Media annotations** (`{[audio …]}` / `{[video …]}`) — lifted into typed\n * {@link import('../schemas/Media.js').MediaClip}s by `mediaAnnotations.ts`.\n * - **Standalone template blocks** (`{[quote]}`, `{[statHighlight …]}`, …) —\n * turned into heading-less {@link import('../schemas/Doc.js').Block}s by\n * `annotationBlocks.ts`.\n *\n * Both call the single parser here so the annotation grammar (quoting,\n * escaping, first-token-is-template) stays identical to the heading form.\n */\n\nimport {\n matchTrailingTemplateAnnotation,\n tokenizeAttrTokens,\n splitKeyValueToken,\n} from '../markdown/attrTokens.js';\nimport type { MarkdownBlockNode } from '../markdown/types.js';\n\n/** The template name + params parsed out of a standalone `{[…]}` paragraph. */\nexport interface ParsedAnnotation {\n /** First bare token, when the annotation leads with one (e.g. `quote`). */\n template?: string;\n /** Remaining `key=value` tokens, unquoted/unescaped. */\n params: Record<string, string>;\n}\n\n/**\n * Parse a paragraph node that is *exactly* a `{[…]}` annotation (nothing\n * before or after). Returns the template + params, or null when the node\n * isn't a standalone annotation (not a paragraph, more than one child, a\n * non-text child such as a resolved inline icon, or trailing text).\n */\nexport function parseStandaloneAnnotation(node: MarkdownBlockNode): ParsedAnnotation | null {\n if (node.type !== 'paragraph') return null;\n const children = node.children ?? [];\n if (children.length !== 1 || children[0].type !== 'text') return null;\n const text = children[0].value.trim();\n const match = matchTrailingTemplateAnnotation(text);\n // index 0 means the whole (trimmed) paragraph is the annotation.\n if (!match || match.index !== 0) return null;\n\n const tokens = tokenizeAttrTokens(match.inner.trim());\n const firstIsParam = tokens.length > 0 && tokens[0].indexOf('=') > 0;\n const template = firstIsParam || tokens.length === 0 ? undefined : tokens[0];\n const startIdx = firstIsParam ? 0 : 1;\n const params: Record<string, string> = {};\n for (let i = startIdx; i < tokens.length; i++) {\n const kv = splitKeyValueToken(tokens[i]);\n if (kv) params[kv.key] = kv.value;\n }\n return { template, params };\n}\n","/**\n * Media-annotation extraction.\n *\n * Lifts standalone body-level media annotations — `{[audio …]}` / `{[video …]}`\n * — out of a block's markdown contents into typed {@link MediaClip}s. A block\n * can hold several clips (unlike the single heading annotation). An annotation\n * flagged `anchor=document` (or `span=document`) becomes a document-spanning\n * clip instead. The annotation grammar is the shared one used for heading\n * annotations, so quoting/escaping behave identically.\n */\n\nimport { parseTimeSeconds } from '../markdown/annotationCoercion.js';\nimport type { MarkdownBlockNode } from '../markdown/types.js';\nimport type { MediaClip } from '../schemas/Media.js';\nimport { parseStandaloneAnnotation, type ParsedAnnotation } from './standaloneAnnotation.js';\n\n/** Annotation template names that produce a timed media clip. */\nconst MEDIA_TEMPLATES = new Set(['audio', 'video', 'media']);\n\n/**\n * Whether a standalone-annotation template name is a media name\n * (`audio`/`video`/`media`). The standalone template-block extractor uses\n * this to leave media annotations to {@link extractMediaFromContents}.\n */\nexport function isMediaAnnotationName(name: string | undefined): boolean {\n return name != null && MEDIA_TEMPLATES.has(name);\n}\n\nfunction time(raw: string | undefined): number | null {\n if (raw == null) return null;\n return parseTimeSeconds(raw);\n}\n\n/** Build a {@link MediaClip} from a parsed media annotation, or null if invalid. */\nfunction toMediaClip(parsed: ParsedAnnotation, id: string): MediaClip | null {\n const { template, params } = parsed;\n if (!template || !MEDIA_TEMPLATES.has(template)) return null;\n const src = params.src;\n if (!src) return null;\n\n const kind: MediaClip['kind'] = template === 'video' ? 'video' : 'audio';\n const anchor: MediaClip['anchor'] =\n params.anchor === 'document' || params.span === 'document' ? 'document' : 'block';\n // `startAt` is canonical; `startTime` is accepted as an alias (it is the\n // heading-attribute spelling, so authors reach for it here too).\n const clip: MediaClip = {\n id,\n src,\n kind,\n startAt: time(params.startAt ?? params.startTime) ?? 0,\n anchor,\n };\n\n const clipStart = time(params.clipStart);\n const clipEnd = time(params.clipEnd);\n if (clipStart != null) clip.clipStart = clipStart;\n if (clipEnd != null) clip.clipEnd = clipEnd;\n // `spillover` or `spillover=true` → true; absent/`false` → omitted.\n if (params.spillover === 'true' || params.spillover === '') clip.spillover = true;\n\n return clip;\n}\n\nexport interface MediaExtraction {\n /** Block-anchored clips (anchor='block'). */\n media: MediaClip[];\n /** Document-spanning clips (anchor='document'). */\n documentMedia: MediaClip[];\n /** Contents with the lifted media-annotation paragraphs removed. */\n remaining: MarkdownBlockNode[];\n}\n\n/**\n * Walk `contents`, lifting every standalone media annotation into a\n * {@link MediaClip}. `makeId(src, index)` produces stable clip ids.\n */\nexport function extractMediaFromContents(\n contents: MarkdownBlockNode[] | undefined,\n makeId: (src: string, index: number) => string,\n): MediaExtraction {\n const media: MediaClip[] = [];\n const documentMedia: MediaClip[] = [];\n const remaining: MarkdownBlockNode[] = [];\n let index = 0;\n\n for (const node of contents ?? []) {\n const parsed = parseStandaloneAnnotation(node);\n const clip = parsed ? toMediaClip(parsed, '') : null;\n if (parsed && clip) {\n clip.id = makeId(clip.src, index++);\n const line = node.position?.start.line;\n if (line != null) clip.sourceLine = line;\n (clip.anchor === 'document' ? documentMedia : media).push(clip);\n } else {\n remaining.push(node);\n }\n }\n\n return { media, documentMedia, remaining };\n}\n","/**\n * Standalone template-annotation block extraction.\n *\n * A paragraph whose entire trimmed text is exactly `{[templateName key=value …]}`\n * — where `templateName` is not a media name (media annotations are extracted\n * first, into typed clips) — becomes a *heading-less template block*. The body\n * nodes that follow it, up to the next heading or the next standalone\n * annotation, become that block's `contents`.\n *\n * This is the block analog of {@link import('./mediaAnnotations.js').extractMediaFromContents}:\n * both walk a block's body `contents` and lift standalone `{[…]}` paragraphs\n * out, sharing the {@link parseStandaloneAnnotation} parser. Runs AFTER media\n * extraction (so media names keep their meaning) and BEFORE structured-data\n * parsing (so `json data` / `yaml data` fences attach to the new blocks).\n */\n\nimport type { Block } from '../schemas/Doc.js';\nimport type { MarkdownBlockNode } from '../markdown/types.js';\nimport { coerceAnnotationValues, type CoercedBlockMeta } from '../markdown/annotationCoercion.js';\nimport { resolveTemplateName } from './templates/index.js';\nimport { isMediaAnnotationName } from './mediaAnnotations.js';\nimport { parseStandaloneAnnotation, type ParsedAnnotation } from './standaloneAnnotation.js';\n\n/** Configuration for {@link extractTemplateBlocksFromContents}. */\nexport interface StandaloneBlockOptions {\n /** Produce a unique block id for a parsed standalone annotation. */\n makeId: (parsed: ParsedAnnotation) => string;\n /** Initial `duration` for a produced block (the timing pass overrides it). */\n defaultDuration: number;\n}\n\n/** The split produced by {@link extractTemplateBlocksFromContents}. */\nexport interface TemplateBlockExtraction {\n /** Contents before the first standalone template annotation (stay on the\n * containing block). */\n leading: MarkdownBlockNode[];\n /** Heading-less blocks, each carrying the body nodes that followed its\n * annotation up to the next annotation / end of contents. */\n blocks: Block[];\n}\n\n/**\n * Apply coerced block-meta values onto a produced block. Mirrors\n * `markdownToDoc`'s `applyBlockMeta` so a standalone `{[quote duration=8]}`\n * honors the same block-meta keys a heading `{[…]}` annotation would.\n */\nfunction applyBlockMeta(block: Block, m: CoercedBlockMeta): void {\n if (m.x != null) block.x = m.x;\n if (m.y != null) block.y = m.y;\n if (m.startTime != null) block.startTime = m.startTime;\n if (m.duration != null) block.duration = m.duration;\n if (m.connectsTo) block.connectsTo = m.connectsTo;\n if (m.transition) block.transition = m.transition;\n}\n\n/** Build a heading-less block from a parsed standalone template annotation. */\nfunction makeStandaloneBlock(parsed: ParsedAnnotation, options: StandaloneBlockOptions): Block {\n // `parsed.template` is guaranteed defined by the caller's guard.\n const rawTemplate = parsed.template as string;\n const params = parsed.params;\n const hasParams = Object.keys(params).length > 0;\n\n const block: Block = {\n id: options.makeId(parsed),\n startTime: 0,\n duration: options.defaultDuration,\n audioSegment: 0,\n template: resolveTemplateName(rawTemplate),\n standaloneAnnotation: true,\n // Keep the raw (un-resolved) name so validation can report the exact\n // requested name — symmetric with `sourceHeading.templateAnnotation`.\n sourceAnnotation: { template: rawTemplate, ...(hasParams ? { params } : {}) },\n // Only carry a display title when the author supplied one.\n ...(params.title ? { title: params.title } : {}),\n };\n\n if (hasParams) {\n block.templateOverrides = params;\n applyBlockMeta(block, coerceAnnotationValues(params).blockMeta);\n }\n\n return block;\n}\n\n/**\n * Walk `contents`, lifting standalone template-annotation paragraphs into\n * heading-less blocks. Nodes after an annotation (until the next annotation\n * or the end of `contents`) attach to that block. Media annotations are left\n * in place — they are extracted separately, before this runs. `contents` is\n * not mutated; the split is returned.\n */\nexport function extractTemplateBlocksFromContents(\n contents: MarkdownBlockNode[] | undefined,\n options: StandaloneBlockOptions,\n): TemplateBlockExtraction {\n const leading: MarkdownBlockNode[] = [];\n const blocks: Block[] = [];\n let current: Block | null = null;\n\n for (const node of contents ?? []) {\n const parsed = parseStandaloneAnnotation(node);\n const isTemplateAnnotation =\n parsed != null && parsed.template != null && !isMediaAnnotationName(parsed.template);\n\n if (isTemplateAnnotation) {\n const block = makeStandaloneBlock(parsed as ParsedAnnotation, options);\n blocks.push(block);\n current = block;\n } else if (current) {\n (current.contents ??= []).push(node);\n } else {\n leading.push(node);\n }\n }\n\n return { leading, blocks };\n}\n","/**\n * Markdown → Doc Conversion\n *\n * Converts a MarkdownDocument into a hierarchical Doc whose Block tree\n * mirrors the heading structure of the markdown. Every heading (H1–H6)\n * becomes a Block; body content between headings populates `contents`;\n * sub-headings nest as `children`.\n *\n * This enables using BlockTemplates to supply alternate visualizations\n * for each section of a markdown document.\n *\n * @example\n * ```ts\n * import { parseMarkdown } from '@bendyline/squisq/markdown';\n * import { markdownToDoc } from '@bendyline/squisq/doc';\n *\n * const md = parseMarkdown('# Intro\\n\\nHello world\\n\\n## Details\\n\\nMore text');\n * const doc = markdownToDoc(md);\n * // doc.blocks[0].sourceHeading.depth === 1 (\"# Intro\")\n * // doc.blocks[0].contents → [paragraph(\"Hello world\")]\n * // doc.blocks[0].children[0].sourceHeading.depth === 2 (\"## Details\")\n * ```\n */\n\nimport type {\n Doc,\n Block,\n CaptionTrack,\n CaptionPhrase,\n StartBlockConfig,\n DocDiagnostic,\n} from '../schemas/Doc.js';\nimport { readCustomTemplatesFromFrontmatter } from './customTemplatesFrontmatter.js';\nimport { readCustomThemesFromFrontmatter } from './customThemesFrontmatter.js';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownHeading,\n MarkdownNode,\n HtmlNode,\n} from '../markdown/types.js';\nimport { extractPlainText } from '../markdown/utils.js';\nimport { coerceAnnotationValues, type CoercedBlockMeta } from '../markdown/annotationCoercion.js';\nimport { estimateReadingTime } from '../timing/readingTime.js';\nimport { resolveTemplateName, isContainerTemplate } from './templates/index.js';\nimport { isDataFence, parseDataFence, findFirstTable, extractTableData } from './structuredData.js';\nimport { extractMediaFromContents } from './mediaAnnotations.js';\nimport { extractTemplateBlocksFromContents } from './annotationBlocks.js';\nimport type { ParsedAnnotation } from './standaloneAnnotation.js';\nimport { profileBlockContents, pickAutoTemplate } from '../recommend/templates.js';\nimport { deriveTemplateInputs } from './templateInputs.js';\nimport type { MediaClip } from '../schemas/Media.js';\n\n// ============================================\n// Options\n// ============================================\n\n/**\n * Options for markdownToDoc().\n */\nexport interface MarkdownToDocOptions {\n /** Article ID for the generated Doc. Default: 'markdown-doc' */\n articleId?: string;\n\n /** Default template name for heading blocks. Default: 'sectionHeader' */\n defaultTemplate?: string;\n\n /** Default duration per block in seconds. Default: 5 */\n defaultDuration?: number;\n\n /** Custom ID generator. Receives the heading node and its index. */\n generateId?: (heading: MarkdownHeading, index: number) => string;\n\n /**\n * Whether to auto-generate a cover startBlock from the first H1 heading.\n * When true (default), a StartBlockConfig is created using the first H1's\n * text as the title. If the document contains an image, the first image\n * is used as the hero. Set to false to suppress automatic cover generation.\n */\n generateCoverBlock?: boolean;\n\n /**\n * Timestamp recorded as `captions.generatedAt`. When omitted, the field\n * is left unset so that conversion is fully deterministic — the same\n * markdown always converts to an identical Doc (important for snapshot\n * tests, caching, and agents that verify their output by re-converting).\n */\n captionsGeneratedAt?: string;\n\n /**\n * Content-aware template auto-picking for unannotated headings\n * (default: true). When a heading block carries a strong content signal\n * — a table, images, a blockquote, a list, a stat-looking line — the\n * matching template is applied (with inputs derived from the body)\n * instead of the structural `defaultTemplate`. Explicit `{[template]}`\n * annotations always win. Authors can also disable per document with\n * frontmatter `squisq-auto-templates: false`.\n */\n autoTemplates?: boolean;\n}\n\n// ============================================\n// ID Generation\n// ============================================\n\n/**\n * Convert text to a URL-friendly slug.\n * \"Getting Started & More\" → \"getting-started-more\"\n */\nconst SLUG_NON_WORD_RE = /[^\\w\\s-]/g;\nconst SLUG_SPACES_RE = /[\\s_]+/g;\nconst SLUG_MULTI_HYPHEN_RE = /-+/g;\nconst SLUG_TRIM_HYPHEN_RE = /^-|-$/g;\n\nfunction slugify(text: string): string {\n return (\n text\n .toLowerCase()\n .replace(SLUG_NON_WORD_RE, '') // Remove non-word chars (except spaces and hyphens)\n .replace(SLUG_SPACES_RE, '-') // Replace spaces/underscores with hyphens\n .replace(SLUG_MULTI_HYPHEN_RE, '-') // Collapse multiple hyphens\n .replace(SLUG_TRIM_HYPHEN_RE, '') || // Trim leading/trailing hyphens\n 'block'\n ); // Fallback for empty result\n}\n\n/**\n * Creates an ID generator that produces unique slugified IDs.\n * Appends -2, -3, etc. for duplicate headings. `reserve()` registers an\n * author-pinned `{#id}` so a later heading whose slug matches doesn't\n * collide with it.\n */\nfunction createIdGenerator() {\n const used = new Map<string, number>();\n\n const generate = (heading: MarkdownHeading, _index: number): string => {\n const text = extractPlainText(heading);\n const base = slugify(text);\n const count = used.get(base) ?? 0;\n used.set(base, count + 1);\n\n if (count === 0) return base;\n return `${base}-${count + 1}`;\n };\n\n generate.reserve = (id: string): void => {\n used.set(id, Math.max(used.get(id) ?? 0, 1));\n };\n\n // Slug + dedupe from arbitrary text (used for heading-less standalone\n // annotation blocks, which have no MarkdownHeading to slug from). Shares\n // the same `used` map so a standalone id can't collide with a heading id.\n generate.fromText = (text: string): string => {\n const base = slugify(text);\n const count = used.get(base) ?? 0;\n used.set(base, count + 1);\n return count === 0 ? base : `${base}-${count + 1}`;\n };\n\n return generate;\n}\n\n// ============================================\n// Core Conversion\n// ============================================\n\n/**\n * Convert a MarkdownDocument into a Doc with a heading-driven Block hierarchy.\n *\n * **Algorithm:**\n * 1. Walk the document's top-level children sequentially\n * 2. Each MarkdownHeading starts a new block at its depth level\n * 3. Non-heading nodes accumulate as `contents` on the current block\n * 4. Sub-headings (deeper depth) nest as `children` of the current block\n * 5. Same-level or shallower headings close the current block and start a sibling/ancestor\n * 6. Content before the first heading goes into a \"preamble\" block with no sourceHeading\n *\n * @param markdownDoc - A parsed MarkdownDocument\n * @param options - Conversion options\n * @returns A Doc whose blocks mirror the markdown heading structure\n */\n/**\n * Apply coerced block-meta values onto a block's typed fields. Only keys\n * actually present are written, so a later caller (the Pandoc attribute\n * block) can override values supplied by the squiggly `{[…]}` annotation.\n */\nfunction applyBlockMeta(block: Block, m: CoercedBlockMeta): void {\n if (m.x != null) block.x = m.x;\n if (m.y != null) block.y = m.y;\n if (m.startTime != null) block.startTime = m.startTime;\n if (m.duration != null) block.duration = m.duration;\n if (m.connectsTo) block.connectsTo = m.connectsTo;\n if (m.transition) block.transition = m.transition;\n}\n\n/**\n * The block-meta an author pinned on a heading, drawn from either the\n * Pandoc `{key=value}` block or the squiggly `{[key=value]}` annotation\n * (Pandoc wins on conflict — same precedence as {@link makeBlock}). Used by\n * the timing passes to tell \"author pinned this\" from \"derive it\".\n */\nfunction pinnedHeadingMeta(heading: MarkdownHeading | undefined): CoercedBlockMeta {\n if (!heading) return {};\n const fromAnnotation = heading.templateAnnotation?.params\n ? coerceAnnotationValues(heading.templateAnnotation.params).blockMeta\n : {};\n const fromPandoc = heading.attributes?.blockMeta ?? {};\n return { ...fromAnnotation, ...fromPandoc };\n}\n\n/**\n * Pinned block-meta for any block: the heading pins ({@link pinnedHeadingMeta})\n * for heading blocks, or the standalone-annotation params for heading-less\n * standalone blocks. Lets a standalone `{[quote duration=8]}` pin timing the\n * same way a heading annotation would.\n */\nfunction pinnedMeta(block: Block): CoercedBlockMeta {\n if (block.sourceHeading) return pinnedHeadingMeta(block.sourceHeading);\n if (block.sourceAnnotation?.params) {\n return coerceAnnotationValues(block.sourceAnnotation.params).blockMeta;\n }\n return {};\n}\n\n/**\n * Rebuild a block list, lifting standalone `{[…]}` template annotations out of\n * each block's body into heading-less sibling blocks (recursing into children\n * first). See {@link extractTemplateBlocksFromContents}.\n */\nfunction expandStandaloneAnnotations(\n blocks: Block[],\n makeId: (parsed: ParsedAnnotation) => string,\n defaultDuration: number,\n): Block[] {\n const out: Block[] = [];\n for (const block of blocks) {\n if (block.children && block.children.length > 0) {\n block.children = expandStandaloneAnnotations(block.children, makeId, defaultDuration);\n }\n const { leading, blocks: produced } = extractTemplateBlocksFromContents(block.contents, {\n makeId,\n defaultDuration,\n });\n if (produced.length > 0) {\n block.contents = leading;\n }\n out.push(block, ...produced);\n }\n return out;\n}\n\nexport function markdownToDoc(markdownDoc: MarkdownDocument, options?: MarkdownToDocOptions): Doc {\n const articleId = options?.articleId ?? 'markdown-doc';\n const defaultTemplate = options?.defaultTemplate ?? 'sectionHeader';\n const defaultDuration = options?.defaultDuration ?? 5;\n const idGenerator = options?.generateId ? null : createIdGenerator();\n const generateId = options?.generateId ?? idGenerator!;\n\n let rootBlocks: Block[] = [];\n const diagnostics: DocDiagnostic[] = [];\n let headingIndex = 0;\n\n // Stack tracks the nesting context: each entry is a block and its heading depth.\n // We push when we go deeper, pop when we come back up.\n const stack: Array<{ block: Block; depth: number }> = [];\n\n // Accumulator for content nodes that appear before any heading (preamble)\n // or between the current heading and the next heading/sub-heading.\n let pendingContents: MarkdownBlockNode[] = [];\n let currentBlock: Block | null = null;\n\n function flushContents() {\n if (currentBlock && pendingContents.length > 0) {\n currentBlock.contents = (currentBlock.contents ?? []).concat(pendingContents);\n pendingContents = [];\n }\n }\n\n function makeBlock(heading: MarkdownHeading | null): Block {\n // Pandoc `{#id}` overrides slug-from-heading when present. Pinned ids\n // are reserved in the slug generator so a later un-pinned heading with\n // the same slugified text doesn't collide.\n const pandocId = heading?.attributes?.id;\n const id = pandocId ? pandocId : heading ? generateId(heading, headingIndex++) : 'preamble';\n if (pandocId && idGenerator) {\n idGenerator.reserve(pandocId);\n }\n\n // Use template from annotation if present, otherwise fall back to default.\n // Legacy template ids (e.g. `titleBlock`) are normalized to their canonical\n // short form (`title`) so downstream comparisons can rely on the new names\n // without sprinkling alias resolution everywhere.\n const annotation = heading?.templateAnnotation;\n const rawTemplate = annotation?.template ?? (heading ? defaultTemplate : undefined);\n const template = rawTemplate != null ? resolveTemplateName(rawTemplate) : undefined;\n\n // Extract heading text so templates (e.g. sectionHeader) that expect a\n // `title` property receive it without having to reach into sourceHeading.\n const title = heading ? extractPlainText(heading) : undefined;\n\n const block: Block = {\n id,\n startTime: 0,\n duration: defaultDuration,\n audioSegment: 0,\n template,\n ...(heading ? { sourceHeading: heading } : {}),\n ...(title ? { title } : {}),\n };\n\n // {[…]} template params → templateOverrides (template-specific overrides).\n // Known block-meta keys (duration, startTime, x, y, connectsTo) are *also*\n // honored in the squiggly form, so an author — or the timeline editor —\n // can write `{[duration=8]}` instead of the Pandoc `{duration=8}` block.\n // The raw params still flow to `templateOverrides` for round-tripping; the\n // Pandoc attribute block applied below wins when a key appears in both.\n if (annotation?.params) {\n block.templateOverrides = annotation.params;\n applyBlockMeta(block, coerceAnnotationValues(annotation.params).blockMeta);\n }\n\n // Pandoc {#id .class key=value} attributes → block-level fields.\n const attrs = heading?.attributes;\n if (attrs) {\n if (attrs.blockMeta) {\n applyBlockMeta(block, attrs.blockMeta);\n }\n if (attrs.classes && attrs.classes.length > 0) {\n block.classes = attrs.classes;\n }\n if (attrs.metadata && Object.keys(attrs.metadata).length > 0) {\n block.metadata = attrs.metadata;\n }\n }\n\n return block;\n }\n\n for (const node of markdownDoc.children) {\n if (node.type === 'heading') {\n const heading = node as MarkdownHeading;\n const depth = heading.depth;\n\n // Flush any accumulated content to the current block\n flushContents();\n\n // Create the new block for this heading\n const newBlock = makeBlock(heading);\n\n if (stack.length === 0) {\n // If there's a preamble block with no heading, push it first\n if (currentBlock && !currentBlock.sourceHeading) {\n rootBlocks.push(currentBlock);\n }\n\n // This is a root-level block (or the first heading)\n rootBlocks.push(newBlock);\n stack.push({ block: newBlock, depth });\n } else {\n // Find where this heading fits in the hierarchy\n // Pop blocks from the stack until we find a parent with lower depth\n while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {\n stack.pop();\n }\n\n if (stack.length === 0) {\n // Same level as root or shallower — new root block\n rootBlocks.push(newBlock);\n stack.push({ block: newBlock, depth });\n } else {\n // Deeper — child of the current stack top\n const parent = stack[stack.length - 1].block;\n if (!parent.children) parent.children = [];\n parent.children.push(newBlock);\n stack.push({ block: newBlock, depth });\n }\n }\n\n currentBlock = newBlock;\n } else {\n // Non-heading block node — accumulate as content\n if (!currentBlock) {\n // Content before any heading → create preamble block\n currentBlock = makeBlock(null);\n }\n pendingContents.push(node);\n }\n }\n\n // Flush remaining content\n flushContents();\n\n // If we only had preamble content (no headings at all), push it\n if (currentBlock && !currentBlock.sourceHeading && !rootBlocks.includes(currentBlock)) {\n rootBlocks.push(currentBlock);\n }\n\n // Lift standalone body-level media annotations (`{[audio …]}` / `{[video …]}`)\n // into typed clips: block-anchored clips ride on `block.media`; clips flagged\n // `anchor=document` collect into the doc-level `documentMedia`. Done before\n // the reading-time pass so annotation text doesn't inflate block durations.\n const documentMedia: MediaClip[] = [];\n for (const block of flattenBlocks(rootBlocks)) {\n const {\n media,\n documentMedia: docMedia,\n remaining,\n } = extractMediaFromContents(\n block.contents,\n (src, i) => `${block.id}-media-${i}-${src.replace(/[^a-zA-Z0-9]+/g, '-')}`,\n );\n if (media.length > 0) block.media = media;\n if (docMedia.length > 0) documentMedia.push(...docMedia);\n if (block.contents) block.contents = remaining;\n }\n\n // A preamble block that held only document-media annotations is now empty —\n // drop it so a doc-spanning narration doesn't leave a phantom 5s block at the\n // top of the timeline. Only the heading-less preamble (rootBlocks[0]) can be\n // emptied this way.\n if (\n rootBlocks.length > 0 &&\n !rootBlocks[0].sourceHeading &&\n (rootBlocks[0].contents?.length ?? 0) === 0 &&\n (rootBlocks[0].children?.length ?? 0) === 0 &&\n !rootBlocks[0].media\n ) {\n rootBlocks.shift();\n }\n\n // Standalone `{[templateName …]}` paragraphs (non-media) become heading-less\n // template blocks; the body after each one, up to the next annotation,\n // becomes its `contents`. Done after media extraction (so media names keep\n // their meaning) and before structured-data parsing (so `json data` fences\n // attach to the produced blocks). Produced blocks are inserted as siblings\n // following the block whose body held the annotation.\n const makeStandaloneId = (parsed: ParsedAnnotation): string => {\n const base = parsed.params.title ?? parsed.template ?? 'block';\n return idGenerator ? idGenerator.fromText(base) : slugify(base);\n };\n rootBlocks = expandStandaloneAnnotations(rootBlocks, makeStandaloneId, defaultDuration);\n // A leading preamble whose entire body was consumed into standalone blocks\n // is now empty — drop it so it doesn't leave a phantom slide.\n if (\n rootBlocks.length > 0 &&\n !rootBlocks[0].sourceHeading &&\n !rootBlocks[0].standaloneAnnotation &&\n (rootBlocks[0].contents?.length ?? 0) === 0 &&\n (rootBlocks[0].children?.length ?? 0) === 0 &&\n !rootBlocks[0].media\n ) {\n rootBlocks.shift();\n }\n\n const allBlocks = flattenBlocks(rootBlocks);\n\n // Structured template data: ```json data / ```yaml data fences in a\n // block's body parse into `templateData`; for dataTable blocks the first\n // GFM table supplies headers/rows when not explicitly provided. Parse\n // failures degrade gracefully (the fence stays visible as code) and are\n // recorded as diagnostics.\n for (const block of allBlocks) {\n applyStructuredData(block, diagnostics);\n }\n\n // Content-aware template auto-pick (default on): unannotated heading\n // blocks whose body carries a strong signal get the matching template\n // plus inputs derived from that body. Strict derivation — when the\n // essential input can't be built (e.g. feature without an image src),\n // the block keeps the structural default.\n if ((options?.autoTemplates ?? true) && !frontmatterDisablesAutoTemplates(markdownDoc)) {\n applyAutoTemplates(rootBlocks, resolveTemplateName(defaultTemplate), { featureIndex: 0 });\n }\n\n // Duplicate ids make connections and navigation ambiguous. Generated\n // slugs are already deduped; this catches author-pinned `{#id}` clashes.\n const seenIds = new Map<string, Block>();\n for (const block of allBlocks) {\n if (seenIds.has(block.id)) {\n diagnostics.push({\n severity: 'error',\n code: 'duplicate-id',\n message: `Duplicate block id \"${block.id}\" — later connections and links are ambiguous`,\n blockId: block.id,\n ...lineOf(block),\n });\n } else {\n seenIds.set(block.id, block);\n }\n }\n\n // connectsTo targets must resolve to a block id in this doc.\n for (const block of allBlocks) {\n for (const conn of block.connectsTo ?? []) {\n if (!seenIds.has(conn.target)) {\n diagnostics.push({\n severity: 'warning',\n code: 'unresolved-connection',\n message: `Block \"${block.id}\" connects to unknown target \"${conn.target}\"`,\n blockId: block.id,\n ...lineOf(block),\n });\n }\n }\n }\n\n // Calculate reading-time-based durations and generate captions\n const minDuration = 3; // seconds — minimum for blocks with little/no text\n const phrases: CaptionPhrase[] = [];\n\n // First pass: compute duration from body-content reading time.\n // Skip blocks that pinned an explicit duration via a heading attribute —\n // author intent wins over reading-time heuristics.\n for (const block of allBlocks) {\n if (pinnedMeta(block).duration != null) continue;\n const bodyText = getBlockBodyText(block);\n if (bodyText.length > 0) {\n const estimate = estimateReadingTime(bodyText);\n block.duration = Math.max(minDuration, estimate.seconds);\n } else {\n block.duration = defaultDuration;\n }\n }\n\n // Second pass: assign start times sequentially and build caption phrases.\n // Blocks with an explicit `startTime` attribute keep their pinned value;\n // the running cursor still advances by their duration so following blocks\n // sequence after them.\n let currentTime = 0;\n for (const block of allBlocks) {\n const explicitStart = pinnedMeta(block).startTime;\n if (explicitStart == null) {\n block.startTime = currentTime;\n }\n\n // Generate caption phrases from the block's body content\n const bodyText = getBlockBodyText(block);\n if (bodyText.length > 0) {\n const sentences = splitIntoSentences(bodyText);\n if (sentences.length > 0) {\n const timePerSentence = block.duration / sentences.length;\n for (let i = 0; i < sentences.length; i++) {\n phrases.push({\n text: sentences[i],\n startTime: currentTime + i * timePerSentence,\n endTime: currentTime + (i + 1) * timePerSentence,\n audioSegment: 0,\n });\n }\n }\n }\n\n currentTime += block.duration;\n }\n\n // `generatedAt` is only stamped when the caller provides a timestamp —\n // conversion itself never reads the clock, so identical markdown always\n // converts to an identical Doc.\n const captions: CaptionTrack | undefined =\n phrases.length > 0\n ? {\n phrases,\n ...(options?.captionsGeneratedAt ? { generatedAt: options.captionsGeneratedAt } : {}),\n version: 1,\n }\n : undefined;\n\n const customTemplates = readCustomTemplatesFromFrontmatter(markdownDoc.frontmatter);\n const customThemes = readCustomThemesFromFrontmatter(markdownDoc.frontmatter);\n const doc: Doc = {\n articleId,\n duration: currentTime,\n blocks: rootBlocks,\n audio: {\n segments: [],\n },\n ...(captions ? { captions } : {}),\n ...(markdownDoc.frontmatter ? { frontmatter: markdownDoc.frontmatter } : {}),\n ...(customTemplates ? { customTemplates } : {}),\n ...(customThemes ? { customThemes } : {}),\n ...(diagnostics.length > 0 ? { diagnostics } : {}),\n ...(documentMedia.length > 0 ? { documentMedia } : {}),\n };\n\n // Auto-generate cover startBlock from the first H1 heading\n if (options?.generateCoverBlock ?? true) {\n const coverConfig = buildStartBlock(markdownDoc, rootBlocks);\n if (coverConfig) {\n doc.startBlock = coverConfig;\n }\n }\n\n return doc;\n}\n\n// ============================================\n// Utilities\n// ============================================\n\n/**\n * Flatten a nested block tree into a depth-first ordered array.\n * Useful for calculating sequential timing or iterating all blocks.\n */\nexport function flattenBlocks(blocks: Block[]): Block[] {\n const result: Block[] = [];\n for (const block of blocks) {\n result.push(block);\n if (block.children) {\n result.push(...flattenBlocks(block.children));\n }\n }\n return result;\n}\n\n/**\n * Flatten the block tree into the list of *independently renderable*\n * blocks — like {@link flattenBlocks}, but it does NOT descend into the\n * children of a container template (`diagram`, `drawing`; see\n * `isContainerTemplate`). Those children are consumed by the parent's\n * render (as nodes / shapes), so they must not also surface as their own\n * slides or sections.\n *\n * Use this anywhere a doc is flattened for rendering (slideshow, video,\n * static pages). Use {@link flattenBlocks} when you genuinely need every\n * block regardless of role — validation, timing, duplicate-id checks.\n */\nexport function flattenRenderableBlocks(blocks: Block[]): Block[] {\n const result: Block[] = [];\n for (const block of blocks) {\n result.push(block);\n if (block.children && !isContainerTemplate(block.template)) {\n result.push(...flattenRenderableBlocks(block.children));\n }\n }\n return result;\n}\n\n/**\n * Count the total number of blocks in a nested tree (including children at all levels).\n */\nexport function countBlocks(blocks: Block[]): number {\n let count = 0;\n for (const block of blocks) {\n count += 1;\n if (block.children) {\n count += countBlocks(block.children);\n }\n }\n return count;\n}\n\n/**\n * Get the heading depth for a block. Returns 0 for preamble blocks (no heading).\n */\nexport function getBlockDepth(block: Block): number {\n return block.sourceHeading?.depth ?? 0;\n}\n\n// ============================================\n// Internal helpers\n// ============================================\n\n/** Source line of a block's heading, when position info is available. */\nfunction lineOf(block: Block): { line: number } | Record<string, never> {\n const line = block.sourceHeading?.position?.start.line;\n return line != null ? { line } : {};\n}\n\n/**\n * Populate `block.templateData` from structured body content:\n * 1. Every ```json data / ```yaml data fence in the body merges its parsed\n * object in (later fences override earlier keys).\n * 2. For `dataTable` blocks, the first GFM table supplies headers/rows/align\n * unless the author already provided them via a fence or `{[…]}` params.\n * Parse failures are recorded on `diagnostics` and skip the fence.\n */\nfunction applyStructuredData(block: Block, diagnostics: DocDiagnostic[]): void {\n let data: Record<string, unknown> | undefined;\n\n for (const node of block.contents ?? []) {\n if (node.type !== 'code' || !isDataFence(node)) continue;\n const result = parseDataFence(node);\n if (result.error) {\n diagnostics.push({\n severity: 'error',\n code: 'data-fence-parse',\n message: `Data fence in block \"${block.id}\": ${result.error}`,\n blockId: block.id,\n ...(node.position ? { line: node.position.start.line } : lineOf(block)),\n });\n continue;\n }\n data = { ...data, ...result.data };\n }\n\n if (block.template === 'dataTable') {\n const provided = (key: string) =>\n (data && key in data) || (block.templateOverrides && key in block.templateOverrides);\n if (!provided('headers') && !provided('rows')) {\n const table = findFirstTable(block.contents);\n if (table) {\n data = { ...extractTableData(table), ...data };\n }\n }\n }\n\n if (data && Object.keys(data).length > 0) {\n block.templateData = data;\n }\n}\n\n/** Truthiness of the `squisq-auto-templates` frontmatter kill-switch. */\nfunction frontmatterDisablesAutoTemplates(markdownDoc: MarkdownDocument): boolean {\n const v = markdownDoc.frontmatter?.['squisq-auto-templates'];\n return v === false || v === 'false' || v === 'off' || v === 'no' || v === 0;\n}\n\n/**\n * Walk heading blocks and apply content-aware templates to the ones that\n * still hold the structural default. Children of container templates\n * (diagram/drawing/layout) are consumed by their parent and never\n * re-templated. `state.featureIndex` alternates left/right feature\n * composition across the document.\n */\nfunction applyAutoTemplates(\n blocks: Block[],\n resolvedDefault: string,\n state: { featureIndex: number },\n): void {\n for (const block of blocks) {\n const annotated = !!block.sourceHeading?.templateAnnotation?.template;\n if (block.sourceHeading && !annotated && block.template === resolvedDefault) {\n const profile = profileBlockContents(block.contents ?? []);\n const picked = pickAutoTemplate(profile, state.featureIndex);\n if (picked) {\n const inputs = deriveTemplateInputs(picked, block.title ?? '', block.contents);\n if (inputs) {\n if (picked === 'leftFeature' || picked === 'rightFeature') state.featureIndex += 1;\n block.template = picked;\n block.autoTemplate = true;\n // Author-provided structured data (```json data fences) wins\n // over derived inputs.\n block.templateData = { ...inputs, ...block.templateData };\n }\n }\n }\n if (block.children && block.children.length > 0 && !isContainerTemplate(block.template)) {\n applyAutoTemplates(block.children, resolvedDefault, state);\n }\n }\n}\n\n/**\n * Extract the plain text from a block's body contents (excluding heading text).\n */\nfunction getBlockBodyText(block: Block): string {\n if (!block.contents || block.contents.length === 0) return '';\n // Join with newlines to preserve paragraph/list-item boundaries.\n // splitIntoPhrases uses these newlines as natural split points.\n return block.contents\n .map((node) => extractPlainText(node))\n .join('\\n')\n .trim();\n}\n\n/** Maximum words per caption phrase. Long sentences get split at this point. */\nconst MAX_PHRASE_WORDS = 12;\n\n/**\n * Split text into caption-sized phrases.\n *\n * Splits on:\n * 1. Newlines (paragraph/list-item boundaries from markdown)\n * 2. Sentence endings (.!?) followed by whitespace\n * 3. Long fragments (> MAX_PHRASE_WORDS) at clause boundaries (commas, semicolons, dashes)\n *\n * Merges very short fragments (< 15 chars) with the previous phrase.\n */\nfunction splitIntoSentences(text: string): string[] {\n // First split on newlines (each paragraph/list item becomes separate)\n const lines = text\n .split(/\\n+/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n\n // Then split each line on sentence boundaries\n const sentenceRe = /(?<=[.!?])\\s+/;\n const fragments: string[] = [];\n for (const line of lines) {\n const sentences = line\n .split(sentenceRe)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n fragments.push(...sentences);\n }\n\n if (fragments.length === 0) return [];\n\n // Split long fragments at clause boundaries\n const split: string[] = [];\n for (const frag of fragments) {\n const words = frag.split(/\\s+/);\n if (words.length > MAX_PHRASE_WORDS) {\n // Try to split at comma, semicolon, or dash near the midpoint\n const mid = Math.floor(frag.length / 2);\n const clauseRe = /[,;]\\s+|\\s+—\\s+|\\s+-\\s+/g;\n let bestSplit = -1;\n let bestDist = Infinity;\n let match;\n while ((match = clauseRe.exec(frag)) !== null) {\n const dist = Math.abs(match.index - mid);\n if (dist < bestDist) {\n bestDist = dist;\n bestSplit = match.index + match[0].length;\n }\n }\n if (bestSplit > 0 && bestSplit < frag.length - 5) {\n split.push(frag.slice(0, bestSplit).trim());\n split.push(frag.slice(bestSplit).trim());\n } else {\n split.push(frag);\n }\n } else {\n split.push(frag);\n }\n }\n\n // Merge very short fragments (< 15 chars) with the previous phrase\n const merged: string[] = [split[0]];\n for (let i = 1; i < split.length; i++) {\n if (split[i].length < 15 && merged.length > 0) {\n merged[merged.length - 1] += ' ' + split[i];\n } else {\n merged.push(split[i]);\n }\n }\n return merged;\n}\n\ninterface ImageRef {\n url: string;\n alt?: string;\n}\n\n/**\n * Walk a parsed HTML sub-tree depth-first for an `<img>` with a `src`.\n * Used to surface images that the WYSIWYG editor serialized as raw HTML\n * (e.g. when an image was resized — markdown's `![alt](url)` shorthand\n * has no width syntax, so resized images round-trip as `<img src width>`).\n */\nfunction findFirstHtmlImage(nodes: HtmlNode[]): ImageRef | undefined {\n for (const node of nodes) {\n if (node.type !== 'htmlElement') continue;\n if (node.tagName === 'img' && node.attributes.src) {\n return { url: node.attributes.src, alt: node.attributes.alt };\n }\n const nested = findFirstHtmlImage(node.children);\n if (nested) return nested;\n }\n return undefined;\n}\n\n/**\n * Walk a MarkdownNode tree depth-first to find the first image reference,\n * whether expressed as a markdown image node or a raw HTML `<img>` tag.\n */\nfunction findFirstImage(node: MarkdownNode): ImageRef | undefined {\n if (node.type === 'image') {\n const img = node as { url: string; alt?: string };\n return { url: img.url, alt: img.alt };\n }\n if (node.type === 'htmlBlock' || node.type === 'htmlInline') {\n const html = node as { htmlChildren?: HtmlNode[] };\n if (html.htmlChildren) {\n const found = findFirstHtmlImage(html.htmlChildren);\n if (found) return found;\n }\n }\n if ('children' in node && Array.isArray((node as { children?: unknown[] }).children)) {\n for (const child of (node as { children: MarkdownNode[] }).children) {\n const found = findFirstImage(child);\n if (found) return found;\n }\n }\n return undefined;\n}\n\n/**\n * Build a StartBlockConfig from the document's first H1 heading and optional\n * first image. Returns undefined if the document has no H1 heading.\n */\nfunction buildStartBlock(\n markdownDoc: MarkdownDocument,\n rootBlocks: Block[],\n): StartBlockConfig | undefined {\n // Find the first H1 block — it provides the cover title\n const firstH1 = rootBlocks.find((b) => b.sourceHeading?.depth === 1);\n if (!firstH1) return undefined;\n\n const title = firstH1.title ?? extractPlainText(firstH1.sourceHeading!);\n if (!title) return undefined;\n\n // Look for the first paragraph immediately after the H1 to use as subtitle\n const subtitle =\n firstH1.contents?.[0]?.type === 'paragraph' ? extractPlainText(firstH1.contents[0]) : undefined;\n\n // Scan the whole document for the first image to use as the hero\n const firstImage = findFirstImage(markdownDoc);\n\n const config: StartBlockConfig = {\n title,\n ...(subtitle ? { subtitle } : {}),\n ...(firstImage ? { heroSrc: firstImage.url, heroAlt: firstImage.alt ?? title } : {}),\n ambientMotion: 'zoomIn',\n };\n\n return config;\n}\n","/**\n * Doc → Markdown Conversion\n *\n * Converts a Doc with a heading-driven Block hierarchy back into a\n * MarkdownDocument. This is the reverse of markdownToDoc() and enables\n * round-tripping: edit a Doc's block tree, then serialize back to markdown.\n *\n * **Algorithm:**\n * Walk the block tree depth-first. For each block:\n * 1. If it has a `sourceHeading`, emit that heading node\n * 2. Emit all nodes in `contents`\n * 3. Recurse into `children`\n *\n * @example\n * ```ts\n * import { markdownToDoc, docToMarkdown } from '@bendyline/squisq/doc';\n * import { parseMarkdown, stringifyMarkdown } from '@bendyline/squisq/markdown';\n *\n * const md = '# Hello\\n\\nWorld\\n';\n * const doc = markdownToDoc(parseMarkdown(md));\n * const roundTripped = stringifyMarkdown(docToMarkdown(doc));\n * ```\n */\n\nimport type { Doc, Block } from '../schemas/Doc.js';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownHeading,\n MarkdownParagraph,\n} from '../markdown/types.js';\nimport { serializeAnnotation } from '../markdown/attrTokens.js';\nimport {\n FRONTMATTER_CUSTOM_TEMPLATES_KEY,\n writeCustomTemplatesToFrontmatter,\n} from './customTemplatesFrontmatter.js';\nimport {\n FRONTMATTER_CUSTOM_THEMES_KEY,\n writeCustomThemesToFrontmatter,\n} from './customThemesFrontmatter.js';\n\n/**\n * Convert a Doc with heading-driven blocks back to a MarkdownDocument.\n *\n * Walks the block tree depth-first, emitting heading nodes and contents\n * in document order. Blocks without a `sourceHeading` (preamble blocks)\n * emit only their contents.\n *\n * If a block has a `template` or `templateOverrides` that aren't already\n * reflected in the heading's `templateAnnotation`, the annotation is\n * injected so the round-trip preserves template assignments.\n *\n * @param doc - A Doc whose blocks may have `sourceHeading`, `contents`, and `children`\n * @returns A MarkdownDocument that can be stringified back to markdown\n */\nexport function docToMarkdown(doc: Doc): MarkdownDocument {\n const children: MarkdownBlockNode[] = [];\n\n function emitBlock(block: Block): void {\n // Emit the heading node if present\n if (block.sourceHeading) {\n const heading = ensureAnnotation(block, block.sourceHeading);\n children.push(heading);\n } else if (block.standaloneAnnotation) {\n // Heading-less standalone block: re-emit its `{[…]}` annotation as a\n // paragraph before its contents. The serializer's quoting is paired\n // with the tokenizer, and stringifyMarkdown un-escapes `{[…]}` spans,\n // so the annotation round-trips.\n children.push(synthesizeAnnotationParagraph(block));\n }\n\n // Emit body content\n if (block.contents) {\n children.push(...block.contents);\n }\n\n // Recurse into children (sub-headings)\n if (block.children) {\n for (const child of block.children) {\n emitBlock(child);\n }\n }\n }\n\n for (const block of doc.blocks) {\n emitBlock(block);\n }\n\n // Merge `Doc.customTemplates` into the outgoing frontmatter so the\n // markdown remains the source-of-truth: edits made via the editor's\n // TemplateDesigner round-trip through `docToMarkdown` →\n // `stringifyMarkdown` and are visible to anyone reading the file.\n const customPayload = writeCustomTemplatesToFrontmatter(doc.customTemplates);\n let frontmatter = doc.frontmatter;\n if (customPayload) {\n frontmatter = { ...(frontmatter ?? {}), [FRONTMATTER_CUSTOM_TEMPLATES_KEY]: customPayload };\n } else if (frontmatter && FRONTMATTER_CUSTOM_TEMPLATES_KEY in frontmatter) {\n // The doc has no custom templates anymore — drop the stale\n // frontmatter key so the markdown reflects the current state.\n const { [FRONTMATTER_CUSTOM_TEMPLATES_KEY]: _drop, ...rest } = frontmatter;\n void _drop;\n frontmatter = rest;\n }\n\n // Mirror the same round-trip for `Doc.customThemes` (the theme analog).\n const customThemesPayload = writeCustomThemesToFrontmatter(doc.customThemes);\n if (customThemesPayload) {\n frontmatter = { ...(frontmatter ?? {}), [FRONTMATTER_CUSTOM_THEMES_KEY]: customThemesPayload };\n } else if (frontmatter && FRONTMATTER_CUSTOM_THEMES_KEY in frontmatter) {\n const { [FRONTMATTER_CUSTOM_THEMES_KEY]: _dropTheme, ...rest } = frontmatter;\n void _dropTheme;\n frontmatter = rest;\n }\n\n return {\n type: 'document',\n children,\n ...(frontmatter && Object.keys(frontmatter).length > 0 ? { frontmatter } : {}),\n };\n}\n\n/**\n * Build the synthesized annotation paragraph for a heading-less standalone\n * block: `{[templateName key=value …]}` from its `sourceAnnotation.template`\n * and current `templateOverrides` (edits round-trip through the latter).\n */\nfunction synthesizeAnnotationParagraph(block: Block): MarkdownParagraph {\n const text = serializeAnnotation(block.sourceAnnotation?.template, block.templateOverrides);\n return { type: 'paragraph', children: [{ type: 'text', value: text }] };\n}\n\n/**\n * Ensure the heading's `templateAnnotation` reflects the block's\n * template and templateOverrides. Returns a (possibly cloned) heading.\n */\nfunction ensureAnnotation(block: Block, heading: MarkdownHeading): MarkdownHeading {\n const attrs = ensureTransitionAttributes(block, heading);\n\n // If the block has a non-default template or overrides, inject an annotation\n const hasExplicitTemplate =\n block.template && block.template !== 'sectionHeader' && block.autoTemplate !== true;\n const hasOverrides = block.templateOverrides && Object.keys(block.templateOverrides).length > 0;\n\n // If the heading already has an annotation, trust it (it came from parsing).\n // Transition attrs may still have been injected above if the block changed.\n if (heading.templateAnnotation) {\n return attrs === heading.attributes ? heading : { ...heading, attributes: attrs };\n }\n\n if (!hasExplicitTemplate && !hasOverrides) {\n return attrs === heading.attributes ? heading : { ...heading, attributes: attrs };\n }\n\n // Clone to avoid mutating the original\n return {\n ...heading,\n children: [...heading.children],\n attributes: attrs,\n templateAnnotation: {\n template: block.template ?? 'sectionHeader',\n ...(hasOverrides ? { params: block.templateOverrides } : {}),\n },\n };\n}\n\nfunction ensureTransitionAttributes(\n block: Block,\n heading: MarkdownHeading,\n): MarkdownHeading['attributes'] {\n const transition = block.transition;\n if (!transition) return heading.attributes;\n\n // The transition is deliberately written to BOTH channels, which serve\n // different consumers:\n // - `params` is what `stringifyMarkdown` serializes into the Pandoc `{…}`\n // attribute text (serializePandocAttributes reads only id/classes/params).\n // - `blockMeta` is what `markdownToDoc` reads back directly (makeBlock,\n // pinnedHeadingMeta) — an in-memory Doc → MarkdownDocument → Doc round\n // trip never re-parses params, so a stale/missing `blockMeta.transition`\n // would lose or resurrect the wrong transition.\n const attrs = heading.attributes ?? {};\n return {\n ...attrs,\n params: {\n ...(attrs.params ?? {}),\n transition: transition.type,\n ...(transition.duration !== undefined\n ? { transitionDuration: String(transition.duration) }\n : {}),\n ...(transition.direction ? { transitionDirection: transition.direction } : {}),\n },\n blockMeta: {\n ...(attrs.blockMeta ?? {}),\n transition,\n },\n };\n}\n","/**\n * Doc-scoped theme resolution.\n *\n * The theme analog of how `buildRegistry` resolves custom templates: pure and\n * doc-scoped, with no global state. `resolveThemeForDoc` looks up the active\n * theme id in the doc's own custom themes first, then falls back to the\n * built-in / globally-registered themes via `resolveTheme`.\n *\n * It accepts either a squisq `Doc` (which carries a typed `customThemes`\n * array) or anything with raw `frontmatter` — notably a `MarkdownDocument`,\n * which is the shape every export pipeline pivots through. In the latter case\n * the inline themes are decoded from the `squisq-custom-themes` frontmatter\n * payload. Either way, resolution needs no `registerTheme` call — an inline\n * custom theme ships with the doc, exactly like custom templates.\n */\n\nimport type { Theme } from '../schemas/Theme.js';\nimport { resolveTheme } from '../schemas/themeLibrary.js';\nimport { readFrontmatterThemeId } from '../markdown/utils.js';\nimport { readCustomThemesFromFrontmatter } from './customThemesFrontmatter.js';\n\n/**\n * The minimal shape `resolveThemeForDoc` needs. Satisfied by both the squisq\n * `Doc` (typed `customThemes` + `themeId`) and a `MarkdownDocument` (raw\n * `frontmatter` carrying the encoded payload).\n */\nexport interface ThemeResolvable {\n themeId?: string;\n customThemes?: Theme[];\n frontmatter?: Record<string, unknown>;\n}\n\n/**\n * Resolve the Theme a document should render with.\n *\n * @param doc - the document (may carry inline custom themes plus a `themeId`\n * / `squisq-theme` frontmatter selector).\n * @param explicitId - an id that overrides the doc's own selection (e.g. the\n * editor's theme dropdown, or an export `--theme` option). When omitted, the\n * doc's `themeId` / frontmatter theme id is used.\n * @returns the matching inline custom theme (doc-scoped) when present, else\n * the built-in / registered theme for that id, else the default theme.\n */\nexport function resolveThemeForDoc(\n doc: ThemeResolvable | null | undefined,\n explicitId?: string,\n): Theme {\n const id = explicitId ?? doc?.themeId ?? readFrontmatterThemeId(doc?.frontmatter);\n // Prefer the already-parsed list on a squisq Doc; otherwise decode the\n // frontmatter payload (the shape exports see on a MarkdownDocument).\n const inline = doc?.customThemes ?? readCustomThemesFromFrontmatter(doc?.frontmatter);\n const custom = id ? inline?.find((t) => t.id === id) : undefined;\n if (custom) return custom;\n return resolveTheme(id);\n}\n","/**\n * Fallback Block Rendering\n *\n * The graceful-degradation guarantee: a block whose template can't render —\n * unknown template name, template function threw, or returned a non-array —\n * still renders its heading and body text as a plain, readable card instead\n * of a blank slide. A small notice line names the problem so authors (and\n * agents reviewing screenshots) can see *why* the block degraded.\n *\n * This is intentionally not a registered template: it can't be requested by\n * name, only reached when a requested template fails. See `getLayers()`.\n */\n\nimport type { Layer } from '../../schemas/Doc.js';\nimport type { Block } from '../../schemas/Doc.js';\nimport type { DocBlock, TemplateContext } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme, getThemeFont, themedFontSize } from '../utils/themeUtils.js';\nimport { extractPlainText } from '../../markdown/utils.js';\n\n/** Cap body text so a long section doesn't overflow the card. */\nconst MAX_BODY_CHARS = 600;\n\n/**\n * Render a block's heading + body text as plain layers, with a notice line\n * describing why the requested template didn't render.\n *\n * @param block The block that failed to render via its template.\n * @param context Template context (theme, viewport).\n * @param notice Short problem description, e.g. `Unknown template \"photGrid\"`.\n */\nexport function fallbackBlockLayers(\n block: DocBlock,\n context: TemplateContext,\n notice: string,\n): Layer[] {\n const colors = resolveColorScheme(context, 'blue');\n const { layout } = context;\n\n const title = (block as Block).title ?? (block as Block).id ?? '';\n const body = extractBodyText(block as Block);\n\n const layers: Layer[] = [\n {\n type: 'shape',\n id: 'fallback-bg',\n content: { shape: 'rect', fill: colors.bg },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n if (title) {\n layers.push({\n type: 'text',\n id: 'fallback-title',\n content: {\n text: title,\n style: {\n fontSize: themedFontSize(56, context, true),\n fontFamily: getThemeFont(context, 'title'),\n fontWeight: 'bold',\n color: colors.text,\n textAlign: 'center',\n },\n },\n position: {\n x: '50%',\n y: body ? '28%' : '50%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n });\n }\n\n if (body) {\n layers.push({\n type: 'text',\n id: 'fallback-body',\n content: {\n text: body,\n style: {\n fontSize: themedFontSize(28, context),\n fontFamily: getThemeFont(context, 'body'),\n color: colors.text,\n textAlign: 'center',\n },\n },\n position: {\n x: '50%',\n y: '55%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n });\n }\n\n layers.push({\n type: 'text',\n id: 'fallback-notice',\n content: {\n text: `⚠ ${notice}`,\n style: {\n fontSize: themedFontSize(18, context),\n fontFamily: getThemeFont(context, 'body'),\n color: `${colors.text}99`,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: '92%', anchor: 'center', width: layout.maxTextWidth },\n });\n\n return layers;\n}\n\n/** Plain text of a block's body contents, truncated for card display. */\nfunction extractBodyText(block: Block): string {\n if (!block.contents || block.contents.length === 0) return '';\n const text = block.contents\n .map((node) => extractPlainText(node))\n .join('\\n')\n .trim();\n if (text.length <= MAX_BODY_CHARS) return text;\n return `${text.slice(0, MAX_BODY_CHARS).trimEnd()}…`;\n}\n","/**\n * getLayers — Compute visual layers for a block on demand.\n *\n * This is the preferred way to obtain renderable layers for a block.\n * Instead of storing pre-computed layers on the Block object, call\n * `getLayers(block, context)` to derive them from the block's template\n * name, content, and the current render context (theme, viewport, etc.).\n *\n * For raw blocks that already carry a `layers` array, those layers are\n * returned directly (with optional persistent layer injection).\n *\n * @example\n * ```ts\n * import { getLayers } from '@bendyline/squisq/doc';\n *\n * const layers = getLayers(block, {\n * theme: DEFAULT_THEME,\n * viewport: VIEWPORT_PRESETS.landscape,\n * blockIndex: 0,\n * totalBlocks: 10,\n * });\n * ```\n */\n\nimport type { Block, Layer } from '../schemas/Doc.js';\nimport type {\n TemplateBlock,\n TemplateContext,\n PersistentLayerConfig,\n DocBlock,\n} from '../schemas/BlockTemplates.js';\nimport type { Theme } from '../schemas/Theme.js';\nimport type { ViewportConfig } from '../schemas/Viewport.js';\nimport { VIEWPORT_PRESETS } from '../schemas/Viewport.js';\nimport { createTemplateContext, isTemplateBlock } from '../schemas/BlockTemplates.js';\nimport { DEFAULT_THEME } from '../schemas/themeLibrary.js';\nimport { templateRegistry, resolveTemplateName } from './templates/index.js';\nimport { coerceTemplateParams } from './templates/inputDescriptors.js';\nimport { expandPersistentLayers, wrapWithPersistentLayers } from './templates/persistentLayers.js';\nimport { applyRenderStyleToLayers } from './utils/applyRenderStyle.js';\nimport { fallbackBlockLayers } from './templates/fallbackBlock.js';\n\n// ============================================\n// RenderContext\n// ============================================\n\n/**\n * Context needed to compute layers for a block.\n *\n * Captures the visual rendering parameters: theme colors, viewport\n * configuration, persistent layers, and the block's position within\n * the doc (for template functions that vary output by index).\n */\nexport interface RenderContext {\n /** Theme for template rendering. Defaults to DEFAULT_THEME (documentary). */\n theme?: Theme;\n\n /** Target viewport configuration. Defaults to 16:9 landscape. */\n viewport?: ViewportConfig;\n\n /** Persistent layers injected behind and/or on top of all block content. */\n persistentLayers?: PersistentLayerConfig;\n\n /** 0-based index of this block in the sequence. Defaults to 0. */\n blockIndex?: number;\n\n /** Total number of blocks in the doc. Defaults to 1. */\n totalBlocks?: number;\n}\n\n// ============================================\n// getLayers\n// ============================================\n\n/**\n * Compute the visual layers for a block.\n *\n * Resolution order:\n * 1. If the block already has a non-empty `layers` array, use it (raw block).\n * 2. If the block has a `template` name that exists in the registry,\n * call the template function to generate layers.\n * 3. Otherwise return an empty array.\n *\n * Persistent layers (bottom/top) from the render context are injected\n * around the result unless the block opts out via `useBottomLayer: false`\n * or `useTopLayer: false`.\n *\n * @param block A Block or TemplateBlock to render.\n * @param context Render context (theme, viewport, persistent layers, position).\n * @returns The computed Layer[] for the block, ready for BlockRenderer.\n */\nexport function getLayers(block: DocBlock, context: RenderContext = {}): Layer[] {\n const theme = context.theme ?? DEFAULT_THEME;\n const viewport = context.viewport ?? VIEWPORT_PRESETS.landscape;\n const blockIndex = context.blockIndex ?? 0;\n const totalBlocks = context.totalBlocks ?? 1;\n\n // 1. Raw block path: block already has pre-computed layers\n const existingLayers = (block as Block).layers;\n if (existingLayers && existingLayers.length > 0 && !isTemplateBlock(block)) {\n return injectPersistentLayers(existingLayers, block, context);\n }\n\n // 2. Template path: look up and call the template function.\n //\n // Resolve through TEMPLATE_ALIASES so legacy ids (`titleBlock`,\n // `quoteBlock`, `mapBlock`, `listBlock`) hit the registry by their\n // canonical short names. Without this the block-section path renders\n // an empty SVG card: `hasTemplate()` accepts the alias (so\n // `isAnnotatedBlock` returns true and the card wrapper renders), but\n // a raw `block.template in templateRegistry` check below misses it\n // and the layer list comes back empty.\n if (isTemplateBlock(block)) {\n const resolved = resolveTemplateName(block.template);\n const templateCtx = createTemplateContext(theme, blockIndex, totalBlocks, viewport);\n if (resolved in templateRegistry) {\n const templateName = resolved as keyof typeof templateRegistry;\n // Aggregate templates (e.g. `diagram`) consume the block's children.\n const maybeChildren = (block as Block).children;\n if (maybeChildren && maybeChildren.length > 0) {\n templateCtx.children = maybeChildren;\n }\n // Effective template input: the block's own fields, then structured\n // body data (```json data fences, GFM tables), then `{[…]}` string\n // overrides — the same merge order buildPreviewDoc uses. Inline overrides\n // are coerced to their typed shape (`center=\"47.6,-122.3\"` → `{lat,lng}`,\n // `zoom=9` → `9`) so pure-inline annotations render without any change to\n // the template functions. `block.templateOverrides` itself stays raw\n // strings for lossless round-tripping — only this ephemeral input is coerced.\n const { templateData, templateOverrides } = block as Block;\n const input =\n templateData || templateOverrides\n ? ({\n ...block,\n ...templateData,\n ...coerceTemplateParams(block.template, templateOverrides ?? {}).input,\n } as TemplateBlock)\n : block;\n let layers: Layer[];\n try {\n // Each registry entry accepts its specific TemplateBlock variant; the\n // discriminated union guarantees the shapes match at runtime.\n const templateFn = templateRegistry[templateName] as (\n input: TemplateBlock,\n ctx: TemplateContext,\n ) => Layer[];\n layers = templateFn(input, templateCtx);\n if (!Array.isArray(layers)) {\n console.warn(`Template ${templateName} did not return an array, got:`, typeof layers);\n layers = fallbackBlockLayers(block, templateCtx, `Template \"${block.template}\" failed`);\n }\n } catch (err: unknown) {\n console.warn(`Error expanding template ${templateName}:`, err);\n layers = fallbackBlockLayers(block, templateCtx, `Template \"${block.template}\" failed`);\n }\n\n // Theme motion defaults apply to template-generated layers only\n // (mirrors expandDocBlocks — the two paths must agree).\n layers = applyRenderStyleToLayers(layers, block as Block, theme);\n\n return injectPersistentLayers(layers, block, context);\n }\n\n // Unknown template — graceful-degradation guarantee: render the\n // block's heading + body text as a plain card with a visible notice\n // instead of a blank slide.\n return injectPersistentLayers(\n fallbackBlockLayers(block, templateCtx, `Unknown template \"${block.template}\"`),\n block,\n context,\n );\n }\n\n // 3. Fallback: no layers and no template requested\n return injectPersistentLayers([], block, context);\n}\n\n// ============================================\n// Internal helpers\n// ============================================\n\n/**\n * Inject persistent bottom/top layers around the block's own layers,\n * respecting per-block opt-out flags.\n */\nfunction injectPersistentLayers(layers: Layer[], block: DocBlock, context: RenderContext): Layer[] {\n const { persistentLayers } = context;\n if (!persistentLayers) return layers;\n\n const theme = context.theme ?? DEFAULT_THEME;\n const bottomLayers = expandPersistentLayers(persistentLayers.bottomLayers, theme);\n const topLayers = expandPersistentLayers(persistentLayers.topLayers, theme);\n\n return wrapWithPersistentLayers(layers, block as TemplateBlock, bottomLayers, topLayers);\n}\n","/**\n * Audio Mapping\n *\n * Associates audio segments (MP3 files) with document blocks.\n * Supports two modes:\n *\n * 1. **Explicit annotation**: Blocks with `{[audio=filename.mp3]}` in their\n * heading annotation are directly mapped to that audio file.\n *\n * 2. **Auto-matching**: When no annotations exist but the container has MP3s\n * with `.timing.json` files, matches MP3s to blocks by comparing the\n * timing.json `sourceText` against block text content (word overlap).\n * Falls back to filename-based matching (slugified title comparison).\n */\n\nimport type { Doc, Block, AudioSegment, AudioTimingData } from '../schemas/Doc.js';\nimport type { ContentContainer } from '../storage/ContentContainer.js';\nimport { extractPlainText } from '../markdown/utils.js';\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\n/**\n * Parse a timing.json file from raw data.\n */\nfunction parseTimingJson(data: ArrayBuffer): AudioTimingData | null {\n try {\n const text = new TextDecoder().decode(data);\n const parsed = JSON.parse(text);\n if (parsed && typeof parsed.sourceText === 'string' && typeof parsed.duration === 'number') {\n return {\n sourceText: parsed.sourceText,\n bookmarks: Array.isArray(parsed.bookmarks) ? parsed.bookmarks : [],\n duration: parsed.duration,\n };\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Normalize text for comparison: lowercase, strip punctuation, split into word set.\n */\nfunction normalizeToWords(text: string): Set<string> {\n const words = text\n .toLowerCase()\n .replace(/[^\\w\\s]/g, ' ')\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n return new Set(words);\n}\n\n/**\n * Calculate Jaccard similarity between two texts (word overlap).\n * Returns 0–1 where 1 = identical word sets.\n */\nexport function scoreTextSimilarity(a: string, b: string): number {\n const wordsA = normalizeToWords(a);\n const wordsB = normalizeToWords(b);\n if (wordsA.size === 0 || wordsB.size === 0) return 0;\n\n let intersection = 0;\n for (const w of wordsA) {\n if (wordsB.has(w)) intersection++;\n }\n\n const union = new Set([...wordsA, ...wordsB]).size;\n return union > 0 ? intersection / union : 0;\n}\n\n/**\n * Slugify a title for filename comparison.\n * \"The Behring Gambit\" → \"the-behring-gambit\"\n */\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n}\n\n/**\n * Extract plain text from a block's contents and title.\n */\nfunction blockToPlainText(block: Block): string {\n const parts: string[] = [];\n if (block.title) parts.push(block.title);\n if (block.contents) {\n for (const node of block.contents) {\n parts.push(extractPlainText(node));\n }\n }\n return parts.join('\\n');\n}\n\n/**\n * Flatten a block tree into a linear array of leaf blocks\n * (blocks that represent actual content sections).\n */\nfunction flattenForAudio(blocks: Block[]): Block[] {\n const result: Block[] = [];\n for (const block of blocks) {\n if (block.children && block.children.length > 0) {\n result.push(...flattenForAudio(block.children));\n } else {\n result.push(block);\n }\n }\n return result;\n}\n\n/**\n * Info about a narration audio file found in the container.\n *\n * Originally limited to MP3, this now also covers WebM/MP4/Ogg/M4A\n * audio files that the browser recorder writes into `audio/`. The type\n * name is retained for backwards compatibility with the surrounding\n * matching logic; the data shape is identical regardless of codec.\n */\ninterface Mp3Info {\n /** Path in the container (e.g., \"audio/narration-2026.webm\"). */\n path: string;\n /** Filename without path (e.g., \"narration-2026.webm\"). */\n filename: string;\n /** Parsed timing data (if .timing.json exists). */\n timing: AudioTimingData | null;\n}\n\n/**\n * File extensions that unambiguously denote audio anywhere in the\n * container. These predate the recorder feature.\n */\nconst AUDIO_ONLY_EXTENSIONS = ['.mp3', '.wav', '.ogg', '.m4a'];\n\n/**\n * Extensions that could be audio or video — `.webm` and `.mp4` are the\n * containers `MediaRecorder` produces for browser-side narration, but\n * they're also used for video clips. Only treat them as narration when\n * they live under the `audio/` directory the recorder writes to.\n */\nconst AMBIGUOUS_EXTENSIONS = ['.webm', '.mp4'];\n\nfunction isNarrationAudioPath(path: string): boolean {\n const lower = path.toLowerCase();\n if (AUDIO_ONLY_EXTENSIONS.some((ext) => lower.endsWith(ext))) return true;\n if (AMBIGUOUS_EXTENSIONS.some((ext) => lower.endsWith(ext))) {\n return lower.startsWith('audio/');\n }\n return false;\n}\n\n/** Strip a known narration audio extension from a filename. */\nfunction stripAudioExtension(filename: string): string {\n const lower = filename.toLowerCase();\n for (const ext of [...AUDIO_ONLY_EXTENSIONS, ...AMBIGUOUS_EXTENSIONS]) {\n if (lower.endsWith(ext)) return filename.slice(0, -ext.length);\n }\n return filename;\n}\n\n// ── Core Functions ───────────────────────────────────────────────────\n\n/**\n * Parse a consolidated timing.json (version 2) which contains all section\n * timing data in a single file.\n */\nfunction parseConsolidatedTiming(data: ArrayBuffer): Record<string, AudioTimingData> | null {\n try {\n const text = new TextDecoder().decode(data);\n const parsed = JSON.parse(text);\n if (parsed && parsed.version === 2 && parsed.sections) {\n return parsed.sections as Record<string, AudioTimingData>;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Discover all narration audio files in a container and load their\n * timing data. Covers historical `.mp3`/`.wav`/`.ogg`/`.m4a` files\n * anywhere in the container plus `.webm`/`.mp4` files under `audio/`\n * (the destination the browser recorder writes to).\n *\n * Checks for a consolidated timing.json first, then falls back to\n * per-file `.timing.json` sidecars.\n */\nasync function discoverMp3s(container: ContentContainer): Promise<Mp3Info[]> {\n const files = await container.listFiles();\n const audioFiles = files.filter((f) => isNarrationAudioPath(f.path));\n const results: Mp3Info[] = [];\n\n // Try to load consolidated timing.json\n let consolidatedSections: Record<string, AudioTimingData> | null = null;\n const consolidatedData = await container.readFile('timing.json');\n if (consolidatedData) {\n consolidatedSections = parseConsolidatedTiming(consolidatedData);\n }\n\n for (const file of audioFiles) {\n const filename = file.path.split('/').pop() ?? file.path;\n let timing: AudioTimingData | null = null;\n\n // Try consolidated timing first: match section name from filename\n if (consolidatedSections) {\n const audioBase = stripAudioExtension(filename);\n for (const [sectionName, sectionTiming] of Object.entries(consolidatedSections)) {\n if (audioBase.endsWith(`-${sectionName}`)) {\n timing = sectionTiming;\n break;\n }\n }\n }\n\n // Fall back to per-file .timing.json\n if (!timing) {\n const timingPath = `${file.path}.timing.json`;\n const timingData = await container.readFile(timingPath);\n if (timingData) {\n timing = parseTimingJson(timingData);\n }\n }\n\n results.push({ path: file.path, filename, timing });\n }\n\n return results;\n}\n\n/**\n * Resolve audio from explicit `{[audio=filename.mp3]}` annotations on blocks.\n * Returns the matched pairs or an empty array if no annotations found.\n */\nfunction resolveFromAnnotations(\n blocks: Block[],\n mp3s: Mp3Info[],\n): Array<{ block: Block; mp3: Mp3Info }> {\n const matches: Array<{ block: Block; mp3: Mp3Info }> = [];\n const flat = flattenForAudio(blocks);\n\n for (const block of flat) {\n const audioRef = block.templateOverrides?.audio;\n if (!audioRef) continue;\n\n // Find the MP3 by filename (exact or suffix match)\n const mp3 = mp3s.find(\n (m) => m.filename === audioRef || m.path === audioRef || m.path.endsWith(`/${audioRef}`),\n );\n\n if (mp3) {\n matches.push({ block, mp3 });\n }\n }\n\n return matches;\n}\n\n/** Minimum similarity score to consider a content match. */\nconst MIN_CONTENT_SIMILARITY = 0.35;\n\n/** Minimum similarity score for filename-based matching. */\nconst MIN_FILENAME_SIMILARITY = 0.5;\n\n/**\n * Auto-match MP3 files to blocks by content similarity.\n * Uses timing.json sourceText when available, falls back to filename matching.\n */\nfunction autoMatchBlocks(\n blocks: Block[],\n mp3s: Mp3Info[],\n): Array<{ block: Block; mp3: Mp3Info; score: number }> {\n const flat = flattenForAudio(blocks);\n if (flat.length === 0 || mp3s.length === 0) return [];\n\n // Score all (mp3, block) pairs\n const scoredPairs: Array<{\n block: Block;\n blockIndex: number;\n mp3: Mp3Info;\n mp3Index: number;\n score: number;\n }> = [];\n\n for (let mi = 0; mi < mp3s.length; mi++) {\n const mp3 = mp3s[mi];\n\n for (let bi = 0; bi < flat.length; bi++) {\n const block = flat[bi];\n const blockText = blockToPlainText(block);\n if (!blockText.trim()) continue;\n\n let score = 0;\n\n // Primary: content similarity via timing.json sourceText\n if (mp3.timing?.sourceText) {\n score = scoreTextSimilarity(mp3.timing.sourceText, blockText);\n }\n\n // Fallback: filename-based matching\n if (score < MIN_CONTENT_SIMILARITY && block.title) {\n const titleSlug = slugify(block.title);\n const fileSlug = slugify(stripAudioExtension(mp3.filename));\n // Check if the title slug appears in the filename slug\n if (fileSlug.includes(titleSlug) && titleSlug.length >= 3) {\n score = Math.max(score, MIN_FILENAME_SIMILARITY + 0.1);\n }\n }\n\n if (score >= MIN_CONTENT_SIMILARITY) {\n scoredPairs.push({ block, blockIndex: bi, mp3, mp3Index: mi, score });\n }\n }\n }\n\n // Greedy assignment: highest score first, each mp3 and block used at most once\n scoredPairs.sort((a, b) => b.score - a.score);\n const usedBlocks = new Set<number>();\n const usedMp3s = new Set<number>();\n const matches: Array<{ block: Block; mp3: Mp3Info; score: number }> = [];\n\n for (const pair of scoredPairs) {\n if (usedBlocks.has(pair.blockIndex) || usedMp3s.has(pair.mp3Index)) continue;\n usedBlocks.add(pair.blockIndex);\n usedMp3s.add(pair.mp3Index);\n matches.push({ block: pair.block, mp3: pair.mp3, score: pair.score });\n }\n\n return matches;\n}\n\n// ── Public API ───────────────────────────────────────────────────────\n\n/**\n * Resolve audio mapping for a Doc using a ContentContainer.\n *\n * Tries explicit `{[audio=filename.mp3]}` annotations first,\n * then falls back to auto-matching by content similarity.\n *\n * Returns a new Doc with `audio.segments` populated and blocks\n * assigned to the correct `audioSegment` indices.\n *\n * @param doc - The source Doc (not mutated).\n * @param container - ContentContainer with MP3 and timing files.\n * @returns New Doc with audio segments resolved, or original doc if no matches.\n */\nexport async function resolveAudioMapping(doc: Doc, container: ContentContainer): Promise<Doc> {\n // Discover MP3 files and their timing data\n const mp3s = await discoverMp3s(container);\n if (mp3s.length === 0) return doc;\n\n // Try annotations first\n let matches = resolveFromAnnotations(doc.blocks, mp3s);\n\n // Fall back to auto-matching if no annotations found\n if (matches.length === 0) {\n matches = autoMatchBlocks(doc.blocks, mp3s).map((m) => ({\n block: m.block,\n mp3: m.mp3,\n }));\n }\n\n if (matches.length === 0) return doc;\n\n // Build audio segments in document order\n // Sort matches by the block's position in the flattened tree\n const flat = flattenForAudio(doc.blocks);\n const blockOrder = new Map(flat.map((b, i) => [b.id, i]));\n matches.sort((a, b) => (blockOrder.get(a.block.id) ?? 0) - (blockOrder.get(b.block.id) ?? 0));\n\n // Build segment list and assign indices\n const segments: AudioSegment[] = [];\n const blockToSegment = new Map<string, number>();\n let startTime = 0;\n\n for (let i = 0; i < matches.length; i++) {\n const { block, mp3 } = matches[i];\n const duration = mp3.timing?.duration ?? 30; // Fallback duration if no timing\n\n segments.push({\n src: mp3.path,\n name: block.title ?? block.id,\n duration,\n startTime,\n });\n\n blockToSegment.set(block.id, i);\n startTime += duration;\n }\n\n // Deep clone blocks and assign audioSegment indices\n const newBlocks = assignSegmentIndices(doc.blocks, blockToSegment);\n\n return {\n ...doc,\n blocks: newBlocks,\n duration: startTime,\n audio: { segments },\n };\n}\n\n/**\n * Recursively clone blocks and assign audioSegment indices.\n * Blocks not in the map keep their original audioSegment.\n * Children of a matched block inherit the parent's segment.\n */\nfunction assignSegmentIndices(\n blocks: Block[],\n blockToSegment: Map<string, number>,\n inheritedSegment?: number,\n): Block[] {\n return blocks.map((block) => {\n const segment = blockToSegment.get(block.id) ?? inheritedSegment ?? block.audioSegment;\n const newBlock: Block = {\n ...block,\n audioSegment: segment,\n };\n\n if (block.children) {\n newBlock.children = assignSegmentIndices(block.children, blockToSegment, segment);\n }\n\n return newBlock;\n });\n}\n","/**\n * Markdown document validation.\n *\n * `validateMarkdownSource()` gives authors and agents fast, structural\n * feedback on a squisq markdown document — the same checks the renderer\n * applies, surfaced as diagnostics instead of degraded slides:\n *\n * - unknown template names (with a did-you-mean suggestion)\n * - unknown drawing shapes, and shape annotations used outside a `{[drawing]}`\n * - `{[…]}` text that was not recognized as an annotation (bad quoting,\n * non-heading placement, unknown inline icon)\n * - malformed heading-attribute values (`x=abc`, bad `startTime`) and\n * non-numeric drawing-shape geometry\n * - `connectsTo` / drawing `from`/`to` targets that don't resolve,\n * duplicate block ids, unparseable data fences (reported by `markdownToDoc`)\n * - image references missing from the provided asset set (.dbk contents)\n *\n * The CLI exposes this as `squisq validate <input>`; agents can also call\n * it directly and iterate until the diagnostics list is empty.\n */\n\nimport type { Doc, Block, DocDiagnostic } from '../schemas/Doc.js';\nimport type {\n MarkdownDocument,\n MarkdownHeading,\n MarkdownNode,\n HtmlNode,\n} from '../markdown/types.js';\nimport { parseMarkdown } from '../markdown/parse.js';\nimport { getChildren, extractPlainText } from '../markdown/utils.js';\nimport { coerceAnnotationValues, KNOWN_BLOCK_META_KEYS } from '../markdown/annotationCoercion.js';\nimport { markdownToDoc, flattenBlocks } from './markdownToDoc.js';\nimport { parseStandaloneAnnotation } from './standaloneAnnotation.js';\nimport { isDataFence, parseDataFence } from './structuredData.js';\nimport { deriveTemplateInputs } from './templateInputs.js';\nimport { lintTemplateParams } from './templates/inputDescriptors.js';\nimport {\n templateRegistry,\n TEMPLATE_ALIASES,\n resolveTemplateName,\n isShapeName,\n normalizeShapeKind,\n SHAPE_NAMES,\n} from './templates/index.js';\nimport { nearestName } from './utils/nearest.js';\n\n// ============================================\n// Options & result\n// ============================================\n\nexport interface ValidateOptions {\n /**\n * Known asset paths for image/video reference checks — typically the\n * file list of a .dbk container or the document's folder. Relative\n * references not present here produce a `missing-asset` warning.\n * When omitted, asset checks are skipped.\n */\n assets?: ReadonlySet<string> | ((path: string) => boolean);\n\n /**\n * Additional template names to accept beyond the built-in registry and\n * the document's own `customTemplates` (e.g. app-registered templates).\n */\n extraTemplates?: readonly string[];\n}\n\nexport interface MarkdownValidationResult {\n /** All findings, in source order where line information exists. */\n diagnostics: DocDiagnostic[];\n errorCount: number;\n warningCount: number;\n infoCount: number;\n /** The converted Doc (conversion diagnostics are included above). */\n doc: Doc;\n}\n\n// ============================================\n// Entry points\n// ============================================\n\n/**\n * Validate a squisq markdown source string. Parses, converts (collecting\n * the conversion's own diagnostics), and runs the structural checks above.\n */\nexport function validateMarkdownSource(\n source: string,\n options?: ValidateOptions,\n): MarkdownValidationResult {\n return validateMarkdownDoc(parseMarkdown(source), options);\n}\n\n/**\n * Validate an already-parsed MarkdownDocument. See {@link validateMarkdownSource}.\n */\nexport function validateMarkdownDoc(\n markdownDoc: MarkdownDocument,\n options?: ValidateOptions,\n): MarkdownValidationResult {\n const doc = markdownToDoc(markdownDoc);\n const diagnostics: DocDiagnostic[] = [...(doc.diagnostics ?? [])];\n const blocks = flattenBlocks(doc.blocks);\n\n const knownTemplates = collectKnownTemplates(doc, options);\n // Custom template names are skipped by the input linter — they carry\n // arbitrary author-defined inputs the built-in descriptors can't know about.\n const customNames = new Set<string>(doc.customTemplates?.map((t) => t.name) ?? []);\n\n // Parent-aware walk: a `{[shape]}` annotation is only meaningful on the\n // direct child of a `{[drawing]}` block, so the template check needs each\n // block's parent. Drawing connectors are validated against their siblings.\n const visit = (block: Block, parent: Block | undefined): void => {\n checkTemplate(block, parent, knownTemplates, diagnostics);\n checkAttributeCoercion(block, parent, diagnostics);\n checkConflictingAnnotationKeys(block, diagnostics);\n checkTemplateInputs(block, parent, customNames, diagnostics);\n checkPossibleDataFences(block, diagnostics);\n if (isDrawingBlock(block)) checkDrawingConnectors(block, diagnostics);\n for (const child of block.children ?? []) visit(child, block);\n };\n for (const block of doc.blocks) visit(block, undefined);\n\n checkUnrecognizedAnnotations(markdownDoc, diagnostics);\n\n if (options?.assets) {\n const hasAsset = normalizeAssetCheck(options.assets);\n checkAssets(markdownDoc, blocks, hasAsset, diagnostics);\n }\n\n diagnostics.sort(\n (a, b) => (a.line ?? Number.MAX_SAFE_INTEGER) - (b.line ?? Number.MAX_SAFE_INTEGER),\n );\n\n return {\n diagnostics,\n errorCount: diagnostics.filter((d) => d.severity === 'error').length,\n warningCount: diagnostics.filter((d) => d.severity === 'warning').length,\n infoCount: diagnostics.filter((d) => d.severity === 'info').length,\n doc,\n };\n}\n\n// ============================================\n// Template checks\n// ============================================\n\nfunction collectKnownTemplates(doc: Doc, options?: ValidateOptions): Set<string> {\n return new Set<string>([\n ...Object.keys(templateRegistry),\n ...Object.keys(TEMPLATE_ALIASES),\n ...(doc.customTemplates?.map((t) => t.name) ?? []),\n ...(options?.extraTemplates ?? []),\n ]);\n}\n\n/** True when `block` is a `{[drawing]}` container (after alias resolution). */\nfunction isDrawingBlock(block: Block): boolean {\n return resolveTemplateName(block.template ?? '') === 'drawing';\n}\n\nfunction checkTemplate(\n block: Block,\n parent: Block | undefined,\n known: Set<string>,\n diagnostics: DocDiagnostic[],\n): void {\n // Heading blocks carry the requested name on the heading annotation;\n // heading-less standalone blocks carry it on `sourceAnnotation` (raw,\n // un-resolved) — read whichever is present.\n const requested =\n block.sourceHeading?.templateAnnotation?.template ?? block.sourceAnnotation?.template;\n if (!requested) return;\n\n // Inside a drawing, the annotation names a shape primitive, not a template.\n if (parent && isDrawingBlock(parent)) {\n if (isShapeName(requested)) return;\n const suggestion = nearestName(requested, new Set(SHAPE_NAMES), {\n map: resolveTemplateName,\n });\n diagnostics.push({\n severity: 'warning',\n code: 'unknown-shape',\n message:\n `Unknown shape \"${requested}\" in drawing — the child will be skipped` +\n (suggestion ? `. Did you mean \"${suggestion}\"?` : ''),\n blockId: block.id,\n ...lineOf(block),\n });\n return;\n }\n\n // A shape annotation outside a drawing has no parent to interpret it.\n if (isShapeName(requested)) {\n diagnostics.push({\n severity: 'warning',\n code: 'shape-outside-drawing',\n message: `Shape \"${requested}\" is only recognized on a child heading of a {[drawing]} block`,\n blockId: block.id,\n ...lineOf(block),\n });\n return;\n }\n\n if (known.has(requested) || known.has(resolveTemplateName(requested))) return;\n\n const suggestion = nearestName(requested, known, { map: resolveTemplateName });\n diagnostics.push({\n severity: 'warning',\n code: 'unknown-template',\n message:\n `Unknown template \"${requested}\" — the block will render as a plain fallback card` +\n (suggestion ? `. Did you mean \"${suggestion}\"?` : ''),\n blockId: block.id,\n ...lineOf(block),\n });\n}\n\n/**\n * Lint a template block's `{[…]}` inputs against the built-in input\n * descriptors: unknown keys (with a did-you-mean), values outside a closed\n * enum / that fail coercion, and missing required inputs. All are warnings.\n *\n * Only built-in templates with descriptors are checked — shapes, drawing\n * children, custom templates, and not-yet-described built-ins are skipped\n * (`lintTemplateParams` returns `[]` for the latter). A `missing-input`\n * finding is suppressed when the value is actually satisfiable from the\n * block's own fields, structured data, or body derivation.\n */\nfunction checkTemplateInputs(\n block: Block,\n parent: Block | undefined,\n customNames: ReadonlySet<string>,\n diagnostics: DocDiagnostic[],\n): void {\n const requested = block.sourceHeading?.templateAnnotation?.template;\n if (!requested) return;\n // Inside a drawing, the annotation names a shape primitive, not a template.\n if (parent && isDrawingBlock(parent)) return;\n if (isShapeName(requested)) return;\n\n const canonical = resolveTemplateName(requested);\n if (customNames.has(requested) || customNames.has(canonical)) return;\n\n const params = block.sourceHeading?.templateAnnotation?.params ?? {};\n for (const finding of lintTemplateParams(canonical, params)) {\n if (finding.kind === 'missing-input' && isInputSatisfiable(canonical, block, finding.key)) {\n continue;\n }\n diagnostics.push({\n severity: 'warning',\n code: finding.kind,\n message: `Block \"${block.id}\": ${finding.message}`,\n blockId: block.id,\n ...lineOf(block),\n });\n }\n}\n\n/**\n * Whether a required template input is satisfiable even though it's absent\n * from the `{[…]}` params: present in a structured-data fence, carried as a\n * block-level field (e.g. `title` from the heading), or derivable from the\n * heading + body (`deriveTemplateInputs` in strict mode).\n */\nfunction isInputSatisfiable(canonical: string, block: Block, key: string): boolean {\n if (block.templateData && key in block.templateData) return true;\n if ((block as unknown as Record<string, unknown>)[key] != null) return true;\n const derived = deriveTemplateInputs(canonical, block.title ?? '', block.contents);\n return derived != null && key in derived;\n}\n\n/** Numeric geometry keys carried in a shape's `{[…]}` params. */\nconst NUMERIC_SHAPE_KEYS = ['x', 'y', 'width', 'height', 'w', 'h', 'strokeWidth', 'borderRadius'];\n\n/** Re-run heading-attribute coercion to surface its warnings with context. */\nfunction checkAttributeCoercion(\n block: Block,\n parent: Block | undefined,\n diagnostics: DocDiagnostic[],\n): void {\n // Pandoc `{#id key=value}` params (x/y/connectsTo/startTime/duration).\n const params = block.sourceHeading?.attributes?.params;\n if (params) {\n const { warnings } = coerceAnnotationValues(params);\n for (const warning of warnings) {\n diagnostics.push({\n severity: 'warning',\n code: 'invalid-attribute',\n message: `Block \"${block.id}\": ${warning}`,\n blockId: block.id,\n ...lineOf(block),\n });\n }\n }\n\n // A drawing shape's geometry lives in its `{[shape …]}` params\n // (templateOverrides), not the Pandoc block — validate those numerics too.\n if (parent && isDrawingBlock(parent) && isShapeName(block.template)) {\n const overrides = block.templateOverrides ?? {};\n for (const key of NUMERIC_SHAPE_KEYS) {\n const raw = overrides[key];\n if (raw == null) continue;\n const value = raw.replace(/,\\s*$/, '').trim();\n if (value !== '' && !Number.isFinite(Number(value))) {\n diagnostics.push({\n severity: 'warning',\n code: 'invalid-attribute',\n message: `Drawing shape \"${block.id}\": \"${key}\" is not a number (${JSON.stringify(raw)})`,\n blockId: block.id,\n ...lineOf(block),\n });\n }\n }\n }\n}\n\n/**\n * A heading can carry the same block-meta key in both the Pandoc `{k=v}`\n * block and the squiggly `{[tpl k=v]}` annotation. The Pandoc value wins\n * (same precedence as `makeBlock` in markdownToDoc), which is easy to miss\n * when the two disagree — surface an info diagnostic so the author knows\n * which value is in effect.\n */\nfunction checkConflictingAnnotationKeys(block: Block, diagnostics: DocDiagnostic[]): void {\n const annotationParams = block.sourceHeading?.templateAnnotation?.params;\n const pandocParams = block.sourceHeading?.attributes?.params;\n if (!annotationParams || !pandocParams) return;\n\n for (const key of Object.keys(KNOWN_BLOCK_META_KEYS)) {\n const fromAnnotation = annotationParams[key];\n const fromPandoc = pandocParams[key];\n if (fromAnnotation == null || fromPandoc == null || fromAnnotation === fromPandoc) continue;\n diagnostics.push({\n severity: 'info',\n code: 'conflicting-annotation-key',\n message:\n `\"${key}\" is set in both {[…]} (${fromAnnotation}) and {…} (${fromPandoc}) — ` +\n `the {…} value wins.`,\n blockId: block.id,\n ...lineOf(block),\n });\n }\n}\n\n/**\n * A drawing's `line`/`arrow` connectors reference sibling shapes by id via\n * `from`/`to`; flag any that don't resolve (mirrors the `connectsTo`\n * unresolved-connection check, but for the `{[…]}` param form).\n */\nfunction checkDrawingConnectors(block: Block, diagnostics: DocDiagnostic[]): void {\n const children = block.children ?? [];\n const ids = new Set(children.map((c) => c.id));\n for (const child of children) {\n const kind = normalizeShapeKind(child.sourceHeading?.templateAnnotation?.template);\n if (kind !== 'line' && kind !== 'arrow') continue;\n const overrides = child.templateOverrides ?? {};\n for (const key of ['from', 'to'] as const) {\n const raw = overrides[key];\n if (raw == null) continue;\n const ref = raw.replace(/,\\s*$/, '').trim();\n if (ref && !ids.has(ref)) {\n diagnostics.push({\n severity: 'warning',\n code: 'unresolved-connection',\n message: `Drawing connector \"${child.id}\" references unknown ${key} \"${ref}\"`,\n blockId: child.id,\n ...lineOf(child),\n });\n }\n }\n }\n}\n\n/**\n * A plain ```json / ```yaml fence (no `data` marker) inside a template-\n * annotated block, whose content parses as a top-level object, is almost\n * certainly meant as template input — surface an info nudge to add the\n * `data` marker. Deliberately scoped to *annotated* blocks so ordinary code\n * samples in prose sections never trigger it.\n */\nfunction checkPossibleDataFences(block: Block, diagnostics: DocDiagnostic[]): void {\n const annotated = !!(\n block.sourceHeading?.templateAnnotation?.template || block.sourceAnnotation?.template\n );\n if (!annotated) return;\n\n for (const node of block.contents ?? []) {\n if (node.type !== 'code') continue;\n const lang = node.lang?.toLowerCase();\n if (lang !== 'json' && lang !== 'yaml' && lang !== 'yml') continue;\n if (isDataFence(node)) continue; // already a data fence — nothing to nudge\n\n const result = parseDataFence(node);\n if (!result.data || Object.keys(result.data).length === 0) continue;\n\n diagnostics.push({\n severity: 'info',\n code: 'possible-data-fence',\n message:\n `This ${lang} block renders as code — add \"data\" to the fence ` +\n `(\\`\\`\\`${lang} data) if it's meant as template input.`,\n blockId: block.id,\n ...(node.position ? { line: node.position.start.line } : lineOf(block)),\n });\n }\n}\n\n// ============================================\n// Unrecognized `{[…]}` text\n// ============================================\n\nconst ANNOTATION_LIKE_RE = /\\{\\[/;\n\n/**\n * Flag literal `{[` text that survived parsing. On a heading this usually\n * means broken quoting; in body content it usually means the author\n * expected list-item/paragraph annotations, which only headings support\n * (or referenced an unknown inline icon).\n */\nfunction checkUnrecognizedAnnotations(\n markdownDoc: MarkdownDocument,\n diagnostics: DocDiagnostic[],\n): void {\n const walk = (node: MarkdownNode, headingLine: number | undefined, topLevel: boolean): void => {\n if (node.type === 'heading') {\n const heading = node as MarkdownHeading;\n const text = extractPlainText(heading);\n if (ANNOTATION_LIKE_RE.test(text)) {\n diagnostics.push({\n severity: 'warning',\n code: 'unparsed-annotation',\n message:\n `Heading \"${truncate(text)}\" contains \"{[…]}\" text that was not recognized — ` +\n `check the quoting, or whether the token is a known inline icon`,\n ...(heading.position ? { line: heading.position.start.line } : {}),\n });\n }\n return; // heading inline children already covered by extractPlainText\n }\n // A *top-level* paragraph that is exactly a `{[templateName …]}` /\n // `{[audio …]}` annotation is recognized (it becomes a standalone template\n // block or a media clip) — don't flag it, and don't descend into its text.\n // Nested paragraphs (inside a list / blockquote) are NOT extracted, so\n // they still warn.\n if (node.type === 'paragraph' && topLevel) {\n const parsed = parseStandaloneAnnotation(node);\n if (parsed && parsed.template != null) return;\n }\n if (node.type === 'text' && ANNOTATION_LIKE_RE.test(node.value)) {\n diagnostics.push({\n severity: 'warning',\n code: 'unparsed-annotation',\n message:\n `Body text contains \"{[…]}\" — template annotations are recognized on headings ` +\n `and as standalone paragraphs (or the token is not a known inline icon)`,\n ...(node.position\n ? { line: node.position.start.line }\n : headingLine != null\n ? { line: headingLine }\n : {}),\n });\n return;\n }\n if (node.type === 'code') return; // code blocks legitimately contain {[\n for (const child of getChildren(node)) {\n walk(child, headingLine, false);\n }\n };\n\n for (const child of markdownDoc.children) {\n walk(child, child.position?.start.line, true);\n }\n}\n\n// ============================================\n// Asset checks\n// ============================================\n\nfunction normalizeAssetCheck(\n assets: ReadonlySet<string> | ((path: string) => boolean),\n): (path: string) => boolean {\n if (typeof assets === 'function') return assets;\n return (path: string) => assets.has(path) || assets.has(path.replace(/^\\.\\//, ''));\n}\n\nconst URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;\n\n/** A reference that should resolve inside the bundle: relative, no scheme. */\nfunction isBundleRelative(url: string): boolean {\n return (\n url.length > 0 && !URL_SCHEME_RE.test(url) && !url.startsWith('//') && !url.startsWith('#')\n );\n}\n\n/** Template param keys whose values reference media assets. */\nconst ASSET_PARAM_KEYS = ['src', 'imageSrc', 'videoSrc', 'poster', 'backgroundImage'];\n\nfunction checkAssets(\n markdownDoc: MarkdownDocument,\n blocks: Block[],\n hasAsset: (path: string) => boolean,\n diagnostics: DocDiagnostic[],\n): void {\n const report = (url: string, line?: number, blockId?: string) => {\n if (!isBundleRelative(url) || hasAsset(url)) return;\n diagnostics.push({\n severity: 'warning',\n code: 'missing-asset',\n message: `Referenced asset \"${url}\" was not found in the bundle`,\n ...(blockId ? { blockId } : {}),\n ...(line != null ? { line } : {}),\n });\n };\n\n // Markdown image nodes + raw-HTML <img src>.\n const walk = (node: MarkdownNode): void => {\n if (node.type === 'image') {\n report(node.url, node.position?.start.line);\n }\n if (node.type === 'htmlBlock' || node.type === 'htmlInline') {\n walkHtmlImages(node.htmlChildren, (src) => report(src, node.position?.start.line));\n }\n for (const child of getChildren(node)) walk(child);\n };\n for (const child of markdownDoc.children) walk(child);\n\n // Media references in template params / structured data.\n for (const block of blocks) {\n const sources: Array<Record<string, unknown> | undefined> = [\n block.templateOverrides,\n block.templateData,\n ];\n for (const source of sources) {\n if (!source) continue;\n for (const key of ASSET_PARAM_KEYS) {\n const value = source[key];\n if (typeof value === 'string') {\n report(value, block.sourceHeading?.position?.start.line, block.id);\n }\n }\n const images = source['images'];\n const list =\n typeof images === 'string'\n ? images.split(',').map((s) => s.trim())\n : Array.isArray(images)\n ? images.filter((i): i is string => typeof i === 'string')\n : [];\n for (const img of list) {\n report(img, block.sourceHeading?.position?.start.line, block.id);\n }\n }\n }\n}\n\nfunction walkHtmlImages(nodes: HtmlNode[], report: (src: string) => void): void {\n for (const node of nodes) {\n if (node.type !== 'htmlElement') continue;\n if (node.tagName === 'img' && node.attributes.src) {\n report(node.attributes.src);\n }\n walkHtmlImages(node.children, report);\n }\n}\n\n// ============================================\n// Helpers\n// ============================================\n\nfunction lineOf(block: Block): { line: number } | Record<string, never> {\n const line = block.sourceHeading?.position?.start.line;\n return line != null ? { line } : {};\n}\n\nfunction truncate(text: string, max = 40): string {\n return text.length <= max ? text : `${text.slice(0, max)}…`;\n}\n"],"mappings":"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","names":["DEFAULT_OPTS","PADDING","numParam","strParam","bodyText","round","emptyHint","bodyText","normalizeCandidates","applyBlockMeta","bodyText","getBlockBodyText","slugify","suggestion","lineOf"]}