@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
|
@@ -19,6 +19,43 @@ import {
|
|
|
19
19
|
} from '../utils/themeUtils.js';
|
|
20
20
|
import { createAccentLayers, getAccentLayout, adjustY, DEFAULT_LAYOUT } from './accentImage.js';
|
|
21
21
|
|
|
22
|
+
const LIST_ITEM_LINE_HEIGHT = 1.2;
|
|
23
|
+
const LIST_ITEM_GAP_PX = 18;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Mirror the renderer's character-based wrapping closely enough to reserve
|
|
27
|
+
* vertical space for each independently positioned text layer.
|
|
28
|
+
*/
|
|
29
|
+
function estimateWrappedLineCount(text: string, fontSize: number, maxWidth: number): number {
|
|
30
|
+
if (!text.trim()) return 1;
|
|
31
|
+
|
|
32
|
+
const charsPerLine = Math.floor(maxWidth / (fontSize * 0.5));
|
|
33
|
+
if (charsPerLine <= 0) return 1;
|
|
34
|
+
|
|
35
|
+
let lineCount = 0;
|
|
36
|
+
let currentLineLength = 0;
|
|
37
|
+
|
|
38
|
+
for (const word of text.split(/\s+/)) {
|
|
39
|
+
const testLineLength = currentLineLength ? currentLineLength + 1 + word.length : word.length;
|
|
40
|
+
|
|
41
|
+
if (testLineLength <= charsPerLine) {
|
|
42
|
+
currentLineLength = testLineLength;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (currentLineLength) lineCount += 1;
|
|
47
|
+
|
|
48
|
+
let remainingLength = word.length;
|
|
49
|
+
while (remainingLength > charsPerLine) {
|
|
50
|
+
lineCount += 1;
|
|
51
|
+
remainingLength -= charsPerLine;
|
|
52
|
+
}
|
|
53
|
+
currentLineLength = remainingLength;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Math.max(1, lineCount + (currentLineLength ? 1 : 0));
|
|
57
|
+
}
|
|
58
|
+
|
|
22
59
|
export function listBlock(input: ListBlockInput, context: TemplateContext): Layer[] {
|
|
23
60
|
const { title, accentImage } = input;
|
|
24
61
|
// `items` is required by the schema, but malformed / partially-authored
|
|
@@ -66,8 +103,9 @@ export function listBlock(input: ListBlockInput, context: TemplateContext): Laye
|
|
|
66
103
|
// defensive against future edits to the accent-layout types).
|
|
67
104
|
const centerX = parseFloat(accentLayout.textCenterX);
|
|
68
105
|
const widthPct = parseFloat(accentLayout.textWidth);
|
|
69
|
-
const
|
|
70
|
-
Number.isFinite(centerX) && Number.isFinite(widthPct) ?
|
|
106
|
+
const leftPct =
|
|
107
|
+
Number.isFinite(centerX) && Number.isFinite(widthPct) ? centerX - widthPct / 2 : 8;
|
|
108
|
+
const leftX = `${leftPct}%`;
|
|
71
109
|
|
|
72
110
|
// Title if provided
|
|
73
111
|
const startY = title ? 34 : 26;
|
|
@@ -95,28 +133,49 @@ export function listBlock(input: ListBlockInput, context: TemplateContext): Laye
|
|
|
95
133
|
});
|
|
96
134
|
}
|
|
97
135
|
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const LIST_ITEM_BASE_PX = 34;
|
|
110
|
-
const LIST_ITEM_LINE_HEIGHT = 1.2;
|
|
111
|
-
const LIST_ITEM_GAP_PX = 18;
|
|
112
|
-
const DESIGN_HEIGHT_PX = 1080;
|
|
113
|
-
const spacing =
|
|
114
|
-
((LIST_ITEM_BASE_PX * LIST_ITEM_LINE_HEIGHT + LIST_ITEM_GAP_PX) / DESIGN_HEIGHT_PX) * 100;
|
|
136
|
+
// Render the number and body in separate columns. The body is therefore a
|
|
137
|
+
// single text layer whose wrapped lines all share the same x coordinate,
|
|
138
|
+
// producing a hanging indent instead of wrapping underneath the number.
|
|
139
|
+
const textWidthPx = (Number.isFinite(widthPct) ? widthPct / 100 : 0.85) * context.viewport.width;
|
|
140
|
+
const markerText = `${items.length}.`;
|
|
141
|
+
const markerWidthPx = Math.max(itemFontSize, markerText.length * itemFontSize * 0.5);
|
|
142
|
+
const markerGapPx = itemFontSize * 0.35;
|
|
143
|
+
const bodyIndentPx = markerWidthPx + markerGapPx;
|
|
144
|
+
const bodyLeftX = `${leftPct + (bodyIndentPx / context.viewport.width) * 100}%`;
|
|
145
|
+
const bodyWidthPx = Math.max(itemFontSize, textWidthPx - bodyIndentPx);
|
|
146
|
+
let itemY = startY;
|
|
115
147
|
|
|
116
148
|
// List items with staggered animation
|
|
117
149
|
for (let i = 0; i < items.length; i++) {
|
|
118
|
-
const
|
|
119
|
-
const
|
|
150
|
+
const itemText = items[i]!;
|
|
151
|
+
const lineCount = estimateWrappedLineCount(itemText, itemFontSize, bodyWidthPx);
|
|
152
|
+
const animation = themedEntrance(context, 'text', {
|
|
153
|
+
type: 'fadeIn',
|
|
154
|
+
duration: 0.8,
|
|
155
|
+
delay: 0.3 + 0.3 * i,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
layers.push({
|
|
159
|
+
type: 'text',
|
|
160
|
+
id: `item-${i}-marker`,
|
|
161
|
+
content: {
|
|
162
|
+
text: `${i + 1}.`,
|
|
163
|
+
style: {
|
|
164
|
+
fontSize: itemFontSize,
|
|
165
|
+
fontFamily: getThemeFont(context, 'body'),
|
|
166
|
+
color: theme.colors.text,
|
|
167
|
+
textAlign: 'right',
|
|
168
|
+
lineHeight: LIST_ITEM_LINE_HEIGHT,
|
|
169
|
+
shadow: shouldUseShadow(context),
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
position: {
|
|
173
|
+
x: leftX,
|
|
174
|
+
y: adjustY(`${itemY}%`, accentLayout),
|
|
175
|
+
width: markerWidthPx,
|
|
176
|
+
},
|
|
177
|
+
animation,
|
|
178
|
+
});
|
|
120
179
|
|
|
121
180
|
layers.push({
|
|
122
181
|
type: 'text',
|
|
@@ -128,21 +187,20 @@ export function listBlock(input: ListBlockInput, context: TemplateContext): Laye
|
|
|
128
187
|
fontFamily: getThemeFont(context, 'body'),
|
|
129
188
|
color: theme.colors.text,
|
|
130
189
|
textAlign: 'left',
|
|
131
|
-
lineHeight:
|
|
190
|
+
lineHeight: LIST_ITEM_LINE_HEIGHT,
|
|
132
191
|
shadow: shouldUseShadow(context),
|
|
133
192
|
},
|
|
134
193
|
},
|
|
135
194
|
position: {
|
|
136
|
-
x:
|
|
137
|
-
y: adjustY(`${
|
|
138
|
-
width:
|
|
195
|
+
x: bodyLeftX,
|
|
196
|
+
y: adjustY(`${itemY}%`, accentLayout),
|
|
197
|
+
width: bodyWidthPx,
|
|
139
198
|
},
|
|
140
|
-
animation
|
|
141
|
-
type: 'fadeIn',
|
|
142
|
-
duration: 0.8,
|
|
143
|
-
delay: 0.3 + 0.3 * i,
|
|
144
|
-
}),
|
|
199
|
+
animation,
|
|
145
200
|
});
|
|
201
|
+
|
|
202
|
+
const itemHeightPx = lineCount * itemFontSize * LIST_ITEM_LINE_HEIGHT;
|
|
203
|
+
itemY += ((itemHeightPx + LIST_ITEM_GAP_PX) / context.viewport.height) * 100;
|
|
146
204
|
}
|
|
147
205
|
|
|
148
206
|
return layers;
|
|
@@ -108,6 +108,16 @@ export const TEMPLATE_METADATA: Record<string, TemplateMetadata> = {
|
|
|
108
108
|
description:
|
|
109
109
|
'Renders sub-headings as connected nodes — edit visually as a node-and-edge diagram.',
|
|
110
110
|
},
|
|
111
|
+
tree: {
|
|
112
|
+
label: 'Tree',
|
|
113
|
+
description:
|
|
114
|
+
'Renders an ASCII file-tree / outline fence as a filesystem-style treeview — edit as an outline.',
|
|
115
|
+
},
|
|
116
|
+
timeline: {
|
|
117
|
+
label: 'Timeline',
|
|
118
|
+
description:
|
|
119
|
+
'Plots events across one or more horizontal tracks with callouts and optional branching links.',
|
|
120
|
+
},
|
|
111
121
|
layout: {
|
|
112
122
|
label: 'Layout',
|
|
113
123
|
description:
|
|
@@ -27,6 +27,7 @@ import type {
|
|
|
27
27
|
ProgressIndicatorConfig,
|
|
28
28
|
} from '../../schemas/BlockTemplates.js';
|
|
29
29
|
import type { Theme } from '../../schemas/Theme.js';
|
|
30
|
+
import { cloneData } from '../../internal/immutable.js';
|
|
30
31
|
import { isPersistentLayerTemplate } from '../../schemas/BlockTemplates.js';
|
|
31
32
|
import { oklchDarken, withAlpha } from '../../schemas/colorUtils.js';
|
|
32
33
|
|
|
@@ -415,9 +416,9 @@ function expandProgressIndicator(config: ProgressIndicatorConfig): Layer {
|
|
|
415
416
|
* Expand a single persistent layer (template or raw) to raw Layer(s).
|
|
416
417
|
*/
|
|
417
418
|
export function expandPersistentLayer(layer: PersistentLayer, theme?: Theme): Layer[] {
|
|
418
|
-
// If already a raw Layer, return
|
|
419
|
+
// If already a raw Layer, return an owned copy.
|
|
419
420
|
if (!isPersistentLayerTemplate(layer)) {
|
|
420
|
-
return [layer as Layer];
|
|
421
|
+
return [cloneData(layer as Layer)];
|
|
421
422
|
}
|
|
422
423
|
|
|
423
424
|
const template = layer as PersistentLayerTemplate;
|
|
@@ -466,128 +467,6 @@ export function expandPersistentLayers(
|
|
|
466
467
|
);
|
|
467
468
|
}
|
|
468
469
|
|
|
469
|
-
// ============================================
|
|
470
|
-
// Style Presets (legacy helper — prefer theme.persistentLayers)
|
|
471
|
-
// ============================================
|
|
472
|
-
|
|
473
|
-
/** Legacy style preset names. Prefer using `Theme.persistentLayers` directly. */
|
|
474
|
-
export type DocStylePreset = 'minimal' | 'documentary' | 'branded' | 'cinematic' | 'clean';
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Get a PersistentLayerConfig from a style preset.
|
|
478
|
-
*
|
|
479
|
-
* @param preset - Style preset name
|
|
480
|
-
* @param articleTitle - Article title for title caption
|
|
481
|
-
* @param heroSrc - Optional hero image for cinematic style
|
|
482
|
-
*/
|
|
483
|
-
export function getDocStyleConfig(
|
|
484
|
-
preset: DocStylePreset,
|
|
485
|
-
articleTitle: string,
|
|
486
|
-
heroSrc?: string,
|
|
487
|
-
subtitle?: string,
|
|
488
|
-
): PersistentLayerConfig {
|
|
489
|
-
switch (preset) {
|
|
490
|
-
case 'minimal':
|
|
491
|
-
return {};
|
|
492
|
-
|
|
493
|
-
case 'documentary':
|
|
494
|
-
return {
|
|
495
|
-
bottomLayers: [
|
|
496
|
-
{
|
|
497
|
-
template: 'gradientBackground',
|
|
498
|
-
config: { type: 'gradientBackground', preset: 'dark-vignette' },
|
|
499
|
-
},
|
|
500
|
-
],
|
|
501
|
-
topLayers: [
|
|
502
|
-
{
|
|
503
|
-
template: 'titleCaption',
|
|
504
|
-
config: {
|
|
505
|
-
type: 'titleCaption',
|
|
506
|
-
title: articleTitle,
|
|
507
|
-
subtitle,
|
|
508
|
-
position: 'bottom-left',
|
|
509
|
-
fontSize: 24,
|
|
510
|
-
showThumbnail: !!heroSrc,
|
|
511
|
-
thumbnailSrc: heroSrc,
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
case 'branded':
|
|
518
|
-
return {
|
|
519
|
-
bottomLayers: [
|
|
520
|
-
{
|
|
521
|
-
template: 'gradientBackground',
|
|
522
|
-
config: { type: 'gradientBackground', preset: 'cool-blue' },
|
|
523
|
-
},
|
|
524
|
-
],
|
|
525
|
-
topLayers: [
|
|
526
|
-
{
|
|
527
|
-
template: 'titleCaption',
|
|
528
|
-
config: {
|
|
529
|
-
type: 'titleCaption',
|
|
530
|
-
title: articleTitle,
|
|
531
|
-
subtitle,
|
|
532
|
-
position: 'bottom-left',
|
|
533
|
-
fontSize: 26,
|
|
534
|
-
showThumbnail: !!heroSrc,
|
|
535
|
-
thumbnailSrc: heroSrc,
|
|
536
|
-
},
|
|
537
|
-
},
|
|
538
|
-
],
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
case 'cinematic':
|
|
542
|
-
return {
|
|
543
|
-
bottomLayers: heroSrc
|
|
544
|
-
? [
|
|
545
|
-
{
|
|
546
|
-
template: 'imageBackground',
|
|
547
|
-
config: {
|
|
548
|
-
type: 'imageBackground',
|
|
549
|
-
src: heroSrc,
|
|
550
|
-
blur: 12,
|
|
551
|
-
opacity: 0.3,
|
|
552
|
-
ambientMotion: 'zoomIn',
|
|
553
|
-
},
|
|
554
|
-
},
|
|
555
|
-
]
|
|
556
|
-
: [
|
|
557
|
-
{
|
|
558
|
-
template: 'gradientBackground',
|
|
559
|
-
config: { type: 'gradientBackground', preset: 'radial-dark' },
|
|
560
|
-
},
|
|
561
|
-
],
|
|
562
|
-
topLayers: [
|
|
563
|
-
{
|
|
564
|
-
template: 'titleCaption',
|
|
565
|
-
config: {
|
|
566
|
-
type: 'titleCaption',
|
|
567
|
-
title: articleTitle,
|
|
568
|
-
subtitle,
|
|
569
|
-
position: 'bottom-right',
|
|
570
|
-
fontSize: 22,
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
],
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
case 'clean':
|
|
577
|
-
return {
|
|
578
|
-
bottomLayers: [
|
|
579
|
-
{
|
|
580
|
-
template: 'solidBackground',
|
|
581
|
-
config: { type: 'solidBackground', color: '#1a202c' },
|
|
582
|
-
},
|
|
583
|
-
],
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
default:
|
|
587
|
-
return {};
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
470
|
/**
|
|
592
471
|
* Get persistent layers from a Theme. Returns the theme's baked-in
|
|
593
472
|
* persistentLayers config, or an empty config when the theme has none.
|
|
@@ -625,7 +504,7 @@ export function resolvePersistentLayers(
|
|
|
625
504
|
/**
|
|
626
505
|
* Compose a block's layers between pre-expanded persistent bottom/top
|
|
627
506
|
* layers, honoring the per-block `useBottomLayer` / `useTopLayer` opt-outs.
|
|
628
|
-
*
|
|
507
|
+
* Shared persistent-layer primitive used by `materializeBlockLayers`.
|
|
629
508
|
*/
|
|
630
509
|
export function wrapWithPersistentLayers(
|
|
631
510
|
layers: Layer[],
|
|
@@ -636,5 +515,12 @@ export function wrapWithPersistentLayers(
|
|
|
636
515
|
if (bottomLayers.length === 0 && topLayers.length === 0) return layers;
|
|
637
516
|
const useBottom = block.useBottomLayer !== false;
|
|
638
517
|
const useTop = block.useTopLayer !== false;
|
|
639
|
-
|
|
518
|
+
// Each expanded block owns its persistent layers. This prevents a renderer
|
|
519
|
+
// (or a later timing/style pass) from mutating the caller's config or a
|
|
520
|
+
// sibling block through a shared layer reference.
|
|
521
|
+
return [
|
|
522
|
+
...(useBottom ? bottomLayers.map((layer) => cloneData(layer)) : []),
|
|
523
|
+
...layers,
|
|
524
|
+
...(useTop ? topLayers.map((layer) => cloneData(layer)) : []),
|
|
525
|
+
];
|
|
640
526
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Runtime registry composition for built-in and document-scoped templates. */
|
|
2
|
+
|
|
3
|
+
import type { Layer } from '../../schemas/Doc.js';
|
|
4
|
+
import type {
|
|
5
|
+
TemplateBlock,
|
|
6
|
+
TemplateContext,
|
|
7
|
+
TemplateRegistry,
|
|
8
|
+
} from '../../schemas/BlockTemplates.js';
|
|
9
|
+
import type { CustomTemplateDefinition } from '../../schemas/CustomTemplates.js';
|
|
10
|
+
import { makeCustomTemplateFn } from './customTemplate.js';
|
|
11
|
+
|
|
12
|
+
import { titleBlock } from './titleBlock.js';
|
|
13
|
+
import { sectionHeader } from './sectionHeader.js';
|
|
14
|
+
import { statHighlight } from './statHighlight.js';
|
|
15
|
+
import { quoteBlock } from './quoteBlock.js';
|
|
16
|
+
import { factCard } from './factCard.js';
|
|
17
|
+
import { twoColumn } from './twoColumn.js';
|
|
18
|
+
import { dateEvent } from './dateEvent.js';
|
|
19
|
+
import { imageWithCaption } from './imageWithCaption.js';
|
|
20
|
+
import { leftFeature, rightFeature } from './featureBlock.js';
|
|
21
|
+
import { mapBlock } from './mapBlock.js';
|
|
22
|
+
import { fullBleedQuote } from './fullBleedQuote.js';
|
|
23
|
+
import { listBlock } from './listBlock.js';
|
|
24
|
+
import { photoGrid } from './photoGrid.js';
|
|
25
|
+
import { definitionCard } from './definitionCard.js';
|
|
26
|
+
import { comparisonBar } from './comparisonBar.js';
|
|
27
|
+
import { pullQuote } from './pullQuote.js';
|
|
28
|
+
import { videoWithCaption } from './videoWithCaption.js';
|
|
29
|
+
import { videoPullQuote } from './videoPullQuote.js';
|
|
30
|
+
import { dataTable } from './dataTable.js';
|
|
31
|
+
import { diagramBlock } from './diagramBlock.js';
|
|
32
|
+
import { treeBlock } from './treeBlock.js';
|
|
33
|
+
import { timelineBlock } from './timelineBlock.js';
|
|
34
|
+
import { drawingBlock } from './drawingBlock.js';
|
|
35
|
+
import { layoutBlock } from './layoutBlock.js';
|
|
36
|
+
|
|
37
|
+
/** Built-in templates keyed by the ids used in document annotations. */
|
|
38
|
+
export const templateRegistry: TemplateRegistry = {
|
|
39
|
+
title: titleBlock,
|
|
40
|
+
sectionHeader,
|
|
41
|
+
statHighlight,
|
|
42
|
+
quote: quoteBlock,
|
|
43
|
+
factCard,
|
|
44
|
+
twoColumn,
|
|
45
|
+
dateEvent,
|
|
46
|
+
imageWithCaption,
|
|
47
|
+
leftFeature,
|
|
48
|
+
rightFeature,
|
|
49
|
+
map: mapBlock,
|
|
50
|
+
fullBleedQuote,
|
|
51
|
+
list: listBlock,
|
|
52
|
+
photoGrid,
|
|
53
|
+
definitionCard,
|
|
54
|
+
comparisonBar,
|
|
55
|
+
pullQuote,
|
|
56
|
+
videoWithCaption,
|
|
57
|
+
videoPullQuote,
|
|
58
|
+
dataTable,
|
|
59
|
+
diagram: diagramBlock,
|
|
60
|
+
tree: treeBlock,
|
|
61
|
+
timeline: timelineBlock,
|
|
62
|
+
layout: layoutBlock,
|
|
63
|
+
drawing: drawingBlock,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Broad runtime view used after a template name has selected its function. */
|
|
67
|
+
export type RuntimeTemplateRegistry = Record<
|
|
68
|
+
string,
|
|
69
|
+
(input: TemplateBlock, context: TemplateContext) => Layer[]
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
/** Merge document-scoped templates without mutating the built-in registry. */
|
|
73
|
+
export function buildRegistry(
|
|
74
|
+
custom?: readonly CustomTemplateDefinition[],
|
|
75
|
+
): RuntimeTemplateRegistry {
|
|
76
|
+
const merged: RuntimeTemplateRegistry = {
|
|
77
|
+
...(templateRegistry as unknown as RuntimeTemplateRegistry),
|
|
78
|
+
};
|
|
79
|
+
if (custom) {
|
|
80
|
+
for (const definition of custom) {
|
|
81
|
+
if (merged[definition.name]) continue;
|
|
82
|
+
merged[definition.name] = makeCustomTemplateFn(
|
|
83
|
+
definition,
|
|
84
|
+
) as unknown as RuntimeTemplateRegistry[string];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return merged;
|
|
88
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Canonical aliases for historical template ids. Kept dependency-free. */
|
|
2
|
+
export const TEMPLATE_ALIASES: Readonly<Record<string, string>> = Object.freeze({
|
|
3
|
+
titleBlock: 'title',
|
|
4
|
+
quoteBlock: 'quote',
|
|
5
|
+
mapBlock: 'map',
|
|
6
|
+
listBlock: 'list',
|
|
7
|
+
diagramBlock: 'diagram',
|
|
8
|
+
diagramNode: 'diagram',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
/** Resolve a historical template id to its canonical registry key. */
|
|
12
|
+
export function resolveTemplateName(name: string): string {
|
|
13
|
+
return TEMPLATE_ALIASES[name] ?? name;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Templates that consume their child headings rather than rendering them. */
|
|
17
|
+
export const CONTAINER_TEMPLATES: ReadonlySet<string> = new Set(['diagram', 'drawing', 'layout']);
|
|
18
|
+
|
|
19
|
+
/** True when `name` (or its alias) consumes child blocks. */
|
|
20
|
+
export function isContainerTemplate(name: string | undefined): boolean {
|
|
21
|
+
return !!name && CONTAINER_TEMPLATES.has(resolveTemplateName(name));
|
|
22
|
+
}
|