@bendyline/squisq 1.5.2 → 2.0.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/README.md +63 -10
- package/dist/{Doc-BBVGq1_9.d.ts → Doc-ylqWKDc_.d.ts} +248 -37
- package/dist/{ImageEditDoc-FuyGtt6o.d.ts → ImageEditDoc-D828IXJ1.d.ts} +1 -1
- package/dist/annotationCoercion-Dr_DY9Hx.d.ts +86 -0
- package/dist/{chunk-T5UK6YOB.js → chunk-2PWOO4ZE.js} +60 -17
- package/dist/chunk-2PWOO4ZE.js.map +1 -0
- package/dist/{chunk-VWUFZ6ZG.js → chunk-3E5F2XMR.js} +2 -29
- package/dist/chunk-3E5F2XMR.js.map +1 -0
- package/dist/chunk-4X3JQXNM.js +35 -0
- package/dist/chunk-4X3JQXNM.js.map +1 -0
- package/dist/{chunk-P3K7NLHF.js → chunk-5DKBFXPY.js} +31 -121
- package/dist/chunk-5DKBFXPY.js.map +1 -0
- package/dist/{chunk-DLXZMS5K.js → chunk-5J4HKRJF.js} +51 -3
- package/dist/chunk-5J4HKRJF.js.map +1 -0
- package/dist/chunk-5UYI3RJB.js +2503 -0
- package/dist/chunk-5UYI3RJB.js.map +1 -0
- package/dist/{chunk-BEQODJWV.js → chunk-66QO7XFB.js} +11 -291
- package/dist/chunk-66QO7XFB.js.map +1 -0
- package/dist/{chunk-HWVFJAAH.js → chunk-AMIR72JP.js} +28 -10
- package/dist/chunk-AMIR72JP.js.map +1 -0
- package/dist/chunk-CY2R2VPR.js +884 -0
- package/dist/chunk-CY2R2VPR.js.map +1 -0
- package/dist/chunk-FVJUUTEM.js +74 -0
- package/dist/chunk-FVJUUTEM.js.map +1 -0
- package/dist/{chunk-Q3ROPT5H.js → chunk-GZY66SDN.js} +1 -1
- package/dist/{chunk-Q3ROPT5H.js.map → chunk-GZY66SDN.js.map} +1 -1
- package/dist/chunk-JOQYPRDZ.js +127 -0
- package/dist/chunk-JOQYPRDZ.js.map +1 -0
- package/dist/{chunk-A4O7GIWE.js → chunk-MMPBOUK7.js} +10 -73
- package/dist/chunk-MMPBOUK7.js.map +1 -0
- package/dist/{chunk-LDQ2HJIX.js → chunk-PXLVXTLW.js} +252 -54
- package/dist/chunk-PXLVXTLW.js.map +1 -0
- package/dist/{chunk-TQWLI6S2.js → chunk-Q7LROV2H.js} +4 -2
- package/dist/{chunk-TQWLI6S2.js.map → chunk-Q7LROV2H.js.map} +1 -1
- package/dist/{chunk-GPLTCSXO.js → chunk-R2GC6RLD.js} +7 -7
- package/dist/chunk-R2GC6RLD.js.map +1 -0
- package/dist/chunk-R2YZTCLO.js +292 -0
- package/dist/chunk-R2YZTCLO.js.map +1 -0
- package/dist/chunk-S7A5D7ZJ.js +174 -0
- package/dist/chunk-S7A5D7ZJ.js.map +1 -0
- package/dist/chunk-TFJVXUHH.js +2358 -0
- package/dist/chunk-TFJVXUHH.js.map +1 -0
- package/dist/{chunk-LTN6I7QW.js → chunk-TUWMK62B.js} +55 -4
- package/dist/chunk-TUWMK62B.js.map +1 -0
- package/dist/{chunk-IMSCRLLV.js → chunk-VR7UPJ7C.js} +35 -25
- package/dist/chunk-VR7UPJ7C.js.map +1 -0
- package/dist/{chunk-KKNUBQ6Y.js → chunk-W4O2TX5I.js} +157 -92
- package/dist/chunk-W4O2TX5I.js.map +1 -0
- package/dist/{chunk-5TYS5V3G.js → chunk-X6DUONGT.js} +4204 -3560
- package/dist/chunk-X6DUONGT.js.map +1 -0
- package/dist/doc/index.d.ts +2334 -5
- package/dist/doc/index.js +127 -33
- package/dist/generate/index.d.ts +7 -2
- package/dist/generate/index.js +1 -1
- package/dist/imageEdit/index.d.ts +9 -6
- package/dist/imageEdit/index.js +3 -3
- package/dist/index.d.ts +12 -10
- package/dist/index.js +246 -80
- package/dist/jsonForm/index.d.ts +7 -3
- package/dist/jsonForm/index.js +9 -4
- package/dist/markdown/index.d.ts +6 -70
- package/dist/markdown/index.js +13 -9
- package/dist/narration/index.d.ts +618 -0
- package/dist/narration/index.js +82 -0
- package/dist/recommend/index.d.ts +15 -1
- package/dist/recommend/index.js +2 -2
- package/dist/schemas/index.d.ts +28 -6
- package/dist/schemas/index.js +15 -13
- package/dist/spatial/index.d.ts +0 -1
- package/dist/spatial/index.js +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/{themeLibrary-CehcJhzz.d.ts → themeLibrary-RjVgLpUi.d.ts} +10 -11
- package/dist/timing/index.js +5 -3
- package/dist/transform/index.d.ts +35 -24
- package/dist/transform/index.js +9 -10
- package/dist/{types-DlAZ7MW4.d.ts → types-BwMPl2CB.d.ts} +1 -1
- package/dist/{types-D98li1gV.d.ts → types-C4kiRn_k.d.ts} +3 -1
- package/dist/versions/index.d.ts +2 -1
- package/dist/versions/index.js +2 -2
- package/package.json +6 -6
- package/src/__tests__/applyNarrationTiming.test.ts +190 -0
- package/src/__tests__/applyRenderStyle.test.ts +3 -3
- package/src/__tests__/asciiDiagramBattle.test.ts +246 -0
- package/src/__tests__/asciiDiagramDetect.test.ts +162 -0
- package/src/__tests__/asciiDiagramMapping.test.ts +38 -0
- package/src/__tests__/asciiDiagramParse.test.ts +233 -0
- package/src/__tests__/asciiDiagramPipeline.test.ts +273 -0
- package/src/__tests__/asciiDiagramRender.test.ts +162 -0
- package/src/__tests__/asciiDiagramRepair.test.ts +84 -0
- package/src/__tests__/asciiTimelineDetect.test.ts +157 -0
- package/src/__tests__/asciiTimelineParse.test.ts +205 -0
- package/src/__tests__/asciiTimelinePipeline.test.ts +349 -0
- package/src/__tests__/asciiTimelineRender.test.ts +376 -0
- package/src/__tests__/attrTokens.test.ts +5 -0
- package/src/__tests__/contentContainer.test.ts +43 -0
- package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -15
- package/src/__tests__/customThemesFrontmatter.test.ts +1 -0
- package/src/__tests__/dataTable.test.ts +25 -15
- package/src/__tests__/diagramBlock.test.ts +3 -3
- package/src/__tests__/diagramText.test.ts +26 -0
- package/src/__tests__/docToMarkdownMedia.test.ts +83 -0
- package/src/__tests__/drawingBlock.test.ts +3 -3
- package/src/__tests__/fixtures/asciiDiagramBattle.ts +512 -0
- package/src/__tests__/fixtures/asciiDiagrams.ts +287 -0
- package/src/__tests__/fixtures/asciiTimelines.ts +35 -0
- package/src/__tests__/fixtures/treeviewFixtures.ts +243 -0
- package/src/__tests__/getLayers.test.ts +32 -18
- package/src/__tests__/getLayersFallback.test.ts +8 -8
- package/src/__tests__/imageEditVersions.test.ts +2 -0
- package/src/__tests__/jsonForm.pointer.test.ts +47 -1
- package/src/__tests__/layoutBlock.test.ts +3 -3
- package/src/__tests__/majorApiTypes.test.ts +18 -0
- package/src/__tests__/markdown.test.ts +54 -4
- package/src/__tests__/markdownSanitize.test.ts +7 -5
- package/src/__tests__/matchFontFamily.test.ts +114 -0
- package/src/__tests__/materializeBlockLayers.test.ts +135 -0
- package/src/__tests__/materializeTestUtils.ts +17 -0
- package/src/__tests__/narrationAlign.test.ts +150 -0
- package/src/__tests__/narrationFeatures.test.ts +71 -0
- package/src/__tests__/narrationNuclei.test.ts +45 -0
- package/src/__tests__/narrationPacing.test.ts +189 -0
- package/src/__tests__/narrationScript.test.ts +149 -0
- package/src/__tests__/narrationSessionRepro.test.ts +62 -0
- package/src/__tests__/narrationSidecar.test.ts +88 -0
- package/src/__tests__/narrationSyllables.test.ts +65 -0
- package/src/__tests__/narrationTestSignals.ts +167 -0
- package/src/__tests__/narrationVad.test.ts +66 -0
- package/src/__tests__/resolveDocTheme.test.ts +16 -2
- package/src/__tests__/rootExports.test.ts +18 -0
- package/src/__tests__/scopedContentContainer.test.ts +20 -0
- package/src/__tests__/shapeGeometry.test.ts +12 -0
- package/src/__tests__/templateAnnotationParse.test.ts +10 -0
- package/src/__tests__/templateInputs.test.ts +69 -0
- package/src/__tests__/templateMetadata.test.ts +2 -1
- package/src/__tests__/templates.test.ts +143 -17
- package/src/__tests__/themeCompile.test.ts +49 -22
- package/src/__tests__/transformProvenance.test.ts +160 -0
- package/src/__tests__/transformV2.test.ts +40 -14
- package/src/__tests__/treeviewBattle.test.ts +76 -0
- package/src/__tests__/treeviewDetect.test.ts +168 -0
- package/src/__tests__/treeviewParse.test.ts +118 -0
- package/src/__tests__/treeviewPipeline.test.ts +183 -0
- package/src/__tests__/treeviewRender.test.ts +91 -0
- package/src/__tests__/versions.test.ts +44 -0
- package/src/doc/applyNarrationTiming.ts +244 -0
- package/src/doc/asciiDiagram/chars.ts +210 -0
- package/src/doc/asciiDiagram/detect.ts +175 -0
- package/src/doc/asciiDiagram/index.ts +38 -0
- package/src/doc/asciiDiagram/mapping.ts +225 -0
- package/src/doc/asciiDiagram/parse.ts +1095 -0
- package/src/doc/asciiDiagram/render.ts +963 -0
- package/src/doc/asciiDiagram/repair.ts +400 -0
- package/src/doc/asciiDiagram/types.ts +65 -0
- package/src/doc/asciiTimeline/detect.ts +138 -0
- package/src/doc/asciiTimeline/index.ts +26 -0
- package/src/doc/asciiTimeline/mapping.ts +43 -0
- package/src/doc/asciiTimeline/parse.ts +870 -0
- package/src/doc/asciiTimeline/render.ts +427 -0
- package/src/doc/asciiTimeline/types.ts +73 -0
- package/src/doc/asciiTimeline/wrappedFlow.ts +427 -0
- package/src/doc/audioMapping.ts +60 -46
- package/src/doc/customTemplatesFrontmatter.ts +0 -41
- package/src/doc/docToMarkdown.ts +70 -2
- package/src/doc/index.ts +76 -4
- package/src/doc/markdownToDoc.ts +138 -2
- package/src/doc/materializeBlockLayers.ts +304 -0
- package/src/doc/mediaAnnotations.ts +20 -0
- package/src/doc/resolveDocTheme.ts +8 -7
- package/src/doc/templateInputs.ts +185 -2
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +22 -8
- package/src/doc/templates/__tests__/listBlock.test.ts +64 -0
- package/src/doc/templates/diagramBlock.ts +237 -38
- package/src/doc/templates/fallbackBlock.ts +1 -1
- package/src/doc/templates/index.ts +134 -279
- package/src/doc/templates/inputDescriptors.ts +20 -1
- package/src/doc/templates/listBlock.ts +88 -30
- package/src/doc/templates/metadata.ts +10 -0
- package/src/doc/templates/persistentLayers.ts +12 -126
- package/src/doc/templates/registry.ts +88 -0
- package/src/doc/templates/templateNames.ts +22 -0
- package/src/doc/templates/timelineBlock.ts +613 -0
- package/src/doc/templates/treeBlock.ts +131 -0
- package/src/doc/treeview/chars.ts +72 -0
- package/src/doc/treeview/detect.ts +145 -0
- package/src/doc/treeview/index.ts +30 -0
- package/src/doc/treeview/mapping.ts +95 -0
- package/src/doc/treeview/parse.ts +201 -0
- package/src/doc/treeview/render.ts +41 -0
- package/src/doc/treeview/types.ts +43 -0
- package/src/doc/utils/applyRenderStyle.ts +2 -9
- package/src/doc/utils/diagramText.ts +78 -0
- package/src/doc/utils/shapeGeometry.ts +67 -5
- package/src/doc/validate.ts +1 -1
- package/src/generate/templateMapper.ts +7 -0
- package/src/imageEdit/export.ts +6 -5
- package/src/imageEdit/versionPaths.ts +13 -26
- package/src/imageEdit/versions.ts +21 -100
- package/src/index.ts +1 -0
- package/src/internal/immutable.ts +58 -0
- package/src/jsonForm/index.ts +9 -1
- package/src/jsonForm/pointer.ts +68 -23
- package/src/markdown/attrTokens.ts +7 -5
- package/src/markdown/index.ts +1 -0
- package/src/markdown/stringify.ts +4 -9
- package/src/markdown/utils.ts +36 -14
- package/src/narration/align.ts +514 -0
- package/src/narration/features.ts +231 -0
- package/src/narration/index.ts +86 -0
- package/src/narration/nuclei.ts +118 -0
- package/src/narration/pacing.ts +211 -0
- package/src/narration/script.ts +239 -0
- package/src/narration/session.ts +100 -0
- package/src/narration/sidecar.ts +187 -0
- package/src/narration/syllables.ts +85 -0
- package/src/narration/trace.ts +50 -0
- package/src/narration/types.ts +299 -0
- package/src/narration/vad.ts +131 -0
- package/src/recommend/numberHighlight.ts +25 -0
- package/src/recommend/templates.ts +108 -12
- package/src/schemas/BlockTemplates.ts +156 -7
- package/src/schemas/Doc.ts +72 -9
- package/src/schemas/Media.ts +12 -2
- package/src/schemas/Theme.ts +45 -25
- package/src/schemas/Viewport.ts +2 -2
- package/src/schemas/fontStacks.ts +79 -0
- package/src/schemas/themeCompile.ts +15 -0
- package/src/schemas/themeConstants.ts +2 -0
- package/src/schemas/themeLibrary.ts +25 -19
- package/src/schemas/themeValidator.ts +1 -1
- package/src/schemas/themes/gezellig.json +1 -1
- package/src/spatial/Geohash.ts +0 -3
- package/src/storage/ContentContainer.ts +17 -9
- package/src/storage/ScopedContentContainer.ts +45 -15
- package/src/transform/applyTransform.ts +5 -5
- package/src/transform/index.ts +3 -1
- package/src/transform/registry.ts +169 -49
- package/src/transform/templateSelector.ts +21 -2
- package/src/transform/timingAllocator.ts +152 -21
- package/src/transform/types.ts +14 -1
- package/src/versions/gitignore.ts +34 -0
- package/src/versions/index.ts +2 -0
- package/src/versions/operations.ts +18 -104
- package/src/versions/paths.ts +13 -28
- package/src/versions/snapshotStore.ts +202 -0
- package/src/versions/types.ts +3 -1
- package/dist/chunk-5TYS5V3G.js.map +0 -1
- package/dist/chunk-A4O7GIWE.js.map +0 -1
- package/dist/chunk-BEQODJWV.js.map +0 -1
- package/dist/chunk-DLXZMS5K.js.map +0 -1
- package/dist/chunk-GPLTCSXO.js.map +0 -1
- package/dist/chunk-HWVFJAAH.js.map +0 -1
- package/dist/chunk-IMSCRLLV.js.map +0 -1
- package/dist/chunk-JELIRVDP.js +0 -24
- package/dist/chunk-JELIRVDP.js.map +0 -1
- package/dist/chunk-KKNUBQ6Y.js.map +0 -1
- package/dist/chunk-LDQ2HJIX.js.map +0 -1
- package/dist/chunk-LTN6I7QW.js.map +0 -1
- package/dist/chunk-P3K7NLHF.js.map +0 -1
- package/dist/chunk-T5UK6YOB.js.map +0 -1
- package/dist/chunk-UY7KGQ5R.js +0 -192
- package/dist/chunk-UY7KGQ5R.js.map +0 -1
- package/dist/chunk-VLJJHDAC.js +0 -218
- package/dist/chunk-VLJJHDAC.js.map +0 -1
- package/dist/chunk-VWUFZ6ZG.js.map +0 -1
- package/dist/story/index.d.ts +0 -1801
- package/dist/story/index.js +0 -279
- package/src/doc/getLayers.ts +0 -195
- /package/dist/{story → narration}/index.js.map +0 -0
|
@@ -6,7 +6,7 @@ var VIEWPORT_PRESETS = {
|
|
|
6
6
|
portrait: { width: 1080, height: 1920, name: "9:16 Portrait" },
|
|
7
7
|
/** 1:1 square (social media) */
|
|
8
8
|
square: { width: 1080, height: 1080, name: "1:1 Square" },
|
|
9
|
-
/** 4:3 standard
|
|
9
|
+
/** 4:3 standard */
|
|
10
10
|
standard: { width: 1440, height: 1080, name: "4:3 Standard" }
|
|
11
11
|
};
|
|
12
12
|
function getViewport(viewport) {
|
|
@@ -147,6 +147,52 @@ function isPersistentLayerTemplate(layer) {
|
|
|
147
147
|
return "template" in layer && typeof layer.template === "string";
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// src/internal/immutable.ts
|
|
151
|
+
function cloneData(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
152
|
+
if (value === null || typeof value !== "object") return value;
|
|
153
|
+
const existing = seen.get(value);
|
|
154
|
+
if (existing !== void 0) return existing;
|
|
155
|
+
const tag = Object.prototype.toString.call(value);
|
|
156
|
+
if (tag === "[object ArrayBuffer]") {
|
|
157
|
+
const source2 = new Uint8Array(value);
|
|
158
|
+
const copy2 = new Uint8Array(source2.byteLength);
|
|
159
|
+
copy2.set(source2);
|
|
160
|
+
return copy2.buffer;
|
|
161
|
+
}
|
|
162
|
+
if (tag === "[object Date]") {
|
|
163
|
+
return new Date(Date.prototype.getTime.call(value));
|
|
164
|
+
}
|
|
165
|
+
if (Array.isArray(value)) {
|
|
166
|
+
const copy2 = [];
|
|
167
|
+
seen.set(value, copy2);
|
|
168
|
+
for (const item of value) copy2.push(cloneData(item, seen));
|
|
169
|
+
return copy2;
|
|
170
|
+
}
|
|
171
|
+
const source = value;
|
|
172
|
+
const copy = {};
|
|
173
|
+
seen.set(value, copy);
|
|
174
|
+
for (const key of Object.keys(source)) {
|
|
175
|
+
Object.defineProperty(copy, key, {
|
|
176
|
+
value: cloneData(source[key], seen),
|
|
177
|
+
enumerable: true,
|
|
178
|
+
configurable: true,
|
|
179
|
+
writable: true
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return copy;
|
|
183
|
+
}
|
|
184
|
+
function deepFreezeData(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
185
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return value;
|
|
186
|
+
seen.add(value);
|
|
187
|
+
for (const key of Object.keys(value)) {
|
|
188
|
+
deepFreezeData(value[key], seen);
|
|
189
|
+
}
|
|
190
|
+
return Object.freeze(value);
|
|
191
|
+
}
|
|
192
|
+
function cloneAndFreezeData(value) {
|
|
193
|
+
return deepFreezeData(cloneData(value));
|
|
194
|
+
}
|
|
195
|
+
|
|
150
196
|
export {
|
|
151
197
|
VIEWPORT_PRESETS,
|
|
152
198
|
getViewport,
|
|
@@ -162,6 +208,8 @@ export {
|
|
|
162
208
|
isTemplateBlock,
|
|
163
209
|
createTemplateContext,
|
|
164
210
|
scaledFontSize2,
|
|
165
|
-
isPersistentLayerTemplate
|
|
211
|
+
isPersistentLayerTemplate,
|
|
212
|
+
cloneData,
|
|
213
|
+
cloneAndFreezeData
|
|
166
214
|
};
|
|
167
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-5J4HKRJF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schemas/Viewport.ts","../src/schemas/LayoutStrategy.ts","../src/schemas/BlockTemplates.ts","../src/internal/immutable.ts"],"sourcesContent":["/**\n * Viewport Configuration\n *\n * Defines viewport dimensions for doc rendering across different aspect ratios.\n * Templates use this configuration to adapt their layouts appropriately.\n *\n * Supported aspect ratios:\n * - landscape (16:9) - Default, standard video/presentation format\n * - portrait (9:16) - Vertical video, mobile stories\n * - square (1:1) - Social media posts\n * - standard (4:3) - Classic presentation format\n */\n\n/**\n * Viewport configuration for doc rendering.\n */\nexport interface ViewportConfig {\n /** Canonical width in virtual pixels */\n width: number;\n /** Canonical height in virtual pixels */\n height: number;\n /** Human-readable name for debugging/display */\n name: string;\n}\n\n/**\n * Standard viewport presets for common aspect ratios.\n */\nexport const VIEWPORT_PRESETS = {\n /** 16:9 landscape (default, 1080p) */\n landscape: { width: 1920, height: 1080, name: '16:9 Landscape' },\n /** 9:16 portrait (vertical video, stories) */\n portrait: { width: 1080, height: 1920, name: '9:16 Portrait' },\n /** 1:1 square (social media) */\n square: { width: 1080, height: 1080, name: '1:1 Square' },\n /** 4:3 standard */\n standard: { width: 1440, height: 1080, name: '4:3 Standard' },\n} as const;\n\n/**\n * Viewport preset name.\n */\nexport type ViewportPreset = keyof typeof VIEWPORT_PRESETS;\n\n/**\n * Viewport orientation derived from aspect ratio.\n */\nexport type ViewportOrientation = 'landscape' | 'portrait' | 'square';\n\n/**\n * Get a viewport configuration from a preset name or return the config if already a ViewportConfig.\n */\nexport function getViewport(viewport: ViewportPreset | ViewportConfig): ViewportConfig {\n if (typeof viewport === 'string') {\n return VIEWPORT_PRESETS[viewport];\n }\n return viewport;\n}\n\n/**\n * Calculate font scale factor for a viewport.\n * Based on diagonal pixels relative to 1080p landscape reference.\n * This ensures text remains readable across different viewport sizes.\n */\nconst REFERENCE_DIAGONAL = Math.sqrt(1920 * 1920 + 1080 * 1080); // ~2203\n\nexport function calculateFontScale(viewport: ViewportConfig): number {\n const currentDiagonal = Math.sqrt(\n viewport.width * viewport.width + viewport.height * viewport.height,\n );\n return currentDiagonal / REFERENCE_DIAGONAL;\n}\n\n/**\n * Determine if viewport is landscape, portrait, or square.\n */\nexport function getViewportOrientation(viewport: ViewportConfig): ViewportOrientation {\n const ratio = viewport.width / viewport.height;\n // Allow small tolerance for \"square\" classification\n if (Math.abs(ratio - 1) < 0.05) return 'square';\n return ratio > 1 ? 'landscape' : 'portrait';\n}\n\n/**\n * Get aspect ratio as a string (e.g., \"16:9\", \"9:16\", \"1:1\").\n */\nexport function getAspectRatioString(viewport: ViewportConfig): string {\n const gcd = (a: number, b: number): number => (b === 0 ? a : gcd(b, a % b));\n const divisor = gcd(viewport.width, viewport.height);\n return `${viewport.width / divisor}:${viewport.height / divisor}`;\n}\n","/**\n * Layout Strategy\n *\n * Provides orientation-specific layout hints for slide templates.\n * Templates use these hints to position content appropriately for\n * different aspect ratios (landscape, portrait, square).\n *\n * Key adaptations by orientation:\n * - Portrait: Larger relative text, stacked layouts instead of side-by-side\n * - Square: Balanced layout with moderate adjustments\n * - Landscape: Default/reference layout\n */\n\nimport type { ViewportOrientation, ViewportConfig } from './Viewport.js';\nimport { calculateFontScale } from './Viewport.js';\n\n/**\n * Layout hints for template positioning based on orientation.\n */\nexport interface LayoutHints {\n /** Primary content Y position (percentage string, e.g., \"35%\") */\n primaryY: string;\n /** Secondary content Y position (percentage string) */\n secondaryY: string;\n /** Tertiary/detail Y position (percentage string) */\n tertiaryY: string;\n /** Title font size multiplier (relative to base) */\n titleScale: number;\n /** Body text font size multiplier (relative to base) */\n bodyScale: number;\n /** Two-column: left X position (percentage string) */\n columnLeftX: string;\n /** Two-column: right X position (percentage string) */\n columnRightX: string;\n /** Two-column in portrait: top Y position for stacked layout */\n columnTopY: string;\n /** Two-column in portrait: bottom Y position for stacked layout */\n columnBottomY: string;\n /** Whether two-column should stack vertically */\n stackColumns: boolean;\n /** Max text width (percentage string) for wrapping */\n maxTextWidth: string;\n /** Horizontal padding from edges (percentage string) */\n horizontalPadding: string;\n /** Caption position Y for imageWithCaption (percentage string) */\n captionY: string;\n /** Caption font size multiplier */\n captionScale: number;\n}\n\n/**\n * Layout configurations by orientation.\n */\nconst LAYOUT_CONFIGS: Record<ViewportOrientation, LayoutHints> = {\n landscape: {\n primaryY: '35%',\n secondaryY: '55%',\n tertiaryY: '75%',\n titleScale: 1.0,\n bodyScale: 1.0,\n columnLeftX: '25%',\n columnRightX: '75%',\n columnTopY: '35%',\n columnBottomY: '65%',\n stackColumns: false,\n maxTextWidth: '80%',\n horizontalPadding: '10%',\n captionY: '85%',\n captionScale: 1.0,\n },\n portrait: {\n // Content positioned higher for thumb-reach zone on mobile\n primaryY: '25%',\n secondaryY: '42%',\n tertiaryY: '58%',\n // Titles scale down for narrower width; body text scales up to fill vertical space\n titleScale: 0.7,\n bodyScale: 1.5,\n // Stacked columns instead of side-by-side\n columnLeftX: '50%',\n columnRightX: '50%',\n columnTopY: '30%',\n columnBottomY: '55%',\n stackColumns: true,\n // Wider relative to viewport to use available space\n maxTextWidth: '90%',\n horizontalPadding: '5%',\n captionY: '75%',\n captionScale: 0.85,\n },\n square: {\n primaryY: '30%',\n secondaryY: '50%',\n tertiaryY: '70%',\n titleScale: 0.85,\n bodyScale: 0.9,\n columnLeftX: '25%',\n columnRightX: '75%',\n columnTopY: '35%',\n columnBottomY: '65%',\n stackColumns: false,\n maxTextWidth: '85%',\n horizontalPadding: '7.5%',\n captionY: '82%',\n captionScale: 0.9,\n },\n};\n\n/**\n * Get layout hints for a given orientation.\n */\nexport function getLayoutHints(orientation: ViewportOrientation): LayoutHints {\n return LAYOUT_CONFIGS[orientation];\n}\n\n/**\n * Calculate a scaled font size based on viewport and layout hints.\n *\n * @param basePx - Base font size in pixels (designed for 1920x1080)\n * @param viewport - Target viewport configuration\n * @param orientation - Viewport orientation\n * @param isTitle - Whether this is title text (uses titleScale) or body (uses bodyScale)\n */\nexport function scaledFontSize(\n basePx: number,\n viewport: ViewportConfig,\n orientation: ViewportOrientation,\n isTitle: boolean = false,\n): number {\n const fontScale = calculateFontScale(viewport);\n const layout = getLayoutHints(orientation);\n const typeScale = isTitle ? layout.titleScale : layout.bodyScale;\n return Math.round(basePx * fontScale * typeScale);\n}\n\n/**\n * Get position for two-column layout based on orientation.\n * Returns { left: {x, y}, right: {x, y} } positions.\n */\nexport function getTwoColumnPositions(orientation: ViewportOrientation): {\n left: { x: string; y: string };\n right: { x: string; y: string };\n} {\n const layout = getLayoutHints(orientation);\n\n if (layout.stackColumns) {\n // Portrait: stack vertically\n return {\n left: { x: '50%', y: layout.columnTopY },\n right: { x: '50%', y: layout.columnBottomY },\n };\n }\n\n // Landscape/square: side by side\n return {\n left: { x: layout.columnLeftX, y: '50%' },\n right: { x: layout.columnRightX, y: '50%' },\n };\n}\n\n/**\n * Get safe text bounds for a given orientation.\n * Returns margins to keep text away from edges.\n */\nexport function getSafeTextBounds(orientation: ViewportOrientation): {\n left: string;\n right: string;\n top: string;\n bottom: string;\n} {\n const layout = getLayoutHints(orientation);\n return {\n left: layout.horizontalPadding,\n right: layout.horizontalPadding,\n top: '10%',\n bottom: '10%',\n };\n}\n","/**\n * Block Templates Schema\n *\n * Defines a template system for AI-generated doc blocks. Instead of\n * specifying low-level layers, positions, and animations, AI can use\n * high-level templates like \"title\" or \"statHighlight\" with simple\n * content parameters.\n *\n * Templates are expanded into full Block structures at render time,\n * ensuring consistent styling and reducing AI generation errors.\n *\n * Template Types:\n * - title: Doc intro with title + subtitle\n * - sectionHeader: Colored section divider\n * - statHighlight: Big number/stat with description\n * - quote: Large centered quote\n * - factCard: Key fact with explanation\n * - twoColumn: Side-by-side comparison\n * - dateEvent: Timeline-style date + description\n * - imageWithCaption: Image with text overlay\n */\n\nimport type { Layer, Block, Transition, MapTileStyle, MapMarker, MarkerStyle } from './Doc.js';\nimport type { ViewportConfig, ViewportOrientation } from './Viewport.js';\nimport type { LayoutHints } from './LayoutStrategy.js';\nimport type { Theme } from './Theme.js';\nimport { VIEWPORT_PRESETS, getViewportOrientation, calculateFontScale } from './Viewport.js';\nimport { getLayoutHints, scaledFontSize as scaleFontSize } from './LayoutStrategy.js';\n\n/**\n * Name of a color scheme defined in the active theme's `colorSchemes` map.\n * Templates reference schemes by name (e.g. 'blue', 'green') and the theme\n * resolves them to actual colors at render time via `resolveColorScheme()`.\n */\nexport type ColorScheme = string;\n\n/**\n * Default font family for doc text.\n * PT Serif provides a classic, readable appearance for documentary-style content.\n * Used as a fallback in TextLayer when no font is specified.\n */\nexport const DEFAULT_DOC_FONT = 'system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif';\n\n/**\n * Default font family for titles.\n */\nexport const DEFAULT_TITLE_FONT = 'Georgia, \"Times New Roman\", serif';\n\n// ============================================\n// Accent Image System\n// ============================================\n\n/**\n * Position options for accent images on text-based blocks.\n * Accents are tasteful image additions that complement rather than overwhelm text.\n */\nexport type AccentPosition =\n | 'left-strip' // Vertical strip on left (25-30% width), text shifts right\n | 'right-strip' // Vertical strip on right (25-30% width), text shifts left\n | 'bottom-strip' // Horizontal strip at bottom (25-30% height), text shifts up\n | 'corner-inset'; // Small inset image in corner with vignette edge\n\n/**\n * Accent image configuration for text-based blocks.\n * When present, the template adjusts layout to accommodate the image tastefully.\n */\nexport interface AccentImage {\n /** Path to image file (relative to article media dir) */\n src: string;\n /** Alt text for accessibility */\n alt: string;\n /** Where to position the accent image */\n position: AccentPosition;\n /** Ambient motion effect (subtle Ken Burns) */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n /** Photo credit / artist name */\n credit?: string;\n /** License identifier (e.g., 'CC BY-SA 4.0') */\n license?: string;\n}\n\n// ============================================\n// Template Input Types\n// ============================================\n\n/**\n * Base properties shared by all template blocks.\n */\ninterface BaseTemplateBlock {\n /** Unique block ID */\n id: string;\n /** Block duration in seconds */\n duration: number;\n /** Which audio segment this block belongs to */\n audioSegment: number;\n /** Entry transition */\n transition?: Transition;\n /** Show doc's bottom layers (default: true) */\n useBottomLayer?: boolean;\n /** Show doc's top layers (default: true) */\n useTopLayer?: boolean;\n /**\n * Start time within the audio segment when this content is spoken.\n * Used to sync the block appearance with the narration.\n * If not provided, blocks are distributed evenly across the segment.\n */\n sourceStartTime?: number;\n /**\n * Duration of the spoken content this block represents.\n * Used with sourceStartTime to precisely position the block.\n */\n sourceDuration?: number;\n /**\n * Transform provenance: the id of the source Block this template block\n * was derived from. Lets narration timing (and any per-source styling)\n * be remapped onto transformed/summarized docs.\n */\n sourceBlockId?: string;\n /** All source block ids when the block summarizes more than one. */\n sourceBlockIds?: string[];\n /**\n * Char offset of the extraction inside the source block's plain text.\n * Provenance only — NOT a time (see sourceStartTime for seconds).\n */\n sourceCharOffset?: number;\n /**\n * Per-block override for the theme's photographic image grade\n * (`theme.style.imageTreatment`). `'none'` opts this block's imagery out;\n * a treatment type forces that grade regardless of the theme.\n */\n imageTreatment?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool';\n}\n\n/**\n * Title block - doc intro with large title and subtitle.\n */\nexport interface TitleBlockInput extends BaseTemplateBlock {\n template: 'title';\n /** Main title text */\n title: string;\n /** Subtitle or tagline (supports \\n for line breaks) */\n subtitle?: string;\n /** Background color (defaults to theme primary) */\n backgroundColor?: string;\n}\n\n/**\n * Section header - section title card with optional background image.\n * Displays the section title prominently, optionally over an image.\n */\nexport interface SectionHeaderInput extends BaseTemplateBlock {\n template: 'sectionHeader';\n /** Section title */\n title: string;\n /** Color scheme for the section (used for fallback background) */\n colorScheme?: ColorScheme;\n /** Optional background image path */\n imageSrc?: string;\n /** Alt text for background image */\n imageAlt?: string;\n /** Ambient motion for background image */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n}\n\n/**\n * Stat highlight - big number with description and optional detail.\n */\nexport interface StatHighlightInput extends BaseTemplateBlock {\n template: 'statHighlight';\n /** The statistic (e.g., \"89%\", \"2x\", \"7 miles\") */\n stat: string;\n /** Description of what the stat means */\n description: string;\n /** Additional detail or context */\n detail?: string;\n /** Color scheme for the stat */\n colorScheme?: ColorScheme;\n /** Optional accent image to complement the text */\n accentImage?: AccentImage;\n}\n\n/**\n * Quote block - large centered quote text.\n */\nexport interface QuoteBlockInput extends BaseTemplateBlock {\n template: 'quote';\n /** The quote text (supports \\n for line breaks) */\n quote: string;\n /** Attribution (author, source) */\n attribution?: string;\n /** Optional accent image to complement the text */\n accentImage?: AccentImage;\n}\n\n/**\n * Fact card - key fact with explanation.\n */\nexport interface FactCardInput extends BaseTemplateBlock {\n template: 'factCard';\n /** The main fact or statement */\n fact: string;\n /** Explanation or context */\n explanation: string;\n /** Optional source citation */\n source?: string;\n /** Optional accent image to complement the text */\n accentImage?: AccentImage;\n}\n\n/**\n * Two column - side-by-side comparison.\n */\nexport interface TwoColumnInput extends BaseTemplateBlock {\n template: 'twoColumn';\n /** Left column content */\n left: {\n label: string;\n sublabel?: string;\n };\n /** Right column content */\n right: {\n label: string;\n sublabel?: string;\n };\n /** Optional header above columns */\n header?: string;\n /** Color schemes for left and right */\n leftColor?: ColorScheme;\n rightColor?: ColorScheme;\n}\n\n/**\n * Date event - timeline-style date with description.\n */\nexport interface DateEventInput extends BaseTemplateBlock {\n template: 'dateEvent';\n /** The date (e.g., \"July 14, 1974\") */\n date: string;\n /** Description of what happened (supports \\n) */\n description: string;\n /** Optional footer text */\n footer?: string;\n /** Mood: 'neutral', 'somber', 'celebratory' */\n mood?: 'neutral' | 'somber' | 'celebratory';\n /** Optional accent image to complement the text */\n accentImage?: AccentImage;\n}\n\n/**\n * Image with caption - background image with text overlay.\n */\nexport interface ImageWithCaptionInput extends BaseTemplateBlock {\n template: 'imageWithCaption';\n /** Path to image file */\n imageSrc: string;\n /** Alt text for accessibility */\n imageAlt: string;\n /** Caption text */\n caption?: string;\n /** Caption position */\n captionPosition?: 'bottom' | 'top' | 'center';\n /** Ambient motion effect: slow zoom or pan for visual interest */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n /** When true, style caption as a title (larger, centered, prominent) */\n isTitle?: boolean;\n /** Optional subtitle shown below the title (only when isTitle is true) */\n subtitle?: string;\n /** Photo credit / artist name */\n imageCredit?: string;\n /** License identifier (e.g., 'CC BY-SA 4.0') */\n imageLicense?: string;\n}\n\n/**\n * Shared fields for left/right feature blocks. A \"feature\" pairs an\n * image on one side of the block with a title and body paragraph on\n * the other — a common editorial layout for product highlights,\n * profile callouts, or section intros.\n */\ninterface FeatureInputBase extends BaseTemplateBlock {\n /** Path to the feature image. */\n imageSrc: string;\n /** Alt text for accessibility. */\n imageAlt?: string;\n /**\n * Explicit display width of the image, in pixels (typically pulled\n * from a `<img width>` attribute when the WYSIWYG editor resized it).\n * When set, the renderer treats the image as a sized asset and\n * centers it inside its half with padding instead of stretching it\n * to fill the cell.\n */\n imageWidth?: number;\n /** Explicit display height of the image, in pixels. */\n imageHeight?: number;\n /** Heading / title text shown next to the image. */\n title?: string;\n /** Body text shown below the title. */\n body?: string;\n}\n\n/**\n * Left feature — image on the left, title + body stacked on the right.\n */\nexport interface LeftFeatureInput extends FeatureInputBase {\n template: 'leftFeature';\n}\n\n/**\n * Right feature — image on the right, title + body stacked on the left.\n */\nexport interface RightFeatureInput extends FeatureInputBase {\n template: 'rightFeature';\n}\n\n/**\n * Map block - geographic map showing article location.\n *\n * Displays a map centered on given coordinates with optional title,\n * caption, and markers. Great for establishing geographic context.\n * See docs/MAP_TILES.md for available tile styles and attribution.\n */\nexport interface MapBlockInput extends BaseTemplateBlock {\n template: 'map';\n /** Map center coordinates */\n center: {\n lat: number;\n lng: number;\n };\n /** Zoom level (4-16, typically 8-12 for regional context) */\n zoom: number;\n /** Map tile style (default: 'terrain') */\n mapStyle?: MapTileStyle;\n /** Optional title overlay at top */\n title?: string;\n /** Optional caption at bottom */\n caption?: string;\n /** Optional markers to show on map */\n markers?: MapMarker[];\n /** Ambient motion effect: slow zoom or pan for visual interest */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n /** Pre-rendered static image path (set by prerender-maps command) */\n staticSrc?: string;\n}\n\n/**\n * Full-bleed quote - dramatic short text filling the viewport.\n * Like a movie title card. For short, punchy sentences < 60 chars.\n */\nexport interface FullBleedQuoteInput extends BaseTemplateBlock {\n template: 'fullBleedQuote';\n /** Short dramatic text (< 60 chars recommended) */\n text: string;\n /** Color scheme for text tint */\n colorScheme?: ColorScheme;\n}\n\n/**\n * List block - 3-5 items stacked vertically with subtle numbering.\n * Good for enumerations like \"things to see\" or \"key features\".\n */\nexport interface ListBlockInput extends BaseTemplateBlock {\n template: 'list';\n /** List items (3-5 recommended) */\n items: string[];\n /** Optional header above the list */\n title?: string;\n /** Color scheme for numbering accents */\n colorScheme?: ColorScheme;\n /** Optional accent image */\n accentImage?: AccentImage;\n}\n\n/**\n * Tree block — renders a hierarchical filesystem-style treeview from an\n * ASCII tree fence (file trees, dependency trees, outlines). The nested\n * `items` arrive via `templateData`, derived from the fence by the pipeline\n * (peer to how `diagram` receives `nodes`/`edges`).\n */\nexport interface TreeBlockInput extends BaseTemplateBlock {\n template: 'tree';\n /** Nested tree items (usually via `templateData.items`). */\n items?: TreeTemplateItem[];\n /** Optional title above the tree. */\n title?: string;\n /** Color scheme for folder rows / icons. */\n colorScheme?: ColorScheme;\n}\n\n/** One node supplied via `templateData.items` (mirrors `TreeLayerItem`). */\nexport interface TreeTemplateItem {\n id: string;\n label: string;\n isDir?: boolean;\n comment?: string;\n children: TreeTemplateItem[];\n}\n\n/**\n * One point on a timeline track. Event positions are normalized across the\n * whole timeline (0 = left edge, 1 = right edge) so events on separate tracks\n * remain horizontally comparable.\n */\nexport interface TimelineTemplateEvent {\n /** Stable id used by cross-track branch links. */\n id: string;\n /** Short label displayed next to the event dot. */\n label: string;\n /** Optional longer callout text. */\n description?: string;\n /** Normalized horizontal position; renderers clamp values to 0..1. */\n position: number;\n /** Preferred callout placement. Defaults to alternating above/below. */\n side?: 'above' | 'below';\n /** Description placement when it should sit opposite the event label. */\n descriptionSide?: 'above' | 'below';\n /** False renders the point only (useful for dense cadence tracks). */\n callout?: boolean;\n /** Dot treatment. Defaults to `filled`. */\n marker?: 'filled' | 'hollow' | 'diamond';\n}\n\n/** A horizontal timeline lane with zero or more positioned events. */\nexport interface TimelineTemplateTrack {\n id: string;\n /** Optional label displayed to the left of the track. */\n label?: string;\n /** Optional label displayed after the track's terminal arrow. */\n endLabel?: string;\n events: TimelineTemplateEvent[];\n}\n\n/**\n * A branch or relationship between two events. Source and target resolve\n * against event ids globally, so links may cross tracks.\n */\nexport interface TimelineTemplateLink {\n source: string;\n target: string;\n /** Optional text displayed near the branch path. */\n label?: string;\n}\n\n/**\n * Timeline block - renders one or more horizontal tracks with event dots,\n * text callouts, and optional branches between events.\n */\nexport interface TimelineBlockInput extends BaseTemplateBlock {\n template: 'timeline';\n /** Tracks and their events (usually supplied through `templateData`). */\n tracks?: TimelineTemplateTrack[];\n /** Optional branch/cross-track links between event ids. */\n links?: TimelineTemplateLink[];\n /** Optional title above the timeline. */\n title?: string;\n /** Color scheme for tracks, markers, and branch links. */\n colorScheme?: ColorScheme;\n}\n\n/**\n * Photo grid - 2-4 images in a tiled layout.\n * Used when multiple images are available for visual variety.\n */\nexport interface PhotoGridInput extends BaseTemplateBlock {\n template: 'photoGrid';\n /** Images to display (2-4) */\n images: { src: string; alt: string; credit?: string; license?: string }[];\n /** Optional caption below the grid */\n caption?: string;\n /** Ambient motion effect for the largest image */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n}\n\n/**\n * Definition card - dictionary-style term with definition.\n * Large term at top, definition below, optional origin note.\n */\nexport interface DefinitionCardInput extends BaseTemplateBlock {\n template: 'definitionCard';\n /** The term being defined */\n term: string;\n /** The definition text */\n definition: string;\n /** Optional etymology or origin note */\n origin?: string;\n /** Color scheme for term accent */\n colorScheme?: ColorScheme;\n /** Optional accent image */\n accentImage?: AccentImage;\n}\n\n/**\n * Comparison bar - two horizontal bars showing relative numeric values.\n * Good for side-by-side numeric comparisons with visual weight.\n */\nexport interface ComparisonBarInput extends BaseTemplateBlock {\n template: 'comparisonBar';\n /** Left bar label */\n leftLabel: string;\n /** Left bar numeric value */\n leftValue: number;\n /** Right bar label */\n rightLabel: string;\n /** Right bar numeric value */\n rightValue: number;\n /** Unit label (e.g., \"km\", \"people\") */\n unit?: string;\n /** Color scheme */\n colorScheme?: ColorScheme;\n}\n\n/**\n * Pull quote - quote text over a full-bleed background image with dark overlay.\n * Cinematic alternative to quoteBlock when a high-quality image is available.\n */\nexport interface PullQuoteInput extends BaseTemplateBlock {\n template: 'pullQuote';\n /** Quote text */\n text: string;\n /** Optional attribution */\n attribution?: string;\n /** Background image (fills entire viewport) */\n backgroundImage: { src: string; alt: string; credit?: string; license?: string };\n /** Ambient motion for background image */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n}\n\n/**\n * Video with caption - background video clip with text overlay.\n * Plays a short clip from a source video, always muted (narration is the audio track).\n * Mirrors ImageWithCaptionInput but with video-specific fields.\n */\nexport interface VideoWithCaptionInput extends BaseTemplateBlock {\n template: 'videoWithCaption';\n /** Path to video file */\n videoSrc: string;\n /** Path to poster frame (shown before video loads) */\n posterSrc?: string;\n /** Alt text for accessibility */\n videoAlt: string;\n /** Start time within source video (seconds) */\n clipStart: number;\n /** End time within source video (seconds) */\n clipEnd: number;\n /** Source video total duration (for validation) */\n sourceDuration?: number;\n /** Caption text overlay */\n caption?: string;\n /** Caption position */\n captionPosition?: 'bottom' | 'top' | 'center';\n /** Video credit / artist name */\n videoCredit?: string;\n /** License identifier */\n videoLicense?: string;\n}\n\n/**\n * Video pull quote - quote text over a video clip background with dark overlay.\n * Cinematic alternative to pullQuote when a video clip is available.\n */\nexport interface VideoPullQuoteInput extends BaseTemplateBlock {\n template: 'videoPullQuote';\n /** Quote text */\n text: string;\n /** Optional attribution */\n attribution?: string;\n /** Background video clip */\n backgroundVideo: {\n src: string;\n posterSrc?: string;\n alt: string;\n clipStart: number;\n clipEnd: number;\n credit?: string;\n license?: string;\n };\n}\n\n/**\n * One diagram node supplied via `templateData.nodes` (canvas units, the\n * same coordinate space as `Block.x`/`Block.y`). This is the data-driven\n * alternative to child headings — used when a diagram is derived from an\n * ASCII-art fence.\n */\nexport interface DiagramTemplateNode {\n id: string;\n /** Display label; may contain `\\n` for multi-line labels. */\n label: string;\n x: number;\n y: number;\n /** Per-node width override in canvas units (defaults to the standard card width). */\n w?: number;\n /** Per-node height override in canvas units. */\n h?: number;\n /** Id of the node visually containing this one; containers draw behind their children. */\n container?: string;\n}\n\n/** A position along one side of a diagram node, normalized from 0 to 1. */\nexport interface DiagramEdgeAnchor {\n side: 'top' | 'right' | 'bottom' | 'left';\n /** Left→right on horizontal sides; top→bottom on vertical sides. */\n offset: number;\n}\n\n/** An edge for `DiagramTemplateNode` lists. `directed` defaults to true. */\nexport interface DiagramTemplateEdge {\n source: string;\n target: string;\n /** Edge label rendered at the midpoint. */\n label?: string;\n /** False suppresses the end arrowhead (a plain connecting line). */\n directed?: boolean;\n /** Optional authored attachment on the source node. */\n sourceAnchor?: DiagramEdgeAnchor;\n /** Optional authored attachment on the target node. */\n targetAnchor?: DiagramEdgeAnchor;\n /** Per-edge path routing; ASCII-derived edges use orthogonal routing. */\n routing?: 'straight' | 'orthogonal' | 'curved';\n}\n\n/**\n * Diagram block - renders a node-and-edge diagram.\n *\n * Two data sources, in precedence order:\n * 1. Child headings (`context.children`) — each child is a node positioned\n * by its `x`/`y` and connected by its `connectsTo` (legacy authored form).\n * 2. `nodes`/`edges` on this input (usually via `templateData`, derived\n * from an ASCII-art diagram fence in the block body).\n * Per-diagram options on this input control overall appearance.\n */\nexport interface DiagramBlockInput extends BaseTemplateBlock {\n template: 'diagram';\n /** Optional diagram title displayed above the canvas. */\n title?: string;\n /** Color scheme for nodes and edges. */\n colorScheme?: ColorScheme;\n /** Node shape style (default: 'rounded'). */\n nodeShape?: 'rounded' | 'rect' | 'pill';\n /** Edge routing (default: 'curved'). */\n edgeStyle?: 'curved' | 'straight' | 'orthogonal';\n /** Marker at each edge's start (default: 'none'). */\n startStyle?: MarkerStyle;\n /** Marker at each edge's end (default: 'arrow'). */\n endStyle?: MarkerStyle;\n /** Edge line style (default: 'solid'). */\n lineStyle?: 'solid' | 'dashed' | 'dotted';\n /** Data-driven node list; used only when the block has no children. */\n nodes?: DiagramTemplateNode[];\n /** Edges for `nodes`. */\n edges?: DiagramTemplateEdge[];\n}\n\n/**\n * Free-form 2D canvas block (`layout`). Like `drawing`/`diagram`, the\n * `layout` template is a children-driven container: each child heading is\n * one absolutely-positioned layer (`{[text …]}` / `{[rectangle …]}` /\n * `{[image …]}`), read from `context.children`. A text box's content is the\n * child's body markdown. Backed by the `layoutBlock` template.\n *\n * (Legacy documents stored layers as a base64-JSON `layers=` param on the\n * heading; the editor auto-migrates those to child sub-blocks on first edit.)\n */\nexport interface RawLayersInput extends BaseTemplateBlock {\n template: 'layout';\n}\n\n/**\n * Drawing block — renders child headings as free-form shapes on a canvas.\n *\n * Like `diagram`, the drawing template reads its content from the parent\n * block's `children` (passed via `context.children`). Each child heading\n * is a shape: its `{[shape …]}` annotation names the primitive\n * (`rectangle`/`rect`, `circle`, `line`, `arrow`, `path`, `text`) and\n * carries geometry/style as params; its `{#id}` makes it referenceable;\n * its heading text is the shape label and its body text an optional\n * sublabel. Connectors (`line`/`arrow` with `from`/`to`) join shapes by id.\n *\n * Coordinates are author-defined units; the template fits the bounding box\n * of all shapes to the viewport (same scaling as `diagram`), so authors\n * don't have to think in absolute pixels.\n *\n * Legacy drawings authored visually persist their `Layer[]` as a base64\n * `layers=\"…\"` Pandoc param instead of children; the template decodes and\n * returns those directly when no children are present (see `drawingBlock`).\n */\nexport interface DrawingBlockInput extends BaseTemplateBlock {\n template: 'drawing';\n /** Optional title displayed above the canvas. */\n title?: string;\n /** Color scheme for shapes that don't pin their own fill/stroke. */\n colorScheme?: ColorScheme;\n /** Default fill for shapes that don't specify one (default: 'none'). */\n fill?: string;\n /** Default stroke for shapes that don't specify one (default: theme text). */\n stroke?: string;\n}\n\n/**\n * Data table - renders a themed table with headers and rows.\n * Ideal for structured data, comparisons, and reference information.\n */\nexport interface DataTableInput extends BaseTemplateBlock {\n template: 'dataTable';\n /** Optional title displayed above the table */\n title?: string;\n /** Header cell values */\n headers: string[];\n /** Data rows (array of cell value arrays) */\n rows: string[][];\n /** Per-column alignment */\n align?: (('left' | 'right' | 'center') | null)[];\n /** Color scheme for the table header */\n colorScheme?: ColorScheme;\n}\n\n/**\n * Union of all template block types.\n */\nexport type TemplateBlock =\n | TitleBlockInput\n | SectionHeaderInput\n | StatHighlightInput\n | QuoteBlockInput\n | FactCardInput\n | TwoColumnInput\n | DateEventInput\n | ImageWithCaptionInput\n | LeftFeatureInput\n | RightFeatureInput\n | MapBlockInput\n | FullBleedQuoteInput\n | ListBlockInput\n | PhotoGridInput\n | DefinitionCardInput\n | ComparisonBarInput\n | PullQuoteInput\n | VideoWithCaptionInput\n | VideoPullQuoteInput\n | DataTableInput\n | DiagramBlockInput\n | TreeBlockInput\n | TimelineBlockInput\n | DrawingBlockInput\n | RawLayersInput;\n\n/**\n * A block can be either a raw Block or a TemplateBlock.\n */\nexport type DocBlock = Block | TemplateBlock;\n\n/**\n * Check if a block is a template block.\n */\nexport function isTemplateBlock(block: DocBlock): block is TemplateBlock {\n return 'template' in block && typeof block.template === 'string';\n}\n\n// ============================================\n// Template Function Type\n// ============================================\n\n/**\n * Template context passed to template functions.\n * Includes viewport information for multi-aspect ratio support.\n */\nexport interface TemplateContext {\n /** Full theme (colors, typography, style, renderStyle, colorSchemes) */\n theme: Theme;\n /** Block index in the doc */\n blockIndex: number;\n /** Total number of blocks */\n totalBlocks: number;\n /** Target viewport configuration */\n viewport: ViewportConfig;\n /** Computed font scale factor for this viewport */\n fontScale: number;\n /** Viewport orientation (landscape, portrait, square) */\n orientation: ViewportOrientation;\n /** Layout hints for this orientation */\n layout: LayoutHints;\n /**\n * The block's direct children (set by `materializeBlockLayers` when the block has\n * `children`). Most templates ignore this; aggregate templates like\n * `diagram` consume it to render each child as part of their output.\n */\n children?: Block[];\n /**\n * The source block being expanded. Used by user-defined custom\n * templates so the token resolver can substitute `{title}`,\n * `{content}`, `{children}`, and `{image:N}` against the block's\n * data at render time. Built-in templates ignore this and rely on\n * their typed `input` parameters instead.\n */\n block?: Block;\n}\n\n/**\n * Create a template context with viewport-derived values.\n */\nexport function createTemplateContext(\n theme: Theme,\n blockIndex: number,\n totalBlocks: number,\n viewport: ViewportConfig = VIEWPORT_PRESETS.landscape,\n): TemplateContext {\n const orientation = getViewportOrientation(viewport);\n // Theme renderStyle may override individual layout hints (e.g. a theme\n // that wants a lower primaryY across every template).\n const overrides = theme.renderStyle.layoutOverrides;\n const baseLayout = getLayoutHints(orientation);\n return {\n theme,\n blockIndex,\n totalBlocks,\n viewport,\n fontScale: calculateFontScale(viewport),\n orientation,\n layout: overrides ? { ...baseLayout, ...overrides } : baseLayout,\n };\n}\n\n/**\n * Calculate a scaled font size for the given context.\n * Use this in templates to ensure text scales appropriately.\n *\n * @param basePx - Base font size in pixels (designed for 1920x1080)\n * @param context - Template context with viewport info\n * @param isTitle - Whether this is title text (larger scale) or body text\n */\nexport function scaledFontSize(\n basePx: number,\n context: TemplateContext,\n isTitle: boolean = false,\n): number {\n return scaleFontSize(basePx, context.viewport, context.orientation, isTitle);\n}\n\n/**\n * Template function signature.\n * Takes template input and returns layers array.\n */\nexport type TemplateFunction<T extends TemplateBlock> = (\n input: T,\n context: TemplateContext,\n) => Layer[];\n\n/**\n * Registry of all template functions.\n */\nexport type TemplateRegistry = {\n [K in TemplateBlock['template']]: TemplateFunction<Extract<TemplateBlock, { template: K }>>;\n};\n\n// ============================================\n// Persistent Layer System\n// ============================================\n\n/**\n * Configuration for persistent doc-wide layers.\n * Bottom layers render behind all block content, top layers on top.\n */\nexport interface PersistentLayerConfig {\n /** Layers rendered behind all block content */\n bottomLayers?: PersistentLayer[];\n /** Layers rendered on top of all block content */\n topLayers?: PersistentLayer[];\n}\n\n/**\n * A persistent layer - either a template or raw Layer.\n */\nexport type PersistentLayer = PersistentLayerTemplate | Layer;\n\n/**\n * Check if a persistent layer is a template.\n */\nexport function isPersistentLayerTemplate(\n layer: PersistentLayer,\n): layer is PersistentLayerTemplate {\n return 'template' in layer && typeof layer.template === 'string';\n}\n\n/**\n * Template-based persistent layer for easy configuration.\n */\nexport interface PersistentLayerTemplate {\n template: PersistentLayerTemplateType;\n config: PersistentLayerTemplateConfig;\n}\n\n/**\n * Available persistent layer template types.\n */\nexport type PersistentLayerTemplateType =\n | 'solidBackground' // Solid color fill\n | 'gradientBackground' // CSS gradient or preset\n | 'imageBackground' // Blurred/faded hero image\n | 'patternBackground' // Subtle pattern (dots, grid, diagonal, noise/grain)\n | 'vignette' // Soft radial edge darkening (top layer)\n | 'ambientGradient' // Slowly drifting surface gradient (bottom layer)\n | 'titleCaption' // Article title in corner\n | 'cornerBranding' // Logo or text badge\n | 'progressIndicator'; // Bar/dots showing position\n\n/**\n * Union of all persistent layer template configs.\n */\nexport type PersistentLayerTemplateConfig =\n | SolidBackgroundConfig\n | GradientBackgroundConfig\n | ImageBackgroundConfig\n | PatternBackgroundConfig\n | VignetteConfig\n | AmbientGradientConfig\n | TitleCaptionConfig\n | CornerBrandingConfig\n | ProgressIndicatorConfig;\n\n// ============================================\n// Background Layer Configs\n// ============================================\n\n/**\n * Solid color background.\n */\nexport interface SolidBackgroundConfig {\n type: 'solidBackground';\n /** CSS color value */\n color: string;\n}\n\n/**\n * Gradient background (CSS gradient or preset).\n */\nexport interface GradientBackgroundConfig {\n type: 'gradientBackground';\n /** Custom CSS gradient string */\n gradient?: string;\n /** Preset gradient name */\n preset?: 'dark-vignette' | 'radial-dark' | 'warm-sunset' | 'cool-blue' | 'earth-tones';\n}\n\n/**\n * Image background (blurred/faded hero image).\n */\nexport interface ImageBackgroundConfig {\n type: 'imageBackground';\n /** Path to image file */\n src: string;\n /** Blur radius in pixels (0-20) */\n blur?: number;\n /** Opacity (0-1) */\n opacity?: number;\n /** Ambient motion effect */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n}\n\n/**\n * Pattern background (subtle repeating pattern).\n */\nexport interface PatternBackgroundConfig {\n type: 'patternBackground';\n /** Pattern type */\n pattern: 'dots' | 'grid' | 'diagonal' | 'noise';\n /** Pattern color */\n color?: string;\n /** Pattern opacity (0-1) */\n opacity?: number;\n /** Pattern scale multiplier */\n scale?: number;\n}\n\n/**\n * Soft radial vignette (top layer) — transparent center darkening toward\n * the frame edges. The classic film-look framing device.\n */\nexport interface VignetteConfig {\n type: 'vignette';\n /** Edge darkness 0-1 (default 0.3). */\n strength?: number;\n /** Vignette color (default black). */\n color?: string;\n}\n\n/**\n * Slowly drifting surface gradient (bottom layer). Colors default to the\n * theme's surface family at expansion time.\n */\nexport interface AmbientGradientConfig {\n type: 'ambientGradient';\n /** Gradient start color. */\n from?: string;\n /** Gradient end color. */\n to?: string;\n /** Drift loop duration in seconds (default 40). */\n duration?: number;\n}\n\n// ============================================\n// Overlay Layer Configs\n// ============================================\n\n/**\n * Title caption overlay (article title in corner).\n */\nexport interface TitleCaptionConfig {\n type: 'titleCaption';\n /** Title text */\n title: string;\n /** Subtitle text (e.g., short URL like \"qual..la/slug\") */\n subtitle?: string;\n /** Position on screen */\n position: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';\n /** Font size in pixels (default: 18) */\n fontSize?: number;\n /** Show thumbnail alongside title */\n showThumbnail?: boolean;\n /** Thumbnail image path */\n thumbnailSrc?: string;\n}\n\n/**\n * Corner branding overlay (logo or text badge).\n */\nexport interface CornerBrandingConfig {\n type: 'cornerBranding';\n /** Text content or image path */\n content: string;\n /** Position on screen */\n position: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';\n /** Whether content is an image path (vs text) */\n isImage?: boolean;\n}\n\n/**\n * Progress indicator overlay (bar or dots showing position).\n */\nexport interface ProgressIndicatorConfig {\n type: 'progressIndicator';\n /** Indicator style */\n style: 'bar' | 'dots' | 'fraction';\n /** Position on screen */\n position: 'bottom' | 'top';\n /** Indicator color */\n color?: string;\n}\n","/**\n * Clone JSON-shaped runtime configuration without relying on structuredClone\n * (which is unavailable in some of the ES2020/browser runtimes core supports).\n * Data objects are recreated with safe own-property definitions so even a key\n * named `__proto__` remains ordinary data.\n */\nexport function cloneData<T>(value: T, seen: WeakMap<object, unknown> = new WeakMap()): T {\n if (value === null || typeof value !== 'object') return value;\n\n const existing = seen.get(value);\n if (existing !== undefined) return existing as T;\n\n const tag = Object.prototype.toString.call(value);\n if (tag === '[object ArrayBuffer]') {\n const source = new Uint8Array(value as unknown as ArrayBuffer);\n const copy = new Uint8Array(source.byteLength);\n copy.set(source);\n return copy.buffer as T;\n }\n if (tag === '[object Date]') {\n return new Date(Date.prototype.getTime.call(value)) as T;\n }\n\n if (Array.isArray(value)) {\n const copy: unknown[] = [];\n seen.set(value, copy);\n for (const item of value) copy.push(cloneData(item, seen));\n return copy as T;\n }\n\n const source = value as Record<string, unknown>;\n const copy: Record<string, unknown> = {};\n seen.set(value, copy);\n for (const key of Object.keys(source)) {\n Object.defineProperty(copy, key, {\n value: cloneData(source[key], seen),\n enumerable: true,\n configurable: true,\n writable: true,\n });\n }\n return copy as T;\n}\n\n/** Recursively freeze a data graph. Cycles are supported. */\nexport function deepFreezeData<T>(value: T, seen: WeakSet<object> = new WeakSet()): T {\n if (value === null || typeof value !== 'object' || seen.has(value)) return value;\n seen.add(value);\n for (const key of Object.keys(value)) {\n deepFreezeData((value as Record<string, unknown>)[key], seen);\n }\n return Object.freeze(value);\n}\n\n/** Take an immutable snapshot suitable for storing in a process registry. */\nexport function cloneAndFreezeData<T>(value: T): T {\n return deepFreezeData(cloneData(value));\n}\n"],"mappings":"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","names":["scaledFontSize","source","copy"]}
|