@bendyline/squisq 0.1.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.
- package/dist/Types-sh2VRxfo.d.ts +15 -0
- package/dist/chunk-7UDSRZKG.js +103 -0
- package/dist/chunk-7UDSRZKG.js.map +1 -0
- package/dist/chunk-O5HCIUAD.js +577 -0
- package/dist/chunk-O5HCIUAD.js.map +1 -0
- package/dist/chunk-PJ7AODIM.js +3040 -0
- package/dist/chunk-PJ7AODIM.js.map +1 -0
- package/dist/chunk-QWVRN6I4.js +81 -0
- package/dist/chunk-QWVRN6I4.js.map +1 -0
- package/dist/chunk-S4F2RY37.js +92 -0
- package/dist/chunk-S4F2RY37.js.map +1 -0
- package/dist/chunk-URU6QMLY.js +888 -0
- package/dist/chunk-URU6QMLY.js.map +1 -0
- package/dist/chunk-VJN7UB2Z.js +145 -0
- package/dist/chunk-VJN7UB2Z.js.map +1 -0
- package/dist/doc/index.d.ts +3 -0
- package/dist/doc/index.js +126 -0
- package/dist/doc/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/index.d.ts +237 -0
- package/dist/markdown/index.js +33 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/schemas/index.d.ts +70 -0
- package/dist/schemas/index.js +61 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/spatial/index.d.ts +111 -0
- package/dist/spatial/index.js +25 -0
- package/dist/spatial/index.js.map +1 -0
- package/dist/storage/index.d.ts +91 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/story/index.d.ts +691 -0
- package/dist/story/index.js +126 -0
- package/dist/story/index.js.map +1 -0
- package/dist/themeLibrary-DySHPcgj.d.ts +1352 -0
- package/dist/types-DHiv_Pnm.d.ts +496 -0
- package/package.json +91 -0
- package/src/__tests__/animationUtils.test.ts +77 -0
- package/src/__tests__/geohash.test.ts +109 -0
- package/src/__tests__/getLayers.test.ts +200 -0
- package/src/__tests__/haversine.test.ts +63 -0
- package/src/__tests__/localForageAdapter.test.ts +155 -0
- package/src/__tests__/markdown.test.ts +806 -0
- package/src/__tests__/markdownToDoc.test.ts +374 -0
- package/src/__tests__/storage.test.ts +60 -0
- package/src/__tests__/templates.test.ts +247 -0
- package/src/doc/docToMarkdown.ts +98 -0
- package/src/doc/getLayers.ts +152 -0
- package/src/doc/index.ts +8 -0
- package/src/doc/markdownToDoc.ts +277 -0
- package/src/doc/templates/accentImage.ts +338 -0
- package/src/doc/templates/captionUtils.ts +33 -0
- package/src/doc/templates/comparisonBar.ts +140 -0
- package/src/doc/templates/coverBlock.ts +156 -0
- package/src/doc/templates/dateEvent.ts +122 -0
- package/src/doc/templates/definitionCard.ts +129 -0
- package/src/doc/templates/factCard.ts +114 -0
- package/src/doc/templates/fullBleedQuote.ts +59 -0
- package/src/doc/templates/imageWithCaption.ts +167 -0
- package/src/doc/templates/index.ts +571 -0
- package/src/doc/templates/listBlock.ts +101 -0
- package/src/doc/templates/mapBlock.ts +132 -0
- package/src/doc/templates/persistentLayers.ts +522 -0
- package/src/doc/templates/photoGrid.ts +199 -0
- package/src/doc/templates/pullQuote.ts +119 -0
- package/src/doc/templates/quoteBlock.ts +112 -0
- package/src/doc/templates/sectionHeader.ts +105 -0
- package/src/doc/templates/statHighlight.ts +113 -0
- package/src/doc/templates/titleBlock.ts +100 -0
- package/src/doc/templates/twoColumn.ts +239 -0
- package/src/doc/templates/videoPullQuote.ts +128 -0
- package/src/doc/templates/videoWithCaption.ts +126 -0
- package/src/doc/utils/animationUtils.ts +135 -0
- package/src/doc/utils/themeUtils.ts +139 -0
- package/src/index.ts +5 -0
- package/src/markdown/convert.ts +897 -0
- package/src/markdown/htmlParse.ts +212 -0
- package/src/markdown/index.ts +110 -0
- package/src/markdown/parse.ts +103 -0
- package/src/markdown/stringify.ts +122 -0
- package/src/markdown/types.ts +605 -0
- package/src/markdown/utils.ts +165 -0
- package/src/ngeohash.d.ts +11 -0
- package/src/schemas/BlockTemplates.ts +706 -0
- package/src/schemas/Doc.ts +565 -0
- package/src/schemas/LayoutStrategy.ts +178 -0
- package/src/schemas/MediaProvider.ts +70 -0
- package/src/schemas/Theme.ts +235 -0
- package/src/schemas/Types.ts +15 -0
- package/src/schemas/Viewport.ts +91 -0
- package/src/schemas/index.ts +14 -0
- package/src/schemas/themeLibrary.ts +492 -0
- package/src/spatial/Geohash.ts +168 -0
- package/src/spatial/Haversine.ts +59 -0
- package/src/spatial/index.ts +2 -0
- package/src/storage/LocalForageAdapter.ts +103 -0
- package/src/storage/LocalStorageAdapter.ts +57 -0
- package/src/storage/MemoryStorageAdapter.ts +37 -0
- package/src/storage/Storage.ts +28 -0
- package/src/storage/index.ts +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/doc/templates/persistentLayers.ts","../src/doc/templates/titleBlock.ts","../src/doc/utils/themeUtils.ts","../src/doc/templates/sectionHeader.ts","../src/doc/templates/accentImage.ts","../src/doc/templates/captionUtils.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/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/coverBlock.ts","../src/doc/templates/index.ts","../src/doc/utils/animationUtils.ts","../src/doc/markdownToDoc.ts","../src/doc/docToMarkdown.ts","../src/doc/getLayers.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 TitleCaptionConfig,\n CornerBrandingConfig,\n ProgressIndicatorConfig,\n} from '../../schemas/BlockTemplates.js';\nimport type { Theme } from '../../schemas/Theme.js';\nimport { isPersistentLayerTemplate } from '../../schemas/BlockTemplates.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-sunset': 'linear-gradient(135deg, rgba(124,58,48,0.9) 0%, rgba(26,32,44,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(26,32,44,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\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 },\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 const opacity = config.opacity ?? 0.1;\n const color = config.color ?? `rgba(255,255,255,${opacity})`;\n\n // Pattern as SVG data URI for rect patterns\n // For simplicity, use a solid subtle color with reduced opacity\n return {\n type: 'shape',\n id: 'persistent-bg-pattern',\n content: {\n shape: 'rect',\n fill: color,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\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): 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 '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(layers: PersistentLayer[] | undefined): 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[]>((acc, layer) => acc.concat(expandPersistentLayer(layer)), []);\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 * Title Block Template\n *\n * Large title with optional subtitle for doc intros.\n * Centered text with fade-in animations.\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\n\nexport function titleBlock(input: TitleBlockInput, context: TemplateContext): Layer[] {\n const { title, subtitle, backgroundColor } = input;\n const { theme, layout } = context;\n\n // Scale font sizes for viewport\n const titleFontSize = scaledFontSize(96, context, true);\n const subtitleFontSize = scaledFontSize(36, context, false);\n\n const baseBg = backgroundColor || theme.colors.primary;\n\n const layers: Layer[] = [\n // Background — radial gradient for depth instead of flat color\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: `radial-gradient(ellipse at 50% 40%, ${baseBg} 0%, ${theme.colors.background} 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n // Subtle decorative line above title\n {\n type: 'shape',\n id: 'accent-line',\n content: {\n shape: 'rect',\n fill: 'rgba(255, 255, 255, 0.2)',\n },\n position: {\n x: '40%',\n y: subtitle ? '28%' : '38%',\n width: '20%',\n height: '2px',\n },\n },\n // Title\n {\n type: 'text',\n id: 'title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: subtitle ? layout.primaryY : '50%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 2 },\n },\n ];\n\n // Add subtitle if provided\n if (subtitle) {\n layers.push({\n type: 'text',\n id: 'subtitle',\n content: {\n text: subtitle,\n style: {\n fontSize: subtitleFontSize,\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n },\n },\n position: {\n x: '50%',\n y: layout.secondaryY,\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 * 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 { AnimationType } from '../../schemas/Doc.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 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// 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// 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 * 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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme } from '../utils/themeUtils.js';\n\nexport function sectionHeader(input: SectionHeaderInput, context: TemplateContext): Layer[] {\n const { title, colorScheme = 'blue', imageSrc, imageAlt, ambientMotion } = input;\n const { layout } = context;\n const colors = resolveColorScheme(context, colorScheme);\n\n // Scale font sizes for viewport\n const titleFontSize = scaledFontSize(72, 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 },\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 // Dark overlay for text readability\n layers.push({\n type: 'shape',\n id: 'overlay',\n content: {\n shape: 'rect',\n fill: 'rgba(0, 0, 0, 0.5)',\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 - white text with shadow for readability over images\n layers.push({\n type: 'text',\n id: 'title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontWeight: 'bold',\n color: imageSrc ? '#ffffff' : colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: '50%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { 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 { Layer, ImageLayer, ShapeLayer, Animation } 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(accent: AccentImage, slideId: string): 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(createStripImage(src, alt, 'left', slideId, ambientMotion, credit, license));\n layers.push(createStripGradient('left', slideId));\n break;\n\n case 'right-strip':\n layers.push(createStripImage(src, alt, 'right', slideId, ambientMotion, credit, license));\n layers.push(createStripGradient('right', slideId));\n break;\n\n case 'bottom-strip':\n layers.push(createBottomStripImage(src, alt, slideId, ambientMotion, credit, license));\n layers.push(createBottomStripGradient(slideId));\n break;\n\n case 'corner-inset':\n layers.push(createCornerInsetImage(src, alt, slideId, ambientMotion, credit, license));\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): 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 },\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): 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 },\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): 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 },\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 * 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 * 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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme } from '../utils/themeUtils.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer } from './captionUtils.js';\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 = scaledFontSize(148, context, true);\n const descFontSize = scaledFontSize(32, context, false);\n const detailFontSize = scaledFontSize(26, context, false);\n\n const layers: Layer[] = [\n createBackgroundLayer(\n 'bg',\n `linear-gradient(180deg, ${theme.colors.background} 0%, #0f1520 100%)`,\n ),\n ];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(...createAccentLayers(accentImage, input.id));\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 fontWeight: 'bold',\n color: colors.text,\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('32%', accentLayout),\n anchor: 'center',\n },\n animation: { type: 'zoomIn', duration: 0.6 },\n });\n\n // Description — smaller and understated beneath the stat\n layers.push({\n type: 'text',\n id: 'description',\n content: {\n text: description,\n style: {\n fontSize: descFontSize,\n color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.6,\n shadow: !!accentImage,\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: 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 color: colors.accent,\n textAlign: 'center',\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('75%', accentLayout),\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.\n * Good for impactful statements or descriptions.\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer } from './captionUtils.js';\n\nexport function quoteBlock(input: QuoteBlockInput, context: TemplateContext): Layer[] {\n const { quote, attribution, accentImage } = input;\n const { theme, layout } = 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 = scaledFontSize(48, context, true);\n const attrFontSize = scaledFontSize(24, context, false);\n\n // Decorative quotation mark font size\n const decorativeQuoteFontSize = scaledFontSize(280, context, true);\n\n const layers: Layer[] = [\n createBackgroundLayer(\n 'bg',\n `linear-gradient(160deg, ${theme.colors.backgroundLight} 0%, #1e2636 100%)`,\n ),\n ];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(...createAccentLayers(accentImage, input.id));\n }\n\n // Decorative opening quotation mark — oversized, low-opacity behind quote\n layers.push({\n type: 'text',\n id: 'deco-quote',\n content: {\n text: '\\u201C',\n style: {\n fontSize: decorativeQuoteFontSize,\n color: 'rgba(255, 255, 255, 0.06)',\n textAlign: 'center',\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('18%', accentLayout),\n anchor: 'center',\n },\n });\n\n // Quote text - positioned based on accent layout\n const quoteY = attribution ? layout.primaryY : '50%';\n layers.push({\n type: 'text',\n id: 'quote',\n content: {\n text: quote,\n style: {\n fontSize: quoteFontSize,\n color: theme.colors.text,\n textAlign: 'center',\n lineHeight: 1.6,\n shadow: true,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(quoteY, accentLayout),\n anchor: 'center',\n width: accentLayout.textWidth,\n },\n animation: { type: 'fadeIn', duration: 2 },\n });\n\n // Add attribution if provided\n if (attribution) {\n layers.push({\n type: 'text',\n id: 'attribution',\n content: {\n text: `— ${attribution}`,\n style: {\n fontSize: attrFontSize,\n color: theme.colors.textMuted,\n textAlign: 'center',\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(layout.captionY, 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.\n * Useful for presenting important information clearly.\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\nimport { createBackgroundLayer } from './captionUtils.js';\n\nexport function factCard(input: FactCardInput, context: TemplateContext): Layer[] {\n const { fact, explanation, source, accentImage } = input;\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 // Scale font sizes for viewport\n const factFontSize = scaledFontSize(56, context, true);\n const explainFontSize = scaledFontSize(32, context, false);\n const sourceFontSize = scaledFontSize(20, context, false);\n\n const layers: Layer[] = [\n createBackgroundLayer(\n 'bg',\n `linear-gradient(170deg, #1e2030 0%, ${theme.colors.background} 100%)`,\n ),\n ];\n\n // Add accent image layers (behind text, after background)\n if (accentImage) {\n layers.push(...createAccentLayers(accentImage, input.id));\n }\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 fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n lineHeight: 1.4,\n shadow: true,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('35%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: { 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 color: theme.colors.textMuted,\n textAlign: 'center',\n lineHeight: 1.5,\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('60%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: { 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 color: theme.colors.textMuted,\n textAlign: 'center',\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('85%', 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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme } from '../utils/themeUtils.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\n const headerFontSize = scaledFontSize(32, context, false);\n const labelFontSize = scaledFontSize(44, context, true);\n const sublabelFontSize = scaledFontSize(22, context, false);\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: `linear-gradient(135deg, ${theme.colors.background} 0%, #16202e 100%)`,\n },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n if (!isStacked) {\n // Left column background panel\n layers.push({\n type: 'shape',\n id: 'left-panel',\n content: {\n shape: 'rect',\n fill: 'rgba(255, 255, 255, 0.04)',\n },\n position: { x: '3%', y: '15%', width: '44%', height: '70%' },\n });\n\n // Right column background panel\n layers.push({\n type: 'shape',\n id: 'right-panel',\n content: {\n shape: 'rect',\n fill: 'rgba(255, 255, 255, 0.04)',\n },\n position: { x: '53%', y: '15%', width: '44%', height: '70%' },\n });\n }\n\n // Header position adjusts based on layout\n const headerY = isStacked ? '12%' : '22%';\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 color: theme.colors.textMuted,\n textAlign: 'center',\n },\n },\n position: { x: '50%', y: headerY, anchor: 'center' },\n animation: { 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 panels (15%-85%)\n const baseY = header ? '47%' : '45%';\n const sublabelOffset = header ? '58%' : '56%';\n leftY = baseY;\n leftSublabelY = sublabelOffset;\n rightY = baseY;\n rightSublabelY = sublabelOffset;\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 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: { 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 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 (bold arrow)\n const connectorFontSize = scaledFontSize(120, context, true);\n const connectorY = isStacked ? '48%' : '50%'; // Centered in viewport\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 fontWeight: 'bold',\n color: 'rgba(255, 255, 255, 0.6)',\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 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: { 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 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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\n\nconst MOOD_COLORS = {\n neutral: { bg: '#1a202c', date: '#63b3ed', text: '#ffffff' },\n somber: { bg: '#1a1a2e', date: '#e53e3e', text: '#a0aec0' },\n celebratory: { bg: '#1a365d', date: '#68d391', text: '#ffffff' },\n};\n\nexport function dateEvent(input: DateEventInput, context: TemplateContext): Layer[] {\n const { date, description, footer, mood = 'neutral', accentImage } = input;\n const { theme } = context;\n const colors = MOOD_COLORS[mood];\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 = scaledFontSize(96, context, true);\n const descFontSize = scaledFontSize(30, context, false);\n const footerFontSize = scaledFontSize(26, context, false);\n\n const layers: Layer[] = [\n // Background — unique diagonal gradient to differentiate from other dark templates\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: `linear-gradient(135deg, ${colors.bg} 0%, #0d1117 100%)`,\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(...createAccentLayers(accentImage, input.id));\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 fontWeight: 'bold',\n color: colors.date,\n shadow: !!accentImage,\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 color: colors.text,\n textAlign: 'center',\n lineHeight: 1.8,\n shadow: !!accentImage,\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 color: theme.colors.textMuted,\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('82%', 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 { scaledFontSize } from '../../schemas/BlockTemplates.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 // Scale font sizes for viewport\n const captionFontSize = scaledFontSize(36, context, false);\n const titleFontSize = scaledFontSize(96, context, true);\n const subtitleFontSize = scaledFontSize(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 },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: imageAnimation,\n },\n ];\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, 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 // 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 fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\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 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 // Subtle gradient at bottom for caption readability (raised above media controls)\n layers.push({\n type: 'shape',\n id: 'caption-gradient',\n content: {\n shape: 'rect',\n fill: 'linear-gradient(0deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.3) 60%, transparent 100%)',\n },\n position: {\n x: 0,\n y: '65%',\n width: '100%',\n height: '35%',\n },\n });\n\n // Caption text - raised above media controls\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: '82%',\n anchor: 'center',\n width: layout.maxTextWidth,\n },\n animation: { type: 'fadeIn', duration: 1.5, delay: 0.5 },\n });\n }\n\n return layers;\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 { scaledFontSize } from '../../schemas/BlockTemplates.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 = scaledFontSize(64, context, true);\n const captionFontSize = scaledFontSize(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 // Add title overlay if provided\n if (title) {\n // Gradient overlay at top for title readability\n layers.push({\n type: 'shape',\n id: 'title-overlay',\n content: {\n shape: 'rect',\n fill: 'rgba(0,0,0,0.6)',\n },\n position: { x: 0, y: 0, width: '100%', height: '18%' },\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 fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\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 // Gradient overlay at bottom for caption readability\n layers.push({\n type: 'shape',\n id: 'caption-overlay',\n content: {\n shape: 'rect',\n fill: 'rgba(0,0,0,0.6)',\n },\n position: { x: 0, y: '82%', width: '100%', height: '18%' },\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 color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme } from '../utils/themeUtils.js';\n\nexport function fullBleedQuote(input: FullBleedQuoteInput, context: TemplateContext): Layer[] {\n const { text, colorScheme = 'blue' } = input;\n const { theme } = context;\n const colors = resolveColorScheme(context, colorScheme);\n\n // Massive font for dramatic impact\n const textFontSize = scaledFontSize(120, context, true);\n\n return [\n // Background — dark radial vignette for cinematic feel\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: `radial-gradient(ellipse at 50% 50%, ${theme.colors.backgroundLight} 0%, #000000 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 fontWeight: 'bold',\n color: colors.text,\n textAlign: 'center',\n lineHeight: 1.2,\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: '50%',\n anchor: 'center',\n width: '85%',\n },\n animation: { 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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';\n\nexport function listBlock(input: ListBlockInput, context: TemplateContext): Layer[] {\n const { items, title, accentImage } = input;\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 = scaledFontSize(44, context, true);\n const itemFontSize = scaledFontSize(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(...createAccentLayers(accentImage, input.id));\n }\n\n // Title if provided\n const startY = title ? 30 : 22;\n if (title) {\n layers.push({\n type: 'text',\n id: 'list-title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('16%', accentLayout),\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1 },\n });\n }\n\n // Calculate vertical spacing for items\n const endY = 80;\n const spacing = items.length > 1 ? (endY - startY) / (items.length - 1) : 0;\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 color: theme.colors.text,\n lineHeight: 1.4,\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY(`${y}%`, accentLayout),\n anchor: 'center',\n width: accentLayout.textWidth,\n },\n animation: { type: 'fadeIn', duration: 0.8, delay: 0.3 + 0.3 * i },\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { cleanCaption } from './captionUtils.js';\n\n/** Gap between images as percentage */\nconst GAP = 0.5;\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 layers: Layer[] = [\n // Black background (visible in gaps between images)\n {\n type: 'shape',\n id: 'bg',\n content: { shape: 'rect', fill: '#000000' },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n },\n ];\n\n // Reserve bottom space for caption — more room in landscape for larger text\n const captionHeight = caption ? (layout.stackColumns ? 18 : 22) : 0;\n const gridHeight = 100 - captionHeight;\n\n // Generate image positions based on count; stack vertically in portrait\n const positions = getGridPositions(images.length, gridHeight, layout.stackColumns);\n\n const altFontSize = scaledFontSize(24, context, false);\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 // Alt-text label on placeholder\n if (img.alt) {\n layers.push({\n type: 'text',\n id: `grid-alt-${i}`,\n content: {\n text: img.alt,\n style: {\n fontSize: altFontSize,\n color: theme.colors.textMuted,\n textAlign: 'center' as const,\n },\n },\n position: { x: `${pos.x + pos.w / 2}%`, y: `${pos.y + pos.h / 2}%`, anchor: 'center' },\n });\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 },\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 with gradient overlay\n if (caption) {\n const captionFontSize = scaledFontSize(layout.stackColumns ? 28 : 40, context, false);\n\n layers.push({\n type: 'shape',\n id: 'caption-bg',\n content: {\n shape: 'rect',\n fill: 'linear-gradient(transparent, rgba(0,0,0,0.8))',\n },\n position: { x: 0, y: `${gridHeight - 4}%`, width: '100%', height: `${captionHeight + 4}%` },\n });\n\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: `${gridHeight + (layout.stackColumns ? captionHeight / 2 : captionHeight * 0.3)}%`,\n anchor: 'center',\n width: '90%',\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme } 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 = scaledFontSize(72, context, true);\n const defFontSize = scaledFontSize(32, context, false);\n const originFontSize = scaledFontSize(22, context, false);\n\n const layers: Layer[] = [\n createBackgroundLayer(\n 'bg',\n `linear-gradient(145deg, #1e2030 0%, ${theme.colors.background} 100%)`,\n ),\n ];\n\n // Add accent image layers\n if (accentImage) {\n layers.push(...createAccentLayers(accentImage, input.id));\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 fontWeight: 'bold',\n color: colors.text,\n textAlign: 'center',\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('30%', accentLayout),\n anchor: 'center',\n },\n animation: { 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 color: theme.colors.text,\n textAlign: 'center',\n lineHeight: 1.6,\n maxLines: 4,\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('55%', accentLayout),\n width: accentLayout.textWidth,\n anchor: 'center',\n },\n animation: { 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 color: theme.colors.textMuted,\n textAlign: 'center',\n shadow: !!accentImage,\n },\n },\n position: {\n x: accentLayout.textCenterX,\n y: adjustY('78%', 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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport { resolveColorScheme } from '../utils/themeUtils.js';\n\nexport function comparisonBar(input: ComparisonBarInput, context: TemplateContext): Layer[] {\n const { leftLabel, leftValue, rightLabel, rightValue, unit, colorScheme = 'blue' } = input;\n const { theme } = context;\n const colors = resolveColorScheme(context, colorScheme);\n\n const labelFontSize = scaledFontSize(28, context, false);\n const valueFontSize = scaledFontSize(48, context, true);\n\n // Calculate proportional bar widths (max bar = 65% of viewport width)\n const maxValue = Math.max(leftValue, rightValue, 1);\n const maxBarWidth = 65;\n const leftBarWidth = (leftValue / maxValue) * maxBarWidth;\n const rightBarWidth = (rightValue / maxValue) * maxBarWidth;\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 // Format values for display\n const formatValue = (v: number): string => {\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 return [\n // Background\n {\n type: 'shape',\n id: 'bg',\n content: {\n shape: 'rect',\n fill: `linear-gradient(180deg, ${theme.colors.background} 0%, #0f1520 100%)`,\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: { fontSize: labelFontSize, color: theme.colors.textMuted },\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: { fontSize: valueFontSize, fontWeight: 'bold', color: colors.text },\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: { fontSize: labelFontSize, color: theme.colors.textMuted },\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: { fontSize: valueFontSize, fontWeight: 'bold', color: colors.accent },\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\n\nexport function pullQuote(input: PullQuoteInput, context: TemplateContext): Layer[] {\n const { text, attribution, backgroundImage, ambientMotion } = input;\n\n // Guard: backgroundImage is required\n if (!backgroundImage?.src) return [];\n\n const quoteFontSize = scaledFontSize(52, context, true);\n const attrFontSize = scaledFontSize(24, context, false);\n const decoFontSize = scaledFontSize(200, context, true);\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 },\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 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 color: '#ffffff',\n textAlign: 'center',\n lineHeight: 1.5,\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: attribution ? '45%' : '50%',\n anchor: 'center',\n width: '80%',\n },\n animation: { type: 'fadeIn', duration: 2 },\n },\n ];\n\n // Attribution\n if (attribution) {\n layers.push({\n type: 'text',\n id: 'attribution',\n content: {\n text: `\\u2014 ${attribution}`,\n style: {\n fontSize: attrFontSize,\n color: 'rgba(255, 255, 255, 0.7)',\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: '72%',\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 { scaledFontSize } from '../../schemas/BlockTemplates.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, layout } = context;\n\n const captionFontSize = scaledFontSize(36, context, false);\n const creditFontSize = scaledFontSize(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 at bottom with gradient for readability\n if (caption) {\n layers.push({\n type: 'shape',\n id: 'caption-gradient',\n content: {\n shape: 'rect',\n fill: 'linear-gradient(0deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.3) 60%, transparent 100%)',\n },\n position: {\n x: 0,\n y: captionPosition === 'top' ? 0 : '65%',\n width: '100%',\n height: '35%',\n },\n });\n\n const captionY =\n captionPosition === 'top' ? '15%' : captionPosition === 'center' ? '50%' : '82%';\n\n layers.push({\n type: 'text',\n id: 'caption',\n content: {\n text: caption,\n style: {\n fontSize: captionFontSize,\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: captionY,\n anchor: 'center',\n width: layout.maxTextWidth,\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 color: 'rgba(255, 255, 255, 0.5)',\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\n\nexport function videoPullQuote(input: VideoPullQuoteInput, context: TemplateContext): Layer[] {\n const { text, attribution, backgroundVideo } = input;\n\n // Guard: backgroundVideo is required\n if (!backgroundVideo?.src) return [];\n\n const quoteFontSize = scaledFontSize(52, context, true);\n const attrFontSize = scaledFontSize(24, context, false);\n const decoFontSize = scaledFontSize(200, context, true);\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 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 color: '#ffffff',\n textAlign: 'center',\n lineHeight: 1.5,\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: attribution ? '45%' : '50%',\n anchor: 'center',\n width: '80%',\n },\n animation: { type: 'fadeIn', duration: 2 },\n },\n ];\n\n // Attribution\n if (attribution) {\n layers.push({\n type: 'text',\n id: 'attribution',\n content: {\n text: `\\u2014 ${attribution}`,\n style: {\n fontSize: attrFontSize,\n color: 'rgba(255, 255, 255, 0.7)',\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: '72%',\n anchor: 'center',\n },\n animation: { type: 'fadeIn', duration: 1, delay: 1.5 },\n });\n }\n\n return layers;\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 { scaledFontSize } from '../../schemas/BlockTemplates.js';\nimport type { StartBlockConfig } from '../../schemas/Doc.js';\nimport { mapAmbientMotion } from './accentImage.js';\n\n/**\n * Input for coverBlock template - matches StartBlockConfig\n */\nexport interface CoverBlockInput {\n /** Path to hero image */\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}\n\n/**\n * Generate cover block layers from StartBlockConfig.\n */\nexport function coverBlock(input: CoverBlockInput, context: TemplateContext): Layer[] {\n const { heroSrc, heroAlt, title, subtitle, ambientMotion, heroCredit, heroLicense } = input;\n const { theme, layout } = context;\n\n // Scale font sizes for viewport - use larger sizes for the cover\n const titleFontSize = scaledFontSize(96, context, true);\n const subtitleFontSize = scaledFontSize(36, context, false);\n\n // Determine Ken Burns animation\n const imageAnimation = mapAmbientMotion(ambientMotion);\n\n const layers: Layer[] = [\n // Full-screen hero image\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 },\n position: { x: 0, y: 0, width: '100%', height: '100%' },\n animation: imageAnimation,\n },\n // Gradient overlay for text readability - from bottom\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 // Title - positioned lower in the frame to work with hero composition\n {\n type: 'text',\n id: 'cover-title',\n content: {\n text: title,\n style: {\n fontSize: titleFontSize,\n fontWeight: 'bold',\n color: theme.colors.text,\n textAlign: 'center',\n shadow: true,\n },\n },\n position: {\n x: '50%',\n y: subtitle ? '70%' : '75%',\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\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 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/**\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 { isTemplateBlock, createTemplateContext } from '../../schemas/BlockTemplates.js';\nimport { DEFAULT_THEME as defaultTheme } from '../../schemas/themeLibrary.js';\nimport { expandPersistentLayers } from './persistentLayers.js';\nimport type { ViewportConfig } from '../../schemas/Viewport.js';\nimport { VIEWPORT_PRESETS } from '../../schemas/Viewport.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 { 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';\n\n/**\n * Registry mapping template names to their implementation functions.\n * Note: coverBlock is not in the registry as it's used directly for start blocks,\n * not as a regular template in the block sequence.\n */\nexport const templateRegistry: TemplateRegistry = {\n titleBlock,\n sectionHeader,\n statHighlight,\n quoteBlock,\n factCard,\n twoColumn,\n dateEvent,\n imageWithCaption,\n mapBlock,\n fullBleedQuote,\n listBlock,\n photoGrid,\n definitionCard,\n comparisonBar,\n pullQuote,\n videoWithCaption,\n videoPullQuote,\n};\n\n/**\n * Expand a template block into a full Block with layers.\n */\nexport function expandTemplateBlock(templateBlock: TemplateBlock, context: TemplateContext): Block {\n const templateFn = templateRegistry[templateBlock.template];\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 // Generate layers from template with error handling\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 layers = (templateFn as (input: TemplateBlock, ctx: TemplateContext) => Layer[])(\n templateBlock,\n context,\n );\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\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 */\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 } = opts;\n const totalBlocks = blocks.length;\n\n // Pre-expand persistent layers once\n const bottomLayers = expandPersistentLayers(persistentLayers?.bottomLayers);\n const topLayers = expandPersistentLayers(persistentLayers?.topLayers);\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 expandedBlock = isTemplateBlock(block)\n ? expandTemplateBlock(block, context)\n : (block as Block);\n\n // Inject persistent layers\n const templateBlock = block as TemplateBlock;\n const useBottom = templateBlock.useBottomLayer !== false;\n const useTop = templateBlock.useTopLayer !== false;\n if (bottomLayers.length > 0 || topLayers.length > 0) {\n expandedBlock.layers = [\n ...(useBottom ? bottomLayers : []),\n ...(expandedBlock.layers ?? []),\n ...(useTop ? topLayers : []),\n ];\n }\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 expandedBlock = isTemplateBlock(block)\n ? expandTemplateBlock(block, context)\n : (block as Block);\n\n const templateBlock = block as TemplateBlock;\n const useBottom = templateBlock.useBottomLayer !== false;\n const useTop = templateBlock.useTopLayer !== false;\n if (bottomLayers.length > 0 || topLayers.length > 0) {\n expandedBlock.layers = [\n ...(useBottom ? bottomLayers : []),\n ...(expandedBlock.layers ?? []),\n ...(useTop ? topLayers : []),\n ];\n }\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 expandedBlock = isTemplateBlock(block)\n ? expandTemplateBlock(block, context)\n : (block as Block);\n\n const templateBlock = block as TemplateBlock;\n const useBottom = templateBlock.useBottomLayer !== false;\n const useTop = templateBlock.useTopLayer !== false;\n if (bottomLayers.length > 0 || topLayers.length > 0) {\n expandedBlock.layers = [\n ...(useBottom ? bottomLayers : []),\n ...(expandedBlock.layers ?? []),\n ...(useTop ? topLayers : []),\n ];\n }\n\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.\n */\nexport function hasTemplate(name: string): boolean {\n return 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 { expandPersistentLayers, getDocStyleConfig } 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 { 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';\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 { Animation, AnimationType, TransitionType } 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(type: TransitionType, entering: boolean): string {\n const mode = entering ? 'enter' : 'exit';\n return `transition-${type}-${mode}`;\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 * 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 { Doc, Block } from '../schemas/Doc.js';\nimport type { MarkdownDocument, MarkdownBlockNode, MarkdownHeading } from '../markdown/types.js';\nimport { extractPlainText } from '../markdown/utils.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// ============================================\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.\n */\nfunction createIdGenerator() {\n const used = new Map<string, number>();\n\n return (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\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 */\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 generateId = options?.generateId ?? createIdGenerator();\n\n const rootBlocks: Block[] = [];\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 const id = heading ? generateId(heading, headingIndex++) : 'preamble';\n\n // Use template from annotation if present, otherwise fall back to default\n const annotation = heading?.templateAnnotation;\n const template = annotation?.template ?? (heading ? defaultTemplate : undefined);\n\n const block: Block = {\n id,\n startTime: 0,\n duration: defaultDuration,\n audioSegment: 0,\n template,\n ...(heading ? { sourceHeading: heading } : {}),\n };\n\n // Propagate key-value params from annotation to templateOverrides\n if (annotation?.params) {\n block.templateOverrides = annotation.params;\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 // Calculate basic timing\n const allBlocks = flattenBlocks(rootBlocks);\n let currentTime = 0;\n for (const block of allBlocks) {\n block.startTime = currentTime;\n currentTime += block.duration;\n }\n\n return {\n articleId,\n duration: currentTime,\n blocks: rootBlocks,\n audio: {\n segments: [],\n },\n ...(markdownDoc.frontmatter ? { frontmatter: markdownDoc.frontmatter } : {}),\n };\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 * 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 * 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 { MarkdownDocument, MarkdownBlockNode, MarkdownHeading } from '../markdown/types.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 }\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 return {\n type: 'document',\n children,\n };\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 // If the heading already has an annotation, trust it (it came from parsing)\n if (heading.templateAnnotation) return heading;\n\n // If the block has a non-default template or overrides, inject an annotation\n const hasExplicitTemplate = block.template && block.template !== 'sectionHeader';\n const hasOverrides = block.templateOverrides && Object.keys(block.templateOverrides).length > 0;\n\n if (!hasExplicitTemplate && !hasOverrides) return heading;\n\n // Clone to avoid mutating the original\n return {\n ...heading,\n children: [...heading.children],\n templateAnnotation: {\n template: block.template ?? 'sectionHeader',\n ...(hasOverrides ? { params: block.templateOverrides } : {}),\n },\n };\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 } from './templates/index.js';\nimport { expandPersistentLayers } from './templates/persistentLayers.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 if (isTemplateBlock(block) && block.template in templateRegistry) {\n const templateName = block.template as keyof typeof templateRegistry;\n const templateCtx = createTemplateContext(theme, blockIndex, totalBlocks, viewport);\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(block, templateCtx);\n if (!Array.isArray(layers)) {\n console.error(`Template ${templateName} did not return an array, got:`, typeof layers);\n layers = [];\n }\n } catch (err: unknown) {\n console.error(`Error expanding template ${templateName}:`, err);\n layers = [];\n }\n\n return injectPersistentLayers(layers, block, context);\n }\n\n // 3. Fallback: no layers and no known template\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 bottomLayers = expandPersistentLayers(persistentLayers.bottomLayers);\n const topLayers = expandPersistentLayers(persistentLayers.topLayers);\n\n if (bottomLayers.length === 0 && topLayers.length === 0) return layers;\n\n const templateBlock = block as TemplateBlock;\n const useBottom = templateBlock.useBottomLayer !== false;\n const useTop = templateBlock.useTopLayer !== false;\n\n return [...(useBottom ? bottomLayers : []), ...layers, ...(useTop ? topLayers : [])];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/markdown/utils.ts
|
|
2
|
+
function getChildren(node) {
|
|
3
|
+
if ("children" in node && Array.isArray(node.children)) {
|
|
4
|
+
return node.children;
|
|
5
|
+
}
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
function walkMarkdownTree(node, visitor, parent) {
|
|
9
|
+
const skip = visitor(node, parent);
|
|
10
|
+
if (skip === true) return;
|
|
11
|
+
const children = getChildren(node);
|
|
12
|
+
for (const child of children) {
|
|
13
|
+
walkMarkdownTree(child, visitor, node);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function findNodesByType(root, type) {
|
|
17
|
+
const results = [];
|
|
18
|
+
walkMarkdownTree(root, (node) => {
|
|
19
|
+
if (node.type === type) {
|
|
20
|
+
results.push(node);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return results;
|
|
24
|
+
}
|
|
25
|
+
function extractPlainText(node) {
|
|
26
|
+
if ("value" in node && typeof node.value === "string") {
|
|
27
|
+
return node.value;
|
|
28
|
+
}
|
|
29
|
+
const children = getChildren(node);
|
|
30
|
+
return children.map(extractPlainText).join("");
|
|
31
|
+
}
|
|
32
|
+
function countNodes(node) {
|
|
33
|
+
let count = 1;
|
|
34
|
+
const children = getChildren(node);
|
|
35
|
+
for (const child of children) {
|
|
36
|
+
count += countNodes(child);
|
|
37
|
+
}
|
|
38
|
+
return count;
|
|
39
|
+
}
|
|
40
|
+
function parseFrontmatter(yaml) {
|
|
41
|
+
if (!yaml || !yaml.trim()) return null;
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const line of yaml.split("\n")) {
|
|
44
|
+
const trimmed = line.trim();
|
|
45
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
46
|
+
const colonIdx = trimmed.indexOf(":");
|
|
47
|
+
if (colonIdx < 1) continue;
|
|
48
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
49
|
+
let value = trimmed.slice(colonIdx + 1).trim();
|
|
50
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
51
|
+
value = value.slice(1, -1);
|
|
52
|
+
}
|
|
53
|
+
if (value === "true") {
|
|
54
|
+
result[key] = true;
|
|
55
|
+
} else if (value === "false") {
|
|
56
|
+
result[key] = false;
|
|
57
|
+
} else if (value !== "" && !isNaN(Number(value))) {
|
|
58
|
+
result[key] = Number(value);
|
|
59
|
+
} else {
|
|
60
|
+
result[key] = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
64
|
+
}
|
|
65
|
+
function createDocument(...children) {
|
|
66
|
+
return {
|
|
67
|
+
type: "document",
|
|
68
|
+
children
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
getChildren,
|
|
74
|
+
walkMarkdownTree,
|
|
75
|
+
findNodesByType,
|
|
76
|
+
extractPlainText,
|
|
77
|
+
countNodes,
|
|
78
|
+
parseFrontmatter,
|
|
79
|
+
createDocument
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=chunk-QWVRN6I4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/markdown/utils.ts"],"sourcesContent":["/**\n * Markdown Tree Utilities\n *\n * Helper functions for traversing and querying the MarkdownDocument tree.\n * All functions are pure and operate on the JSON node interfaces.\n */\n\nimport type { MarkdownNode, MarkdownDocument } from './types.js';\n\n/**\n * Get the children of any markdown node, if it has children.\n * Returns an empty array for leaf nodes (text, code, break, etc.).\n *\n * This is useful for generic tree walking where you don't want to\n * check the specific node type.\n */\nexport function getChildren(node: MarkdownNode): MarkdownNode[] {\n if ('children' in node && Array.isArray(node.children)) {\n return node.children as MarkdownNode[];\n }\n return [];\n}\n\n/**\n * Walk the markdown tree depth-first, calling the visitor for each node.\n *\n * The visitor receives the current node and its parent. Return `true`\n * from the visitor to skip the node's children (prune).\n *\n * @param node - The root node to start walking from\n * @param visitor - Called for each node; return true to skip children\n * @param parent - (internal) Parent node\n */\nexport function walkMarkdownTree(\n node: MarkdownNode,\n visitor: (node: MarkdownNode, parent?: MarkdownNode) => void | boolean,\n parent?: MarkdownNode,\n): void {\n const skip = visitor(node, parent);\n if (skip === true) return;\n\n const children = getChildren(node);\n for (const child of children) {\n walkMarkdownTree(child, visitor, node);\n }\n}\n\n/**\n * Find all nodes of a specific type in the tree.\n *\n * @param root - The document or node to search within\n * @param type - The node type to find (e.g., 'heading', 'link', 'text')\n * @returns Array of matching nodes\n *\n * @example\n * ```ts\n * const headings = findNodesByType(doc, 'heading');\n * const links = findNodesByType(doc, 'link');\n * ```\n */\nexport function findNodesByType<T extends MarkdownNode>(root: MarkdownNode, type: T['type']): T[] {\n const results: T[] = [];\n walkMarkdownTree(root, (node) => {\n if (node.type === type) {\n results.push(node as T);\n }\n });\n return results;\n}\n\n/**\n * Extract all plain text content from a node and its descendants.\n * Concatenates text values, ignoring formatting, links, etc.\n *\n * @param node - The node to extract text from\n * @returns Plain text content\n *\n * @example\n * ```ts\n * const heading = doc.children[0]; // { type: 'heading', children: [{ type: 'text', value: 'Hello' }] }\n * extractPlainText(heading); // 'Hello'\n * ```\n */\nexport function extractPlainText(node: MarkdownNode): string {\n if ('value' in node && typeof node.value === 'string') {\n return node.value;\n }\n\n const children = getChildren(node);\n return children.map(extractPlainText).join('');\n}\n\n/**\n * Count the total number of nodes in the tree.\n *\n * @param node - The root node\n * @returns Total node count (including the root)\n */\nexport function countNodes(node: MarkdownNode): number {\n let count = 1;\n const children = getChildren(node);\n for (const child of children) {\n count += countNodes(child);\n }\n return count;\n}\n\n/**\n * Parse a YAML frontmatter string into a key-value record.\n *\n * Handles simple `key: value` pairs common in markdown frontmatter.\n * Values are trimmed; quoted strings have their quotes removed.\n * Returns `null` if parsing fails or the input is empty.\n *\n * @param yaml - The raw YAML string (without the `---` delimiters)\n * @returns A record of string keys to parsed values, or null\n */\nexport function parseFrontmatter(yaml: string): Record<string, unknown> | null {\n if (!yaml || !yaml.trim()) return null;\n\n const result: Record<string, unknown> = {};\n\n for (const line of yaml.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx < 1) continue;\n\n const key = trimmed.slice(0, colonIdx).trim();\n let value: string | boolean | number = trimmed.slice(colonIdx + 1).trim();\n\n // Remove surrounding quotes\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n // Parse booleans and numbers\n if (value === 'true') {\n result[key] = true;\n } else if (value === 'false') {\n result[key] = false;\n } else if (value !== '' && !isNaN(Number(value))) {\n result[key] = Number(value);\n } else {\n result[key] = value;\n }\n }\n\n return Object.keys(result).length > 0 ? result : null;\n}\n\n/**\n * Create a minimal MarkdownDocument from a list of block nodes.\n * Convenience function for programmatic document construction.\n */\nexport function createDocument(...children: MarkdownDocument['children']): MarkdownDocument {\n return {\n type: 'document',\n children,\n };\n}\n"],"mappings":";AAgBO,SAAS,YAAY,MAAoC;AAC9D,MAAI,cAAc,QAAQ,MAAM,QAAQ,KAAK,QAAQ,GAAG;AACtD,WAAO,KAAK;AAAA,EACd;AACA,SAAO,CAAC;AACV;AAYO,SAAS,iBACd,MACA,SACA,QACM;AACN,QAAM,OAAO,QAAQ,MAAM,MAAM;AACjC,MAAI,SAAS,KAAM;AAEnB,QAAM,WAAW,YAAY,IAAI;AACjC,aAAW,SAAS,UAAU;AAC5B,qBAAiB,OAAO,SAAS,IAAI;AAAA,EACvC;AACF;AAeO,SAAS,gBAAwC,MAAoB,MAAsB;AAChG,QAAM,UAAe,CAAC;AACtB,mBAAiB,MAAM,CAAC,SAAS;AAC/B,QAAI,KAAK,SAAS,MAAM;AACtB,cAAQ,KAAK,IAAS;AAAA,IACxB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAeO,SAAS,iBAAiB,MAA4B;AAC3D,MAAI,WAAW,QAAQ,OAAO,KAAK,UAAU,UAAU;AACrD,WAAO,KAAK;AAAA,EACd;AAEA,QAAM,WAAW,YAAY,IAAI;AACjC,SAAO,SAAS,IAAI,gBAAgB,EAAE,KAAK,EAAE;AAC/C;AAQO,SAAS,WAAW,MAA4B;AACrD,MAAI,QAAQ;AACZ,QAAM,WAAW,YAAY,IAAI;AACjC,aAAW,SAAS,UAAU;AAC5B,aAAS,WAAW,KAAK;AAAA,EAC3B;AACA,SAAO;AACT;AAYO,SAAS,iBAAiB,MAA8C;AAC7E,MAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAG,QAAO;AAElC,QAAM,SAAkC,CAAC;AAEzC,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AAEzC,UAAM,WAAW,QAAQ,QAAQ,GAAG;AACpC,QAAI,WAAW,EAAG;AAElB,UAAM,MAAM,QAAQ,MAAM,GAAG,QAAQ,EAAE,KAAK;AAC5C,QAAI,QAAmC,QAAQ,MAAM,WAAW,CAAC,EAAE,KAAK;AAGxE,QACG,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC3C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAC5C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AAGA,QAAI,UAAU,QAAQ;AACpB,aAAO,GAAG,IAAI;AAAA,IAChB,WAAW,UAAU,SAAS;AAC5B,aAAO,GAAG,IAAI;AAAA,IAChB,WAAW,UAAU,MAAM,CAAC,MAAM,OAAO,KAAK,CAAC,GAAG;AAChD,aAAO,GAAG,IAAI,OAAO,KAAK;AAAA,IAC5B,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AACnD;AAMO,SAAS,kBAAkB,UAA0D;AAC1F,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,EACF;AACF;","names":[]}
|