@docen/docx 0.3.1 → 0.3.3

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 (114) hide show
  1. package/dist/converters/docx.d.mts +220 -1
  2. package/dist/converters/docx.mjs +1352 -1
  3. package/dist/converters/html.d.mts +28 -1
  4. package/dist/converters/html.mjs +103 -1
  5. package/dist/converters/markdown.d.mts +12 -1
  6. package/dist/converters/markdown.mjs +17 -1
  7. package/dist/converters/patch.d.mts +52 -1
  8. package/dist/converters/patch.mjs +35 -1
  9. package/dist/converters/prepare.d.mts +62 -1
  10. package/dist/converters/prepare.mjs +121 -1
  11. package/dist/converters/styles.d.mts +91 -2
  12. package/dist/converters/styles.mjs +308 -2
  13. package/dist/core-B8ba_FNi.mjs +717 -0
  14. package/dist/core-DRaLI8nd.d.mts +203 -0
  15. package/dist/core.d.mts +1 -1
  16. package/dist/core.mjs +1 -1
  17. package/dist/document-Cws7XTYL.d.mts +28 -0
  18. package/dist/editor.d.mts +20 -1
  19. package/dist/editor.mjs +20 -1
  20. package/dist/extensions/blockquote.d.mts +1 -1
  21. package/dist/extensions/blockquote.mjs +30 -2
  22. package/dist/extensions/code-block.d.mts +1 -1
  23. package/dist/extensions/code-block.mjs +22 -1
  24. package/dist/extensions/column-break.d.mts +1 -1
  25. package/dist/extensions/column-break.mjs +1 -1
  26. package/dist/extensions/details.d.mts +1 -1
  27. package/dist/extensions/details.mjs +17 -2
  28. package/dist/extensions/document.d.mts +1 -1
  29. package/dist/extensions/document.mjs +45 -1
  30. package/dist/extensions/extensions.d.mts +4 -5
  31. package/dist/extensions/extensions.mjs +13 -14
  32. package/dist/extensions/formatting-marks.d.mts +7 -1
  33. package/dist/extensions/formatting-marks.mjs +1 -1
  34. package/dist/extensions/heading.mjs +187 -1
  35. package/dist/extensions/image.d.mts +1 -1
  36. package/dist/extensions/image.mjs +395 -1
  37. package/dist/extensions/index.d.mts +4 -3
  38. package/dist/extensions/index.mjs +4 -3
  39. package/dist/extensions/mention.d.mts +1 -1
  40. package/dist/extensions/mention.mjs +43 -2
  41. package/dist/extensions/ordered-list.d.mts +1 -1
  42. package/dist/extensions/ordered-list.mjs +40 -2
  43. package/dist/extensions/page-break.d.mts +1 -33
  44. package/dist/extensions/page-break.mjs +1 -1
  45. package/dist/extensions/paragraph.mjs +145 -1
  46. package/dist/extensions/passthrough.d.mts +1 -1
  47. package/dist/extensions/passthrough.mjs +1 -1
  48. package/dist/extensions/section-break.d.mts +19 -2
  49. package/dist/extensions/section-break.mjs +1 -1
  50. package/dist/extensions/strike.mjs +45 -1
  51. package/dist/extensions/table-cell.mjs +106 -1
  52. package/dist/extensions/table-header.mjs +106 -1
  53. package/dist/extensions/table-row.mjs +78 -1
  54. package/dist/extensions/table.mjs +154 -1
  55. package/dist/extensions/task-item.d.mts +1 -1
  56. package/dist/extensions/task-item.mjs +36 -2
  57. package/dist/extensions/text-style.mjs +145 -1
  58. package/dist/extensions/tiptap.d.mts +2 -2
  59. package/dist/extensions/tiptap.mjs +31 -2
  60. package/dist/extensions/toc-field.d.mts +2 -0
  61. package/dist/extensions/toc-field.mjs +2 -0
  62. package/dist/extensions/utils.d.mts +2 -62
  63. package/dist/extensions/utils.mjs +508 -2
  64. package/dist/extensions/wpg-group.d.mts +2 -0
  65. package/dist/extensions/wpg-group.mjs +2 -0
  66. package/dist/extensions/wps-shape.d.mts +2 -0
  67. package/dist/extensions/wps-shape.mjs +2 -0
  68. package/dist/index.d.mts +101 -29
  69. package/dist/index.mjs +13 -10
  70. package/dist/{tiptap-TErPjuNJ.d.mts → tiptap-pZsNPsvV.d.mts} +1 -3
  71. package/dist/utils-BJwDQts7.d.mts +137 -0
  72. package/package.json +39 -40
  73. package/dist/blockquote-Ky9xD_Q-.mjs +0 -31
  74. package/dist/code-block-BgNjhADy.mjs +0 -23
  75. package/dist/core-DEqBHRzE.mjs +0 -406
  76. package/dist/core-fBMCCj4D.d.mts +0 -97
  77. package/dist/details-Dh8luWb2.mjs +0 -18
  78. package/dist/dist-B6B3HnVZ.mjs +0 -1838
  79. package/dist/document-BH1y4qHM.d.mts +0 -6
  80. package/dist/document-CvTym-FM.mjs +0 -50
  81. package/dist/docx-C5wyjI-0.d.mts +0 -200
  82. package/dist/docx-CONyAedl.mjs +0 -1231
  83. package/dist/editor-Bf3CxQnY.mjs +0 -21
  84. package/dist/editor-DReY3Rb9.d.mts +0 -21
  85. package/dist/emoji-BZ1dJD7d.d.mts +0 -17
  86. package/dist/emoji-C2OsTWR7.mjs +0 -27
  87. package/dist/extensions/emoji.d.mts +0 -2
  88. package/dist/extensions/emoji.mjs +0 -2
  89. package/dist/extensions/image-group.d.mts +0 -6
  90. package/dist/extensions/image-group.mjs +0 -2
  91. package/dist/heading-DAubgJJN.mjs +0 -166
  92. package/dist/html-DCC_DSHv.d.mts +0 -18
  93. package/dist/html-DedDWPoO.mjs +0 -66
  94. package/dist/image-CuEIXoZl.mjs +0 -334
  95. package/dist/markdown-BzJmRsBs.mjs +0 -18
  96. package/dist/markdown-D0k0sRYS.d.mts +0 -13
  97. package/dist/mention-C-dTR6MK.mjs +0 -44
  98. package/dist/ordered-list-V-3xFQmV.mjs +0 -41
  99. package/dist/paragraph-BrWH-BOV.mjs +0 -147
  100. package/dist/patch-Cdp5WKJz.d.mts +0 -53
  101. package/dist/patch-IZ13tXTu.mjs +0 -36
  102. package/dist/prepare-Bdk8Cjkf.mjs +0 -122
  103. package/dist/prepare-CPAvcv6q.d.mts +0 -63
  104. package/dist/strike-hWxShOgb.mjs +0 -50
  105. package/dist/styles-QSX3RhxK.mjs +0 -285
  106. package/dist/styles-ugspkrxz.d.mts +0 -85
  107. package/dist/table-DrA12lxX.mjs +0 -159
  108. package/dist/table-cell-DmkfYwDc.mjs +0 -112
  109. package/dist/table-header-Bwr5GKWg.mjs +0 -112
  110. package/dist/table-row-vew1y7WH.mjs +0 -84
  111. package/dist/task-item-BnlXGrIG.mjs +0 -37
  112. package/dist/text-style-tMeBq5XQ.mjs +0 -151
  113. package/dist/tiptap-D7pl4I5U.mjs +0 -33
  114. package/dist/utils-BPT4tjcn.mjs +0 -333
@@ -1,2 +1,308 @@
1
- import { a as stylesToCss, i as quickStyles, n as effectiveRunProps, r as inlineStyles, t as defaultParagraphStyleId } from "../styles-QSX3RhxK.mjs";
2
- export { defaultParagraphStyleId, effectiveRunProps, inlineStyles, quickStyles, stylesToCss };
1
+ import { renderParagraphStyles, renderRunStyles, resolveFontName } from "../extensions/utils.mjs";
2
+ //#region src/converters/styles.ts
3
+ /** Escape a style id for safe use in a CSS class selector. OOXML style ids are
4
+ * NCNames so this is rarely needed, but it keeps arbitrary ids safe. */
5
+ function escapeClass(id) {
6
+ return id.replace(/[^A-Za-z0-9_-]/g, (ch) => `\\${ch}`);
7
+ }
8
+ /** The pStyle val (class hook `docx-style-{id}`) for a built-in named style
9
+ * nested under DefaultStylesOptions: the key with its first letter upper-cased
10
+ * ("heading1" → "Heading1", "title" → "Title", "listParagraph" → "ListParagraph").
11
+ * This matches office-open's HeadingLevel literals / pStyle ids, so we derive
12
+ * the id from the key instead of hard-coding a name table. */
13
+ function pStyleIdFromKey(key) {
14
+ return key.charAt(0).toUpperCase() + key.slice(1);
15
+ }
16
+ /** The styleId of the document's default paragraph style (`w:default="1"`
17
+ * type="paragraph") — the implicit style applied to every paragraph WITHOUT an
18
+ * explicit pStyle. OOXML renders a pStyle-less paragraph as this style (usually
19
+ * "Normal"), so the editor's `.docx-default` class — emitted by Paragraph/
20
+ * Heading renderHTML on a styleId-less node — targets it. Searched in
21
+ * `paragraphStyles` and the built-in named styles nested under `default`
22
+ * (key → pStyle id). null when the document declares none. */
23
+ function defaultParagraphStyleId(styles) {
24
+ if (!styles) return null;
25
+ for (const ps of styles.paragraphStyles ?? []) if (ps.default) return ps.id;
26
+ const defaults = styles.default;
27
+ for (const [key, style] of Object.entries(defaults ?? {})) {
28
+ if (key === "document" || !style) continue;
29
+ if (style.default) return pStyleIdFromKey(key);
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * Generate scoped CSS from a document's `styles.xml` model (`StylesOptions`) so
35
+ * named paragraph/character styles and the document defaults render correctly
36
+ * in the editor. All rules are scoped to `.docen-page` (the editor's page
37
+ * surface); the editor injects the result into a `<style>` after load.
38
+ *
39
+ * - `default.document` → `.docen-page` base run + paragraph defaults (the doc's
40
+ * default font/size, line spacing, …).
41
+ * - `paragraphStyles[]` → `.docen-page .docx-style-{id}` (run + paragraph CSS).
42
+ * Paragraph nodes carry `class="docx-style-{styleId}"` (see the Paragraph /
43
+ * Heading extensions' pStyle ↔ styleId round-trip).
44
+ * - `characterStyles[]` → `.docen-page .docx-char-{id}` (run CSS).
45
+ *
46
+ * `basedOn` inheritance is deep-merged per-property into each named style's
47
+ * rule (root first, child overrides) — see mergeStyleChain. CSS inheritance
48
+ * follows the DOM tree, not a style's basedOn, so each `.docx-style-{id}` rule
49
+ * carries its full ancestor chain rather than relying on source order.
50
+ */
51
+ function stylesToCss(styles, scope) {
52
+ if (!styles) return "";
53
+ const rules = [];
54
+ const within = scope ? `${scope} ` : "";
55
+ const doc = styles.default?.document;
56
+ if (doc) {
57
+ const runDecls = doc.run ? renderRunStyles(doc.run) : [];
58
+ if (runDecls.length && scope) rules.push(`${scope} { ${runDecls.join(";")} }`);
59
+ const paraDecls = doc.paragraph ? renderParagraphStyles(doc.paragraph) : [];
60
+ if (paraDecls.length) {
61
+ const paraSel = `${within}p, ${within}h1, ${within}h2, ${within}h3, ${within}h4, ${within}h5, ${within}h6`;
62
+ rules.push(`${paraSel} { ${paraDecls.join(";")} }`);
63
+ }
64
+ }
65
+ const byId = indexParagraphStyles(styles);
66
+ const defaultId = defaultParagraphStyleId(styles);
67
+ for (const id of byId.keys()) {
68
+ const { run, paragraph } = mergeStyleChain(byId, id);
69
+ const decls = [];
70
+ if (Object.keys(run).length) decls.push(...renderRunStyles(run));
71
+ if (Object.keys(paragraph).length) decls.push(...renderParagraphStyles(paragraph));
72
+ if (!decls.length) continue;
73
+ const selector = id === defaultId ? `${within}.docx-default, ${within}.docx-style-${escapeClass(id)}` : `${within}.docx-style-${escapeClass(id)}`;
74
+ rules.push(`${selector} { ${decls.join(";")} }`);
75
+ }
76
+ for (const cs of styles.characterStyles ?? []) {
77
+ const decls = [];
78
+ if (cs.run) decls.push(...renderRunStyles(cs.run));
79
+ if (decls.length) rules.push(`${within}.docx-char-${escapeClass(cs.id)} { ${decls.join(";")} }`);
80
+ }
81
+ return rules.join("\n");
82
+ }
83
+ /** Build an id → style-entry index over every paragraph style: the explicit
84
+ * `paragraphStyles` plus the built-in named styles nested under `default`
85
+ * (key → pStyle id via pStyleIdFromKey). `document` is docDefaults, not a
86
+ * named style, so it is excluded. A built-in that also appears in
87
+ * paragraphStyles is deduped by id (paragraphStyles wins on insertion order). */
88
+ const styleIndexCache = /* @__PURE__ */ new WeakMap();
89
+ function indexParagraphStyles(styles) {
90
+ const cached = styleIndexCache.get(styles);
91
+ if (cached) return cached;
92
+ const byId = /* @__PURE__ */ new Map();
93
+ for (const ps of styles.paragraphStyles ?? []) byId.set(ps.id, ps);
94
+ const defaults = styles.default;
95
+ for (const [key, style] of Object.entries(defaults ?? {})) {
96
+ if (key === "document" || !style) continue;
97
+ byId.set(pStyleIdFromKey(key), style);
98
+ }
99
+ styleIndexCache.set(styles, byId);
100
+ return byId;
101
+ }
102
+ /** Whether `v` is a plain object — an OOXML property group (spacing/indent/
103
+ * border/shading/font) that merges key by key — as opposed to an array
104
+ * (tabStops) or scalar, which replace. */
105
+ function isPlainObject(v) {
106
+ return typeof v === "object" && v !== null && !Array.isArray(v);
107
+ }
108
+ /** Deep-merge `source` into `target` (mutates target) per the OOXML `basedOn`
109
+ * model: nested property groups merge key by key (a child's spacing.before
110
+ * merges with, not replaces, the parent's spacing.line); arrays and scalars
111
+ * replace. Nullish source values are skipped so an unset child key doesn't
112
+ * clobber an inherited value. */
113
+ function deepMergeInto(target, source) {
114
+ for (const [key, srcVal] of Object.entries(source)) {
115
+ if (srcVal === null || srcVal === void 0) continue;
116
+ const tgtVal = target[key];
117
+ target[key] = isPlainObject(srcVal) && isPlainObject(tgtVal) ? deepMergeInto({ ...tgtVal }, srcVal) : isPlainObject(srcVal) ? { ...srcVal } : srcVal;
118
+ }
119
+ return target;
120
+ }
121
+ /** Merge a paragraph style's run/paragraph properties with its `basedOn` chain
122
+ * (root first, child overrides per-property) — the OOXML inheritance model.
123
+ * Nested property groups (spacing/indent/border/font) merge key by key; arrays
124
+ * and scalars replace. Shared by stylesToCss (rendering) and effectiveRunProps
125
+ * (the caret resolver) so the gallery box and the rendered page resolve
126
+ * identical values. */
127
+ function mergeStyleChain(byId, styleId) {
128
+ const chain = [];
129
+ const visited = /* @__PURE__ */ new Set();
130
+ let curId = styleId || void 0;
131
+ while (curId && !visited.has(curId)) {
132
+ visited.add(curId);
133
+ const style = byId.get(curId);
134
+ if (!style) break;
135
+ chain.unshift(style);
136
+ curId = style.basedOn ?? void 0;
137
+ }
138
+ const run = {};
139
+ const paragraph = {};
140
+ for (const style of chain) {
141
+ const s = style;
142
+ if (s.run) deepMergeInto(run, s.run);
143
+ if (s.paragraph) deepMergeInto(paragraph, s.paragraph);
144
+ }
145
+ return {
146
+ run,
147
+ paragraph
148
+ };
149
+ }
150
+ /** The `DefaultStylesOptions` keys whose values are character styles, not
151
+ * paragraph styles. The Quick Styles gallery is paragraph-only, so these are
152
+ * excluded even when flagged `quickFormat` (the authoritative source is
153
+ * office-open's DefaultStylesOptions interface). */
154
+ const CHARACTER_DEFAULT_KEYS = new Set([
155
+ "hyperlink",
156
+ "footnoteReference",
157
+ "footnoteTextChar",
158
+ "endnoteReference",
159
+ "endnoteTextChar"
160
+ ]);
161
+ /**
162
+ * The paragraph styles to list in the Quick Styles gallery, matching Word's
163
+ * default behavior: the gallery is a *paragraph-style* selector (it applies a
164
+ * pStyle), so only paragraph styles appear — never character styles, even
165
+ * those flagged `quickFormat` (those live in the Styles task pane). Among
166
+ * paragraph styles, only those flagged `quickFormat` are listed, ordered by
167
+ * `uiPriority` (Word orders the gallery this way).
168
+ *
169
+ * Reads `quickFormat`/`uiPriority`/`name` straight from office-open's styles
170
+ * model (`StylesOptions`): `paragraphStyles` (Normal + custom) and the built-in
171
+ * named paragraph styles nested under `default` (title/heading1-9/quote/…). The
172
+ * `default` keys that hold character styles are excluded via
173
+ * `CHARACTER_DEFAULT_KEYS`. When a document carries no quickFormat flags at all
174
+ * (e.g. some LibreOffice-generated files), fall back to all paragraph styles so
175
+ * the gallery is never empty.
176
+ */
177
+ function quickStyles(styles) {
178
+ if (!styles) return [];
179
+ const all = [];
180
+ const seen = /* @__PURE__ */ new Set();
181
+ const push = (id, style) => {
182
+ if (seen.has(id)) return;
183
+ seen.add(id);
184
+ all.push({
185
+ id,
186
+ name: style.name || id,
187
+ uiPriority: style.uiPriority ?? 9999,
188
+ quick: !!style.quickFormat
189
+ });
190
+ };
191
+ for (const ps of styles.paragraphStyles ?? []) push(ps.id, ps);
192
+ const defaults = styles.default;
193
+ for (const [key, style] of Object.entries(defaults)) {
194
+ if (key === "document" || CHARACTER_DEFAULT_KEYS.has(key) || !style) continue;
195
+ push(pStyleIdFromKey(key), style);
196
+ }
197
+ const byPriority = (a, b) => a.uiPriority - b.uiPriority;
198
+ const quick = all.filter((s) => s.quick).sort(byPriority);
199
+ return (quick.length > 0 ? quick : all).map(({ id, name }) => ({
200
+ id,
201
+ name
202
+ }));
203
+ }
204
+ /** Resolve the effective run-level properties (font name, size in points) at the
205
+ * caret, staying in the document's own units — no px conversion. Priority:
206
+ * direct run props (the textStyle mark) → the paragraph style (`styleId`) →
207
+ * its `basedOn` chain → the document defaults. `font` is resolved to a single
208
+ * display name (ascii/hAnsi/eastAsia). Returns null where nothing in the chain
209
+ * sets a property, so the caller can leave the box empty. */
210
+ function effectiveRunProps(styles, styleId, direct) {
211
+ let font = null;
212
+ let size = null;
213
+ if (direct) {
214
+ font = resolveFontName(direct.font);
215
+ if (typeof direct.size === "number" && direct.size > 0) size = direct.size;
216
+ }
217
+ if ((font == null || size == null) && styles) {
218
+ const effStyleId = styleId || defaultParagraphStyleId(styles);
219
+ const { run } = mergeStyleChain(indexParagraphStyles(styles), effStyleId);
220
+ if (font == null) font = resolveFontName(run.font);
221
+ if (size == null && typeof run.size === "number" && run.size > 0) size = run.size;
222
+ if (font == null || size == null) {
223
+ const docRun = styles.default?.document?.run;
224
+ if (docRun) {
225
+ if (font == null) font = resolveFontName(docRun.font);
226
+ if (size == null && typeof docRun.size === "number" && docRun.size > 0) size = docRun.size;
227
+ }
228
+ }
229
+ }
230
+ return {
231
+ font,
232
+ size
233
+ };
234
+ }
235
+ /** Index a document's character styles (explicit `characterStyles` + the
236
+ * built-in character keys under `default`) by pStyle id → run properties. Core
237
+ * phase: a character style's own run only (its basedOn chain is not walked —
238
+ * rare in practice; paragraph styles above already deep-merge). */
239
+ function indexCharacterRunStyles(styles) {
240
+ const byId = /* @__PURE__ */ new Map();
241
+ for (const cs of styles.characterStyles ?? []) if (cs.run) byId.set(cs.id, cs.run);
242
+ const defaults = styles.default;
243
+ for (const [key, style] of Object.entries(defaults ?? {})) {
244
+ if (!style || !style.run || key === "document") continue;
245
+ if (!CHARACTER_DEFAULT_KEYS.has(key)) continue;
246
+ byId.set(pStyleIdFromKey(key), style.run);
247
+ }
248
+ return byId;
249
+ }
250
+ /** Resolve one node's style inheritance: a paragraph/heading absorbs its
251
+ * styleId's basedOn chain (paragraph properties + the default `run`); a
252
+ * textStyle mark absorbs its character style's run. Explicit attrs/marks
253
+ * override the inherited values; styleId is preserved. Pure JSON — no DOM, no
254
+ * marks pushed onto text. */
255
+ function resolveNode(node, paraById, charRunById) {
256
+ const out = { ...node };
257
+ const attrs = node.attrs;
258
+ const type = node.type;
259
+ if ((type === "paragraph" || type === "heading") && attrs?.styleId) {
260
+ const { paragraph, run } = mergeStyleChain(paraById, attrs.styleId);
261
+ const mergedRun = {
262
+ ...run,
263
+ ...attrs.run
264
+ };
265
+ out.attrs = {
266
+ ...paragraph,
267
+ ...attrs,
268
+ run: mergedRun
269
+ };
270
+ }
271
+ if (node.marks) out.marks = node.marks.map((m) => {
272
+ if (m.type !== "textStyle") return m;
273
+ const sid = m.attrs?.styleId;
274
+ const crun = sid ? charRunById.get(sid) : void 0;
275
+ return crun ? {
276
+ ...m,
277
+ attrs: {
278
+ ...crun,
279
+ ...m.attrs
280
+ }
281
+ } : m;
282
+ });
283
+ if (node.content) out.content = node.content.map((c) => resolveNode(c, paraById, charRunById));
284
+ return out;
285
+ }
286
+ /**
287
+ * Inline a document's styles into each node's attrs — a self-contained
288
+ * JSONContent that round-trips and renders without the styles context. A
289
+ * paragraph/heading absorbs its styleId's `basedOn` chain (paragraph properties
290
+ * + the paragraph's default `run`); a textStyle mark absorbs its character
291
+ * style's run. Explicit attrs/marks always override the inherited values;
292
+ * styleId is preserved (the semantic reference is kept). The style definitions
293
+ * themselves are untouched — only properties are copied onto nodes.
294
+ *
295
+ * Styles default to the document's own `attrs.styles` (the round-tripped
296
+ * styles.xml model carried on the doc node), so `inlineStyles(doc)` needs no
297
+ * second argument; pass `styles` explicitly to override. Use cases:
298
+ * cross-document paste (the snippet carries its styling) and self-contained
299
+ * export. The editor still renders via global styles — this only merges JSON
300
+ * properties.
301
+ */
302
+ function inlineStyles(json, styles) {
303
+ const docStyles = styles ?? json.attrs?.styles;
304
+ if (!docStyles) return json;
305
+ return resolveNode(json, indexParagraphStyles(docStyles), indexCharacterRunStyles(docStyles));
306
+ }
307
+ //#endregion
308
+ export { defaultParagraphStyleId, effectiveRunProps, indexParagraphStyles, inlineStyles, quickStyles, stylesToCss };