@portone/docx-editor 0.3.0 → 0.5.0

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 (217) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/assets/editor.png +0 -0
  3. package/dist/DocxEditor.d.ts +1 -1
  4. package/dist/DocxEditor.js +45 -25
  5. package/dist/core.d.ts +1 -1
  6. package/dist/docx/cloning.js +4 -9
  7. package/dist/docx/commentOnlyChange.d.ts +5 -3
  8. package/dist/docx/comments/grammar.d.ts +27 -10
  9. package/dist/docx/comments/grammar.js +18 -67
  10. package/dist/docx/comments/model.d.ts +5 -6
  11. package/dist/docx/comments/model.js +0 -9
  12. package/dist/docx/comments/parts.d.ts +16 -10
  13. package/dist/docx/comments/parts.js +65 -13
  14. package/dist/docx/comments/people.d.ts +11 -3
  15. package/dist/docx/comments/people.js +20 -6
  16. package/dist/docx/comments/policy.js +7 -1
  17. package/dist/docx/comments/reading.d.ts +4 -2
  18. package/dist/docx/comments/reading.js +1 -14
  19. package/dist/docx/comments/writing.js +78 -35
  20. package/dist/docx/exportDocx.js +30 -18
  21. package/dist/docx/exportRefs.d.ts +8 -3
  22. package/dist/docx/exportRefs.js +2 -1
  23. package/dist/docx/fidelity.js +24 -10
  24. package/dist/docx/fields.d.ts +42 -0
  25. package/dist/docx/fields.js +76 -0
  26. package/dist/docx/formatting/attrs.d.ts +20 -2
  27. package/dist/docx/formatting/attrs.js +57 -2
  28. package/dist/docx/formatting/context.d.ts +10 -2
  29. package/dist/docx/formatting/context.js +23 -5
  30. package/dist/docx/formatting/resolve.d.ts +10 -4
  31. package/dist/docx/formatting/resolve.js +31 -11
  32. package/dist/docx/formatting/styles.d.ts +25 -2
  33. package/dist/docx/formatting/styles.js +84 -3
  34. package/dist/docx/headersFooters.d.ts +89 -20
  35. package/dist/docx/headersFooters.js +163 -172
  36. package/dist/docx/hyperlink.d.ts +14 -4
  37. package/dist/docx/hyperlink.js +4 -1
  38. package/dist/docx/importDocx.d.ts +1 -1
  39. package/dist/docx/importDocx.js +135 -74
  40. package/dist/docx/importParagraph.d.ts +16 -4
  41. package/dist/docx/importParagraph.js +87 -93
  42. package/dist/docx/importPolicy.d.ts +70 -0
  43. package/dist/docx/importPolicy.js +190 -0
  44. package/dist/docx/importPreserved.d.ts +30 -0
  45. package/dist/docx/importPreserved.js +54 -0
  46. package/dist/docx/importTable.d.ts +2 -2
  47. package/dist/docx/importTable.js +107 -66
  48. package/dist/docx/invariants.js +42 -31
  49. package/dist/docx/newLists.d.ts +30 -0
  50. package/dist/docx/newLists.js +24 -1
  51. package/dist/docx/notes.d.ts +6 -1
  52. package/dist/docx/notes.js +6 -16
  53. package/dist/docx/numberingPlanner.d.ts +6 -3
  54. package/dist/docx/numberingPlanner.js +38 -7
  55. package/dist/docx/pageGeometry.d.ts +5 -14
  56. package/dist/docx/pageGeometry.js +0 -12
  57. package/dist/docx/protectionPolicy.d.ts +13 -5
  58. package/dist/docx/protectionPolicy.js +32 -22
  59. package/dist/docx/scan.d.ts +18 -8
  60. package/dist/docx/scan.js +17 -11
  61. package/dist/docx/sections.d.ts +126 -0
  62. package/dist/docx/sections.js +207 -0
  63. package/dist/docx/serializeBlock.d.ts +13 -4
  64. package/dist/docx/serializeBlock.js +16 -18
  65. package/dist/docx/serializeParagraph.d.ts +4 -0
  66. package/dist/docx/serializeParagraph.js +1 -0
  67. package/dist/docx/serializePreserved.d.ts +14 -0
  68. package/dist/docx/serializePreserved.js +24 -0
  69. package/dist/docx/serializeStory.d.ts +17 -0
  70. package/dist/docx/serializeStory.js +17 -0
  71. package/dist/docx/serializeTable.js +10 -13
  72. package/dist/docx/session.d.ts +24 -7
  73. package/dist/docx/session.js +37 -6
  74. package/dist/docx/story.d.ts +140 -0
  75. package/dist/docx/story.js +237 -0
  76. package/dist/docx/storyProjection.js +6 -1
  77. package/dist/docx/tableFormatting/conditions.d.ts +76 -0
  78. package/dist/docx/tableFormatting/conditions.js +196 -0
  79. package/dist/docx/tableFormatting/editing.d.ts +7 -3
  80. package/dist/docx/tableFormatting/editing.js +4 -5
  81. package/dist/docx/tableFormatting/reading.d.ts +49 -19
  82. package/dist/docx/tableFormatting/reading.js +109 -35
  83. package/dist/docx/tableFormatting.d.ts +1 -0
  84. package/dist/docx/tableFormatting.js +1 -0
  85. package/dist/docx/tableTemplate.js +12 -12
  86. package/dist/editor/clipboard/htmlReader.d.ts +30 -0
  87. package/dist/editor/clipboard/htmlReader.js +302 -0
  88. package/dist/editor/clipboard/internalChannel.d.ts +57 -0
  89. package/dist/editor/clipboard/internalChannel.js +58 -0
  90. package/dist/editor/clipboard/normalizers.d.ts +99 -0
  91. package/dist/editor/clipboard/normalizers.js +199 -0
  92. package/dist/editor/clipboard/parser.d.ts +34 -0
  93. package/dist/editor/clipboard/parser.js +58 -0
  94. package/dist/editor/clipboard/plugin.d.ts +21 -0
  95. package/dist/editor/clipboard/plugin.js +320 -0
  96. package/dist/editor/clipboard/readContext.d.ts +24 -0
  97. package/dist/editor/clipboard/readContext.js +19 -0
  98. package/dist/editor/clipboard/readers.d.ts +33 -0
  99. package/dist/editor/clipboard/readers.js +37 -0
  100. package/dist/editor/commands/comments/editing.d.ts +26 -6
  101. package/dist/editor/commands/comments/editing.js +175 -105
  102. package/dist/editor/commands/comments/model.d.ts +34 -17
  103. package/dist/editor/commands/comments/model.js +11 -0
  104. package/dist/editor/commands/comments/reading.d.ts +5 -3
  105. package/dist/editor/commands/comments/reading.js +8 -58
  106. package/dist/editor/commands/formatting/propertyCommands.js +6 -1
  107. package/dist/editor/commands/index.d.ts +2 -2
  108. package/dist/editor/commands/index.js +2 -0
  109. package/dist/editor/commands/listCommands.d.ts +12 -3
  110. package/dist/editor/commands/listCommands.js +76 -30
  111. package/dist/editor/commands/noteQueries.d.ts +22 -5
  112. package/dist/editor/commands/noteQueries.js +34 -6
  113. package/dist/editor/commands/paragraphCommands.js +6 -1
  114. package/dist/editor/createEditor.js +14 -4
  115. package/dist/editor/documentStyles.d.ts +26 -4
  116. package/dist/editor/documentStyles.js +9 -4
  117. package/dist/editor/editorDocument.d.ts +7 -6
  118. package/dist/editor/editorDocument.js +18 -6
  119. package/dist/editor/imageFiles.d.ts +2 -2
  120. package/dist/editor/imageFiles.js +2 -0
  121. package/dist/editor/insertTable.d.ts +1 -1
  122. package/dist/editor/insertTable.js +2 -2
  123. package/dist/editor/paragraphEdits.d.ts +16 -1
  124. package/dist/editor/paragraphEdits.js +13 -5
  125. package/dist/editor/paragraphPlacement.d.ts +11 -0
  126. package/dist/editor/paragraphPlacement.js +18 -0
  127. package/dist/editor/plainText.d.ts +12 -0
  128. package/dist/editor/plainText.js +2 -1
  129. package/dist/editor/plugins/columnResize.js +6 -3
  130. package/dist/editor/plugins/commentComposer.d.ts +28 -0
  131. package/dist/editor/plugins/commentComposer.js +46 -0
  132. package/dist/editor/plugins/commentDecorations.d.ts +29 -2
  133. package/dist/editor/plugins/commentDecorations.js +89 -15
  134. package/dist/editor/plugins/documentProjection.d.ts +31 -0
  135. package/dist/editor/plugins/documentProjection.js +22 -0
  136. package/dist/editor/plugins/imagePaste.js +20 -10
  137. package/dist/editor/plugins/linkPanel.d.ts +7 -3
  138. package/dist/editor/plugins/linkPanel.js +13 -27
  139. package/dist/editor/plugins/numberingDecorations.d.ts +10 -4
  140. package/dist/editor/plugins/numberingDecorations.js +43 -2
  141. package/dist/editor/plugins/panelState.d.ts +67 -0
  142. package/dist/editor/plugins/panelState.js +54 -0
  143. package/dist/editor/plugins/paragraphDisplay.js +9 -3
  144. package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
  145. package/dist/editor/plugins/tableContextMenu.js +30 -47
  146. package/dist/editor/plugins/tableDisplay.js +19 -5
  147. package/dist/editor/plugins/textContextMenu.d.ts +1 -1
  148. package/dist/editor/plugins/textContextMenu.js +30 -47
  149. package/dist/model/format.d.ts +50 -0
  150. package/dist/model/format.js +65 -0
  151. package/dist/numbering/listRegistry.d.ts +37 -0
  152. package/dist/numbering/listRegistry.js +112 -0
  153. package/dist/numbering/listTemplate.d.ts +26 -17
  154. package/dist/numbering/listTemplate.js +66 -20
  155. package/dist/numbering/markers.d.ts +15 -6
  156. package/dist/numbering/markers.js +26 -59
  157. package/dist/numbering/parseNumbering.d.ts +70 -5
  158. package/dist/numbering/parseNumbering.js +100 -26
  159. package/dist/numbering/spellers.d.ts +15 -0
  160. package/dist/numbering/spellers.js +96 -0
  161. package/dist/numbering/writeNumbering.d.ts +5 -3
  162. package/dist/numbering/writeNumbering.js +15 -15
  163. package/dist/ooxml/conformance.d.ts +28 -0
  164. package/dist/ooxml/conformance.js +20 -0
  165. package/dist/ooxml/errors.d.ts +5 -5
  166. package/dist/ooxml/fragment.d.ts +11 -0
  167. package/dist/ooxml/fragment.js +14 -4
  168. package/dist/ooxml/image.d.ts +3 -4
  169. package/dist/ooxml/image.js +50 -5
  170. package/dist/ooxml/names.d.ts +15 -0
  171. package/dist/ooxml/names.js +10 -1
  172. package/dist/ooxml/partSplice.d.ts +1 -1
  173. package/dist/ooxml/partSplice.js +10 -3
  174. package/dist/ooxml/props.d.ts +8 -0
  175. package/dist/ooxml/props.js +14 -4
  176. package/dist/ooxml/rangeMarkers.d.ts +5 -0
  177. package/dist/ooxml/rangeMarkers.js +24 -0
  178. package/dist/ooxml/xml.d.ts +8 -9
  179. package/dist/ooxml/xml.js +6 -6
  180. package/dist/page/PageGuides.d.ts +8 -4
  181. package/dist/page/PageGuides.js +13 -37
  182. package/dist/page/pageLayout.d.ts +62 -13
  183. package/dist/page/pageLayout.js +91 -37
  184. package/dist/page/usePageLayout.d.ts +14 -11
  185. package/dist/page/usePageLayout.js +21 -29
  186. package/dist/schema/attrRoles.js +44 -19
  187. package/dist/schema/docxSchema.d.ts +9 -1
  188. package/dist/schema/docxSchema.js +220 -139
  189. package/dist/schema/editGuard.d.ts +1 -1
  190. package/dist/schema/guards.js +2 -2
  191. package/dist/schema/index.d.ts +1 -0
  192. package/dist/schema/preservedFragments.d.ts +12 -0
  193. package/dist/schema/preservedFragments.js +38 -0
  194. package/dist/schema/preservedGuards.d.ts +9 -7
  195. package/dist/schema/preservedGuards.js +112 -11
  196. package/dist/schema/protection.d.ts +34 -5
  197. package/dist/schema/protection.js +42 -14
  198. package/dist/schema/rendering.js +3 -1
  199. package/dist/schema/stories.d.ts +49 -0
  200. package/dist/schema/stories.js +78 -0
  201. package/dist/styles/classNames.d.ts +16 -7
  202. package/dist/styles/classNames.js +16 -7
  203. package/dist/styles.css +90 -30
  204. package/dist/table/cellFormatting.js +1 -2
  205. package/dist/table/gridBorders.d.ts +11 -15
  206. package/dist/table/gridBorders.js +32 -18
  207. package/dist/table/resize.d.ts +5 -3
  208. package/dist/ui/CommentsPanel.d.ts +1 -3
  209. package/dist/ui/CommentsPanel.js +24 -15
  210. package/dist/ui/NotesPanel.js +2 -2
  211. package/dist/ui/TextMenu.d.ts +1 -3
  212. package/dist/ui/TextMenu.js +10 -6
  213. package/dist/ui/comments/CommentComposer.d.ts +6 -1
  214. package/dist/ui/comments/CommentComposer.js +26 -3
  215. package/package.json +1 -1
  216. package/dist/editor/externalClipboard.d.ts +0 -11
  217. package/dist/editor/externalClipboard.js +0 -456
@@ -0,0 +1,302 @@
1
+ // src/editor/clipboard/htmlReader.ts
2
+ import { Fragment, Slice } from "prosemirror-model";
3
+ import {
4
+ paragraphAttrsFor,
5
+ styleIdOf
6
+ } from "../../docx/formatting.js";
7
+ import {
8
+ withListNumbering,
9
+ withParagraphStyle
10
+ } from "../../docx/paraProps.js";
11
+ import {
12
+ listsWorn,
13
+ NEW_LISTS_ATTR,
14
+ newListsValue
15
+ } from "../../numbering/listRegistry.js";
16
+ import {
17
+ allocateList,
18
+ MAX_ILVL,
19
+ templateList
20
+ } from "../../numbering/listTemplate.js";
21
+ import { docxSchema } from "../../schema/index.js";
22
+ import { editorClassNames } from "../../styles/classNames.js";
23
+ import { numIdsIn } from "../commands/listCommands.js";
24
+ import { PASTED_IMAGE_ATTRIBUTE } from "./images.js";
25
+ import {
26
+ appendInline,
27
+ contextFor,
28
+ marksFor,
29
+ withInlineStyle
30
+ } from "./inlineFormatting.js";
31
+ var BLOCK_TAGS = /* @__PURE__ */ new Set([
32
+ "ADDRESS",
33
+ "ARTICLE",
34
+ "ASIDE",
35
+ "BLOCKQUOTE",
36
+ "DIV",
37
+ "H1",
38
+ "H2",
39
+ "H3",
40
+ "H4",
41
+ "H5",
42
+ "H6",
43
+ "LI",
44
+ "P",
45
+ "PRE",
46
+ "SECTION"
47
+ ]);
48
+ var HEADING_LEVELS = {
49
+ H1: 1,
50
+ H2: 2,
51
+ H3: 3,
52
+ H4: 4,
53
+ H5: 5,
54
+ H6: 6
55
+ };
56
+ var HEADING_FALLBACKS = {
57
+ 1: { bold: true, fontSizePt: 24 },
58
+ 2: { bold: true, fontSizePt: 18 },
59
+ 3: { bold: true, fontSizePt: 14 },
60
+ 4: { bold: true, fontSizePt: 12 },
61
+ 5: { bold: true, fontSizePt: 10 },
62
+ 6: { bold: true, fontSizePt: 8 }
63
+ };
64
+ var COPIED_STYLE_ATTRIBUTE = "data-style";
65
+ function normalizedStyleName(value) {
66
+ return value.toLowerCase().replace(/[\s_-]+/g, "");
67
+ }
68
+ function copiedStyleId(element) {
69
+ if (!element.classList.contains(editorClassNames.paragraph)) return null;
70
+ return element.getAttribute(COPIED_STYLE_ATTRIBUTE) ?? styleIdOf(element.getAttribute("data-ppr"));
71
+ }
72
+ function headingLevel(element, sourceStyleId) {
73
+ const direct = HEADING_LEVELS[element.tagName];
74
+ if (direct !== void 0) return direct;
75
+ const match = /^heading([1-6])$/.exec(
76
+ normalizedStyleName(sourceStyleId ?? "")
77
+ );
78
+ return match ? Number.parseInt(match[1] ?? "", 10) : null;
79
+ }
80
+ function destinationStyleId(styles, sourceStyleId, level) {
81
+ if (sourceStyleId && styles.some((style) => style.id === sourceStyleId)) {
82
+ return sourceStyleId;
83
+ }
84
+ if (level === null) return null;
85
+ const wanted = `heading${level}`;
86
+ return styles.find(
87
+ (style) => normalizedStyleName(style.id) === wanted || normalizedStyleName(style.name) === wanted
88
+ )?.id ?? null;
89
+ }
90
+ function blockContext(context, parent, element, preserveParagraphStyle) {
91
+ const editorParagraph = element.classList.contains(
92
+ editorClassNames.paragraph
93
+ );
94
+ const sourceStyleId = copiedStyleId(element);
95
+ const level = headingLevel(element, sourceStyleId);
96
+ if (!preserveParagraphStyle && editorParagraph) {
97
+ return {
98
+ inline: contextFor(parent, element),
99
+ paragraph: null
100
+ };
101
+ }
102
+ const styleId = destinationStyleId(
103
+ context.paragraphStyles,
104
+ sourceStyleId,
105
+ level
106
+ );
107
+ const paragraph = styleId === null ? null : withParagraphStyle(null, styleId);
108
+ const usesDestinationStyle = editorParagraph && (sourceStyleId === null || paragraph !== null);
109
+ let inline = parent;
110
+ if (paragraph === null && level !== null) {
111
+ inline = withInlineStyle(inline, HEADING_FALLBACKS[level] ?? {});
112
+ }
113
+ return {
114
+ inline: contextFor(inline, element, !usesDestinationStyle),
115
+ paragraph
116
+ };
117
+ }
118
+ function listParagraphProps(list) {
119
+ if (!list || list.numId === null) return null;
120
+ return withListNumbering(null, {
121
+ numbering: { numId: list.numId, ilvl: Math.min(MAX_ILVL, list.level) },
122
+ indent: { kind: "keep" }
123
+ });
124
+ }
125
+ function paragraphAttrs(formatting, list, paragraph) {
126
+ const props = listParagraphProps(list) ?? paragraph;
127
+ return props ? { pPr: props.pPr, ...paragraphAttrsFor(props.pPr, formatting) } : null;
128
+ }
129
+ var HtmlReader = class {
130
+ constructor(context, preserveParagraphStyles) {
131
+ this.context = context;
132
+ this.preserveParagraphStyles = preserveParagraphStyles;
133
+ this.formatting = context.formatting;
134
+ this.used = new Set(context.numbering.used);
135
+ this.canCreateLists = context.numbering.canCreate;
136
+ }
137
+ context;
138
+ preserveParagraphStyles;
139
+ blocks = [];
140
+ used;
141
+ canCreateLists;
142
+ /**
143
+ * What the pasted paragraphs are resolved against: the document's context, holding the
144
+ * definition of each pasted list as it is read, so that an item is drawn against the list it
145
+ * is joining rather than against a list nothing yet defines.
146
+ */
147
+ formatting;
148
+ /** The definitions of every list started while editing, the pasted ones among them */
149
+ get newLists() {
150
+ return this.formatting.numbering.added;
151
+ }
152
+ read(root) {
153
+ this.readFlow(root, { style: {}, href: null, preserveWhitespace: false });
154
+ return this.blocks;
155
+ }
156
+ appendInline(target, node, context) {
157
+ appendInline(target, node, context, (content, element, inline) => {
158
+ if (element.tagName !== "IMG") return false;
159
+ const token = element.getAttribute(PASTED_IMAGE_ATTRIBUTE);
160
+ const image = token === null ? void 0 : this.context.images.get(token);
161
+ if (!image) return true;
162
+ content.push(
163
+ docxSchema.nodes.image.create(
164
+ {
165
+ ...image,
166
+ // The source drawing may point at a relationship in another package.
167
+ xml: null
168
+ },
169
+ null,
170
+ marksFor(inline)
171
+ )
172
+ );
173
+ return true;
174
+ });
175
+ }
176
+ addParagraph(content, list = null, paragraph = null) {
177
+ if (list && list.numId === null) {
178
+ const marker = list.kind === "bullet" ? "\u2022 " : "1. ";
179
+ content.unshift(docxSchema.text(marker));
180
+ }
181
+ this.blocks.push(
182
+ docxSchema.nodes.paragraph.create(
183
+ paragraphAttrs(this.formatting, list, paragraph),
184
+ content
185
+ )
186
+ );
187
+ }
188
+ readFlow(parent, context, paragraph = null) {
189
+ let inline = [];
190
+ const flush = () => {
191
+ if (inline.length === 0) return;
192
+ this.addParagraph(inline, null, paragraph);
193
+ inline = [];
194
+ };
195
+ for (const child of parent.childNodes) {
196
+ const element = child.nodeType === child.ELEMENT_NODE ? child : null;
197
+ if (element?.tagName === "UL" || element?.tagName === "OL") {
198
+ flush();
199
+ this.readList(element, context, 0, null);
200
+ } else if (element && BLOCK_TAGS.has(element.tagName)) {
201
+ flush();
202
+ const before = this.blocks.length;
203
+ const block = blockContext(
204
+ this.context,
205
+ context,
206
+ element,
207
+ this.preserveParagraphStyles
208
+ );
209
+ this.readFlow(element, block.inline, block.paragraph);
210
+ if (this.blocks.length === before) {
211
+ this.addParagraph([], null, block.paragraph);
212
+ }
213
+ } else {
214
+ this.appendInline(inline, child, context);
215
+ }
216
+ }
217
+ flush();
218
+ }
219
+ /**
220
+ * The number a pasted list takes, registered with the definition that list is drawn and written
221
+ * with. A pasted list is started here just as the list button starts one.
222
+ */
223
+ takeNumId(kind) {
224
+ if (!this.canCreateLists) return null;
225
+ const started = allocateList(
226
+ this.formatting.numbering,
227
+ this.used,
228
+ templateList(kind)
229
+ );
230
+ this.formatting = { ...this.formatting, numbering: started.numbering };
231
+ this.used.add(started.numId);
232
+ return started.numId;
233
+ }
234
+ readList(list, context, level, inherited) {
235
+ const kind = list.tagName === "UL" ? "bullet" : "numbered";
236
+ const listContext = {
237
+ kind,
238
+ numId: inherited?.kind === kind ? inherited.numId : this.takeNumId(kind),
239
+ level
240
+ };
241
+ const items = Array.from(list.children).filter(
242
+ (child) => child.tagName === "LI"
243
+ );
244
+ for (const item of items) {
245
+ const itemContext = contextFor(context, item);
246
+ const content = [];
247
+ for (const child of item.childNodes) {
248
+ const nested = child.nodeType === child.ELEMENT_NODE ? child : null;
249
+ if (nested?.tagName === "UL" || nested?.tagName === "OL") continue;
250
+ this.appendInline(content, child, itemContext);
251
+ }
252
+ this.addParagraph(content, listContext);
253
+ for (const nested of Array.from(item.children)) {
254
+ if (nested.tagName === "UL" || nested.tagName === "OL") {
255
+ this.readList(
256
+ nested,
257
+ itemContext,
258
+ level + 1,
259
+ listContext
260
+ );
261
+ }
262
+ }
263
+ }
264
+ }
265
+ };
266
+ function sliceDepth(root) {
267
+ const marker = root.querySelector("[data-pm-slice]");
268
+ const openStart = Number.parseInt(
269
+ marker?.getAttribute("data-pm-slice")?.split(/\s+/, 1)[0] ?? "",
270
+ 10
271
+ );
272
+ if (Number.isFinite(openStart)) return openStart === 0 ? 0 : 1;
273
+ const blocks = [...BLOCK_TAGS, "OL", "UL"].map((tag) => tag.toLowerCase());
274
+ return root.querySelector(blocks.join(",")) ? 0 : 1;
275
+ }
276
+ function readHtml(root, context) {
277
+ const open = sliceDepth(root);
278
+ const reader = new HtmlReader(context, open === 0);
279
+ const blocks = reader.read(root);
280
+ return blocks.length === 0 ? null : {
281
+ slice: new Slice(Fragment.fromArray([...blocks]), open, open),
282
+ newLists: reader.newLists
283
+ };
284
+ }
285
+ function readHtmlSlice(context, source) {
286
+ if (source.trim() === "") return null;
287
+ const template = context.document.createElement("template");
288
+ template.innerHTML = source;
289
+ return readHtml(template.content, context);
290
+ }
291
+ function withPastedContent(tr, content) {
292
+ return tr.setDocAttribute(
293
+ NEW_LISTS_ATTR,
294
+ newListsValue(listsWorn(content.newLists, numIdsIn(tr.doc)))
295
+ );
296
+ }
297
+ export {
298
+ COPIED_STYLE_ATTRIBUTE,
299
+ readHtml,
300
+ readHtmlSlice,
301
+ withPastedContent
302
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The channel a copy made in this editor comes back through.
3
+ *
4
+ * What leaves for the clipboard is HTML, and HTML carries only what a reader can make out of it:
5
+ * a paragraph's own properties, a table's grid, the relationship a link hung off are none of them
6
+ * writable there without publishing the document's insides. So the slice itself stays here, under
7
+ * a name the copied HTML carries, and a paste that hands that name back is given what was copied.
8
+ *
9
+ * The name is trusted only alongside the session it was copied from: another open document -
10
+ * a second editor on the same page among them - numbers its lists and relates its images against
11
+ * its own file, so a slice of it means something else here, and such a paste falls to the reader
12
+ * that reads the markup instead. A token nothing kept, a token from a copy since replaced, and a
13
+ * token invented by whoever wrote the markup all miss the same way.
14
+ *
15
+ * The slice is kept alongside the kind of each list its paragraphs name, which is the one thing
16
+ * about the copy that only the editor it was copied from can answer: a number says nothing about
17
+ * whether it counted or bulleted, and the definition that said so is the source document's. It is
18
+ * read here because this is where both ends are the same session.
19
+ */
20
+ import type { Slice } from "prosemirror-model";
21
+ import type { ListKind } from "../../numbering/listTemplate";
22
+ import type { Numbering } from "../../numbering/parseNumbering";
23
+ /** Where the copied HTML carries the name of the slice this editor kept */
24
+ export declare const INTERNAL_TOKEN_ATTRIBUTE = "data-docx-clip";
25
+ /** The kind of list each numbering id of a slice stood for where the slice was copied */
26
+ export type ListKinds = ReadonlyMap<number, ListKind>;
27
+ export declare const NO_LIST_KINDS: ListKinds;
28
+ /**
29
+ * The two ways a copy leaves the editor.
30
+ *
31
+ * A drag is serialized through the very same path a copy takes, so the one the drag wrote would
32
+ * replace the one the clipboard still holds, and a drag begun and let go would send the next paste
33
+ * of that clipboard to the reader that reads markup. They are kept apart instead, and each is
34
+ * replaced only by the next copy made the same way.
35
+ */
36
+ export type CopyRoute = "clipboard" | "drag";
37
+ /** What one name stands for: the slice that was copied, and what its list numbers meant there */
38
+ export interface RecalledCopy {
39
+ readonly slice: Slice;
40
+ readonly listKinds: ListKinds;
41
+ }
42
+ /**
43
+ * Keeps the copied slice under a fresh name, and answers with the name to write into the markup.
44
+ * Null for a state built without a document: with no session to bind the name to there is nothing
45
+ * to tell this editor's copy from another's, and the copy travels as markup alone.
46
+ */
47
+ export declare function rememberCopied(route: CopyRoute, slice: Slice, sessionId: string | null, numbering: Numbering): string | null;
48
+ /** The copy that name stands for, and only when the session asking is the one that copied it */
49
+ export declare function recallCopied(token: string | null, sessionId: string | null): RecalledCopy | null;
50
+ /**
51
+ * The name the pasted markup carries, if it carries one.
52
+ *
53
+ * The copy writes it on the first element it draws. `prosemirror-view` may wrap that element in
54
+ * the parents its tag needs (a row in a table) and descends back through them before parsing, so
55
+ * the element handed here is either the one carrying the name or its parent.
56
+ */
57
+ export declare function internalTokenOf(dom: Node): string | null;
@@ -0,0 +1,58 @@
1
+ // src/editor/clipboard/internalChannel.ts
2
+ import { docxSchema } from "../../schema/index.js";
3
+ import { listKindOf, listRefOf } from "../commands/listCommands.js";
4
+ var INTERNAL_TOKEN_ATTRIBUTE = "data-docx-clip";
5
+ var NO_LIST_KINDS = /* @__PURE__ */ new Map();
6
+ var copied = /* @__PURE__ */ new Map();
7
+ var copies = 0;
8
+ function nextToken() {
9
+ copies += 1;
10
+ return `c${copies}-${Math.random().toString(36).slice(2, 10)}`;
11
+ }
12
+ function rememberCopied(route, slice, sessionId, numbering) {
13
+ if (sessionId === null) return null;
14
+ const token = nextToken();
15
+ copied.set(route, {
16
+ token,
17
+ sessionId,
18
+ slice,
19
+ listKinds: listKindsIn(slice, numbering)
20
+ });
21
+ return token;
22
+ }
23
+ function recallCopied(token, sessionId) {
24
+ if (token === null || sessionId === null) return null;
25
+ const found = [...copied.values()].find(
26
+ (copy) => copy.token === token && copy.sessionId === sessionId
27
+ );
28
+ return found === void 0 ? null : { slice: found.slice, listKinds: found.listKinds };
29
+ }
30
+ function listKindsIn(slice, numbering) {
31
+ const kinds = /* @__PURE__ */ new Map();
32
+ const read = (node) => {
33
+ if (node.type === docxSchema.nodes.paragraph) {
34
+ const ref = listRefOf(node);
35
+ const kind = ref === null ? null : listKindOf(numbering, ref);
36
+ if (ref !== null && kind !== null) kinds.set(ref.numId, kind);
37
+ return;
38
+ }
39
+ node.forEach(read);
40
+ };
41
+ slice.content.forEach(read);
42
+ return kinds.size === 0 ? NO_LIST_KINDS : kinds;
43
+ }
44
+ function internalTokenOf(dom) {
45
+ if (dom instanceof Element) {
46
+ const own = dom.getAttribute(INTERNAL_TOKEN_ATTRIBUTE);
47
+ if (own !== null) return own;
48
+ }
49
+ const first = dom instanceof Element || dom instanceof DocumentFragment ? dom.firstElementChild : null;
50
+ return first?.getAttribute(INTERNAL_TOKEN_ATTRIBUTE) ?? null;
51
+ }
52
+ export {
53
+ INTERNAL_TOKEN_ATTRIBUTE,
54
+ NO_LIST_KINDS,
55
+ internalTokenOf,
56
+ recallCopied,
57
+ rememberCopied
58
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * What a slice has to obey before it may be put into this document.
3
+ *
4
+ * A slice reaching a paste says where its blocks came from, which list its paragraphs belong to,
5
+ * which comment its markers open, and what everything is drawn with. None of those mean anything
6
+ * outside the document that wrote them: a block naming a fragment of the file would be written
7
+ * from that fragment a second time, a list number naming no definition here would be exported
8
+ * bare, and a comment marker copied along would open a range the comment part never opens. So a
9
+ * paste is a normalization rather than an insertion, and each rule stands as one normalizer over
10
+ * the whole slice, in the order they are declared.
11
+ *
12
+ * A move drop is the exception the first two rules read: content dragged from one place in this
13
+ * very document to another is the same content, so it keeps the identity it had and the anchors it
14
+ * stood in. The source of a move is deleted along with the drop, so nothing is duplicated.
15
+ */
16
+ import { type Node as PMNode, Slice } from "prosemirror-model";
17
+ import type { EditorState } from "prosemirror-state";
18
+ import { type ListKind } from "../../numbering/listTemplate";
19
+ import type { PastedContent } from "./htmlReader";
20
+ import { type ListKinds } from "./internalChannel";
21
+ /** What one normalizer answers about the document the slice is going into */
22
+ export interface NormalizeContext {
23
+ readonly state: EditorState;
24
+ /** A drop that moves what it carries rather than copying it, which keeps its source identity */
25
+ readonly move: boolean;
26
+ /**
27
+ * The list numbers this document answers for: the ones it defines, the ones a list started
28
+ * while editing was registered under, the ones its paragraphs wear, and the ones this very
29
+ * paste has started.
30
+ */
31
+ readonly knownLists: ReadonlySet<number>;
32
+ /**
33
+ * What the numbers the slice names meant where it was copied, for the numbers this document
34
+ * cannot answer for. Empty for a slice that came in as markup, whose lists were given numbers
35
+ * of this document as they were read.
36
+ */
37
+ readonly listKinds: ListKinds;
38
+ /**
39
+ * A number for a list this slice starts, registered with the definition it is started with, as
40
+ * the list button starts one. Null in a document with nowhere to write a definition.
41
+ */
42
+ startList(kind: ListKind): number | null;
43
+ }
44
+ export type SliceNormalizer = (slice: Slice, context: NormalizeContext) => Slice;
45
+ /** The slice with every node rewritten depth first, and the ones mapped to null taken out */
46
+ export declare function mapSliceNodes(slice: Slice, f: (node: PMNode) => PMNode | null): Slice;
47
+ /**
48
+ * Takes the source identity off what is pasted.
49
+ *
50
+ * A block names the fragment of the opened file it came from, and the export writes that fragment
51
+ * back verbatim rather than building one. Two blocks naming the same fragment would therefore go
52
+ * out as the same bytes twice, identifiers and all, so a pasted copy names none and is written
53
+ * from the model instead. A preserved block holding its own XML is that XML wherever it stands and
54
+ * travels whole; one that only names a fragment has nothing of its own to travel with, so it goes.
55
+ */
56
+ export declare const dropSourceIdentity: SliceNormalizer;
57
+ /**
58
+ * Gives a pasted list a number this document defines.
59
+ *
60
+ * A number the destination knows nothing about is exported as a list nothing defines, which the
61
+ * export invariants refuse. The paragraphs that shared such a number still share the one they are
62
+ * given, so a pasted list stays one list, started the way the list button starts one and drawn and
63
+ * written from the destination's own definition from here on.
64
+ *
65
+ * Which of the two kinds it is started as is the one thing the number alone does not say. A copy
66
+ * made in this same session was remembered alongside what its numbers meant there
67
+ * (`./internalChannel`), so a bulleted list comes back bulleted; a number arriving any other way
68
+ * has no kind to be read off it and counts.
69
+ */
70
+ export declare const rekeyNumbering: SliceNormalizer;
71
+ /**
72
+ * Takes the anchors off what is pasted.
73
+ *
74
+ * A comment marker, a bookmark and a note reference all point at something written elsewhere in
75
+ * the package: the comment, the bookmark's other end, the note's body. A copy of the marker alone
76
+ * points at the same thing a second time, which is a duplicate identifier the file may not hold
77
+ * and a range the reader cannot close. Nothing here duplicates what they point at, so the anchor
78
+ * goes and the text it stood in stays, which is what Word does with a note it has no body for.
79
+ *
80
+ * A move drop duplicates nothing: the source goes as the drop lands, so the one anchor there was
81
+ * travels with the text it opened. Taking it off would delete a marker the document has no way to
82
+ * write back, which is a change the preserved guard refuses whole, and the drag would do nothing.
83
+ */
84
+ export declare const detachAnchors: SliceNormalizer;
85
+ /**
86
+ * Works out again what the pasted paragraphs are drawn with.
87
+ *
88
+ * The values a paragraph and the runs in it carry were resolved against the styles, the defaults
89
+ * and the list definitions of wherever the slice came from, this document's own list definitions
90
+ * after the numbers have just been reissued among them. Resolving them here is what makes the
91
+ * numbering the paste registers the numbering the document is then found to wear.
92
+ */
93
+ export declare const rederiveDisplay: SliceNormalizer;
94
+ export declare const DEFAULT_NORMALIZERS: readonly SliceNormalizer[];
95
+ /**
96
+ * The pasted content as this document may hold it, and the definitions of the lists the paste
97
+ * starts - the ones a reading started, and the ones a reissued number was taken for.
98
+ */
99
+ export declare function normalizePasted(content: PastedContent, state: EditorState, move: boolean, normalizers?: readonly SliceNormalizer[]): PastedContent;