@bendyline/squisq-formats 1.4.2 → 2.0.1

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 (140) hide show
  1. package/README.md +48 -2
  2. package/dist/chunk-2KPARF2P.js +99 -0
  3. package/dist/chunk-2KPARF2P.js.map +1 -0
  4. package/dist/{chunk-5LSSWZPU.js → chunk-4VUWTSGM.js} +72 -25
  5. package/dist/chunk-4VUWTSGM.js.map +1 -0
  6. package/dist/{chunk-YESS7VY3.js → chunk-ABVI556T.js} +154 -39
  7. package/dist/chunk-ABVI556T.js.map +1 -0
  8. package/dist/{chunk-WWBIKRNP.js → chunk-CRAVSMPZ.js} +13 -11
  9. package/dist/chunk-CRAVSMPZ.js.map +1 -0
  10. package/dist/chunk-FE6OJV6O.js +560 -0
  11. package/dist/chunk-FE6OJV6O.js.map +1 -0
  12. package/dist/{chunk-D7IFPWNZ.js → chunk-GVS2XXV6.js} +27 -26
  13. package/dist/chunk-GVS2XXV6.js.map +1 -0
  14. package/dist/{chunk-RTS5XBZ7.js → chunk-HTW2M27H.js} +341 -100
  15. package/dist/chunk-HTW2M27H.js.map +1 -0
  16. package/dist/{chunk-46FMDOWZ.js → chunk-LXYLOOST.js} +49 -17
  17. package/dist/chunk-LXYLOOST.js.map +1 -0
  18. package/dist/{chunk-MJGRI6XR.js → chunk-MLX2BOJC.js} +12 -7
  19. package/dist/chunk-MLX2BOJC.js.map +1 -0
  20. package/dist/chunk-NKAJPJ4G.js +106 -0
  21. package/dist/chunk-NKAJPJ4G.js.map +1 -0
  22. package/dist/{chunk-DQAZR57U.js → chunk-O3GVVND4.js} +1 -1
  23. package/dist/chunk-ODL3SSPT.js +18 -0
  24. package/dist/chunk-ODL3SSPT.js.map +1 -0
  25. package/dist/chunk-QRVN6A6E.js +127 -0
  26. package/dist/chunk-QRVN6A6E.js.map +1 -0
  27. package/dist/chunk-U32AG3G3.js +195 -0
  28. package/dist/chunk-U32AG3G3.js.map +1 -0
  29. package/dist/{chunk-EHLGMSTE.js → chunk-VJJM2SSH.js} +10 -8
  30. package/dist/chunk-VJJM2SSH.js.map +1 -0
  31. package/dist/chunk-VSYHZECT.js +778 -0
  32. package/dist/chunk-VSYHZECT.js.map +1 -0
  33. package/dist/{chunk-SHLPSADL.js → chunk-WC7WULGV.js} +81 -189
  34. package/dist/chunk-WC7WULGV.js.map +1 -0
  35. package/dist/chunk-XKUMNGBW.js +131 -0
  36. package/dist/chunk-XKUMNGBW.js.map +1 -0
  37. package/dist/chunk-YRT7GQ5Y.js +28 -0
  38. package/dist/chunk-YRT7GQ5Y.js.map +1 -0
  39. package/dist/container/index.d.ts +13 -3
  40. package/dist/container/index.js +23 -20
  41. package/dist/container/index.js.map +1 -1
  42. package/dist/docx/index.d.ts +6 -2
  43. package/dist/docx/index.js +5 -3
  44. package/dist/epub/index.d.ts +3 -1
  45. package/dist/epub/index.js +3 -3
  46. package/dist/export-D2NkylDT.d.ts +69 -0
  47. package/dist/extract-H6RXJMHP.js +15 -0
  48. package/dist/extract-H6RXJMHP.js.map +1 -0
  49. package/dist/html/index.d.ts +15 -85
  50. package/dist/html/index.js +3 -3
  51. package/dist/images-7FBWPKE3.js +7 -0
  52. package/dist/images-7FBWPKE3.js.map +1 -0
  53. package/dist/import-DTkDxHmZ.d.ts +84 -0
  54. package/dist/import-K8mfc0fz.d.ts +136 -0
  55. package/dist/index.d.ts +14 -4
  56. package/dist/index.js +33 -12
  57. package/dist/infer/index.d.ts +138 -0
  58. package/dist/infer/index.js +28 -0
  59. package/dist/infer/index.js.map +1 -0
  60. package/dist/layouts-BHrgZ5FS.d.ts +150 -0
  61. package/dist/layouts-QVPK3ZCU.js +12 -0
  62. package/dist/layouts-QVPK3ZCU.js.map +1 -0
  63. package/dist/mapTheme-IR27S6IV.js +11 -0
  64. package/dist/mapTheme-IR27S6IV.js.map +1 -0
  65. package/dist/ooxml/index.d.ts +21 -127
  66. package/dist/ooxml/index.js +31 -9
  67. package/dist/pdf/index.d.ts +3 -1
  68. package/dist/pdf/index.js +1 -1
  69. package/dist/pptx/index.d.ts +8 -108
  70. package/dist/pptx/index.js +13 -4
  71. package/dist/reader-B9L8Ucbj.d.ts +140 -0
  72. package/dist/registry/index.d.ts +60 -8
  73. package/dist/registry/index.js +9 -4
  74. package/dist/themeReader-DJKErl_j.d.ts +56 -0
  75. package/dist/xlsx/index.d.ts +6 -66
  76. package/dist/xlsx/index.js +4 -2
  77. package/dist/zipLimits-BOKCB7qk.d.ts +31 -0
  78. package/package.json +8 -2
  79. package/src/__tests__/container.test.ts +41 -0
  80. package/src/__tests__/convert.test.ts +310 -1
  81. package/src/__tests__/epub.test.ts +102 -1
  82. package/src/__tests__/html.test.ts +62 -0
  83. package/src/__tests__/inferTheme.test.ts +135 -0
  84. package/src/__tests__/lossyWarnings.test.ts +41 -0
  85. package/src/__tests__/ooxml.test.ts +76 -2
  86. package/src/__tests__/ooxmlCancellation.test.ts +113 -0
  87. package/src/__tests__/ooxmlThemeReader.test.ts +92 -0
  88. package/src/__tests__/plainHtml.test.ts +15 -1
  89. package/src/__tests__/plainHtmlBundle.test.ts +18 -0
  90. package/src/__tests__/pptxImport.test.ts +30 -0
  91. package/src/__tests__/pptxInferFixtures.ts +314 -0
  92. package/src/__tests__/pptxLayoutInfer.test.ts +395 -0
  93. package/src/__tests__/zipSafety.test.ts +317 -0
  94. package/src/container/index.ts +37 -32
  95. package/src/docx/export.ts +4 -2
  96. package/src/docx/import.ts +4 -3
  97. package/src/epub/export.ts +94 -25
  98. package/src/html/docsHtmlBundle.ts +6 -2
  99. package/src/html/htmlTemplate.ts +31 -6
  100. package/src/html/imageUtils.ts +14 -5
  101. package/src/html/index.ts +12 -1
  102. package/src/html/plainHtml.ts +7 -3
  103. package/src/html/plainHtmlBundle.ts +11 -9
  104. package/src/index.ts +23 -0
  105. package/src/infer/extract.ts +127 -0
  106. package/src/infer/index.ts +199 -0
  107. package/src/infer/mapTheme.ts +176 -0
  108. package/src/infer/types.ts +27 -0
  109. package/src/ooxml/index.ts +14 -0
  110. package/src/ooxml/readUtils.ts +44 -0
  111. package/src/ooxml/reader.ts +129 -29
  112. package/src/ooxml/themeReader.ts +197 -0
  113. package/src/ooxml/types.ts +8 -9
  114. package/src/ooxml/writer.ts +28 -10
  115. package/src/pdf/export.ts +4 -2
  116. package/src/pdf/import.ts +40 -34
  117. package/src/pptx/export.ts +4 -2
  118. package/src/pptx/import.ts +214 -32
  119. package/src/pptx/index.ts +14 -0
  120. package/src/pptx/layouts.ts +1222 -0
  121. package/src/registry/convert.ts +168 -53
  122. package/src/registry/defaultFormats.ts +135 -45
  123. package/src/registry/index.ts +6 -1
  124. package/src/registry/types.ts +69 -6
  125. package/src/shared/boundedZipArchive.ts +383 -0
  126. package/src/shared/fidelity.ts +130 -0
  127. package/src/shared/zipEntryCount.ts +151 -0
  128. package/src/shared/zipLimits.ts +296 -0
  129. package/src/shared/zipSafety.ts +19 -0
  130. package/src/xlsx/import.ts +3 -2
  131. package/dist/chunk-46FMDOWZ.js.map +0 -1
  132. package/dist/chunk-5LSSWZPU.js.map +0 -1
  133. package/dist/chunk-D7IFPWNZ.js.map +0 -1
  134. package/dist/chunk-EHLGMSTE.js.map +0 -1
  135. package/dist/chunk-MJGRI6XR.js.map +0 -1
  136. package/dist/chunk-RTS5XBZ7.js.map +0 -1
  137. package/dist/chunk-SHLPSADL.js.map +0 -1
  138. package/dist/chunk-WWBIKRNP.js.map +0 -1
  139. package/dist/chunk-YESS7VY3.js.map +0 -1
  140. /package/dist/{chunk-DQAZR57U.js.map → chunk-O3GVVND4.js.map} +0 -0
@@ -0,0 +1,131 @@
1
+ import {
2
+ baseDirOf,
3
+ findRelByType,
4
+ resolveTarget
5
+ } from "./chunk-YRT7GQ5Y.js";
6
+ import {
7
+ NS_DRAWINGML,
8
+ REL_THEME,
9
+ getPartRelationships,
10
+ getPartXml
11
+ } from "./chunk-WC7WULGV.js";
12
+
13
+ // src/ooxml/themeReader.ts
14
+ var HEX6_RE = /^[0-9a-fA-F]{6}$/;
15
+ var SYS_COLOR_DEFAULTS = {
16
+ windowText: "000000",
17
+ window: "ffffff"
18
+ };
19
+ function resolveSchemeColor(slot) {
20
+ for (let i = 0; i < slot.childNodes.length; i++) {
21
+ const child = slot.childNodes[i];
22
+ if (!child || child.nodeType !== 1) continue;
23
+ const el = child;
24
+ if (el.localName === "srgbClr") {
25
+ const val = el.getAttribute("val");
26
+ return val && HEX6_RE.test(val) ? `#${val.toLowerCase()}` : null;
27
+ }
28
+ if (el.localName === "sysClr") {
29
+ const lastClr = el.getAttribute("lastClr");
30
+ if (lastClr && HEX6_RE.test(lastClr)) return `#${lastClr.toLowerCase()}`;
31
+ const val = el.getAttribute("val");
32
+ const known = val ? SYS_COLOR_DEFAULTS[val] : void 0;
33
+ return known ? `#${known}` : null;
34
+ }
35
+ return null;
36
+ }
37
+ return null;
38
+ }
39
+ function firstNS(parent, local) {
40
+ const els = parent.getElementsByTagNameNS(NS_DRAWINGML, local);
41
+ return els.length > 0 ? els[0] : null;
42
+ }
43
+ function parseColorScheme(clrScheme, warnings) {
44
+ const resolve = (slotName) => {
45
+ const slot = firstNS(clrScheme, slotName);
46
+ if (!slot) return null;
47
+ return resolveSchemeColor(slot);
48
+ };
49
+ const dk1 = resolve("dk1");
50
+ const lt1 = resolve("lt1");
51
+ if (!dk1) warnings.push("theme: dk1 color could not be resolved; using #000000");
52
+ if (!lt1) warnings.push("theme: lt1 color could not be resolved; using #ffffff");
53
+ const dk2 = resolve("dk2");
54
+ const lt2 = resolve("lt2");
55
+ const colors = {
56
+ dk1: dk1 ?? "#000000",
57
+ lt1: lt1 ?? "#ffffff",
58
+ dk2: dk2 ?? dk1 ?? "#000000",
59
+ lt2: lt2 ?? lt1 ?? "#ffffff"
60
+ };
61
+ for (const slot of ["accent1", "accent2", "accent3", "accent4", "accent5", "accent6"]) {
62
+ const hex = resolve(slot);
63
+ if (hex) colors[slot] = hex;
64
+ else warnings.push(`theme: ${slot} color could not be resolved; dropped`);
65
+ }
66
+ for (const slot of ["hlink", "folHlink"]) {
67
+ const hex = resolve(slot);
68
+ if (hex) colors[slot] = hex;
69
+ }
70
+ return colors;
71
+ }
72
+ function parseFontScheme(fontScheme, warnings) {
73
+ const readLatin = (fontName) => {
74
+ const font = firstNS(fontScheme, fontName);
75
+ if (!font) return void 0;
76
+ const latin = firstNS(font, "latin");
77
+ const typeface = latin?.getAttribute("typeface")?.trim();
78
+ if (!typeface) {
79
+ warnings.push(`theme: ${fontName} has no latin typeface`);
80
+ return void 0;
81
+ }
82
+ return typeface;
83
+ };
84
+ const majorLatin = readLatin("majorFont");
85
+ const minorLatin = readLatin("minorFont");
86
+ if (!majorLatin && !minorLatin) return void 0;
87
+ return {
88
+ ...majorLatin ? { majorLatin } : {},
89
+ ...minorLatin ? { minorLatin } : {}
90
+ };
91
+ }
92
+ function parseThemeXml(themeDoc) {
93
+ const warnings = [];
94
+ const root = firstNS(themeDoc, "theme");
95
+ const name = root?.getAttribute("name")?.trim() || void 0;
96
+ const clrScheme = firstNS(themeDoc, "clrScheme");
97
+ let colors;
98
+ if (clrScheme) {
99
+ colors = parseColorScheme(clrScheme, warnings);
100
+ } else {
101
+ warnings.push("theme: no clrScheme found");
102
+ }
103
+ const fontScheme = firstNS(themeDoc, "fontScheme");
104
+ let fonts;
105
+ if (fontScheme) {
106
+ fonts = parseFontScheme(fontScheme, warnings);
107
+ } else {
108
+ warnings.push("theme: no fontScheme found");
109
+ }
110
+ return {
111
+ ...name ? { name } : {},
112
+ ...colors ? { colors } : {},
113
+ ...fonts ? { fonts } : {},
114
+ warnings
115
+ };
116
+ }
117
+ async function readThemePart(pkg, fromPartPath) {
118
+ const rels = await getPartRelationships(pkg, fromPartPath);
119
+ const rel = findRelByType(rels, REL_THEME);
120
+ if (!rel) return null;
121
+ const themePath = resolveTarget(baseDirOf(fromPartPath), rel.target);
122
+ const doc = await getPartXml(pkg, themePath);
123
+ if (!doc) return null;
124
+ return parseThemeXml(doc);
125
+ }
126
+
127
+ export {
128
+ parseThemeXml,
129
+ readThemePart
130
+ };
131
+ //# sourceMappingURL=chunk-XKUMNGBW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ooxml/themeReader.ts"],"sourcesContent":["/**\n * OOXML theme reader — parses a `theme1.xml` part (DrawingML `a:theme`)\n * into normalized color and font schemes.\n *\n * Shared by DOCX/PPTX/XLSX theme inference: all three formats carry the same\n * theme part shape, differing only in where the part hangs (document/master/\n * workbook relationships). This module never fails hard — unreadable slots\n * degrade to fallbacks or absence, with a warning per degradation.\n */\n\nimport type { OoxmlPackage } from './types.js';\nimport { getPartRelationships, getPartXml } from './reader.js';\nimport { NS_DRAWINGML, REL_THEME } from './namespaces.js';\nimport { baseDirOf, findRelByType, resolveTarget } from './readUtils.js';\n\n/**\n * The clrScheme slots as normalized `#rrggbb` strings. The four surface/text\n * slots always resolve (falling back to black/white when degraded); accent and\n * hyperlink slots are omitted when unresolvable.\n */\nexport interface OoxmlColorScheme {\n dk1: string;\n lt1: string;\n dk2: string;\n lt2: string;\n accent1?: string;\n accent2?: string;\n accent3?: string;\n accent4?: string;\n accent5?: string;\n accent6?: string;\n hlink?: string;\n folHlink?: string;\n}\n\nexport interface OoxmlFontScheme {\n /** `a:fontScheme > a:majorFont > a:latin@typeface` (headings). */\n majorLatin?: string;\n /** `a:fontScheme > a:minorFont > a:latin@typeface` (body). */\n minorLatin?: string;\n}\n\nexport interface OoxmlTheme {\n /** `a:theme@name` (e.g. \"Office Theme\", \"Ion\"). */\n name?: string;\n /** Absent when the theme carries no parseable `a:clrScheme`. */\n colors?: OoxmlColorScheme;\n /** Absent when the theme carries no `a:fontScheme` latin typefaces. */\n fonts?: OoxmlFontScheme;\n warnings: string[];\n}\n\nconst HEX6_RE = /^[0-9a-fA-F]{6}$/;\n\n/** Well-known sysClr values that resolve without a `lastClr` hint. */\nconst SYS_COLOR_DEFAULTS: Record<string, string> = {\n windowText: '000000',\n window: 'ffffff',\n};\n\n/**\n * Resolve one clrScheme slot element to a `#rrggbb` string, or null.\n * Only the base color matters for theming — trailing transform children\n * (`a:lumMod`, `a:tint`, …) are ignored.\n */\nfunction resolveSchemeColor(slot: Element): string | null {\n for (let i = 0; i < slot.childNodes.length; i++) {\n const child = slot.childNodes[i];\n if (!child || child.nodeType !== 1) continue;\n const el = child as Element;\n if (el.localName === 'srgbClr') {\n const val = el.getAttribute('val');\n return val && HEX6_RE.test(val) ? `#${val.toLowerCase()}` : null;\n }\n if (el.localName === 'sysClr') {\n const lastClr = el.getAttribute('lastClr');\n if (lastClr && HEX6_RE.test(lastClr)) return `#${lastClr.toLowerCase()}`;\n const val = el.getAttribute('val');\n const known = val ? SYS_COLOR_DEFAULTS[val] : undefined;\n return known ? `#${known}` : null;\n }\n // Any other color form (schemeClr self-reference, prstClr, …) is\n // unresolvable without a full DrawingML color pipeline.\n return null;\n }\n return null;\n}\n\nfunction firstNS(parent: Element | Document, local: string): Element | null {\n const els = parent.getElementsByTagNameNS(NS_DRAWINGML, local);\n return els.length > 0 ? els[0]! : null;\n}\n\nfunction parseColorScheme(clrScheme: Element, warnings: string[]): OoxmlColorScheme {\n const resolve = (slotName: string): string | null => {\n const slot = firstNS(clrScheme, slotName);\n if (!slot) return null;\n return resolveSchemeColor(slot);\n };\n\n const dk1 = resolve('dk1');\n const lt1 = resolve('lt1');\n if (!dk1) warnings.push('theme: dk1 color could not be resolved; using #000000');\n if (!lt1) warnings.push('theme: lt1 color could not be resolved; using #ffffff');\n\n const dk2 = resolve('dk2');\n const lt2 = resolve('lt2');\n\n const colors: OoxmlColorScheme = {\n dk1: dk1 ?? '#000000',\n lt1: lt1 ?? '#ffffff',\n dk2: dk2 ?? dk1 ?? '#000000',\n lt2: lt2 ?? lt1 ?? '#ffffff',\n };\n\n for (const slot of ['accent1', 'accent2', 'accent3', 'accent4', 'accent5', 'accent6'] as const) {\n const hex = resolve(slot);\n if (hex) colors[slot] = hex;\n else warnings.push(`theme: ${slot} color could not be resolved; dropped`);\n }\n for (const slot of ['hlink', 'folHlink'] as const) {\n const hex = resolve(slot);\n if (hex) colors[slot] = hex;\n }\n return colors;\n}\n\nfunction parseFontScheme(fontScheme: Element, warnings: string[]): OoxmlFontScheme | undefined {\n const readLatin = (fontName: 'majorFont' | 'minorFont'): string | undefined => {\n const font = firstNS(fontScheme, fontName);\n if (!font) return undefined;\n const latin = firstNS(font, 'latin');\n const typeface = latin?.getAttribute('typeface')?.trim();\n if (!typeface) {\n warnings.push(`theme: ${fontName} has no latin typeface`);\n return undefined;\n }\n return typeface;\n };\n\n const majorLatin = readLatin('majorFont');\n const minorLatin = readLatin('minorFont');\n if (!majorLatin && !minorLatin) return undefined;\n return {\n ...(majorLatin ? { majorLatin } : {}),\n ...(minorLatin ? { minorLatin } : {}),\n };\n}\n\n/** Parse an already-loaded theme part Document. */\nexport function parseThemeXml(themeDoc: Document): OoxmlTheme {\n const warnings: string[] = [];\n\n const root = firstNS(themeDoc, 'theme');\n const name = root?.getAttribute('name')?.trim() || undefined;\n\n const clrScheme = firstNS(themeDoc, 'clrScheme');\n let colors: OoxmlColorScheme | undefined;\n if (clrScheme) {\n colors = parseColorScheme(clrScheme, warnings);\n } else {\n warnings.push('theme: no clrScheme found');\n }\n\n const fontScheme = firstNS(themeDoc, 'fontScheme');\n let fonts: OoxmlFontScheme | undefined;\n if (fontScheme) {\n fonts = parseFontScheme(fontScheme, warnings);\n } else {\n warnings.push('theme: no fontScheme found');\n }\n\n return {\n ...(name ? { name } : {}),\n ...(colors ? { colors } : {}),\n ...(fonts ? { fonts } : {}),\n warnings,\n };\n}\n\n/**\n * Resolve the theme part related from `fromPartPath` (via `REL_THEME`) and\n * parse it. Returns null when the part has no theme relationship or the\n * target part is missing — callers decide whether that's an error.\n */\nexport async function readThemePart(\n pkg: OoxmlPackage,\n fromPartPath: string,\n): Promise<OoxmlTheme | null> {\n const rels = await getPartRelationships(pkg, fromPartPath);\n const rel = findRelByType(rels, REL_THEME);\n if (!rel) return null;\n const themePath = resolveTarget(baseDirOf(fromPartPath), rel.target);\n const doc = await getPartXml(pkg, themePath);\n if (!doc) return null;\n return parseThemeXml(doc);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,28 @@
1
+ // src/ooxml/readUtils.ts
2
+ function attrNS(el, ns, local, qualified) {
3
+ return el.getAttributeNS(ns, local) ?? el.getAttribute(qualified);
4
+ }
5
+ function baseDirOf(path) {
6
+ const slash = path.lastIndexOf("/");
7
+ return slash === -1 ? "" : path.slice(0, slash);
8
+ }
9
+ function resolveTarget(baseDir, target) {
10
+ if (target.startsWith("/")) return target.replace(/^\//, "");
11
+ const stack = baseDir ? baseDir.split("/") : [];
12
+ for (const seg of target.split("/")) {
13
+ if (seg === "..") stack.pop();
14
+ else if (seg !== ".") stack.push(seg);
15
+ }
16
+ return stack.join("/");
17
+ }
18
+ function findRelByType(rels, type) {
19
+ return rels.find((r) => r.type === type);
20
+ }
21
+
22
+ export {
23
+ attrNS,
24
+ baseDirOf,
25
+ resolveTarget,
26
+ findRelByType
27
+ };
28
+ //# sourceMappingURL=chunk-YRT7GQ5Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ooxml/readUtils.ts"],"sourcesContent":["/**\n * Shared DOM-read helpers for OOXML importers.\n *\n * Every importer walks parts with `getElementsByTagNameNS` and resolves\n * relationship targets relative to the referencing part; these helpers hold\n * the common pieces so new readers (theme, slide layouts) don't re-implement\n * them per format.\n */\n\nimport type { Relationship } from './types.js';\n\n/**\n * Read a namespaced attribute with a qualified-name fallback for documents\n * whose producer omitted the namespace declaration (e.g. `r:id`).\n */\nexport function attrNS(el: Element, ns: string, local: string, qualified: string): string | null {\n return el.getAttributeNS(ns, local) ?? el.getAttribute(qualified);\n}\n\n/** Directory portion of a part path, e.g. `ppt/slides/slide1.xml` → `ppt/slides`. */\nexport function baseDirOf(path: string): string {\n const slash = path.lastIndexOf('/');\n return slash === -1 ? '' : path.slice(0, slash);\n}\n\n/**\n * Resolve a relationship target against the referencing part's directory.\n * Absolute targets (leading `/`) are package-rooted; relative targets may\n * climb with `..` segments (e.g. `../slideLayouts/slideLayout1.xml`).\n */\nexport function resolveTarget(baseDir: string, target: string): string {\n if (target.startsWith('/')) return target.replace(/^\\//, '');\n const stack = baseDir ? baseDir.split('/') : [];\n for (const seg of target.split('/')) {\n if (seg === '..') stack.pop();\n else if (seg !== '.') stack.push(seg);\n }\n return stack.join('/');\n}\n\n/** First relationship of the given type, if any. */\nexport function findRelByType(rels: Relationship[], type: string): Relationship | undefined {\n return rels.find((r) => r.type === type);\n}\n"],"mappings":";AAeO,SAAS,OAAO,IAAa,IAAY,OAAe,WAAkC;AAC/F,SAAO,GAAG,eAAe,IAAI,KAAK,KAAK,GAAG,aAAa,SAAS;AAClE;AAGO,SAAS,UAAU,MAAsB;AAC9C,QAAM,QAAQ,KAAK,YAAY,GAAG;AAClC,SAAO,UAAU,KAAK,KAAK,KAAK,MAAM,GAAG,KAAK;AAChD;AAOO,SAAS,cAAc,SAAiB,QAAwB;AACrE,MAAI,OAAO,WAAW,GAAG,EAAG,QAAO,OAAO,QAAQ,OAAO,EAAE;AAC3D,QAAM,QAAQ,UAAU,QAAQ,MAAM,GAAG,IAAI,CAAC;AAC9C,aAAW,OAAO,OAAO,MAAM,GAAG,GAAG;AACnC,QAAI,QAAQ,KAAM,OAAM,IAAI;AAAA,aACnB,QAAQ,IAAK,OAAM,KAAK,GAAG;AAAA,EACtC;AACA,SAAO,MAAM,KAAK,GAAG;AACvB;AAGO,SAAS,cAAc,MAAsB,MAAwC;AAC1F,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AACzC;","names":[]}
@@ -1,4 +1,6 @@
1
1
  import { ContentContainer, MemoryContentContainer } from '@bendyline/squisq/storage';
2
+ import { c as ZipSafetyLimits } from '../zipLimits-BOKCB7qk.js';
3
+ export { Z as ZipSafetyError, a as ZipSafetyErrorCode, b as ZipSafetyErrorOptions } from '../zipLimits-BOKCB7qk.js';
2
4
 
3
5
  /**
4
6
  * Container ZIP serialization — convert between ContentContainer and ZIP archives.
@@ -13,6 +15,14 @@ import { ContentContainer, MemoryContentContainer } from '@bendyline/squisq/stor
13
15
  * timing.json
14
16
  */
15
17
 
18
+ type ZipToContainerOptions = ZipSafetyLimits;
19
+ interface ContainerToZipOptions {
20
+ /** STORE avoids producing archives that violate strict import compression-ratio policies. */
21
+ compression?: 'STORE' | 'DEFLATE';
22
+ /** JSZip DEFLATE level; ignored for STORE. */
23
+ compressionLevel?: number;
24
+ }
25
+
16
26
  /**
17
27
  * Serialize a ContentContainer to a ZIP blob.
18
28
  *
@@ -22,7 +32,7 @@ import { ContentContainer, MemoryContentContainer } from '@bendyline/squisq/stor
22
32
  * @param container — The container to serialize
23
33
  * @returns A Blob containing the ZIP archive
24
34
  */
25
- declare function containerToZip(container: ContentContainer): Promise<Blob>;
35
+ declare function containerToZip(container: ContentContainer, options?: ContainerToZipOptions): Promise<Blob>;
26
36
  /**
27
37
  * Deserialize a ZIP archive into a MemoryContentContainer.
28
38
  *
@@ -33,6 +43,6 @@ declare function containerToZip(container: ContentContainer): Promise<Blob>;
33
43
  * @param zipData — The ZIP archive as ArrayBuffer, Uint8Array, or Blob
34
44
  * @returns A MemoryContentContainer populated with the ZIP's contents
35
45
  */
36
- declare function zipToContainer(zipData: ArrayBuffer | Uint8Array | Blob): Promise<MemoryContentContainer>;
46
+ declare function zipToContainer(zipData: ArrayBuffer | Uint8Array | Blob, options?: ZipToContainerOptions): Promise<MemoryContentContainer>;
37
47
 
38
- export { containerToZip, zipToContainer };
48
+ export { type ContainerToZipOptions, ZipSafetyLimits, type ZipToContainerOptions, containerToZip, zipToContainer };
@@ -1,42 +1,45 @@
1
+ import {
2
+ ZipSafetyError,
3
+ assertSafeZipPath,
4
+ openBoundedZipArchive
5
+ } from "../chunk-FE6OJV6O.js";
6
+
1
7
  // src/container/index.ts
2
8
  import JSZip from "jszip";
3
9
  import { MemoryContentContainer } from "@bendyline/squisq/storage";
4
- async function containerToZip(container) {
10
+ async function containerToZip(container, options = {}) {
5
11
  const zip = new JSZip();
6
12
  const entries = await container.listFiles();
7
13
  for (const entry of entries) {
14
+ assertSafeZipPath(entry.path);
8
15
  const data = await container.readFile(entry.path);
9
16
  if (data) {
10
17
  zip.file(entry.path, new Uint8Array(data));
11
18
  }
12
19
  }
20
+ const compression = options.compression ?? "DEFLATE";
21
+ const compressionLevel = options.compressionLevel ?? 6;
22
+ if (!Number.isSafeInteger(compressionLevel) || compressionLevel < 1 || compressionLevel > 9) {
23
+ throw new Error("ZIP compression level must be an integer between 1 and 9");
24
+ }
13
25
  return zip.generateAsync({
14
26
  type: "blob",
15
- compression: "DEFLATE",
16
- compressionOptions: { level: 6 }
27
+ compression,
28
+ ...compression === "DEFLATE" ? { compressionOptions: { level: compressionLevel } } : {}
17
29
  });
18
30
  }
19
- async function zipToContainer(zipData) {
20
- const zip = await JSZip.loadAsync(zipData);
31
+ async function zipToContainer(zipData, options = {}) {
32
+ const archive = await openBoundedZipArchive(zipData, options);
21
33
  const container = new MemoryContentContainer();
22
- const filePromises = [];
23
- zip.forEach((relativePath, zipEntry) => {
24
- if (zipEntry.dir) return;
25
- const entryPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
26
- if (!entryPath) return;
27
- if (entryPath.startsWith("/") || entryPath.includes("\\") || entryPath.split("/").some((seg) => seg === "..")) {
28
- return;
29
- }
30
- filePromises.push(
31
- zipEntry.async("arraybuffer").then((data) => {
32
- return container.writeFile(entryPath, data);
33
- })
34
- );
35
- });
36
- await Promise.all(filePromises);
34
+ for (const { path } of archive.entries) {
35
+ const data = await archive.read(path);
36
+ if (data) await container.writeFile(path, data);
37
+ archive.release(path);
38
+ }
37
39
  return container;
38
40
  }
39
41
  export {
42
+ ZipSafetyError,
40
43
  containerToZip,
41
44
  zipToContainer
42
45
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/container/index.ts"],"sourcesContent":["/**\n * Container ZIP serialization — convert between ContentContainer and ZIP archives.\n *\n * Uses JSZip (already a formats dependency) to serialize a ContentContainer to\n * a ZIP blob and to deserialize a ZIP blob into a MemoryContentContainer.\n *\n * ZIP structure mirrors the container's flat path hierarchy directly:\n * index.md\n * images/hero.jpg\n * audio/narration.mp3\n * timing.json\n */\n\nimport JSZip from 'jszip';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport { MemoryContentContainer } from '@bendyline/squisq/storage';\n\n/**\n * Serialize a ContentContainer to a ZIP blob.\n *\n * All files in the container are written to the ZIP archive preserving\n * their path structure. The resulting blob can be saved as a .zip file.\n *\n * @param container — The container to serialize\n * @returns A Blob containing the ZIP archive\n */\nexport async function containerToZip(container: ContentContainer): Promise<Blob> {\n const zip = new JSZip();\n const entries = await container.listFiles();\n\n for (const entry of entries) {\n const data = await container.readFile(entry.path);\n if (data) {\n zip.file(entry.path, new Uint8Array(data));\n }\n }\n\n return zip.generateAsync({\n type: 'blob',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n });\n}\n\n/**\n * Deserialize a ZIP archive into a MemoryContentContainer.\n *\n * Reads all files from the ZIP and writes them into a new MemoryContentContainer.\n * Directory entries are skipped. The resulting container can be used immediately\n * for rendering, editing, or saving to persistent storage.\n *\n * @param zipData — The ZIP archive as ArrayBuffer, Uint8Array, or Blob\n * @returns A MemoryContentContainer populated with the ZIP's contents\n */\nexport async function zipToContainer(\n zipData: ArrayBuffer | Uint8Array | Blob,\n): Promise<MemoryContentContainer> {\n const zip = await JSZip.loadAsync(zipData);\n const container = new MemoryContentContainer();\n\n const filePromises: Promise<void>[] = [];\n\n zip.forEach((relativePath, zipEntry) => {\n // Skip directories\n if (zipEntry.dir) return;\n\n // Strip leading slash if present\n const entryPath = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath;\n if (!entryPath) return;\n\n // Path-traversal protection: reject absolute paths, backslashes, and .. segments\n if (\n entryPath.startsWith('/') ||\n entryPath.includes('\\\\') ||\n entryPath.split('/').some((seg) => seg === '..')\n ) {\n return;\n }\n\n filePromises.push(\n zipEntry.async('arraybuffer').then((data) => {\n return container.writeFile(entryPath, data);\n }),\n );\n });\n\n await Promise.all(filePromises);\n return container;\n}\n"],"mappings":";AAaA,OAAO,WAAW;AAElB,SAAS,8BAA8B;AAWvC,eAAsB,eAAe,WAA4C;AAC/E,QAAM,MAAM,IAAI,MAAM;AACtB,QAAM,UAAU,MAAM,UAAU,UAAU;AAE1C,aAAW,SAAS,SAAS;AAC3B,UAAM,OAAO,MAAM,UAAU,SAAS,MAAM,IAAI;AAChD,QAAI,MAAM;AACR,UAAI,KAAK,MAAM,MAAM,IAAI,WAAW,IAAI,CAAC;AAAA,IAC3C;AAAA,EACF;AAEA,SAAO,IAAI,cAAc;AAAA,IACvB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,oBAAoB,EAAE,OAAO,EAAE;AAAA,EACjC,CAAC;AACH;AAYA,eAAsB,eACpB,SACiC;AACjC,QAAM,MAAM,MAAM,MAAM,UAAU,OAAO;AACzC,QAAM,YAAY,IAAI,uBAAuB;AAE7C,QAAM,eAAgC,CAAC;AAEvC,MAAI,QAAQ,CAAC,cAAc,aAAa;AAEtC,QAAI,SAAS,IAAK;AAGlB,UAAM,YAAY,aAAa,WAAW,GAAG,IAAI,aAAa,MAAM,CAAC,IAAI;AACzE,QAAI,CAAC,UAAW;AAGhB,QACE,UAAU,WAAW,GAAG,KACxB,UAAU,SAAS,IAAI,KACvB,UAAU,MAAM,GAAG,EAAE,KAAK,CAAC,QAAQ,QAAQ,IAAI,GAC/C;AACA;AAAA,IACF;AAEA,iBAAa;AAAA,MACX,SAAS,MAAM,aAAa,EAAE,KAAK,CAAC,SAAS;AAC3C,eAAO,UAAU,UAAU,WAAW,IAAI;AAAA,MAC5C,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,QAAQ,IAAI,YAAY;AAC9B,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/container/index.ts"],"sourcesContent":["/**\n * Container ZIP serialization — convert between ContentContainer and ZIP archives.\n *\n * Uses JSZip (already a formats dependency) to serialize a ContentContainer to\n * a ZIP blob and to deserialize a ZIP blob into a MemoryContentContainer.\n *\n * ZIP structure mirrors the container's flat path hierarchy directly:\n * index.md\n * images/hero.jpg\n * audio/narration.mp3\n * timing.json\n */\n\nimport JSZip from 'jszip';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport { MemoryContentContainer } from '@bendyline/squisq/storage';\nimport {\n assertSafeZipPath,\n openBoundedZipArchive,\n ZipSafetyError,\n type ZipSafetyLimits,\n type ZipSafetyErrorCode,\n type ZipSafetyErrorOptions,\n} from '../shared/zipSafety.js';\n\nexport type ZipToContainerOptions = ZipSafetyLimits;\nexport interface ContainerToZipOptions {\n /** STORE avoids producing archives that violate strict import compression-ratio policies. */\n compression?: 'STORE' | 'DEFLATE';\n /** JSZip DEFLATE level; ignored for STORE. */\n compressionLevel?: number;\n}\nexport { ZipSafetyError };\nexport type { ZipSafetyLimits, ZipSafetyErrorCode, ZipSafetyErrorOptions };\n\n/**\n * Serialize a ContentContainer to a ZIP blob.\n *\n * All files in the container are written to the ZIP archive preserving\n * their path structure. The resulting blob can be saved as a .zip file.\n *\n * @param container — The container to serialize\n * @returns A Blob containing the ZIP archive\n */\nexport async function containerToZip(\n container: ContentContainer,\n options: ContainerToZipOptions = {},\n): Promise<Blob> {\n const zip = new JSZip();\n const entries = await container.listFiles();\n\n for (const entry of entries) {\n assertSafeZipPath(entry.path);\n const data = await container.readFile(entry.path);\n if (data) {\n zip.file(entry.path, new Uint8Array(data));\n }\n }\n\n const compression = options.compression ?? 'DEFLATE';\n const compressionLevel = options.compressionLevel ?? 6;\n if (!Number.isSafeInteger(compressionLevel) || compressionLevel < 1 || compressionLevel > 9) {\n throw new Error('ZIP compression level must be an integer between 1 and 9');\n }\n return zip.generateAsync({\n type: 'blob',\n compression,\n ...(compression === 'DEFLATE' ? { compressionOptions: { level: compressionLevel } } : {}),\n });\n}\n\n/**\n * Deserialize a ZIP archive into a MemoryContentContainer.\n *\n * Reads all files from the ZIP and writes them into a new MemoryContentContainer.\n * Directory entries are skipped. The resulting container can be used immediately\n * for rendering, editing, or saving to persistent storage.\n *\n * @param zipData — The ZIP archive as ArrayBuffer, Uint8Array, or Blob\n * @returns A MemoryContentContainer populated with the ZIP's contents\n */\nexport async function zipToContainer(\n zipData: ArrayBuffer | Uint8Array | Blob,\n options: ZipToContainerOptions = {},\n): Promise<MemoryContentContainer> {\n const archive = await openBoundedZipArchive(zipData, options);\n const container = new MemoryContentContainer();\n for (const { path } of archive.entries) {\n const data = await archive.read(path);\n if (data) await container.writeFile(path, data);\n archive.release(path);\n }\n return container;\n}\n"],"mappings":";;;;;;;AAaA,OAAO,WAAW;AAElB,SAAS,8BAA8B;AA6BvC,eAAsB,eACpB,WACA,UAAiC,CAAC,GACnB;AACf,QAAM,MAAM,IAAI,MAAM;AACtB,QAAM,UAAU,MAAM,UAAU,UAAU;AAE1C,aAAW,SAAS,SAAS;AAC3B,sBAAkB,MAAM,IAAI;AAC5B,UAAM,OAAO,MAAM,UAAU,SAAS,MAAM,IAAI;AAChD,QAAI,MAAM;AACR,UAAI,KAAK,MAAM,MAAM,IAAI,WAAW,IAAI,CAAC;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,MAAI,CAAC,OAAO,cAAc,gBAAgB,KAAK,mBAAmB,KAAK,mBAAmB,GAAG;AAC3F,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO,IAAI,cAAc;AAAA,IACvB,MAAM;AAAA,IACN;AAAA,IACA,GAAI,gBAAgB,YAAY,EAAE,oBAAoB,EAAE,OAAO,iBAAiB,EAAE,IAAI,CAAC;AAAA,EACzF,CAAC;AACH;AAYA,eAAsB,eACpB,SACA,UAAiC,CAAC,GACD;AACjC,QAAM,UAAU,MAAM,sBAAsB,SAAS,OAAO;AAC5D,QAAM,YAAY,IAAI,uBAAuB;AAC7C,aAAW,EAAE,KAAK,KAAK,QAAQ,SAAS;AACtC,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI;AACpC,QAAI,KAAM,OAAM,UAAU,UAAU,MAAM,IAAI;AAC9C,YAAQ,QAAQ,IAAI;AAAA,EACtB;AACA,SAAO;AACT;","names":[]}
@@ -1,6 +1,8 @@
1
- import { Doc } from '@bendyline/squisq/schemas';
1
+ import { ThemeRegistry, Doc } from '@bendyline/squisq/schemas';
2
2
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
3
+ import { O as OoxmlOpenOptions } from '../reader-B9L8Ucbj.js';
3
4
  import { ContentContainer } from '@bendyline/squisq/storage';
5
+ import '../zipLimits-BOKCB7qk.js';
4
6
 
5
7
  /**
6
8
  * DOCX Export
@@ -41,6 +43,8 @@ interface DocxExportOptions {
41
43
  * the theme's primary color to headings.
42
44
  */
43
45
  themeId?: string;
46
+ /** Explicit caller-owned registry for non-document custom themes. */
47
+ themeRegistry?: ThemeRegistry;
44
48
  /**
45
49
  * Pre-resolved image data keyed by image URL/path as it appears in the
46
50
  * markdown source. When provided, images are embedded in the .docx file
@@ -94,7 +98,7 @@ declare function docToDocx(doc: Doc, options?: DocxExportOptions): Promise<Array
94
98
  /**
95
99
  * Options for DOCX import.
96
100
  */
97
- interface DocxImportOptions {
101
+ interface DocxImportOptions extends OoxmlOpenOptions {
98
102
  /**
99
103
  * Whether to extract embedded images as base64 data URIs.
100
104
  * When false, images are represented as `[Image]` placeholders.
@@ -4,13 +4,15 @@ import {
4
4
  docxToDoc,
5
5
  docxToMarkdownDoc,
6
6
  markdownDocToDocx
7
- } from "../chunk-WWBIKRNP.js";
7
+ } from "../chunk-CRAVSMPZ.js";
8
8
  import "../chunk-6M7Z25LA.js";
9
9
  import "../chunk-PN52A5AA.js";
10
- import "../chunk-SHLPSADL.js";
10
+ import "../chunk-U32AG3G3.js";
11
11
  import "../chunk-U4MRIFKL.js";
12
+ import "../chunk-WC7WULGV.js";
13
+ import "../chunk-FE6OJV6O.js";
12
14
  import "../chunk-SSUPBUF5.js";
13
- import "../chunk-DQAZR57U.js";
15
+ import "../chunk-O3GVVND4.js";
14
16
  export {
15
17
  docToDocx,
16
18
  docxToContainer,
@@ -1,4 +1,4 @@
1
- import { AudioSegment, Doc } from '@bendyline/squisq/schemas';
1
+ import { ThemeRegistry, AudioSegment, Doc } from '@bendyline/squisq/schemas';
2
2
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
3
3
 
4
4
  /**
@@ -37,6 +37,8 @@ interface EpubExportOptions {
37
37
  publisher?: string;
38
38
  /** Squisq theme ID for CSS styling */
39
39
  themeId?: string;
40
+ /** Explicit caller-owned registry for non-document custom themes. */
41
+ themeRegistry?: ThemeRegistry;
40
42
  /** Pre-resolved image data keyed by relative path as it appears in the markdown */
41
43
  images?: Map<string, ArrayBuffer>;
42
44
  /** Cover image data (JPEG or PNG) */
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  docToEpub,
3
3
  markdownDocToEpub
4
- } from "../chunk-5LSSWZPU.js";
4
+ } from "../chunk-4VUWTSGM.js";
5
5
  import "../chunk-PN52A5AA.js";
6
6
  import "../chunk-U4MRIFKL.js";
7
- import "../chunk-MJGRI6XR.js";
8
- import "../chunk-DQAZR57U.js";
7
+ import "../chunk-MLX2BOJC.js";
8
+ import "../chunk-O3GVVND4.js";
9
9
  export {
10
10
  docToEpub,
11
11
  markdownDocToEpub
@@ -0,0 +1,69 @@
1
+ import { MarkdownDocument } from '@bendyline/squisq/markdown';
2
+ import { O as OoxmlOpenOptions } from './reader-B9L8Ucbj.js';
3
+ import { Doc } from '@bendyline/squisq/schemas';
4
+
5
+ /**
6
+ * XLSX import — SpreadsheetML (.xlsx) → MarkdownDocument.
7
+ *
8
+ * Reuses the shared ooxml/ reader (zip + DOMParser). Reads the workbook's
9
+ * sheet list, resolves each sheet part via relationships, pulls shared strings,
10
+ * and turns each worksheet's cell grid into a markdown table (first row treated
11
+ * as the header). By default every sheet is imported, each preceded by an H1 of
12
+ * the sheet name; pass `options.sheet` (index or name) to import just one.
13
+ */
14
+
15
+ interface XlsxImportOptions extends OoxmlOpenOptions {
16
+ /** Which sheet to import (0-based index or sheet name). Default: all sheets. */
17
+ sheet?: number | string;
18
+ }
19
+ declare function xlsxToMarkdownDoc(data: ArrayBuffer | Blob, options?: XlsxImportOptions): Promise<MarkdownDocument>;
20
+
21
+ /**
22
+ * XLSX export — MarkdownDocument → SpreadsheetML (.xlsx).
23
+ *
24
+ * Tables-only fidelity (honestly documented): every `table` node in the
25
+ * markdown AST becomes one worksheet; all other content (headings, prose,
26
+ * lists, images, …) is dropped except that the nearest preceding heading is
27
+ * used to name the sheet. This mirrors the import side (`xlsxToMarkdownDoc`),
28
+ * which turns each worksheet grid back into a markdown table.
29
+ *
30
+ * Cells are emitted as inline strings (`t="inlineStr"`) so no sharedStrings
31
+ * part is needed, except values matching a plain-number pattern, which are
32
+ * emitted as numeric cells. The package is assembled with the shared ooxml/
33
+ * writer (auto-generates `[Content_Types].xml` + `_rels`), so only the
34
+ * SpreadsheetML-specific parts (workbook, worksheets, styles) are written here.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { parseMarkdown } from '@bendyline/squisq/markdown';
39
+ * import { markdownDocToXlsx } from '@bendyline/squisq-formats/xlsx';
40
+ *
41
+ * const md = parseMarkdown('# Metrics\n\n| A | B |\n| - | - |\n| 1 | 2 |');
42
+ * const buffer = await markdownDocToXlsx(md);
43
+ * ```
44
+ */
45
+
46
+ /**
47
+ * Options for XLSX export.
48
+ */
49
+ interface XlsxExportOptions {
50
+ /** Workbook title (written to core properties). */
51
+ title?: string;
52
+ /** Workbook author (written to core properties). */
53
+ author?: string;
54
+ /** Prefix used for auto-named sheets when no heading precedes a table. Default: "Sheet". */
55
+ sheetNamePrefix?: string;
56
+ }
57
+ /**
58
+ * Convert a MarkdownDocument to a .xlsx file (tables-only fidelity).
59
+ *
60
+ * Each markdown `table` becomes one worksheet; a document with no tables
61
+ * yields a single empty sheet (a valid, openable file — never throws).
62
+ */
63
+ declare function markdownDocToXlsx(doc: MarkdownDocument, options?: XlsxExportOptions): Promise<ArrayBuffer>;
64
+ /**
65
+ * Convert a squisq Doc to a .xlsx file (via the markdown table model).
66
+ */
67
+ declare function docToXlsx(doc: Doc, options?: XlsxExportOptions): Promise<ArrayBuffer>;
68
+
69
+ export { type XlsxExportOptions as X, type XlsxImportOptions as a, docToXlsx as d, markdownDocToXlsx as m, xlsxToMarkdownDoc as x };
@@ -0,0 +1,15 @@
1
+ import {
2
+ extractDocxTheme,
3
+ extractPptxTheme,
4
+ extractXlsxTheme
5
+ } from "./chunk-2KPARF2P.js";
6
+ import "./chunk-XKUMNGBW.js";
7
+ import "./chunk-YRT7GQ5Y.js";
8
+ import "./chunk-WC7WULGV.js";
9
+ import "./chunk-FE6OJV6O.js";
10
+ export {
11
+ extractDocxTheme,
12
+ extractPptxTheme,
13
+ extractXlsxTheme
14
+ };
15
+ //# sourceMappingURL=extract-H6RXJMHP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,52 +1,8 @@
1
- import { Doc, Theme } from '@bendyline/squisq/schemas';
1
+ import { Theme, ThemeRegistry, Doc } from '@bendyline/squisq/schemas';
2
+ import { H as HtmlExportOptions } from '../import-DTkDxHmZ.js';
3
+ export { a as HtmlImportOptions, c as collectImagePaths, h as htmlToMarkdown, b as htmlToMarkdownDoc, d as htmlToMarkdownDocSync } from '../import-DTkDxHmZ.js';
2
4
  import { HtmlPolicy, MarkdownDocument } from '@bendyline/squisq/markdown';
3
5
 
4
- /**
5
- * HTML Template Generation for SquisqPlayer Exports
6
- *
7
- * Generates complete, self-contained HTML documents that load the SquisqPlayer
8
- * IIFE bundle and render a Doc as either an interactive slideshow or a static
9
- * scrollable document.
10
- *
11
- * Two variants:
12
- * 1. **Inline** — All JS, CSS, and images are embedded in the HTML (single file).
13
- * 2. **External** — JS is referenced via `<script src>`, images via relative paths.
14
- */
15
-
16
- interface HtmlExportOptions {
17
- /** The IIFE player bundle source code (from @bendyline/squisq-react/standalone-source) */
18
- playerScript: string;
19
- /**
20
- * Map of relative image paths (as they appear in the Doc) to binary image data.
21
- * For inline HTML export, these are converted to base64 data URIs.
22
- * For ZIP export, these are written as separate files.
23
- */
24
- images?: Map<string, ArrayBuffer>;
25
- /**
26
- * Map of audio segment identifiers to binary audio data.
27
- * Keys should match the audio segment `name` or `url` fields in the Doc.
28
- * Only used in ZIP exports — single HTML uses timer-based playback.
29
- */
30
- audio?: Map<string, ArrayBuffer>;
31
- /** Rendering mode: 'slideshow' (interactive, default) or 'static' (scrollable) */
32
- mode?: 'slideshow' | 'static';
33
- /** HTML page title (default: 'Squisq Document') */
34
- title?: string;
35
- /** Auto-play slideshow on load (default: false) */
36
- autoPlay?: boolean;
37
- /**
38
- * Squisq theme ID to apply (e.g., 'documentary', 'cinematic').
39
- * When set, the theme is assigned to the Doc before rendering,
40
- * so the SquisqPlayer renders with that theme's colors and typography.
41
- */
42
- themeId?: string;
43
- }
44
- /**
45
- * Collect all relative image paths referenced in a Doc's layers and template blocks.
46
- * Returns a Set of unique relative paths that need to be resolved.
47
- */
48
- declare function collectImagePaths(doc: Doc): Set<string>;
49
-
50
6
  /**
51
7
  * Image Utilities for HTML Export
52
8
  *
@@ -134,6 +90,8 @@ interface PlainHtmlExportOptions {
134
90
  * When both `theme` and `themeId` are provided, `theme` wins.
135
91
  */
136
92
  themeId?: string;
93
+ /** Explicit caller-owned registry for non-document custom themes. */
94
+ themeRegistry?: ThemeRegistry;
137
95
  /**
138
96
  * Optional FontAwesome CSS text to inline into the rendered page,
139
97
  * replacing the default cross-origin `<link>` to cdnjs. Required for
@@ -191,13 +149,14 @@ interface PlainHtmlBundleOptions {
191
149
  /** Optional theme applied uniformly to every page. Overrides {@link themeId}. */
192
150
  theme?: Theme;
193
151
  /**
194
- * Optional theme id (e.g. `'warm-earth'`, `'gezellig'`). Resolved via
195
- * `resolveTheme` and applied to every page. Convenient for callers
196
- * that track themes by id (like the host export dialog) without
197
- * having to resolve to a `Theme` object first. When both `theme` and
198
- * `themeId` are supplied, `theme` wins.
152
+ * Optional theme id (e.g. `'warm-earth'`, `'gezellig'`) applied to every
153
+ * page. Each document resolves its own inline definition before consulting
154
+ * {@link themeRegistry}. When both `theme` and `themeId` are supplied,
155
+ * `theme` wins.
199
156
  */
200
157
  themeId?: string;
158
+ /** Explicit caller-owned registry for non-document custom themes. */
159
+ themeRegistry?: ThemeRegistry;
201
160
  /** Maximum recursion depth (default: unlimited; cycles always handled). */
202
161
  maxDepth?: number;
203
162
  /**
@@ -242,6 +201,7 @@ declare function collectLinkRefs(doc: MarkdownDocument): Set<string>;
242
201
  * `<a href>` at runtime — there is no post-render link rewrite hook in
243
202
  * the player path.
244
203
  */
204
+
245
205
  interface HtmlBundleOptions {
246
206
  /** Entry document path relative to the container root (e.g. `'home.md'`). */
247
207
  entryPath: string;
@@ -255,6 +215,8 @@ interface HtmlBundleOptions {
255
215
  title?: string;
256
216
  /** Theme id applied uniformly to every page. */
257
217
  themeId?: string;
218
+ /** Explicit caller-owned registry for non-document custom themes. */
219
+ themeRegistry?: ThemeRegistry;
258
220
  /** Rendering mode for every page (default: 'static' — scrollable, link-friendly). */
259
221
  mode?: 'slideshow' | 'static';
260
222
  /** Maximum recursion depth (default: unlimited; cycles always handled). */
@@ -278,38 +240,6 @@ interface HtmlBundleOptions {
278
240
  */
279
241
  declare function markdownDocsToHtmlBundle(options: HtmlBundleOptions): Promise<Blob>;
280
242
 
281
- /**
282
- * HTML Import Module — @bendyline/squisq-formats/html
283
- *
284
- * Converts an HTML document (or fragment) into a squisq `MarkdownDocument`.
285
- * Built on the existing `parseHtmlToNodes` (parse5-backed) + `sanitizeHtmlNodes`
286
- * from `@bendyline/squisq/markdown`, so no new dependency is introduced.
287
- *
288
- * The primary consumer is email: HTML mail bodies are hostile input, so the
289
- * parse is sanitized by default (scripts/styles/event handlers/dangerous URLs
290
- * stripped) before the tree is walked into markdown nodes.
291
- *
292
- * Two entry points mirror the docx/pdf importers:
293
- * - `htmlToMarkdownDoc(data, options?)` → `MarkdownDocument`
294
- * - `htmlToMarkdown(html, options?)` → markdown string (convenience for the
295
- * string-in/string-out path email body conversion needs)
296
- */
297
-
298
- interface HtmlImportOptions {
299
- /**
300
- * Strip scripts / styles / event handlers / dangerous URLs before walking
301
- * the tree. Default `true` — HTML email is untrusted; only disable for
302
- * trusted input where you want raw fidelity.
303
- */
304
- sanitize?: boolean;
305
- }
306
- /** Parse HTML into a squisq `MarkdownDocument`. */
307
- declare function htmlToMarkdownDocSync(html: string, options?: HtmlImportOptions): MarkdownDocument;
308
- /** Async, ArrayBuffer-accepting entry mirroring docx/pdf importers. */
309
- declare function htmlToMarkdownDoc(data: ArrayBuffer | Uint8Array | string, options?: HtmlImportOptions): Promise<MarkdownDocument>;
310
- /** Convenience string→string conversion (used for email HTML bodies). */
311
- declare function htmlToMarkdown(html: string, options?: HtmlImportOptions): string;
312
-
313
243
  /**
314
244
  * HTML Export Module — @bendyline/squisq-formats/html
315
245
  *
@@ -404,4 +334,4 @@ declare function docToHtml(doc: Doc, options: HtmlExportOptions): string;
404
334
  */
405
335
  declare function docToHtmlZip(doc: Doc, options: HtmlZipExportOptions): Promise<Blob>;
406
336
 
407
- export { type HtmlBundleOptions, type HtmlExportOptions, type HtmlImportOptions, type HtmlZipExportOptions, type PlainHtmlBundleOptions, type PlainHtmlExportOptions, arrayBufferToBase64DataUrl, collectImagePaths, collectLinkRefs, docToHtml, docToHtmlZip, extractFilename, htmlToMarkdown, htmlToMarkdownDoc, htmlToMarkdownDocSync, inferMimeType, markdownDocToPlainHtml, markdownDocsToHtmlBundle, markdownDocsToPlainHtmlBundle };
337
+ export { type HtmlBundleOptions, HtmlExportOptions, type HtmlZipExportOptions, type PlainHtmlBundleOptions, type PlainHtmlExportOptions, arrayBufferToBase64DataUrl, collectLinkRefs, docToHtml, docToHtmlZip, extractFilename, inferMimeType, markdownDocToPlainHtml, markdownDocsToHtmlBundle, markdownDocsToPlainHtmlBundle };
@@ -9,13 +9,13 @@ import {
9
9
  markdownDocToPlainHtml,
10
10
  markdownDocsToHtmlBundle,
11
11
  markdownDocsToPlainHtmlBundle
12
- } from "../chunk-46FMDOWZ.js";
12
+ } from "../chunk-LXYLOOST.js";
13
13
  import {
14
14
  arrayBufferToBase64DataUrl,
15
15
  extractFilename,
16
16
  inferMimeType
17
- } from "../chunk-MJGRI6XR.js";
18
- import "../chunk-DQAZR57U.js";
17
+ } from "../chunk-MLX2BOJC.js";
18
+ import "../chunk-O3GVVND4.js";
19
19
  export {
20
20
  arrayBufferToBase64DataUrl,
21
21
  collectImagePaths,