@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.
Files changed (110) hide show
  1. package/README.md +2 -3
  2. package/dist/blockquote-Ky9xD_Q-.mjs +31 -0
  3. package/dist/code-block-BgNjhADy.mjs +23 -0
  4. package/dist/converters/docx.d.mts +1 -109
  5. package/dist/converters/docx.mjs +1 -623
  6. package/dist/converters/html.d.mts +1 -13
  7. package/dist/converters/html.mjs +1 -17
  8. package/dist/converters/markdown.d.mts +1 -12
  9. package/dist/converters/markdown.mjs +1 -17
  10. package/dist/converters/patch.d.mts +1 -52
  11. package/dist/converters/patch.mjs +1 -36
  12. package/dist/converters/prepare.d.mts +2 -51
  13. package/dist/converters/prepare.mjs +2 -76
  14. package/dist/converters/styles.d.mts +2 -0
  15. package/dist/converters/styles.mjs +2 -0
  16. package/dist/core-DEqBHRzE.mjs +406 -0
  17. package/dist/core-fBMCCj4D.d.mts +97 -0
  18. package/dist/core.d.mts +1 -1
  19. package/dist/core.mjs +1 -1
  20. package/dist/details-Dh8luWb2.mjs +18 -0
  21. package/dist/dist-B6B3HnVZ.mjs +1838 -0
  22. package/dist/document-BH1y4qHM.d.mts +6 -0
  23. package/dist/document-CvTym-FM.mjs +50 -0
  24. package/dist/docx-C5wyjI-0.d.mts +200 -0
  25. package/dist/docx-CONyAedl.mjs +1231 -0
  26. package/dist/editor-Bf3CxQnY.mjs +21 -0
  27. package/dist/editor-DReY3Rb9.d.mts +21 -0
  28. package/dist/editor.d.mts +1 -20
  29. package/dist/editor.mjs +1 -19
  30. package/dist/emoji-BZ1dJD7d.d.mts +17 -0
  31. package/dist/emoji-C2OsTWR7.mjs +27 -0
  32. package/dist/extensions/blockquote.d.mts +23 -0
  33. package/dist/extensions/blockquote.mjs +3 -0
  34. package/dist/extensions/code-block.d.mts +19 -0
  35. package/dist/extensions/code-block.mjs +2 -0
  36. package/dist/extensions/column-break.d.mts +31 -0
  37. package/dist/extensions/column-break.mjs +2 -0
  38. package/dist/extensions/details.d.mts +19 -0
  39. package/dist/extensions/details.mjs +3 -0
  40. package/dist/extensions/document.d.mts +2 -0
  41. package/dist/extensions/document.mjs +2 -0
  42. package/dist/extensions/emoji.d.mts +2 -0
  43. package/dist/extensions/emoji.mjs +2 -0
  44. package/dist/extensions/extensions.d.mts +6 -5
  45. package/dist/extensions/extensions.mjs +14 -12
  46. package/dist/extensions/formatting-marks.d.mts +32 -0
  47. package/dist/extensions/formatting-marks.mjs +2 -0
  48. package/dist/extensions/heading.mjs +1 -144
  49. package/dist/extensions/image-group.d.mts +6 -0
  50. package/dist/extensions/image-group.mjs +2 -0
  51. package/dist/extensions/image.d.mts +2 -2
  52. package/dist/extensions/image.mjs +1 -200
  53. package/dist/extensions/index.d.mts +3 -2
  54. package/dist/extensions/index.mjs +3 -2
  55. package/dist/extensions/mention.d.mts +25 -0
  56. package/dist/extensions/mention.mjs +3 -0
  57. package/dist/extensions/ordered-list.d.mts +25 -0
  58. package/dist/extensions/ordered-list.mjs +3 -0
  59. package/dist/extensions/page-break.d.mts +34 -0
  60. package/dist/extensions/page-break.mjs +2 -0
  61. package/dist/extensions/paragraph.d.mts +1 -1
  62. package/dist/extensions/paragraph.mjs +1 -115
  63. package/dist/extensions/passthrough.d.mts +2 -0
  64. package/dist/extensions/passthrough.mjs +2 -0
  65. package/dist/extensions/section-break.d.mts +27 -0
  66. package/dist/extensions/section-break.mjs +2 -0
  67. package/dist/extensions/strike.mjs +1 -49
  68. package/dist/extensions/table-cell.mjs +1 -111
  69. package/dist/extensions/table-header.mjs +1 -111
  70. package/dist/extensions/table-row.mjs +1 -83
  71. package/dist/extensions/table.mjs +1 -133
  72. package/dist/extensions/task-item.d.mts +27 -0
  73. package/dist/extensions/task-item.mjs +3 -0
  74. package/dist/extensions/text-style.mjs +1 -133
  75. package/dist/extensions/tiptap.mjs +1 -32
  76. package/dist/extensions/types.d.mts +7 -5
  77. package/dist/extensions/utils.d.mts +16 -3
  78. package/dist/extensions/utils.mjs +2 -289
  79. package/dist/heading-DAubgJJN.mjs +166 -0
  80. package/dist/html-DCC_DSHv.d.mts +18 -0
  81. package/dist/html-DedDWPoO.mjs +66 -0
  82. package/dist/image-CuEIXoZl.mjs +334 -0
  83. package/dist/index.d.mts +68 -18
  84. package/dist/index.mjs +10 -8
  85. package/dist/markdown-BzJmRsBs.mjs +18 -0
  86. package/dist/markdown-D0k0sRYS.d.mts +13 -0
  87. package/dist/mention-C-dTR6MK.mjs +44 -0
  88. package/dist/ordered-list-V-3xFQmV.mjs +41 -0
  89. package/dist/paragraph-BrWH-BOV.mjs +147 -0
  90. package/dist/paragraph-D8mpHo_o.d.mts +8 -0
  91. package/dist/patch-Cdp5WKJz.d.mts +53 -0
  92. package/dist/patch-IZ13tXTu.mjs +36 -0
  93. package/dist/prepare-Bdk8Cjkf.mjs +122 -0
  94. package/dist/prepare-CPAvcv6q.d.mts +63 -0
  95. package/dist/strike-hWxShOgb.mjs +50 -0
  96. package/dist/styles-QSX3RhxK.mjs +285 -0
  97. package/dist/styles-ugspkrxz.d.mts +85 -0
  98. package/dist/table-DrA12lxX.mjs +159 -0
  99. package/dist/table-cell-DmkfYwDc.mjs +112 -0
  100. package/dist/table-header-Bwr5GKWg.mjs +112 -0
  101. package/dist/table-row-vew1y7WH.mjs +84 -0
  102. package/dist/task-item-BnlXGrIG.mjs +37 -0
  103. package/dist/text-style-tMeBq5XQ.mjs +151 -0
  104. package/dist/tiptap-D7pl4I5U.mjs +33 -0
  105. package/dist/utils-BPT4tjcn.mjs +333 -0
  106. package/package.json +3 -2
  107. package/dist/core-CFIQVRfx.mjs +0 -88
  108. package/dist/core-omBKMRtl.d.mts +0 -34
  109. package/dist/image-Ge1y6uam.d.mts +0 -47
  110. package/dist/paragraph-fhEXtAN2.d.mts +0 -16
@@ -1,53 +1,2 @@
1
- import { a as JSONContent } from "../core-omBKMRtl.mjs";
2
- import { PrepareStep } from "./prepare.mjs";
3
- import { OutputByType, OutputType, patchDocument } from "@office-open/docx";
4
-
5
- //#region src/converters/patch.d.ts
6
- /**
7
- * A patch's replacement content, expressed as Tiptap JSON. `compileDocument`
8
- * converts it to office-open `SectionChild[]` before applying.
9
- */
10
- interface DocxPatchContent {
11
- content: JSONContent;
12
- }
13
- /**
14
- * Options for patching a DOCX template.
15
- *
16
- * Mirrors the legacy `@docen/export-docx` DocxPatchOptions minus `exportOptions`
17
- * — `compileDocument` already derives all styling from the Tiptap attrs, so no
18
- * separate export configuration is needed.
19
- */
20
- interface DocxPatchOptions<T extends OutputType = OutputType> {
21
- /** Template DOCX to patch. */
22
- template: Parameters<typeof patchDocument>[0]["data"];
23
- /** Placeholder name → replacement content. */
24
- patches: Record<string, DocxPatchContent>;
25
- /**
26
- * Pre-compilation steps run on each patch's content in place (default:
27
- * `prepareImages()`). `false` skips; `PrepareStep[]` runs custom steps.
28
- * Required when patch content references http image URLs.
29
- */
30
- prepare?: boolean | PrepareStep[];
31
- /** Custom placeholder delimiters (default `{{` / `}}`). */
32
- placeholderDelimiters?: {
33
- start?: string;
34
- end?: string;
35
- };
36
- /** Keep the template's paragraph styles on patched content. */
37
- keepOriginalStyles?: boolean;
38
- /** Recurse into nested patches. */
39
- recursive?: boolean;
40
- /** Output container type. */
41
- outputType: T;
42
- }
43
- /**
44
- * Patch a DOCX template by replacing placeholders with Tiptap-JSON content.
45
- *
46
- * Each patch's `content` is prepared (default: `prepareImages`, in place) then
47
- * compiled (`compileDocument` → `DocumentOptions`); its first section's
48
- * `children` become the replacement. Patching is delegated to
49
- * `@office-open/docx`'s `patchDocument`.
50
- */
51
- declare function patchDOCX<T extends OutputType>(options: DocxPatchOptions<T>): Promise<OutputByType[T]>;
52
- //#endregion
1
+ import { n as DocxPatchOptions, r as patchDOCX, t as DocxPatchContent } from "../patch-Cdp5WKJz.mjs";
53
2
  export { DocxPatchContent, DocxPatchOptions, patchDOCX };
@@ -1,37 +1,2 @@
1
- import { prepareDocument } from "./prepare.mjs";
2
- import { compileDocument } from "./docx.mjs";
3
- import { PatchType, patchDocument } from "@office-open/docx";
4
- //#region src/converters/patch.ts
5
- /**
6
- * Patch a DOCX template by replacing placeholders with Tiptap-JSON content.
7
- *
8
- * Each patch's `content` is prepared (default: `prepareImages`, in place) then
9
- * compiled (`compileDocument` → `DocumentOptions`); its first section's
10
- * `children` become the replacement. Patching is delegated to
11
- * `@office-open/docx`'s `patchDocument`.
12
- */
13
- async function patchDOCX(options) {
14
- const { template, patches, prepare = true, placeholderDelimiters, keepOriginalStyles, recursive, outputType } = options;
15
- const patchesObject = {};
16
- for (const [key, patchContent] of Object.entries(patches)) {
17
- if (prepare !== false) await prepareDocument(patchContent.content, prepare === true ? void 0 : prepare);
18
- const children = compileDocument(patchContent.content).sections?.[0]?.children ?? [];
19
- patchesObject[key] = {
20
- type: PatchType.DOCUMENT,
21
- children
22
- };
23
- }
24
- return patchDocument({
25
- outputType,
26
- data: template,
27
- patches: patchesObject,
28
- ...keepOriginalStyles !== void 0 && { keepOriginalStyles },
29
- ...recursive !== void 0 && { recursive },
30
- ...placeholderDelimiters && { placeholderDelimiters: {
31
- start: placeholderDelimiters.start ?? "{{",
32
- end: placeholderDelimiters.end ?? "}}"
33
- } }
34
- });
35
- }
36
- //#endregion
1
+ import { t as patchDOCX } from "../patch-IZ13tXTu.mjs";
37
2
  export { patchDOCX };
@@ -1,51 +1,2 @@
1
- import { a as JSONContent } from "../core-omBKMRtl.mjs";
2
-
3
- //#region src/converters/prepare.d.ts
4
- /**
5
- * A prepare step that transforms Tiptap JSON in place (e.g. fetch external resources).
6
- */
7
- type PrepareStep = (json: JSONContent) => Promise<void>;
8
- /**
9
- * Fetch handler for external image URLs.
10
- *
11
- * Receives the URL, returns the image binary data.
12
- * Override to customize fetching (proxy, auth, caching, etc.).
13
- */
14
- type ImageFetchHandler = (url: string) => Promise<Uint8Array>;
15
- /**
16
- * Default fetch handler using the global `fetch` API (Node 18+ and browsers).
17
- */
18
- declare function fetchImageHandler(url: string): Promise<Uint8Array>;
19
- /**
20
- * Create a prepare step that fetches external image URLs and converts them to data URLs.
21
- *
22
- * @param handler - Custom fetch handler (defaults to `fetchImageHandler`)
23
- *
24
- * @example
25
- * ```ts
26
- * // Use with defaults
27
- * await prepareDocument(json);
28
- *
29
- * // Custom handler
30
- * await prepareDocument(json, [prepareImages(myHandler)]);
31
- * ```
32
- */
33
- declare function prepareImages(handler?: ImageFetchHandler): PrepareStep;
34
- /**
35
- * Run prepare steps on a Tiptap JSON document before compilation.
36
- *
37
- * Each step receives the JSON and may mutate it in place (e.g. replace external
38
- * URLs with embedded data). Steps run sequentially in order.
39
- *
40
- * Defaults to `[prepareImages()]` when no steps are provided.
41
- *
42
- * @example
43
- * ```ts
44
- * const json = parseHTML(html);
45
- * await prepareDocument(json); // default: fetch images
46
- * const docOpts = compileDocument(json);
47
- * ```
48
- */
49
- declare function prepareDocument(json: JSONContent, steps?: readonly PrepareStep[]): Promise<void>;
50
- //#endregion
51
- export { ImageFetchHandler, PrepareStep, fetchImageHandler, prepareDocument, prepareImages };
1
+ import { a as prepareImageSizes, i as prepareDocument, n as PrepareStep, o as prepareImages, r as fetchImageHandler, t as ImageFetchHandler } from "../prepare-CPAvcv6q.mjs";
2
+ export { ImageFetchHandler, PrepareStep, fetchImageHandler, prepareDocument, prepareImageSizes, prepareImages };
@@ -1,76 +1,2 @@
1
- //#region src/converters/prepare.ts
2
- /**
3
- * Default fetch handler using the global `fetch` API (Node 18+ and browsers).
4
- */
5
- async function fetchImageHandler(url) {
6
- const response = await fetch(url);
7
- if (!response.ok) throw new Error(`Failed to fetch image from ${url}: ${response.status} ${response.statusText}`);
8
- const buffer = await response.arrayBuffer();
9
- return new Uint8Array(buffer);
10
- }
11
- /**
12
- * Create a prepare step that fetches external image URLs and converts them to data URLs.
13
- *
14
- * @param handler - Custom fetch handler (defaults to `fetchImageHandler`)
15
- *
16
- * @example
17
- * ```ts
18
- * // Use with defaults
19
- * await prepareDocument(json);
20
- *
21
- * // Custom handler
22
- * await prepareDocument(json, [prepareImages(myHandler)]);
23
- * ```
24
- */
25
- function prepareImages(handler = fetchImageHandler) {
26
- return async (json) => {
27
- await walkImages(json, handler);
28
- };
29
- }
30
- /** Built-in prepare steps, run when no custom steps are provided. */
31
- const DEFAULT_STEPS = [prepareImages()];
32
- /**
33
- * Run prepare steps on a Tiptap JSON document before compilation.
34
- *
35
- * Each step receives the JSON and may mutate it in place (e.g. replace external
36
- * URLs with embedded data). Steps run sequentially in order.
37
- *
38
- * Defaults to `[prepareImages()]` when no steps are provided.
39
- *
40
- * @example
41
- * ```ts
42
- * const json = parseHTML(html);
43
- * await prepareDocument(json); // default: fetch images
44
- * const docOpts = compileDocument(json);
45
- * ```
46
- */
47
- async function prepareDocument(json, steps = DEFAULT_STEPS) {
48
- for (const step of steps) await step(json);
49
- }
50
- async function toDataUrl(src, handler) {
51
- const data = await handler(src);
52
- return `data:${{
53
- jpg: "image/jpeg",
54
- jpeg: "image/jpeg",
55
- png: "image/png",
56
- gif: "image/gif",
57
- bmp: "image/bmp",
58
- svg: "image/svg+xml",
59
- webp: "image/webp"
60
- }[src.split(".").pop()?.toLowerCase() ?? "png"] ?? "image/png"};base64,${typeof btoa !== "undefined" ? btoa(String.fromCharCode(...data)) : Buffer.from(data).toString("base64")}`;
61
- }
62
- async function walkImages(node, handler) {
63
- if (node.type === "image" && node.attrs) {
64
- const src = node.attrs.src;
65
- if (src && (src.startsWith("http://") || src.startsWith("https://"))) try {
66
- node.attrs.src = await toDataUrl(src, handler);
67
- } catch (error) {
68
- console.warn(`Failed to fetch image: ${src}`, error instanceof Error ? error.message : error);
69
- }
70
- }
71
- const tasks = [];
72
- for (const child of node.content ?? []) tasks.push(walkImages(child, handler));
73
- await Promise.all(tasks);
74
- }
75
- //#endregion
76
- export { fetchImageHandler, prepareDocument, prepareImages };
1
+ import { i as prepareImages, n as prepareDocument, r as prepareImageSizes, t as fetchImageHandler } from "../prepare-Bdk8Cjkf.mjs";
2
+ export { fetchImageHandler, prepareDocument, prepareImageSizes, prepareImages };
@@ -0,0 +1,2 @@
1
+ import { a as inlineStyles, i as effectiveRunProps, n as StylesOptions, o as quickStyles, r as defaultParagraphStyleId, s as stylesToCss, t as QuickStyleEntry } from "../styles-ugspkrxz.mjs";
2
+ export { QuickStyleEntry, type StylesOptions, defaultParagraphStyleId, effectiveRunProps, inlineStyles, quickStyles, stylesToCss };
@@ -0,0 +1,2 @@
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 };
@@ -0,0 +1,406 @@
1
+ import { B as UndoRedo, C as Mention, D as Subscript, F as Text, I as TextAlign$1, N as TaskItem, O as Superscript, P as TaskList, R as TrailingNode, S as Mathematics, a as CodeBlockLowlight, b as ListItem, c as DetailsSummary, f as Gapcursor, g as HorizontalRule, h as Highlight, i as Code, n as Bold, o as Details, p as HardBreak, r as BulletList, s as DetailsContent, t as Blockquote, u as Dropcursor, v as Italic, w as OrderedList, x as ListKeymap, y as Link, z as Underline } from "./tiptap-D7pl4I5U.mjs";
2
+ import "./blockquote-Ky9xD_Q-.mjs";
3
+ import { t as CodeBlock } from "./code-block-BgNjhADy.mjs";
4
+ import "./details-Dh8luWb2.mjs";
5
+ import { t as Document } from "./document-CvTym-FM.mjs";
6
+ import { t as Emoji } from "./emoji-C2OsTWR7.mjs";
7
+ import { t as Heading } from "./heading-DAubgJJN.mjs";
8
+ import { t as Image } from "./image-CuEIXoZl.mjs";
9
+ import { t as encodeBase64 } from "./dist-B6B3HnVZ.mjs";
10
+ import "./mention-C-dTR6MK.mjs";
11
+ import "./ordered-list-V-3xFQmV.mjs";
12
+ import { t as Paragraph } from "./paragraph-BrWH-BOV.mjs";
13
+ import { t as Strike } from "./strike-hWxShOgb.mjs";
14
+ import { t as Table } from "./table-DrA12lxX.mjs";
15
+ import { t as TableCell } from "./table-cell-DmkfYwDc.mjs";
16
+ import { t as TableHeader } from "./table-header-Bwr5GKWg.mjs";
17
+ import { t as TableRow } from "./table-row-vew1y7WH.mjs";
18
+ import "./task-item-BnlXGrIG.mjs";
19
+ import { t as TextStyle } from "./text-style-tMeBq5XQ.mjs";
20
+ import { Editor, Extension, Mark, Node } from "@tiptap/core";
21
+ import { all, createLowlight } from "lowlight";
22
+ import { TextAlign } from "@tiptap/extension-text-align";
23
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
24
+ import { Decoration, DecorationSet } from "@tiptap/pm/view";
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 the on/off boolean for formatting marks. Toggled via a
63
+ * transaction meta so `decorations` recomputes when the user flips the Show/
64
+ * Hide button. */
65
+ const FORMAT_MARKS_KEY = new PluginKey("docen-formatting-marks");
66
+ /**
67
+ * FormattingMarks — paints the non-printing paragraph mark via ProseMirror
68
+ * widget decorations.
69
+ *
70
+ * CSS `p::after { content: "…" }` does NOT work on a ProseMirror-managed
71
+ * paragraph: the view owns the DOM and the trailing-break kludge pushes the
72
+ * pseudo-element off the visible line, so the mark never appears (verified).
73
+ * Marijn Haverbeke endorses widget decorations for exactly this use case
74
+ * (https://discuss.prosemirror.net/t/1442). Each textblock gets a widget at its
75
+ * closing position (inside the textblock, so it stays on the content's line)
76
+ * holding a non-editable `<span class="docen-para-mark">`. `side: 1` keeps the
77
+ * mark to the right of a cursor resting at the paragraph end (the cursor always
78
+ * precedes its own paragraph mark).
79
+ *
80
+ * Glyph: a down-then-left return arrow (↲, U+21B2 "downwards arrow with tip
81
+ * leftwards" — the Enter-key glyph). Word/WPS use the pilcrow ¶ for paragraph
82
+ * marks; that down-then-left arrow is Word's line-break glyph — adopted here as
83
+ * the paragraph mark per the project's visual preference.
84
+ */
85
+ const FormattingMarks = Extension.create({
86
+ name: "formattingMarks",
87
+ addProseMirrorPlugins() {
88
+ return [new Plugin({
89
+ key: FORMAT_MARKS_KEY,
90
+ state: {
91
+ init: () => false,
92
+ apply: (tr, value) => {
93
+ if (tr.getMeta(FORMAT_MARKS_KEY) === "toggle") return !value;
94
+ return value;
95
+ }
96
+ },
97
+ props: { decorations(state) {
98
+ if (!FORMAT_MARKS_KEY.getState(state)) return DecorationSet.empty;
99
+ const decos = [];
100
+ state.doc.descendants((node, pos) => {
101
+ if (!node.isTextblock) return;
102
+ decos.push(Decoration.widget(pos + node.nodeSize - 1, () => {
103
+ const span = document.createElement("span");
104
+ span.className = "docen-para-mark";
105
+ span.contentEditable = "false";
106
+ span.textContent = "↲";
107
+ return span;
108
+ }, { side: 1 }));
109
+ });
110
+ return DecorationSet.create(state.doc, decos);
111
+ } }
112
+ })];
113
+ },
114
+ addCommands() {
115
+ return { toggleFormattingMarks: () => ({ tr, dispatch }) => {
116
+ if (dispatch) dispatch(tr.setMeta(FORMAT_MARKS_KEY, "toggle"));
117
+ return true;
118
+ } };
119
+ }
120
+ });
121
+ //#endregion
122
+ //#region src/extensions/image-group.ts
123
+ /**
124
+ * ImageGroup — inline atom node carrying a DOCX drawing group (wpg:
125
+ * wordprocessingGroup) as an opaque blob. A group bundles pictures, shapes, and
126
+ * nested groups behind a shared transform; the editor doesn't model its
127
+ * interior — the full WpgGroupRunOptions round-trips verbatim in attrs.wpgGroup.
128
+ *
129
+ * renderHTML paints the first picture (common case: a single-logo header group)
130
+ * so the group is visible; shapes/nested groups fall back to a placeholder.
131
+ * Structured group/shape editing is a separate phase.
132
+ */
133
+ const EMU_PER_PX = 9525;
134
+ /** Extract the first picture (pic/MediaData) from a group for display. */
135
+ function firstPicture(group) {
136
+ const pic = group?.children?.find((c) => {
137
+ const t = c.type;
138
+ return typeof t === "string" && t !== "wps" && t !== "wpg";
139
+ });
140
+ if (!pic?.data) return null;
141
+ const bytes = pic.data instanceof Uint8Array ? pic.data : new Uint8Array(Object.values(pic.data));
142
+ const width = pic.transformation?.width;
143
+ const height = pic.transformation?.height;
144
+ return {
145
+ src: `data:image/${pic.type};base64,${encodeBase64(bytes)}`,
146
+ width: typeof width === "number" ? Math.round(width / EMU_PER_PX) : void 0,
147
+ height: typeof height === "number" ? Math.round(height / EMU_PER_PX) : void 0
148
+ };
149
+ }
150
+ const attrWpgGroup = () => ({
151
+ default: null,
152
+ rendered: false,
153
+ parseHTML: (element) => {
154
+ const raw = element.getAttribute("data-wpg-group");
155
+ if (!raw) return null;
156
+ try {
157
+ return JSON.parse(raw);
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
162
+ });
163
+ const ImageGroup = Node.create({
164
+ name: "imageGroup",
165
+ group: "inline",
166
+ inline: true,
167
+ atom: true,
168
+ addAttributes() {
169
+ return { wpgGroup: attrWpgGroup() };
170
+ },
171
+ parseHTML() {
172
+ return [{ tag: "span[data-image-group]" }];
173
+ },
174
+ renderHTML({ node }) {
175
+ const attrs = {
176
+ "data-image-group": "",
177
+ contenteditable: "false"
178
+ };
179
+ const pic = firstPicture(node.attrs.wpgGroup);
180
+ if (pic) {
181
+ const imgAttrs = {
182
+ src: pic.src,
183
+ alt: ""
184
+ };
185
+ if (pic.width && pic.height) imgAttrs.style = `width:${pic.width}px;height:${pic.height}px`;
186
+ return [
187
+ "span",
188
+ attrs,
189
+ ["img", imgAttrs]
190
+ ];
191
+ }
192
+ return [
193
+ "span",
194
+ attrs,
195
+ "♢"
196
+ ];
197
+ }
198
+ });
199
+ //#endregion
200
+ //#region src/extensions/page-break.ts
201
+ /**
202
+ * PageBreak — inline atom node for DOCX page breaks (`<w:br w:type="page"/>`).
203
+ *
204
+ * OOXML page breaks live inside a run (inline), so the Tiptap node is inline
205
+ * too (group "inline"). This keeps a break inside its paragraph on round-trip
206
+ * — a break mid-paragraph stays mid-paragraph instead of splitting the
207
+ * paragraph into separate blocks (which would lose the original structure).
208
+ * Sibling to HardBreak: both render `<w:br>`, differing only by type.
209
+ *
210
+ * The DOCX payload (`{ pageBreak: true }`) is inlined in DocxManager — a
211
+ * one-liner with no per-node variance, so no extension helper for it.
212
+ *
213
+ * Inserting a page break must also visually reflow (the C-route paginator only
214
+ * forces a new page after a block *containing* a pageBreak atom). So
215
+ * `setPageBreak` inserts the atom then splits the paragraph — the atom lands at
216
+ * the end of the first half, the trailing text becomes a fresh paragraph, and
217
+ * the paginator's `forcesPageBreakAfter` moves it to the next page (matching
218
+ * Word's Ctrl+Enter behavior).
219
+ */
220
+ const PageBreak = Node.create({
221
+ name: "pageBreak",
222
+ inline: true,
223
+ group: "inline",
224
+ atom: true,
225
+ parseHTML() {
226
+ return [{ tag: "span[data-type=\"pageBreak\"]" }, { tag: "br[data-type=\"pageBreak\"]" }];
227
+ },
228
+ renderHTML() {
229
+ return ["span", {
230
+ "data-type": "pageBreak",
231
+ style: "break-after:page"
232
+ }];
233
+ },
234
+ addCommands() {
235
+ return { setPageBreak: () => ({ tr, state, dispatch }) => {
236
+ if (!dispatch) return true;
237
+ tr.replaceSelectionWith(state.schema.nodes.pageBreak.create());
238
+ tr.split(tr.selection.from, 1);
239
+ tr.scrollIntoView();
240
+ return true;
241
+ } };
242
+ }
243
+ });
244
+ //#endregion
245
+ //#region src/extensions/passthrough.ts
246
+ /**
247
+ * Passthrough — block atom carrying an opaque {@link SectionChild} that has
248
+ * no native Tiptap representation (rawXml, bookmarkStart/End, toc, textbox,
249
+ * altChunk, subDoc, customXml).
250
+ *
251
+ * The full SectionChild is stored as JSON in `attrs.data` so the DOCX→JSON→DOCX
252
+ * round-trip stays byte-faithful: office-open's stringify handles the inner
253
+ * structure verbatim (including a textbox's nested children, which remain as
254
+ * structured ParagraphOptions inside the blob rather than editable Tiptap
255
+ * nodes). The node is not editable — it renders a read-only placeholder in HTML.
256
+ *
257
+ * DOCX serialization is inlined in DocxManager (compile/resolve read/write
258
+ * `attrs.data` directly), so no renderDocx/parseDocx is needed here.
259
+ */
260
+ const Passthrough = Node.create({
261
+ name: "passthrough",
262
+ group: "block",
263
+ atom: true,
264
+ addAttributes() {
265
+ return { data: {
266
+ default: "{}",
267
+ rendered: false
268
+ } };
269
+ },
270
+ parseHTML() {
271
+ return [{ tag: "div[data-passthrough]" }];
272
+ },
273
+ renderHTML({ node }) {
274
+ let label = "DOCX";
275
+ try {
276
+ const parsed = JSON.parse(node.attrs.data || "{}");
277
+ const key = Object.keys(parsed)[0];
278
+ if (key) label = key;
279
+ } catch {}
280
+ return [
281
+ "div",
282
+ {
283
+ "data-passthrough": label,
284
+ contenteditable: "false",
285
+ 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"
286
+ },
287
+ [
288
+ "span",
289
+ {},
290
+ `[${label}]`
291
+ ]
292
+ ];
293
+ }
294
+ });
295
+ //#endregion
296
+ //#region src/extensions/section-break.ts
297
+ /**
298
+ * SectionBreak — command extension that marks a paragraph as a section boundary.
299
+ *
300
+ * OOXML sections (sectPr) attach to a section's LAST paragraph's pPr, NOT a
301
+ * standalone node. So this extension provides only the `setSectionBreak`
302
+ * command (stamps sectionProperties on the current paragraph); the paragraph
303
+ * extension carries the sectionProperties/sectionHeaders/sectionFooters attrs,
304
+ * and DocxManager splits/merges sections in compile/resolve by reading them off
305
+ * the paragraph.
306
+ *
307
+ * The final section's sectPr rides on doc.attrs.sectionProperties (it lives at
308
+ * <w:body>'s end in OOXML). Single-section documents have no section-carrying
309
+ * paragraph at all.
310
+ */
311
+ const SectionBreak = Extension.create({
312
+ name: "sectionBreak",
313
+ addCommands() {
314
+ return { setSectionBreak: () => ({ commands }) => commands.updateAttributes("paragraph", { sectionProperties: {} }) };
315
+ }
316
+ });
317
+ //#endregion
318
+ //#region src/extensions/extensions.ts
319
+ const tiptapNodeExtensions = [
320
+ Document,
321
+ Paragraph,
322
+ Text,
323
+ HardBreak,
324
+ PageBreak,
325
+ ColumnBreak,
326
+ SectionBreak,
327
+ Passthrough,
328
+ Blockquote,
329
+ OrderedList,
330
+ BulletList,
331
+ ListItem,
332
+ CodeBlock.configure({ lowlight: createLowlight(all) }),
333
+ Details,
334
+ DetailsSummary,
335
+ DetailsContent,
336
+ Emoji,
337
+ HorizontalRule,
338
+ Image.configure({ inline: true }),
339
+ ImageGroup,
340
+ Mathematics,
341
+ Mention,
342
+ Table,
343
+ TableRow,
344
+ TableCell,
345
+ TableHeader,
346
+ TaskList,
347
+ TaskItem,
348
+ Heading,
349
+ TextAlign$1.configure({ types: ["heading", "paragraph"] })
350
+ ];
351
+ const tiptapMarkExtensions = [
352
+ Bold,
353
+ Code,
354
+ Highlight,
355
+ Italic,
356
+ Link,
357
+ Strike,
358
+ Subscript,
359
+ Superscript,
360
+ TextStyle,
361
+ Underline
362
+ ];
363
+ const docxExtensions = [
364
+ ...tiptapNodeExtensions,
365
+ ...tiptapMarkExtensions,
366
+ FormattingMarks,
367
+ UndoRedo,
368
+ Dropcursor,
369
+ Gapcursor,
370
+ TrailingNode,
371
+ ListKeymap
372
+ ];
373
+ const DocxKit = Extension.create({
374
+ name: "docxKit",
375
+ addExtensions() {
376
+ const extensions = [];
377
+ if (this.options.bold !== false) extensions.push(Bold.configure(this.options.bold));
378
+ if (this.options.blockquote !== false) extensions.push(Blockquote.configure(this.options.blockquote));
379
+ if (this.options.bulletList !== false) extensions.push(BulletList.configure(this.options.bulletList));
380
+ if (this.options.code !== false) extensions.push(Code.configure(this.options.code));
381
+ if (this.options.codeBlock !== false) extensions.push(CodeBlockLowlight.configure({
382
+ lowlight: createLowlight(all),
383
+ ...this.options.codeBlock
384
+ }));
385
+ if (this.options.document !== false) extensions.push(Document);
386
+ if (this.options.dropcursor !== false) extensions.push(Dropcursor.configure(this.options.dropcursor));
387
+ if (this.options.gapcursor !== false) extensions.push(Gapcursor);
388
+ if (this.options.hardBreak !== false) extensions.push(HardBreak.configure(this.options.hardBreak));
389
+ if (this.options.heading !== false) extensions.push(Heading.configure(this.options.heading));
390
+ if (this.options.undoRedo !== false) extensions.push(UndoRedo.configure(this.options.undoRedo));
391
+ if (this.options.horizontalRule !== false) extensions.push(HorizontalRule.configure(this.options.horizontalRule));
392
+ if (this.options.italic !== false) extensions.push(Italic.configure(this.options.italic));
393
+ if (this.options.listItem !== false) extensions.push(ListItem.configure(this.options.listItem));
394
+ if (this.options.listKeymap !== false) extensions.push(ListKeymap.configure(this.options.listKeymap));
395
+ if (this.options.link !== false) extensions.push(Link.configure(this.options.link));
396
+ if (this.options.orderedList !== false) extensions.push(OrderedList.configure(this.options.orderedList));
397
+ if (this.options.paragraph !== false) extensions.push(Paragraph.configure(this.options.paragraph));
398
+ if (this.options.strike !== false) extensions.push(Strike.configure(this.options.strike));
399
+ if (this.options.text !== false) extensions.push(Text);
400
+ if (this.options.underline !== false) extensions.push(Underline.configure(this.options.underline));
401
+ if (this.options.trailingNode !== false) extensions.push(TrailingNode.configure(this.options.trailingNode));
402
+ return extensions;
403
+ }
404
+ });
405
+ //#endregion
406
+ export { DocxKit as a, tiptapMarkExtensions as c, Passthrough as d, PageBreak as f, ColumnBreak as h, Node as i, tiptapNodeExtensions as l, FormattingMarks as m, Extension as n, TextAlign as o, ImageGroup as p, Mark as r, docxExtensions as s, Editor as t, SectionBreak as u };