@docen/docx 0.3.0 → 0.3.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.
- package/README.md +2 -3
- package/dist/blockquote-Ky9xD_Q-.mjs +31 -0
- package/dist/code-block-BgNjhADy.mjs +23 -0
- package/dist/converters/docx.d.mts +1 -109
- package/dist/converters/docx.mjs +1 -623
- package/dist/converters/html.d.mts +1 -13
- package/dist/converters/html.mjs +1 -17
- package/dist/converters/markdown.d.mts +1 -12
- package/dist/converters/markdown.mjs +1 -17
- package/dist/converters/patch.d.mts +1 -52
- package/dist/converters/patch.mjs +1 -36
- package/dist/converters/prepare.d.mts +2 -51
- package/dist/converters/prepare.mjs +2 -76
- package/dist/converters/styles.d.mts +2 -0
- package/dist/converters/styles.mjs +2 -0
- package/dist/core-DEqBHRzE.mjs +406 -0
- package/dist/core-fBMCCj4D.d.mts +97 -0
- package/dist/core.d.mts +1 -1
- package/dist/core.mjs +1 -1
- package/dist/details-Dh8luWb2.mjs +18 -0
- package/dist/dist-B6B3HnVZ.mjs +1838 -0
- package/dist/document-BH1y4qHM.d.mts +6 -0
- package/dist/document-CvTym-FM.mjs +50 -0
- package/dist/docx-C5wyjI-0.d.mts +200 -0
- package/dist/docx-CONyAedl.mjs +1231 -0
- package/dist/editor-Bf3CxQnY.mjs +21 -0
- package/dist/editor-DReY3Rb9.d.mts +21 -0
- package/dist/editor.d.mts +1 -20
- package/dist/editor.mjs +1 -19
- package/dist/emoji-BZ1dJD7d.d.mts +17 -0
- package/dist/emoji-C2OsTWR7.mjs +27 -0
- package/dist/extensions/blockquote.d.mts +23 -0
- package/dist/extensions/blockquote.mjs +3 -0
- package/dist/extensions/code-block.d.mts +19 -0
- package/dist/extensions/code-block.mjs +2 -0
- package/dist/extensions/column-break.d.mts +31 -0
- package/dist/extensions/column-break.mjs +2 -0
- package/dist/extensions/details.d.mts +19 -0
- package/dist/extensions/details.mjs +3 -0
- package/dist/extensions/document.d.mts +2 -0
- package/dist/extensions/document.mjs +2 -0
- package/dist/extensions/emoji.d.mts +2 -0
- package/dist/extensions/emoji.mjs +2 -0
- package/dist/extensions/extensions.d.mts +6 -5
- package/dist/extensions/extensions.mjs +14 -12
- package/dist/extensions/formatting-marks.d.mts +32 -0
- package/dist/extensions/formatting-marks.mjs +2 -0
- package/dist/extensions/heading.mjs +1 -144
- package/dist/extensions/image-group.d.mts +6 -0
- package/dist/extensions/image-group.mjs +2 -0
- package/dist/extensions/image.d.mts +2 -2
- package/dist/extensions/image.mjs +1 -200
- package/dist/extensions/index.d.mts +3 -2
- package/dist/extensions/index.mjs +3 -2
- package/dist/extensions/mention.d.mts +25 -0
- package/dist/extensions/mention.mjs +3 -0
- package/dist/extensions/ordered-list.d.mts +25 -0
- package/dist/extensions/ordered-list.mjs +3 -0
- package/dist/extensions/page-break.d.mts +34 -0
- package/dist/extensions/page-break.mjs +2 -0
- package/dist/extensions/paragraph.d.mts +1 -1
- package/dist/extensions/paragraph.mjs +1 -115
- package/dist/extensions/passthrough.d.mts +2 -0
- package/dist/extensions/passthrough.mjs +2 -0
- package/dist/extensions/section-break.d.mts +27 -0
- package/dist/extensions/section-break.mjs +2 -0
- package/dist/extensions/strike.mjs +1 -49
- package/dist/extensions/table-cell.mjs +1 -111
- package/dist/extensions/table-header.mjs +1 -111
- package/dist/extensions/table-row.mjs +1 -83
- package/dist/extensions/table.mjs +1 -133
- package/dist/extensions/task-item.d.mts +27 -0
- package/dist/extensions/task-item.mjs +3 -0
- package/dist/extensions/text-style.mjs +1 -133
- package/dist/extensions/tiptap.mjs +1 -32
- package/dist/extensions/types.d.mts +7 -5
- package/dist/extensions/utils.d.mts +16 -3
- package/dist/extensions/utils.mjs +2 -289
- package/dist/heading-DAubgJJN.mjs +166 -0
- package/dist/html-DCC_DSHv.d.mts +18 -0
- package/dist/html-DedDWPoO.mjs +66 -0
- package/dist/image-CuEIXoZl.mjs +334 -0
- package/dist/index.d.mts +68 -18
- package/dist/index.mjs +10 -8
- package/dist/markdown-BzJmRsBs.mjs +18 -0
- package/dist/markdown-D0k0sRYS.d.mts +13 -0
- package/dist/mention-C-dTR6MK.mjs +44 -0
- package/dist/ordered-list-V-3xFQmV.mjs +41 -0
- package/dist/paragraph-BrWH-BOV.mjs +147 -0
- package/dist/paragraph-D8mpHo_o.d.mts +8 -0
- package/dist/patch-Cdp5WKJz.d.mts +53 -0
- package/dist/patch-IZ13tXTu.mjs +36 -0
- package/dist/prepare-Bdk8Cjkf.mjs +122 -0
- package/dist/prepare-CPAvcv6q.d.mts +63 -0
- package/dist/strike-hWxShOgb.mjs +50 -0
- package/dist/styles-QSX3RhxK.mjs +285 -0
- package/dist/styles-ugspkrxz.d.mts +85 -0
- package/dist/table-DrA12lxX.mjs +159 -0
- package/dist/table-cell-DmkfYwDc.mjs +112 -0
- package/dist/table-header-Bwr5GKWg.mjs +112 -0
- package/dist/table-row-vew1y7WH.mjs +84 -0
- package/dist/task-item-BnlXGrIG.mjs +37 -0
- package/dist/text-style-tMeBq5XQ.mjs +151 -0
- package/dist/tiptap-D7pl4I5U.mjs +33 -0
- package/dist/utils-BPT4tjcn.mjs +333 -0
- package/package.json +3 -2
- package/dist/core-CFIQVRfx.mjs +0 -88
- package/dist/core-omBKMRtl.d.mts +0 -34
- package/dist/image-Ge1y6uam.d.mts +0 -47
- package/dist/paragraph-fhEXtAN2.d.mts +0 -16
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { f as renderParagraphStyles, h as resolveFontName, p as renderRunStyles } from "./utils-BPT4tjcn.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
|
+
function indexParagraphStyles(styles) {
|
|
89
|
+
const byId = /* @__PURE__ */ new Map();
|
|
90
|
+
for (const ps of styles.paragraphStyles ?? []) byId.set(ps.id, ps);
|
|
91
|
+
const defaults = styles.default;
|
|
92
|
+
for (const [key, style] of Object.entries(defaults ?? {})) {
|
|
93
|
+
if (key === "document" || !style) continue;
|
|
94
|
+
byId.set(pStyleIdFromKey(key), style);
|
|
95
|
+
}
|
|
96
|
+
return byId;
|
|
97
|
+
}
|
|
98
|
+
/** Merge a paragraph style's run/paragraph properties with its `basedOn` chain
|
|
99
|
+
* (root first, child overrides per-property) — the OOXML inheritance model. A
|
|
100
|
+
* flat property-level merge: a child property overrides the parent's; an unset
|
|
101
|
+
* property is inherited. Shared by stylesToCss (rendering) and
|
|
102
|
+
* effectiveRunProps (the caret resolver) so the gallery box and the rendered
|
|
103
|
+
* page resolve identical values. */
|
|
104
|
+
function mergeStyleChain(byId, styleId) {
|
|
105
|
+
const chain = [];
|
|
106
|
+
const visited = /* @__PURE__ */ new Set();
|
|
107
|
+
let curId = styleId || void 0;
|
|
108
|
+
while (curId && !visited.has(curId)) {
|
|
109
|
+
visited.add(curId);
|
|
110
|
+
const style = byId.get(curId);
|
|
111
|
+
if (!style) break;
|
|
112
|
+
chain.unshift(style);
|
|
113
|
+
curId = style.basedOn ?? void 0;
|
|
114
|
+
}
|
|
115
|
+
const run = {};
|
|
116
|
+
const paragraph = {};
|
|
117
|
+
for (const style of chain) {
|
|
118
|
+
const s = style;
|
|
119
|
+
if (s.run) Object.assign(run, s.run);
|
|
120
|
+
if (s.paragraph) Object.assign(paragraph, s.paragraph);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
run,
|
|
124
|
+
paragraph
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** The `DefaultStylesOptions` keys whose values are character styles, not
|
|
128
|
+
* paragraph styles. The Quick Styles gallery is paragraph-only, so these are
|
|
129
|
+
* excluded even when flagged `quickFormat` (the authoritative source is
|
|
130
|
+
* office-open's DefaultStylesOptions interface). */
|
|
131
|
+
const CHARACTER_DEFAULT_KEYS = new Set([
|
|
132
|
+
"hyperlink",
|
|
133
|
+
"footnoteReference",
|
|
134
|
+
"footnoteTextChar",
|
|
135
|
+
"endnoteReference",
|
|
136
|
+
"endnoteTextChar"
|
|
137
|
+
]);
|
|
138
|
+
/**
|
|
139
|
+
* The paragraph styles to list in the Quick Styles gallery, matching Word's
|
|
140
|
+
* default behavior: the gallery is a *paragraph-style* selector (it applies a
|
|
141
|
+
* pStyle), so only paragraph styles appear — never character styles, even
|
|
142
|
+
* those flagged `quickFormat` (those live in the Styles task pane). Among
|
|
143
|
+
* paragraph styles, only those flagged `quickFormat` are listed, ordered by
|
|
144
|
+
* `uiPriority` (Word orders the gallery this way).
|
|
145
|
+
*
|
|
146
|
+
* Reads `quickFormat`/`uiPriority`/`name` straight from office-open's styles
|
|
147
|
+
* model (`StylesOptions`): `paragraphStyles` (Normal + custom) and the built-in
|
|
148
|
+
* named paragraph styles nested under `default` (title/heading1-9/quote/…). The
|
|
149
|
+
* `default` keys that hold character styles are excluded via
|
|
150
|
+
* `CHARACTER_DEFAULT_KEYS`. When a document carries no quickFormat flags at all
|
|
151
|
+
* (e.g. some LibreOffice-generated files), fall back to all paragraph styles so
|
|
152
|
+
* the gallery is never empty.
|
|
153
|
+
*/
|
|
154
|
+
function quickStyles(styles) {
|
|
155
|
+
if (!styles) return [];
|
|
156
|
+
const all = [];
|
|
157
|
+
const seen = /* @__PURE__ */ new Set();
|
|
158
|
+
const push = (id, style) => {
|
|
159
|
+
if (seen.has(id)) return;
|
|
160
|
+
seen.add(id);
|
|
161
|
+
all.push({
|
|
162
|
+
id,
|
|
163
|
+
name: style.name || id,
|
|
164
|
+
uiPriority: style.uiPriority ?? 9999,
|
|
165
|
+
quick: !!style.quickFormat
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
for (const ps of styles.paragraphStyles ?? []) push(ps.id, ps);
|
|
169
|
+
const defaults = styles.default;
|
|
170
|
+
for (const [key, style] of Object.entries(defaults)) {
|
|
171
|
+
if (key === "document" || CHARACTER_DEFAULT_KEYS.has(key) || !style) continue;
|
|
172
|
+
push(pStyleIdFromKey(key), style);
|
|
173
|
+
}
|
|
174
|
+
const byPriority = (a, b) => a.uiPriority - b.uiPriority;
|
|
175
|
+
const quick = all.filter((s) => s.quick).sort(byPriority);
|
|
176
|
+
return (quick.length > 0 ? quick : all).map(({ id, name }) => ({
|
|
177
|
+
id,
|
|
178
|
+
name
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
/** Resolve the effective run-level properties (font name, size in points) at the
|
|
182
|
+
* caret, staying in the document's own units — no px conversion. Priority:
|
|
183
|
+
* direct run props (the textStyle mark) → the paragraph style (`styleId`) →
|
|
184
|
+
* its `basedOn` chain → the document defaults. `font` is resolved to a single
|
|
185
|
+
* display name (ascii/hAnsi/eastAsia). Returns null where nothing in the chain
|
|
186
|
+
* sets a property, so the caller can leave the box empty. */
|
|
187
|
+
function effectiveRunProps(styles, styleId, direct) {
|
|
188
|
+
let font = null;
|
|
189
|
+
let size = null;
|
|
190
|
+
if (direct) {
|
|
191
|
+
font = resolveFontName(direct.font);
|
|
192
|
+
if (typeof direct.size === "number" && direct.size > 0) size = direct.size;
|
|
193
|
+
}
|
|
194
|
+
if ((font == null || size == null) && styles) {
|
|
195
|
+
const effStyleId = styleId || defaultParagraphStyleId(styles);
|
|
196
|
+
const { run } = mergeStyleChain(indexParagraphStyles(styles), effStyleId);
|
|
197
|
+
if (font == null) font = resolveFontName(run.font);
|
|
198
|
+
if (size == null && typeof run.size === "number" && run.size > 0) size = run.size;
|
|
199
|
+
if (font == null || size == null) {
|
|
200
|
+
const docRun = styles.default?.document?.run;
|
|
201
|
+
if (docRun) {
|
|
202
|
+
if (font == null) font = resolveFontName(docRun.font);
|
|
203
|
+
if (size == null && typeof docRun.size === "number" && docRun.size > 0) size = docRun.size;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
font,
|
|
209
|
+
size
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/** Index a document's character styles (explicit `characterStyles` + the
|
|
213
|
+
* built-in character keys under `default`) by pStyle id → run properties. Core
|
|
214
|
+
* phase: a character style's own run only (its basedOn chain is not walked —
|
|
215
|
+
* rare in practice; paragraph styles above already deep-merge). */
|
|
216
|
+
function indexCharacterRunStyles(styles) {
|
|
217
|
+
const byId = /* @__PURE__ */ new Map();
|
|
218
|
+
for (const cs of styles.characterStyles ?? []) if (cs.run) byId.set(cs.id, cs.run);
|
|
219
|
+
const defaults = styles.default;
|
|
220
|
+
for (const [key, style] of Object.entries(defaults ?? {})) {
|
|
221
|
+
if (!style || !style.run || key === "document") continue;
|
|
222
|
+
if (!CHARACTER_DEFAULT_KEYS.has(key)) continue;
|
|
223
|
+
byId.set(pStyleIdFromKey(key), style.run);
|
|
224
|
+
}
|
|
225
|
+
return byId;
|
|
226
|
+
}
|
|
227
|
+
/** Resolve one node's style inheritance: a paragraph/heading absorbs its
|
|
228
|
+
* styleId's basedOn chain (paragraph properties + the default `run`); a
|
|
229
|
+
* textStyle mark absorbs its character style's run. Explicit attrs/marks
|
|
230
|
+
* override the inherited values; styleId is preserved. Pure JSON — no DOM, no
|
|
231
|
+
* marks pushed onto text. */
|
|
232
|
+
function resolveNode(node, paraById, charRunById) {
|
|
233
|
+
const out = { ...node };
|
|
234
|
+
const attrs = node.attrs;
|
|
235
|
+
const type = node.type;
|
|
236
|
+
if ((type === "paragraph" || type === "heading") && attrs?.styleId) {
|
|
237
|
+
const { paragraph, run } = mergeStyleChain(paraById, attrs.styleId);
|
|
238
|
+
const mergedRun = {
|
|
239
|
+
...run,
|
|
240
|
+
...attrs.run
|
|
241
|
+
};
|
|
242
|
+
out.attrs = {
|
|
243
|
+
...paragraph,
|
|
244
|
+
...attrs,
|
|
245
|
+
run: mergedRun
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (node.marks) out.marks = node.marks.map((m) => {
|
|
249
|
+
if (m.type !== "textStyle") return m;
|
|
250
|
+
const sid = m.attrs?.styleId;
|
|
251
|
+
const crun = sid ? charRunById.get(sid) : void 0;
|
|
252
|
+
return crun ? {
|
|
253
|
+
...m,
|
|
254
|
+
attrs: {
|
|
255
|
+
...crun,
|
|
256
|
+
...m.attrs
|
|
257
|
+
}
|
|
258
|
+
} : m;
|
|
259
|
+
});
|
|
260
|
+
if (node.content) out.content = node.content.map((c) => resolveNode(c, paraById, charRunById));
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Inline a document's styles into each node's attrs — a self-contained
|
|
265
|
+
* JSONContent that round-trips and renders without the styles context. A
|
|
266
|
+
* paragraph/heading absorbs its styleId's `basedOn` chain (paragraph properties
|
|
267
|
+
* + the paragraph's default `run`); a textStyle mark absorbs its character
|
|
268
|
+
* style's run. Explicit attrs/marks always override the inherited values;
|
|
269
|
+
* styleId is preserved (the semantic reference is kept). The style definitions
|
|
270
|
+
* themselves are untouched — only properties are copied onto nodes.
|
|
271
|
+
*
|
|
272
|
+
* Styles default to the document's own `attrs.styles` (the round-tripped
|
|
273
|
+
* styles.xml model carried on the doc node), so `inlineStyles(doc)` needs no
|
|
274
|
+
* second argument; pass `styles` explicitly to override. Use cases:
|
|
275
|
+
* cross-document paste (the snippet carries its styling) and self-contained
|
|
276
|
+
* export. The editor still renders via global styles — this only merges JSON
|
|
277
|
+
* properties.
|
|
278
|
+
*/
|
|
279
|
+
function inlineStyles(json, styles) {
|
|
280
|
+
const docStyles = styles ?? json.attrs?.styles;
|
|
281
|
+
if (!docStyles) return json;
|
|
282
|
+
return resolveNode(json, indexParagraphStyles(docStyles), indexCharacterRunStyles(docStyles));
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
285
|
+
export { stylesToCss as a, quickStyles as i, effectiveRunProps as n, inlineStyles as r, defaultParagraphStyleId as t };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { JSONContent } from "@tiptap/core";
|
|
2
|
+
import { StylesOptions } from "@office-open/docx";
|
|
3
|
+
|
|
4
|
+
//#region src/converters/styles.d.ts
|
|
5
|
+
/** The styleId of the document's default paragraph style (`w:default="1"`
|
|
6
|
+
* type="paragraph") — the implicit style applied to every paragraph WITHOUT an
|
|
7
|
+
* explicit pStyle. OOXML renders a pStyle-less paragraph as this style (usually
|
|
8
|
+
* "Normal"), so the editor's `.docx-default` class — emitted by Paragraph/
|
|
9
|
+
* Heading renderHTML on a styleId-less node — targets it. Searched in
|
|
10
|
+
* `paragraphStyles` and the built-in named styles nested under `default`
|
|
11
|
+
* (key → pStyle id). null when the document declares none. */
|
|
12
|
+
declare function defaultParagraphStyleId(styles: StylesOptions | null | undefined): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Generate scoped CSS from a document's `styles.xml` model (`StylesOptions`) so
|
|
15
|
+
* named paragraph/character styles and the document defaults render correctly
|
|
16
|
+
* in the editor. All rules are scoped to `.docen-page` (the editor's page
|
|
17
|
+
* surface); the editor injects the result into a `<style>` after load.
|
|
18
|
+
*
|
|
19
|
+
* - `default.document` → `.docen-page` base run + paragraph defaults (the doc's
|
|
20
|
+
* default font/size, line spacing, …).
|
|
21
|
+
* - `paragraphStyles[]` → `.docen-page .docx-style-{id}` (run + paragraph CSS).
|
|
22
|
+
* Paragraph nodes carry `class="docx-style-{styleId}"` (see the Paragraph /
|
|
23
|
+
* Heading extensions' pStyle ↔ styleId round-trip).
|
|
24
|
+
* - `characterStyles[]` → `.docen-page .docx-char-{id}` (run CSS).
|
|
25
|
+
*
|
|
26
|
+
* `basedOn` inheritance is deep-merged per-property into each named style's
|
|
27
|
+
* rule (root first, child overrides) — see mergeStyleChain. CSS inheritance
|
|
28
|
+
* follows the DOM tree, not a style's basedOn, so each `.docx-style-{id}` rule
|
|
29
|
+
* carries its full ancestor chain rather than relying on source order.
|
|
30
|
+
*/
|
|
31
|
+
declare function stylesToCss(styles: StylesOptions | null | undefined, scope: string): string;
|
|
32
|
+
/** A gallery-ready paragraph-style entry for the Styles combobox. */
|
|
33
|
+
interface QuickStyleEntry {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The paragraph styles to list in the Quick Styles gallery, matching Word's
|
|
39
|
+
* default behavior: the gallery is a *paragraph-style* selector (it applies a
|
|
40
|
+
* pStyle), so only paragraph styles appear — never character styles, even
|
|
41
|
+
* those flagged `quickFormat` (those live in the Styles task pane). Among
|
|
42
|
+
* paragraph styles, only those flagged `quickFormat` are listed, ordered by
|
|
43
|
+
* `uiPriority` (Word orders the gallery this way).
|
|
44
|
+
*
|
|
45
|
+
* Reads `quickFormat`/`uiPriority`/`name` straight from office-open's styles
|
|
46
|
+
* model (`StylesOptions`): `paragraphStyles` (Normal + custom) and the built-in
|
|
47
|
+
* named paragraph styles nested under `default` (title/heading1-9/quote/…). The
|
|
48
|
+
* `default` keys that hold character styles are excluded via
|
|
49
|
+
* `CHARACTER_DEFAULT_KEYS`. When a document carries no quickFormat flags at all
|
|
50
|
+
* (e.g. some LibreOffice-generated files), fall back to all paragraph styles so
|
|
51
|
+
* the gallery is never empty.
|
|
52
|
+
*/
|
|
53
|
+
declare function quickStyles(styles: StylesOptions | null | undefined): QuickStyleEntry[];
|
|
54
|
+
/** Resolve the effective run-level properties (font name, size in points) at the
|
|
55
|
+
* caret, staying in the document's own units — no px conversion. Priority:
|
|
56
|
+
* direct run props (the textStyle mark) → the paragraph style (`styleId`) →
|
|
57
|
+
* its `basedOn` chain → the document defaults. `font` is resolved to a single
|
|
58
|
+
* display name (ascii/hAnsi/eastAsia). Returns null where nothing in the chain
|
|
59
|
+
* sets a property, so the caller can leave the box empty. */
|
|
60
|
+
declare function effectiveRunProps(styles: StylesOptions | null | undefined, styleId: string | null | undefined, direct?: {
|
|
61
|
+
font?: unknown;
|
|
62
|
+
size?: unknown;
|
|
63
|
+
}): {
|
|
64
|
+
font: string | null;
|
|
65
|
+
size: number | null;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Inline a document's styles into each node's attrs — a self-contained
|
|
69
|
+
* JSONContent that round-trips and renders without the styles context. A
|
|
70
|
+
* paragraph/heading absorbs its styleId's `basedOn` chain (paragraph properties
|
|
71
|
+
* + the paragraph's default `run`); a textStyle mark absorbs its character
|
|
72
|
+
* style's run. Explicit attrs/marks always override the inherited values;
|
|
73
|
+
* styleId is preserved (the semantic reference is kept). The style definitions
|
|
74
|
+
* themselves are untouched — only properties are copied onto nodes.
|
|
75
|
+
*
|
|
76
|
+
* Styles default to the document's own `attrs.styles` (the round-tripped
|
|
77
|
+
* styles.xml model carried on the doc node), so `inlineStyles(doc)` needs no
|
|
78
|
+
* second argument; pass `styles` explicitly to override. Use cases:
|
|
79
|
+
* cross-document paste (the snippet carries its styling) and self-contained
|
|
80
|
+
* export. The editor still renders via global styles — this only merges JSON
|
|
81
|
+
* properties.
|
|
82
|
+
*/
|
|
83
|
+
declare function inlineStyles(json: JSONContent, styles?: StylesOptions | null): JSONContent;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { inlineStyles as a, effectiveRunProps as i, StylesOptions as n, quickStyles as o, defaultParagraphStyleId as r, stylesToCss as s, QuickStyleEntry as t };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { k as Table$1 } from "./tiptap-D7pl4I5U.mjs";
|
|
2
|
+
import { S as twipToCss, _ as shadingFromElement, d as renderBorderCSS, i as bordersFromElement, n as alignmentFromElement, r as alignmentToCss, v as shadingToCss } from "./utils-BPT4tjcn.mjs";
|
|
3
|
+
//#region src/extensions/table.ts
|
|
4
|
+
/**
|
|
5
|
+
* Table extension with nested office-open attrs.
|
|
6
|
+
*
|
|
7
|
+
* Attrs mirror TableOptions (width/float/layout/borders/alignment/margins/indent/
|
|
8
|
+
* cellSpacing/tableLook/columnWidths/etc.). DOCX round-trip is near-identity:
|
|
9
|
+
* renderDocx/parseDocx pass attrs through (omitting only `rows`, which DocxManager
|
|
10
|
+
* rebuilds from the row/cell nodes). CSS conversion happens only in renderHTML.
|
|
11
|
+
*/
|
|
12
|
+
/** Structural key rebuilt by DocxManager (compileTableNode walks the row nodes). */
|
|
13
|
+
const SKIP_KEYS = new Set(["rows", "columnWidthsRevision"]);
|
|
14
|
+
function renderDocx(node) {
|
|
15
|
+
const attrs = node.attrs ?? {};
|
|
16
|
+
const opts = {};
|
|
17
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
18
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
19
|
+
if (value !== null && value !== void 0) opts[key] = value;
|
|
20
|
+
}
|
|
21
|
+
return opts;
|
|
22
|
+
}
|
|
23
|
+
function parseDocx(opts) {
|
|
24
|
+
const attrs = {};
|
|
25
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
26
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
27
|
+
attrs[key] = value ?? null;
|
|
28
|
+
}
|
|
29
|
+
return attrs;
|
|
30
|
+
}
|
|
31
|
+
const attrNative = () => ({
|
|
32
|
+
default: null,
|
|
33
|
+
parseHTML: () => null,
|
|
34
|
+
rendered: false
|
|
35
|
+
});
|
|
36
|
+
const Table = Table$1.extend({
|
|
37
|
+
addAttributes() {
|
|
38
|
+
return {
|
|
39
|
+
...this.parent?.(),
|
|
40
|
+
width: attrNative(),
|
|
41
|
+
columnWidths: attrNative(),
|
|
42
|
+
indent: attrNative(),
|
|
43
|
+
margins: attrNative(),
|
|
44
|
+
float: attrNative(),
|
|
45
|
+
borders: {
|
|
46
|
+
default: null,
|
|
47
|
+
rendered: false,
|
|
48
|
+
parseHTML: (el) => bordersFromElement(el)
|
|
49
|
+
},
|
|
50
|
+
shading: {
|
|
51
|
+
default: null,
|
|
52
|
+
rendered: false,
|
|
53
|
+
parseHTML: (el) => shadingFromElement(el)
|
|
54
|
+
},
|
|
55
|
+
alignment: {
|
|
56
|
+
default: null,
|
|
57
|
+
rendered: false,
|
|
58
|
+
parseHTML: (el) => alignmentFromElement(el)
|
|
59
|
+
},
|
|
60
|
+
layout: {
|
|
61
|
+
default: null,
|
|
62
|
+
rendered: false,
|
|
63
|
+
parseHTML: (el) => el.style.tableLayout === "fixed" ? "fixed" : null
|
|
64
|
+
},
|
|
65
|
+
style: attrNative(),
|
|
66
|
+
visuallyRightToLeft: attrNative(),
|
|
67
|
+
tableLook: attrNative(),
|
|
68
|
+
cellSpacing: attrNative(),
|
|
69
|
+
styleRowBandSize: attrNative(),
|
|
70
|
+
styleColBandSize: attrNative(),
|
|
71
|
+
caption: attrNative(),
|
|
72
|
+
description: attrNative()
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
76
|
+
const a = node.attrs;
|
|
77
|
+
const attrs = { ...HTMLAttributes };
|
|
78
|
+
const styles = [];
|
|
79
|
+
const align = alignmentToCss(a.alignment);
|
|
80
|
+
if (align === "center") styles.push("margin-left:auto", "margin-right:auto");
|
|
81
|
+
else if (align === "right") styles.push("margin-left:auto", "margin-right:0");
|
|
82
|
+
else if (align) styles.push("margin-left:0", "margin-right:auto");
|
|
83
|
+
if (a.layout === "fixed") styles.push("table-layout:fixed");
|
|
84
|
+
if (a.width && typeof a.width === "object") {
|
|
85
|
+
const w = a.width;
|
|
86
|
+
const numSize = typeof w.size === "string" ? parseFloat(w.size) : w.size;
|
|
87
|
+
if (w.type === "pct") {
|
|
88
|
+
if (typeof w.size === "string" && w.size.includes("%")) styles.push(`width:${w.size}`);
|
|
89
|
+
else if (!Number.isNaN(numSize)) styles.push(`width:${numSize / 50}%`);
|
|
90
|
+
} else if (w.type === "auto") styles.push("width:100%");
|
|
91
|
+
else if (numSize != null) {
|
|
92
|
+
const css = twipToCss(numSize);
|
|
93
|
+
if (css) styles.push(`width:${css}`);
|
|
94
|
+
}
|
|
95
|
+
} else styles.push("width:100%");
|
|
96
|
+
if (a.indent && typeof a.indent === "object") {
|
|
97
|
+
const ind = a.indent;
|
|
98
|
+
if (ind.size != null) {
|
|
99
|
+
const css = twipToCss(ind.size);
|
|
100
|
+
if (css) styles.push(`margin-left:${css}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (a.cellSpacing && typeof a.cellSpacing === "object") {
|
|
104
|
+
const cs = a.cellSpacing;
|
|
105
|
+
if (cs.value != null) {
|
|
106
|
+
const css = twipToCss(cs.value);
|
|
107
|
+
if (css) styles.push(`border-spacing:${css}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const bg = shadingToCss(a.shading);
|
|
111
|
+
if (bg) styles.push(`background-color:${bg}`);
|
|
112
|
+
if (a.borders && typeof a.borders === "object") {
|
|
113
|
+
const b = a.borders;
|
|
114
|
+
const sides = [
|
|
115
|
+
["top", b.top],
|
|
116
|
+
["bottom", b.bottom],
|
|
117
|
+
["left", b.left],
|
|
118
|
+
["right", b.right]
|
|
119
|
+
];
|
|
120
|
+
for (const [side, border] of sides) {
|
|
121
|
+
const css = renderBorderCSS(border);
|
|
122
|
+
if (css) styles.push(`border-${side}:${css}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const firstRow = node.firstChild;
|
|
126
|
+
const cellPx = [];
|
|
127
|
+
if (firstRow) for (let i = 0; i < firstRow.childCount; i++) {
|
|
128
|
+
const cw = firstRow.child(i).attrs.colwidth;
|
|
129
|
+
if (Array.isArray(cw) && cw.length) for (const w of cw) cellPx.push(w || 0);
|
|
130
|
+
else cellPx.push(0);
|
|
131
|
+
}
|
|
132
|
+
const hasCellWidths = cellPx.some((w) => w > 0);
|
|
133
|
+
const tblGridPx = (a.columnWidths ?? []).map((w) => Math.round((w || 0) / 15));
|
|
134
|
+
const gridPx = tblGridPx.some((w) => w > 0) ? tblGridPx : hasCellWidths ? cellPx : tblGridPx;
|
|
135
|
+
if (styles.length > 0) attrs.style = styles.join(";");
|
|
136
|
+
if (gridPx.some((w) => w > 0)) {
|
|
137
|
+
const gridTotal = gridPx.reduce((sum, w) => sum + w, 0);
|
|
138
|
+
return [
|
|
139
|
+
"table",
|
|
140
|
+
attrs,
|
|
141
|
+
[
|
|
142
|
+
"colgroup",
|
|
143
|
+
{},
|
|
144
|
+
...gridPx.map((w) => ["col", { style: `width:${gridTotal > 0 ? (w / gridTotal * 100).toFixed(2) : 0}%` }])
|
|
145
|
+
],
|
|
146
|
+
["tbody", 0]
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
return [
|
|
150
|
+
"table",
|
|
151
|
+
attrs,
|
|
152
|
+
0
|
|
153
|
+
];
|
|
154
|
+
},
|
|
155
|
+
renderDocx,
|
|
156
|
+
parseDocx
|
|
157
|
+
});
|
|
158
|
+
//#endregion
|
|
159
|
+
export { parseDocx as n, renderDocx as r, Table as t };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { A as TableCell$1 } from "./tiptap-D7pl4I5U.mjs";
|
|
2
|
+
import { _ as shadingFromElement, i as bordersFromElement, m as renderTableCellStyles } from "./utils-BPT4tjcn.mjs";
|
|
3
|
+
//#region src/extensions/table-cell.ts
|
|
4
|
+
/**
|
|
5
|
+
* Table cell extension with nested office-open attrs.
|
|
6
|
+
*
|
|
7
|
+
* Attrs mirror TableCellPropertiesOptionsBase (shading/margins/borders/width as
|
|
8
|
+
* nested objects + scalar OOXML properties) plus the inherited Tiptap structural
|
|
9
|
+
* names colspan/rowspan/colwidth/align (rendered: false for the OOXML-named ones).
|
|
10
|
+
* DOCX round-trip is near-identity: renderDocx/parseDocx pass OOXML-native attrs
|
|
11
|
+
* through and only map the Tiptap structural names colspan/rowspan/colwidth to
|
|
12
|
+
* OOXML columnSpan/rowSpan/width. CSS conversion happens solely in renderHTML via
|
|
13
|
+
* utils.renderTableCellStyles (consuming nested shading/verticalAlign/noWrap).
|
|
14
|
+
*/
|
|
15
|
+
/** Tiptap structural attrs expressed via OOXML structural fields, not passed through. */
|
|
16
|
+
const SKIP_KEYS = new Set([
|
|
17
|
+
"colspan",
|
|
18
|
+
"rowspan",
|
|
19
|
+
"colwidth",
|
|
20
|
+
"children"
|
|
21
|
+
]);
|
|
22
|
+
function renderDocx(node) {
|
|
23
|
+
const attrs = node.attrs ?? {};
|
|
24
|
+
const opts = {};
|
|
25
|
+
const colspan = attrs.colspan;
|
|
26
|
+
if (colspan && colspan > 1) opts.columnSpan = colspan;
|
|
27
|
+
const rowspan = attrs.rowspan;
|
|
28
|
+
if (rowspan && rowspan > 1) opts.rowSpan = rowspan;
|
|
29
|
+
const colwidth = attrs.colwidth;
|
|
30
|
+
if (colwidth && colwidth.length > 0) {
|
|
31
|
+
const totalPx = colwidth.reduce((sum, w) => sum + (w || 0), 0);
|
|
32
|
+
if (totalPx > 0) opts.width = {
|
|
33
|
+
size: totalPx * 15,
|
|
34
|
+
type: "dxa"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
38
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
39
|
+
if (value !== null && value !== void 0) opts[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return opts;
|
|
42
|
+
}
|
|
43
|
+
function parseDocx(opts) {
|
|
44
|
+
const resolved = typeof opts === "string" ? { text: opts } : opts;
|
|
45
|
+
const attrs = {};
|
|
46
|
+
if (resolved.columnSpan != null) attrs.colspan = resolved.columnSpan;
|
|
47
|
+
if (resolved.rowSpan != null) attrs.rowspan = resolved.rowSpan;
|
|
48
|
+
if (resolved.width) {
|
|
49
|
+
const twips = resolved.width.size ?? 0;
|
|
50
|
+
if (twips) attrs.colwidth = [Math.round(twips / 15)];
|
|
51
|
+
}
|
|
52
|
+
for (const [key, value] of Object.entries(resolved)) {
|
|
53
|
+
if (key === "columnSpan" || key === "rowSpan" || key === "width" || key === "children") continue;
|
|
54
|
+
attrs[key] = value ?? null;
|
|
55
|
+
}
|
|
56
|
+
return attrs;
|
|
57
|
+
}
|
|
58
|
+
const attrNative = () => ({
|
|
59
|
+
default: null,
|
|
60
|
+
parseHTML: () => null,
|
|
61
|
+
rendered: false
|
|
62
|
+
});
|
|
63
|
+
const TableCell = TableCell$1.extend({
|
|
64
|
+
addAttributes() {
|
|
65
|
+
return {
|
|
66
|
+
...this.parent?.(),
|
|
67
|
+
shading: {
|
|
68
|
+
default: null,
|
|
69
|
+
rendered: false,
|
|
70
|
+
parseHTML: (el) => shadingFromElement(el)
|
|
71
|
+
},
|
|
72
|
+
borders: {
|
|
73
|
+
default: null,
|
|
74
|
+
rendered: false,
|
|
75
|
+
parseHTML: (el) => bordersFromElement(el)
|
|
76
|
+
},
|
|
77
|
+
verticalAlign: {
|
|
78
|
+
default: null,
|
|
79
|
+
rendered: false,
|
|
80
|
+
parseHTML: (el) => el.style.verticalAlign || null
|
|
81
|
+
},
|
|
82
|
+
textDirection: attrNative(),
|
|
83
|
+
width: attrNative(),
|
|
84
|
+
margins: attrNative(),
|
|
85
|
+
noWrap: {
|
|
86
|
+
default: null,
|
|
87
|
+
rendered: false,
|
|
88
|
+
parseHTML: (el) => el.style.whiteSpace === "nowrap" ? true : null
|
|
89
|
+
},
|
|
90
|
+
verticalMerge: attrNative(),
|
|
91
|
+
horizontalMerge: attrNative(),
|
|
92
|
+
fitText: attrNative(),
|
|
93
|
+
hideMark: attrNative(),
|
|
94
|
+
headers: attrNative(),
|
|
95
|
+
cnfStyle: attrNative()
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
99
|
+
const styles = renderTableCellStyles(node.attrs);
|
|
100
|
+
const attrs = { ...HTMLAttributes };
|
|
101
|
+
if (styles.length > 0) attrs.style = styles.join(";");
|
|
102
|
+
return [
|
|
103
|
+
"td",
|
|
104
|
+
attrs,
|
|
105
|
+
0
|
|
106
|
+
];
|
|
107
|
+
},
|
|
108
|
+
renderDocx,
|
|
109
|
+
parseDocx
|
|
110
|
+
});
|
|
111
|
+
//#endregion
|
|
112
|
+
export { parseDocx as n, renderDocx as r, TableCell as t };
|