@bendyline/docblocks-react 2.1.0 → 2.1.2

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 (49) hide show
  1. package/README.md +12 -3
  2. package/dist/ExportDialog-VYATD5UC.js +9 -0
  3. package/dist/{ExportToolbarControls-GHAOJMH6.js → ExportToolbarControls-C46GMPKC.js} +70 -41
  4. package/dist/{GitToolbarControl-GNMRVKWQ.js → GitToolbarControl-FW3BZFXV.js} +2 -3
  5. package/dist/{GitUI-VHOBQY43.js → GitUI-GMH3PAYL.js} +3 -4
  6. package/dist/LazyEditorShell-LU4QFPDX.js +5 -0
  7. package/dist/{ShareDialog-63ENBEDZ.js → ShareDialog-PAMOQQFB.js} +0 -1
  8. package/dist/{chunk-UNYZQXWR.js → chunk-3CZ27FOA.js} +45 -10
  9. package/dist/{chunk-FEWQUQML.js → chunk-3QSZY74C.js} +1 -2
  10. package/dist/chunk-BI7NVU6T.js +31 -0
  11. package/dist/{chunk-VHDP4IPL.js → chunk-BPGEBGAN.js} +0 -1
  12. package/dist/{chunk-DD4VFJQI.js → chunk-EBWYGTN7.js} +2 -1
  13. package/dist/{chunk-ZI56BNB7.js → chunk-GHQ4X7KM.js} +27 -7
  14. package/dist/{chunk-5A4KEWKT.js → chunk-HFYPTTCH.js} +0 -1
  15. package/dist/{chunk-FMGTHSTX.js → chunk-LG6HAWCK.js} +0 -1
  16. package/dist/{chunk-57ZJJOC5.js → chunk-TFMO7EVO.js} +0 -1
  17. package/dist/{chunk-4XXSWN3T.js → chunk-YBEYTVU2.js} +0 -1
  18. package/dist/editor.js +1 -2
  19. package/dist/export/index.d.ts +18 -8
  20. package/dist/export/index.js +10 -7
  21. package/dist/index.d.ts +16 -7
  22. package/dist/index.js +359 -103
  23. package/dist/run-export-Z5BVFLIR.js +9 -0
  24. package/dist/settings/index.js +2 -3
  25. package/package.json +8 -8
  26. package/src/styles/docblocks.css +485 -9
  27. package/dist/ExportDialog-7LDIXJZ3.js +0 -10
  28. package/dist/ExportDialog-7LDIXJZ3.js.map +0 -1
  29. package/dist/ExportToolbarControls-GHAOJMH6.js.map +0 -1
  30. package/dist/GitToolbarControl-GNMRVKWQ.js.map +0 -1
  31. package/dist/GitUI-VHOBQY43.js.map +0 -1
  32. package/dist/LazyEditorShell-EF2BWRXU.js +0 -6
  33. package/dist/LazyEditorShell-EF2BWRXU.js.map +0 -1
  34. package/dist/ShareDialog-63ENBEDZ.js.map +0 -1
  35. package/dist/chunk-4XXSWN3T.js.map +0 -1
  36. package/dist/chunk-57ZJJOC5.js.map +0 -1
  37. package/dist/chunk-5A4KEWKT.js.map +0 -1
  38. package/dist/chunk-DD4VFJQI.js.map +0 -1
  39. package/dist/chunk-FEWQUQML.js.map +0 -1
  40. package/dist/chunk-FMGTHSTX.js.map +0 -1
  41. package/dist/chunk-UNYZQXWR.js.map +0 -1
  42. package/dist/chunk-VHDP4IPL.js.map +0 -1
  43. package/dist/chunk-ZI56BNB7.js.map +0 -1
  44. package/dist/editor.js.map +0 -1
  45. package/dist/export/index.js.map +0 -1
  46. package/dist/index.js.map +0 -1
  47. package/dist/run-export-ICTZITXC.js +0 -10
  48. package/dist/run-export-ICTZITXC.js.map +0 -1
  49. package/dist/settings/index.js.map +0 -1
package/README.md CHANGED
@@ -38,11 +38,19 @@ export default defineConfig({ worker: { format: 'es' } });
38
38
  The canonical DocBlocks experience in one component — file explorer, workspace picker, app menu, the Squisq editor with its Editor / Markdown / Play views, and the export pipeline.
39
39
 
40
40
  ```tsx
41
- <DocBlocksShell theme="auto" logoUrl="/logo.png" />
41
+ <DocBlocksShell
42
+ theme="auto"
43
+ logoUrl="/logo.png"
44
+ ffmpegWasm={{
45
+ coreURL: '/ffmpeg-core/ffmpeg-core.js',
46
+ wasmURL: '/ffmpeg-core/ffmpeg-core.wasm',
47
+ }}
48
+ />
42
49
  ```
43
50
 
44
51
  - `theme` — `'light' | 'dark' | 'auto'` (auto follows `prefers-color-scheme`)
45
52
  - `logoUrl` — brand mark for the app menu button
53
+ - `ffmpegWasm` — optional same-origin ffmpeg core URLs; supplying them enables the built-in Animated GIF flow on cross-origin-isolated hosts
46
54
 
47
55
  Storage is abstracted behind the byte-authoritative `FileSystemProviderV2` contract from `@bendyline/docblocks/filesystem`: browser-local (IndexedDB), native folders (File System Access API), transient memory workspaces, or the Electron host. Built-in compatibility facades expose it as `provider.v2`; first-party shell and file-tree operations are v2-first.
48
56
 
@@ -80,7 +88,7 @@ and an optional initial Use mode. Opening the URL creates a session-only
80
88
  workspace; generated links warn after 4,096 characters and are capped at
81
89
  32,768 characters.
82
90
 
83
- The export flow: quick re-export of the last configuration plus the full dialog — format (PDF, Word, PowerPoint, HTML, Markdown), visual theme, and page size.
91
+ The export flow: quick re-export of the last configuration plus the full dialog — format (PDF, Word, PowerPoint, EPUB, HTML, Markdown), visual theme, and page size. Hosts that supply `ffmpegWasm` also expose Animated GIF with timing, orientation, captions, looping, and color controls.
84
92
 
85
93
  ## Hooks
86
94
 
@@ -100,7 +108,8 @@ File tree state management over any v2-capable provider — returns the tree, se
100
108
  import '@bendyline/docblocks-react/styles';
101
109
  ```
102
110
 
103
- One stylesheet (`docblocks.css`) covers all components in both themes. The package also bundles the self-hosted woff2 fonts used by document themes (see `NOTICE.md` at the repo root for licenses).
111
+ One explicit stylesheet import covers the Squisq editor and all DocBlocks components in both
112
+ themes. Theme fonts are supplied by the host application; this package does not bundle fonts.
104
113
 
105
114
  ## Where new UI belongs
106
115
 
@@ -0,0 +1,9 @@
1
+ import {
2
+ ExportDialog
3
+ } from "./chunk-3CZ27FOA.js";
4
+ import "./chunk-YBEYTVU2.js";
5
+ import "./chunk-LG6HAWCK.js";
6
+ import "./chunk-EBWYGTN7.js";
7
+ export {
8
+ ExportDialog
9
+ };
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  loadTransformStyleSummaries
3
- } from "./chunk-4XXSWN3T.js";
3
+ } from "./chunk-YBEYTVU2.js";
4
4
  import {
5
5
  Dialog
6
- } from "./chunk-FMGTHSTX.js";
6
+ } from "./chunk-LG6HAWCK.js";
7
7
  import {
8
8
  DEFAULT_OPTIONS,
9
9
  FORMAT_EXTENSIONS,
10
10
  loadLastExportOptions,
11
11
  saveExportOptions
12
- } from "./chunk-DD4VFJQI.js";
12
+ } from "./chunk-EBWYGTN7.js";
13
13
 
14
14
  // src/Export/ExportToolbarControls.tsx
15
15
  import {
@@ -26,14 +26,14 @@ import { getThemeSummaries } from "@bendyline/squisq/schemas";
26
26
  import { parseMarkdown } from "@bendyline/squisq/markdown";
27
27
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
28
28
  var ExportDialog = lazy(
29
- () => import("./ExportDialog-7LDIXJZ3.js").then((module) => ({ default: module.ExportDialog }))
29
+ () => import("./ExportDialog-VYATD5UC.js").then((module) => ({ default: module.ExportDialog }))
30
30
  );
31
31
  var ShareDialog = lazy(
32
- () => import("./ShareDialog-63ENBEDZ.js").then((module) => ({ default: module.ShareDialog }))
32
+ () => import("./ShareDialog-PAMOQQFB.js").then((module) => ({ default: module.ShareDialog }))
33
33
  );
34
34
  var runExportModulePromise = null;
35
35
  function loadRunExportModule() {
36
- runExportModulePromise ?? (runExportModulePromise = import("./run-export-ICTZITXC.js"));
36
+ runExportModulePromise ?? (runExportModulePromise = import("./run-export-Z5BVFLIR.js"));
37
37
  return runExportModulePromise;
38
38
  }
39
39
  var SHARED_USE_MODES = Object.freeze({
@@ -103,6 +103,7 @@ function ExportToolbarControls({
103
103
  destinationAdapter,
104
104
  trigger = "menu",
105
105
  showVideoExport = true,
106
+ ffmpegWasm,
106
107
  colorScheme = "light",
107
108
  videoExportPalette,
108
109
  shareBaseUrl,
@@ -113,6 +114,7 @@ function ExportToolbarControls({
113
114
  const [dialogOpen, setDialogOpen] = useState(false);
114
115
  const [shareDialogOpen, setShareDialogOpen] = useState(false);
115
116
  const [videoModalOpen, setVideoModalOpen] = useState(false);
117
+ const [videoOutputFormat, setVideoOutputFormat] = useState("mp4");
116
118
  const [videoLoading, setVideoLoading] = useState(false);
117
119
  const [videoLoadError, setVideoLoadError] = useState(null);
118
120
  const [videoModules, setVideoModules] = useState(null);
@@ -197,21 +199,36 @@ function ExportToolbarControls({
197
199
  const handleCloseShareDialog = useCallback(() => {
198
200
  setShareDialogOpen(false);
199
201
  }, []);
200
- const handleOpenVideoModal = useCallback(async () => {
201
- setMenuOpen(false);
202
- setVideoModalOpen(true);
203
- setVideoLoading(true);
204
- setVideoLoadError(null);
205
- try {
206
- const modules = videoModules ?? await loadVideoExportModules();
207
- setVideoModules(modules);
208
- setVideoDoc(modules.markdownToDoc(parseMarkdown(markdownSource)));
209
- } catch {
210
- setVideoLoadError("Video export could not be loaded.");
211
- } finally {
212
- setVideoLoading(false);
213
- }
214
- }, [markdownSource, videoModules]);
202
+ const handleOpenVideoModal = useCallback(
203
+ async (outputFormat) => {
204
+ setMenuOpen(false);
205
+ setVideoOutputFormat(outputFormat);
206
+ setVideoModalOpen(true);
207
+ setVideoLoadError(null);
208
+ if (outputFormat === "gif" && typeof SharedArrayBuffer === "undefined") {
209
+ setVideoLoading(false);
210
+ setVideoLoadError(
211
+ "Animated GIF export needs cross-origin isolation. If this app just finished installing for offline use, reload it once and try again."
212
+ );
213
+ return;
214
+ }
215
+ setVideoLoading(true);
216
+ try {
217
+ const modules = videoModules ?? await loadVideoExportModules();
218
+ setVideoModules(modules);
219
+ setVideoDoc(modules.markdownToDoc(parseMarkdown(markdownSource)));
220
+ } catch {
221
+ setVideoLoadError("Video export could not be loaded.");
222
+ } finally {
223
+ setVideoLoading(false);
224
+ }
225
+ },
226
+ [markdownSource, videoModules]
227
+ );
228
+ const handleOpenAnimatedGifFromDialog = useCallback(() => {
229
+ setDialogOpen(false);
230
+ void handleOpenVideoModal("gif");
231
+ }, [handleOpenVideoModal]);
215
232
  const handleCloseVideoModal = useCallback(() => {
216
233
  setVideoModalOpen(false);
217
234
  setVideoDoc(null);
@@ -320,6 +337,7 @@ function ExportToolbarControls({
320
337
  setExportError(null);
321
338
  }, []);
322
339
  const LoadedVideoExportModal = videoModules?.Modal;
340
+ const showAnimatedGifExport = showVideoExport && typeof ffmpegWasm?.coreURL === "string" && ffmpegWasm.coreURL.length > 0;
323
341
  return /* @__PURE__ */ jsxs(Fragment, { children: [
324
342
  initialSharedMode && /* @__PURE__ */ jsx(SharedModeInitializer, { mode: initialSharedMode }),
325
343
  trigger === "button" ? /* @__PURE__ */ jsx(
@@ -334,7 +352,7 @@ function ExportToolbarControls({
334
352
  children: /* @__PURE__ */ jsx(ExportGlyph, {})
335
353
  }
336
354
  ) : /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu", ref: menuRef, children: [
337
- /* @__PURE__ */ jsx(
355
+ /* @__PURE__ */ jsxs(
338
356
  "button",
339
357
  {
340
358
  type: "button",
@@ -344,7 +362,10 @@ function ExportToolbarControls({
344
362
  "aria-haspopup": "menu",
345
363
  "aria-expanded": menuOpen,
346
364
  title: "Export and share",
347
- children: /* @__PURE__ */ jsx(ShareGlyph, {})
365
+ children: [
366
+ /* @__PURE__ */ jsx(ShareGlyph, {}),
367
+ /* @__PURE__ */ jsx("span", { className: "db-toolbar-menu-trigger-label", children: "Export" })
368
+ ]
348
369
  }
349
370
  ),
350
371
  menuOpen && /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu-dropdown", role: "menu", children: [
@@ -387,8 +408,18 @@ function ExportToolbarControls({
387
408
  type: "button",
388
409
  role: "menuitem",
389
410
  className: "db-toolbar-menu-item",
390
- onClick: handleOpenVideoModal,
391
- children: "Export Video..."
411
+ onClick: () => void handleOpenVideoModal("mp4"),
412
+ children: "Export video..."
413
+ }
414
+ ),
415
+ showAnimatedGifExport && /* @__PURE__ */ jsx(
416
+ "button",
417
+ {
418
+ type: "button",
419
+ role: "menuitem",
420
+ className: "db-toolbar-menu-item",
421
+ onClick: () => void handleOpenVideoModal("gif"),
422
+ children: "Export animated gif..."
392
423
  }
393
424
  )
394
425
  ] })
@@ -407,6 +438,7 @@ function ExportToolbarControls({
407
438
  } : void 0,
408
439
  onExport: handleExport,
409
440
  onOptionsChange: destinationAdapter ? handleOptionsChange : void 0,
441
+ onAnimatedGifExport: showAnimatedGifExport ? handleOpenAnimatedGifFromDialog : void 0,
410
442
  onClose: handleCloseDialog
411
443
  }
412
444
  ) }),
@@ -436,21 +468,14 @@ function ExportToolbarControls({
436
468
  onClose: handleCloseShareDialog
437
469
  }
438
470
  ) }),
439
- videoModalOpen && (videoLoading || videoLoadError) && /* @__PURE__ */ jsx("div", { className: "db-dialog-overlay", children: /* @__PURE__ */ jsxs("div", { className: "db-dialog", children: [
440
- /* @__PURE__ */ jsxs("div", { className: "db-dialog-header", children: [
441
- /* @__PURE__ */ jsx("h2", { className: "db-dialog-title", children: "Export Video" }),
442
- /* @__PURE__ */ jsx(
443
- "button",
444
- {
445
- className: "db-dialog-close",
446
- onClick: handleCloseVideoModal,
447
- "aria-label": "Close",
448
- children: "\xD7"
449
- }
450
- )
451
- ] }),
452
- /* @__PURE__ */ jsx("div", { className: "db-dialog-body", children: /* @__PURE__ */ jsx("p", { className: "db-export-hint", children: videoLoadError ?? "Loading..." }) })
453
- ] }) }),
471
+ videoModalOpen && (videoLoading || videoLoadError) && /* @__PURE__ */ jsx(
472
+ Dialog,
473
+ {
474
+ title: videoOutputFormat === "gif" ? "Export Animated GIF" : "Export Video",
475
+ onClose: handleCloseVideoModal,
476
+ children: /* @__PURE__ */ jsx("p", { className: "db-export-hint", role: videoLoadError ? "alert" : void 0, children: videoLoadError ?? "Loading..." })
477
+ }
478
+ ),
454
479
  videoModalOpen && videoModules && LoadedVideoExportModal && videoDoc && !videoLoading && !videoLoadError && /* @__PURE__ */ jsx(
455
480
  LoadedVideoExportModal,
456
481
  {
@@ -458,6 +483,11 @@ function ExportToolbarControls({
458
483
  playerScript: videoModules.playerScript,
459
484
  colorScheme,
460
485
  uiPalette: videoExportPalette,
486
+ defaultConfig: {
487
+ audioPolicy: "best-effort",
488
+ ...ffmpegWasm ? { ffmpegWasm } : {},
489
+ outputFormat: videoOutputFormat
490
+ },
461
491
  onClose: handleCloseVideoModal
462
492
  }
463
493
  )
@@ -505,4 +535,3 @@ function ShareGlyph() {
505
535
  export {
506
536
  ExportToolbarControls
507
537
  };
508
- //# sourceMappingURL=ExportToolbarControls-GHAOJMH6.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  useMenuKeyboard
3
- } from "./chunk-57ZJJOC5.js";
3
+ } from "./chunk-TFMO7EVO.js";
4
4
  import {
5
5
  isFileDirty,
6
6
  useGitContext
7
- } from "./chunk-5A4KEWKT.js";
7
+ } from "./chunk-HFYPTTCH.js";
8
8
 
9
9
  // src/Git/GitToolbarControl.tsx
10
10
  import { useEffect, useRef, useState } from "react";
@@ -133,4 +133,3 @@ function BranchGlyph() {
133
133
  export {
134
134
  GitToolbarControl
135
135
  };
136
- //# sourceMappingURL=GitToolbarControl-GNMRVKWQ.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useMenuKeyboard
3
- } from "./chunk-57ZJJOC5.js";
3
+ } from "./chunk-TFMO7EVO.js";
4
4
  import {
5
5
  BADGE_GLYPHS,
6
6
  BADGE_LABELS,
@@ -8,10 +8,10 @@ import {
8
8
  formatAheadBehind,
9
9
  summarizeDirty,
10
10
  useGitContext
11
- } from "./chunk-5A4KEWKT.js";
11
+ } from "./chunk-HFYPTTCH.js";
12
12
  import {
13
13
  Dialog
14
- } from "./chunk-FMGTHSTX.js";
14
+ } from "./chunk-LG6HAWCK.js";
15
15
 
16
16
  // src/Git/GitStatusBar.tsx
17
17
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -1130,4 +1130,3 @@ function GitUI({ onOpenFile, onWorkspaceCloned }) {
1130
1130
  export {
1131
1131
  GitUI
1132
1132
  };
1133
- //# sourceMappingURL=GitUI-VHOBQY43.js.map
@@ -0,0 +1,5 @@
1
+ // src/DocBlocksShell/LazyEditorShell.ts
2
+ import { EditorShell } from "@bendyline/squisq-editor-react/shell";
3
+ export {
4
+ EditorShell as default
5
+ };
@@ -210,4 +210,3 @@ function ShareDialog({ markdown, selectedFile, baseUrl, onClose }) {
210
210
  export {
211
211
  ShareDialog
212
212
  };
213
- //# sourceMappingURL=ShareDialog-63ENBEDZ.js.map
@@ -1,23 +1,24 @@
1
1
  import {
2
2
  loadTransformStyleSummaries
3
- } from "./chunk-4XXSWN3T.js";
3
+ } from "./chunk-YBEYTVU2.js";
4
4
  import {
5
5
  Dialog
6
- } from "./chunk-FMGTHSTX.js";
6
+ } from "./chunk-LG6HAWCK.js";
7
7
  import {
8
8
  FORMAT_LABELS,
9
9
  saveExportOptions
10
- } from "./chunk-DD4VFJQI.js";
10
+ } from "./chunk-EBWYGTN7.js";
11
11
 
12
12
  // src/Export/ExportDialog.tsx
13
13
  import { useState, useCallback, useEffect, useMemo } from "react";
14
14
  import { getThemeSummaries } from "@bendyline/squisq/schemas";
15
15
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
- var FORMATS = ["pdf", "docx", "pptx", "html", "md"];
16
+ var FORMATS = ["pdf", "docx", "pptx", "epub", "html", "md"];
17
17
  var FORMAT_CHIP_LABELS = {
18
18
  pdf: "PDF",
19
19
  docx: "Word",
20
20
  pptx: "PowerPoint",
21
+ epub: "EPUB",
21
22
  html: "HTML",
22
23
  md: "Markdown"
23
24
  };
@@ -75,6 +76,7 @@ function ExportDialog({
75
76
  destination,
76
77
  onExport,
77
78
  onOptionsChange,
79
+ onAnimatedGifExport,
78
80
  onClose
79
81
  }) {
80
82
  const [format, setFormat] = useState(initial.format);
@@ -87,7 +89,7 @@ function ExportDialog({
87
89
  const [entryAsIndex, setEntryAsIndex] = useState(initial.entryAsIndex);
88
90
  const [transforms, setTransforms] = useState([]);
89
91
  const themes = getThemeSummaries();
90
- const showTheme = format === "docx" || format === "pdf" || format === "pptx" || format === "html";
92
+ const showTheme = format === "docx" || format === "pdf" || format === "pptx" || format === "epub" || format === "html";
91
93
  const showTransform = format === "pptx";
92
94
  const showPageSize = format === "pdf";
93
95
  const showHtmlOptions = format === "html";
@@ -134,9 +136,10 @@ function ExportDialog({
134
136
  onOptionsChange?.(currentOptions);
135
137
  }, [currentOptions, onOptionsChange]);
136
138
  const handleExport = useCallback(() => {
139
+ if (destination?.error) return;
137
140
  saveExportOptions(currentOptions);
138
141
  onExport(currentOptions);
139
- }, [currentOptions, onExport]);
142
+ }, [currentOptions, destination?.error, onExport]);
140
143
  return /* @__PURE__ */ jsxs(
141
144
  Dialog,
142
145
  {
@@ -160,7 +163,7 @@ function ExportDialog({
160
163
  type: "button",
161
164
  className: "db-export-btn db-export-btn--primary",
162
165
  onClick: handleExport,
163
- disabled: exporting,
166
+ disabled: exporting || Boolean(destination?.error),
164
167
  children: exporting ? "Exporting..." : "Export"
165
168
  }
166
169
  )
@@ -191,10 +194,13 @@ function ExportDialog({
191
194
  id: "db-export-path",
192
195
  className: "db-export-path-input",
193
196
  value: destination.value,
194
- readOnly: true,
197
+ onChange: destination.onChange ? (event) => destination.onChange?.(event.target.value, currentOptions) : void 0,
198
+ readOnly: !destination.onChange,
195
199
  disabled: exporting,
196
200
  spellCheck: false,
197
- autoComplete: "off"
201
+ autoComplete: "off",
202
+ "aria-invalid": Boolean(destination.error),
203
+ "aria-describedby": destination.error ? "db-export-path-error" : void 0
198
204
  }
199
205
  ),
200
206
  /* @__PURE__ */ jsx(
@@ -210,6 +216,15 @@ function ExportDialog({
210
216
  }
211
217
  )
212
218
  ] }),
219
+ destination.error && /* @__PURE__ */ jsx(
220
+ "span",
221
+ {
222
+ id: "db-export-path-error",
223
+ className: "db-export-hint db-export-hint--error",
224
+ role: "alert",
225
+ children: destination.error
226
+ }
227
+ ),
213
228
  destination.hint && /* @__PURE__ */ jsx("span", { className: "db-export-hint", children: destination.hint })
214
229
  ] }),
215
230
  showHtmlOptions && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -312,6 +327,27 @@ function ExportDialog({
312
327
  }
313
328
  )
314
329
  ] }),
330
+ onAnimatedGifExport && /* @__PURE__ */ jsxs("div", { className: "db-export-field", children: [
331
+ /* @__PURE__ */ jsx("span", { className: "db-export-label", children: "Animated media" }),
332
+ /* @__PURE__ */ jsx(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ className: "db-export-btn db-export-btn--secondary",
337
+ onClick: onAnimatedGifExport,
338
+ children: "Animated GIF..."
339
+ }
340
+ ),
341
+ /* @__PURE__ */ jsx("span", { className: "db-export-hint", children: "Opens timing, orientation, captions, looping, and color controls." })
342
+ ] }),
343
+ /* @__PURE__ */ jsxs("p", { className: "db-export-more-formats", children: [
344
+ "Need spreadsheets, ",
345
+ onAnimatedGifExport ? "" : "animated media, ",
346
+ "portable bundles, or automation?",
347
+ " ",
348
+ /* @__PURE__ */ jsx("a", { href: "https://docblocks.com/cli/", target: "_blank", rel: "noopener noreferrer", children: "More formats with the DocBlocks CLI" }),
349
+ "."
350
+ ] }),
315
351
  error && /* @__PURE__ */ jsx("p", { className: "db-export-error", role: "alert", children: error })
316
352
  ]
317
353
  }
@@ -321,4 +357,3 @@ function ExportDialog({
321
357
  export {
322
358
  ExportDialog
323
359
  };
324
- //# sourceMappingURL=chunk-UNYZQXWR.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dialog
3
- } from "./chunk-FMGTHSTX.js";
3
+ } from "./chunk-LG6HAWCK.js";
4
4
 
5
5
  // src/preferences/theme.ts
6
6
  var ACCENT_COLORS = [
@@ -253,4 +253,3 @@ export {
253
253
  AccentColorSettings,
254
254
  WriteCanvasSettingsControls
255
255
  };
256
- //# sourceMappingURL=chunk-FEWQUQML.js.map
@@ -0,0 +1,31 @@
1
+ // src/Export/export-destination.ts
2
+ function updateExportTargetExtension(targetPath, suggestedFilename) {
3
+ const nextExtension = extensionOf(suggestedFilename);
4
+ if (!nextExtension) return targetPath;
5
+ if (!targetPath.trim()) return suggestedFilename;
6
+ const suffixIndex = firstSuffixIndex(targetPath);
7
+ const pathPart = suffixIndex === -1 ? targetPath : targetPath.slice(0, suffixIndex);
8
+ const suffix = suffixIndex === -1 ? "" : targetPath.slice(suffixIndex);
9
+ const separatorIndex = Math.max(pathPart.lastIndexOf("/"), pathPart.lastIndexOf("\\"));
10
+ const dotIndex = pathPart.lastIndexOf(".");
11
+ const stem = dotIndex > separatorIndex ? pathPart.slice(0, dotIndex) : pathPart;
12
+ return `${stem}${nextExtension}${suffix}`;
13
+ }
14
+ function extensionOf(value) {
15
+ const suffixIndex = firstSuffixIndex(value);
16
+ const pathPart = suffixIndex === -1 ? value : value.slice(0, suffixIndex);
17
+ const separatorIndex = Math.max(pathPart.lastIndexOf("/"), pathPart.lastIndexOf("\\"));
18
+ const dotIndex = pathPart.lastIndexOf(".");
19
+ return dotIndex > separatorIndex ? pathPart.slice(dotIndex) : "";
20
+ }
21
+ function firstSuffixIndex(value) {
22
+ const query = value.indexOf("?");
23
+ const fragment = value.indexOf("#");
24
+ if (query === -1) return fragment;
25
+ if (fragment === -1) return query;
26
+ return Math.min(query, fragment);
27
+ }
28
+
29
+ export {
30
+ updateExportTargetExtension
31
+ };
@@ -67,4 +67,3 @@ export {
67
67
  EMPTY_DOCUMENT_PROMPTS,
68
68
  pickEmptyDocumentPrompt
69
69
  };
70
- //# sourceMappingURL=chunk-VHDP4IPL.js.map
@@ -3,6 +3,7 @@ var FORMAT_LABELS = {
3
3
  docx: "Word Document (.docx)",
4
4
  pdf: "PDF (.pdf)",
5
5
  pptx: "PowerPoint (.pptx)",
6
+ epub: "EPUB e-book (.epub)",
6
7
  md: "Markdown (.md)",
7
8
  html: "HTML (.html)"
8
9
  };
@@ -10,6 +11,7 @@ var FORMAT_EXTENSIONS = {
10
11
  docx: ".docx",
11
12
  pdf: ".pdf",
12
13
  pptx: ".pptx",
14
+ epub: ".epub",
13
15
  md: ".md",
14
16
  html: ".html"
15
17
  };
@@ -48,4 +50,3 @@ export {
48
50
  loadLastExportOptions,
49
51
  saveExportOptions
50
52
  };
51
- //# sourceMappingURL=chunk-DD4VFJQI.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FORMAT_EXTENSIONS
3
- } from "./chunk-DD4VFJQI.js";
3
+ } from "./chunk-EBWYGTN7.js";
4
4
 
5
5
  // src/Export/run-export.ts
6
6
  import { parseMarkdown } from "@bendyline/squisq/markdown";
@@ -8,6 +8,7 @@ var MIME_TYPES = {
8
8
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
9
9
  pdf: "application/pdf",
10
10
  pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
11
+ epub: "application/epub+zip",
11
12
  md: "text/markdown",
12
13
  html: "text/html"
13
14
  };
@@ -38,7 +39,7 @@ function buildExportFilename(selectedFile, options) {
38
39
  }
39
40
  return base + FORMAT_EXTENSIONS[options.format];
40
41
  }
41
- async function runExport(markdown, selectedFile, options, mediaContainer, saveBlob) {
42
+ async function runExport(markdown, selectedFile, options, mediaContainer, saveBlob, converterOverrides = {}) {
42
43
  const filename = buildFilename(selectedFile, options.format);
43
44
  const themeId = options.themeId !== "standard" ? options.themeId : void 0;
44
45
  if (options.format === "md") {
@@ -66,19 +67,29 @@ async function runExport(markdown, selectedFile, options, mediaContainer, saveBl
66
67
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.pdf }), filename, saveBlob);
67
68
  return;
68
69
  }
70
+ if (options.format === "epub") {
71
+ const { markdownDocToEpub } = await import("@bendyline/squisq-formats/epub");
72
+ const images = mediaContainer ? await resolveImageData(doc, mediaContainer) : void 0;
73
+ const buf = await markdownDocToEpub(doc, { themeId, images });
74
+ await saveExportBlob(new Blob([buf], { type: MIME_TYPES.epub }), filename, saveBlob);
75
+ return;
76
+ }
69
77
  if (options.format === "pptx") {
70
- const [{ markdownToDoc }, { applyTransform }, { docToPptx }] = await Promise.all([
78
+ const pptxModule = converterOverrides.docToPptx ? Promise.resolve({ docToPptx: converterOverrides.docToPptx }) : import("@bendyline/squisq-formats/pptx");
79
+ const [{ markdownToDoc }, { applyTransform }, { buildPreviewDoc }, { docToPptx }] = await Promise.all([
71
80
  import("@bendyline/squisq/doc"),
72
81
  import("@bendyline/squisq/transform"),
73
- import("@bendyline/squisq-formats/pptx")
82
+ import("@bendyline/squisq-editor-react"),
83
+ pptxModule
74
84
  ]);
75
85
  const baseDoc = markdownToDoc(doc);
76
86
  const transformed = applyTransform(baseDoc, options.transformStyle);
77
- const enrichedDoc = transformed.doc;
87
+ const enrichedDoc = buildPreviewDoc(transformed.doc);
78
88
  if (themeId) {
79
89
  enrichedDoc.themeId = themeId;
80
90
  }
81
- const buf = await docToPptx(enrichedDoc, { themeId });
91
+ const images = mediaContainer ? await resolveImageData(doc, mediaContainer) : void 0;
92
+ const buf = await docToPptx(enrichedDoc, { themeId, images });
82
93
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.pptx }), filename, saveBlob);
83
94
  return;
84
95
  }
@@ -242,6 +253,16 @@ async function resolveImages(doc, container) {
242
253
  }
243
254
  return map;
244
255
  }
256
+ async function resolveImageData(doc, container) {
257
+ const urls = collectMarkdownImageUrls(doc);
258
+ const map = /* @__PURE__ */ new Map();
259
+ for (const url of urls) {
260
+ if (map.has(url) || isExternalUrl(url)) continue;
261
+ const data = await container.readFile(url);
262
+ if (data) map.set(url, data);
263
+ }
264
+ return map;
265
+ }
245
266
  function basenameForBundle(selectedFile) {
246
267
  const clean = selectedFile.replace(/^\/+/, "");
247
268
  const idx = clean.lastIndexOf("/");
@@ -267,4 +288,3 @@ export {
267
288
  buildExportFilename,
268
289
  runExport
269
290
  };
270
- //# sourceMappingURL=chunk-ZI56BNB7.js.map
@@ -94,4 +94,3 @@ export {
94
94
  conflictedPaths,
95
95
  summarizeDirty
96
96
  };
97
- //# sourceMappingURL=chunk-5A4KEWKT.js.map
@@ -107,4 +107,3 @@ var FOCUSABLE_SELECTOR = [
107
107
  export {
108
108
  Dialog
109
109
  };
110
- //# sourceMappingURL=chunk-FMGTHSTX.js.map
@@ -86,4 +86,3 @@ function useMenuKeyboard(open, setOpen) {
86
86
  export {
87
87
  useMenuKeyboard
88
88
  };
89
- //# sourceMappingURL=chunk-57ZJJOC5.js.map
@@ -10,4 +10,3 @@ function loadTransformStyleSummaries() {
10
10
  export {
11
11
  loadTransformStyleSummaries
12
12
  };
13
- //# sourceMappingURL=chunk-4XXSWN3T.js.map
package/dist/editor.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  EMPTY_DOCUMENT_PROMPTS,
3
3
  pickEmptyDocumentPrompt
4
- } from "./chunk-VHDP4IPL.js";
4
+ } from "./chunk-BPGEBGAN.js";
5
5
  export {
6
6
  EMPTY_DOCUMENT_PROMPTS,
7
7
  pickEmptyDocumentPrompt
8
8
  };
9
- //# sourceMappingURL=editor.js.map