@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.
- package/dist/converters/docx.d.mts +220 -1
- package/dist/converters/docx.mjs +1352 -1
- package/dist/converters/html.d.mts +28 -1
- package/dist/converters/html.mjs +103 -1
- package/dist/converters/markdown.d.mts +12 -1
- package/dist/converters/markdown.mjs +17 -1
- package/dist/converters/patch.d.mts +52 -1
- package/dist/converters/patch.mjs +35 -1
- package/dist/converters/prepare.d.mts +62 -1
- package/dist/converters/prepare.mjs +121 -1
- package/dist/converters/styles.d.mts +91 -2
- package/dist/converters/styles.mjs +308 -2
- package/dist/core-B8ba_FNi.mjs +717 -0
- package/dist/core-DRaLI8nd.d.mts +203 -0
- package/dist/core.d.mts +1 -1
- package/dist/core.mjs +1 -1
- package/dist/document-Cws7XTYL.d.mts +28 -0
- package/dist/editor.d.mts +20 -1
- package/dist/editor.mjs +20 -1
- package/dist/extensions/blockquote.d.mts +1 -1
- package/dist/extensions/blockquote.mjs +30 -2
- package/dist/extensions/code-block.d.mts +1 -1
- package/dist/extensions/code-block.mjs +22 -1
- package/dist/extensions/column-break.d.mts +1 -1
- package/dist/extensions/column-break.mjs +1 -1
- package/dist/extensions/details.d.mts +1 -1
- package/dist/extensions/details.mjs +17 -2
- package/dist/extensions/document.d.mts +1 -1
- package/dist/extensions/document.mjs +45 -1
- package/dist/extensions/extensions.d.mts +4 -5
- package/dist/extensions/extensions.mjs +13 -14
- package/dist/extensions/formatting-marks.d.mts +7 -1
- package/dist/extensions/formatting-marks.mjs +1 -1
- package/dist/extensions/heading.mjs +187 -1
- package/dist/extensions/image.d.mts +1 -1
- package/dist/extensions/image.mjs +395 -1
- package/dist/extensions/index.d.mts +4 -3
- package/dist/extensions/index.mjs +4 -3
- package/dist/extensions/mention.d.mts +1 -1
- package/dist/extensions/mention.mjs +43 -2
- package/dist/extensions/ordered-list.d.mts +1 -1
- package/dist/extensions/ordered-list.mjs +40 -2
- package/dist/extensions/page-break.d.mts +1 -33
- package/dist/extensions/page-break.mjs +1 -1
- package/dist/extensions/paragraph.mjs +145 -1
- package/dist/extensions/passthrough.d.mts +1 -1
- package/dist/extensions/passthrough.mjs +1 -1
- package/dist/extensions/section-break.d.mts +19 -2
- package/dist/extensions/section-break.mjs +1 -1
- package/dist/extensions/strike.mjs +45 -1
- package/dist/extensions/table-cell.mjs +106 -1
- package/dist/extensions/table-header.mjs +106 -1
- package/dist/extensions/table-row.mjs +78 -1
- package/dist/extensions/table.mjs +154 -1
- package/dist/extensions/task-item.d.mts +1 -1
- package/dist/extensions/task-item.mjs +36 -2
- package/dist/extensions/text-style.mjs +145 -1
- package/dist/extensions/tiptap.d.mts +2 -2
- package/dist/extensions/tiptap.mjs +31 -2
- package/dist/extensions/toc-field.d.mts +2 -0
- package/dist/extensions/toc-field.mjs +2 -0
- package/dist/extensions/utils.d.mts +2 -62
- package/dist/extensions/utils.mjs +508 -2
- package/dist/extensions/wpg-group.d.mts +2 -0
- package/dist/extensions/wpg-group.mjs +2 -0
- package/dist/extensions/wps-shape.d.mts +2 -0
- package/dist/extensions/wps-shape.mjs +2 -0
- package/dist/index.d.mts +101 -29
- package/dist/index.mjs +13 -10
- package/dist/{tiptap-TErPjuNJ.d.mts → tiptap-pZsNPsvV.d.mts} +1 -3
- package/dist/utils-BJwDQts7.d.mts +137 -0
- package/package.json +39 -40
- package/dist/blockquote-Ky9xD_Q-.mjs +0 -31
- package/dist/code-block-BgNjhADy.mjs +0 -23
- package/dist/core-DEqBHRzE.mjs +0 -406
- package/dist/core-fBMCCj4D.d.mts +0 -97
- package/dist/details-Dh8luWb2.mjs +0 -18
- package/dist/dist-B6B3HnVZ.mjs +0 -1838
- package/dist/document-BH1y4qHM.d.mts +0 -6
- package/dist/document-CvTym-FM.mjs +0 -50
- package/dist/docx-C5wyjI-0.d.mts +0 -200
- package/dist/docx-CONyAedl.mjs +0 -1231
- package/dist/editor-Bf3CxQnY.mjs +0 -21
- package/dist/editor-DReY3Rb9.d.mts +0 -21
- package/dist/emoji-BZ1dJD7d.d.mts +0 -17
- package/dist/emoji-C2OsTWR7.mjs +0 -27
- package/dist/extensions/emoji.d.mts +0 -2
- package/dist/extensions/emoji.mjs +0 -2
- package/dist/extensions/image-group.d.mts +0 -6
- package/dist/extensions/image-group.mjs +0 -2
- package/dist/heading-DAubgJJN.mjs +0 -166
- package/dist/html-DCC_DSHv.d.mts +0 -18
- package/dist/html-DedDWPoO.mjs +0 -66
- package/dist/image-CuEIXoZl.mjs +0 -334
- package/dist/markdown-BzJmRsBs.mjs +0 -18
- package/dist/markdown-D0k0sRYS.d.mts +0 -13
- package/dist/mention-C-dTR6MK.mjs +0 -44
- package/dist/ordered-list-V-3xFQmV.mjs +0 -41
- package/dist/paragraph-BrWH-BOV.mjs +0 -147
- package/dist/patch-Cdp5WKJz.d.mts +0 -53
- package/dist/patch-IZ13tXTu.mjs +0 -36
- package/dist/prepare-Bdk8Cjkf.mjs +0 -122
- package/dist/prepare-CPAvcv6q.d.mts +0 -63
- package/dist/strike-hWxShOgb.mjs +0 -50
- package/dist/styles-QSX3RhxK.mjs +0 -285
- package/dist/styles-ugspkrxz.d.mts +0 -85
- package/dist/table-DrA12lxX.mjs +0 -159
- package/dist/table-cell-DmkfYwDc.mjs +0 -112
- package/dist/table-header-Bwr5GKWg.mjs +0 -112
- package/dist/table-row-vew1y7WH.mjs +0 -84
- package/dist/task-item-BnlXGrIG.mjs +0 -37
- package/dist/text-style-tMeBq5XQ.mjs +0 -151
- package/dist/tiptap-D7pl4I5U.mjs +0 -33
- package/dist/utils-BPT4tjcn.mjs +0 -333
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import { Blockquote, Bold, BulletList, Code, CodeBlockLowlight, Details, DetailsContent, DetailsSummary, Emoji, HardBreak, Highlight, HorizontalRule, Italic, Link, ListItem, Mathematics, Mention, OrderedList, Subscript, Superscript, TaskItem, TaskList, Text, TextAlign as TextAlign$1, Underline } from "./extensions/tiptap.mjs";
|
|
2
|
+
import "./extensions/blockquote.mjs";
|
|
3
|
+
import { CodeBlock } from "./extensions/code-block.mjs";
|
|
4
|
+
import "./extensions/details.mjs";
|
|
5
|
+
import { attrNative, floatAnchorScope, floatingToStyles, normalizeColorToHex, renderRunStyles } from "./extensions/utils.mjs";
|
|
6
|
+
import { Document } from "./extensions/document.mjs";
|
|
7
|
+
import { Heading } from "./extensions/heading.mjs";
|
|
8
|
+
import { Image } from "./extensions/image.mjs";
|
|
9
|
+
import "./extensions/mention.mjs";
|
|
10
|
+
import "./extensions/ordered-list.mjs";
|
|
11
|
+
import { Paragraph } from "./extensions/paragraph.mjs";
|
|
12
|
+
import { Strike } from "./extensions/strike.mjs";
|
|
13
|
+
import { Table } from "./extensions/table.mjs";
|
|
14
|
+
import { TableCell } from "./extensions/table-cell.mjs";
|
|
15
|
+
import { TableHeader } from "./extensions/table-header.mjs";
|
|
16
|
+
import { TableRow } from "./extensions/table-row.mjs";
|
|
17
|
+
import "./extensions/task-item.mjs";
|
|
18
|
+
import { TextStyle } from "./extensions/text-style.mjs";
|
|
19
|
+
import { Editor, Extension, Mark, Node } from "@tiptap/core";
|
|
20
|
+
import { all, createLowlight } from "lowlight";
|
|
21
|
+
import { TextAlign } from "@tiptap/extension-text-align";
|
|
22
|
+
import { Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
|
|
23
|
+
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
24
|
+
import { convertEmuToPixels, encodeBase64 } from "@office-open/core";
|
|
25
|
+
//#region src/extensions/column-break.ts
|
|
26
|
+
/**
|
|
27
|
+
* ColumnBreak — inline atom node for DOCX column breaks (`<w:br w:type="column"/>`).
|
|
28
|
+
*
|
|
29
|
+
* Sibling to PageBreak and HardBreak: all three render `<w:br>`, differing only
|
|
30
|
+
* by type. Inline (group "inline") to match the run-internal OOXML position,
|
|
31
|
+
* keeping a column break inside its paragraph on round-trip. Visual column
|
|
32
|
+
* layout (paged.js multi-column) is a future concern; the node preserves the
|
|
33
|
+
* break losslessly regardless.
|
|
34
|
+
*
|
|
35
|
+
* The DOCX payload (`{ columnBreak: true }`) is inlined in DocxManager — a
|
|
36
|
+
* one-liner with no per-node variance, so no extension helper for it.
|
|
37
|
+
*
|
|
38
|
+
* `setColumnBreak` only inserts the atom (no paragraph split): a column break
|
|
39
|
+
* does not start a new page, and there is no column layout to reflow yet, so
|
|
40
|
+
* the node is purely for round-trip fidelity until multi-column lands.
|
|
41
|
+
*/
|
|
42
|
+
const ColumnBreak = Node.create({
|
|
43
|
+
name: "columnBreak",
|
|
44
|
+
inline: true,
|
|
45
|
+
group: "inline",
|
|
46
|
+
atom: true,
|
|
47
|
+
parseHTML() {
|
|
48
|
+
return [{ tag: "span[data-type=\"columnBreak\"]" }, { tag: "br[data-type=\"columnBreak\"]" }];
|
|
49
|
+
},
|
|
50
|
+
renderHTML() {
|
|
51
|
+
return ["span", {
|
|
52
|
+
"data-type": "columnBreak",
|
|
53
|
+
style: "break-after:column"
|
|
54
|
+
}];
|
|
55
|
+
},
|
|
56
|
+
addCommands() {
|
|
57
|
+
return { setColumnBreak: () => ({ commands }) => commands.insertContent({ type: "columnBreak" }) };
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/extensions/formatting-marks.ts
|
|
62
|
+
/** Plugin key holding {enabled, decos} for the formatting-marks plugin.
|
|
63
|
+
* `enabled` is toggled by a transaction meta; `decos` caches the widget set so
|
|
64
|
+
* a pure selection/cursor transaction (doc unchanged) reuses it instead of
|
|
65
|
+
* re-traversing every textblock. The un-cached version ran that traversal on
|
|
66
|
+
* every selectionUpdate — 200ms+ on a 1000-page document — making the caret
|
|
67
|
+
* and selection feel frozen. */
|
|
68
|
+
const FORMAT_MARKS_KEY = new PluginKey("docen-formatting-marks");
|
|
69
|
+
/** Build a widget set with one paragraph-mark widget at each textblock's
|
|
70
|
+
* closing position. Called only on doc change or marks toggle. */
|
|
71
|
+
function buildParagraphMarkDecos(doc) {
|
|
72
|
+
const decos = [];
|
|
73
|
+
doc.descendants((node, pos) => {
|
|
74
|
+
if (!node.isTextblock) return;
|
|
75
|
+
decos.push(Decoration.widget(pos + node.nodeSize - 1, () => {
|
|
76
|
+
const span = document.createElement("span");
|
|
77
|
+
span.className = "docen-para-mark";
|
|
78
|
+
span.contentEditable = "false";
|
|
79
|
+
span.textContent = "↲";
|
|
80
|
+
return span;
|
|
81
|
+
}, { side: 1 }));
|
|
82
|
+
});
|
|
83
|
+
return DecorationSet.create(doc, decos);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* FormattingMarks — paints the non-printing paragraph mark via ProseMirror
|
|
87
|
+
* widget decorations.
|
|
88
|
+
*
|
|
89
|
+
* CSS `p::after { content: "…" }` does NOT work on a ProseMirror-managed
|
|
90
|
+
* paragraph: the view owns the DOM and the trailing-break kludge pushes the
|
|
91
|
+
* pseudo-element off the visible line, so the mark never appears (verified).
|
|
92
|
+
* Marijn Haverbeke endorses widget decorations for exactly this use case
|
|
93
|
+
* (https://discuss.prosemirror.net/t/1442). Each textblock gets a widget at its
|
|
94
|
+
* closing position (inside the textblock, so it stays on the content's line)
|
|
95
|
+
* holding a non-editable `<span class="docen-para-mark">`. `side: 1` keeps the
|
|
96
|
+
* mark to the right of a cursor resting at the paragraph end (the cursor always
|
|
97
|
+
* precedes its own paragraph mark).
|
|
98
|
+
*
|
|
99
|
+
* Glyph: a down-then-left return arrow (↲, U+21B2 "downwards arrow with tip
|
|
100
|
+
* leftwards" — the Enter-key glyph). Word/WPS use the pilcrow ¶ for paragraph
|
|
101
|
+
* marks; that down-then-left arrow is Word's line-break glyph — adopted here as
|
|
102
|
+
* the paragraph mark per the project's visual preference.
|
|
103
|
+
*
|
|
104
|
+
* Performance: the widget set is cached in plugin state and rebuilt only on a
|
|
105
|
+
* doc change or a marks toggle — NOT on every selectionUpdate. Without the
|
|
106
|
+
* cache, each caret move / selection re-traverses the whole document (one
|
|
107
|
+
* widget per textblock) and rebuilds the set, which costs 200ms+ on 1000-page
|
|
108
|
+
* documents. Pure selection transactions now reuse the cached set in O(1).
|
|
109
|
+
*/
|
|
110
|
+
const FormattingMarks = Extension.create({
|
|
111
|
+
name: "formattingMarks",
|
|
112
|
+
addProseMirrorPlugins() {
|
|
113
|
+
return [new Plugin({
|
|
114
|
+
key: FORMAT_MARKS_KEY,
|
|
115
|
+
state: {
|
|
116
|
+
init: () => ({
|
|
117
|
+
enabled: false,
|
|
118
|
+
decos: DecorationSet.empty
|
|
119
|
+
}),
|
|
120
|
+
apply: (tr, prev) => {
|
|
121
|
+
const toggled = tr.getMeta(FORMAT_MARKS_KEY) === "toggle";
|
|
122
|
+
const enabled = toggled ? !prev.enabled : prev.enabled;
|
|
123
|
+
if (!enabled) return {
|
|
124
|
+
enabled: false,
|
|
125
|
+
decos: DecorationSet.empty
|
|
126
|
+
};
|
|
127
|
+
if (toggled || tr.docChanged) return {
|
|
128
|
+
enabled,
|
|
129
|
+
decos: buildParagraphMarkDecos(tr.doc)
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
enabled,
|
|
133
|
+
decos: prev.decos
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
props: { decorations: (state) => FORMAT_MARKS_KEY.getState(state).decos }
|
|
138
|
+
})];
|
|
139
|
+
},
|
|
140
|
+
addCommands() {
|
|
141
|
+
return { toggleFormattingMarks: () => ({ tr, dispatch }) => {
|
|
142
|
+
if (dispatch) dispatch(tr.setMeta(FORMAT_MARKS_KEY, "toggle"));
|
|
143
|
+
return true;
|
|
144
|
+
} };
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/extensions/page-break.ts
|
|
149
|
+
/**
|
|
150
|
+
* PageBreak — inline atom node for DOCX page breaks (`<w:br w:type="page"/>`).
|
|
151
|
+
*
|
|
152
|
+
* OOXML page breaks live inside a run (inline), so the Tiptap node is inline
|
|
153
|
+
* too (group "inline"). This keeps a break inside its paragraph on round-trip
|
|
154
|
+
* — a break mid-paragraph stays mid-paragraph instead of splitting the
|
|
155
|
+
* paragraph into separate blocks (which would lose the original structure).
|
|
156
|
+
* Sibling to HardBreak: both render `<w:br>`, differing only by type.
|
|
157
|
+
*
|
|
158
|
+
* The DOCX payload (`{ pageBreak: true }`) is inlined in DocxManager — a
|
|
159
|
+
* one-liner with no per-node variance, so no extension helper for it.
|
|
160
|
+
*
|
|
161
|
+
* Inserting a page break must also visually reflow (the C-route paginator only
|
|
162
|
+
* forces a new page after a block *containing* a pageBreak atom). So
|
|
163
|
+
* `setPageBreak` inserts the atom then splits the paragraph — the atom lands at
|
|
164
|
+
* the end of the first half, the trailing text becomes a fresh paragraph, and
|
|
165
|
+
* the paginator's `forcesPageBreakAfter` moves it to the next page (matching
|
|
166
|
+
* Word's Ctrl+Enter behavior).
|
|
167
|
+
*/
|
|
168
|
+
const PageBreak = Node.create({
|
|
169
|
+
name: "pageBreak",
|
|
170
|
+
inline: true,
|
|
171
|
+
group: "inline",
|
|
172
|
+
atom: true,
|
|
173
|
+
parseHTML() {
|
|
174
|
+
return [{ tag: "span[data-type=\"pageBreak\"]" }, { tag: "br[data-type=\"pageBreak\"]" }];
|
|
175
|
+
},
|
|
176
|
+
renderHTML() {
|
|
177
|
+
return ["span", {
|
|
178
|
+
"data-type": "pageBreak",
|
|
179
|
+
style: "break-after:page"
|
|
180
|
+
}];
|
|
181
|
+
},
|
|
182
|
+
addProseMirrorPlugins() {
|
|
183
|
+
return [new Plugin({ appendTransaction: (transactions, _oldState, newState) => {
|
|
184
|
+
if (!transactions.some((tr) => tr.docChanged)) return null;
|
|
185
|
+
const doc = newState.doc;
|
|
186
|
+
const tr = newState.tr;
|
|
187
|
+
const splits = [];
|
|
188
|
+
doc.descendants((node, pos) => {
|
|
189
|
+
if (!node.isTextblock) return;
|
|
190
|
+
const last = node.childCount - 1;
|
|
191
|
+
node.forEach((child, offset, index) => {
|
|
192
|
+
if (child.type.name === "pageBreak" && index < last) splits.push(pos + 1 + offset + child.nodeSize);
|
|
193
|
+
});
|
|
194
|
+
return false;
|
|
195
|
+
});
|
|
196
|
+
if (splits.length === 0) return null;
|
|
197
|
+
splits.sort((a, b) => b - a);
|
|
198
|
+
for (const splitPos of splits) tr.split(splitPos, 1);
|
|
199
|
+
tr.setSelection(newState.selection.map(tr.doc, tr.mapping));
|
|
200
|
+
return tr;
|
|
201
|
+
} })];
|
|
202
|
+
},
|
|
203
|
+
addCommands() {
|
|
204
|
+
return { setPageBreak: () => ({ tr, state, dispatch }) => {
|
|
205
|
+
if (!dispatch) return true;
|
|
206
|
+
const { $from } = state.selection;
|
|
207
|
+
const depth = $from.depth;
|
|
208
|
+
if (($from.index(depth) > 0 ? $from.parent.child($from.index(depth) - 1) : null)?.type.name === "pageBreak") {
|
|
209
|
+
const after = $from.after(depth);
|
|
210
|
+
const carrier = state.schema.nodes.paragraph.create(null, [state.schema.nodes.pageBreak.create()]);
|
|
211
|
+
tr.insert(after, carrier);
|
|
212
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(after + carrier.nodeSize)));
|
|
213
|
+
tr.scrollIntoView();
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
tr.replaceSelectionWith(state.schema.nodes.pageBreak.create());
|
|
217
|
+
tr.split(tr.selection.from, 1);
|
|
218
|
+
tr.scrollIntoView();
|
|
219
|
+
return true;
|
|
220
|
+
} };
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/extensions/passthrough.ts
|
|
225
|
+
/**
|
|
226
|
+
* Passthrough — block atom carrying an opaque {@link SectionChild} that has
|
|
227
|
+
* no native Tiptap representation (rawXml, bookmarkStart/End, textbox,
|
|
228
|
+
* altChunk, subDoc, customXml).
|
|
229
|
+
*
|
|
230
|
+
* The full SectionChild is stored as JSON in `attrs.data` so the DOCX→JSON→DOCX
|
|
231
|
+
* round-trip stays byte-faithful: office-open's stringify handles the inner
|
|
232
|
+
* structure verbatim (including a textbox's nested children, which remain as
|
|
233
|
+
* structured ParagraphOptions inside the blob rather than editable Tiptap
|
|
234
|
+
* nodes). The node is not editable — it renders a read-only placeholder in HTML.
|
|
235
|
+
*
|
|
236
|
+
* DOCX serialization is inlined in DocxManager (compile/resolve read/write
|
|
237
|
+
* `attrs.data` directly), so no renderDocx/parseDocx is needed here.
|
|
238
|
+
*/
|
|
239
|
+
const Passthrough = Node.create({
|
|
240
|
+
name: "passthrough",
|
|
241
|
+
group: "block",
|
|
242
|
+
atom: true,
|
|
243
|
+
addAttributes() {
|
|
244
|
+
return { data: {
|
|
245
|
+
default: "{}",
|
|
246
|
+
rendered: false
|
|
247
|
+
} };
|
|
248
|
+
},
|
|
249
|
+
parseHTML() {
|
|
250
|
+
return [{ tag: "div[data-passthrough]" }];
|
|
251
|
+
},
|
|
252
|
+
renderHTML({ node }) {
|
|
253
|
+
let label = "DOCX";
|
|
254
|
+
let key = "";
|
|
255
|
+
try {
|
|
256
|
+
const parsed = JSON.parse(node.attrs.data || "{}");
|
|
257
|
+
key = Object.keys(parsed)[0] ?? "";
|
|
258
|
+
if (key) label = key;
|
|
259
|
+
} catch {}
|
|
260
|
+
if (key === "bookmarkStart" || key === "bookmarkEnd") return ["div", {
|
|
261
|
+
"data-passthrough": label,
|
|
262
|
+
contenteditable: "false",
|
|
263
|
+
style: "display:none"
|
|
264
|
+
}];
|
|
265
|
+
return [
|
|
266
|
+
"div",
|
|
267
|
+
{
|
|
268
|
+
"data-passthrough": label,
|
|
269
|
+
contenteditable: "false",
|
|
270
|
+
style: "display:block;padding:0.5em 0.75em;margin:0.5em 0;border:1px dashed #bbb;border-radius:4px;color:#888;font-size:0.85em;background:#fafafa"
|
|
271
|
+
},
|
|
272
|
+
[
|
|
273
|
+
"span",
|
|
274
|
+
{},
|
|
275
|
+
`[${label}]`
|
|
276
|
+
]
|
|
277
|
+
];
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
//#endregion
|
|
281
|
+
//#region src/extensions/section-break.ts
|
|
282
|
+
/**
|
|
283
|
+
* SectionBreak — command extension that marks a paragraph as a section boundary.
|
|
284
|
+
*
|
|
285
|
+
* OOXML sections (sectPr) attach to a section's LAST paragraph's pPr, NOT a
|
|
286
|
+
* standalone node. So this extension provides only the `setSectionBreak`
|
|
287
|
+
* command (stamps sectionProperties on the current paragraph); the paragraph
|
|
288
|
+
* extension carries the sectionProperties/sectionHeaders/sectionFooters attrs,
|
|
289
|
+
* and DocxManager splits/merges sections in compile/resolve by reading them off
|
|
290
|
+
* the paragraph.
|
|
291
|
+
*
|
|
292
|
+
* The final section's sectPr rides on doc.attrs.sectionProperties (it lives at
|
|
293
|
+
* <w:body>'s end in OOXML). Single-section documents have no section-carrying
|
|
294
|
+
* paragraph at all.
|
|
295
|
+
*
|
|
296
|
+
* Next Page semantics: `setSectionBreak` stamps sectionProperties on the current
|
|
297
|
+
* paragraph (making it the section's last paragraph) AND inserts a fresh empty
|
|
298
|
+
* paragraph after it (the next section's first paragraph), then moves the
|
|
299
|
+
* selection into that new paragraph. The page-plugin's `forcesPageBreakAfter`
|
|
300
|
+
* treats a sectionProperties-bearing paragraph as a page break, so repaginate
|
|
301
|
+
* pushes the new paragraph onto the next page — and the caret follows. This
|
|
302
|
+
* mirrors Word's "Section Break (Next Page)".
|
|
303
|
+
*
|
|
304
|
+
* Split hygiene: pressing Enter inside a section-carrying paragraph must NOT
|
|
305
|
+
* split it. A split would place the new paragraph past the section boundary —
|
|
306
|
+
* forcesPageBreakAfter then pushes it onto the next page (next section), and
|
|
307
|
+
* splitBlock would copy sectionProperties onto it (a second break mark). Word
|
|
308
|
+
* instead inserts a fresh paragraph BEFORE the section's last paragraph, so the
|
|
309
|
+
* new paragraph stays in this section and the stamped paragraph remains last.
|
|
310
|
+
* The Enter shortcut does exactly that; non-section paragraphs fall through to
|
|
311
|
+
* the default Enter unchanged.
|
|
312
|
+
*/
|
|
313
|
+
const SectionBreak = Extension.create({
|
|
314
|
+
name: "sectionBreak",
|
|
315
|
+
priority: 1e3,
|
|
316
|
+
addCommands() {
|
|
317
|
+
return { setSectionBreak: () => ({ tr, state, dispatch }) => {
|
|
318
|
+
if (!dispatch) return true;
|
|
319
|
+
const { $from } = tr.selection;
|
|
320
|
+
const para = $from.parent;
|
|
321
|
+
if (para.type.name !== "paragraph") return false;
|
|
322
|
+
const paraPos = $from.before($from.depth);
|
|
323
|
+
tr.setNodeMarkup(paraPos, void 0, {
|
|
324
|
+
...para.attrs,
|
|
325
|
+
sectionProperties: {}
|
|
326
|
+
});
|
|
327
|
+
const paraEnd = paraPos + para.nodeSize;
|
|
328
|
+
tr.insert(paraEnd, state.schema.nodes.paragraph.create());
|
|
329
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(paraEnd + 1)));
|
|
330
|
+
tr.scrollIntoView();
|
|
331
|
+
return true;
|
|
332
|
+
} };
|
|
333
|
+
},
|
|
334
|
+
addKeyboardShortcuts() {
|
|
335
|
+
return { Enter: ({ editor }) => {
|
|
336
|
+
const { $from } = editor.state.selection;
|
|
337
|
+
const para = $from.parent;
|
|
338
|
+
if (para.attrs.sectionProperties == null) return false;
|
|
339
|
+
const paraPos = $from.before($from.depth);
|
|
340
|
+
return editor.chain().insertContentAt(paraPos, { type: para.type.name }).setTextSelection(paraPos + 1).run();
|
|
341
|
+
} };
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/extensions/toc-field.ts
|
|
346
|
+
/**
|
|
347
|
+
* TOC field (`tocField`) — a block container representing a DOCX table of
|
|
348
|
+
* contents, with the rendered entries as editable `content` and the TOC field
|
|
349
|
+
* switches on `attrs.options`.
|
|
350
|
+
*
|
|
351
|
+
* Named `tocField` (not `tableOfContents`) to avoid colliding with the official
|
|
352
|
+
* `@tiptap/extension-table-of-contents`, which is an `Extension` (a live outline
|
|
353
|
+
* generator injected into the editor) that already owns the `tableOfContents`
|
|
354
|
+
* name. Same-name extensions dedupe in Tiptap, and since the official one is not
|
|
355
|
+
* a node it would erase this node type from the schema. The two coexist for
|
|
356
|
+
* different purposes: this node persists a DOCX's rendered TOC; the official
|
|
357
|
+
* extension drives the heading-outline pane.
|
|
358
|
+
*
|
|
359
|
+
* Structuring the TOC as a node (instead of opaque passthrough) is what fixes
|
|
360
|
+
* the export crash. Each entry paragraph's `w:hyperlink` wraps a HYPERLINK field
|
|
361
|
+
* whose content-less runs (fldChar begin/separate/end) office-open parses as
|
|
362
|
+
* `null`. As opaque passthrough those nulls survived verbatim to
|
|
363
|
+
* `generateDocument`, where office-open's `stringifyRunInline(null).break`
|
|
364
|
+
* crashed. Resolving the entries through `resolveParagraphChildren` drops the
|
|
365
|
+
* nulls (the existing `child !== null` guard), so compile rebuilds clean entries
|
|
366
|
+
* and the generate path never sees a null — no office-open change required.
|
|
367
|
+
*
|
|
368
|
+
* DOCX serialization is inlined in DocxManager (resolve/compile read/write
|
|
369
|
+
* `attrs.options` + the entry content directly), so no renderDocx/parseDocx is
|
|
370
|
+
* needed here — the same pattern as the details extension.
|
|
371
|
+
*/
|
|
372
|
+
const TocField = Node.create({
|
|
373
|
+
name: "tocField",
|
|
374
|
+
group: "block",
|
|
375
|
+
content: "block+",
|
|
376
|
+
addAttributes() {
|
|
377
|
+
return { options: attrNative() };
|
|
378
|
+
},
|
|
379
|
+
parseHTML() {
|
|
380
|
+
return [{ tag: "div.docx-toc" }];
|
|
381
|
+
},
|
|
382
|
+
renderHTML() {
|
|
383
|
+
return [
|
|
384
|
+
"div",
|
|
385
|
+
{ class: "docx-toc" },
|
|
386
|
+
0
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/extensions/wpg-group.ts
|
|
392
|
+
/**
|
|
393
|
+
* wpgGroup — inline atom carrying a DOCX drawing group (wpg: wordprocessingGroup)
|
|
394
|
+
* as an opaque blob. Mirrors the office-open `WpgGroupRunOptions` / ParagraphChild
|
|
395
|
+
* `wpgGroup` field verbatim in attrs.wpgGroup, so the node name and attr stay
|
|
396
|
+
* aligned with the OOXML concept (CT_WordprocessingGroup).
|
|
397
|
+
*
|
|
398
|
+
* A group bundles pictures (pic), shapes (wps), and nested groups (wpg) behind a
|
|
399
|
+
* shared coordinate space (grpSpPr chOff/chExt → extent). renderHTML lays each
|
|
400
|
+
* child out at its transformed position/size so the group renders as Word draws
|
|
401
|
+
* it (e.g. a row of colored decoration bars), instead of a single picture.
|
|
402
|
+
*/
|
|
403
|
+
const EMU_PER_PT = 12700;
|
|
404
|
+
const EMU_PER_PX = 9525;
|
|
405
|
+
/** Solid fill → CSS color (noFill/none/gradient → undefined). */
|
|
406
|
+
function fillToCss(fill) {
|
|
407
|
+
if (!fill || fill.type !== "solid") return void 0;
|
|
408
|
+
return normalizeColorToHex(fill.color?.value);
|
|
409
|
+
}
|
|
410
|
+
/** Shape outline → CSS border (EMU width → pt). noFill → undefined. */
|
|
411
|
+
function outlineToCss(outline) {
|
|
412
|
+
if (!outline || outline.type === "noFill" || outline.type === "none") return void 0;
|
|
413
|
+
const color = normalizeColorToHex(outline.color?.value) ?? "black";
|
|
414
|
+
return `border:${outline.width != null ? `${outline.width / EMU_PER_PT}pt` : "0.75pt"} ${outline.dash === "sysDot" || outline.dash === "sysDash" ? "dashed" : "solid"} ${color}`;
|
|
415
|
+
}
|
|
416
|
+
/** Group extent in px: top-level groups carry {width,height}; nested wpg children
|
|
417
|
+
* carry a ChildTransformation whose pixels/emus give the size. */
|
|
418
|
+
function groupExtent(group) {
|
|
419
|
+
const t = group.transformation;
|
|
420
|
+
return {
|
|
421
|
+
w: t?.width != null ? convertEmuToPixels(t.width) : t?.pixels?.x ?? 0,
|
|
422
|
+
h: t?.height != null ? convertEmuToPixels(t.height) : t?.pixels?.y ?? 0
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/** Child coord (EMU, group's chOff/chExt space) → group-local px. */
|
|
426
|
+
function childBox(child, chOff, scaleX, scaleY) {
|
|
427
|
+
const t = child.transformation;
|
|
428
|
+
const off = t?.offset?.emus ?? {
|
|
429
|
+
x: 0,
|
|
430
|
+
y: 0
|
|
431
|
+
};
|
|
432
|
+
const size = t?.emus ?? {
|
|
433
|
+
x: 0,
|
|
434
|
+
y: 0
|
|
435
|
+
};
|
|
436
|
+
return {
|
|
437
|
+
x: (off.x - chOff.x) * scaleX,
|
|
438
|
+
y: (off.y - chOff.y) * scaleY,
|
|
439
|
+
w: size.x * scaleX,
|
|
440
|
+
h: size.y * scaleY
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
/** pic media type + raw bytes → data URL (bytes may arrive as Uint8Array or a
|
|
444
|
+
* plain object of byte values across the JSON boundary). */
|
|
445
|
+
function picSrc(mediaType, data) {
|
|
446
|
+
if (!data) return null;
|
|
447
|
+
return `data:image/${mediaType};base64,${encodeBase64(data instanceof Uint8Array ? data : new Uint8Array(Object.values(data)))}`;
|
|
448
|
+
}
|
|
449
|
+
function renderChild(child, chOff, scaleX, scaleY) {
|
|
450
|
+
const box = childBox(child, chOff, scaleX, scaleY);
|
|
451
|
+
const base = `position:absolute;left:${box.x}px;top:${box.y}px;width:${box.w}px;height:${box.h}px;box-sizing:border-box`;
|
|
452
|
+
if (child.type === "wpg") return renderGroup(child, box.w, box.h, base);
|
|
453
|
+
if (child.type === "wps") {
|
|
454
|
+
const data = child.data ?? {};
|
|
455
|
+
return renderWpsInterior(data, base, {
|
|
456
|
+
rotation: data.bodyProperties?.rotation ?? child.transformation?.rotation,
|
|
457
|
+
attrs: { "data-wpg-wps": "" }
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (child.type) {
|
|
461
|
+
const src = picSrc(child.type, child.data);
|
|
462
|
+
if (src) return ["img", {
|
|
463
|
+
src,
|
|
464
|
+
alt: "",
|
|
465
|
+
style: base
|
|
466
|
+
}];
|
|
467
|
+
}
|
|
468
|
+
return [];
|
|
469
|
+
}
|
|
470
|
+
/** wps text-body paragraphs (office-open ParagraphOptions[]) → inline HTML runs
|
|
471
|
+
* so a text-box shape shows its text. Each paragraph's run defaults merge with
|
|
472
|
+
* each text run; renderRunStyles converts font/color/size/… to CSS. Advanced run
|
|
473
|
+
* props (shadow w14RawXml, kern) are carried in attrs but not rendered. */
|
|
474
|
+
function renderWpsText(children) {
|
|
475
|
+
if (!Array.isArray(children)) return [];
|
|
476
|
+
return children.map((para) => {
|
|
477
|
+
if (typeof para === "string") return [
|
|
478
|
+
"p",
|
|
479
|
+
{ style: "margin:0" },
|
|
480
|
+
para
|
|
481
|
+
];
|
|
482
|
+
const defaultRun = para.run ?? {};
|
|
483
|
+
const runs = (para.children ?? []).map((r) => {
|
|
484
|
+
if (typeof r === "string") return r;
|
|
485
|
+
const css = renderRunStyles({
|
|
486
|
+
...defaultRun,
|
|
487
|
+
...r
|
|
488
|
+
});
|
|
489
|
+
return [
|
|
490
|
+
"span",
|
|
491
|
+
css.length ? { style: css.join(";") } : {},
|
|
492
|
+
r.text ?? ""
|
|
493
|
+
];
|
|
494
|
+
});
|
|
495
|
+
return [
|
|
496
|
+
"p",
|
|
497
|
+
{ style: para.alignment ? `margin:0;text-align:${para.alignment}` : "margin:0" },
|
|
498
|
+
...runs
|
|
499
|
+
];
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Render a wps shape's interior (fill/outline/insets/rotation/writing-mode + text
|
|
504
|
+
* body) as a positioned div. Shared by the wpg group's inline wps children and
|
|
505
|
+
* the standalone wpsShape node, so a text-box shape renders identically whether
|
|
506
|
+
* it floats alone (wp:anchor > wps:wsp) or sits inside a group. `positionStyle`
|
|
507
|
+
* carries the placement — absolute group coords for a group child, the floating
|
|
508
|
+
* anchor CSS for a standalone shape. `opts.rotation` overrides bodyPr rotation
|
|
509
|
+
* (a group child's rotation may come from the group transform, not bodyPr).
|
|
510
|
+
*/
|
|
511
|
+
function renderWpsInterior(data, positionStyle, opts) {
|
|
512
|
+
const styles = [positionStyle];
|
|
513
|
+
const fill = fillToCss(data.fill);
|
|
514
|
+
if (fill) styles.push(`background-color:${fill}`);
|
|
515
|
+
const outline = outlineToCss(data.outline);
|
|
516
|
+
if (outline) styles.push(outline);
|
|
517
|
+
const bp = data.bodyProperties;
|
|
518
|
+
if (bp) {
|
|
519
|
+
const ins = (v) => v != null ? `${(v / EMU_PER_PX).toFixed(1)}px` : "0px";
|
|
520
|
+
styles.push(`padding:${ins(bp.tIns)} ${ins(bp.rIns)} ${ins(bp.bIns)} ${ins(bp.lIns)}`);
|
|
521
|
+
}
|
|
522
|
+
const rotation = opts?.rotation ?? bp?.rotation;
|
|
523
|
+
if (rotation) styles.push(`transform:rotate(${rotation}deg)`);
|
|
524
|
+
const vert = bp?.vert;
|
|
525
|
+
if (vert && vert !== "horz") styles.push(`writing-mode:${vert === "vert270" ? "vertical-lr" : "vertical-rl"}`);
|
|
526
|
+
const attrs = { style: styles.join(";") };
|
|
527
|
+
if (opts?.attrs) Object.assign(attrs, opts.attrs);
|
|
528
|
+
return [
|
|
529
|
+
"div",
|
|
530
|
+
attrs,
|
|
531
|
+
...renderWpsText(data.children)
|
|
532
|
+
];
|
|
533
|
+
}
|
|
534
|
+
/** Render a wpg group (top-level or nested) as a positioned container.
|
|
535
|
+
* actualW/H are the group's real pixel size — top-level groups read them from
|
|
536
|
+
* transformation.width/height; nested groups receive the box their parent
|
|
537
|
+
* transform mapped them to. containerStyle (when set) carries the absolute
|
|
538
|
+
* placement for a nested group; otherwise the group is an inline-block. */
|
|
539
|
+
function renderGroup(group, actualW, actualH, containerStyle, extraAttrs) {
|
|
540
|
+
const chOff = group.childOffset ?? {
|
|
541
|
+
x: 0,
|
|
542
|
+
y: 0
|
|
543
|
+
};
|
|
544
|
+
const chExt = group.childExtent ?? {
|
|
545
|
+
cx: 1,
|
|
546
|
+
cy: 1
|
|
547
|
+
};
|
|
548
|
+
const scaleX = actualW / chExt.cx;
|
|
549
|
+
const scaleY = actualH / chExt.cy;
|
|
550
|
+
const style = containerStyle ?? `position:relative;display:inline-block;vertical-align:middle;width:${actualW}px;height:${actualH}px`;
|
|
551
|
+
const children = (group.children ?? []).map((c) => renderChild(c, chOff, scaleX, scaleY)).filter((c) => Array.isArray(c) && c.length > 0);
|
|
552
|
+
const attrs = {
|
|
553
|
+
"data-wpg-group": "",
|
|
554
|
+
style
|
|
555
|
+
};
|
|
556
|
+
if (extraAttrs) Object.assign(attrs, extraAttrs);
|
|
557
|
+
return [
|
|
558
|
+
"span",
|
|
559
|
+
attrs,
|
|
560
|
+
...children
|
|
561
|
+
];
|
|
562
|
+
}
|
|
563
|
+
const attrWpgGroup = () => ({
|
|
564
|
+
default: null,
|
|
565
|
+
rendered: false,
|
|
566
|
+
parseHTML: (element) => {
|
|
567
|
+
const raw = element.getAttribute("data-wpg-group");
|
|
568
|
+
if (!raw) return null;
|
|
569
|
+
try {
|
|
570
|
+
return JSON.parse(raw);
|
|
571
|
+
} catch {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
const WpgGroup = Node.create({
|
|
577
|
+
name: "wpgGroup",
|
|
578
|
+
group: "inline",
|
|
579
|
+
inline: true,
|
|
580
|
+
atom: true,
|
|
581
|
+
addAttributes() {
|
|
582
|
+
return { wpgGroup: attrWpgGroup() };
|
|
583
|
+
},
|
|
584
|
+
parseHTML() {
|
|
585
|
+
return [{ tag: "span[data-wpg-group]" }];
|
|
586
|
+
},
|
|
587
|
+
renderHTML({ node }) {
|
|
588
|
+
const wpg = node.attrs.wpgGroup ?? {};
|
|
589
|
+
const { w, h } = groupExtent(wpg);
|
|
590
|
+
if (wpg.floating) return renderGroup(wpg, w, h, [
|
|
591
|
+
...floatingToStyles(wpg.floating, void 0, w),
|
|
592
|
+
`width:${w}px`,
|
|
593
|
+
`height:${h}px`
|
|
594
|
+
].join(";"), floatAnchorScope(wpg.floating) === "paragraph" ? { "data-float-anchor": "paragraph" } : void 0);
|
|
595
|
+
return renderGroup(wpg, w, h);
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region src/extensions/wps-shape.ts
|
|
600
|
+
const attrWpsShape = () => ({
|
|
601
|
+
default: null,
|
|
602
|
+
rendered: false,
|
|
603
|
+
parseHTML: (element) => {
|
|
604
|
+
const raw = element.getAttribute("data-wps-shape");
|
|
605
|
+
if (!raw) return null;
|
|
606
|
+
try {
|
|
607
|
+
return JSON.parse(raw);
|
|
608
|
+
} catch {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
const WpsShape = Node.create({
|
|
614
|
+
name: "wpsShape",
|
|
615
|
+
group: "inline",
|
|
616
|
+
inline: true,
|
|
617
|
+
atom: true,
|
|
618
|
+
addAttributes() {
|
|
619
|
+
return { wpsShape: attrWpsShape() };
|
|
620
|
+
},
|
|
621
|
+
parseHTML() {
|
|
622
|
+
return [{ tag: "div[data-wps-shape]" }];
|
|
623
|
+
},
|
|
624
|
+
renderHTML({ node }) {
|
|
625
|
+
const ws = node.attrs.wpsShape ?? {};
|
|
626
|
+
const sizeStyle = `width:${ws.transformation?.width != null ? convertEmuToPixels(ws.transformation.width) : 0}px;height:${ws.transformation?.height != null ? convertEmuToPixels(ws.transformation.height) : 0}px;box-sizing:border-box`;
|
|
627
|
+
if (ws.floating) {
|
|
628
|
+
const pos = [...floatingToStyles(ws.floating, void 0, ws.transformation?.width), sizeStyle].join(";");
|
|
629
|
+
const attrs = { "data-wps-shape": "" };
|
|
630
|
+
if (floatAnchorScope(ws.floating) === "paragraph") attrs["data-float-anchor"] = "paragraph";
|
|
631
|
+
return renderWpsInterior(ws, pos, { attrs });
|
|
632
|
+
}
|
|
633
|
+
return renderWpsInterior(ws, `display:inline-block;vertical-align:middle;${sizeStyle}`, { attrs: { "data-wps-shape": "" } });
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/extensions/extensions.ts
|
|
638
|
+
const tiptapNodeExtensions = [
|
|
639
|
+
Document,
|
|
640
|
+
Paragraph,
|
|
641
|
+
Text,
|
|
642
|
+
HardBreak,
|
|
643
|
+
PageBreak,
|
|
644
|
+
ColumnBreak,
|
|
645
|
+
SectionBreak,
|
|
646
|
+
Passthrough,
|
|
647
|
+
TocField,
|
|
648
|
+
Blockquote,
|
|
649
|
+
OrderedList,
|
|
650
|
+
BulletList,
|
|
651
|
+
ListItem,
|
|
652
|
+
CodeBlock.configure({ lowlight: createLowlight(all) }),
|
|
653
|
+
Details,
|
|
654
|
+
DetailsSummary,
|
|
655
|
+
DetailsContent,
|
|
656
|
+
Emoji,
|
|
657
|
+
HorizontalRule,
|
|
658
|
+
Image.configure({ inline: true }),
|
|
659
|
+
WpgGroup,
|
|
660
|
+
WpsShape,
|
|
661
|
+
Mathematics,
|
|
662
|
+
Mention,
|
|
663
|
+
Table,
|
|
664
|
+
TableRow,
|
|
665
|
+
TableCell,
|
|
666
|
+
TableHeader,
|
|
667
|
+
TaskList,
|
|
668
|
+
TaskItem,
|
|
669
|
+
Heading,
|
|
670
|
+
TextAlign$1.configure({ types: ["heading", "paragraph"] })
|
|
671
|
+
];
|
|
672
|
+
const tiptapMarkExtensions = [
|
|
673
|
+
Bold,
|
|
674
|
+
Code,
|
|
675
|
+
Highlight,
|
|
676
|
+
Italic,
|
|
677
|
+
Link,
|
|
678
|
+
Strike,
|
|
679
|
+
Subscript,
|
|
680
|
+
Superscript,
|
|
681
|
+
TextStyle,
|
|
682
|
+
Underline
|
|
683
|
+
];
|
|
684
|
+
const docxExtensions = [
|
|
685
|
+
...tiptapNodeExtensions,
|
|
686
|
+
...tiptapMarkExtensions,
|
|
687
|
+
FormattingMarks
|
|
688
|
+
];
|
|
689
|
+
const DocxKit = Extension.create({
|
|
690
|
+
name: "docxKit",
|
|
691
|
+
addExtensions() {
|
|
692
|
+
const extensions = [];
|
|
693
|
+
if (this.options.bold !== false) extensions.push(Bold.configure(this.options.bold));
|
|
694
|
+
if (this.options.blockquote !== false) extensions.push(Blockquote.configure(this.options.blockquote));
|
|
695
|
+
if (this.options.bulletList !== false) extensions.push(BulletList.configure(this.options.bulletList));
|
|
696
|
+
if (this.options.code !== false) extensions.push(Code.configure(this.options.code));
|
|
697
|
+
if (this.options.codeBlock !== false) extensions.push(CodeBlockLowlight.configure({
|
|
698
|
+
lowlight: createLowlight(all),
|
|
699
|
+
...this.options.codeBlock
|
|
700
|
+
}));
|
|
701
|
+
if (this.options.document !== false) extensions.push(Document);
|
|
702
|
+
if (this.options.hardBreak !== false) extensions.push(HardBreak.configure(this.options.hardBreak));
|
|
703
|
+
if (this.options.heading !== false) extensions.push(Heading.configure(this.options.heading));
|
|
704
|
+
if (this.options.horizontalRule !== false) extensions.push(HorizontalRule.configure(this.options.horizontalRule));
|
|
705
|
+
if (this.options.italic !== false) extensions.push(Italic.configure(this.options.italic));
|
|
706
|
+
if (this.options.listItem !== false) extensions.push(ListItem.configure(this.options.listItem));
|
|
707
|
+
if (this.options.link !== false) extensions.push(Link.configure(this.options.link));
|
|
708
|
+
if (this.options.orderedList !== false) extensions.push(OrderedList.configure(this.options.orderedList));
|
|
709
|
+
if (this.options.paragraph !== false) extensions.push(Paragraph.configure(this.options.paragraph));
|
|
710
|
+
if (this.options.strike !== false) extensions.push(Strike.configure(this.options.strike));
|
|
711
|
+
if (this.options.text !== false) extensions.push(Text);
|
|
712
|
+
if (this.options.underline !== false) extensions.push(Underline.configure(this.options.underline));
|
|
713
|
+
return extensions;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
//#endregion
|
|
717
|
+
export { PageBreak as _, DocxKit as a, tiptapMarkExtensions as c, WpgGroup as d, renderWpsInterior as f, Passthrough as g, SectionBreak as h, Node as i, tiptapNodeExtensions as l, TocField as m, Extension as n, TextAlign as o, renderWpsText as p, Mark as r, docxExtensions as s, Editor as t, WpsShape as u, FormattingMarks as v, ColumnBreak as y };
|