@portone/docx-editor 0.4.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 (155) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/assets/editor.png +0 -0
  3. package/dist/DocxEditor.d.ts +1 -1
  4. package/dist/DocxEditor.js +44 -25
  5. package/dist/docx/cloning.js +4 -9
  6. package/dist/docx/commentOnlyChange.d.ts +5 -3
  7. package/dist/docx/comments/grammar.d.ts +27 -10
  8. package/dist/docx/comments/grammar.js +18 -67
  9. package/dist/docx/comments/model.d.ts +5 -6
  10. package/dist/docx/comments/model.js +0 -9
  11. package/dist/docx/comments/parts.d.ts +16 -10
  12. package/dist/docx/comments/parts.js +65 -13
  13. package/dist/docx/comments/people.d.ts +11 -3
  14. package/dist/docx/comments/people.js +20 -6
  15. package/dist/docx/comments/policy.js +7 -1
  16. package/dist/docx/comments/reading.d.ts +4 -2
  17. package/dist/docx/comments/reading.js +1 -14
  18. package/dist/docx/comments/writing.js +65 -28
  19. package/dist/docx/exportDocx.js +10 -14
  20. package/dist/docx/exportRefs.d.ts +8 -3
  21. package/dist/docx/exportRefs.js +2 -1
  22. package/dist/docx/fidelity.js +24 -10
  23. package/dist/docx/fields.d.ts +42 -0
  24. package/dist/docx/fields.js +76 -0
  25. package/dist/docx/headersFooters.d.ts +89 -20
  26. package/dist/docx/headersFooters.js +163 -172
  27. package/dist/docx/importDocx.d.ts +1 -1
  28. package/dist/docx/importDocx.js +100 -48
  29. package/dist/docx/importParagraph.d.ts +16 -4
  30. package/dist/docx/importParagraph.js +87 -93
  31. package/dist/docx/importPolicy.d.ts +70 -0
  32. package/dist/docx/importPolicy.js +190 -0
  33. package/dist/docx/importPreserved.d.ts +30 -0
  34. package/dist/docx/importPreserved.js +54 -0
  35. package/dist/docx/importTable.js +56 -12
  36. package/dist/docx/invariants.js +16 -18
  37. package/dist/docx/notes.d.ts +6 -1
  38. package/dist/docx/notes.js +6 -16
  39. package/dist/docx/pageGeometry.d.ts +5 -14
  40. package/dist/docx/pageGeometry.js +0 -12
  41. package/dist/docx/protectionPolicy.d.ts +13 -5
  42. package/dist/docx/protectionPolicy.js +32 -22
  43. package/dist/docx/scan.d.ts +18 -8
  44. package/dist/docx/scan.js +17 -11
  45. package/dist/docx/sections.d.ts +126 -0
  46. package/dist/docx/sections.js +207 -0
  47. package/dist/docx/serializeBlock.d.ts +13 -4
  48. package/dist/docx/serializeBlock.js +16 -18
  49. package/dist/docx/serializeParagraph.d.ts +4 -0
  50. package/dist/docx/serializeParagraph.js +1 -0
  51. package/dist/docx/serializePreserved.d.ts +14 -0
  52. package/dist/docx/serializePreserved.js +24 -0
  53. package/dist/docx/serializeStory.d.ts +17 -0
  54. package/dist/docx/serializeStory.js +17 -0
  55. package/dist/docx/serializeTable.js +10 -13
  56. package/dist/docx/session.d.ts +23 -6
  57. package/dist/docx/session.js +26 -5
  58. package/dist/docx/story.d.ts +140 -0
  59. package/dist/docx/story.js +237 -0
  60. package/dist/docx/storyProjection.js +6 -1
  61. package/dist/editor/clipboard/htmlReader.d.ts +30 -0
  62. package/dist/editor/{externalClipboard.js → clipboard/htmlReader.js} +39 -223
  63. package/dist/editor/clipboard/internalChannel.d.ts +57 -0
  64. package/dist/editor/clipboard/internalChannel.js +58 -0
  65. package/dist/editor/clipboard/normalizers.d.ts +99 -0
  66. package/dist/editor/clipboard/normalizers.js +199 -0
  67. package/dist/editor/clipboard/parser.d.ts +34 -0
  68. package/dist/editor/clipboard/parser.js +58 -0
  69. package/dist/editor/clipboard/plugin.d.ts +21 -0
  70. package/dist/editor/clipboard/plugin.js +320 -0
  71. package/dist/editor/clipboard/readContext.d.ts +24 -0
  72. package/dist/editor/clipboard/readContext.js +19 -0
  73. package/dist/editor/clipboard/readers.d.ts +33 -0
  74. package/dist/editor/clipboard/readers.js +37 -0
  75. package/dist/editor/commands/comments/editing.d.ts +26 -6
  76. package/dist/editor/commands/comments/editing.js +175 -105
  77. package/dist/editor/commands/comments/model.d.ts +34 -17
  78. package/dist/editor/commands/comments/model.js +11 -0
  79. package/dist/editor/commands/comments/reading.d.ts +5 -3
  80. package/dist/editor/commands/comments/reading.js +8 -58
  81. package/dist/editor/commands/index.d.ts +2 -2
  82. package/dist/editor/commands/index.js +2 -0
  83. package/dist/editor/commands/listCommands.d.ts +6 -0
  84. package/dist/editor/commands/listCommands.js +3 -2
  85. package/dist/editor/commands/noteQueries.d.ts +22 -5
  86. package/dist/editor/commands/noteQueries.js +34 -6
  87. package/dist/editor/createEditor.js +13 -3
  88. package/dist/editor/documentStyles.d.ts +26 -4
  89. package/dist/editor/documentStyles.js +9 -4
  90. package/dist/editor/imageFiles.d.ts +2 -2
  91. package/dist/editor/imageFiles.js +2 -0
  92. package/dist/editor/insertTable.d.ts +1 -1
  93. package/dist/editor/insertTable.js +2 -2
  94. package/dist/editor/plainText.d.ts +12 -0
  95. package/dist/editor/plainText.js +2 -1
  96. package/dist/editor/plugins/columnResize.js +6 -3
  97. package/dist/editor/plugins/commentComposer.d.ts +28 -0
  98. package/dist/editor/plugins/commentComposer.js +46 -0
  99. package/dist/editor/plugins/commentDecorations.d.ts +29 -2
  100. package/dist/editor/plugins/commentDecorations.js +89 -15
  101. package/dist/editor/plugins/documentProjection.d.ts +31 -0
  102. package/dist/editor/plugins/documentProjection.js +22 -0
  103. package/dist/editor/plugins/imagePaste.js +15 -8
  104. package/dist/editor/plugins/linkPanel.d.ts +7 -3
  105. package/dist/editor/plugins/linkPanel.js +13 -27
  106. package/dist/editor/plugins/panelState.d.ts +67 -0
  107. package/dist/editor/plugins/panelState.js +54 -0
  108. package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
  109. package/dist/editor/plugins/tableContextMenu.js +30 -47
  110. package/dist/editor/plugins/textContextMenu.d.ts +1 -1
  111. package/dist/editor/plugins/textContextMenu.js +30 -47
  112. package/dist/ooxml/fragment.d.ts +11 -0
  113. package/dist/ooxml/fragment.js +14 -4
  114. package/dist/ooxml/image.d.ts +3 -4
  115. package/dist/ooxml/image.js +50 -5
  116. package/dist/ooxml/names.d.ts +6 -0
  117. package/dist/ooxml/names.js +2 -0
  118. package/dist/ooxml/props.d.ts +8 -0
  119. package/dist/ooxml/props.js +14 -4
  120. package/dist/ooxml/rangeMarkers.d.ts +5 -0
  121. package/dist/ooxml/rangeMarkers.js +24 -0
  122. package/dist/page/PageGuides.d.ts +7 -3
  123. package/dist/page/PageGuides.js +13 -24
  124. package/dist/page/pageLayout.d.ts +64 -5
  125. package/dist/page/pageLayout.js +90 -29
  126. package/dist/page/usePageLayout.d.ts +14 -4
  127. package/dist/page/usePageLayout.js +19 -20
  128. package/dist/schema/attrRoles.js +38 -20
  129. package/dist/schema/docxSchema.d.ts +9 -1
  130. package/dist/schema/docxSchema.js +191 -139
  131. package/dist/schema/editGuard.d.ts +1 -1
  132. package/dist/schema/guards.js +2 -2
  133. package/dist/schema/index.d.ts +1 -0
  134. package/dist/schema/preservedFragments.d.ts +12 -0
  135. package/dist/schema/preservedFragments.js +38 -0
  136. package/dist/schema/preservedGuards.d.ts +9 -7
  137. package/dist/schema/preservedGuards.js +112 -11
  138. package/dist/schema/protection.d.ts +34 -5
  139. package/dist/schema/protection.js +42 -14
  140. package/dist/schema/rendering.js +3 -1
  141. package/dist/schema/stories.d.ts +49 -0
  142. package/dist/schema/stories.js +78 -0
  143. package/dist/styles/classNames.d.ts +3 -5
  144. package/dist/styles/classNames.js +3 -5
  145. package/dist/styles.css +74 -12
  146. package/dist/table/resize.d.ts +5 -3
  147. package/dist/ui/CommentsPanel.d.ts +1 -3
  148. package/dist/ui/CommentsPanel.js +24 -15
  149. package/dist/ui/NotesPanel.js +2 -2
  150. package/dist/ui/TextMenu.d.ts +1 -3
  151. package/dist/ui/TextMenu.js +10 -6
  152. package/dist/ui/comments/CommentComposer.d.ts +6 -1
  153. package/dist/ui/comments/CommentComposer.js +26 -3
  154. package/package.json +1 -1
  155. package/dist/editor/externalClipboard.d.ts +0 -22
@@ -0,0 +1,30 @@
1
+ import { Slice } from "prosemirror-model";
2
+ import type { Transaction } from "prosemirror-state";
3
+ import { type NewLists } from "../../numbering/listRegistry";
4
+ import type { ListKinds } from "./internalChannel";
5
+ import type { HtmlReadContext } from "./readContext";
6
+ /**
7
+ * The paragraph style a copy carries, which is the one thing a paste needs that the drawing does
8
+ * not already say. It is the style's id alone, where the editor draws the whole `w:pPr`.
9
+ */
10
+ export declare const COPIED_STYLE_ATTRIBUTE = "data-style";
11
+ /** What a paste puts in: the content, and the definitions of the lists it started */
12
+ export interface PastedContent {
13
+ slice: Slice;
14
+ newLists: NewLists;
15
+ /**
16
+ * What the numbers this slice's paragraphs name meant where it was copied
17
+ * (`./internalChannel`). Absent for a reading that gave the lists it read numbers of its own,
18
+ * which knows what kind each is from the markup it read them out of.
19
+ */
20
+ listKinds?: ListKinds;
21
+ }
22
+ /** The content one piece of already parsed markup reads as, or null when it reads as nothing */
23
+ export declare function readHtml(root: ParentNode, context: HtmlReadContext): PastedContent | null;
24
+ /** The same reading, for a caller holding the markup as a string rather than as elements */
25
+ export declare function readHtmlSlice(context: HtmlReadContext, source: string): PastedContent | null;
26
+ /**
27
+ * The transaction a paste is put in with: the content, and the definitions of the lists it started
28
+ * recorded on the document node so that the export writes them out and undo takes them back.
29
+ */
30
+ export declare function withPastedContent(tr: Transaction, content: PastedContent): Transaction;
@@ -1,43 +1,33 @@
1
- // src/editor/externalClipboard.ts
2
- import {
3
- DOMSerializer,
4
- Fragment,
5
- Slice
6
- } from "prosemirror-model";
7
- import { Plugin } from "prosemirror-state";
1
+ // src/editor/clipboard/htmlReader.ts
2
+ import { Fragment, Slice } from "prosemirror-model";
8
3
  import {
9
4
  paragraphAttrsFor,
10
5
  styleIdOf
11
- } from "../docx/formatting.js";
6
+ } from "../../docx/formatting.js";
12
7
  import {
13
8
  withListNumbering,
14
9
  withParagraphStyle
15
- } from "../docx/paraProps.js";
10
+ } from "../../docx/paraProps.js";
16
11
  import {
17
12
  listsWorn,
18
13
  NEW_LISTS_ATTR,
19
14
  newListsValue
20
- } from "../numbering/listRegistry.js";
15
+ } from "../../numbering/listRegistry.js";
21
16
  import {
22
17
  allocateList,
23
18
  MAX_ILVL,
24
19
  templateList
25
- } from "../numbering/listTemplate.js";
26
- import { docxSchema, isPageBreak } from "../schema/index.js";
27
- import { editorClassNames } from "../styles/classNames.js";
28
- import { PASTED_IMAGE_ATTRIBUTE } from "./clipboard/images.js";
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";
29
25
  import {
30
26
  appendInline,
31
27
  contextFor,
32
28
  marksFor,
33
- safeHref,
34
29
  withInlineStyle
35
- } from "./clipboard/inlineFormatting.js";
36
- import { numIdsIn } from "./commands/listCommands.js";
37
- import { documentFormatting, documentParagraphStyles } from "./documentStyles.js";
38
- import { insertPlainText } from "./plainText.js";
39
- import { moveCaretToDrop } from "./plugins/dropCaret.js";
40
- import { canStartNewList } from "./plugins/numberingDecorations.js";
30
+ } from "./inlineFormatting.js";
41
31
  var BLOCK_TAGS = /* @__PURE__ */ new Set([
42
32
  "ADDRESS",
43
33
  "ARTICLE",
@@ -71,143 +61,7 @@ var HEADING_FALLBACKS = {
71
61
  5: { bold: true, fontSizePt: 10 },
72
62
  6: { bold: true, fontSizePt: 8 }
73
63
  };
74
- var PUBLIC_ATTRIBUTES = /* @__PURE__ */ new Set([
75
- "alt",
76
- "aria-label",
77
- "class",
78
- "colspan",
79
- "height",
80
- "href",
81
- "lang",
82
- "rowspan",
83
- "src",
84
- "style",
85
- "width"
86
- ]);
87
64
  var COPIED_STYLE_ATTRIBUTE = "data-style";
88
- function isAttributes(value) {
89
- return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Node);
90
- }
91
- function publicAttributes(attrs) {
92
- return Object.fromEntries(
93
- Object.entries(attrs).filter(([name]) => PUBLIC_ATTRIBUTES.has(name))
94
- );
95
- }
96
- function isSpecArray(value) {
97
- return Array.isArray(value) && typeof value[0] === "string";
98
- }
99
- function stripPrivateAttributes(spec) {
100
- if (!isSpecArray(spec)) return spec;
101
- const [tag, ...rest] = spec;
102
- const attrs = isAttributes(rest[0]) ? publicAttributes(rest[0]) : null;
103
- const children = (attrs === null ? rest : rest.slice(1)).map(
104
- (child) => isSpecArray(child) ? stripPrivateAttributes(child) : child
105
- );
106
- return attrs === null ? [tag, ...children] : [tag, attrs, ...children];
107
- }
108
- function withDomAttribute(spec, name, value) {
109
- if (!isSpecArray(spec)) return spec;
110
- const [tag, ...rest] = spec;
111
- return isAttributes(rest[0]) ? [tag, { ...rest[0], [name]: value }, ...rest.slice(1)] : [tag, { [name]: value }, ...rest];
112
- }
113
- function asTag(spec, tag) {
114
- return isSpecArray(spec) ? [tag, ...spec.slice(1)] : spec;
115
- }
116
- function copiedMarkSpec(mark, spec) {
117
- const stripped = stripPrivateAttributes(spec);
118
- if (mark.type !== docxSchema.marks.link) return stripped;
119
- const href = safeHref(
120
- typeof mark.attrs.href === "string" ? mark.attrs.href : null
121
- );
122
- return href === null ? stripped : asTag(withDomAttribute(stripped, "href", href), "a");
123
- }
124
- function copiedNodeSpec(node, spec) {
125
- const stripped = stripPrivateAttributes(spec);
126
- if (node.type !== docxSchema.nodes.paragraph) return stripped;
127
- const styleId = styleIdOf(node.attrs.pPr);
128
- return styleId === null ? stripped : withDomAttribute(stripped, COPIED_STYLE_ATTRIBUTE, styleId);
129
- }
130
- function clipboardSerializer() {
131
- const drawn = DOMSerializer.fromSchema(docxSchema);
132
- const nodes = Object.fromEntries(
133
- Object.entries(drawn.nodes).map(([name, toDOM]) => [
134
- name,
135
- (node) => copiedNodeSpec(node, toDOM(node))
136
- ])
137
- );
138
- const marks = Object.fromEntries(
139
- Object.entries(drawn.marks).map(([name, toDOM]) => [
140
- name,
141
- (mark, inline) => copiedMarkSpec(mark, toDOM(mark, inline))
142
- ])
143
- );
144
- return new DOMSerializer(nodes, marks);
145
- }
146
- var UNREADABLE_BLOCKS = /* @__PURE__ */ new Set([
147
- "bookmarkBlock",
148
- "docxRaw",
149
- "rawBlock"
150
- ]);
151
- function stringAttr(value) {
152
- return typeof value === "string" ? value : "";
153
- }
154
- function inlineText(node) {
155
- if (node.isText) return node.text ?? "";
156
- if (node.type === docxSchema.nodes.hardBreak) {
157
- return isPageBreak(node.attrs.brAttrs) ? "\f" : "\n";
158
- }
159
- if (node.type === docxSchema.nodes.image) return stringAttr(node.attrs.alt);
160
- if (node.type === docxSchema.nodes.noteReference) {
161
- return node.attrs.customMarkFollows === true ? "" : stringAttr(node.attrs.label);
162
- }
163
- return "";
164
- }
165
- function rowsText(table) {
166
- const rows = [];
167
- table.forEach((row) => {
168
- const cells = [];
169
- row.forEach((cell) => {
170
- cells.push(fragmentText(cell.content));
171
- });
172
- rows.push(cells.join(" "));
173
- });
174
- return rows.join("\n");
175
- }
176
- function blockText(node) {
177
- if (node.type === docxSchema.nodes.table) return rowsText(node);
178
- if (UNREADABLE_BLOCKS.has(node.type.name)) return "";
179
- return fragmentText(node.content);
180
- }
181
- function fragmentText(fragment) {
182
- const pieces = [];
183
- fragment.forEach((child) => {
184
- pieces.push(child.isInline ? inlineText(child) : blockText(child));
185
- });
186
- return pieces.join(fragment.firstChild?.isInline === true ? "" : "\n");
187
- }
188
- function bareWrappers(content, openStart, openEnd) {
189
- const wrapper = content.firstChild;
190
- if (openStart <= 1 || openEnd <= 1 || content.childCount !== 1)
191
- return content;
192
- if (wrapper === null || wrapper.childCount !== 1) return content;
193
- return Fragment.from(
194
- wrapper.type.create(
195
- null,
196
- bareWrappers(wrapper.content, openStart - 1, openEnd - 1),
197
- wrapper.marks
198
- )
199
- );
200
- }
201
- function copiedSlice(slice) {
202
- return new Slice(
203
- bareWrappers(slice.content, slice.openStart, slice.openEnd),
204
- slice.openStart,
205
- slice.openEnd
206
- );
207
- }
208
- function clipboardText(slice) {
209
- return fragmentText(slice.content);
210
- }
211
65
  function normalizedStyleName(value) {
212
66
  return value.toLowerCase().replace(/[\s_-]+/g, "");
213
67
  }
@@ -223,8 +77,7 @@ function headingLevel(element, sourceStyleId) {
223
77
  );
224
78
  return match ? Number.parseInt(match[1] ?? "", 10) : null;
225
79
  }
226
- function destinationStyleId(state, sourceStyleId, level) {
227
- const styles = documentParagraphStyles(state);
80
+ function destinationStyleId(styles, sourceStyleId, level) {
228
81
  if (sourceStyleId && styles.some((style) => style.id === sourceStyleId)) {
229
82
  return sourceStyleId;
230
83
  }
@@ -234,7 +87,7 @@ function destinationStyleId(state, sourceStyleId, level) {
234
87
  (style) => normalizedStyleName(style.id) === wanted || normalizedStyleName(style.name) === wanted
235
88
  )?.id ?? null;
236
89
  }
237
- function blockContext(state, parent, element, preserveParagraphStyle) {
90
+ function blockContext(context, parent, element, preserveParagraphStyle) {
238
91
  const editorParagraph = element.classList.contains(
239
92
  editorClassNames.paragraph
240
93
  );
@@ -246,7 +99,11 @@ function blockContext(state, parent, element, preserveParagraphStyle) {
246
99
  paragraph: null
247
100
  };
248
101
  }
249
- const styleId = destinationStyleId(state, sourceStyleId, level);
102
+ const styleId = destinationStyleId(
103
+ context.paragraphStyles,
104
+ sourceStyleId,
105
+ level
106
+ );
250
107
  const paragraph = styleId === null ? null : withParagraphStyle(null, styleId);
251
108
  const usesDestinationStyle = editorParagraph && (sourceStyleId === null || paragraph !== null);
252
109
  let inline = parent;
@@ -270,17 +127,15 @@ function paragraphAttrs(formatting, list, paragraph) {
270
127
  return props ? { pPr: props.pPr, ...paragraphAttrsFor(props.pPr, formatting) } : null;
271
128
  }
272
129
  var HtmlReader = class {
273
- constructor(state, preserveParagraphStyles, images) {
274
- this.state = state;
130
+ constructor(context, preserveParagraphStyles) {
131
+ this.context = context;
275
132
  this.preserveParagraphStyles = preserveParagraphStyles;
276
- this.images = images;
277
- this.formatting = documentFormatting(state);
278
- this.used = numIdsIn(state.doc);
279
- this.canCreateLists = canStartNewList(state);
133
+ this.formatting = context.formatting;
134
+ this.used = new Set(context.numbering.used);
135
+ this.canCreateLists = context.numbering.canCreate;
280
136
  }
281
- state;
137
+ context;
282
138
  preserveParagraphStyles;
283
- images;
284
139
  blocks = [];
285
140
  used;
286
141
  canCreateLists;
@@ -302,7 +157,7 @@ var HtmlReader = class {
302
157
  appendInline(target, node, context, (content, element, inline) => {
303
158
  if (element.tagName !== "IMG") return false;
304
159
  const token = element.getAttribute(PASTED_IMAGE_ATTRIBUTE);
305
- const image = token === null ? void 0 : this.images.get(token);
160
+ const image = token === null ? void 0 : this.context.images.get(token);
306
161
  if (!image) return true;
307
162
  content.push(
308
163
  docxSchema.nodes.image.create(
@@ -346,7 +201,7 @@ var HtmlReader = class {
346
201
  flush();
347
202
  const before = this.blocks.length;
348
203
  const block = blockContext(
349
- this.state,
204
+ this.context,
350
205
  context,
351
206
  element,
352
207
  this.preserveParagraphStyles
@@ -418,69 +273,30 @@ function sliceDepth(root) {
418
273
  const blocks = [...BLOCK_TAGS, "OL", "UL"].map((tag) => tag.toLowerCase());
419
274
  return root.querySelector(blocks.join(",")) ? 0 : 1;
420
275
  }
421
- function richHtmlSlice(state, document, source, images = /* @__PURE__ */ new Map()) {
422
- if (source.trim() === "") return null;
423
- const template = document.createElement("template");
424
- template.innerHTML = source;
425
- const open = sliceDepth(template.content);
426
- const reader = new HtmlReader(state, open === 0, images);
427
- const blocks = reader.read(template.content);
276
+ function readHtml(root, context) {
277
+ const open = sliceDepth(root);
278
+ const reader = new HtmlReader(context, open === 0);
279
+ const blocks = reader.read(root);
428
280
  return blocks.length === 0 ? null : {
429
281
  slice: new Slice(Fragment.fromArray([...blocks]), open, open),
430
282
  newLists: reader.newLists
431
283
  };
432
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
+ }
433
291
  function withPastedContent(tr, content) {
434
292
  return tr.setDocAttribute(
435
293
  NEW_LISTS_ATTR,
436
294
  newListsValue(listsWorn(content.newLists, numIdsIn(tr.doc)))
437
295
  );
438
296
  }
439
- function insertRichHtml(view, source) {
440
- const content = richHtmlSlice(view.state, view.dom.ownerDocument, source);
441
- if (content === null) return false;
442
- view.dispatch(
443
- withPastedContent(
444
- view.state.tr.replaceSelection(content.slice),
445
- content
446
- ).scrollIntoView()
447
- );
448
- return true;
449
- }
450
- function insertClipboardData(view, data) {
451
- if (insertRichHtml(view, data.html ?? "")) return;
452
- insertPlainText(view, data.text ?? "");
453
- }
454
- function externalClipboard() {
455
- const serializer = clipboardSerializer();
456
- return new Plugin({
457
- props: {
458
- clipboardSerializer: serializer,
459
- clipboardTextSerializer: clipboardText,
460
- transformCopied: copiedSlice,
461
- handlePaste(view, event) {
462
- insertClipboardData(view, {
463
- html: event.clipboardData?.getData("text/html"),
464
- text: event.clipboardData?.getData("text/plain")
465
- });
466
- return true;
467
- },
468
- handleDrop(view, event) {
469
- if (view.dragging) return false;
470
- const text = event.dataTransfer?.getData("text/plain") ?? "";
471
- if (text) {
472
- moveCaretToDrop(view, event);
473
- insertPlainText(view, text);
474
- }
475
- return true;
476
- }
477
- }
478
- });
479
- }
480
297
  export {
481
- externalClipboard,
482
- insertClipboardData,
483
- insertRichHtml,
484
- richHtmlSlice,
298
+ COPIED_STYLE_ATTRIBUTE,
299
+ readHtml,
300
+ readHtmlSlice,
485
301
  withPastedContent
486
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;