@bendyline/squisq 1.4.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/{ContentContainer-DNx460y8.d.ts → ContentContainer-BXUlIf18.d.ts} +6 -2
- package/dist/{Doc-BeSeH2iX.d.ts → Doc-BBVGq1_9.d.ts} +922 -273
- package/dist/{ImageEditDoc-BTIvw0oq.d.ts → ImageEditDoc-FuyGtt6o.d.ts} +11 -4
- package/dist/{chunk-ZTX4PFFT.js → chunk-2OIBZYKP.js} +75 -2
- package/dist/chunk-2OIBZYKP.js.map +1 -0
- package/dist/chunk-2UQOHYE2.js +45 -0
- package/dist/chunk-2UQOHYE2.js.map +1 -0
- package/dist/chunk-6ATE2PSM.js +344 -0
- package/dist/chunk-6ATE2PSM.js.map +1 -0
- package/dist/{chunk-57CVQCFX.js → chunk-A4O7GIWE.js} +74 -23
- package/dist/chunk-A4O7GIWE.js.map +1 -0
- package/dist/{chunk-5WFRKURX.js → chunk-DLXZMS5K.js} +4 -2
- package/dist/chunk-DLXZMS5K.js.map +1 -0
- package/dist/chunk-ET53IIEP.js +1 -0
- package/dist/chunk-ET53IIEP.js.map +1 -0
- package/dist/chunk-HWVFJAAH.js +362 -0
- package/dist/chunk-HWVFJAAH.js.map +1 -0
- package/dist/{chunk-2TMAWB4Y.js → chunk-IMSCRLLV.js} +1 -1
- package/dist/chunk-IMSCRLLV.js.map +1 -0
- package/dist/{chunk-B4EEON3N.js → chunk-KKNUBQ6Y.js} +536 -103
- package/dist/chunk-KKNUBQ6Y.js.map +1 -0
- package/dist/chunk-LDQ2HJIX.js +596 -0
- package/dist/chunk-LDQ2HJIX.js.map +1 -0
- package/dist/chunk-LH7I6SH7.js +7208 -0
- package/dist/chunk-LH7I6SH7.js.map +1 -0
- package/dist/chunk-PGGGKYWG.js +1 -0
- package/dist/chunk-PGGGKYWG.js.map +1 -0
- package/dist/{chunk-7UDSRZKG.js → chunk-Q3ROPT5H.js} +35 -14
- package/dist/chunk-Q3ROPT5H.js.map +1 -0
- package/dist/chunk-QE7TWGVT.js +178 -0
- package/dist/chunk-QE7TWGVT.js.map +1 -0
- package/dist/chunk-RUDYOTA6.js +345 -0
- package/dist/chunk-RUDYOTA6.js.map +1 -0
- package/dist/{chunk-K32VJONL.js → chunk-T5UK6YOB.js} +41 -2
- package/dist/chunk-T5UK6YOB.js.map +1 -0
- package/dist/{chunk-3K5OG6KI.js → chunk-TEEEILMP.js} +412 -186
- package/dist/chunk-TEEEILMP.js.map +1 -0
- package/dist/{chunk-FR2RBTKO.js → chunk-TQWLI6S2.js} +93 -27
- package/dist/chunk-TQWLI6S2.js.map +1 -0
- package/dist/{chunk-WMOI7FN3.js → chunk-UY7KGQ5R.js} +12 -2
- package/dist/{chunk-WMOI7FN3.js.map → chunk-UY7KGQ5R.js.map} +1 -1
- package/dist/{chunk-3E5F2XMR.js → chunk-VWUFZ6ZG.js} +29 -2
- package/dist/chunk-VWUFZ6ZG.js.map +1 -0
- package/dist/chunk-ZLQKNOZQ.js +1 -0
- package/dist/chunk-ZLQKNOZQ.js.map +1 -0
- package/dist/doc/index.d.ts +5 -5
- package/dist/doc/index.js +135 -8
- package/dist/generate/index.d.ts +3 -74
- package/dist/generate/index.js +2 -7
- package/dist/icons/index.d.ts +3 -26
- package/dist/icons/index.js +13 -0
- package/dist/icons/inlineIconMarker.d.ts +1 -0
- package/dist/icons/inlineIconMarker.js +15 -0
- package/dist/icons/inlineIconMarker.js.map +1 -0
- package/dist/imageEdit/index.d.ts +12 -6
- package/dist/imageEdit/index.js +4 -1
- package/dist/index.d.ts +14 -11
- package/dist/index.js +265 -37
- package/dist/inlineIconMarker-CiE8Hlvz.d.ts +59 -0
- package/dist/jsonForm/index.d.ts +42 -1
- package/dist/jsonForm/index.js +9 -1
- package/dist/markdown/index.d.ts +228 -3
- package/dist/markdown/index.js +42 -7
- package/dist/recommend/index.d.ts +1 -1
- package/dist/recommend/index.js +3 -2
- package/dist/schemas/index.d.ts +28 -8
- package/dist/schemas/index.js +36 -8
- package/dist/spatial/index.d.ts +9 -4
- package/dist/spatial/index.js +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +1 -1
- package/dist/story/index.d.ts +1021 -41
- package/dist/story/index.js +135 -8
- package/dist/{themeLibrary-BHjJ0Qx6.d.ts → themeLibrary-CehcJhzz.d.ts} +1 -1
- package/dist/timing/index.js +3 -5
- package/dist/transform/index.d.ts +48 -10
- package/dist/transform/index.js +17 -481
- package/dist/transform/index.js.map +1 -1
- package/dist/{types-BdZkdJ3z.d.ts → types-DlAZ7MW4.d.ts} +115 -1
- package/dist/versions/index.d.ts +1 -1
- package/package.json +6 -1
- package/src/__tests__/animationUtils.test.ts +40 -0
- package/src/__tests__/annotationCoercion.test.ts +243 -0
- package/src/__tests__/applyRenderStyle.test.ts +146 -0
- package/src/__tests__/atmosphereLayers.test.ts +108 -0
- package/src/__tests__/attrTokens.test.ts +162 -0
- package/src/__tests__/contentExtractor.test.ts +60 -0
- package/src/__tests__/coverBlock.test.ts +46 -1
- package/src/__tests__/customTemplateSample.test.ts +144 -0
- package/src/__tests__/customTemplatesFrontmatter.test.ts +236 -0
- package/src/__tests__/customThemesFrontmatter.test.ts +145 -0
- package/src/__tests__/diagramBlock.test.ts +122 -0
- package/src/__tests__/diagramLayout.test.ts +134 -0
- package/src/__tests__/drawing.test.ts +109 -0
- package/src/__tests__/drawingBlock.test.ts +124 -0
- package/src/__tests__/drawingLayout.test.ts +171 -0
- package/src/__tests__/geohash.test.ts +6 -5
- package/src/__tests__/getLayers.test.ts +6 -2
- package/src/__tests__/getLayersFallback.test.ts +103 -0
- package/src/__tests__/imageEditExportSvg.test.ts +70 -0
- package/src/__tests__/imageEditVersions.test.ts +72 -0
- package/src/__tests__/inlineIconMarker.test.ts +36 -0
- package/src/__tests__/inputDescriptors.test.ts +162 -0
- package/src/__tests__/jsonForm.tokens.test.ts +84 -0
- package/src/__tests__/layoutBlock.test.ts +83 -0
- package/src/__tests__/layoutLayout.test.ts +169 -0
- package/src/__tests__/markdown.test.ts +87 -0
- package/src/__tests__/markdownSanitize.test.ts +79 -0
- package/src/__tests__/markdownToDoc.test.ts +391 -2
- package/src/__tests__/mediaAnnotations.test.ts +91 -0
- package/src/__tests__/mediaSchedule.test.ts +118 -0
- package/src/__tests__/pandocAttrParse.test.ts +187 -0
- package/src/__tests__/resolveDocTheme.test.ts +55 -0
- package/src/__tests__/rootExports.test.ts +43 -0
- package/src/__tests__/shapeGeometry.test.ts +89 -0
- package/src/__tests__/shapeVocabulary.test.ts +133 -0
- package/src/__tests__/structuredData.test.ts +249 -0
- package/src/__tests__/templateAnnotationParse.test.ts +108 -0
- package/src/__tests__/templateMetadata.test.ts +31 -0
- package/src/__tests__/templates.test.ts +257 -4
- package/src/__tests__/themeCompile.test.ts +42 -0
- package/src/__tests__/themeValidator.test.ts +6 -0
- package/src/__tests__/transformV2.test.ts +137 -0
- package/src/__tests__/transitionNormalize.test.ts +61 -0
- package/src/__tests__/validateDoc.test.ts +260 -0
- package/src/doc/annotationBlocks.ts +117 -0
- package/src/doc/customTemplatesFrontmatter.ts +314 -0
- package/src/doc/customThemesFrontmatter.ts +95 -0
- package/src/doc/docToMarkdown.ts +104 -5
- package/src/doc/getLayers.ts +49 -17
- package/src/doc/index.ts +47 -1
- package/src/doc/markdownToDoc.ts +411 -13
- package/src/doc/mediaAnnotations.ts +100 -0
- package/src/doc/resolveDocTheme.ts +55 -0
- package/src/doc/standaloneAnnotation.ts +58 -0
- package/src/doc/structuredData.ts +301 -0
- package/src/doc/templateInputs.ts +240 -0
- package/src/doc/templates/__tests__/customTemplate.test.ts +170 -0
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +110 -0
- package/src/doc/templates/accentImage.ts +30 -6
- package/src/doc/templates/captionUtils.ts +23 -0
- package/src/doc/templates/comparisonBar.ts +33 -18
- package/src/doc/templates/coverBlock.ts +35 -5
- package/src/doc/templates/customTemplate.ts +51 -0
- package/src/doc/templates/dataTable.ts +37 -19
- package/src/doc/templates/dateEvent.ts +41 -20
- package/src/doc/templates/definitionCard.ts +26 -18
- package/src/doc/templates/diagramBlock.ts +204 -0
- package/src/doc/templates/diagramLayout.ts +159 -0
- package/src/doc/templates/drawingBlock.ts +325 -0
- package/src/doc/templates/drawingLayout.ts +539 -0
- package/src/doc/templates/factCard.ts +54 -26
- package/src/doc/templates/fallbackBlock.ts +123 -0
- package/src/doc/templates/featureBlock.ts +20 -13
- package/src/doc/templates/fullBleedQuote.ts +18 -8
- package/src/doc/templates/imageWithCaption.ts +30 -15
- package/src/doc/templates/index.ts +225 -46
- package/src/doc/templates/inputDescriptors.ts +432 -0
- package/src/doc/templates/layoutBlock.ts +43 -0
- package/src/doc/templates/layoutLayout.ts +243 -0
- package/src/doc/templates/listBlock.ts +56 -30
- package/src/doc/templates/mapBlock.ts +15 -12
- package/src/doc/templates/metadata.ts +120 -0
- package/src/doc/templates/persistentLayers.ts +129 -11
- package/src/doc/templates/photoGrid.ts +22 -34
- package/src/doc/templates/pullQuote.ts +30 -12
- package/src/doc/templates/quoteBlock.ts +54 -29
- package/src/doc/templates/sectionHeader.ts +25 -11
- package/src/doc/templates/statHighlight.ts +39 -22
- package/src/doc/templates/titleBlock.ts +59 -21
- package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +487 -0
- package/src/doc/templates/tokens/resolveTokens.ts +453 -0
- package/src/doc/templates/twoColumn.ts +48 -30
- package/src/doc/templates/videoPullQuote.ts +22 -12
- package/src/doc/templates/videoWithCaption.ts +21 -15
- package/src/doc/utils/animationUtils.ts +267 -3
- package/src/doc/utils/applyRenderStyle.ts +113 -0
- package/src/doc/utils/imageTreatment.ts +70 -0
- package/src/doc/utils/nearest.ts +59 -0
- package/src/doc/utils/shapeGeometry.ts +464 -0
- package/src/doc/utils/themeUtils.ts +75 -1
- package/src/doc/validate.ts +575 -0
- package/src/generate/contentExtractor.ts +148 -35
- package/src/generate/index.ts +0 -3
- package/src/icons/index.ts +8 -0
- package/src/icons/inlineIconMarker.ts +73 -0
- package/src/imageEdit/export.ts +85 -2
- package/src/imageEdit/index.ts +1 -0
- package/src/imageEdit/persistence.ts +51 -0
- package/src/index.ts +2 -0
- package/src/jsonForm/index.ts +2 -0
- package/src/jsonForm/tokens.ts +78 -0
- package/src/markdown/annotationCoercion.ts +295 -0
- package/src/markdown/attrTokens.ts +241 -0
- package/src/markdown/convert.ts +229 -26
- package/src/markdown/index.ts +37 -0
- package/src/markdown/sanitize.ts +318 -0
- package/src/markdown/stringify.ts +96 -6
- package/src/markdown/types.ts +66 -0
- package/src/markdown/utils.ts +111 -2
- package/src/recommend/templates.ts +24 -0
- package/src/schemas/BlockTemplates.ts +133 -4
- package/src/schemas/CustomTemplates.ts +126 -0
- package/src/schemas/Doc.ts +387 -65
- package/src/schemas/ImageEditDoc.ts +11 -4
- package/src/schemas/Media.ts +169 -0
- package/src/schemas/Theme.ts +23 -1
- package/src/schemas/Transitions.ts +224 -0
- package/src/schemas/colorUtils.ts +33 -0
- package/src/schemas/fontStacks.ts +5 -2
- package/src/schemas/index.ts +3 -0
- package/src/schemas/themeCompile.ts +24 -5
- package/src/schemas/themeValidator.ts +49 -11
- package/src/schemas/themes/cinematic.json +69 -12
- package/src/schemas/themes/documentary.json +58 -11
- package/src/schemas/themes/gezellig.json +60 -11
- package/src/schemas/themes/magazine.json +54 -9
- package/src/schemas/themes/morning-light.json +52 -9
- package/src/schemas/themes/standard-dark.json +48 -13
- package/src/schemas/themes/standard.json +44 -12
- package/src/schemas/themes/tech-dark.json +65 -12
- package/src/spatial/Geohash.ts +52 -21
- package/src/storage/ContentContainer.ts +6 -2
- package/src/transform/applyTransform.ts +43 -4
- package/src/transform/index.ts +1 -0
- package/src/transform/registry.ts +44 -13
- package/src/transform/styles/dataDriven.ts +2 -1
- package/src/transform/styles/documentary.ts +2 -1
- package/src/transform/styles/magazine.ts +3 -1
- package/src/transform/styles/minimal.ts +2 -1
- package/src/transform/styles/narrative.ts +3 -1
- package/src/transform/templateSelector.ts +87 -11
- package/src/transform/types.ts +35 -2
- package/dist/chunk-2TMAWB4Y.js.map +0 -1
- package/dist/chunk-3E5F2XMR.js.map +0 -1
- package/dist/chunk-3K5OG6KI.js.map +0 -1
- package/dist/chunk-4X3JQXNM.js +0 -35
- package/dist/chunk-4X3JQXNM.js.map +0 -1
- package/dist/chunk-57CVQCFX.js.map +0 -1
- package/dist/chunk-5WFRKURX.js.map +0 -1
- package/dist/chunk-7UDSRZKG.js.map +0 -1
- package/dist/chunk-B4EEON3N.js.map +0 -1
- package/dist/chunk-FR2RBTKO.js.map +0 -1
- package/dist/chunk-H3AXU4MJ.js +0 -179
- package/dist/chunk-H3AXU4MJ.js.map +0 -1
- package/dist/chunk-K32VJONL.js.map +0 -1
- package/dist/chunk-KSWJR66U.js +0 -3706
- package/dist/chunk-KSWJR66U.js.map +0 -1
- package/dist/chunk-MYIH7FWD.js +0 -145
- package/dist/chunk-MYIH7FWD.js.map +0 -1
- package/dist/chunk-ZTX4PFFT.js.map +0 -1
- package/src/__tests__/slideshowGenerator.test.ts +0 -128
- package/src/generate/slideshowGenerator.ts +0 -273
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shapeGeometry — pure SVG geometry for the drawing/diagram shape vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* Three concerns, all deterministic and dependency-free:
|
|
5
|
+
* - `shapePath(kind, x, y, w, h)` → an SVG path `d` for a named shape inscribed
|
|
6
|
+
* in a bounding box. Returns null for the natively-rendered primitives
|
|
7
|
+
* (rect / circle / line / text / path) so the caller can keep using a
|
|
8
|
+
* `ShapeLayer` for those; everything else becomes a computed `PathLayer`.
|
|
9
|
+
* - `markerPath(style, dir)` → the `<marker>` path for a line end-style, in the
|
|
10
|
+
* standard 0–10 marker viewBox (shared by the SSR `PathLayer` renderer and the
|
|
11
|
+
* editor's edge renderer).
|
|
12
|
+
* - `connectorPath(routing, start, end)` + `clipEndpoints(a, b)` → the line/edge
|
|
13
|
+
* geometry between two shapes (straight / orthogonal / curved), lifted from the
|
|
14
|
+
* diagram template so drawings and diagrams draw identical connectors.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { MarkerStyle } from '../../schemas/Doc.js';
|
|
18
|
+
|
|
19
|
+
// ============================================
|
|
20
|
+
// Shapes
|
|
21
|
+
// ============================================
|
|
22
|
+
|
|
23
|
+
/** Connector routing styles. */
|
|
24
|
+
export type ConnectorRouting = 'straight' | 'orthogonal' | 'curved';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Shape kinds rendered as computed paths (everything except the primitives the
|
|
28
|
+
* renderer draws natively). Used by templates to decide ShapeLayer vs PathLayer.
|
|
29
|
+
*/
|
|
30
|
+
export const PATH_SHAPE_KINDS = new Set([
|
|
31
|
+
'triangle',
|
|
32
|
+
'right-triangle',
|
|
33
|
+
'diamond',
|
|
34
|
+
'pentagon',
|
|
35
|
+
'hexagon',
|
|
36
|
+
'octagon',
|
|
37
|
+
'star',
|
|
38
|
+
'star4',
|
|
39
|
+
'star6',
|
|
40
|
+
'parallelogram',
|
|
41
|
+
'trapezoid',
|
|
42
|
+
'plus',
|
|
43
|
+
'chevron',
|
|
44
|
+
'arrow-right',
|
|
45
|
+
'arrow-left',
|
|
46
|
+
'arrow-up',
|
|
47
|
+
'arrow-down',
|
|
48
|
+
'double-arrow',
|
|
49
|
+
'callout',
|
|
50
|
+
'cylinder',
|
|
51
|
+
'cloud',
|
|
52
|
+
'heart',
|
|
53
|
+
'lightning',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
/** Round to 1 decimal for compact, stable path output. */
|
|
57
|
+
function r(n: number): number {
|
|
58
|
+
return Math.round(n * 10) / 10;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Close a list of points into an `M … L … Z` polygon path. */
|
|
62
|
+
function poly(points: ReadonlyArray<readonly [number, number]>): string {
|
|
63
|
+
if (points.length === 0) return '';
|
|
64
|
+
const [first, ...rest] = points;
|
|
65
|
+
const head = `M ${r(first[0])} ${r(first[1])}`;
|
|
66
|
+
const tail = rest.map(([x, y]) => `L ${r(x)} ${r(y)}`).join(' ');
|
|
67
|
+
return `${head} ${tail} Z`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Regular polygon points inscribed in the box ellipse, `rotation` in radians. */
|
|
71
|
+
function regular(
|
|
72
|
+
cx: number,
|
|
73
|
+
cy: number,
|
|
74
|
+
rx: number,
|
|
75
|
+
ry: number,
|
|
76
|
+
sides: number,
|
|
77
|
+
rotation: number,
|
|
78
|
+
): Array<[number, number]> {
|
|
79
|
+
const pts: Array<[number, number]> = [];
|
|
80
|
+
for (let i = 0; i < sides; i++) {
|
|
81
|
+
const a = rotation + (i * 2 * Math.PI) / sides;
|
|
82
|
+
pts.push([cx + rx * Math.cos(a), cy + ry * Math.sin(a)]);
|
|
83
|
+
}
|
|
84
|
+
return pts;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Star points alternating outer/inner radius. */
|
|
88
|
+
function star(
|
|
89
|
+
cx: number,
|
|
90
|
+
cy: number,
|
|
91
|
+
rx: number,
|
|
92
|
+
ry: number,
|
|
93
|
+
spikes: number,
|
|
94
|
+
innerRatio: number,
|
|
95
|
+
): Array<[number, number]> {
|
|
96
|
+
const pts: Array<[number, number]> = [];
|
|
97
|
+
const rot = -Math.PI / 2; // first point up
|
|
98
|
+
for (let i = 0; i < spikes * 2; i++) {
|
|
99
|
+
const outer = i % 2 === 0;
|
|
100
|
+
const a = rot + (i * Math.PI) / spikes;
|
|
101
|
+
const fx = outer ? rx : rx * innerRatio;
|
|
102
|
+
const fy = outer ? ry : ry * innerRatio;
|
|
103
|
+
pts.push([cx + fx * Math.cos(a), cy + fy * Math.sin(a)]);
|
|
104
|
+
}
|
|
105
|
+
return pts;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* SVG path `d` for a named shape inscribed in `[x, y, w, h]`, or null when the
|
|
110
|
+
* kind is rendered natively (rect / circle / line / text / path / arrow).
|
|
111
|
+
*/
|
|
112
|
+
export function shapePath(kind: string, x: number, y: number, w: number, h: number): string | null {
|
|
113
|
+
const cx = x + w / 2;
|
|
114
|
+
const cy = y + h / 2;
|
|
115
|
+
const rx = w / 2;
|
|
116
|
+
const ry = h / 2;
|
|
117
|
+
const right = x + w;
|
|
118
|
+
const bottom = y + h;
|
|
119
|
+
|
|
120
|
+
switch (kind) {
|
|
121
|
+
case 'triangle':
|
|
122
|
+
return poly([
|
|
123
|
+
[cx, y],
|
|
124
|
+
[right, bottom],
|
|
125
|
+
[x, bottom],
|
|
126
|
+
]);
|
|
127
|
+
case 'right-triangle':
|
|
128
|
+
return poly([
|
|
129
|
+
[x, y],
|
|
130
|
+
[x, bottom],
|
|
131
|
+
[right, bottom],
|
|
132
|
+
]);
|
|
133
|
+
case 'diamond':
|
|
134
|
+
return poly([
|
|
135
|
+
[cx, y],
|
|
136
|
+
[right, cy],
|
|
137
|
+
[cx, bottom],
|
|
138
|
+
[x, cy],
|
|
139
|
+
]);
|
|
140
|
+
case 'pentagon':
|
|
141
|
+
return poly(regular(cx, cy, rx, ry, 5, -Math.PI / 2));
|
|
142
|
+
case 'hexagon': {
|
|
143
|
+
// Flat top/bottom hexagon: inset the left/right vertices.
|
|
144
|
+
const inset = w * 0.25;
|
|
145
|
+
return poly([
|
|
146
|
+
[x + inset, y],
|
|
147
|
+
[right - inset, y],
|
|
148
|
+
[right, cy],
|
|
149
|
+
[right - inset, bottom],
|
|
150
|
+
[x + inset, bottom],
|
|
151
|
+
[x, cy],
|
|
152
|
+
]);
|
|
153
|
+
}
|
|
154
|
+
case 'octagon': {
|
|
155
|
+
const cut = Math.min(w, h) * 0.29;
|
|
156
|
+
return poly([
|
|
157
|
+
[x + cut, y],
|
|
158
|
+
[right - cut, y],
|
|
159
|
+
[right, y + cut],
|
|
160
|
+
[right, bottom - cut],
|
|
161
|
+
[right - cut, bottom],
|
|
162
|
+
[x + cut, bottom],
|
|
163
|
+
[x, bottom - cut],
|
|
164
|
+
[x, y + cut],
|
|
165
|
+
]);
|
|
166
|
+
}
|
|
167
|
+
case 'star':
|
|
168
|
+
return poly(star(cx, cy, rx, ry, 5, 0.4));
|
|
169
|
+
case 'star4':
|
|
170
|
+
return poly(star(cx, cy, rx, ry, 4, 0.4));
|
|
171
|
+
case 'star6':
|
|
172
|
+
return poly(star(cx, cy, rx, ry, 6, 0.5));
|
|
173
|
+
case 'parallelogram': {
|
|
174
|
+
const off = w * 0.25;
|
|
175
|
+
return poly([
|
|
176
|
+
[x + off, y],
|
|
177
|
+
[right, y],
|
|
178
|
+
[right - off, bottom],
|
|
179
|
+
[x, bottom],
|
|
180
|
+
]);
|
|
181
|
+
}
|
|
182
|
+
case 'trapezoid': {
|
|
183
|
+
const off = w * 0.25;
|
|
184
|
+
return poly([
|
|
185
|
+
[x + off, y],
|
|
186
|
+
[right - off, y],
|
|
187
|
+
[right, bottom],
|
|
188
|
+
[x, bottom],
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
191
|
+
case 'plus': {
|
|
192
|
+
const t = Math.min(w, h) / 3; // arm thickness
|
|
193
|
+
const lx = x + (w - t) / 2;
|
|
194
|
+
const rxp = lx + t;
|
|
195
|
+
const ty = y + (h - t) / 2;
|
|
196
|
+
const byp = ty + t;
|
|
197
|
+
return poly([
|
|
198
|
+
[lx, y],
|
|
199
|
+
[rxp, y],
|
|
200
|
+
[rxp, ty],
|
|
201
|
+
[right, ty],
|
|
202
|
+
[right, byp],
|
|
203
|
+
[rxp, byp],
|
|
204
|
+
[rxp, bottom],
|
|
205
|
+
[lx, bottom],
|
|
206
|
+
[lx, byp],
|
|
207
|
+
[x, byp],
|
|
208
|
+
[x, ty],
|
|
209
|
+
[lx, ty],
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
case 'chevron': {
|
|
213
|
+
const notch = w * 0.25;
|
|
214
|
+
return poly([
|
|
215
|
+
[x, y],
|
|
216
|
+
[right - notch, y],
|
|
217
|
+
[right, cy],
|
|
218
|
+
[right - notch, bottom],
|
|
219
|
+
[x, bottom],
|
|
220
|
+
[x + notch, cy],
|
|
221
|
+
]);
|
|
222
|
+
}
|
|
223
|
+
case 'arrow-right':
|
|
224
|
+
return blockArrow(x, y, w, h, 'right');
|
|
225
|
+
case 'arrow-left':
|
|
226
|
+
return blockArrow(x, y, w, h, 'left');
|
|
227
|
+
case 'arrow-up':
|
|
228
|
+
return blockArrow(x, y, w, h, 'up');
|
|
229
|
+
case 'arrow-down':
|
|
230
|
+
return blockArrow(x, y, w, h, 'down');
|
|
231
|
+
case 'double-arrow': {
|
|
232
|
+
const head = w * 0.3;
|
|
233
|
+
const bodyT = h * 0.4;
|
|
234
|
+
const ty = cy - bodyT / 2;
|
|
235
|
+
const by = cy + bodyT / 2;
|
|
236
|
+
return poly([
|
|
237
|
+
[x, cy],
|
|
238
|
+
[x + head, y],
|
|
239
|
+
[x + head, ty],
|
|
240
|
+
[right - head, ty],
|
|
241
|
+
[right - head, y],
|
|
242
|
+
[right, cy],
|
|
243
|
+
[right - head, bottom],
|
|
244
|
+
[right - head, by],
|
|
245
|
+
[x + head, by],
|
|
246
|
+
[x + head, bottom],
|
|
247
|
+
]);
|
|
248
|
+
}
|
|
249
|
+
case 'callout': {
|
|
250
|
+
// Speech bubble: rounded-ish body (square corners for path simplicity)
|
|
251
|
+
// with a tail off the lower-left.
|
|
252
|
+
const bodyH = h * 0.78;
|
|
253
|
+
const bb = y + bodyH;
|
|
254
|
+
const tailX = x + w * 0.25;
|
|
255
|
+
return `M ${r(x)} ${r(y)} L ${r(right)} ${r(y)} L ${r(right)} ${r(bb)} L ${r(tailX + w * 0.12)} ${r(bb)} L ${r(tailX)} ${r(bottom)} L ${r(tailX - w * 0.02)} ${r(bb)} L ${r(x)} ${r(bb)} Z`;
|
|
256
|
+
}
|
|
257
|
+
case 'cylinder': {
|
|
258
|
+
const ell = h * 0.16; // ellipse half-height
|
|
259
|
+
return (
|
|
260
|
+
`M ${r(x)} ${r(y + ell)} ` +
|
|
261
|
+
`A ${r(rx)} ${r(ell)} 0 0 1 ${r(right)} ${r(y + ell)} ` +
|
|
262
|
+
`L ${r(right)} ${r(bottom - ell)} ` +
|
|
263
|
+
`A ${r(rx)} ${r(ell)} 0 0 1 ${r(x)} ${r(bottom - ell)} ` +
|
|
264
|
+
`Z ` +
|
|
265
|
+
// top ellipse outline
|
|
266
|
+
`M ${r(x)} ${r(y + ell)} ` +
|
|
267
|
+
`A ${r(rx)} ${r(ell)} 0 0 0 ${r(right)} ${r(y + ell)}`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
case 'cloud': {
|
|
271
|
+
// Four bumps along the top + a flat-ish bottom, via arcs.
|
|
272
|
+
const b = h * 0.5;
|
|
273
|
+
return (
|
|
274
|
+
`M ${r(x + w * 0.2)} ${r(bottom)} ` +
|
|
275
|
+
`A ${r(w * 0.2)} ${r(b)} 0 0 1 ${r(x + w * 0.15)} ${r(cy)} ` +
|
|
276
|
+
`A ${r(w * 0.2)} ${r(b)} 0 0 1 ${r(x + w * 0.45)} ${r(y + h * 0.2)} ` +
|
|
277
|
+
`A ${r(w * 0.2)} ${r(b)} 0 0 1 ${r(x + w * 0.8)} ${r(y + h * 0.25)} ` +
|
|
278
|
+
`A ${r(w * 0.18)} ${r(b)} 0 0 1 ${r(x + w * 0.85)} ${r(bottom)} ` +
|
|
279
|
+
`Z`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
case 'heart': {
|
|
283
|
+
const topY = y + h * 0.3;
|
|
284
|
+
return (
|
|
285
|
+
`M ${r(cx)} ${r(bottom)} ` +
|
|
286
|
+
`C ${r(x - w * 0.1)} ${r(cy)} ${r(x)} ${r(y)} ${r(cx)} ${r(topY)} ` +
|
|
287
|
+
`C ${r(right)} ${r(y)} ${r(right + w * 0.1)} ${r(cy)} ${r(cx)} ${r(bottom)} ` +
|
|
288
|
+
`Z`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
case 'lightning':
|
|
292
|
+
return poly([
|
|
293
|
+
[x + w * 0.55, y],
|
|
294
|
+
[x + w * 0.2, y + h * 0.55],
|
|
295
|
+
[x + w * 0.45, y + h * 0.55],
|
|
296
|
+
[x + w * 0.3, bottom],
|
|
297
|
+
[right, y + h * 0.4],
|
|
298
|
+
[x + w * 0.55, y + h * 0.4],
|
|
299
|
+
]);
|
|
300
|
+
default:
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Block arrow (body rectangle + triangular head) pointing in `dir`. */
|
|
306
|
+
function blockArrow(
|
|
307
|
+
x: number,
|
|
308
|
+
y: number,
|
|
309
|
+
w: number,
|
|
310
|
+
h: number,
|
|
311
|
+
dir: 'right' | 'left' | 'up' | 'down',
|
|
312
|
+
): string {
|
|
313
|
+
const right = x + w;
|
|
314
|
+
const bottom = y + h;
|
|
315
|
+
const cx = x + w / 2;
|
|
316
|
+
const cy = y + h / 2;
|
|
317
|
+
if (dir === 'right' || dir === 'left') {
|
|
318
|
+
const head = w * 0.4;
|
|
319
|
+
const bodyT = h * 0.5;
|
|
320
|
+
const ty = cy - bodyT / 2;
|
|
321
|
+
const by = cy + bodyT / 2;
|
|
322
|
+
if (dir === 'right') {
|
|
323
|
+
return poly([
|
|
324
|
+
[x, ty],
|
|
325
|
+
[right - head, ty],
|
|
326
|
+
[right - head, y],
|
|
327
|
+
[right, cy],
|
|
328
|
+
[right - head, bottom],
|
|
329
|
+
[right - head, by],
|
|
330
|
+
[x, by],
|
|
331
|
+
]);
|
|
332
|
+
}
|
|
333
|
+
return poly([
|
|
334
|
+
[right, ty],
|
|
335
|
+
[x + head, ty],
|
|
336
|
+
[x + head, y],
|
|
337
|
+
[x, cy],
|
|
338
|
+
[x + head, bottom],
|
|
339
|
+
[x + head, by],
|
|
340
|
+
[right, by],
|
|
341
|
+
]);
|
|
342
|
+
}
|
|
343
|
+
const head = h * 0.4;
|
|
344
|
+
const bodyT = w * 0.5;
|
|
345
|
+
const lx = cx - bodyT / 2;
|
|
346
|
+
const rxp = cx + bodyT / 2;
|
|
347
|
+
if (dir === 'down') {
|
|
348
|
+
return poly([
|
|
349
|
+
[lx, y],
|
|
350
|
+
[rxp, y],
|
|
351
|
+
[rxp, bottom - head],
|
|
352
|
+
[right, bottom - head],
|
|
353
|
+
[cx, bottom],
|
|
354
|
+
[x, bottom - head],
|
|
355
|
+
[lx, bottom - head],
|
|
356
|
+
]);
|
|
357
|
+
}
|
|
358
|
+
return poly([
|
|
359
|
+
[lx, bottom],
|
|
360
|
+
[rxp, bottom],
|
|
361
|
+
[rxp, y + head],
|
|
362
|
+
[right, y + head],
|
|
363
|
+
[cx, y],
|
|
364
|
+
[x, y + head],
|
|
365
|
+
[lx, y + head],
|
|
366
|
+
]);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ============================================
|
|
370
|
+
// End markers
|
|
371
|
+
// ============================================
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The `<marker>` path for a line end-style, in the 0–10 marker viewBox.
|
|
375
|
+
* `dir` selects orientation (`end` points along +x; `start` is mirrored).
|
|
376
|
+
* Returns null for `'none'`. `filled` shapes paint with the stroke color;
|
|
377
|
+
* non-filled (`open`) draw as a stroked outline.
|
|
378
|
+
*/
|
|
379
|
+
export function markerPath(
|
|
380
|
+
style: MarkerStyle,
|
|
381
|
+
dir: 'start' | 'end',
|
|
382
|
+
): { d: string; filled: boolean } | null {
|
|
383
|
+
const mirror = dir === 'start';
|
|
384
|
+
const mx = (n: number) => (mirror ? 10 - n : n);
|
|
385
|
+
const tri = (closed: boolean) => `M ${mx(0)} 0 L ${mx(10)} 5 L ${mx(0)} 10${closed ? ' z' : ''}`;
|
|
386
|
+
switch (style) {
|
|
387
|
+
case 'arrow':
|
|
388
|
+
return { d: tri(true), filled: true };
|
|
389
|
+
case 'open':
|
|
390
|
+
return { d: tri(false), filled: false };
|
|
391
|
+
case 'diamond':
|
|
392
|
+
return { d: 'M 0 5 L 5 0 L 10 5 L 5 10 z', filled: true };
|
|
393
|
+
case 'square':
|
|
394
|
+
return { d: 'M 1 1 L 9 1 L 9 9 L 1 9 z', filled: true };
|
|
395
|
+
case 'circle':
|
|
396
|
+
return { d: 'M 0 5 A 5 5 0 1 0 10 5 A 5 5 0 1 0 0 5 z', filled: true };
|
|
397
|
+
case 'none':
|
|
398
|
+
default:
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// ============================================
|
|
404
|
+
// Connector routing
|
|
405
|
+
// ============================================
|
|
406
|
+
|
|
407
|
+
export interface ClipBox {
|
|
408
|
+
cx: number;
|
|
409
|
+
cy: number;
|
|
410
|
+
rx: number;
|
|
411
|
+
ry: number;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** Intersection of the center-to-center line with `from`'s bounding box. */
|
|
415
|
+
export function clipPoint(from: ClipBox, to: ClipBox): { x: number; y: number } {
|
|
416
|
+
const dx = to.cx - from.cx;
|
|
417
|
+
const dy = to.cy - from.cy;
|
|
418
|
+
if (dx === 0 && dy === 0) return { x: from.cx, y: from.cy };
|
|
419
|
+
const sx = Math.abs(dx) > 0 ? from.rx / Math.abs(dx) : Infinity;
|
|
420
|
+
const sy = Math.abs(dy) > 0 ? from.ry / Math.abs(dy) : Infinity;
|
|
421
|
+
const s = Math.min(sx, sy);
|
|
422
|
+
return { x: from.cx + dx * s, y: from.cy + dy * s };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** Clip both endpoints of an edge to the two shapes it joins. */
|
|
426
|
+
export function clipEndpoints(
|
|
427
|
+
a: ClipBox,
|
|
428
|
+
b: ClipBox,
|
|
429
|
+
): { start: { x: number; y: number }; end: { x: number; y: number } } {
|
|
430
|
+
return { start: clipPoint(a, b), end: clipPoint(b, a) };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** Build the SVG `d` for a connector between two clipped endpoints. */
|
|
434
|
+
export function connectorPath(
|
|
435
|
+
routing: ConnectorRouting,
|
|
436
|
+
start: { x: number; y: number },
|
|
437
|
+
end: { x: number; y: number },
|
|
438
|
+
): string {
|
|
439
|
+
if (routing === 'straight') {
|
|
440
|
+
return `M ${r(start.x)} ${r(start.y)} L ${r(end.x)} ${r(end.y)}`;
|
|
441
|
+
}
|
|
442
|
+
if (routing === 'orthogonal') {
|
|
443
|
+
const midX = (start.x + end.x) / 2;
|
|
444
|
+
return `M ${r(start.x)} ${r(start.y)} L ${r(midX)} ${r(start.y)} L ${r(midX)} ${r(end.y)} L ${r(end.x)} ${r(end.y)}`;
|
|
445
|
+
}
|
|
446
|
+
// curved
|
|
447
|
+
const dx = Math.abs(end.x - start.x);
|
|
448
|
+
const cp = Math.max(40, dx / 2);
|
|
449
|
+
return `M ${r(start.x)} ${r(start.y)} C ${r(start.x + cp)} ${r(start.y)}, ${r(end.x - cp)} ${r(end.y)}, ${r(end.x)} ${r(end.y)}`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** Map a `lineStyle` (solid|dashed|dotted) to an SVG `stroke-dasharray`. */
|
|
453
|
+
export function lineStyleDasharray(style: string | undefined): string | undefined {
|
|
454
|
+
switch (style?.toLowerCase()) {
|
|
455
|
+
case 'dashed':
|
|
456
|
+
case 'dash':
|
|
457
|
+
return '8 6';
|
|
458
|
+
case 'dotted':
|
|
459
|
+
case 'dot':
|
|
460
|
+
return '2 6';
|
|
461
|
+
default:
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
import type { TemplateContext } from '../../schemas/BlockTemplates.js';
|
|
10
10
|
import type { ThemeColorScheme, RenderStyle } from '../../schemas/Theme.js';
|
|
11
|
-
import type { AnimationType } from '../../schemas/Doc.js';
|
|
11
|
+
import type { Animation, AnimationType, ImageTreatment } from '../../schemas/Doc.js';
|
|
12
12
|
import { resolveFontFamily } from '../../schemas/fontStacks.js';
|
|
13
|
+
import { oklchDarken, withAlpha } from '../../schemas/colorUtils.js';
|
|
13
14
|
|
|
14
15
|
// ============================================
|
|
15
16
|
// Color Scheme Resolution
|
|
@@ -123,6 +124,30 @@ export function getDefaultAnimation(
|
|
|
123
124
|
return layerType === 'text' ? rs.defaultTextAnimation : rs.defaultImageAnimation;
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Theme-aware entrance animation for a template layer.
|
|
129
|
+
*
|
|
130
|
+
* The theme's `renderStyle.defaultTextAnimation` / `defaultImageAnimation`
|
|
131
|
+
* overrides the entrance *type*; the template keeps its authored duration,
|
|
132
|
+
* delay, and stagger. Templates opt in per layer:
|
|
133
|
+
*
|
|
134
|
+
* ```ts
|
|
135
|
+
* animation: themedEntrance(context, 'text', { type: 'fadeIn', duration: 2 })
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* `slowZoom` is excluded — it's an ambient treatment handled by the
|
|
139
|
+
* `applyRenderStyleToLayers` post-pass, not an entrance.
|
|
140
|
+
*/
|
|
141
|
+
export function themedEntrance(
|
|
142
|
+
context: TemplateContext,
|
|
143
|
+
layerType: 'text' | 'image',
|
|
144
|
+
fallback: Animation,
|
|
145
|
+
): Animation {
|
|
146
|
+
const type = getDefaultAnimation(context, layerType);
|
|
147
|
+
if (!type || type === 'slowZoom' || type === fallback.type) return fallback;
|
|
148
|
+
return { ...fallback, type };
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
// ============================================
|
|
127
152
|
// Style Helpers
|
|
128
153
|
// ============================================
|
|
@@ -142,6 +167,55 @@ export function getOverlayOpacity(context: TemplateContext): number {
|
|
|
142
167
|
return context.theme.style.overlayOpacity ?? 0.5;
|
|
143
168
|
}
|
|
144
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Resolve the effective photographic treatment for a block's imagery.
|
|
172
|
+
*
|
|
173
|
+
* Precedence: the block's `imageTreatment` override ('none' opts out, a
|
|
174
|
+
* type forces that grade) → the theme's `style.imageTreatment` → none.
|
|
175
|
+
* Duotone tints default to the theme primary. Templates pass the result
|
|
176
|
+
* straight into `ImageLayer.content.treatment`.
|
|
177
|
+
*/
|
|
178
|
+
export function themedImageTreatment(
|
|
179
|
+
context: TemplateContext,
|
|
180
|
+
override?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool',
|
|
181
|
+
): ImageTreatment | undefined {
|
|
182
|
+
if (override === 'none') return undefined;
|
|
183
|
+
const themeTreatment = context.theme.style.imageTreatment;
|
|
184
|
+
const base: ImageTreatment | undefined = override
|
|
185
|
+
? { type: override, strength: themeTreatment?.strength }
|
|
186
|
+
: themeTreatment;
|
|
187
|
+
if (!base || base.type === 'none') return undefined;
|
|
188
|
+
if (base.type === 'duotone' && !base.color) {
|
|
189
|
+
return { ...base, color: context.theme.colors.primary };
|
|
190
|
+
}
|
|
191
|
+
return base;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Theme-derived surface gradient for text-first templates.
|
|
196
|
+
*
|
|
197
|
+
* Runs from the theme's alternate surface into a slightly deepened
|
|
198
|
+
* background, staying inside the theme's own hue. Templates must use
|
|
199
|
+
* this instead of hard-coding a dark endpoint (`#0f1520`, `#1e2030`, …):
|
|
200
|
+
* hard-coded navy endpoints made light themes render dark panels and
|
|
201
|
+
* warm themes drift cold halfway down the block.
|
|
202
|
+
*/
|
|
203
|
+
export function themedSurfaceGradient(context: TemplateContext, angleDeg = 160): string {
|
|
204
|
+
const { background, backgroundLight } = context.theme.colors;
|
|
205
|
+
return `linear-gradient(${angleDeg}deg, ${backgroundLight} 0%, ${oklchDarken(background, 0.04)} 100%)`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Translucent overlay tint derived from the theme background, for text
|
|
210
|
+
* bands/scrims painted over imagery. Light themes get a light scrim with
|
|
211
|
+
* dark theme text on top; dark themes get the familiar dark scrim —
|
|
212
|
+
* `theme.colors.text` stays legible on it by construction.
|
|
213
|
+
*/
|
|
214
|
+
export function themedScrim(context: TemplateContext, alpha?: number): string {
|
|
215
|
+
const a = alpha ?? Math.max(0.55, getOverlayOpacity(context));
|
|
216
|
+
return withAlpha(context.theme.colors.background, a);
|
|
217
|
+
}
|
|
218
|
+
|
|
145
219
|
// ============================================
|
|
146
220
|
// Template Hint Access
|
|
147
221
|
// ============================================
|