@bendyline/docblocks-react 2.7.2 → 2.7.4

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 (39) hide show
  1. package/THIRD_PARTY_NOTICES.txt +215 -23
  2. package/dist/{AiDiagrams-LFV4YU6W.js → AiDiagrams-QZMJNGDT.js} +3 -3
  3. package/dist/{AiEditorAssistant-GN2KXKZD.js → AiEditorAssistant-4HFFGVU4.js} +205 -166
  4. package/dist/AiStudyDialog-P3E7PJV5.js +1150 -0
  5. package/dist/{AudioExportDialog-F23QXNPB.js → AudioExportDialog-IOYIEAER.js} +1 -1
  6. package/dist/{DocumentConflictDialog-5LRVIGXS.js → DocumentConflictDialog-DZ2BV2IH.js} +1 -1
  7. package/dist/ExportDialog-LGWBXIWN.js +9 -0
  8. package/dist/{ExportToolbarControls-2FEIHNMW.js → ExportToolbarControls-HOV424TO.js} +114 -14
  9. package/dist/{GitUI-RCWUZVS6.js → GitUI-F3CNBURE.js} +1 -1
  10. package/dist/{KeyboardShortcutsDialog-NOD4P25G.js → KeyboardShortcutsDialog-HVKDFP5Z.js} +2 -2
  11. package/dist/{PresentationMaker-ITNP5SDO.js → PresentationMaker-MUMFAXEI.js} +3 -3
  12. package/dist/{QuickOpenDialog-2KK4SEBU.js → QuickOpenDialog-ENVFGIHS.js} +1 -1
  13. package/dist/{Settings-RDYvDDI0.d.ts → Settings-CfhY489G.d.ts} +3 -5
  14. package/dist/{ShareDialog-XUA2OVGI.js → ShareDialog-PF7D7ORP.js} +1 -1
  15. package/dist/{SpeechToolbarControl-CLU7RB2G.js → SpeechToolbarControl-KNI2C4JJ.js} +1 -1
  16. package/dist/{TextFromNarration-NITJQ4IB.js → TextFromNarration-EYOMW2IW.js} +1 -1
  17. package/dist/{chunk-HRWEGPTY.js → chunk-7CSBRAMT.js} +109 -24
  18. package/dist/{chunk-NAH52736.js → chunk-7V34VUK6.js} +969 -98
  19. package/dist/{chunk-K7XJDJUJ.js → chunk-ACQF63SB.js} +14 -4
  20. package/dist/{chunk-X3DDTJKR.js → chunk-D4HIOS7E.js} +256 -18
  21. package/dist/{chunk-BLUEZG7T.js → chunk-FBPCURCS.js} +9 -2
  22. package/dist/{chunk-Y5OE3HJC.js → chunk-GFK4TCKN.js} +11 -5
  23. package/dist/chunk-H3BK5MK6.js +34 -0
  24. package/dist/{chunk-6B7G33D5.js → chunk-KBUHWZCF.js} +1 -1
  25. package/dist/{chunk-SSB43524.js → chunk-PNZFIIWW.js} +91 -12
  26. package/dist/{chunk-56KO2LYN.js → chunk-VJPGZCFS.js} +4 -0
  27. package/dist/chunk-XNT66JC2.js +168 -0
  28. package/dist/editor.js +4 -4
  29. package/dist/export/index.d.ts +19 -4
  30. package/dist/export/index.js +4 -4
  31. package/dist/index.d.ts +19 -5
  32. package/dist/index.js +858 -557
  33. package/dist/{run-export-CS3HRNAZ.js → run-export-SPNNH32M.js} +1 -1
  34. package/dist/settings/index.d.ts +1 -1
  35. package/dist/settings/index.js +2 -2
  36. package/package.json +8 -8
  37. package/src/styles/docblocks.css +526 -63
  38. package/dist/ExportDialog-JRVZCG3J.js +0 -9
  39. package/dist/chunk-YBEYTVU2.js +0 -12
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-RP3DVNTI.js";
8
8
  import {
9
9
  Dialog
10
- } from "./chunk-56KO2LYN.js";
10
+ } from "./chunk-VJPGZCFS.js";
11
11
 
12
12
  // src/Speech/AudioExportDialog.tsx
13
13
  import { useEffect, useId, useRef, useState } from "react";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dialog
3
- } from "./chunk-56KO2LYN.js";
3
+ } from "./chunk-VJPGZCFS.js";
4
4
 
5
5
  // src/DocBlocksShell/DocumentConflictDialog.tsx
6
6
  import { useEffect, useState } from "react";
@@ -0,0 +1,9 @@
1
+ import {
2
+ ExportDialog
3
+ } from "./chunk-GFK4TCKN.js";
4
+ import "./chunk-H3BK5MK6.js";
5
+ import "./chunk-6MSHXQNB.js";
6
+ import "./chunk-VJPGZCFS.js";
7
+ export {
8
+ ExportDialog
9
+ };
@@ -16,8 +16,9 @@ import {
16
16
  buildExportFilename
17
17
  } from "./chunk-5O5YTOXG.js";
18
18
  import {
19
+ OpenAfterSaveCheckbox,
19
20
  loadTransformStyleSummaries
20
- } from "./chunk-YBEYTVU2.js";
21
+ } from "./chunk-H3BK5MK6.js";
21
22
  import {
22
23
  FORMAT_EXTENSIONS,
23
24
  loadLastExportOptions,
@@ -26,7 +27,7 @@ import {
26
27
  } from "./chunk-6MSHXQNB.js";
27
28
  import {
28
29
  Dialog
29
- } from "./chunk-56KO2LYN.js";
30
+ } from "./chunk-VJPGZCFS.js";
30
31
 
31
32
  // src/Export/ExportToolbarControls.tsx
32
33
  import {
@@ -45,22 +46,40 @@ import {
45
46
  } from "@bendyline/squisq-editor-react";
46
47
  import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
47
48
  import { getThemeSummaries } from "@bendyline/squisq/schemas";
49
+
50
+ // src/preferences/export.ts
51
+ var STORAGE_KEY = "docblocks:openAfterExport";
52
+ function loadOpenAfterExport() {
53
+ try {
54
+ return localStorage.getItem(STORAGE_KEY) === "true";
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+ function saveOpenAfterExport(value) {
60
+ try {
61
+ localStorage.setItem(STORAGE_KEY, String(value));
62
+ } catch {
63
+ }
64
+ }
65
+
66
+ // src/Export/ExportToolbarControls.tsx
48
67
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
49
68
  var ExportDialog = lazy(
50
- () => import("./ExportDialog-JRVZCG3J.js").then((module) => ({ default: module.ExportDialog }))
69
+ () => import("./ExportDialog-LGWBXIWN.js").then((module) => ({ default: module.ExportDialog }))
51
70
  );
52
71
  var ShareDialog = lazy(
53
- () => import("./ShareDialog-XUA2OVGI.js").then((module) => ({ default: module.ShareDialog }))
72
+ () => import("./ShareDialog-PF7D7ORP.js").then((module) => ({ default: module.ShareDialog }))
54
73
  );
55
74
  var runExportModulePromise = null;
56
75
  function loadRunExportModule() {
57
- runExportModulePromise ?? (runExportModulePromise = import("./run-export-CS3HRNAZ.js"));
76
+ runExportModulePromise ?? (runExportModulePromise = import("./run-export-SPNNH32M.js"));
58
77
  return runExportModulePromise;
59
78
  }
60
79
  var ExportCancelledError = class extends Error {
61
80
  };
62
81
  var AudioExportDialog = lazy(
63
- () => import("./AudioExportDialog-F23QXNPB.js").then((module) => ({
82
+ () => import("./AudioExportDialog-IOYIEAER.js").then((module) => ({
64
83
  default: module.AudioExportDialog
65
84
  }))
66
85
  );
@@ -159,6 +178,13 @@ function ExportToolbarControls({
159
178
  const [transformSummaries, setTransformSummaries] = useState([]);
160
179
  const [exporting, setExporting] = useState(false);
161
180
  const [exportError, setExportError] = useState(null);
181
+ const [openAfterSave, setOpenAfterSave] = useState(loadOpenAfterExport);
182
+ const [openError, setOpenError] = useState(null);
183
+ const handleOpenAfterSaveChange = useCallback((checked) => {
184
+ setOpenAfterSave(checked);
185
+ saveOpenAfterExport(checked);
186
+ }, []);
187
+ const [exportNotice, setExportNotice] = useState(null);
162
188
  const [destinationTarget, setDestinationTarget] = useState(null);
163
189
  const [quickDestination, setQuickDestination] = useState(null);
164
190
  const [quickDestinationPending, setQuickDestinationPending] = useState(false);
@@ -300,6 +326,7 @@ function ExportToolbarControls({
300
326
  setVideoModalOpen(false);
301
327
  setVideoDoc(null);
302
328
  setVideoLoadError(null);
329
+ setOpenError(null);
303
330
  }, []);
304
331
  const handleOptionsChange = useCallback(
305
332
  (options) => {
@@ -327,13 +354,28 @@ function ExportToolbarControls({
327
354
  const savedTarget = await destinationAdapter.saveBlob(blob, filename, target);
328
355
  if (!savedTarget) throw new ExportCancelledError();
329
356
  setDestinationTarget(savedTarget);
357
+ return savedTarget;
330
358
  },
331
359
  [destinationAdapter]
332
360
  );
361
+ const openSaved = useCallback(
362
+ async (target) => {
363
+ if (!openAfterSave || !destinationAdapter?.openSaved) return;
364
+ try {
365
+ await destinationAdapter.openSaved(target);
366
+ } catch (caught) {
367
+ const detail = hostErrorDetail(caught);
368
+ setOpenError(`The file was saved, but it couldn't be opened.${detail ? ` ${detail}` : ""}`);
369
+ }
370
+ },
371
+ [destinationAdapter, openAfterSave]
372
+ );
333
373
  const handleExport = useCallback(
334
374
  async (opts) => {
335
375
  setExporting(true);
336
376
  setExportError(null);
377
+ setOpenError(null);
378
+ setExportNotice(null);
337
379
  try {
338
380
  let exportTarget = destinationTarget;
339
381
  if (!fileShare && destinationAdapter?.pickBeforeSave) {
@@ -343,7 +385,8 @@ function ExportToolbarControls({
343
385
  setDestinationTarget(exportTarget);
344
386
  }
345
387
  const { runExport } = await loadRunExportModule();
346
- await runExport(
388
+ let savedTarget;
389
+ const result = await runExport(
347
390
  markdownSource,
348
391
  selectedFile,
349
392
  opts,
@@ -351,9 +394,13 @@ function ExportToolbarControls({
351
394
  fileShare && destinationAdapter?.shareBlob ? async (blob, filename) => {
352
395
  const outcome = await destinationAdapter.shareBlob(blob, filename);
353
396
  if (outcome === "cancelled") throw new ExportCancelledError();
354
- } : destinationAdapter ? (blob, filename) => saveToDestination(blob, filename, exportTarget) : saveBlob
397
+ } : destinationAdapter ? async (blob, filename) => {
398
+ savedTarget = await saveToDestination(blob, filename, exportTarget);
399
+ } : saveBlob
355
400
  );
356
401
  setDialogOpen(false);
402
+ if (result.warnings.length > 0) setExportNotice(result.warnings);
403
+ if (savedTarget) await openSaved(savedTarget);
357
404
  } catch (caught) {
358
405
  if (!(caught instanceof ExportCancelledError)) setExportError(exportErrorMessage(caught));
359
406
  } finally {
@@ -368,7 +415,8 @@ function ExportToolbarControls({
368
415
  destinationAdapter,
369
416
  destinationTarget,
370
417
  saveToDestination,
371
- saveBlob
418
+ saveBlob,
419
+ openSaved
372
420
  ]
373
421
  );
374
422
  const handleQuickExport = useCallback(async () => {
@@ -376,6 +424,8 @@ function ExportToolbarControls({
376
424
  closeMenu(true);
377
425
  setExporting(true);
378
426
  setExportError(null);
427
+ setOpenError(null);
428
+ setExportNotice(null);
379
429
  try {
380
430
  saveExportOptions(lastOptions);
381
431
  let quickTarget = null;
@@ -390,13 +440,18 @@ function ExportToolbarControls({
390
440
  }
391
441
  }
392
442
  const { runExport } = await loadRunExportModule();
393
- await runExport(
443
+ let savedTarget;
444
+ const result = await runExport(
394
445
  markdownSource,
395
446
  selectedFile,
396
447
  lastOptions,
397
448
  mediaContainer,
398
- destinationAdapter ? async (blob, filename) => saveToDestination(blob, filename, quickTarget) : saveBlob
449
+ destinationAdapter ? async (blob, filename) => {
450
+ savedTarget = await saveToDestination(blob, filename, quickTarget);
451
+ } : saveBlob
399
452
  );
453
+ if (result.warnings.length > 0) setExportNotice(result.warnings);
454
+ if (savedTarget) await openSaved(savedTarget);
400
455
  } catch (caught) {
401
456
  if (!(caught instanceof ExportCancelledError)) setExportError(exportErrorMessage(caught));
402
457
  } finally {
@@ -411,14 +466,26 @@ function ExportToolbarControls({
411
466
  saveBlob,
412
467
  saveToDestination,
413
468
  selectedFile,
414
- quickDestinationTarget
469
+ quickDestinationTarget,
470
+ openSaved
415
471
  ]);
416
472
  const handleDismissExportError = useCallback(() => {
417
473
  setExportError(null);
418
474
  }, []);
475
+ const handleDismissExportNotice = useCallback(() => {
476
+ setExportNotice(null);
477
+ }, []);
419
478
  const videoSaveOutput = useMemo(
420
- () => destinationAdapter ? createImageSaveOutput(destinationAdapter) : void 0,
421
- [destinationAdapter]
479
+ () => destinationAdapter ? createImageSaveOutput({
480
+ ...destinationAdapter,
481
+ saveBlob: async (blob, filename, target) => {
482
+ setOpenError(null);
483
+ const saved = await destinationAdapter.saveBlob(blob, filename, target);
484
+ if (saved) await openSaved(saved);
485
+ return saved;
486
+ }
487
+ }) : void 0,
488
+ [destinationAdapter, openSaved]
422
489
  );
423
490
  const LoadedVideoExportModal = videoModules?.Modal;
424
491
  const showAnimatedGifExport = showVideoExport && typeof ffmpegWasm?.coreURL === "string" && ffmpegWasm.coreURL.length > 0;
@@ -598,6 +665,7 @@ function ExportToolbarControls({
598
665
  documentThemes,
599
666
  exporting,
600
667
  error: exportError,
668
+ openAfterSave: !fileShare && destinationAdapter?.openSaved ? { checked: openAfterSave, onChange: handleOpenAfterSaveChange } : void 0,
601
669
  destination: destinationAdapter && destinationAdapter.showDestination !== false ? {
602
670
  value: destinationTarget?.displayPath ?? "",
603
671
  onPick: handlePickDestination,
@@ -636,6 +704,28 @@ function ExportToolbarControls({
636
704
  children: /* @__PURE__ */ jsx("p", { className: "db-export-error", role: "alert", children: exportError })
637
705
  }
638
706
  ),
707
+ openError && !videoModalOpen && /* @__PURE__ */ jsx(Dialog, { title: "Open saved export", onClose: () => setOpenError(null), children: /* @__PURE__ */ jsx("p", { role: "alert", children: openError }) }),
708
+ exportNotice && !dialogOpen && !exportError && !openError && /* @__PURE__ */ jsx(
709
+ Dialog,
710
+ {
711
+ title: "Export Document",
712
+ onClose: handleDismissExportNotice,
713
+ footer: /* @__PURE__ */ jsx(
714
+ "button",
715
+ {
716
+ type: "button",
717
+ className: "db-export-btn db-export-btn--secondary",
718
+ onClick: handleDismissExportNotice,
719
+ children: "Close"
720
+ }
721
+ ),
722
+ children: /* @__PURE__ */ jsxs("div", { role: "status", children: [
723
+ /* @__PURE__ */ jsx("p", { children: "The file was saved, but not everything in the document could be included." }),
724
+ exportNotice.map((warning) => /* @__PURE__ */ jsx("p", { children: warning }, warning)),
725
+ /* @__PURE__ */ jsx("p", { children: "Export to Word, HTML or EPUB to keep every character." })
726
+ ] })
727
+ }
728
+ ),
639
729
  shareDialogOpen && /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
640
730
  ShareDialog,
641
731
  {
@@ -662,6 +752,16 @@ function ExportToolbarControls({
662
752
  mediaEditRenders,
663
753
  colorScheme,
664
754
  uiPalette: videoExportPalette,
755
+ saveOptions: destinationAdapter?.openSaved ? /* @__PURE__ */ jsxs(Fragment, { children: [
756
+ /* @__PURE__ */ jsx(
757
+ OpenAfterSaveCheckbox,
758
+ {
759
+ checked: openAfterSave,
760
+ onChange: handleOpenAfterSaveChange
761
+ }
762
+ ),
763
+ openError && /* @__PURE__ */ jsx("p", { role: "alert", children: openError })
764
+ ] }) : void 0,
665
765
  defaultConfig: {
666
766
  audioPolicy: "best-effort",
667
767
  ...ffmpegWasm ? { ffmpegWasm } : {},
@@ -14,7 +14,7 @@ import {
14
14
  } from "./chunk-GMA4KGGO.js";
15
15
  import {
16
16
  Dialog
17
- } from "./chunk-56KO2LYN.js";
17
+ } from "./chunk-VJPGZCFS.js";
18
18
 
19
19
  // src/Git/GitStatusBar.tsx
20
20
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  SHELL_SHORTCUTS,
3
3
  isApplePlatform
4
- } from "./chunk-K7XJDJUJ.js";
4
+ } from "./chunk-ACQF63SB.js";
5
5
  import {
6
6
  Dialog
7
- } from "./chunk-56KO2LYN.js";
7
+ } from "./chunk-VJPGZCFS.js";
8
8
 
9
9
  // src/DocBlocksShell/KeyboardShortcutsDialog.tsx
10
10
  import { Fragment, useId, useMemo } from "react";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  PresentationMaker
3
- } from "./chunk-SSB43524.js";
4
- import "./chunk-NAH52736.js";
3
+ } from "./chunk-PNZFIIWW.js";
4
+ import "./chunk-7V34VUK6.js";
5
5
  import "./chunk-K4I3NJFL.js";
6
- import "./chunk-56KO2LYN.js";
6
+ import "./chunk-VJPGZCFS.js";
7
7
  export {
8
8
  PresentationMaker
9
9
  };
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-ARATARVJ.js";
7
7
  import {
8
8
  Dialog
9
- } from "./chunk-56KO2LYN.js";
9
+ } from "./chunk-VJPGZCFS.js";
10
10
 
11
11
  // src/QuickOpen/QuickOpenDialog.tsx
12
12
  import { useEffect, useId, useMemo, useRef, useState } from "react";
@@ -14,8 +14,7 @@ type AccentColor = (typeof ACCENT_COLORS)[number];
14
14
  * face: the Hanken Grotesk variable binary every surface already ships for
15
15
  * documents set in it, and the sans docblocks.com uses. The app and the site
16
16
  * then read as one product, and the interface renders identically on every
17
- * OS — which is also what lets the visual-regression suite compare one set of
18
- * baselines across platforms at all.
17
+ * OS.
19
18
  *
20
19
  * `system` resolves `system-ui`, for anyone who would rather DocBlocks looked
21
20
  * native on each platform; the chrome then fetches no font.
@@ -35,7 +34,7 @@ declare function saveInterfaceFont(value: InterfaceFontPreference): void;
35
34
  * (the wire/persisted contract) and the `docblocks.writeCanvasFontScheme` enum
36
35
  * in the VS Code `package.json`; a guard test keeps the three in sync.
37
36
  */
38
- type WriteCanvasFontScheme = 'theme' | 'serif-sans' | 'sans-sans' | 'serif-serif' | 'pt-serif' | 'hanken' | 'playfair-pt-serif' | 'hanken-lora' | 'dm-serif-dm-sans' | 'inter';
37
+ type WriteCanvasFontScheme = 'theme' | 'serif-sans' | 'sans-sans' | 'serif-serif' | 'pt-serif' | 'hanken' | 'playfair-pt-serif' | 'hanken-lora' | 'dm-serif-dm-sans' | 'inter' | 'inconsolata';
39
38
  type WriteCanvasFontSchemeGroup = 'theme' | 'system' | 'curated';
40
39
  interface WriteCanvasFontSchemeOption {
41
40
  id: WriteCanvasFontScheme;
@@ -116,8 +115,7 @@ interface InterfaceFontSettingsProps {
116
115
  * The default follows the operating system, which is what almost everyone
117
116
  * should keep. The bundled option exists for two reasons worth stating in the
118
117
  * UI: identical typography across machines, and a rendering that is the same
119
- * everywhere — which is what lets the visual-regression suite compare one set
120
- * of baselines across operating systems at all.
118
+ * on every operating system.
121
119
  */
122
120
  declare function InterfaceFontSettings({ value, onChange, name, }: InterfaceFontSettingsProps): react_jsx_runtime.JSX.Element;
123
121
  interface AccentColorSettingsProps {
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-SPUPWCSI.js";
7
7
  import {
8
8
  Dialog
9
- } from "./chunk-56KO2LYN.js";
9
+ } from "./chunk-VJPGZCFS.js";
10
10
 
11
11
  // src/Export/ShareDialog.tsx
12
12
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-GMA4KGGO.js";
16
16
  import {
17
17
  Dialog
18
- } from "./chunk-56KO2LYN.js";
18
+ } from "./chunk-VJPGZCFS.js";
19
19
 
20
20
  // src/Speech/SpeechToolbarControl.tsx
21
21
  import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-K4I3NJFL.js";
11
11
  import {
12
12
  Dialog
13
- } from "./chunk-56KO2LYN.js";
13
+ } from "./chunk-VJPGZCFS.js";
14
14
 
15
15
  // src/Speech/TextFromNarration.tsx
16
16
  import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
@@ -4,24 +4,53 @@ import {
4
4
 
5
5
  // src/Export/run-export.ts
6
6
  import {
7
+ isSvgImageSource,
7
8
  parseFrontmatter,
8
9
  parseMarkdown,
9
10
  readFrontmatterThemeId,
10
- splitFrontmatterBlock
11
+ splitFrontmatterBlock,
12
+ svgSourceFromBytes
11
13
  } from "@bendyline/squisq/markdown";
14
+ async function diagramRendererFor(overrides) {
15
+ if (overrides.diagramRenderer) return overrides.diagramRenderer;
16
+ const module = await import("@bendyline/squisq-react/diagram-pictures");
17
+ return module.createDiagramPictureRenderer();
18
+ }
12
19
  async function pictureDiagrams(doc, overrides, kinds) {
13
20
  try {
14
- const [{ rasterizeDiagrams }, render] = await Promise.all([
21
+ const [{ rasterizeDiagrams, EXPORT_DIAGRAM_KINDS }, render] = await Promise.all([
15
22
  import("@bendyline/squisq-formats/diagrams"),
16
- overrides.diagramRenderer ?? import("@bendyline/squisq-react/diagram-pictures").then(
17
- (module) => module.createDiagramPictureRenderer()
18
- )
23
+ diagramRendererFor(overrides)
19
24
  ]);
20
- return await rasterizeDiagrams(doc, render, { kinds, urlPrefix: "docblocks-diagram" });
25
+ return await rasterizeDiagrams(doc, render, {
26
+ kinds: kinds === "all" ? EXPORT_DIAGRAM_KINDS : kinds,
27
+ urlPrefix: "docblocks-diagram"
28
+ });
21
29
  } catch {
22
- return { markdownDoc: doc, images: /* @__PURE__ */ new Map(), mermaid: /* @__PURE__ */ new Map() };
30
+ return { markdownDoc: doc, images: /* @__PURE__ */ new Map(), mermaid: /* @__PURE__ */ new Map(), svg: /* @__PURE__ */ new Map() };
23
31
  }
24
32
  }
33
+ async function pictureSvgAttachments(attachments, overrides) {
34
+ const pictures = /* @__PURE__ */ new Map();
35
+ const sources = /* @__PURE__ */ new Set();
36
+ for (const [url, data] of attachments) {
37
+ if (isSvgImageSource(url)) sources.add(svgSourceFromBytes(data).trim());
38
+ }
39
+ if (sources.size === 0) return pictures;
40
+ try {
41
+ const render = await diagramRendererFor(overrides);
42
+ for (const source of sources) {
43
+ const diagram = { kind: "svg", markdown: "", source, template: "", alt: "" };
44
+ const picture = await render(diagram).catch(() => null);
45
+ if (picture && picture.width > 0 && picture.height > 0) pictures.set(source, picture);
46
+ }
47
+ } catch {
48
+ }
49
+ return pictures;
50
+ }
51
+ function imageBytes(images) {
52
+ return [...images].map(([url, image]) => [url, image.data]);
53
+ }
25
54
  var MIME_TYPES = {
26
55
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
27
56
  pdf: "application/pdf",
@@ -58,6 +87,19 @@ function buildFilename(selectedFile, format) {
58
87
  return base + FORMAT_EXTENSIONS[format];
59
88
  }
60
89
  async function runExport(markdown, selectedFile, options, mediaContainer, saveBlob, converterOverrides = {}) {
90
+ const warnings = [];
91
+ await exportDocument(
92
+ markdown,
93
+ selectedFile,
94
+ options,
95
+ mediaContainer,
96
+ saveBlob,
97
+ converterOverrides,
98
+ warnings
99
+ );
100
+ return { warnings };
101
+ }
102
+ async function exportDocument(markdown, selectedFile, options, mediaContainer, saveBlob, converterOverrides, warnings) {
61
103
  const filename = buildFilename(selectedFile, options.format);
62
104
  const themeId = exporterThemeId(markdown, options.themeId);
63
105
  if (options.format === "md") {
@@ -72,26 +114,36 @@ async function runExport(markdown, selectedFile, options, mediaContainer, saveBl
72
114
  if (options.format === "docx") {
73
115
  const { markdownDocToDocx } = await import("@bendyline/squisq-formats/docx");
74
116
  const pictured = await pictureDiagrams(doc, converterOverrides);
75
- const images = new Map([
76
- ...mediaContainer ? await resolveImages(doc, mediaContainer) : [],
77
- ...pictured.images
78
- ]);
79
- const buf = await markdownDocToDocx(pictured.markdownDoc, { themeId, images });
117
+ const { svg } = await pictureDiagrams(doc, converterOverrides, ["svg"]);
118
+ const attachments = await resolveImages(doc, mediaContainer);
119
+ const svgAttachments = await pictureSvgAttachments(imageBytes(attachments), converterOverrides);
120
+ const images = new Map([...attachments, ...pictured.images]);
121
+ const buf = await markdownDocToDocx(pictured.markdownDoc, {
122
+ themeId,
123
+ images,
124
+ svgIslandFallbacks: new Map([...svg, ...svgAttachments])
125
+ });
80
126
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.docx }), filename, saveBlob);
81
127
  return;
82
128
  }
83
129
  if (options.format === "pdf") {
84
130
  const { markdownDocToPdf } = await import("@bendyline/squisq-formats/pdf");
85
- const pictured = await pictureDiagrams(doc, converterOverrides);
86
- const images = new Map([
87
- ...mediaContainer ? await resolveImages(doc, mediaContainer) : [],
88
- ...pictured.images
89
- ]);
131
+ const pictured = await pictureDiagrams(doc, converterOverrides, "all");
132
+ const attachments = await resolveImages(doc, mediaContainer);
133
+ const svgPictures = await pictureSvgAttachments(imageBytes(attachments), converterOverrides);
134
+ const images = new Map([...withSvgPictures(attachments, svgPictures), ...pictured.images]);
90
135
  const buf = await markdownDocToPdf(pictured.markdownDoc, {
91
136
  themeId,
92
137
  pageSize: options.pageSize,
93
- images
138
+ images,
139
+ // Check fidelity before any download or destination write occurs.
140
+ onWarning: (message) => warnings.push(message)
94
141
  });
142
+ if (warnings.length > 0) {
143
+ throw new Error(
144
+ "PDF was not saved because its fonts cannot preserve all of this document\u2019s text. Export as Word, HTML, or EPUB to keep every character. " + warnings.join(" ")
145
+ );
146
+ }
95
147
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.pdf }), filename, saveBlob);
96
148
  return;
97
149
  }
@@ -110,21 +162,37 @@ async function runExport(markdown, selectedFile, options, mediaContainer, saveBl
110
162
  }
111
163
  if (options.format === "pptx") {
112
164
  const pptxModule = converterOverrides.docToPptx ? Promise.resolve({ docToPptx: converterOverrides.docToPptx }) : import("@bendyline/squisq-formats/pptx");
113
- const [{ markdownToDoc }, { applyTransform }, { buildPreviewDoc }, { docToPptx }] = await Promise.all([
165
+ const [
166
+ { markdownToDoc },
167
+ { applyTransform, PRESENTATION_KEY },
168
+ { buildPreviewDoc },
169
+ { docToPptx }
170
+ ] = await Promise.all([
114
171
  import("@bendyline/squisq/doc"),
115
172
  import("@bendyline/squisq/transform"),
116
173
  import("@bendyline/squisq-editor-react"),
117
174
  pptxModule
118
175
  ]);
119
176
  const baseDoc = markdownToDoc(doc);
120
- const transformed = applyTransform(baseDoc, options.transformStyle);
121
- const enrichedDoc = buildPreviewDoc(transformed.doc);
177
+ if (!options.transformStyle) {
178
+ baseDoc.frontmatter = { ...baseDoc.frontmatter };
179
+ delete baseDoc.frontmatter["squisq-transform"];
180
+ delete baseDoc.frontmatter["transform-style"];
181
+ delete baseDoc.frontmatter[PRESENTATION_KEY];
182
+ }
183
+ const contentDoc = options.transformStyle ? applyTransform(baseDoc, options.transformStyle).doc : baseDoc;
184
+ const enrichedDoc = buildPreviewDoc(contentDoc);
122
185
  if (themeId) {
123
186
  enrichedDoc.themeId = themeId;
124
187
  }
125
188
  const images = mediaContainer ? await resolveImageData(doc, mediaContainer) : void 0;
126
- const { mermaid } = await pictureDiagrams(doc, converterOverrides, ["mermaid"]);
127
- const buf = await docToPptx(enrichedDoc, { themeId, images, diagramImages: mermaid });
189
+ const { mermaid, svg } = await pictureDiagrams(doc, converterOverrides, ["mermaid", "svg"]);
190
+ const svgAttachments = await pictureSvgAttachments(images ?? [], converterOverrides);
191
+ const buf = await docToPptx(enrichedDoc, {
192
+ themeId,
193
+ images,
194
+ diagramImages: new Map([...mermaid, ...svg, ...svgAttachments])
195
+ });
128
196
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.pptx }), filename, saveBlob);
129
197
  return;
130
198
  }
@@ -288,8 +356,9 @@ function collectMarkdownImageUrls(doc) {
288
356
  return Array.from(new Set(urls));
289
357
  }
290
358
  async function resolveImages(doc, container) {
291
- const urls = collectMarkdownImageUrls(doc);
292
359
  const map = /* @__PURE__ */ new Map();
360
+ if (!container) return map;
361
+ const urls = collectMarkdownImageUrls(doc);
293
362
  for (const url of urls) {
294
363
  if (map.has(url)) continue;
295
364
  if (isExternalUrl(url)) continue;
@@ -301,6 +370,22 @@ async function resolveImages(doc, container) {
301
370
  }
302
371
  return map;
303
372
  }
373
+ function withSvgPictures(images, pictures) {
374
+ const result = /* @__PURE__ */ new Map();
375
+ for (const [url, image] of images) {
376
+ const picture = isSvgImageSource(url) ? pictures.get(svgSourceFromBytes(image.data).trim()) : void 0;
377
+ result.set(
378
+ url,
379
+ picture ? {
380
+ data: picture.data,
381
+ contentType: "image/png",
382
+ width: picture.width,
383
+ height: picture.height
384
+ } : image
385
+ );
386
+ }
387
+ return result;
388
+ }
304
389
  async function resolveImageData(doc, container) {
305
390
  const urls = collectMarkdownImageUrls(doc);
306
391
  const map = /* @__PURE__ */ new Map();