@bendyline/squisq-formats 2.8.7 → 2.8.9
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 +21 -0
- package/dist/chunk-4LIFEQKX.js +358 -0
- package/dist/{chunk-UXEANVBI.js → chunk-5HUHSLGA.js} +1 -84
- package/dist/{chunk-IYNDHW2C.js → chunk-6HUQFEP3.js} +75 -5
- package/dist/{chunk-BXWNU4T5.js → chunk-DTJQK7U7.js} +43 -0
- package/dist/{chunk-QU4EVC62.js → chunk-GFHUJ7L4.js} +1 -1
- package/dist/{chunk-TMOXUUTG.js → chunk-NQNIFIDP.js} +6 -4
- package/dist/{chunk-B6UQPW75.js → chunk-SZ3OIY6G.js} +697 -44
- package/dist/{chunk-KROH7FNT.js → chunk-VKLJUHC2.js} +6 -4
- package/dist/{chunk-C23JYVPG.js → chunk-WJTC5OD6.js} +2 -0
- package/dist/{chunk-ALDCFHZT.js → chunk-WVDAFJFS.js} +59 -21
- package/dist/chunk-YRFBWYAD.js +128 -0
- package/dist/{chunk-SVYTUYZM.js → chunk-ZJERBWTB.js} +6 -4
- package/dist/data/index.d.ts +27 -5
- package/dist/data/index.js +6 -2
- package/dist/diagrams/index.d.ts +19 -8
- package/dist/diagrams/index.js +7 -242
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/index.js +3 -3
- package/dist/epub/index.d.ts +1 -1
- package/dist/epub/index.js +3 -3
- package/dist/fontAwesome-C6FRDYTY.js +14 -0
- package/dist/html/index.d.ts +214 -2
- package/dist/html/index.js +25 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.js +15 -10
- package/dist/{materialize-G4WC5UG3.js → materialize-ICXNO4S4.js} +4 -1
- package/dist/outside-in/index.d.ts +21 -2
- package/dist/outside-in/index.js +7 -3
- package/dist/outside-in/layout.d.ts +7 -1
- package/dist/outside-in/layout.js +3 -1
- package/dist/pdf/index.d.ts +1 -1
- package/dist/pdf/index.js +3 -3
- package/dist/plainHtmlIcons-KBSIJ5EN.js +62 -0
- package/dist/pptx/index.js +2 -2
- package/dist/readers-3P5A6A2A.js +17 -0
- package/dist/registry/index.js +1 -1
- package/package.json +2 -2
- /package/dist/{chunk-LBXBRFBZ.js → chunk-DDMPZZJF.js} +0 -0
package/README.md
CHANGED
|
@@ -141,6 +141,27 @@ const page = markdownDocToPlainHtml(markdownDoc, { themeId: 'warm-earth' });
|
|
|
141
141
|
const imported = await htmlToMarkdownDoc(htmlString);
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
To place a document inside your own page — a site header, navigation, footer, and stylesheet — write the page as an HTML template with standard `<slot>` elements and fill it:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { applyHtmlPageTemplate, markdownDocToPlainHtmlParts } from '@bendyline/squisq-formats/html';
|
|
148
|
+
|
|
149
|
+
// template: <title><slot name="title">Post</slot> · My Blog</title> … <h1><slot name="title"></slot></h1> <slot></slot>
|
|
150
|
+
const parts = markdownDocToPlainHtmlParts(markdownDoc);
|
|
151
|
+
const page = applyHtmlPageTemplate(
|
|
152
|
+
template,
|
|
153
|
+
{
|
|
154
|
+
bodyHtml: parts.bodyHtml,
|
|
155
|
+
headHtml: parts.featureHeadHtml,
|
|
156
|
+
title: 'Hello',
|
|
157
|
+
values: { date: '2026-10-28' },
|
|
158
|
+
},
|
|
159
|
+
{ templatePath: '_templates/post.html', outputPath: 'posts/hello.html' },
|
|
160
|
+
);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The unnamed `<slot>` receives the body (between `<!-- squisq:content -->` markers, which `htmlToMarkdownDoc` honors on re-import), named slots receive text values or keep their fallback children, relative URLs are rebased from the template's folder to the page's, and everything else in the template is copied byte for byte.
|
|
164
|
+
|
|
144
165
|
The subpath also exports `markdownDocsToPlainHtmlBundle` / `markdownDocsToHtmlBundle` (recursive multi-doc ZIP bundles with `.md` links rewritten to `.html`), `htmlToMarkdownDocSync` / `htmlToMarkdown`, and helpers (`collectImagePaths`, `collectLinkRefs`, `inferMimeType`).
|
|
145
166
|
|
|
146
167
|
### EPUB
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
// src/diagrams/index.ts
|
|
2
|
+
import {
|
|
3
|
+
extractPlainText,
|
|
4
|
+
isSvgIslandLang,
|
|
5
|
+
parseSvgIsland,
|
|
6
|
+
stringifyMarkdown
|
|
7
|
+
} from "@bendyline/squisq/markdown";
|
|
8
|
+
import {
|
|
9
|
+
TABLE_FED_TEMPLATES,
|
|
10
|
+
buildChartData,
|
|
11
|
+
detectAsciiDiagram,
|
|
12
|
+
detectAsciiTimeline,
|
|
13
|
+
detectTree,
|
|
14
|
+
isContainerTemplate,
|
|
15
|
+
isEligibleAsciiFenceLang,
|
|
16
|
+
isEligibleAsciiTimelineFenceLang,
|
|
17
|
+
isEligibleTreeFenceLang,
|
|
18
|
+
isExplicitDiagramLang,
|
|
19
|
+
isExplicitTimelineLang,
|
|
20
|
+
isExplicitTreeLang,
|
|
21
|
+
resolveTemplateName
|
|
22
|
+
} from "@bendyline/squisq/doc";
|
|
23
|
+
var DEFAULT_KINDS = [
|
|
24
|
+
"mermaid",
|
|
25
|
+
"diagram",
|
|
26
|
+
"timeline",
|
|
27
|
+
"tree",
|
|
28
|
+
"container",
|
|
29
|
+
"chart"
|
|
30
|
+
];
|
|
31
|
+
var EXPORT_DIAGRAM_KINDS = [...DEFAULT_KINDS, "svg"];
|
|
32
|
+
var KIND_NAMES = {
|
|
33
|
+
mermaid: "Diagram",
|
|
34
|
+
diagram: "Diagram",
|
|
35
|
+
timeline: "Timeline",
|
|
36
|
+
tree: "Tree",
|
|
37
|
+
container: "Diagram",
|
|
38
|
+
svg: "Illustration",
|
|
39
|
+
chart: "Chart"
|
|
40
|
+
};
|
|
41
|
+
function fenceDiagramKind(node) {
|
|
42
|
+
const lang = node.lang ?? null;
|
|
43
|
+
if ((lang ?? "").trim().toLowerCase() === "mermaid") return "mermaid";
|
|
44
|
+
if (isSvgIslandLang(lang)) return parseSvgIsland(node.value).ok ? "svg" : null;
|
|
45
|
+
if (isEligibleAsciiFenceLang(lang)) {
|
|
46
|
+
const detection = detectAsciiDiagram(node.value, { explicit: isExplicitDiagramLang(lang) });
|
|
47
|
+
if (detection.isDiagram) return "diagram";
|
|
48
|
+
}
|
|
49
|
+
if (isEligibleTreeFenceLang(lang)) {
|
|
50
|
+
if (detectTree(node.value, { explicit: isExplicitTreeLang(lang) }).isTree) return "tree";
|
|
51
|
+
}
|
|
52
|
+
if (isEligibleAsciiTimelineFenceLang(lang)) {
|
|
53
|
+
const detection = detectAsciiTimeline(node.value, { explicit: isExplicitTimelineLang(lang) });
|
|
54
|
+
if (detection.isTimeline) return "timeline";
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function containerTemplateOf(node) {
|
|
59
|
+
if (node.type !== "heading") return null;
|
|
60
|
+
const requested = node.templateAnnotation?.template;
|
|
61
|
+
if (!requested || !isContainerTemplate(requested)) return null;
|
|
62
|
+
return resolveTemplateName(requested);
|
|
63
|
+
}
|
|
64
|
+
function mermaidAlt(source) {
|
|
65
|
+
const title = /^\s*accTitle\s*:\s*(.+?)\s*$/mu.exec(source)?.[1] ?? "";
|
|
66
|
+
const description = /^\s*accDescr\s*:\s*(.+?)\s*$/mu.exec(source)?.[1] ?? "";
|
|
67
|
+
return [title, description].filter(Boolean).join(". ");
|
|
68
|
+
}
|
|
69
|
+
function fenceAlt(kind, source) {
|
|
70
|
+
if (kind === "mermaid") return mermaidAlt(source);
|
|
71
|
+
if (kind === "svg") {
|
|
72
|
+
const result = parseSvgIsland(source);
|
|
73
|
+
return result.ok && result.island.title || "";
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
function fenceMarkdown(node) {
|
|
78
|
+
return stringifyMarkdown({ type: "document", children: [node] }).trim();
|
|
79
|
+
}
|
|
80
|
+
function tableText(table) {
|
|
81
|
+
const [header, ...body] = table.children.map(
|
|
82
|
+
(row) => row.children.map((cell) => extractPlainText(cell).trim())
|
|
83
|
+
);
|
|
84
|
+
return { headers: header ?? [], rows: body };
|
|
85
|
+
}
|
|
86
|
+
function listParam(value) {
|
|
87
|
+
const entries = value?.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
88
|
+
return entries && entries.length > 0 ? entries : void 0;
|
|
89
|
+
}
|
|
90
|
+
var NON_CHART_PARAMS = /* @__PURE__ */ new Set([
|
|
91
|
+
"showTable",
|
|
92
|
+
"src",
|
|
93
|
+
"sheet",
|
|
94
|
+
"anchor",
|
|
95
|
+
"headerRow",
|
|
96
|
+
"titleAnchor",
|
|
97
|
+
"previewRows",
|
|
98
|
+
"sort",
|
|
99
|
+
"filter"
|
|
100
|
+
]);
|
|
101
|
+
function isTruthyParam(value) {
|
|
102
|
+
return value !== void 0 && value !== "false" && value !== "0";
|
|
103
|
+
}
|
|
104
|
+
function findChart(children, index) {
|
|
105
|
+
const heading = children[index];
|
|
106
|
+
const requested = heading.templateAnnotation?.template;
|
|
107
|
+
const template = requested ? resolveTemplateName(requested) : "";
|
|
108
|
+
if (!TABLE_FED_TEMPLATES.has(template) || template === "dataTable") return null;
|
|
109
|
+
let tableIndex = -1;
|
|
110
|
+
for (let at = index + 1; at < children.length; at++) {
|
|
111
|
+
const node = children[at];
|
|
112
|
+
if (node.type === "heading") break;
|
|
113
|
+
if (node.type === "table") {
|
|
114
|
+
tableIndex = at;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (tableIndex < 0) return null;
|
|
119
|
+
const table = children[tableIndex];
|
|
120
|
+
const params = heading.templateAnnotation?.params ?? {};
|
|
121
|
+
const data = buildChartData(tableText(table), {
|
|
122
|
+
labelColumn: params.labelColumn,
|
|
123
|
+
valueColumns: listParam(params.valueColumns)
|
|
124
|
+
});
|
|
125
|
+
if (!data) return null;
|
|
126
|
+
const { showTable } = params;
|
|
127
|
+
const chartParams = Object.fromEntries(
|
|
128
|
+
Object.entries(params).filter(([key]) => !NON_CHART_PARAMS.has(key))
|
|
129
|
+
);
|
|
130
|
+
const chartHeading = {
|
|
131
|
+
...heading,
|
|
132
|
+
templateAnnotation: { template, params: chartParams }
|
|
133
|
+
};
|
|
134
|
+
const title = extractPlainText(heading).trim();
|
|
135
|
+
const plotted = data.series.map((series) => series.name).join(", ");
|
|
136
|
+
const what = `${template.replace(/Chart$/, "")} chart of ${plotted}${data.labelHeader ? ` by ${data.labelHeader}` : ""}`;
|
|
137
|
+
return {
|
|
138
|
+
chart: { headingIndex: index, tableIndex, keepTable: isTruthyParam(showTable) },
|
|
139
|
+
diagram: {
|
|
140
|
+
kind: "chart",
|
|
141
|
+
markdown: stringifyMarkdown({ type: "document", children: [chartHeading, table] }).trim(),
|
|
142
|
+
source: "",
|
|
143
|
+
template,
|
|
144
|
+
alt: title ? `${title}: ${what}` : what.charAt(0).toUpperCase() + what.slice(1)
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function findInDocument(doc) {
|
|
149
|
+
const found = [];
|
|
150
|
+
const children = doc.children;
|
|
151
|
+
const walk = (nodes) => {
|
|
152
|
+
for (const node of nodes) {
|
|
153
|
+
if (node.type === "code") {
|
|
154
|
+
const fence = node;
|
|
155
|
+
const kind = fenceDiagramKind(fence);
|
|
156
|
+
if (kind) {
|
|
157
|
+
found.push({
|
|
158
|
+
fence,
|
|
159
|
+
diagram: {
|
|
160
|
+
kind,
|
|
161
|
+
markdown: fenceMarkdown(fence),
|
|
162
|
+
source: fence.value,
|
|
163
|
+
template: "",
|
|
164
|
+
alt: fenceAlt(kind, fence.value) || KIND_NAMES[kind]
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const nested = node.children;
|
|
171
|
+
if (Array.isArray(nested) && node.type !== "heading" && node.type !== "paragraph") {
|
|
172
|
+
walk(nested);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
for (let index = 0; index < children.length; index++) {
|
|
177
|
+
const node = children[index];
|
|
178
|
+
const template = containerTemplateOf(node);
|
|
179
|
+
if (!template) {
|
|
180
|
+
const chart = node.type === "heading" ? findChart(children, index) : null;
|
|
181
|
+
if (chart) found.push(chart);
|
|
182
|
+
walk([node]);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const depth = node.depth;
|
|
186
|
+
let end = index + 1;
|
|
187
|
+
while (end < children.length) {
|
|
188
|
+
const next = children[end];
|
|
189
|
+
if (next.type === "heading" && next.depth <= depth) break;
|
|
190
|
+
end++;
|
|
191
|
+
}
|
|
192
|
+
const heading = extractPlainText(node).trim();
|
|
193
|
+
found.push({
|
|
194
|
+
containerIndex: index,
|
|
195
|
+
containerEnd: end,
|
|
196
|
+
diagram: {
|
|
197
|
+
kind: "container",
|
|
198
|
+
markdown: stringifyMarkdown({
|
|
199
|
+
type: "document",
|
|
200
|
+
children: children.slice(index, end)
|
|
201
|
+
}).trim(),
|
|
202
|
+
source: "",
|
|
203
|
+
template,
|
|
204
|
+
alt: heading || KIND_NAMES.container
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
index = end - 1;
|
|
208
|
+
}
|
|
209
|
+
return found;
|
|
210
|
+
}
|
|
211
|
+
function imageUrls(nodes, into) {
|
|
212
|
+
for (const node of nodes) {
|
|
213
|
+
if (node.type === "image") into.add(node.url);
|
|
214
|
+
const nested = node.children;
|
|
215
|
+
if (Array.isArray(nested)) imageUrls(nested, into);
|
|
216
|
+
}
|
|
217
|
+
return into;
|
|
218
|
+
}
|
|
219
|
+
function findDiagrams(doc) {
|
|
220
|
+
return findInDocument(doc).map((entry) => entry.diagram);
|
|
221
|
+
}
|
|
222
|
+
function withoutTemplate(heading) {
|
|
223
|
+
const { templateAnnotation: _template, ...plain } = heading;
|
|
224
|
+
return plain;
|
|
225
|
+
}
|
|
226
|
+
function imageParagraph(url, alt) {
|
|
227
|
+
const image = { type: "image", url, alt, title: null };
|
|
228
|
+
return { type: "paragraph", children: [image] };
|
|
229
|
+
}
|
|
230
|
+
function replaceFences(nodes, replacements) {
|
|
231
|
+
let changed = false;
|
|
232
|
+
const next = nodes.map((node) => {
|
|
233
|
+
const replacement = replacements.get(node);
|
|
234
|
+
if (replacement) {
|
|
235
|
+
changed = true;
|
|
236
|
+
return replacement;
|
|
237
|
+
}
|
|
238
|
+
const nested = node.children;
|
|
239
|
+
if (Array.isArray(nested)) {
|
|
240
|
+
const replaced = replaceFences(nested, replacements);
|
|
241
|
+
if (replaced !== nested) {
|
|
242
|
+
changed = true;
|
|
243
|
+
return { ...node, children: replaced };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return node;
|
|
247
|
+
});
|
|
248
|
+
return changed ? next : nodes;
|
|
249
|
+
}
|
|
250
|
+
function cachingDiagramRasterizer(render, limit = 64) {
|
|
251
|
+
const drawn = /* @__PURE__ */ new Map();
|
|
252
|
+
return (diagram) => {
|
|
253
|
+
const key = `${diagram.kind}\0${diagram.markdown}\0${diagram.source}`;
|
|
254
|
+
let picture = drawn.get(key);
|
|
255
|
+
if (picture) {
|
|
256
|
+
drawn.delete(key);
|
|
257
|
+
} else {
|
|
258
|
+
picture = render(diagram);
|
|
259
|
+
picture.catch(() => drawn.delete(key));
|
|
260
|
+
}
|
|
261
|
+
drawn.set(key, picture);
|
|
262
|
+
if (drawn.size > limit) drawn.delete(drawn.keys().next().value);
|
|
263
|
+
return picture;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
async function rasterizeDiagrams(doc, render, options = {}) {
|
|
267
|
+
const kinds = new Set(options.kinds ?? DEFAULT_KINDS);
|
|
268
|
+
const prefix = options.urlPrefix ?? "diagram";
|
|
269
|
+
const taken = imageUrls(doc.children, /* @__PURE__ */ new Set());
|
|
270
|
+
let counter = 0;
|
|
271
|
+
const nextUrl = () => {
|
|
272
|
+
let url;
|
|
273
|
+
do
|
|
274
|
+
url = `${prefix}-${String(++counter)}.png`;
|
|
275
|
+
while (taken.has(url));
|
|
276
|
+
taken.add(url);
|
|
277
|
+
return url;
|
|
278
|
+
};
|
|
279
|
+
const images = /* @__PURE__ */ new Map();
|
|
280
|
+
const mermaid = /* @__PURE__ */ new Map();
|
|
281
|
+
const svg = /* @__PURE__ */ new Map();
|
|
282
|
+
const fenceReplacements = /* @__PURE__ */ new Map();
|
|
283
|
+
const blockReplacements = [];
|
|
284
|
+
for (const entry of findInDocument(doc)) {
|
|
285
|
+
options.signal?.throwIfAborted();
|
|
286
|
+
if (!kinds.has(entry.diagram.kind)) continue;
|
|
287
|
+
let picture = null;
|
|
288
|
+
try {
|
|
289
|
+
picture = await render(entry.diagram);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
options.onWarning?.(
|
|
292
|
+
`A ${KIND_NAMES[entry.diagram.kind].toLowerCase()} could not be drawn and was kept as text: ${error instanceof Error ? error.message : String(error)}`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (!picture || picture.width <= 0 || picture.height <= 0) continue;
|
|
296
|
+
const data = picture.data instanceof Uint8Array ? picture.data : new Uint8Array(picture.data);
|
|
297
|
+
const url = nextUrl();
|
|
298
|
+
images.set(url, {
|
|
299
|
+
data,
|
|
300
|
+
contentType: "image/png",
|
|
301
|
+
width: picture.width,
|
|
302
|
+
height: picture.height
|
|
303
|
+
});
|
|
304
|
+
if (entry.diagram.kind === "mermaid") mermaid.set(entry.diagram.source.trim(), picture);
|
|
305
|
+
if (entry.diagram.kind === "svg") svg.set(entry.diagram.source.trim(), picture);
|
|
306
|
+
const paragraph = imageParagraph(url, entry.diagram.alt);
|
|
307
|
+
if (entry.fence) {
|
|
308
|
+
fenceReplacements.set(entry.fence, paragraph);
|
|
309
|
+
} else if (entry.containerIndex !== void 0 && entry.containerEnd !== void 0) {
|
|
310
|
+
blockReplacements.push({
|
|
311
|
+
start: entry.containerIndex,
|
|
312
|
+
end: entry.containerEnd,
|
|
313
|
+
nodes: [withoutTemplate(doc.children[entry.containerIndex]), paragraph]
|
|
314
|
+
});
|
|
315
|
+
} else if (entry.chart) {
|
|
316
|
+
const { headingIndex, tableIndex, keepTable } = entry.chart;
|
|
317
|
+
const table = doc.children[tableIndex];
|
|
318
|
+
blockReplacements.push(
|
|
319
|
+
{
|
|
320
|
+
start: headingIndex,
|
|
321
|
+
end: headingIndex + 1,
|
|
322
|
+
nodes: [withoutTemplate(doc.children[headingIndex])]
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
start: tableIndex,
|
|
326
|
+
end: tableIndex + 1,
|
|
327
|
+
nodes: keepTable ? [paragraph, table] : [paragraph]
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let children = doc.children;
|
|
333
|
+
if (blockReplacements.length > 0) {
|
|
334
|
+
const rebuilt = [];
|
|
335
|
+
let cursor = 0;
|
|
336
|
+
for (const replacement of blockReplacements) {
|
|
337
|
+
rebuilt.push(...children.slice(cursor, replacement.start), ...replacement.nodes);
|
|
338
|
+
cursor = replacement.end;
|
|
339
|
+
}
|
|
340
|
+
rebuilt.push(...children.slice(cursor));
|
|
341
|
+
children = rebuilt;
|
|
342
|
+
}
|
|
343
|
+
if (fenceReplacements.size > 0) children = replaceFences(children, fenceReplacements);
|
|
344
|
+
return {
|
|
345
|
+
markdownDoc: children === doc.children ? doc : { ...doc, children },
|
|
346
|
+
images,
|
|
347
|
+
mermaid,
|
|
348
|
+
svg
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export {
|
|
353
|
+
EXPORT_DIAGRAM_KINDS,
|
|
354
|
+
fenceDiagramKind,
|
|
355
|
+
findDiagrams,
|
|
356
|
+
cachingDiagramRasterizer,
|
|
357
|
+
rasterizeDiagrams
|
|
358
|
+
};
|
|
@@ -8,9 +8,6 @@ import {
|
|
|
8
8
|
parseCsv
|
|
9
9
|
} from "./chunk-Q77KNJIN.js";
|
|
10
10
|
|
|
11
|
-
// src/data/materialize.ts
|
|
12
|
-
import { isDataFilePath } from "@bendyline/squisq/doc";
|
|
13
|
-
|
|
14
11
|
// src/data/readers.ts
|
|
15
12
|
import { applyTableViewState, parseTableViewState } from "@bendyline/squisq/table";
|
|
16
13
|
function applyViewParams(headers, body, opts) {
|
|
@@ -152,89 +149,9 @@ function defaultDataReaders() {
|
|
|
152
149
|
return [csvDataReader, xlsxDataReader, parquetDataReader];
|
|
153
150
|
}
|
|
154
151
|
|
|
155
|
-
// src/data/materialize.ts
|
|
156
|
-
function tableRow(cells) {
|
|
157
|
-
return {
|
|
158
|
-
type: "tableRow",
|
|
159
|
-
children: cells.map((value) => ({
|
|
160
|
-
type: "tableCell",
|
|
161
|
-
children: value ? [{ type: "text", value }] : []
|
|
162
|
-
}))
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
function extensionOf(path) {
|
|
166
|
-
const base = path.split("/").pop() ?? path;
|
|
167
|
-
const dot = base.lastIndexOf(".");
|
|
168
|
-
return dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
|
|
169
|
-
}
|
|
170
|
-
async function materializeDataReferences(markdownDoc, container, onWarning) {
|
|
171
|
-
const readers = defaultDataReaders();
|
|
172
|
-
const children = [];
|
|
173
|
-
let changed = false;
|
|
174
|
-
for (const node of markdownDoc.children) {
|
|
175
|
-
children.push(node);
|
|
176
|
-
if (node.type !== "heading") continue;
|
|
177
|
-
const params = node.templateAnnotation?.params;
|
|
178
|
-
const src = params?.src;
|
|
179
|
-
if (!src || !isDataFilePath(src)) continue;
|
|
180
|
-
const label = params?.sheet && params?.anchor ? `${params.sheet}!${params.anchor}` : src;
|
|
181
|
-
if (!container) {
|
|
182
|
-
onWarning?.(
|
|
183
|
-
`Data reference "${src}" could not be embedded: the export has no source container to read it from.`
|
|
184
|
-
);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
const ext = extensionOf(src);
|
|
188
|
-
const reader = readers.find((r) => r.extensions.includes(ext));
|
|
189
|
-
if (!reader) {
|
|
190
|
-
onWarning?.(`Data reference "${src}" could not be embedded: no reader for ".${ext}".`);
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
let bytes = null;
|
|
194
|
-
try {
|
|
195
|
-
bytes = await container.readFile(src);
|
|
196
|
-
} catch {
|
|
197
|
-
bytes = null;
|
|
198
|
-
}
|
|
199
|
-
if (!bytes) {
|
|
200
|
-
onWarning?.(`Data reference "${src}" could not be embedded: file not found in container.`);
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
try {
|
|
204
|
-
const table = await reader.read(bytes, {
|
|
205
|
-
maxRows: Number.POSITIVE_INFINITY,
|
|
206
|
-
...params?.sheet ? { sheet: params.sheet } : {},
|
|
207
|
-
...params?.anchor ? { anchor: params.anchor } : {},
|
|
208
|
-
...params?.headerRow !== void 0 ? { headerRow: params.headerRow !== "false" } : {},
|
|
209
|
-
// Exports honor the author's curated view: the sort/filter params
|
|
210
|
-
// shape the embedded full table exactly as they shape previews.
|
|
211
|
-
...params?.sort ? { sort: params.sort } : {},
|
|
212
|
-
...params?.filter ? { filter: params.filter } : {}
|
|
213
|
-
});
|
|
214
|
-
const mdTable = {
|
|
215
|
-
type: "table",
|
|
216
|
-
children: [tableRow(table.headers), ...table.rows.map(tableRow)]
|
|
217
|
-
};
|
|
218
|
-
children.push(mdTable);
|
|
219
|
-
changed = true;
|
|
220
|
-
if (ext === "xlsx") {
|
|
221
|
-
onWarning?.(
|
|
222
|
-
`Region ${label} exported as values; formulas in the sidecar workbook were not carried over.`
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
} catch (err) {
|
|
226
|
-
onWarning?.(
|
|
227
|
-
`Data reference "${src}" could not be embedded: ${err instanceof Error ? err.message : String(err)}`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return changed ? { ...markdownDoc, children } : markdownDoc;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
152
|
export {
|
|
235
153
|
csvDataReader,
|
|
236
154
|
xlsxDataReader,
|
|
237
155
|
parquetDataReader,
|
|
238
|
-
defaultDataReaders
|
|
239
|
-
materializeDataReferences
|
|
156
|
+
defaultDataReaders
|
|
240
157
|
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
convert,
|
|
3
3
|
defaultRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WVDAFJFS.js";
|
|
5
5
|
import {
|
|
6
6
|
ConversionError
|
|
7
7
|
} from "./chunk-KXOZMWBS.js";
|
|
8
8
|
import {
|
|
9
9
|
OUTSIDE_IN_FORMAT_IDS,
|
|
10
|
+
OUTSIDE_IN_OUTPUT_TEMPLATE_KEY,
|
|
10
11
|
resolveOutsideInLayout
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WJTC5OD6.js";
|
|
12
13
|
|
|
13
14
|
// src/outside-in/index.ts
|
|
14
15
|
import {
|
|
@@ -63,6 +64,17 @@ function isOutsideInMarkdownEditingEnabled(source) {
|
|
|
63
64
|
const frontmatter = yaml === null ? null : parseFrontmatter(yaml);
|
|
64
65
|
return frontmatter?.[OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY] === true;
|
|
65
66
|
}
|
|
67
|
+
function readOutsideInOutputTemplate(source) {
|
|
68
|
+
const frontmatter = typeof source === "string" ? (() => {
|
|
69
|
+
const yaml = rawFrontmatter(source);
|
|
70
|
+
return yaml === null ? null : parseFrontmatter(yaml);
|
|
71
|
+
})() : source.frontmatter;
|
|
72
|
+
const value = frontmatter?.[OUTSIDE_IN_OUTPUT_TEMPLATE_KEY];
|
|
73
|
+
if (typeof value !== "string") return null;
|
|
74
|
+
const trimmed = value.trim();
|
|
75
|
+
if (!trimmed) return null;
|
|
76
|
+
return trimmed.toLowerCase() === "none" ? "none" : trimmed;
|
|
77
|
+
}
|
|
66
78
|
function withOutsideInMarkdownEditing(source, layout, enabled = true) {
|
|
67
79
|
return setFrontmatterValues(withOutsideInMetadata(source, layout), {
|
|
68
80
|
[OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY]: enabled
|
|
@@ -176,7 +188,9 @@ async function renderOutsideInDocument(source, options = {}) {
|
|
|
176
188
|
{ format: "html" }
|
|
177
189
|
);
|
|
178
190
|
}
|
|
179
|
-
const formatOptions = {
|
|
191
|
+
const formatOptions = {
|
|
192
|
+
...options.formatOptions ?? {}
|
|
193
|
+
};
|
|
180
194
|
if (layout.format === "html" && options.html) {
|
|
181
195
|
formatOptions.html = {
|
|
182
196
|
...formatOptions.html ?? {},
|
|
@@ -184,10 +198,19 @@ async function renderOutsideInDocument(source, options = {}) {
|
|
|
184
198
|
basePath: options.html.basePath ?? layout.companionName
|
|
185
199
|
};
|
|
186
200
|
}
|
|
187
|
-
|
|
201
|
+
const warnings = [];
|
|
202
|
+
const markdown = await prepareOutsideInMarkdown(
|
|
203
|
+
source.markdown,
|
|
204
|
+
layout.format,
|
|
205
|
+
source.container,
|
|
206
|
+
options,
|
|
207
|
+
formatOptions,
|
|
208
|
+
warnings
|
|
209
|
+
);
|
|
210
|
+
const result = await convert(
|
|
188
211
|
{
|
|
189
212
|
kind: "markdown",
|
|
190
|
-
markdown
|
|
213
|
+
markdown,
|
|
191
214
|
container: source.container,
|
|
192
215
|
baseName: layout.stem
|
|
193
216
|
},
|
|
@@ -198,6 +221,52 @@ async function renderOutsideInDocument(source, options = {}) {
|
|
|
198
221
|
formatOptions
|
|
199
222
|
}
|
|
200
223
|
);
|
|
224
|
+
return warnings.length > 0 ? { ...result, warnings: [...warnings, ...result.warnings] } : result;
|
|
225
|
+
}
|
|
226
|
+
async function prepareOutsideInMarkdown(markdown, format, container, options, formatOptions, warnings) {
|
|
227
|
+
const render = options.renderDiagram;
|
|
228
|
+
if (!render || format !== "docx" && format !== "pdf" && format !== "pptx") return markdown;
|
|
229
|
+
const onWarning = (message) => {
|
|
230
|
+
warnings.push(message);
|
|
231
|
+
};
|
|
232
|
+
let doc = typeof markdown === "string" ? parseMarkdown(markdown) : markdown;
|
|
233
|
+
const { rasterizeDiagrams, EXPORT_DIAGRAM_KINDS } = await import("./diagrams/index.js");
|
|
234
|
+
const signal = options.signal;
|
|
235
|
+
if (format === "pptx") {
|
|
236
|
+
const { mermaid, svg } = await rasterizeDiagrams(doc, render, {
|
|
237
|
+
kinds: ["mermaid", "svg"],
|
|
238
|
+
signal,
|
|
239
|
+
onWarning
|
|
240
|
+
});
|
|
241
|
+
formatOptions.pptx = {
|
|
242
|
+
...formatOptions.pptx,
|
|
243
|
+
diagramImages: new Map([...mermaid, ...svg, ...formatOptions.pptx?.diagramImages ?? []])
|
|
244
|
+
};
|
|
245
|
+
return doc;
|
|
246
|
+
}
|
|
247
|
+
const { materializeDataReferences, DATA_PREVIEW_ROWS } = await import("./materialize-ICXNO4S4.js");
|
|
248
|
+
doc = await materializeDataReferences(doc, container, onWarning, {
|
|
249
|
+
maxRows: DATA_PREVIEW_ROWS
|
|
250
|
+
});
|
|
251
|
+
const pictured = await rasterizeDiagrams(doc, render, {
|
|
252
|
+
kinds: format === "pdf" ? EXPORT_DIAGRAM_KINDS : void 0,
|
|
253
|
+
signal,
|
|
254
|
+
onWarning
|
|
255
|
+
});
|
|
256
|
+
if (format === "docx") {
|
|
257
|
+
const { svg } = await rasterizeDiagrams(doc, render, { kinds: ["svg"], signal, onWarning });
|
|
258
|
+
formatOptions.docx = {
|
|
259
|
+
...formatOptions.docx,
|
|
260
|
+
images: new Map([...formatOptions.docx?.images ?? [], ...pictured.images]),
|
|
261
|
+
svgIslandFallbacks: new Map([...svg, ...formatOptions.docx?.svgIslandFallbacks ?? []])
|
|
262
|
+
};
|
|
263
|
+
} else {
|
|
264
|
+
formatOptions.pdf = {
|
|
265
|
+
...formatOptions.pdf,
|
|
266
|
+
images: new Map([...formatOptions.pdf?.images ?? [], ...pictured.images])
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return pictured.markdownDoc;
|
|
201
270
|
}
|
|
202
271
|
|
|
203
272
|
export {
|
|
@@ -205,6 +274,7 @@ export {
|
|
|
205
274
|
readOutsideInMetadata,
|
|
206
275
|
withOutsideInMetadata,
|
|
207
276
|
isOutsideInMarkdownEditingEnabled,
|
|
277
|
+
readOutsideInOutputTemplate,
|
|
208
278
|
withOutsideInMarkdownEditing,
|
|
209
279
|
importOutsideInDocument,
|
|
210
280
|
renderOutsideInDocument
|
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
// src/shared/blockTags.ts
|
|
2
|
+
import {
|
|
3
|
+
detectPromotableBodyAnnotation,
|
|
4
|
+
hasTemplate,
|
|
5
|
+
isMediaAnnotationName,
|
|
6
|
+
parseStandaloneAnnotation
|
|
7
|
+
} from "@bendyline/squisq/doc";
|
|
8
|
+
function withoutBlockTags(doc, options = {}) {
|
|
9
|
+
const children = withoutPromotedTags(doc.children);
|
|
10
|
+
const kept = children.filter((node) => !isRemovedTag(node, options));
|
|
11
|
+
return kept.length === doc.children.length && children === doc.children ? doc : { ...doc, children: kept };
|
|
12
|
+
}
|
|
13
|
+
function isRemovedTag(node, options) {
|
|
14
|
+
const tag = parseStandaloneAnnotation(node)?.template;
|
|
15
|
+
if (!tag) return false;
|
|
16
|
+
if (isMediaAnnotationName(tag)) return !options.keepMedia;
|
|
17
|
+
return hasTemplate(tag);
|
|
18
|
+
}
|
|
19
|
+
function withoutPromotedTags(children) {
|
|
20
|
+
const out = [];
|
|
21
|
+
let changed = false;
|
|
22
|
+
let index = 0;
|
|
23
|
+
while (index < children.length) {
|
|
24
|
+
const node = children[index++];
|
|
25
|
+
out.push(node);
|
|
26
|
+
if (node.type !== "heading" || node.templateAnnotation?.template) continue;
|
|
27
|
+
let end = index;
|
|
28
|
+
while (end < children.length && children[end].type !== "heading") end++;
|
|
29
|
+
const body = children.slice(index, end);
|
|
30
|
+
const found = detectPromotableBodyAnnotation(body);
|
|
31
|
+
const tag = found?.data.template;
|
|
32
|
+
if (found && tag && !isMediaAnnotationName(tag) && hasTemplate(tag)) {
|
|
33
|
+
out.push(...found.strippedContents);
|
|
34
|
+
changed = true;
|
|
35
|
+
} else {
|
|
36
|
+
out.push(...body);
|
|
37
|
+
}
|
|
38
|
+
index = end;
|
|
39
|
+
}
|
|
40
|
+
return changed ? out : children;
|
|
41
|
+
}
|
|
42
|
+
|
|
1
43
|
// src/shared/footnotes.ts
|
|
2
44
|
var FootnoteIndex = class {
|
|
3
45
|
constructor(doc) {
|
|
@@ -102,6 +144,7 @@ function renumberFootnotes(doc) {
|
|
|
102
144
|
}
|
|
103
145
|
|
|
104
146
|
export {
|
|
147
|
+
withoutBlockTags,
|
|
105
148
|
FootnoteIndex,
|
|
106
149
|
footnoteIds,
|
|
107
150
|
renumberFootnotes
|