@bendyline/docblocks-react 1.1.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/README.md +37 -5
  2. package/dist/ExportDialog-7LDIXJZ3.js +10 -0
  3. package/dist/ExportDialog-7LDIXJZ3.js.map +1 -0
  4. package/dist/ExportToolbarControls-GHAOJMH6.js +508 -0
  5. package/dist/ExportToolbarControls-GHAOJMH6.js.map +1 -0
  6. package/dist/GitToolbarControl-GNMRVKWQ.js +136 -0
  7. package/dist/GitToolbarControl-GNMRVKWQ.js.map +1 -0
  8. package/dist/GitUI-VHOBQY43.js +1133 -0
  9. package/dist/GitUI-VHOBQY43.js.map +1 -0
  10. package/dist/LazyEditorShell-EF2BWRXU.js +6 -0
  11. package/dist/LazyEditorShell-EF2BWRXU.js.map +1 -0
  12. package/dist/ShareDialog-63ENBEDZ.js +213 -0
  13. package/dist/ShareDialog-63ENBEDZ.js.map +1 -0
  14. package/dist/chunk-4XXSWN3T.js +13 -0
  15. package/dist/chunk-4XXSWN3T.js.map +1 -0
  16. package/dist/chunk-57ZJJOC5.js +89 -0
  17. package/dist/chunk-57ZJJOC5.js.map +1 -0
  18. package/dist/chunk-5A4KEWKT.js +97 -0
  19. package/dist/chunk-5A4KEWKT.js.map +1 -0
  20. package/dist/chunk-DD4VFJQI.js +51 -0
  21. package/dist/chunk-DD4VFJQI.js.map +1 -0
  22. package/dist/chunk-FEWQUQML.js +256 -0
  23. package/dist/chunk-FEWQUQML.js.map +1 -0
  24. package/dist/chunk-FMGTHSTX.js +110 -0
  25. package/dist/chunk-FMGTHSTX.js.map +1 -0
  26. package/dist/chunk-UNYZQXWR.js +324 -0
  27. package/dist/chunk-UNYZQXWR.js.map +1 -0
  28. package/dist/chunk-VHDP4IPL.js +70 -0
  29. package/dist/chunk-VHDP4IPL.js.map +1 -0
  30. package/dist/chunk-ZI56BNB7.js +270 -0
  31. package/dist/chunk-ZI56BNB7.js.map +1 -0
  32. package/dist/editor.d.ts +11 -0
  33. package/dist/editor.js +9 -0
  34. package/dist/editor.js.map +1 -0
  35. package/dist/export/index.d.ts +93 -0
  36. package/dist/export/index.js +27 -0
  37. package/dist/export/index.js.map +1 -0
  38. package/dist/index.d.ts +346 -18
  39. package/dist/index.js +6169 -18
  40. package/dist/index.js.map +1 -1
  41. package/dist/run-export-ICTZITXC.js +10 -0
  42. package/dist/run-export-ICTZITXC.js.map +1 -0
  43. package/dist/settings/index.d.ts +36 -0
  44. package/dist/settings/index.js +14 -0
  45. package/dist/settings/index.js.map +1 -0
  46. package/package.json +33 -13
  47. package/src/styles/docblocks.css +2738 -132
  48. package/dist/AppMenu/AppMenu.d.ts +0 -26
  49. package/dist/AppMenu/AppMenu.d.ts.map +0 -1
  50. package/dist/AppMenu/AppMenu.js +0 -28
  51. package/dist/AppMenu/AppMenu.js.map +0 -1
  52. package/dist/AppMenu/index.d.ts +0 -3
  53. package/dist/AppMenu/index.d.ts.map +0 -1
  54. package/dist/AppMenu/index.js +0 -2
  55. package/dist/AppMenu/index.js.map +0 -1
  56. package/dist/DocBlocksShell/DocBlocksShell.d.ts +0 -36
  57. package/dist/DocBlocksShell/DocBlocksShell.d.ts.map +0 -1
  58. package/dist/DocBlocksShell/DocBlocksShell.js +0 -1351
  59. package/dist/DocBlocksShell/DocBlocksShell.js.map +0 -1
  60. package/dist/DocBlocksShell/index.d.ts +0 -3
  61. package/dist/DocBlocksShell/index.d.ts.map +0 -1
  62. package/dist/DocBlocksShell/index.js +0 -2
  63. package/dist/DocBlocksShell/index.js.map +0 -1
  64. package/dist/Export/ExportDialog.d.ts +0 -16
  65. package/dist/Export/ExportDialog.d.ts.map +0 -1
  66. package/dist/Export/ExportDialog.js +0 -158
  67. package/dist/Export/ExportDialog.js.map +0 -1
  68. package/dist/Export/ExportToolbarControls.d.ts +0 -17
  69. package/dist/Export/ExportToolbarControls.d.ts.map +0 -1
  70. package/dist/Export/ExportToolbarControls.js +0 -200
  71. package/dist/Export/ExportToolbarControls.js.map +0 -1
  72. package/dist/Export/export-options.d.ts +0 -53
  73. package/dist/Export/export-options.d.ts.map +0 -1
  74. package/dist/Export/export-options.js +0 -49
  75. package/dist/Export/export-options.js.map +0 -1
  76. package/dist/Export/index.d.ts +0 -6
  77. package/dist/Export/index.d.ts.map +0 -1
  78. package/dist/Export/index.js +0 -3
  79. package/dist/Export/index.js.map +0 -1
  80. package/dist/Export/run-export.d.ts +0 -8
  81. package/dist/Export/run-export.d.ts.map +0 -1
  82. package/dist/Export/run-export.js +0 -296
  83. package/dist/Export/run-export.js.map +0 -1
  84. package/dist/Export/transform-summaries.d.ts +0 -7
  85. package/dist/Export/transform-summaries.d.ts.map +0 -1
  86. package/dist/Export/transform-summaries.js +0 -6
  87. package/dist/Export/transform-summaries.js.map +0 -1
  88. package/dist/FileExplorer/FileExplorer.d.ts +0 -21
  89. package/dist/FileExplorer/FileExplorer.d.ts.map +0 -1
  90. package/dist/FileExplorer/FileExplorer.js +0 -140
  91. package/dist/FileExplorer/FileExplorer.js.map +0 -1
  92. package/dist/FileExplorer/FileTreeNode.d.ts +0 -18
  93. package/dist/FileExplorer/FileTreeNode.d.ts.map +0 -1
  94. package/dist/FileExplorer/FileTreeNode.js +0 -112
  95. package/dist/FileExplorer/FileTreeNode.js.map +0 -1
  96. package/dist/FileExplorer/index.d.ts +0 -6
  97. package/dist/FileExplorer/index.d.ts.map +0 -1
  98. package/dist/FileExplorer/index.js +0 -4
  99. package/dist/FileExplorer/index.js.map +0 -1
  100. package/dist/FileExplorer/useFileTree.d.ts +0 -35
  101. package/dist/FileExplorer/useFileTree.d.ts.map +0 -1
  102. package/dist/FileExplorer/useFileTree.js +0 -124
  103. package/dist/FileExplorer/useFileTree.js.map +0 -1
  104. package/dist/WorkspacePicker/WorkspacePicker.d.ts +0 -17
  105. package/dist/WorkspacePicker/WorkspacePicker.d.ts.map +0 -1
  106. package/dist/WorkspacePicker/WorkspacePicker.js +0 -63
  107. package/dist/WorkspacePicker/WorkspacePicker.js.map +0 -1
  108. package/dist/WorkspacePicker/WorkspaceSettingsButton.d.ts +0 -11
  109. package/dist/WorkspacePicker/WorkspaceSettingsButton.d.ts.map +0 -1
  110. package/dist/WorkspacePicker/WorkspaceSettingsButton.js +0 -27
  111. package/dist/WorkspacePicker/WorkspaceSettingsButton.js.map +0 -1
  112. package/dist/WorkspacePicker/WorkspaceSettingsDialog.d.ts +0 -20
  113. package/dist/WorkspacePicker/WorkspaceSettingsDialog.d.ts.map +0 -1
  114. package/dist/WorkspacePicker/WorkspaceSettingsDialog.js +0 -36
  115. package/dist/WorkspacePicker/WorkspaceSettingsDialog.js.map +0 -1
  116. package/dist/WorkspacePicker/index.d.ts +0 -3
  117. package/dist/WorkspacePicker/index.d.ts.map +0 -1
  118. package/dist/WorkspacePicker/index.js +0 -2
  119. package/dist/WorkspacePicker/index.js.map +0 -1
  120. package/dist/hooks/index.d.ts +0 -2
  121. package/dist/hooks/index.d.ts.map +0 -1
  122. package/dist/hooks/index.js +0 -2
  123. package/dist/hooks/index.js.map +0 -1
  124. package/dist/hooks/useAutoSave.d.ts +0 -15
  125. package/dist/hooks/useAutoSave.d.ts.map +0 -1
  126. package/dist/hooks/useAutoSave.js +0 -83
  127. package/dist/hooks/useAutoSave.js.map +0 -1
  128. package/dist/icons.d.ts +0 -7
  129. package/dist/icons.d.ts.map +0 -1
  130. package/dist/icons.js +0 -27
  131. package/dist/icons.js.map +0 -1
  132. package/dist/index.d.ts.map +0 -1
  133. package/dist/monaco-slim.d.ts +0 -19
  134. package/dist/monaco-slim.d.ts.map +0 -1
  135. package/dist/monaco-slim.js +0 -19
  136. package/dist/monaco-slim.js.map +0 -1
  137. package/dist/preferences/versioning.d.ts +0 -27
  138. package/dist/preferences/versioning.d.ts.map +0 -1
  139. package/dist/preferences/versioning.js +0 -62
  140. package/dist/preferences/versioning.js.map +0 -1
  141. package/src/AppMenu/AppMenu.tsx +0 -264
  142. package/src/AppMenu/index.ts +0 -2
  143. package/src/DocBlocksShell/DocBlocksShell.tsx +0 -1778
  144. package/src/DocBlocksShell/index.ts +0 -2
  145. package/src/Export/ExportDialog.tsx +0 -383
  146. package/src/Export/ExportToolbarControls.tsx +0 -313
  147. package/src/Export/export-options.ts +0 -99
  148. package/src/Export/index.ts +0 -5
  149. package/src/Export/run-export.ts +0 -349
  150. package/src/Export/transform-summaries.ts +0 -14
  151. package/src/FileExplorer/FileExplorer.tsx +0 -282
  152. package/src/FileExplorer/FileTreeNode.tsx +0 -214
  153. package/src/FileExplorer/index.ts +0 -5
  154. package/src/FileExplorer/useFileTree.ts +0 -178
  155. package/src/WorkspacePicker/WorkspacePicker.tsx +0 -137
  156. package/src/WorkspacePicker/WorkspaceSettingsButton.tsx +0 -88
  157. package/src/WorkspacePicker/WorkspaceSettingsDialog.tsx +0 -121
  158. package/src/WorkspacePicker/index.ts +0 -2
  159. package/src/fonts/fonts.css +0 -362
  160. package/src/fonts/licenses/LICENSE-cormorant-garamond.txt +0 -93
  161. package/src/fonts/licenses/LICENSE-crimson-text.txt +0 -93
  162. package/src/fonts/licenses/LICENSE-dm-sans.txt +0 -93
  163. package/src/fonts/licenses/LICENSE-dm-serif-display.txt +0 -93
  164. package/src/fonts/licenses/LICENSE-hanken-grotesk.txt +0 -94
  165. package/src/fonts/licenses/LICENSE-ibm-plex-sans.txt +0 -93
  166. package/src/fonts/licenses/LICENSE-inter.txt +0 -93
  167. package/src/fonts/licenses/LICENSE-jetbrains-mono.txt +0 -93
  168. package/src/fonts/licenses/LICENSE-lora.txt +0 -93
  169. package/src/fonts/licenses/LICENSE-merriweather.txt +0 -93
  170. package/src/fonts/licenses/LICENSE-oswald.txt +0 -93
  171. package/src/fonts/licenses/LICENSE-playfair-display.txt +0 -93
  172. package/src/fonts/licenses/LICENSE-pt-serif.txt +0 -93
  173. package/src/fonts/licenses/LICENSE-roboto.txt +0 -93
  174. package/src/fonts/licenses/LICENSE-source-serif-4.txt +0 -93
  175. package/src/fonts/woff2/cormorant-garamond-400.woff2 +0 -0
  176. package/src/fonts/woff2/cormorant-garamond-700.woff2 +0 -0
  177. package/src/fonts/woff2/crimson-text-400.woff2 +0 -0
  178. package/src/fonts/woff2/crimson-text-400i.woff2 +0 -0
  179. package/src/fonts/woff2/crimson-text-700.woff2 +0 -0
  180. package/src/fonts/woff2/dm-sans-400.woff2 +0 -0
  181. package/src/fonts/woff2/dm-sans-400i.woff2 +0 -0
  182. package/src/fonts/woff2/dm-sans-500.woff2 +0 -0
  183. package/src/fonts/woff2/dm-sans-700.woff2 +0 -0
  184. package/src/fonts/woff2/dm-serif-display-400.woff2 +0 -0
  185. package/src/fonts/woff2/hanken-grotesk-400.woff2 +0 -0
  186. package/src/fonts/woff2/hanken-grotesk-400i.woff2 +0 -0
  187. package/src/fonts/woff2/hanken-grotesk-500.woff2 +0 -0
  188. package/src/fonts/woff2/hanken-grotesk-600.woff2 +0 -0
  189. package/src/fonts/woff2/hanken-grotesk-700.woff2 +0 -0
  190. package/src/fonts/woff2/ibm-plex-sans-400.woff2 +0 -0
  191. package/src/fonts/woff2/ibm-plex-sans-400i.woff2 +0 -0
  192. package/src/fonts/woff2/ibm-plex-sans-500.woff2 +0 -0
  193. package/src/fonts/woff2/ibm-plex-sans-600.woff2 +0 -0
  194. package/src/fonts/woff2/ibm-plex-sans-700.woff2 +0 -0
  195. package/src/fonts/woff2/inter-400.woff2 +0 -0
  196. package/src/fonts/woff2/inter-400i.woff2 +0 -0
  197. package/src/fonts/woff2/inter-500.woff2 +0 -0
  198. package/src/fonts/woff2/inter-600.woff2 +0 -0
  199. package/src/fonts/woff2/inter-700.woff2 +0 -0
  200. package/src/fonts/woff2/jetbrains-mono-400.woff2 +0 -0
  201. package/src/fonts/woff2/jetbrains-mono-400i.woff2 +0 -0
  202. package/src/fonts/woff2/jetbrains-mono-700.woff2 +0 -0
  203. package/src/fonts/woff2/lora-400.woff2 +0 -0
  204. package/src/fonts/woff2/lora-400i.woff2 +0 -0
  205. package/src/fonts/woff2/lora-700.woff2 +0 -0
  206. package/src/fonts/woff2/merriweather-400.woff2 +0 -0
  207. package/src/fonts/woff2/merriweather-700.woff2 +0 -0
  208. package/src/fonts/woff2/oswald-400.woff2 +0 -0
  209. package/src/fonts/woff2/oswald-700.woff2 +0 -0
  210. package/src/fonts/woff2/playfair-display-400.woff2 +0 -0
  211. package/src/fonts/woff2/playfair-display-700.woff2 +0 -0
  212. package/src/fonts/woff2/pt-serif-400.woff2 +0 -0
  213. package/src/fonts/woff2/pt-serif-700.woff2 +0 -0
  214. package/src/fonts/woff2/roboto-400.woff2 +0 -0
  215. package/src/fonts/woff2/roboto-400i.woff2 +0 -0
  216. package/src/fonts/woff2/roboto-700.woff2 +0 -0
  217. package/src/fonts/woff2/source-serif-4-400.woff2 +0 -0
  218. package/src/fonts/woff2/source-serif-4-400i.woff2 +0 -0
  219. package/src/fonts/woff2/source-serif-4-700.woff2 +0 -0
  220. package/src/hooks/index.ts +0 -1
  221. package/src/hooks/useAutoSave.ts +0 -116
  222. package/src/icons.tsx +0 -70
  223. package/src/index.ts +0 -31
  224. package/src/monaco-slim.ts +0 -20
  225. package/src/preferences/versioning.ts +0 -69
@@ -1,99 +0,0 @@
1
- /**
2
- * Export option types and localStorage persistence for quick-export.
3
- */
4
-
5
- export type ExportFormat = 'docx' | 'pdf' | 'pptx' | 'md' | 'html';
6
-
7
- /** Visual style for HTML export. */
8
- export type HtmlStyle =
9
- /** Render via SquisqPlayer (themed, embeds player bundle). */
10
- | 'rendered'
11
- /** Plain semantic HTML — small, no JS, no playback. */
12
- | 'plain';
13
-
14
- /** Packaging for HTML export. */
15
- export type HtmlBundle =
16
- /** Single self-contained .html file (images base64-embedded). */
17
- | 'single'
18
- /** ZIP containing index.html + asset files. */
19
- | 'zip';
20
-
21
- export interface ExportOptions {
22
- format: ExportFormat;
23
- themeId: string;
24
- /** Transform style for PPTX — controls how content is segmented and styled. */
25
- transformStyle: string;
26
- /** Only applies to PDF */
27
- pageSize: 'letter' | 'a4';
28
- /** HTML rendering style. Only applies to format=html. */
29
- htmlStyle: HtmlStyle;
30
- /** HTML bundle layout. Only applies to format=html. */
31
- htmlBundle: HtmlBundle;
32
- /**
33
- * Recursive HTML bundle: walk relative `.md` links from the entry
34
- * document, render every reachable sibling/child page, and rewrite
35
- * cross-doc links from `.md` → `.html`. The output is a ZIP
36
- * regardless of `htmlBundle` because multi-doc output needs a
37
- * directory tree. Supported for both plain (`markdownDocsToPlainHtmlBundle`)
38
- * and rendered (`markdownDocsToHtmlBundle`) HTML styles. Only
39
- * meaningful when `format === 'html'`.
40
- */
41
- includeLinkedDocs: boolean;
42
- /**
43
- * Rename the entry page from `<basename>.html` to `index.html` so the
44
- * export drops straight into a static-site host that expects an
45
- * `index.html` landing page. For recursive bundles, cross-doc links
46
- * targeting the entry also rewrite to `index.html` so siblings don't
47
- * 404. For a single-doc HTML download, this just renames the file.
48
- * Only meaningful when `format === 'html'`.
49
- */
50
- entryAsIndex: boolean;
51
- }
52
-
53
- export const FORMAT_LABELS: Record<ExportFormat, string> = {
54
- docx: 'Word Document (.docx)',
55
- pdf: 'PDF (.pdf)',
56
- pptx: 'PowerPoint (.pptx)',
57
- md: 'Markdown (.md)',
58
- html: 'HTML (.html)',
59
- };
60
-
61
- export const FORMAT_EXTENSIONS: Record<ExportFormat, string> = {
62
- docx: '.docx',
63
- pdf: '.pdf',
64
- pptx: '.pptx',
65
- md: '.md',
66
- html: '.html',
67
- };
68
-
69
- const STORAGE_KEY = 'docblocks-export-options';
70
-
71
- export const DEFAULT_OPTIONS: ExportOptions = {
72
- format: 'pdf',
73
- themeId: 'standard',
74
- transformStyle: 'documentary',
75
- pageSize: 'letter',
76
- htmlStyle: 'plain',
77
- htmlBundle: 'single',
78
- includeLinkedDocs: false,
79
- entryAsIndex: false,
80
- };
81
-
82
- export function loadLastExportOptions(): ExportOptions | null {
83
- try {
84
- const raw = localStorage.getItem(STORAGE_KEY);
85
- if (!raw) return null;
86
- const parsed = JSON.parse(raw) as Partial<ExportOptions>;
87
- return { ...DEFAULT_OPTIONS, ...parsed };
88
- } catch {
89
- return null;
90
- }
91
- }
92
-
93
- export function saveExportOptions(options: ExportOptions): void {
94
- try {
95
- localStorage.setItem(STORAGE_KEY, JSON.stringify(options));
96
- } catch {
97
- // Ignore storage errors
98
- }
99
- }
@@ -1,5 +0,0 @@
1
- export { ExportToolbarControls } from './ExportToolbarControls.js';
2
- export type { ExportToolbarControlsProps } from './ExportToolbarControls.js';
3
- export { ExportDialog } from './ExportDialog.js';
4
- export type { ExportDialogProps } from './ExportDialog.js';
5
- export type { ExportFormat, ExportOptions } from './export-options.js';
@@ -1,349 +0,0 @@
1
- /**
2
- * Execute an export using @bendyline/squisq-formats.
3
- */
4
-
5
- import type {
6
- MarkdownDocument,
7
- MarkdownBlockNode,
8
- MarkdownInlineNode,
9
- } from '@bendyline/squisq/markdown';
10
- import { parseMarkdown } from '@bendyline/squisq/markdown';
11
- import type { Doc } from '@bendyline/squisq/schemas';
12
- import type { ContentContainer } from '@bendyline/squisq/storage';
13
- import type { ExportOptions, ExportFormat } from './export-options.js';
14
- import { FORMAT_EXTENSIONS } from './export-options.js';
15
-
16
- const MIME_TYPES: Record<ExportFormat, string> = {
17
- docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
18
- pdf: 'application/pdf',
19
- pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
20
- md: 'text/markdown',
21
- html: 'text/html',
22
- };
23
-
24
- /** Trigger a browser download for a Blob. */
25
- function downloadBlob(blob: Blob, filename: string): void {
26
- const url = URL.createObjectURL(blob);
27
- const a = document.createElement('a');
28
- a.href = url;
29
- a.download = filename;
30
- a.click();
31
- URL.revokeObjectURL(url);
32
- }
33
-
34
- /** Derive a filename from the selected file path and export format. */
35
- function buildFilename(selectedFile: string | null, format: ExportFormat): string {
36
- const base = selectedFile ? selectedFile.replace(/^\//, '').replace(/\.[^.]+$/, '') : 'document';
37
- return base + FORMAT_EXTENSIONS[format];
38
- }
39
-
40
- /** Run the export and trigger a download. */
41
- export async function runExport(
42
- markdown: string,
43
- selectedFile: string | null,
44
- options: ExportOptions,
45
- mediaContainer?: ContentContainer | null,
46
- ): Promise<void> {
47
- const filename = buildFilename(selectedFile, options.format);
48
- const themeId = options.themeId !== 'standard' ? options.themeId : undefined;
49
-
50
- if (options.format === 'md') {
51
- downloadBlob(new Blob([markdown], { type: MIME_TYPES.md }), filename);
52
- return;
53
- }
54
-
55
- if (options.format === 'html') {
56
- await runHtmlExport(markdown, filename, options, mediaContainer, selectedFile);
57
- return;
58
- }
59
-
60
- const doc = parseMarkdown(markdown);
61
-
62
- if (options.format === 'docx') {
63
- const { markdownDocToDocx } = await import('@bendyline/squisq-formats/docx');
64
- const images = mediaContainer ? await resolveImages(doc, mediaContainer) : undefined;
65
- const buf = await markdownDocToDocx(doc, { themeId, images });
66
- downloadBlob(new Blob([buf], { type: MIME_TYPES.docx }), filename);
67
- return;
68
- }
69
-
70
- if (options.format === 'pdf') {
71
- const { markdownDocToPdf } = await import('@bendyline/squisq-formats/pdf');
72
- const buf = await markdownDocToPdf(doc, {
73
- themeId,
74
- pageSize: options.pageSize,
75
- });
76
- downloadBlob(new Blob([buf], { type: MIME_TYPES.pdf }), filename);
77
- return;
78
- }
79
-
80
- if (options.format === 'pptx') {
81
- const [{ markdownToDoc }, { applyTransform }, { docToPptx }] = await Promise.all([
82
- import('@bendyline/squisq/doc'),
83
- import('@bendyline/squisq/transform'),
84
- import('@bendyline/squisq-formats/pptx'),
85
- ]);
86
- // Use the full transform pipeline: markdown -> Doc -> transform -> PPTX
87
- const baseDoc = markdownToDoc(doc);
88
- const transformed = applyTransform(baseDoc, options.transformStyle);
89
- const enrichedDoc = transformed.doc;
90
- if (themeId) {
91
- enrichedDoc.themeId = themeId;
92
- }
93
- const buf = await docToPptx(enrichedDoc, { themeId });
94
- downloadBlob(new Blob([buf], { type: MIME_TYPES.pptx }), filename);
95
- return;
96
- }
97
- }
98
-
99
- /** Run the HTML export branch — handles all four htmlStyle × htmlBundle combinations. */
100
- async function runHtmlExport(
101
- markdown: string,
102
- htmlFilename: string,
103
- options: ExportOptions,
104
- mediaContainer?: ContentContainer | null,
105
- selectedFile?: string | null,
106
- ): Promise<void> {
107
- const baseName = htmlFilename.replace(/\.html$/, '');
108
- const zipName = `${baseName}.zip`;
109
- // When `entryAsIndex` is on, a single-doc HTML download lands on
110
- // disk as `index.html` so it drops straight into a static host. The
111
- // recursive ZIP path doesn't care — squisq's bundle helpers handle
112
- // the inside-zip rename and the cross-doc link rewrite themselves.
113
- const singleHtmlFilename = options.entryAsIndex ? 'index.html' : htmlFilename;
114
- const themeId = options.themeId !== 'standard' ? options.themeId : undefined;
115
-
116
- // Recursive HTML bundle: when the user opted in, ask squisq to walk
117
- // this doc's relative .md links and emit a ZIP with every reachable
118
- // page rendered + cross-doc hrefs rewritten. Two pipelines:
119
- // • Plain → `markdownDocsToPlainHtmlBundle` (semantic HTML)
120
- // • Rendered → `markdownDocsToHtmlBundle` (SquisqPlayer-rendered,
121
- // shared `squisq-player.js`, Doc-tree link rewrites)
122
- // Both consume the workspace container to resolve sibling files —
123
- // `mediaContainer` is already scoped to the selected file's parent
124
- // dir, which is exactly the bundle's scope root. Falls back to the
125
- // single-doc path if either prerequisite is missing rather than
126
- // silently skipping the recursion.
127
- if (options.includeLinkedDocs && mediaContainer && selectedFile) {
128
- const entryPath = basenameForBundle(selectedFile);
129
- if (options.htmlStyle === 'rendered') {
130
- const [{ markdownDocsToHtmlBundle }, { PLAYER_BUNDLE }] = await Promise.all([
131
- import('@bendyline/squisq-formats/html'),
132
- import('@bendyline/squisq-react/standalone-source'),
133
- ]);
134
- const blob = await markdownDocsToHtmlBundle({
135
- entryPath,
136
- readDocument: (path) => readDocumentFromContainer(mediaContainer, path),
137
- readBinary: (path) => mediaContainer.readFile(path),
138
- playerScript: PLAYER_BUNDLE,
139
- title: baseName,
140
- themeId,
141
- mode: 'static',
142
- entryAsIndex: options.entryAsIndex,
143
- });
144
- downloadBlob(blob, zipName);
145
- return;
146
- }
147
- const { markdownDocsToPlainHtmlBundle } = await import('@bendyline/squisq-formats/html');
148
- const blob = await markdownDocsToPlainHtmlBundle({
149
- entryPath,
150
- readDocument: (path) => readDocumentFromContainer(mediaContainer, path),
151
- readBinary: (path) => mediaContainer.readFile(path),
152
- title: baseName,
153
- themeId,
154
- entryAsIndex: options.entryAsIndex,
155
- });
156
- downloadBlob(blob, zipName);
157
- return;
158
- }
159
-
160
- if (options.htmlStyle === 'rendered') {
161
- const [{ markdownToDoc }, { docToHtml, docToHtmlZip, collectImagePaths }, { PLAYER_BUNDLE }] =
162
- await Promise.all([
163
- import('@bendyline/squisq/doc'),
164
- import('@bendyline/squisq-formats/html'),
165
- import('@bendyline/squisq-react/standalone-source'),
166
- ]);
167
- const mdDoc = parseMarkdown(markdown);
168
- const baseDoc = markdownToDoc(mdDoc);
169
- if (themeId) baseDoc.themeId = themeId;
170
-
171
- const images = mediaContainer
172
- ? await resolveDocImages(baseDoc, mediaContainer, collectImagePaths)
173
- : undefined;
174
-
175
- if (options.htmlBundle === 'zip') {
176
- const blob = await docToHtmlZip(baseDoc, {
177
- playerScript: PLAYER_BUNDLE,
178
- images,
179
- mode: 'static',
180
- title: baseName,
181
- });
182
- downloadBlob(blob, zipName);
183
- return;
184
- }
185
-
186
- const html = docToHtml(baseDoc, {
187
- playerScript: PLAYER_BUNDLE,
188
- images,
189
- mode: 'static',
190
- title: baseName,
191
- });
192
- downloadBlob(new Blob([html], { type: MIME_TYPES.html }), singleHtmlFilename);
193
- return;
194
- }
195
-
196
- // Plain semantic HTML
197
- const mdDoc = parseMarkdown(markdown);
198
- const referencedImages = collectMarkdownImageUrls(mdDoc);
199
- const localImages = referencedImages.filter((u) => !isExternalUrl(u));
200
-
201
- if (options.htmlBundle === 'zip' && mediaContainer && localImages.length > 0) {
202
- const [{ markdownDocToPlainHtml }, { MemoryContentContainer }, { containerToZip }] =
203
- await Promise.all([
204
- import('@bendyline/squisq-formats/html'),
205
- import('@bendyline/squisq/storage'),
206
- import('@bendyline/squisq-formats/container'),
207
- ]);
208
- // Mirror the markdown's relative paths inside the zip so <img src="..."> still resolves.
209
- const html = markdownDocToPlainHtml(mdDoc, { title: baseName, themeId });
210
- const container = new MemoryContentContainer();
211
- await container.writeFile('index.html', new TextEncoder().encode(html));
212
- for (const url of localImages) {
213
- const data = await mediaContainer.readFile(url);
214
- if (!data) continue;
215
- const cleanPath = url.replace(/^\/+/, '');
216
- await container.writeFile(cleanPath, new Uint8Array(data));
217
- }
218
- const blob = await containerToZip(container);
219
- downloadBlob(blob, zipName);
220
- return;
221
- }
222
-
223
- const { markdownDocToPlainHtml } = await import('@bendyline/squisq-formats/html');
224
- // Single-file plain HTML — embed local images as base64 data URIs.
225
- const inlineMap = new Map<string, string>();
226
- if (mediaContainer) {
227
- for (const url of localImages) {
228
- const data = await mediaContainer.readFile(url);
229
- if (!data) continue;
230
- inlineMap.set(url, arrayBufferToDataUrl(data, url));
231
- }
232
- }
233
- const html = markdownDocToPlainHtml(mdDoc, {
234
- title: baseName,
235
- images: inlineMap.size > 0 ? inlineMap : undefined,
236
- themeId,
237
- });
238
- downloadBlob(new Blob([html], { type: MIME_TYPES.html }), singleHtmlFilename);
239
- }
240
-
241
- const IMAGE_MIME: Record<string, string> = {
242
- png: 'image/png',
243
- jpg: 'image/jpeg',
244
- jpeg: 'image/jpeg',
245
- gif: 'image/gif',
246
- webp: 'image/webp',
247
- bmp: 'image/bmp',
248
- svg: 'image/svg+xml',
249
- };
250
-
251
- function isExternalUrl(url: string): boolean {
252
- return (
253
- url.startsWith('http://') ||
254
- url.startsWith('https://') ||
255
- url.startsWith('data:') ||
256
- url.startsWith('blob:')
257
- );
258
- }
259
-
260
- function arrayBufferToDataUrl(data: ArrayBuffer, url: string): string {
261
- const ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase();
262
- const mime = IMAGE_MIME[ext] ?? 'application/octet-stream';
263
- // btoa requires a binary string; chunk to avoid call-stack issues on large files.
264
- const bytes = new Uint8Array(data);
265
- let binary = '';
266
- const CHUNK = 0x8000;
267
- for (let i = 0; i < bytes.length; i += CHUNK) {
268
- binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
269
- }
270
- return `data:${mime};base64,${btoa(binary)}`;
271
- }
272
-
273
- /** Walk the markdown AST and collect all image URLs. */
274
- function collectMarkdownImageUrls(doc: MarkdownDocument): string[] {
275
- const urls: string[] = [];
276
- function walkBlocks(nodes: MarkdownBlockNode[]): void {
277
- for (const node of nodes) {
278
- if ('children' in node && Array.isArray(node.children)) {
279
- for (const child of node.children as (MarkdownBlockNode | MarkdownInlineNode)[]) {
280
- if (child.type === 'image') {
281
- urls.push((child as { url: string }).url);
282
- } else if ('children' in child && Array.isArray(child.children)) {
283
- walkBlocks(child.children as MarkdownBlockNode[]);
284
- }
285
- }
286
- }
287
- }
288
- }
289
- walkBlocks(doc.children);
290
- return Array.from(new Set(urls));
291
- }
292
-
293
- /** Resolve image URLs (markdown → docx) to binary data from a ContentContainer. */
294
- async function resolveImages(
295
- doc: MarkdownDocument,
296
- container: ContentContainer,
297
- ): Promise<Map<string, { data: ArrayBuffer | Uint8Array; contentType: string }>> {
298
- const urls = collectMarkdownImageUrls(doc);
299
- const map = new Map<string, { data: ArrayBuffer | Uint8Array; contentType: string }>();
300
-
301
- for (const url of urls) {
302
- if (map.has(url)) continue;
303
- if (isExternalUrl(url)) continue;
304
- const data = await container.readFile(url);
305
- if (!data) continue;
306
- const ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase();
307
- const contentType = IMAGE_MIME[ext] || 'image/png';
308
- map.set(url, { data, contentType });
309
- }
310
-
311
- return map;
312
- }
313
-
314
- /** Container-relative path for the bundle's entry document. `mediaContainer`
315
- * is already scoped to the selected file's parent directory, so the bundle
316
- * needs the basename ("docs/home.md" → "home.md"). */
317
- function basenameForBundle(selectedFile: string): string {
318
- const clean = selectedFile.replace(/^\/+/, '');
319
- const idx = clean.lastIndexOf('/');
320
- return idx === -1 ? clean : clean.slice(idx + 1);
321
- }
322
-
323
- /** Read a UTF-8 text document from a ContentContainer. Returns null when
324
- * the file is missing — preserves the bundle's "abort with error" contract
325
- * for unreachable .md links. */
326
- async function readDocumentFromContainer(
327
- container: ContentContainer,
328
- path: string,
329
- ): Promise<string | null> {
330
- const data = await container.readFile(path);
331
- if (!data) return null;
332
- return new TextDecoder('utf-8').decode(data);
333
- }
334
-
335
- /** Resolve image paths referenced by a squisq Doc to ArrayBuffers for HTML export. */
336
- async function resolveDocImages(
337
- doc: Doc,
338
- container: ContentContainer,
339
- collectImagePaths: (doc: Doc) => Iterable<string>,
340
- ): Promise<Map<string, ArrayBuffer>> {
341
- const paths = collectImagePaths(doc);
342
- const map = new Map<string, ArrayBuffer>();
343
- for (const path of paths) {
344
- const data = await container.readFile(path);
345
- if (!data) continue;
346
- map.set(path, data);
347
- }
348
- return map;
349
- }
@@ -1,14 +0,0 @@
1
- export interface ExportSummaryOption {
2
- id: string;
3
- name: string;
4
- description?: string;
5
- }
6
-
7
- let transformStyleSummariesPromise: Promise<ExportSummaryOption[]> | null = null;
8
-
9
- export function loadTransformStyleSummaries(): Promise<ExportSummaryOption[]> {
10
- transformStyleSummariesPromise ??= import('@bendyline/squisq/transform').then(
11
- ({ getTransformStyleSummaries }) => getTransformStyleSummaries(),
12
- );
13
- return transformStyleSummariesPromise;
14
- }