@bendyline/squisq-formats 2.8.8 → 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.
Files changed (35) hide show
  1. package/dist/chunk-4LIFEQKX.js +358 -0
  2. package/dist/{chunk-UXEANVBI.js → chunk-5HUHSLGA.js} +1 -84
  3. package/dist/{chunk-6ALSRHD7.js → chunk-6HUQFEP3.js} +61 -4
  4. package/dist/{chunk-BXWNU4T5.js → chunk-DTJQK7U7.js} +43 -0
  5. package/dist/{chunk-QU4EVC62.js → chunk-GFHUJ7L4.js} +1 -1
  6. package/dist/{chunk-TMOXUUTG.js → chunk-NQNIFIDP.js} +6 -4
  7. package/dist/{chunk-LK4KCVN6.js → chunk-SZ3OIY6G.js} +150 -28
  8. package/dist/{chunk-KROH7FNT.js → chunk-VKLJUHC2.js} +6 -4
  9. package/dist/{chunk-GDUZJ75A.js → chunk-WVDAFJFS.js} +59 -21
  10. package/dist/chunk-YRFBWYAD.js +128 -0
  11. package/dist/{chunk-SVYTUYZM.js → chunk-ZJERBWTB.js} +6 -4
  12. package/dist/data/index.d.ts +27 -5
  13. package/dist/data/index.js +6 -2
  14. package/dist/diagrams/index.d.ts +19 -8
  15. package/dist/diagrams/index.js +7 -242
  16. package/dist/docx/index.d.ts +1 -1
  17. package/dist/docx/index.js +3 -3
  18. package/dist/epub/index.d.ts +1 -1
  19. package/dist/epub/index.js +3 -3
  20. package/dist/fontAwesome-C6FRDYTY.js +14 -0
  21. package/dist/html/index.d.ts +85 -1
  22. package/dist/html/index.js +9 -4
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +10 -9
  25. package/dist/{materialize-G4WC5UG3.js → materialize-ICXNO4S4.js} +4 -1
  26. package/dist/outside-in/index.d.ts +12 -0
  27. package/dist/outside-in/index.js +2 -2
  28. package/dist/pdf/index.d.ts +1 -1
  29. package/dist/pdf/index.js +3 -3
  30. package/dist/plainHtmlIcons-KBSIJ5EN.js +62 -0
  31. package/dist/pptx/index.js +2 -2
  32. package/dist/readers-3P5A6A2A.js +17 -0
  33. package/dist/registry/index.js +1 -1
  34. package/package.json +2 -2
  35. /package/dist/{chunk-LBXBRFBZ.js → chunk-DDMPZZJF.js} +0 -0
@@ -1,3 +1,7 @@
1
+ import {
2
+ findDiagrams,
3
+ rasterizeDiagrams
4
+ } from "./chunk-4LIFEQKX.js";
1
5
  import {
2
6
  arrayBufferToBase64DataUrl,
3
7
  extractFilename,
@@ -8,8 +12,9 @@ import {
8
12
  } from "./chunk-VCZUXQUL.js";
9
13
  import {
10
14
  FootnoteIndex,
11
- footnoteIds
12
- } from "./chunk-BXWNU4T5.js";
15
+ footnoteIds,
16
+ withoutBlockTags
17
+ } from "./chunk-DTJQK7U7.js";
13
18
  import {
14
19
  imageSizeAttributes
15
20
  } from "./chunk-JZR32FON.js";
@@ -294,7 +299,9 @@ function escapeHtml(str) {
294
299
  import {
295
300
  findBlockVideoEmbed,
296
301
  isSvgIslandLang,
302
+ mediaKindForUrl,
297
303
  parseSvgIsland,
304
+ parseTimeSeconds,
298
305
  sanitizeHtmlNodes,
299
306
  svgIslandDataUrl,
300
307
  svgSourceFromDataUrl,
@@ -304,7 +311,11 @@ import {
304
311
  readFrontmatterThemeId as readFrontmatterThemeId2
305
312
  } from "@bendyline/squisq/markdown";
306
313
  import { resolveFontFamily, buildGoogleFontsUrl } from "@bendyline/squisq/schemas";
307
- import { resolveThemeForDoc } from "@bendyline/squisq/doc";
314
+ import {
315
+ isMediaAnnotationName,
316
+ parseStandaloneAnnotation,
317
+ resolveThemeForDoc
318
+ } from "@bendyline/squisq/doc";
308
319
  function markdownDocToPlainHtml(doc, options = {}) {
309
320
  const { title, body, fontsLink, iconsLink, themedCss, featureCss } = renderPlainHtml(
310
321
  doc,
@@ -347,7 +358,8 @@ function renderPlainHtml(doc, options) {
347
358
  iconsCss,
348
359
  htmlPolicy = "sanitize",
349
360
  externalResources = "deny",
350
- videoEmbeds
361
+ videoEmbeds,
362
+ icons
351
363
  } = options;
352
364
  const resolveId = themeId ?? readFrontmatterThemeId2(doc.frontmatter);
353
365
  const theme = options.theme ?? (resolveId ? resolveThemeForDoc(doc, resolveId, options.themeRegistry) : void 0);
@@ -358,13 +370,14 @@ function renderPlainHtml(doc, options) {
358
370
  htmlPolicy,
359
371
  footnotes,
360
372
  videoEmbeds,
373
+ icons,
361
374
  textColor: theme?.colors.text
362
375
  };
363
- const body = renderTopLevel(doc.children, ctx) + renderFootnotesSection(footnotes, ctx);
376
+ const blocks = withoutBlockTags(doc, { keepMedia: true }).children;
377
+ const body = renderTopLevel(blocks, ctx) + renderFootnotesSection(footnotes, ctx);
364
378
  const fontsLink = theme && externalResources === "allow" ? renderFontsLink(theme) : "";
365
- const usesIcons = docUsesIcons(doc);
366
379
  let iconsLink = "";
367
- if (usesIcons) {
380
+ if (ctx.usesFontIcons) {
368
381
  iconsLink = iconsCss ? `<style data-fa-inline>
369
382
  ${escapeStyleText(iconsCss)}
370
383
  </style>
@@ -375,13 +388,17 @@ ${escapeStyleText(iconsCss)}
375
388
  ${VIDEO_EMBED_CSS}` : "";
376
389
  const svgCss = docUsesSvgIslands(doc) ? `
377
390
  ${SVG_ISLAND_CSS}` : "";
391
+ const mediaCss = ctx.usesMediaPlayers ? `
392
+ ${MEDIA_PLAYER_CSS}` : "";
393
+ const iconCss = ctx.usesSvgIcons ? `
394
+ ${SVG_ICON_CSS}` : "";
378
395
  return {
379
396
  title,
380
397
  body,
381
398
  fontsLink,
382
399
  iconsLink,
383
400
  themedCss,
384
- featureCss: `${FEATURE_CSS}${videoCss}${svgCss}`
401
+ featureCss: `${FEATURE_CSS}${videoCss}${svgCss}${mediaCss}${iconCss}`
385
402
  };
386
403
  }
387
404
  function escapeStyleText(css) {
@@ -389,18 +406,7 @@ function escapeStyleText(css) {
389
406
  }
390
407
  var FONT_AWESOME_LINK = `<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.3.1/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
391
408
  `;
392
- function docUsesIcons(doc) {
393
- function visit(node) {
394
- if (!node || typeof node !== "object") return false;
395
- const n = node;
396
- if (n.type === "inlineIcon") return true;
397
- if (Array.isArray(n.children)) {
398
- for (const child of n.children) if (visit(child)) return true;
399
- }
400
- return false;
401
- }
402
- return visit(doc);
403
- }
409
+ var SVG_ICON_CSS = ` .squisq-icon { display: inline-block; height: 1em; vertical-align: -0.125em; overflow: visible; }`;
404
410
  function docUsesVideoEmbeds(doc, htmlPolicy) {
405
411
  const iframeIn = (nodes) => nodes.some(
406
412
  (node) => node.type === "htmlElement" && (videoEmbedFromIframe(node) !== null || iframeIn(node.children))
@@ -441,15 +447,21 @@ function renderTopLevel(children, ctx) {
441
447
  continue;
442
448
  }
443
449
  }
444
- const video = node && node.type === "paragraph" ? findBlockVideoEmbed(node) : null;
445
- if (video) {
446
- out.push(videoEmbedToHtml(video.embed, video.title, ctx));
447
- continue;
448
- }
449
- out.push(nodeToHtml(node, ctx));
450
+ const html = topLevelBlockToHtml(node, ctx);
451
+ if (html !== null) out.push(html);
450
452
  }
451
453
  return out.join("\n");
452
454
  }
455
+ function topLevelBlockToHtml(node, ctx) {
456
+ if (node?.type !== "paragraph") return nodeToHtml(node, ctx);
457
+ const video = findBlockVideoEmbed(node);
458
+ if (video) return videoEmbedToHtml(video.embed, video.title, ctx);
459
+ const tag = parseStandaloneAnnotation(node);
460
+ if (tag?.template && isMediaAnnotationName(tag.template)) {
461
+ return mediaAnnotationToHtml(tag.template, tag.params, ctx);
462
+ }
463
+ return nodeToHtml(node, ctx);
464
+ }
453
465
  function renderFootnotesSection(footnotes, ctx) {
454
466
  if (footnotes.isEmpty) return "";
455
467
  const items = footnotes.ordered().map((fn) => {
@@ -484,7 +496,7 @@ function renderFeatureSection(heading, bodyNodes, side, ctx) {
484
496
  const headingHtml = `<${headingTag}>${childrenToHtml({ children: heading.children }, ctx)}</${headingTag}>`;
485
497
  const featured = takeFirstImage(bodyNodes);
486
498
  const media = featured.image ? renderFeatureImage(featured.image, ctx) : '<div class="squisq-feature__media squisq-feature__media--empty"></div>';
487
- const textHtml = [headingHtml, ...featured.remaining.map((n) => nodeToHtml(n, ctx))].filter((s) => s.length > 0).join("\n");
499
+ const textHtml = [headingHtml, ...featured.remaining.map((n) => topLevelBlockToHtml(n, ctx))].filter((s) => !!s).join("\n");
488
500
  const sideClass = side === "leftFeature" ? "squisq-feature--left" : "squisq-feature--right";
489
501
  return `<section class="squisq-feature ${sideClass}">
490
502
  ${media}
@@ -807,6 +819,11 @@ function nodeToHtml(node, ctx) {
807
819
  case "image": {
808
820
  const original = node.url ?? "";
809
821
  const resolved = ctx?.images?.get(original) ?? original;
822
+ const mediaKind = mediaKindForUrl(original);
823
+ if (mediaKind) {
824
+ const label = node.alt || node.title || void 0;
825
+ return mediaPlayerHtml(mediaKind, resolved, label, ctx) ?? escapeHtml2(node.alt ?? "");
826
+ }
810
827
  const safeSrc = sanitizeUrl(resolved, "media") ?? svgDataImageToHtmlSrc(resolved);
811
828
  if (!safeSrc) return escapeHtml2(node.alt ?? "");
812
829
  return `<img src="${escapeAttr(safeSrc)}" alt="${escapeAttr(node.alt ?? "")}"${imageSizeAttributes(node)} />`;
@@ -816,9 +833,16 @@ function nodeToHtml(node, ctx) {
816
833
  case "table":
817
834
  return tableToHtml(node, ctx);
818
835
  case "inlineIcon": {
836
+ const token = escapeAttr(node.token ?? `${node.family}:${node.name}`);
837
+ const svg = ctx?.icons?.get(`${node.family}:${node.name}`);
838
+ if (svg) {
839
+ ctx.usesSvgIcons = true;
840
+ const width = Number((svg.width / svg.height).toFixed(4));
841
+ return `<svg class="squisq-icon" viewBox="0 0 ${svg.width} ${svg.height}" width="${width}em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" data-icon="${token}"><path d="${escapeAttr(svg.path)}"/></svg>`;
842
+ }
843
+ if (ctx) ctx.usesFontIcons = true;
819
844
  const family = escapeAttr(node.family ?? "solid");
820
845
  const name = escapeAttr(node.name ?? "");
821
- const token = escapeAttr(node.token ?? `${node.family}:${node.name}`);
822
846
  return `<i class="fa-${family} fa-${name}" data-icon="${token}" aria-hidden="true"></i>`;
823
847
  }
824
848
  case "htmlBlock":
@@ -955,6 +979,28 @@ var VIDEO_EMBED_CSS = ` .squisq-video-embed { display: block; max-width: 720px;
955
979
  .squisq-video-embed-play::after { content: ''; position: absolute; left: 26px; top: 12px; border-style: solid; border-width: 10px 0 10px 16px; border-color: transparent transparent transparent #fff; }
956
980
  .squisq-video-embed-provider { position: absolute; left: 12px; bottom: 10px; color: #fff; font-size: 0.8em; font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); }
957
981
  .squisq-video-embed-caption { margin-top: 0.4em; font-size: 0.875em; opacity: 0.8; }`;
982
+ function mediaPlayerHtml(kind, src, label, ctx) {
983
+ const safeSrc = sanitizeUrl(src, "media");
984
+ if (!safeSrc) return null;
985
+ if (ctx) ctx.usesMediaPlayers = true;
986
+ const aria = label ? ` aria-label="${escapeAttr(label)}"` : "";
987
+ return `<${kind} class="squisq-media" src="${escapeAttr(safeSrc)}" controls preload="metadata"${aria}></${kind}>`;
988
+ }
989
+ function mediaAnnotationToHtml(template, params, ctx) {
990
+ if (!params.src) return null;
991
+ const kind = template === "video" ? "video" : "audio";
992
+ let src = ctx?.images?.get(params.src) ?? params.src;
993
+ const start = parseTimeSeconds(params.clipStart ?? "");
994
+ const end = parseTimeSeconds(params.clipEnd ?? "");
995
+ if ((start !== null || end !== null) && !src.includes("#")) {
996
+ src += `#t=${start ?? ""}${end !== null ? `,${end}` : ""}`;
997
+ }
998
+ const player = mediaPlayerHtml(kind, src, void 0, ctx);
999
+ return player ? `<figure class="squisq-media-clip">${player}</figure>` : null;
1000
+ }
1001
+ var MEDIA_PLAYER_CSS = ` .squisq-media-clip { margin: 1.25em 0; }
1002
+ video.squisq-media { display: block; width: 100%; max-width: 720px; height: auto; border-radius: 6px; }
1003
+ audio.squisq-media { display: block; width: 100%; max-width: 480px; }`;
958
1004
  function svgDataImageToHtmlSrc(url) {
959
1005
  const source = svgSourceFromDataUrl(url);
960
1006
  const result = source === null ? null : parseSvgIsland(source);
@@ -976,6 +1022,80 @@ function escapeAttr(s) {
976
1022
  return escapeHtml2(s).replace(/"/g, "&quot;");
977
1023
  }
978
1024
 
1025
+ // src/html/plainHtmlPrepare.ts
1026
+ import { isDataFilePath } from "@bendyline/squisq/doc";
1027
+ var PAGE_DIAGRAM_KINDS = [
1028
+ "mermaid",
1029
+ "diagram",
1030
+ "timeline",
1031
+ "container",
1032
+ "chart"
1033
+ ];
1034
+ function needsPlainHtmlPreparation(doc, options = {}) {
1035
+ let icons = false;
1036
+ let data = false;
1037
+ const visit = (node) => {
1038
+ if (!node || typeof node !== "object" || icons && data) return;
1039
+ const n = node;
1040
+ if (n.type === "inlineIcon") icons = true;
1041
+ if (n.type === "heading") {
1042
+ const src = n.templateAnnotation?.params?.src;
1043
+ if (src && isDataFilePath(src)) data = true;
1044
+ }
1045
+ if (Array.isArray(n.children)) n.children.forEach(visit);
1046
+ };
1047
+ doc.children.forEach(visit);
1048
+ if (icons || data && options.container) return true;
1049
+ if (!options.renderDiagram) return false;
1050
+ return findDiagrams(doc).some((diagram) => PAGE_DIAGRAM_KINDS.includes(diagram.kind));
1051
+ }
1052
+ async function prepareMarkdownDocForPlainHtml(doc, options = {}) {
1053
+ const { renderDiagram, container, signal, onWarning } = options;
1054
+ let markdownDoc = doc;
1055
+ if (container) {
1056
+ const { materializeDataReferences, DATA_PREVIEW_ROWS } = await import("./materialize-ICXNO4S4.js");
1057
+ markdownDoc = await materializeDataReferences(markdownDoc, container, onWarning, {
1058
+ maxRows: options.maxDataRows ?? DATA_PREVIEW_ROWS,
1059
+ tableCache: options.dataTableCache
1060
+ });
1061
+ }
1062
+ signal?.throwIfAborted();
1063
+ const { plainHtmlIcons } = await import("./plainHtmlIcons-KBSIJ5EN.js");
1064
+ const icons = await plainHtmlIcons(markdownDoc);
1065
+ const images = /* @__PURE__ */ new Map();
1066
+ if (!renderDiagram) return { markdownDoc, images, icons };
1067
+ const pictured = await rasterizeDiagrams(markdownDoc, renderDiagram, {
1068
+ kinds: PAGE_DIAGRAM_KINDS,
1069
+ urlPrefix: "squisq-picture",
1070
+ signal,
1071
+ onWarning
1072
+ });
1073
+ const widths = /* @__PURE__ */ new Map();
1074
+ for (const [url, picture] of pictured.images) {
1075
+ const bytes = picture.data;
1076
+ const buffer = bytes.buffer.slice(
1077
+ bytes.byteOffset,
1078
+ bytes.byteOffset + bytes.byteLength
1079
+ );
1080
+ images.set(url, arrayBufferToBase64DataUrl(buffer, picture.contentType));
1081
+ widths.set(url, Math.round(picture.width));
1082
+ }
1083
+ return { markdownDoc: withPictureWidths(pictured.markdownDoc, widths), images, icons };
1084
+ }
1085
+ function withPictureWidths(doc, widths) {
1086
+ if (widths.size === 0) return doc;
1087
+ const visit = (node) => {
1088
+ if (node.type === "image") {
1089
+ const width = widths.get(node.url);
1090
+ return width === void 0 ? node : { ...node, width };
1091
+ }
1092
+ const children = node.children;
1093
+ if (!Array.isArray(children)) return node;
1094
+ return { ...node, children: children.map(visit) };
1095
+ };
1096
+ return { ...doc, children: doc.children.map(visit) };
1097
+ }
1098
+
979
1099
  // src/html/plainHtmlBundle.ts
980
1100
  import JSZip from "jszip";
981
1101
  import { parseMarkdown, inferDocumentTitle } from "@bendyline/squisq/markdown";
@@ -2645,6 +2765,8 @@ export {
2645
2765
  generateExternalHtml,
2646
2766
  markdownDocToPlainHtml,
2647
2767
  markdownDocToPlainHtmlParts,
2768
+ needsPlainHtmlPreparation,
2769
+ prepareMarkdownDocForPlainHtml,
2648
2770
  markdownDocsToPlainHtmlBundle,
2649
2771
  collectLinkRefs,
2650
2772
  HTML_PAGE_TEMPLATE_CONTENT_START,
@@ -55,8 +55,9 @@ import {
55
55
  resolveTarget
56
56
  } from "./chunk-S5PCVMKU.js";
57
57
  import {
58
- renumberFootnotes
59
- } from "./chunk-BXWNU4T5.js";
58
+ renumberFootnotes,
59
+ withoutBlockTags
60
+ } from "./chunk-DTJQK7U7.js";
60
61
  import {
61
62
  buildContainer
62
63
  } from "./chunk-IIQYS2YH.js";
@@ -65,7 +66,7 @@ import {
65
66
  fontAwesomeFaces,
66
67
  fontAwesomeGlyph,
67
68
  obfuscateDocxFont
68
- } from "./chunk-LBXBRFBZ.js";
69
+ } from "./chunk-DDMPZZJF.js";
69
70
  import {
70
71
  authoredImageSize,
71
72
  extToMime,
@@ -214,8 +215,9 @@ function pointsToTwips(points) {
214
215
  }
215
216
 
216
217
  // src/docx/export.ts
217
- async function markdownDocToDocx(doc, options = {}) {
218
+ async function markdownDocToDocx(source, options = {}) {
218
219
  options.signal?.throwIfAborted();
220
+ const doc = withoutBlockTags(source);
219
221
  const resolvedOptions = options.themeId !== void 0 ? options : { ...options, themeId: readFrontmatterThemeId(doc.frontmatter) };
220
222
  const ctx = new ExportContext(resolvedOptions, doc);
221
223
  const stories = partitionDocumentStories(doc.children);
@@ -251,7 +251,7 @@ async function collectContainerAssets(container, pattern, signal, flattenBasenam
251
251
  async function collectContainerImages(container, signal) {
252
252
  return collectContainerAssets(container, IMAGE_ASSET_RE, signal);
253
253
  }
254
- function dataReferenceWarnings(doc, formatLabel) {
254
+ function dataReferenceSources(doc) {
255
255
  const srcs = /* @__PURE__ */ new Set();
256
256
  const visit = (blocks) => {
257
257
  for (const block of blocks) {
@@ -261,10 +261,38 @@ function dataReferenceWarnings(doc, formatLabel) {
261
261
  }
262
262
  };
263
263
  visit(doc.blocks);
264
- return [...srcs].map(
265
- (src) => `${formatLabel} cannot embed the data sidecar "${src}"; the export carries its bounded preview and the link will not resolve outside the source container.`
264
+ return srcs;
265
+ }
266
+ function dataReferenceWarnings(doc, formatLabel) {
267
+ return [...dataReferenceSources(doc)].map(
268
+ (src) => `${formatLabel} cannot embed the data sidecar "${src}"; the export shows its first rows and the link will not resolve outside the source container.`
269
+ );
270
+ }
271
+ async function markdownWithDataPreview(input, warnings) {
272
+ const markdownDoc = await markdownOf(input);
273
+ if (dataReferenceSources(input.doc).size === 0) return markdownDoc;
274
+ const { materializeDataReferences, DATA_PREVIEW_ROWS } = await import("./materialize-ICXNO4S4.js");
275
+ return materializeDataReferences(
276
+ markdownDoc,
277
+ input.container,
278
+ (message) => warnings.push(message),
279
+ { maxRows: DATA_PREVIEW_ROWS }
266
280
  );
267
281
  }
282
+ async function docWithDataPreview(input, warnings) {
283
+ if (dataReferenceSources(input.doc).size === 0) return input.doc;
284
+ const [{ resolveDataReferences }, { defaultDataReaders }] = await Promise.all([
285
+ import("@bendyline/squisq/doc"),
286
+ import("./readers-3P5A6A2A.js")
287
+ ]);
288
+ const { doc, diagnostics } = await resolveDataReferences(input.doc, input.container, {
289
+ readers: defaultDataReaders()
290
+ });
291
+ for (const diagnostic of diagnostics) {
292
+ if (diagnostic.severity !== "info") warnings.push(diagnostic.message);
293
+ }
294
+ return doc;
295
+ }
268
296
  async function collectContainerDocxImages(container, signal) {
269
297
  const images = await collectContainerImages(container, signal);
270
298
  const { extToMime } = await import("./images-PQCYRJJ5.js");
@@ -351,7 +379,8 @@ function defaultFormats() {
351
379
  async exportDoc(input, options) {
352
380
  const { markdownDocToDocx } = await import("./docx/index.js");
353
381
  const raw = optionsFor(options, "docx");
354
- const markdownDoc = await markdownOf(input);
382
+ const warnings = [];
383
+ const markdownDoc = await markdownWithDataPreview(input, warnings);
355
384
  const containerImages = await collectContainerDocxImages(input.container, options.signal);
356
385
  const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
357
386
  const buf = await markdownDocToDocx(markdownDoc, {
@@ -361,7 +390,10 @@ function defaultFormats() {
361
390
  themeRegistry: options.themeRegistry ?? raw.themeRegistry,
362
391
  images
363
392
  });
364
- return ok(await toBytes(buf), MIME.docx, markdownFidelityWarnings(markdownDoc, "docx"));
393
+ return ok(await toBytes(buf), MIME.docx, [
394
+ ...markdownFidelityWarnings(markdownDoc, "docx"),
395
+ ...warnings
396
+ ]);
365
397
  }
366
398
  };
367
399
  const pdf = {
@@ -381,8 +413,8 @@ function defaultFormats() {
381
413
  async exportDoc(input, options) {
382
414
  const { markdownDocToPdf } = await import("./pdf/index.js");
383
415
  const raw = optionsFor(options, "pdf");
384
- const markdownDoc = await markdownOf(input);
385
416
  const pdfWarnings = [];
417
+ const markdownDoc = await markdownWithDataPreview(input, pdfWarnings);
386
418
  const containerImages = await collectContainerDocxImages(input.container, options.signal);
387
419
  const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
388
420
  const buf = await markdownDocToPdf(markdownDoc, {
@@ -420,7 +452,8 @@ function defaultFormats() {
420
452
  const containerImages = await collectContainerImages(input.container, options.signal);
421
453
  const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
422
454
  const pptxWarnings = [];
423
- const buf = await docToPptx(input.doc, {
455
+ const doc = await docWithDataPreview(input, pptxWarnings);
456
+ const buf = await docToPptx(doc, {
424
457
  ...raw,
425
458
  ...options.title !== void 0 ? { title: options.title } : {},
426
459
  themeId: resolveThemeId(input, options),
@@ -456,7 +489,7 @@ function defaultFormats() {
456
489
  },
457
490
  async exportDoc(input, options) {
458
491
  const { markdownDocToXlsx } = await import("./xlsx/index.js");
459
- const { materializeDataReferences } = await import("./materialize-G4WC5UG3.js");
492
+ const { materializeDataReferences } = await import("./materialize-ICXNO4S4.js");
460
493
  const warnings = [];
461
494
  const markdownDoc = await materializeDataReferences(
462
495
  await markdownOf(input),
@@ -495,7 +528,7 @@ function defaultFormats() {
495
528
  },
496
529
  async exportDoc(input, options) {
497
530
  const { markdownDocToCsv } = await import("./csv/index.js");
498
- const { materializeDataReferences } = await import("./materialize-G4WC5UG3.js");
531
+ const { materializeDataReferences } = await import("./materialize-ICXNO4S4.js");
499
532
  const warnings = [];
500
533
  const markdownDoc = await materializeDataReferences(
501
534
  await markdownOf(input),
@@ -528,9 +561,11 @@ function defaultFormats() {
528
561
  // so there is nothing to extract into a container this wave.
529
562
  async exportDoc(input, options) {
530
563
  const raw = optionsFor(options, "html");
564
+ const warnings = [];
565
+ const doc = await docWithDataPreview(input, warnings);
531
566
  if (raw.playerScriptPath) {
532
567
  const { generateExternalHtml } = await import("./html/index.js");
533
- const htmlText2 = generateExternalHtml(input.doc, {
568
+ const htmlText2 = generateExternalHtml(doc, {
534
569
  ...raw,
535
570
  playerScriptPath: raw.playerScriptPath,
536
571
  title: options.title ?? input.baseName,
@@ -538,7 +573,7 @@ function defaultFormats() {
538
573
  themeId: resolveThemeId(input, options),
539
574
  themeRegistry: options.themeRegistry ?? raw.themeRegistry
540
575
  });
541
- return ok(new TextEncoder().encode(htmlText2), MIME.html);
576
+ return ok(new TextEncoder().encode(htmlText2), MIME.html, warnings);
542
577
  }
543
578
  const playerScript = await requirePlayerScript(options, "html");
544
579
  const { docToHtml } = await import("./html/index.js");
@@ -550,7 +585,7 @@ function defaultFormats() {
550
585
  false
551
586
  );
552
587
  const images = new Map([...containerImages, ...media, ...raw.images ?? /* @__PURE__ */ new Map()]);
553
- const htmlText = docToHtml(input.doc, {
588
+ const htmlText = docToHtml(doc, {
554
589
  ...raw,
555
590
  playerScript,
556
591
  images,
@@ -560,11 +595,10 @@ function defaultFormats() {
560
595
  themeId: resolveThemeId(input, options),
561
596
  themeRegistry: options.themeRegistry ?? raw.themeRegistry
562
597
  });
563
- return ok(
564
- new TextEncoder().encode(htmlText),
565
- MIME.html,
566
- dataReferenceWarnings(input.doc, "Single-file HTML")
567
- );
598
+ return ok(new TextEncoder().encode(htmlText), MIME.html, [
599
+ ...dataReferenceWarnings(input.doc, "Single-file HTML"),
600
+ ...warnings
601
+ ]);
568
602
  }
569
603
  };
570
604
  const htmlzip = {
@@ -596,7 +630,9 @@ function defaultFormats() {
596
630
  ...media,
597
631
  ...raw.images ?? /* @__PURE__ */ new Map()
598
632
  ]);
599
- const blob = await docToHtmlZip(input.doc, {
633
+ const warnings = [];
634
+ const doc = await docWithDataPreview(input, warnings);
635
+ const blob = await docToHtmlZip(doc, {
600
636
  ...raw,
601
637
  playerScript,
602
638
  images,
@@ -606,7 +642,7 @@ function defaultFormats() {
606
642
  themeId: resolveThemeId(input, options),
607
643
  themeRegistry: options.themeRegistry ?? raw.themeRegistry
608
644
  });
609
- return ok(await toBytes(blob), MIME.zip);
645
+ return ok(await toBytes(blob), MIME.zip, warnings);
610
646
  }
611
647
  };
612
648
  const epub = {
@@ -618,7 +654,8 @@ function defaultFormats() {
618
654
  async exportDoc(input, options) {
619
655
  const { markdownDocToEpub } = await import("./epub/index.js");
620
656
  const raw = optionsFor(options, "epub");
621
- const markdownDoc = await markdownOf(input);
657
+ const warnings = [];
658
+ const markdownDoc = await markdownWithDataPreview(input, warnings);
622
659
  const containerImages = await collectContainerImages(input.container, options.signal);
623
660
  const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
624
661
  const buf = await markdownDocToEpub(markdownDoc, {
@@ -630,7 +667,8 @@ function defaultFormats() {
630
667
  });
631
668
  return ok(await toBytes(buf), MIME.epub, [
632
669
  ...markdownFidelityWarnings(markdownDoc, "epub"),
633
- ...dataReferenceWarnings(input.doc, "EPUB")
670
+ ...dataReferenceWarnings(input.doc, "EPUB"),
671
+ ...warnings
634
672
  ]);
635
673
  }
636
674
  };
@@ -0,0 +1,128 @@
1
+ import {
2
+ defaultDataReaders
3
+ } from "./chunk-5HUHSLGA.js";
4
+
5
+ // src/data/materialize.ts
6
+ import {
7
+ isDataFilePath
8
+ } from "@bendyline/squisq/doc";
9
+ function tableRow(cells) {
10
+ return {
11
+ type: "tableRow",
12
+ children: cells.map((value) => ({
13
+ type: "tableCell",
14
+ children: value ? [{ type: "text", value }] : []
15
+ }))
16
+ };
17
+ }
18
+ function count(n) {
19
+ return n.toLocaleString("en-US");
20
+ }
21
+ function extensionOf(path) {
22
+ const base = path.split("/").pop() ?? path;
23
+ const dot = base.lastIndexOf(".");
24
+ return dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
25
+ }
26
+ var DATA_PREVIEW_ROWS = 100;
27
+ async function materializeDataReferences(markdownDoc, container, onWarning, options = {}) {
28
+ const readers = defaultDataReaders();
29
+ const children = [];
30
+ let changed = false;
31
+ for (const [index, node] of markdownDoc.children.entries()) {
32
+ children.push(node);
33
+ if (node.type !== "heading") continue;
34
+ const params = node.templateAnnotation?.params;
35
+ const src = params?.src;
36
+ if (!src || !isDataFilePath(src)) continue;
37
+ if (markdownDoc.children[index + 1]?.type === "table") continue;
38
+ const label = params?.sheet && params?.anchor ? `${params.sheet}!${params.anchor}` : src;
39
+ if (!container) {
40
+ onWarning?.(
41
+ `Data reference "${src}" could not be embedded: the export has no source container to read it from.`
42
+ );
43
+ continue;
44
+ }
45
+ const ext = extensionOf(src);
46
+ const reader = readers.find((r) => r.extensions.includes(ext));
47
+ if (!reader) {
48
+ onWarning?.(`Data reference "${src}" could not be embedded: no reader for ".${ext}".`);
49
+ continue;
50
+ }
51
+ const previewRows = Number.parseInt(params?.previewRows ?? "", 10);
52
+ const maxRows = options.maxRows === void 0 ? Number.POSITIVE_INFINITY : previewRows > 0 ? previewRows : options.maxRows;
53
+ const readOptions = {
54
+ maxRows,
55
+ ...params?.sheet ? { sheet: params.sheet } : {},
56
+ ...params?.anchor ? { anchor: params.anchor } : {},
57
+ ...params?.headerRow !== void 0 ? { headerRow: params.headerRow !== "false" } : {},
58
+ // Exports honor the author's curated view: the sort/filter params
59
+ // shape the embedded full table exactly as they shape previews.
60
+ ...params?.sort ? { sort: params.sort } : {},
61
+ ...params?.filter ? { filter: params.filter } : {}
62
+ };
63
+ const cacheKey = [
64
+ src,
65
+ readOptions.sheet ?? "",
66
+ readOptions.anchor ?? "",
67
+ String(readOptions.headerRow ?? ""),
68
+ readOptions.sort ?? "",
69
+ readOptions.filter ?? "",
70
+ String(maxRows)
71
+ ].join("\0");
72
+ let pending = options.tableCache?.get(cacheKey);
73
+ if (!pending) {
74
+ let bytes = null;
75
+ try {
76
+ bytes = await container.readFile(src);
77
+ } catch {
78
+ bytes = null;
79
+ }
80
+ if (!bytes) {
81
+ onWarning?.(`Data reference "${src}" could not be embedded: file not found in container.`);
82
+ continue;
83
+ }
84
+ pending = reader.read(bytes, readOptions);
85
+ options.tableCache?.set(cacheKey, pending);
86
+ }
87
+ try {
88
+ const table = await pending;
89
+ const mdTable = {
90
+ type: "table",
91
+ children: [tableRow(table.headers), ...table.rows.map(tableRow)]
92
+ };
93
+ children.push(mdTable);
94
+ if (table.totalRows > table.rows.length) {
95
+ children.push({
96
+ type: "paragraph",
97
+ children: [
98
+ {
99
+ type: "emphasis",
100
+ children: [
101
+ {
102
+ type: "text",
103
+ value: `Showing ${count(table.rows.length)} of ${count(table.totalRows)} rows.`
104
+ }
105
+ ]
106
+ }
107
+ ]
108
+ });
109
+ }
110
+ changed = true;
111
+ if (ext === "xlsx") {
112
+ onWarning?.(
113
+ `Region ${label} exported as values; formulas in the sidecar workbook were not carried over.`
114
+ );
115
+ }
116
+ } catch (err) {
117
+ onWarning?.(
118
+ `Data reference "${src}" could not be embedded: ${err instanceof Error ? err.message : String(err)}`
119
+ );
120
+ }
121
+ }
122
+ return changed ? { ...markdownDoc, children } : markdownDoc;
123
+ }
124
+
125
+ export {
126
+ DATA_PREVIEW_ROWS,
127
+ materializeDataReferences
128
+ };
@@ -14,13 +14,14 @@ import {
14
14
  } from "./chunk-JU2RHXUB.js";
15
15
  import {
16
16
  FootnoteIndex,
17
- footnoteIds
18
- } from "./chunk-BXWNU4T5.js";
17
+ footnoteIds,
18
+ withoutBlockTags
19
+ } from "./chunk-DTJQK7U7.js";
19
20
  import {
20
21
  collectInlineIconFamilies,
21
22
  fontAwesomeFaces,
22
23
  fontAwesomeGlyph
23
- } from "./chunk-LBXBRFBZ.js";
24
+ } from "./chunk-DDMPZZJF.js";
24
25
  import {
25
26
  imageSizeAttributes
26
27
  } from "./chunk-JZR32FON.js";
@@ -30,8 +31,9 @@ import JSZip from "jszip";
30
31
  import { resolveFontFamily } from "@bendyline/squisq/schemas";
31
32
  import { resolveThemeForDoc } from "@bendyline/squisq/doc";
32
33
  import { isHtmlLineBreak, readFrontmatterThemeId, sanitizeUrl } from "@bendyline/squisq/markdown";
33
- async function markdownDocToEpub(sourceDoc, options = {}) {
34
+ async function markdownDocToEpub(authoredDoc, options = {}) {
34
35
  options.signal?.throwIfAborted();
36
+ const sourceDoc = withoutBlockTags(authoredDoc);
35
37
  const themeId = options.themeId ?? readFrontmatterThemeId(sourceDoc.frontmatter);
36
38
  const islandColor = themeId ? resolveThemeForDoc(sourceDoc, themeId, options.themeRegistry).colors.text : void 0;
37
39
  const { doc, images: islandImages } = svgIslandsAsImages(sourceDoc, { color: islandColor });