@bendyline/squisq 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Doc-BeSeH2iX.d.ts → Doc-BsMkmzH8.d.ts} +838 -268
- package/dist/{ImageEditDoc-BTIvw0oq.d.ts → ImageEditDoc-DvoeHMP9.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-B4EEON3N.js → chunk-4AKUQPEE.js} +536 -103
- package/dist/chunk-4AKUQPEE.js.map +1 -0
- package/dist/chunk-4ZFRZBU2.js +6582 -0
- package/dist/chunk-4ZFRZBU2.js.map +1 -0
- package/dist/{chunk-57CVQCFX.js → chunk-7MQGNR76.js} +79 -3
- package/dist/chunk-7MQGNR76.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-MYIH7FWD.js → chunk-FBKP5CAO.js} +1 -1
- package/dist/chunk-FBKP5CAO.js.map +1 -0
- package/dist/chunk-KZNXZ5SM.js +333 -0
- package/dist/chunk-KZNXZ5SM.js.map +1 -0
- package/dist/{chunk-WMOI7FN3.js → chunk-LRCV7N7F.js} +12 -2
- package/dist/{chunk-WMOI7FN3.js.map → chunk-LRCV7N7F.js.map} +1 -1
- package/dist/{chunk-3K5OG6KI.js → chunk-OH43RPTP.js} +403 -187
- package/dist/chunk-OH43RPTP.js.map +1 -0
- package/dist/{chunk-H3AXU4MJ.js → chunk-OKJOQXK4.js} +149 -2
- package/dist/chunk-OKJOQXK4.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-TVSGLUID.js +596 -0
- package/dist/chunk-TVSGLUID.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 +4 -4
- package/dist/doc/index.js +124 -6
- package/dist/generate/index.d.ts +9 -2
- package/dist/generate/index.js +1 -1
- 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 +11 -5
- package/dist/imageEdit/index.js +4 -1
- package/dist/index.d.ts +11 -8
- package/dist/index.js +243 -24
- package/dist/inlineIconMarker-CiE8Hlvz.d.ts +59 -0
- package/dist/markdown/index.d.ts +212 -3
- package/dist/markdown/index.js +40 -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 +35 -5
- package/dist/spatial/index.d.ts +9 -4
- package/dist/spatial/index.js +1 -1
- package/dist/story/index.d.ts +906 -40
- package/dist/story/index.js +124 -6
- package/dist/{themeLibrary-BHjJ0Qx6.d.ts → themeLibrary-DQQKuRMx.d.ts} +1 -1
- 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/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__/customTemplateSample.test.ts +83 -0
- package/src/__tests__/customTemplatesFrontmatter.test.ts +205 -0
- package/src/__tests__/customThemesFrontmatter.test.ts +118 -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__/layoutBlock.test.ts +83 -0
- package/src/__tests__/layoutLayout.test.ts +169 -0
- package/src/__tests__/markdown.test.ts +23 -0
- package/src/__tests__/markdownSanitize.test.ts +64 -0
- package/src/__tests__/markdownToDoc.test.ts +223 -2
- package/src/__tests__/mediaAnnotations.test.ts +64 -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 +179 -0
- package/src/__tests__/templateAnnotationParse.test.ts +108 -0
- package/src/__tests__/templateMetadata.test.ts +21 -0
- package/src/__tests__/templates.test.ts +145 -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 +100 -0
- package/src/doc/customTemplatesFrontmatter.ts +308 -0
- package/src/doc/customThemesFrontmatter.ts +90 -0
- package/src/doc/docToMarkdown.ts +73 -4
- package/src/doc/getLayers.ts +40 -17
- package/src/doc/index.ts +35 -1
- package/src/doc/markdownToDoc.ts +333 -12
- package/src/doc/mediaAnnotations.ts +118 -0
- package/src/doc/resolveDocTheme.ts +55 -0
- package/src/doc/structuredData.ts +230 -0
- package/src/doc/templateInputs.ts +240 -0
- package/src/doc/templates/__tests__/customTemplate.test.ts +151 -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 +26 -18
- package/src/doc/templates/coverBlock.ts +7 -4
- package/src/doc/templates/customTemplate.ts +45 -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 +51 -25
- 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 +22 -9
- package/src/doc/templates/index.ts +215 -46
- 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 +23 -12
- 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 +221 -0
- package/src/doc/templates/tokens/resolveTokens.ts +0 -0
- package/src/doc/templates/twoColumn.ts +48 -30
- package/src/doc/templates/videoPullQuote.ts +22 -12
- package/src/doc/templates/videoWithCaption.ts +13 -8
- 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/shapeGeometry.ts +464 -0
- package/src/doc/utils/themeUtils.ts +75 -1
- package/src/doc/validate.ts +476 -0
- package/src/generate/slideshowGenerator.ts +7 -0
- 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/markdown/annotationCoercion.ts +295 -0
- package/src/markdown/attrTokens.ts +222 -0
- package/src/markdown/convert.ts +229 -26
- package/src/markdown/index.ts +36 -0
- package/src/markdown/sanitize.ts +298 -0
- package/src/markdown/stringify.ts +84 -3
- package/src/markdown/types.ts +66 -0
- package/src/markdown/utils.ts +49 -1
- package/src/recommend/templates.ts +24 -0
- package/src/schemas/BlockTemplates.ts +133 -4
- package/src/schemas/CustomTemplates.ts +74 -0
- package/src/schemas/Doc.ts +353 -29
- 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 +23 -4
- 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/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-3K5OG6KI.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-H3AXU4MJ.js.map +0 -1
- package/dist/chunk-KSWJR66U.js +0 -3706
- package/dist/chunk-KSWJR66U.js.map +0 -1
- package/dist/chunk-MYIH7FWD.js.map +0 -1
- package/dist/chunk-ZTX4PFFT.js.map +0 -1
|
@@ -16,6 +16,60 @@ import { toMdast } from './convert.js';
|
|
|
16
16
|
// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.
|
|
17
17
|
let defaultProcessor: any;
|
|
18
18
|
|
|
19
|
+
// ============================================
|
|
20
|
+
// Annotation-span unescaping
|
|
21
|
+
// ============================================
|
|
22
|
+
|
|
23
|
+
// Quoted runs as they appear in remark-stringify output. Backslashes inside
|
|
24
|
+
// the span are doubled by remark (a literal `\` before punctuation becomes
|
|
25
|
+
// `\\`), so `\\.` covers both authored escapes and remark's own.
|
|
26
|
+
const DQ_RUN = `"(?:[^"\\\\]|\\\\.)*"`;
|
|
27
|
+
const SQ_RUN = `'(?:[^'\\\\]|\\\\.)*'`;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A `{[…]}` template-annotation (or inline-icon) span as it appears in
|
|
31
|
+
* remark output: `{\[inner]}`, where `inner` may contain backslash escapes
|
|
32
|
+
* and quoted runs (which may themselves contain `]`).
|
|
33
|
+
*/
|
|
34
|
+
const ESCAPED_TEMPLATE_SPAN_RE = new RegExp(
|
|
35
|
+
`\\{\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^\\]\\\\])+)\\]\\}`,
|
|
36
|
+
'g',
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** A heading line (annotations are only emitted trailing on headings). */
|
|
40
|
+
const HEADING_LINE_RE = /^#{1,6} .*$/gm;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The trailing Pandoc `{…}` span on a heading line, possibly followed by an
|
|
44
|
+
* already-unescaped `{[…]}` template span (canonical emit order).
|
|
45
|
+
*/
|
|
46
|
+
const TRAILING_ESCAPED_PANDOC_SPAN_RE = new RegExp(
|
|
47
|
+
`\\{(?!\\\\?\\[)((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^}\\\\])*)\\}(?=\\s*(?:\\{\\[.*)?$)`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Backslash+punctuation pairs that are safe to unescape inside an
|
|
52
|
+
* annotation span: the bare character re-parses as literal text in
|
|
53
|
+
* trailing-heading position, so removing the backslash is purely
|
|
54
|
+
* cosmetic and round-trips.
|
|
55
|
+
*
|
|
56
|
+
* Deliberately EXCLUDED (left escaped in the emitted markdown): emphasis
|
|
57
|
+
* and other inline-construct openers (`*`, `_`, `` ` ``, `~`, `<`, `>`,
|
|
58
|
+
* `&`, `!`, `|`, `^`). Unescaping those could make remark-parse split the
|
|
59
|
+
* heading's text run into inline nodes on reparse, which would hide the
|
|
60
|
+
* annotation from the extractor. remark-parse resolves the surviving
|
|
61
|
+
* `\X` escapes back to bare characters, so values still round-trip —
|
|
62
|
+
* the escapes are a cosmetic cost only.
|
|
63
|
+
*
|
|
64
|
+
* Pairs are consumed left to right, so an authored `\"` that remark
|
|
65
|
+
* doubled to `\\"` correctly comes back as `\"`.
|
|
66
|
+
*/
|
|
67
|
+
const UNESCAPE_PUNCT_RE = /\\([\\[\]:#.,+=/?;@%$(){}'"-])/g;
|
|
68
|
+
|
|
69
|
+
function unescapeMarkdownPunct(text: string): string {
|
|
70
|
+
return text.replace(UNESCAPE_PUNCT_RE, '$1');
|
|
71
|
+
}
|
|
72
|
+
|
|
19
73
|
/**
|
|
20
74
|
* Serialize a MarkdownDocument back to a markdown string.
|
|
21
75
|
*
|
|
@@ -106,9 +160,36 @@ export function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOpti
|
|
|
106
160
|
// Stringify mdast → markdown string
|
|
107
161
|
const result = processor.stringify(mdastTree) as string;
|
|
108
162
|
|
|
109
|
-
// remark-stringify escapes
|
|
110
|
-
//
|
|
111
|
-
|
|
163
|
+
// remark-stringify backslash-escapes markdown-significant characters in
|
|
164
|
+
// text nodes (`[` to prevent link syntax, `:` for directives, `*`/`_` for
|
|
165
|
+
// emphasis, a literal `\` before punctuation, …). Our annotations are
|
|
166
|
+
// injected as plain text, so their interiors come back escaped. Undo that
|
|
167
|
+
// inside `{[…]}` template-annotation spans: removing one backslash from
|
|
168
|
+
// each backslash+punctuation pair (left to right) exactly inverts
|
|
169
|
+
// remark's escaping — including restoring an authored `\"` that remark
|
|
170
|
+
// doubled to `\\"`. The span regex understands quoted values, so a
|
|
171
|
+
// quoted `]` doesn't terminate the span.
|
|
172
|
+
let cleaned = result.replace(
|
|
173
|
+
ESCAPED_TEMPLATE_SPAN_RE,
|
|
174
|
+
(_m, inner: string) => `{[${unescapeMarkdownPunct(inner)}]}`,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Same treatment for the trailing Pandoc `{…}` attribute block on heading
|
|
178
|
+
// lines (canonical emit order is Pandoc block then template annotation,
|
|
179
|
+
// so the span sits at end-of-line or just before a `{[…]}` span).
|
|
180
|
+
cleaned = cleaned.replace(HEADING_LINE_RE, (line) =>
|
|
181
|
+
line.replace(
|
|
182
|
+
TRAILING_ESCAPED_PANDOC_SPAN_RE,
|
|
183
|
+
(_m, inner: string) => `{${unescapeMarkdownPunct(inner)}}`,
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// remark-directive escapes `:` in text (since `:name` would round-trip as
|
|
188
|
+
// a text directive). Inside any other Pandoc-style `{…}` brace span, `:`
|
|
189
|
+
// is purely a value separator (e.g. `connectsTo=foo:flow`), so unescape
|
|
190
|
+
// it to keep round-trips lossless. The negative lookahead skips `{[…]}`
|
|
191
|
+
// template annotations — those are handled above.
|
|
192
|
+
cleaned = cleaned.replace(/\{(?!\\?\[)[^}]*\}/g, (match) => match.replace(/\\:/g, ':'));
|
|
112
193
|
|
|
113
194
|
// Prepend YAML frontmatter if present
|
|
114
195
|
if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {
|
package/src/markdown/types.ts
CHANGED
|
@@ -126,6 +126,65 @@ export interface HeadingTemplateAnnotation {
|
|
|
126
126
|
params?: Record<string, string>;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* A directed connection from one block to another, parsed from a heading
|
|
131
|
+
* attribute `connectsTo=target1,target2:type,target3`. Mirrors the schema
|
|
132
|
+
* `BlockConnection` type — duplicated here so this module stays
|
|
133
|
+
* dependency-free from the doc schema.
|
|
134
|
+
*/
|
|
135
|
+
export interface BlockConnection {
|
|
136
|
+
/** Target block id. */
|
|
137
|
+
target: string;
|
|
138
|
+
/** Optional connection type/label (e.g., "flow", "requires"). */
|
|
139
|
+
type?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Pandoc-style heading attribute block, parsed from `{#id .class key=value}`
|
|
144
|
+
* appearing at the end of a heading line (alongside, or instead of, the
|
|
145
|
+
* squisq-native `{[templateName …]}` block).
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```markdown
|
|
149
|
+
* ## Architecture {#arch x=600 y=300 connectsTo=foo:flow,bar:requires}
|
|
150
|
+
* ```
|
|
151
|
+
* →
|
|
152
|
+
* ```ts
|
|
153
|
+
* {
|
|
154
|
+
* id: 'arch',
|
|
155
|
+
* params: { x: '600', y: '300', connectsTo: 'foo:flow,bar:requires' },
|
|
156
|
+
* blockMeta: {
|
|
157
|
+
* x: 600,
|
|
158
|
+
* y: 300,
|
|
159
|
+
* connectsTo: [{ target: 'foo', type: 'flow' }, { target: 'bar', type: 'requires' }],
|
|
160
|
+
* },
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* `params` carries the raw pre-coercion strings (lossless for round-tripping),
|
|
165
|
+
* while `blockMeta` carries the typed values for known keys. Unknown keys flow
|
|
166
|
+
* to `metadata`.
|
|
167
|
+
*/
|
|
168
|
+
export interface HeadingAttributes {
|
|
169
|
+
/** Block id from `#identifier` (overrides slug-from-heading). */
|
|
170
|
+
id?: string;
|
|
171
|
+
/** Class tokens from `.classname` (reserved for downstream use). */
|
|
172
|
+
classes?: string[];
|
|
173
|
+
/** All key=value attributes as raw strings (lossless source for serialization). */
|
|
174
|
+
params?: Record<string, string>;
|
|
175
|
+
/** Typed values for known block-meta keys (coerced from `params`). */
|
|
176
|
+
blockMeta?: {
|
|
177
|
+
x?: number;
|
|
178
|
+
y?: number;
|
|
179
|
+
startTime?: number;
|
|
180
|
+
duration?: number;
|
|
181
|
+
connectsTo?: BlockConnection[];
|
|
182
|
+
transition?: import('../schemas/Transitions.js').Transition;
|
|
183
|
+
};
|
|
184
|
+
/** Subset of `params` whose keys are NOT in the known-key registry. */
|
|
185
|
+
metadata?: Record<string, string>;
|
|
186
|
+
}
|
|
187
|
+
|
|
129
188
|
/**
|
|
130
189
|
* ATX or setext heading (# Heading).
|
|
131
190
|
*/
|
|
@@ -142,6 +201,13 @@ export interface MarkdownHeading extends MarkdownNodeBase {
|
|
|
142
201
|
* stringification, so it round-trips transparently.
|
|
143
202
|
*/
|
|
144
203
|
templateAnnotation?: HeadingTemplateAnnotation;
|
|
204
|
+
/**
|
|
205
|
+
* Pandoc-style heading attributes parsed from trailing `{#id .class key=value}`
|
|
206
|
+
* syntax. Independent from `templateAnnotation` — both may appear on the
|
|
207
|
+
* same heading in either order. Stripped from `children` during parsing
|
|
208
|
+
* and re-injected during stringification.
|
|
209
|
+
*/
|
|
210
|
+
attributes?: HeadingAttributes;
|
|
145
211
|
}
|
|
146
212
|
|
|
147
213
|
/**
|
package/src/markdown/utils.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* All functions are pure and operate on the JSON node interfaces.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { MarkdownNode, MarkdownDocument } from './types.js';
|
|
8
|
+
import type { MarkdownNode, MarkdownDocument, HtmlNode } from './types.js';
|
|
9
|
+
import { parseHtmlToNodes } from './htmlParse.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Get the children of any markdown node, if it has children.
|
|
@@ -162,6 +163,12 @@ const FRONTMATTER_BLOCK_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n)?/;
|
|
|
162
163
|
/** Quote a frontmatter scalar so it round-trips cleanly through `parseFrontmatter`. */
|
|
163
164
|
function formatFrontmatterValue(value: string | number | boolean): string {
|
|
164
165
|
if (typeof value === 'boolean' || typeof value === 'number') return String(value);
|
|
166
|
+
// A single-line JSON object/array literal round-trips verbatim through
|
|
167
|
+
// the line-based `parseFrontmatter` (no leading quote to strip, no
|
|
168
|
+
// newline to split on), so write it unquoted to keep it human-readable
|
|
169
|
+
// — matching how `stringifyMarkdown` emits string values. Used by the
|
|
170
|
+
// compact custom-layouts payload.
|
|
171
|
+
if (/^[{[]/.test(value) && !/[\r\n]/.test(value)) return value;
|
|
165
172
|
// Quote when needed: leading/trailing whitespace, leading punctuation that
|
|
166
173
|
// could trigger YAML modes, or values that look like reserved literals.
|
|
167
174
|
const needsQuote =
|
|
@@ -326,3 +333,44 @@ export function readFrontmatterThemeId(
|
|
|
326
333
|
}
|
|
327
334
|
return undefined;
|
|
328
335
|
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Project an inline-HTML string (as stored on `TextLayer.content.html`) down
|
|
339
|
+
* to plain text — the value mirrored into `content.text` for plain consumers
|
|
340
|
+
* (PDF/markdown export, search, accessibility) and the SVG `<text>` fallback.
|
|
341
|
+
*
|
|
342
|
+
* `<br>` and block-level tags (`p`, `div`, `h1`–`h6`, `li`, `blockquote`,
|
|
343
|
+
* `pre`) become line breaks so multi-line rich content reads sensibly. Entity
|
|
344
|
+
* decoding is handled by {@link parseHtmlToNodes} (parse5-backed), so this is
|
|
345
|
+
* more correct than a regex tag-stripper.
|
|
346
|
+
*/
|
|
347
|
+
export function plainTextFromInlineHtml(html: string): string {
|
|
348
|
+
const BLOCK = new Set([
|
|
349
|
+
'p',
|
|
350
|
+
'div',
|
|
351
|
+
'h1',
|
|
352
|
+
'h2',
|
|
353
|
+
'h3',
|
|
354
|
+
'h4',
|
|
355
|
+
'h5',
|
|
356
|
+
'h6',
|
|
357
|
+
'li',
|
|
358
|
+
'blockquote',
|
|
359
|
+
'pre',
|
|
360
|
+
]);
|
|
361
|
+
const walk = (nodes: HtmlNode[]): string =>
|
|
362
|
+
nodes
|
|
363
|
+
.map((n) => {
|
|
364
|
+
if (n.type === 'htmlText') return n.value;
|
|
365
|
+
if (n.type !== 'htmlElement') return '';
|
|
366
|
+
const tag = n.tagName.toLowerCase();
|
|
367
|
+
if (tag === 'br') return '\n';
|
|
368
|
+
const inner = walk(n.children);
|
|
369
|
+
return BLOCK.has(tag) ? `${inner}\n` : inner;
|
|
370
|
+
})
|
|
371
|
+
.join('');
|
|
372
|
+
return walk(parseHtmlToNodes(html))
|
|
373
|
+
.replace(/\u00A0/g, ' ')
|
|
374
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
375
|
+
.trim();
|
|
376
|
+
}
|
|
@@ -251,6 +251,30 @@ function recommendedNamesForProfile(profile: BlockContentProfile): string[] {
|
|
|
251
251
|
return Array.from(names);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Deterministic single-template choice for an unannotated heading block,
|
|
256
|
+
* from strong content signals only. Returns `undefined` when no signal is
|
|
257
|
+
* decisive — the caller keeps its structural default (`sectionHeader`).
|
|
258
|
+
*
|
|
259
|
+
* Precedence favors the most specific/structured content: a table beats
|
|
260
|
+
* an image beats a blockquote beats a stat-looking line. `blockIndex`
|
|
261
|
+
* alternates left/right feature composition for single-image blocks so a
|
|
262
|
+
* run of image sections doesn't stack the same layout.
|
|
263
|
+
*
|
|
264
|
+
* Used by `markdownToDoc`'s auto template picking (see its `autoTemplates`
|
|
265
|
+
* option); the multi-candidate `recommendTemplatesForBlock` below serves
|
|
266
|
+
* the editor's template-picker UI instead.
|
|
267
|
+
*/
|
|
268
|
+
export function pickAutoTemplate(profile: BlockContentProfile, blockIndex = 0): string | undefined {
|
|
269
|
+
if (profile.hasTable) return 'dataTable';
|
|
270
|
+
if (profile.imageCount >= 2) return 'photoGrid';
|
|
271
|
+
if (profile.hasImage) return blockIndex % 2 === 0 ? 'leftFeature' : 'rightFeature';
|
|
272
|
+
if (profile.hasBlockquote) return 'quote';
|
|
273
|
+
if (profile.hasNumberHighlight) return 'statHighlight';
|
|
274
|
+
if (profile.hasList) return 'list';
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
254
278
|
/**
|
|
255
279
|
* Split a flat list of template names into a "Recommended" bucket
|
|
256
280
|
* (matches the active block's content profile) and a "rest" bucket
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* - imageWithCaption: Image with text overlay
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import type { Layer, Block, Transition, MapTileStyle, MapMarker } from './Doc.js';
|
|
23
|
+
import type { Layer, Block, Transition, MapTileStyle, MapMarker, MarkerStyle } from './Doc.js';
|
|
24
24
|
import type { ViewportConfig, ViewportOrientation } from './Viewport.js';
|
|
25
25
|
import type { LayoutHints } from './LayoutStrategy.js';
|
|
26
26
|
import type { Theme } from './Theme.js';
|
|
@@ -110,6 +110,12 @@ interface BaseTemplateBlock {
|
|
|
110
110
|
* Used with sourceStartTime to precisely position the block.
|
|
111
111
|
*/
|
|
112
112
|
sourceDuration?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Per-block override for the theme's photographic image grade
|
|
115
|
+
* (`theme.style.imageTreatment`). `'none'` opts this block's imagery out;
|
|
116
|
+
* a treatment type forces that grade regardless of the theme.
|
|
117
|
+
*/
|
|
118
|
+
imageTreatment?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool';
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/**
|
|
@@ -470,6 +476,78 @@ export interface VideoPullQuoteInput extends BaseTemplateBlock {
|
|
|
470
476
|
};
|
|
471
477
|
}
|
|
472
478
|
|
|
479
|
+
/**
|
|
480
|
+
* Diagram block - renders child headings as a node-and-edge diagram.
|
|
481
|
+
*
|
|
482
|
+
* Unlike other templates, the diagram template reads its content from
|
|
483
|
+
* the parent block's `children` (passed via `context.children`) — each
|
|
484
|
+
* child heading becomes a node, positioned by its `x`/`y` and connected
|
|
485
|
+
* by its `connectsTo`. Per-diagram options on this input control overall
|
|
486
|
+
* appearance; per-node data lives on the child blocks themselves.
|
|
487
|
+
*/
|
|
488
|
+
export interface DiagramBlockInput extends BaseTemplateBlock {
|
|
489
|
+
template: 'diagram';
|
|
490
|
+
/** Optional diagram title displayed above the canvas. */
|
|
491
|
+
title?: string;
|
|
492
|
+
/** Color scheme for nodes and edges. */
|
|
493
|
+
colorScheme?: ColorScheme;
|
|
494
|
+
/** Node shape style (default: 'rounded'). */
|
|
495
|
+
nodeShape?: 'rounded' | 'rect' | 'pill';
|
|
496
|
+
/** Edge routing (default: 'curved'). */
|
|
497
|
+
edgeStyle?: 'curved' | 'straight' | 'orthogonal';
|
|
498
|
+
/** Marker at each edge's start (default: 'none'). */
|
|
499
|
+
startStyle?: MarkerStyle;
|
|
500
|
+
/** Marker at each edge's end (default: 'arrow'). */
|
|
501
|
+
endStyle?: MarkerStyle;
|
|
502
|
+
/** Edge line style (default: 'solid'). */
|
|
503
|
+
lineStyle?: 'solid' | 'dashed' | 'dotted';
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Free-form 2D canvas block (`layout`). Like `drawing`/`diagram`, the
|
|
508
|
+
* `layout` template is a children-driven container: each child heading is
|
|
509
|
+
* one absolutely-positioned layer (`{[text …]}` / `{[rectangle …]}` /
|
|
510
|
+
* `{[image …]}`), read from `context.children`. A text box's content is the
|
|
511
|
+
* child's body markdown. Backed by the `layoutBlock` template.
|
|
512
|
+
*
|
|
513
|
+
* (Legacy documents stored layers as a base64-JSON `layers=` param on the
|
|
514
|
+
* heading; the editor auto-migrates those to child sub-blocks on first edit.)
|
|
515
|
+
*/
|
|
516
|
+
export interface RawLayersInput extends BaseTemplateBlock {
|
|
517
|
+
template: 'layout';
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Drawing block — renders child headings as free-form shapes on a canvas.
|
|
522
|
+
*
|
|
523
|
+
* Like `diagram`, the drawing template reads its content from the parent
|
|
524
|
+
* block's `children` (passed via `context.children`). Each child heading
|
|
525
|
+
* is a shape: its `{[shape …]}` annotation names the primitive
|
|
526
|
+
* (`rectangle`/`rect`, `circle`, `line`, `arrow`, `path`, `text`) and
|
|
527
|
+
* carries geometry/style as params; its `{#id}` makes it referenceable;
|
|
528
|
+
* its heading text is the shape label and its body text an optional
|
|
529
|
+
* sublabel. Connectors (`line`/`arrow` with `from`/`to`) join shapes by id.
|
|
530
|
+
*
|
|
531
|
+
* Coordinates are author-defined units; the template fits the bounding box
|
|
532
|
+
* of all shapes to the viewport (same scaling as `diagram`), so authors
|
|
533
|
+
* don't have to think in absolute pixels.
|
|
534
|
+
*
|
|
535
|
+
* Legacy drawings authored visually persist their `Layer[]` as a base64
|
|
536
|
+
* `layers="…"` Pandoc param instead of children; the template decodes and
|
|
537
|
+
* returns those directly when no children are present (see `drawingBlock`).
|
|
538
|
+
*/
|
|
539
|
+
export interface DrawingBlockInput extends BaseTemplateBlock {
|
|
540
|
+
template: 'drawing';
|
|
541
|
+
/** Optional title displayed above the canvas. */
|
|
542
|
+
title?: string;
|
|
543
|
+
/** Color scheme for shapes that don't pin their own fill/stroke. */
|
|
544
|
+
colorScheme?: ColorScheme;
|
|
545
|
+
/** Default fill for shapes that don't specify one (default: 'none'). */
|
|
546
|
+
fill?: string;
|
|
547
|
+
/** Default stroke for shapes that don't specify one (default: theme text). */
|
|
548
|
+
stroke?: string;
|
|
549
|
+
}
|
|
550
|
+
|
|
473
551
|
/**
|
|
474
552
|
* Data table - renders a themed table with headers and rows.
|
|
475
553
|
* Ideal for structured data, comparisons, and reference information.
|
|
@@ -511,7 +589,10 @@ export type TemplateBlock =
|
|
|
511
589
|
| PullQuoteInput
|
|
512
590
|
| VideoWithCaptionInput
|
|
513
591
|
| VideoPullQuoteInput
|
|
514
|
-
| DataTableInput
|
|
592
|
+
| DataTableInput
|
|
593
|
+
| DiagramBlockInput
|
|
594
|
+
| DrawingBlockInput
|
|
595
|
+
| RawLayersInput;
|
|
515
596
|
|
|
516
597
|
/**
|
|
517
598
|
* A block can be either a raw Block or a TemplateBlock.
|
|
@@ -548,6 +629,20 @@ export interface TemplateContext {
|
|
|
548
629
|
orientation: ViewportOrientation;
|
|
549
630
|
/** Layout hints for this orientation */
|
|
550
631
|
layout: LayoutHints;
|
|
632
|
+
/**
|
|
633
|
+
* The block's direct children (set by `getLayers` when the block has
|
|
634
|
+
* `children`). Most templates ignore this; aggregate templates like
|
|
635
|
+
* `diagram` consume it to render each child as part of their output.
|
|
636
|
+
*/
|
|
637
|
+
children?: Block[];
|
|
638
|
+
/**
|
|
639
|
+
* The source block being expanded. Used by user-defined custom
|
|
640
|
+
* templates so the token resolver can substitute `{title}`,
|
|
641
|
+
* `{content}`, `{children}`, and `{image:N}` against the block's
|
|
642
|
+
* data at render time. Built-in templates ignore this and rely on
|
|
643
|
+
* their typed `input` parameters instead.
|
|
644
|
+
*/
|
|
645
|
+
block?: Block;
|
|
551
646
|
}
|
|
552
647
|
|
|
553
648
|
/**
|
|
@@ -560,6 +655,10 @@ export function createTemplateContext(
|
|
|
560
655
|
viewport: ViewportConfig = VIEWPORT_PRESETS.landscape,
|
|
561
656
|
): TemplateContext {
|
|
562
657
|
const orientation = getViewportOrientation(viewport);
|
|
658
|
+
// Theme renderStyle may override individual layout hints (e.g. a theme
|
|
659
|
+
// that wants a lower primaryY across every template).
|
|
660
|
+
const overrides = theme.renderStyle.layoutOverrides;
|
|
661
|
+
const baseLayout = getLayoutHints(orientation);
|
|
563
662
|
return {
|
|
564
663
|
theme,
|
|
565
664
|
blockIndex,
|
|
@@ -567,7 +666,7 @@ export function createTemplateContext(
|
|
|
567
666
|
viewport,
|
|
568
667
|
fontScale: calculateFontScale(viewport),
|
|
569
668
|
orientation,
|
|
570
|
-
layout:
|
|
669
|
+
layout: overrides ? { ...baseLayout, ...overrides } : baseLayout,
|
|
571
670
|
};
|
|
572
671
|
}
|
|
573
672
|
|
|
@@ -647,7 +746,9 @@ export type PersistentLayerTemplateType =
|
|
|
647
746
|
| 'solidBackground' // Solid color fill
|
|
648
747
|
| 'gradientBackground' // CSS gradient or preset
|
|
649
748
|
| 'imageBackground' // Blurred/faded hero image
|
|
650
|
-
| 'patternBackground' // Subtle pattern (dots, grid)
|
|
749
|
+
| 'patternBackground' // Subtle pattern (dots, grid, diagonal, noise/grain)
|
|
750
|
+
| 'vignette' // Soft radial edge darkening (top layer)
|
|
751
|
+
| 'ambientGradient' // Slowly drifting surface gradient (bottom layer)
|
|
651
752
|
| 'titleCaption' // Article title in corner
|
|
652
753
|
| 'cornerBranding' // Logo or text badge
|
|
653
754
|
| 'progressIndicator'; // Bar/dots showing position
|
|
@@ -660,6 +761,8 @@ export type PersistentLayerTemplateConfig =
|
|
|
660
761
|
| GradientBackgroundConfig
|
|
661
762
|
| ImageBackgroundConfig
|
|
662
763
|
| PatternBackgroundConfig
|
|
764
|
+
| VignetteConfig
|
|
765
|
+
| AmbientGradientConfig
|
|
663
766
|
| TitleCaptionConfig
|
|
664
767
|
| CornerBrandingConfig
|
|
665
768
|
| ProgressIndicatorConfig;
|
|
@@ -718,6 +821,32 @@ export interface PatternBackgroundConfig {
|
|
|
718
821
|
scale?: number;
|
|
719
822
|
}
|
|
720
823
|
|
|
824
|
+
/**
|
|
825
|
+
* Soft radial vignette (top layer) — transparent center darkening toward
|
|
826
|
+
* the frame edges. The classic film-look framing device.
|
|
827
|
+
*/
|
|
828
|
+
export interface VignetteConfig {
|
|
829
|
+
type: 'vignette';
|
|
830
|
+
/** Edge darkness 0-1 (default 0.3). */
|
|
831
|
+
strength?: number;
|
|
832
|
+
/** Vignette color (default black). */
|
|
833
|
+
color?: string;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Slowly drifting surface gradient (bottom layer). Colors default to the
|
|
838
|
+
* theme's surface family at expansion time.
|
|
839
|
+
*/
|
|
840
|
+
export interface AmbientGradientConfig {
|
|
841
|
+
type: 'ambientGradient';
|
|
842
|
+
/** Gradient start color. */
|
|
843
|
+
from?: string;
|
|
844
|
+
/** Gradient end color. */
|
|
845
|
+
to?: string;
|
|
846
|
+
/** Drift loop duration in seconds (default 40). */
|
|
847
|
+
duration?: number;
|
|
848
|
+
}
|
|
849
|
+
|
|
721
850
|
// ============================================
|
|
722
851
|
// Overlay Layer Configs
|
|
723
852
|
// ============================================
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Templates Schema
|
|
3
|
+
*
|
|
4
|
+
* User-defined block layout templates. The author designs a visual
|
|
5
|
+
* layout once (in the editor's TemplateDesigner) and saves it as a
|
|
6
|
+
* `CustomTemplateDefinition`. The definition lives either in the
|
|
7
|
+
* document's frontmatter (per-doc, portable, ships with the markdown)
|
|
8
|
+
* or in the user's local library (cross-doc reuse via localStorage).
|
|
9
|
+
*
|
|
10
|
+
* A custom template's content is just a `Layer[]` — the same shape that
|
|
11
|
+
* built-in templates emit at expansion time. The novel bit is
|
|
12
|
+
* *placeholder tokens*: TextLayer / ImageLayer content fields may carry
|
|
13
|
+
* strings like `{title}`, `{content}`, `{children}`, or `{image:N}`
|
|
14
|
+
* that the resolver (see `doc/templates/tokens/resolveTokens.ts`)
|
|
15
|
+
* substitutes against the source Block at render time.
|
|
16
|
+
*
|
|
17
|
+
* Responsiveness model: every numeric position field in `layers` is
|
|
18
|
+
* stored as a `%`-string relative to `viewport`, so the same template
|
|
19
|
+
* renders correctly at any of Squisq's viewport presets (landscape,
|
|
20
|
+
* portrait, square). The `viewport` field itself is purely an
|
|
21
|
+
* authoring-time reference — it tells the designer canvas which aspect
|
|
22
|
+
* ratio to mount with, and the thumbnail renderer which viewBox to use.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { Layer } from './Doc.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A single user-defined template. Stored in `Doc.customTemplates`
|
|
29
|
+
* (frontmatter-backed) or in the user's local library.
|
|
30
|
+
*/
|
|
31
|
+
export interface CustomTemplateDefinition {
|
|
32
|
+
/**
|
|
33
|
+
* Stable id used in heading annotations (`### Foo {[name]}`). Must be
|
|
34
|
+
* a slug — lowercase alphanumerics, hyphens. The picker enforces
|
|
35
|
+
* uniqueness within a (doc + library) merged namespace.
|
|
36
|
+
*/
|
|
37
|
+
name: string;
|
|
38
|
+
|
|
39
|
+
/** Human-readable label shown in the template picker. */
|
|
40
|
+
label: string;
|
|
41
|
+
|
|
42
|
+
/** Optional one-sentence description for the picker card. */
|
|
43
|
+
description?: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Authoring viewport — the canvas size the designer drew against.
|
|
47
|
+
* Stored so the designer and thumbnail renderer can reproduce the
|
|
48
|
+
* same canvas. Layer positions are stored as `%`-strings, so the
|
|
49
|
+
* actual render-time viewport can differ from this without breaking
|
|
50
|
+
* the layout. Defaults to 1920×1080 landscape.
|
|
51
|
+
*/
|
|
52
|
+
viewport: {
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The visual content. Every `position.x` / `position.y` /
|
|
59
|
+
* `position.width` / `position.height` should be a `%`-string at
|
|
60
|
+
* save time (the designer's normalizePositions step enforces this).
|
|
61
|
+
*
|
|
62
|
+
* Tokens supported in v1:
|
|
63
|
+
* - TextLayer.content.text — `{title}`, `{content}`, `{children}`,
|
|
64
|
+
* `{image:N}` (substitutes the Nth image's alt text)
|
|
65
|
+
* - ImageLayer.content.src — `{image:N}` (substitutes the URL)
|
|
66
|
+
*/
|
|
67
|
+
layers: Layer[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Tag value for the YAML frontmatter key that carries inlined custom
|
|
72
|
+
* template definitions. Exported so the markdown bridge stays in sync.
|
|
73
|
+
*/
|
|
74
|
+
export const FRONTMATTER_CUSTOM_TEMPLATES_KEY = 'squisq-custom-templates';
|