@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
@@ -1,5 +1,7 @@
1
1
  // src/schema/docxSchema.ts
2
- import { Schema } from "prosemirror-model";
2
+ import {
3
+ Schema
4
+ } from "prosemirror-model";
3
5
  import {
4
6
  spanCount,
5
7
  toBandSizes,
@@ -17,6 +19,7 @@ import {
17
19
  } from "../model/format.js";
18
20
  import {
19
21
  ANY_ELEMENT,
22
+ ANY_ELEMENTS,
20
23
  ATTRIBUTES,
21
24
  acceptRawXml,
22
25
  ELEMENT
@@ -33,6 +36,7 @@ import {
33
36
  } from "../styles/inlineStyle.js";
34
37
  import { imageNodeSpec, runMarkSpec } from "./rendering.js";
35
38
  import { imageNodeSpec as imageNodeSpec2, runMarkSpec as runMarkSpec2 } from "./rendering.js";
39
+ var NO_STORIES = Object.freeze({});
36
40
  function isRecord(value) {
37
41
  return typeof value === "object" && value !== null;
38
42
  }
@@ -78,7 +82,7 @@ function repliesHoldTheirXml(value) {
78
82
  const replies = value;
79
83
  return replies.every((reply) => {
80
84
  if (!isRecord(reply)) return true;
81
- return acceptRawXml(ELEMENT("comment"), text(reply.commentXml) ?? null) !== false && acceptRawXml(ANY_ELEMENT, text(reply.extensionXml) ?? null) !== false;
85
+ return acceptRawXml(ANY_ELEMENT, text(reply.extensionXml) ?? null) !== false;
82
86
  });
83
87
  }
84
88
  var SDT_PREFIX = {
@@ -96,22 +100,65 @@ var LINK_PREFIX = {
96
100
  function rawXml(dom, attribute, shape) {
97
101
  return acceptRawXml(shape, dom.getAttribute(attribute));
98
102
  }
103
+ function preservedDisplayOf(value) {
104
+ switch (value) {
105
+ case "text":
106
+ return "text";
107
+ case "break":
108
+ return "break";
109
+ case "chip":
110
+ return "chip";
111
+ default:
112
+ return "hidden";
113
+ }
114
+ }
115
+ function preservedAttributes(node, className) {
116
+ return {
117
+ class: className,
118
+ "data-xml": text(node.attrs.xml),
119
+ "data-element": text(node.attrs.element),
120
+ "data-display": preservedDisplayOf(node.attrs.display),
121
+ "data-text": text(node.attrs.text),
122
+ "data-guarded": node.attrs.guarded === true ? "1" : void 0
123
+ };
124
+ }
125
+ function preservedDom(node, className) {
126
+ const attributes = preservedAttributes(node, className);
127
+ const display = preservedDisplayOf(node.attrs.display);
128
+ const preview = text(node.attrs.text);
129
+ const element = text(node.attrs.element);
130
+ if (display === "break") return ["span", attributes, ["br"]];
131
+ if (display === "text") return ["span", attributes, preview ?? ""];
132
+ if (display === "chip") {
133
+ return [
134
+ "span",
135
+ { ...attributes, title: element ?? UNKNOWN_PLACEHOLDER },
136
+ preview !== void 0 && preview !== "" ? preview : element ?? "?"
137
+ ];
138
+ }
139
+ return ["span", attributes];
140
+ }
141
+ function preservedAttrs(dom) {
142
+ const xml = rawXml(dom, "data-xml", ANY_ELEMENT);
143
+ if (xml === false) return false;
144
+ return {
145
+ xml,
146
+ element: dom.getAttribute("data-element"),
147
+ display: preservedDisplayOf(dom.getAttribute("data-display")),
148
+ text: dom.getAttribute("data-text"),
149
+ guarded: dom.getAttribute("data-guarded") === "1"
150
+ };
151
+ }
99
152
  var TABLE_PLACEHOLDER = "Table (unsupported layout, original is preserved)";
100
153
  var UNKNOWN_PLACEHOLDER = "Unsupported content (not editable, original is preserved)";
101
154
  var TABLE_NAMES = ["w:tbl", "tbl"];
102
- function rawBlockDom(name) {
103
- if (name !== void 0 && TABLE_NAMES.includes(name)) {
104
- return {
105
- className: editorClassNames.tablePlaceholder,
106
- label: TABLE_PLACEHOLDER
107
- };
108
- }
109
- return { className: editorClassNames.rawBlock, label: UNKNOWN_PLACEHOLDER };
155
+ function placeholderLabel(name) {
156
+ return name !== void 0 && TABLE_NAMES.includes(name) ? TABLE_PLACEHOLDER : UNKNOWN_PLACEHOLDER;
157
+ }
158
+ function preservedNameOf(dom) {
159
+ const name = dom.getAttribute("data-name");
160
+ return name === null || name === "" ? null : name;
110
161
  }
111
- var DOCX_RAW_SELECTOR = [
112
- editorClassNames.rawBlock,
113
- editorClassNames.tablePlaceholder
114
- ].map((className) => `div[data-src].${className}`).join(", ");
115
162
  var docxSchema = new Schema({
116
163
  nodes: {
117
164
  doc: {
@@ -121,7 +168,20 @@ var docxSchema = new Schema({
121
168
  * The definitions of the lists started while editing, which the export writes into
122
169
  * numbering.xml. Read back through `numbering/listRegistry`.
123
170
  */
124
- newLists: { default: null }
171
+ newLists: { default: null },
172
+ /**
173
+ * The section properties that close the body: the paper the document's last section is
174
+ * written on, its headers and its page numbering (§17.6.18). Held as the fragment the
175
+ * document wrote, so an untouched document goes back out as the bytes it arrived as.
176
+ * Read through `docx/sections`.
177
+ */
178
+ sectPr: { default: null },
179
+ /**
180
+ * What each side story - a comment's body, a footnote's - currently says, under the key
181
+ * naming it (`./stories`). Read and written through `docx/story`; it never reaches the
182
+ * DOM, so a copy out of the editor carries none of it.
183
+ */
184
+ stories: { default: NO_STORIES }
125
185
  }
126
186
  },
127
187
  paragraph: {
@@ -221,7 +281,13 @@ var docxSchema = new Schema({
221
281
  */
222
282
  styleConditions: { default: null },
223
283
  /** How many rows and columns one band of the table style is made of */
224
- styleBands: { default: null }
284
+ styleBands: { default: null },
285
+ /**
286
+ * The markers that stood between this table's rows: the ones ahead of the first row
287
+ * (`docx/importTable`). They are invisible and belong to no row, so they ride along on
288
+ * the table and go back where they stood
289
+ */
290
+ leadingXml: { default: null }
225
291
  },
226
292
  toDOM(node) {
227
293
  const format = toTableFormat(node.attrs.format);
@@ -246,7 +312,8 @@ var docxSchema = new Schema({
246
312
  "data-style-conditions": formatJson(
247
313
  toTableStyleConditions(node.attrs.styleConditions)
248
314
  ),
249
- "data-style-bands": formatJson(toBandSizes(node.attrs.styleBands))
315
+ "data-style-bands": formatJson(toBandSizes(node.attrs.styleBands)),
316
+ "data-leading": text(node.attrs.leadingXml)
250
317
  };
251
318
  const body = ["tbody", 0];
252
319
  if (gridCols.length === 0) return ["table", attrs, body];
@@ -269,7 +336,8 @@ var docxSchema = new Schema({
269
336
  "data-gridchange",
270
337
  ELEMENT("tblGridChange")
271
338
  );
272
- if (tblAttrs === false || tblPr === false || gridChange === false) {
339
+ const leadingXml = rawXml(dom, "data-leading", ANY_ELEMENTS);
340
+ if (tblAttrs === false || tblPr === false || gridChange === false || leadingXml === false) {
273
341
  return false;
274
342
  }
275
343
  return {
@@ -291,7 +359,8 @@ var docxSchema = new Schema({
291
359
  ),
292
360
  styleBands: toBandSizes(
293
361
  parseJson(dom.getAttribute("data-style-bands"))
294
- )
362
+ ),
363
+ leadingXml
295
364
  };
296
365
  }
297
366
  }
@@ -307,7 +376,11 @@ var docxSchema = new Schema({
307
376
  tblPrEx: { default: null },
308
377
  /** The whole `<w:trPr>...</w:trPr>` XML */
309
378
  trPr: { default: null },
310
- format: { default: null }
379
+ format: { default: null },
380
+ /** The markers that stood ahead of this row's first cell (`docx/importTable`) */
381
+ leadingXml: { default: null },
382
+ /** The markers that stood between this row and the next one, under the table */
383
+ trailingXml: { default: null }
311
384
  },
312
385
  toDOM(node) {
313
386
  const format = toRowFormat(node.attrs.format);
@@ -319,7 +392,9 @@ var docxSchema = new Schema({
319
392
  "data-trattrs": text(node.attrs.trAttrs),
320
393
  "data-tblprex": text(node.attrs.tblPrEx),
321
394
  "data-trpr": text(node.attrs.trPr),
322
- "data-fmt": formatJson(format)
395
+ "data-fmt": formatJson(format),
396
+ "data-leading": text(node.attrs.leadingXml),
397
+ "data-trailing": text(node.attrs.trailingXml)
323
398
  },
324
399
  0
325
400
  ];
@@ -331,14 +406,18 @@ var docxSchema = new Schema({
331
406
  const trAttrs = rawXml(dom, "data-trattrs", ATTRIBUTES);
332
407
  const tblPrEx = rawXml(dom, "data-tblprex", ELEMENT("tblPrEx"));
333
408
  const trPr = rawXml(dom, "data-trpr", ELEMENT("trPr"));
334
- if (trAttrs === false || tblPrEx === false || trPr === false) {
409
+ const leadingXml = rawXml(dom, "data-leading", ANY_ELEMENTS);
410
+ const trailingXml = rawXml(dom, "data-trailing", ANY_ELEMENTS);
411
+ if (trAttrs === false || tblPrEx === false || trPr === false || leadingXml === false || trailingXml === false) {
335
412
  return false;
336
413
  }
337
414
  return {
338
415
  trAttrs,
339
416
  tblPrEx,
340
417
  trPr,
341
- format: toRowFormat(parseJson(dom.getAttribute("data-fmt")))
418
+ format: toRowFormat(parseJson(dom.getAttribute("data-fmt"))),
419
+ leadingXml,
420
+ trailingXml
342
421
  };
343
422
  }
344
423
  }
@@ -372,7 +451,9 @@ var docxSchema = new Schema({
372
451
  * the lock lives inside that very XML.
373
452
  */
374
453
  sdtContentsLocked: { default: false },
375
- sdtDeletionLocked: { default: false }
454
+ sdtDeletionLocked: { default: false },
455
+ /** The markers that stood between this cell and the next one, under the row */
456
+ trailingXml: { default: null }
376
457
  },
377
458
  toDOM(node) {
378
459
  const format = toCellFormat(node.attrs.format);
@@ -394,7 +475,8 @@ var docxSchema = new Schema({
394
475
  "data-fmt": formatJson(format),
395
476
  "data-sdt-prefix": text(node.attrs.sdtPrefix),
396
477
  "data-sdt-contents-locked": locked ? "1" : void 0,
397
- "data-sdt-deletion-locked": node.attrs.sdtDeletionLocked === true ? "1" : void 0
478
+ "data-sdt-deletion-locked": node.attrs.sdtDeletionLocked === true ? "1" : void 0,
479
+ "data-trailing": text(node.attrs.trailingXml)
398
480
  },
399
481
  0
400
482
  ];
@@ -406,7 +488,8 @@ var docxSchema = new Schema({
406
488
  const tcAttrs = rawXml(dom, "data-tcattrs", ATTRIBUTES);
407
489
  const tcPr = rawXml(dom, "data-tcpr", ELEMENT("tcPr"));
408
490
  const sdtPrefix = rawXml(dom, "data-sdt-prefix", SDT_PREFIX);
409
- if (tcAttrs === false || tcPr === false || sdtPrefix === false) {
491
+ const trailingXml = rawXml(dom, "data-trailing", ANY_ELEMENTS);
492
+ if (tcAttrs === false || tcPr === false || sdtPrefix === false || trailingXml === false) {
410
493
  return false;
411
494
  }
412
495
  return {
@@ -421,109 +504,68 @@ var docxSchema = new Schema({
421
504
  format: toCellFormat(parseJson(dom.getAttribute("data-fmt"))),
422
505
  sdtPrefix,
423
506
  sdtContentsLocked: dom.getAttribute("data-sdt-contents-locked") === "1",
424
- sdtDeletionLocked: dom.getAttribute("data-sdt-deletion-locked") === "1"
507
+ sdtDeletionLocked: dom.getAttribute("data-sdt-deletion-locked") === "1",
508
+ trailingXml
425
509
  };
426
510
  }
427
511
  }
428
512
  ]
429
513
  },
430
514
  /**
431
- * The node that carries, as original XML, a block inside a table cell that we do
432
- * not model (a nested table, for instance).
433
- * Unlike a body block it has no original fragment number, so it carries its own
434
- * XML around with it.
515
+ * The one node a block the editor has no model for stands as, wherever it stood: under the
516
+ * body, or inside a table cell.
517
+ *
518
+ * Which of the two it came from is what it carries the original as. A body block is one of the
519
+ * fragments the session holds, so it names that fragment and the bytes go back out untouched;
520
+ * a block inside a cell was never a fragment of its own, so it carries its XML with it.
521
+ *
522
+ * `display` says whether anything of it is on screen. A range marker draws nothing - it stands
523
+ * between blocks and marks a spot rather than holding content - while a table nobody could take
524
+ * apart draws a box saying so.
435
525
  */
436
526
  rawBlock: {
437
527
  group: "block preserved",
438
528
  atom: true,
439
529
  selectable: false,
440
530
  attrs: {
531
+ /** The element, verbatim, for a block the session holds no fragment of */
441
532
  xml: { default: null },
533
+ /** The fragment of the session this block was opened from */
534
+ srcId: { default: null },
442
535
  /** The original element name (for example `w:tbl`) */
443
- name: { default: null }
536
+ name: { default: null },
537
+ display: { default: "chip" },
538
+ /** Whether the deletion guard answers for this block (`./preservedGuards`) */
539
+ guarded: { default: false }
444
540
  },
445
541
  toDOM(node) {
446
- return [
447
- "div",
448
- {
449
- class: `${editorClassNames.rawBlock} ${editorClassNames.rawXmlBlock}`,
450
- "data-xml": text(node.attrs.xml),
451
- "data-name": text(node.attrs.name)
452
- },
453
- UNKNOWN_PLACEHOLDER
454
- ];
542
+ const name = text(node.attrs.name);
543
+ const display = preservedDisplayOf(node.attrs.display);
544
+ const attributes = {
545
+ class: editorClassNames.rawBlock,
546
+ "data-xml": text(node.attrs.xml),
547
+ "data-src": text(node.attrs.srcId) ?? "",
548
+ "data-name": name ?? "",
549
+ "data-display": display,
550
+ "data-guarded": node.attrs.guarded === true ? "1" : void 0
551
+ };
552
+ if (display !== "chip") return ["div", attributes];
553
+ return ["div", attributes, placeholderLabel(name)];
455
554
  },
456
555
  parseDOM: [
457
556
  {
458
- tag: `div.${editorClassNames.rawXmlBlock}`,
557
+ tag: `div.${editorClassNames.rawBlock}[data-name]`,
459
558
  getAttrs: (dom) => {
460
559
  const xml = rawXml(dom, "data-xml", ANY_ELEMENT);
461
560
  if (xml === false) return false;
462
- return { xml, name: dom.getAttribute("data-name") };
463
- }
464
- }
465
- ]
466
- },
467
- /** The node that carries a non-paragraph body block (a table we could not model, sectPr, an unknown element) exactly as it came */
468
- docxRaw: {
469
- group: "block preserved",
470
- atom: true,
471
- selectable: false,
472
- attrs: {
473
- srcId: { default: null },
474
- /** The original element name (for example `w:tbl`) */
475
- name: { default: null }
476
- },
477
- toDOM(node) {
478
- const { className, label } = rawBlockDom(text(node.attrs.name));
479
- return [
480
- "div",
481
- {
482
- class: className,
483
- "data-src": text(node.attrs.srcId) ?? "",
484
- "data-name": text(node.attrs.name)
485
- },
486
- label
487
- ];
488
- },
489
- parseDOM: [
490
- {
491
- tag: DOCX_RAW_SELECTOR,
492
- getAttrs: (dom) => ({
493
- srcId: srcIdOf(dom),
494
- name: dom.getAttribute("data-name")
495
- })
496
- }
497
- ]
498
- },
499
- /** A bookmark range marker that occurs directly under w:body rather than inside a paragraph */
500
- bookmarkBlock: {
501
- group: "block preserved",
502
- atom: true,
503
- isolating: true,
504
- selectable: false,
505
- attrs: {
506
- srcId: { default: null },
507
- name: { default: null }
508
- },
509
- toDOM(node) {
510
- return [
511
- "div",
512
- {
513
- class: editorClassNames.bookmarkBlock,
514
- "data-src": text(node.attrs.srcId) ?? "",
515
- "data-name": text(node.attrs.name),
516
- hidden: "hidden"
561
+ return {
562
+ xml,
563
+ srcId: srcIdOf(dom),
564
+ name: preservedNameOf(dom),
565
+ display: preservedDisplayOf(dom.getAttribute("data-display")),
566
+ guarded: dom.getAttribute("data-guarded") === "1"
567
+ };
517
568
  }
518
- ];
519
- },
520
- parseDOM: [
521
- {
522
- tag: `div.${editorClassNames.bookmarkBlock}`,
523
- getAttrs: (dom) => ({
524
- srcId: srcIdOf(dom),
525
- name: dom.getAttribute("data-name")
526
- })
527
569
  }
528
570
  ]
529
571
  },
@@ -672,9 +714,6 @@ var docxSchema = new Schema({
672
714
  authorId: { default: null },
673
715
  initials: { default: null },
674
716
  date: { default: null },
675
- text: { default: "" },
676
- commentXml: { default: null },
677
- imported: { default: false },
678
717
  paraId: { default: null },
679
718
  resolved: { default: false },
680
719
  extensionXml: { default: null },
@@ -693,9 +732,6 @@ var docxSchema = new Schema({
693
732
  "data-comment-author-id": text(node.attrs.authorId),
694
733
  "data-comment-initials": text(node.attrs.initials),
695
734
  "data-comment-date": text(node.attrs.date),
696
- "data-comment-text": text(node.attrs.text),
697
- "data-comment-xml": text(node.attrs.commentXml),
698
- "data-comment-imported": node.attrs.imported === true ? "1" : void 0,
699
735
  "data-comment-para-id": text(node.attrs.paraId),
700
736
  "data-comment-resolved": node.attrs.resolved === true ? "1" : void 0,
701
737
  "data-comment-extension-xml": text(node.attrs.extensionXml),
@@ -713,18 +749,13 @@ var docxSchema = new Schema({
713
749
  "data-reference-xml",
714
750
  ELEMENT("commentReference")
715
751
  );
716
- const commentXml = rawXml(
717
- dom,
718
- "data-comment-xml",
719
- ELEMENT("comment")
720
- );
721
752
  const extensionXml = rawXml(
722
753
  dom,
723
754
  "data-comment-extension-xml",
724
755
  ANY_ELEMENT
725
756
  );
726
757
  const replies = parseJson(dom.getAttribute("data-comment-replies")) ?? [];
727
- if (referenceXml === false || commentXml === false || extensionXml === false || !repliesHoldTheirXml(replies)) {
758
+ if (referenceXml === false || extensionXml === false || !repliesHoldTheirXml(replies)) {
728
759
  return false;
729
760
  }
730
761
  return {
@@ -734,9 +765,6 @@ var docxSchema = new Schema({
734
765
  authorId: dom.getAttribute("data-comment-author-id"),
735
766
  initials: dom.getAttribute("data-comment-initials"),
736
767
  date: dom.getAttribute("data-comment-date"),
737
- text: dom.getAttribute("data-comment-text") ?? "",
738
- commentXml,
739
- imported: dom.getAttribute("data-comment-imported") === "1",
740
768
  paraId: dom.getAttribute("data-comment-para-id"),
741
769
  resolved: dom.getAttribute("data-comment-resolved") === "1",
742
770
  extensionXml,
@@ -757,14 +785,12 @@ var docxSchema = new Schema({
757
785
  kind: { default: "footnote" },
758
786
  id: { default: null },
759
787
  label: { default: "?" },
760
- text: { default: "" },
761
788
  customMarkFollows: { default: false },
762
789
  referenceXml: { default: null }
763
790
  },
764
791
  toDOM(node) {
765
792
  const kind = node.attrs.kind === "endnote" ? "Endnote" : "Footnote";
766
793
  const label = text(node.attrs.label) ?? "?";
767
- const body = text(node.attrs.text) ?? "";
768
794
  return [
769
795
  "sup",
770
796
  {
@@ -772,11 +798,9 @@ var docxSchema = new Schema({
772
798
  "data-note-kind": text(node.attrs.kind),
773
799
  "data-note-id": text(node.attrs.id),
774
800
  "data-note-label": label,
775
- "data-note-text": body,
776
801
  "data-custom-mark-follows": node.attrs.customMarkFollows === true ? "1" : void 0,
777
802
  "data-reference-xml": text(node.attrs.referenceXml),
778
- "aria-label": `${kind} ${label}`,
779
- title: body
803
+ "aria-label": `${kind} ${label}`
780
804
  },
781
805
  node.attrs.customMarkFollows === true ? "" : label
782
806
  ];
@@ -795,7 +819,6 @@ var docxSchema = new Schema({
795
819
  kind: dom.getAttribute("data-note-kind") === "endnote" ? "endnote" : "footnote",
796
820
  id: dom.getAttribute("data-note-id"),
797
821
  label: dom.getAttribute("data-note-label") ?? "?",
798
- text: dom.getAttribute("data-note-text") ?? "",
799
822
  customMarkFollows: dom.getAttribute("data-custom-mark-follows") === "1",
800
823
  referenceXml
801
824
  };
@@ -803,29 +826,58 @@ var docxSchema = new Schema({
803
826
  }
804
827
  ]
805
828
  },
829
+ /**
830
+ * One child of a run the editor has no node for, kept as the XML it came as.
831
+ *
832
+ * It wears the run mark, so it is written back inside the same `<w:r>` as the text beside it,
833
+ * which is where `EG_RunInnerContent` says it may stand.
834
+ */
835
+ rawRunContent: {
836
+ group: "inline",
837
+ inline: true,
838
+ atom: true,
839
+ marks: "run sdt link",
840
+ attrs: {
841
+ xml: { default: null },
842
+ /** The local name of the element, for example `fldChar` */
843
+ element: { default: null },
844
+ display: { default: "hidden" },
845
+ /** What a `text` display draws, and the preview a chip shows */
846
+ text: { default: null },
847
+ /** Whether the deletion guard answers for this fragment (`schema/preservedGuards`) */
848
+ guarded: { default: false }
849
+ },
850
+ toDOM(node) {
851
+ return preservedDom(node, editorClassNames.rawRunContent);
852
+ },
853
+ parseDOM: [
854
+ {
855
+ tag: `span.${editorClassNames.rawRunContent}`,
856
+ getAttrs: preservedAttrs
857
+ }
858
+ ]
859
+ },
806
860
  /** The node that carries a non-run element inside a paragraph (a bookmark, for instance) exactly as it came */
807
861
  rawInline: {
808
862
  group: "inline",
809
863
  inline: true,
810
864
  atom: true,
811
865
  marks: "sdt link",
812
- attrs: { xml: { default: null } },
866
+ attrs: {
867
+ xml: { default: null },
868
+ /** The local name of the element; `r` for a run with no content, kept whole */
869
+ element: { default: null },
870
+ display: { default: "hidden" },
871
+ text: { default: null },
872
+ guarded: { default: false }
873
+ },
813
874
  toDOM(node) {
814
- return [
815
- "span",
816
- {
817
- class: editorClassNames.rawInline,
818
- "data-xml": text(node.attrs.xml)
819
- }
820
- ];
875
+ return preservedDom(node, editorClassNames.rawInline);
821
876
  },
822
877
  parseDOM: [
823
878
  {
824
879
  tag: `span.${editorClassNames.rawInline}`,
825
- getAttrs: (dom) => {
826
- const xml = rawXml(dom, "data-xml", ANY_ELEMENT);
827
- return xml === false ? false : { xml };
828
- }
880
+ getAttrs: preservedAttrs
829
881
  }
830
882
  ]
831
883
  },
@@ -46,7 +46,7 @@ export type EditIntent =
46
46
  * against the places it puts that guard to the test, and a place naming a guard that no longer
47
47
  * stands has to be a mistake the compiler catches rather than an annotation answering for nothing.
48
48
  */
49
- export type EditGuardName = "protection" | "lock" | "bookmark" | "note" | "section";
49
+ export type EditGuardName = "protection" | "lock" | "preserved" | "note" | "section";
50
50
  /** What every guard answers, whichever of the two judgements it is written as */
51
51
  interface GuardCommon {
52
52
  readonly name: EditGuardName;
@@ -4,7 +4,7 @@ import {
4
4
  transactionReaches
5
5
  } from "./editGuard.js";
6
6
  import { lockGuard } from "./locks.js";
7
- import { bookmarkGuard, noteGuard, sectionGuard } from "./preservedGuards.js";
7
+ import { noteGuard, preservedGuard, sectionGuard } from "./preservedGuards.js";
8
8
  import {
9
9
  isCommentNode,
10
10
  protectionAllows
@@ -36,7 +36,7 @@ var protectionGuard = {
36
36
  var EDIT_GUARDS = [
37
37
  protectionGuard,
38
38
  lockGuard,
39
- bookmarkGuard,
39
+ preservedGuard,
40
40
  noteGuard,
41
41
  sectionGuard
42
42
  ];
@@ -1,4 +1,5 @@
1
1
  export type { AttrClass } from "./attrRoles";
2
2
  export { attrsOfClass } from "./attrRoles";
3
+ export type { PreservedDisplay } from "./docxSchema";
3
4
  export { docxSchema, isPageBreak } from "./docxSchema";
4
5
  export { imageNodeSpec, runMarkSpec } from "./rendering";
@@ -0,0 +1,12 @@
1
+ import type { Node as PMNode } from "prosemirror-model";
2
+ type MarkerAttr = "leadingXml" | "trailingXml";
3
+ /**
4
+ * Whether this node is content the editor keeps as it came rather than one it models.
5
+ *
6
+ * The block placeholder declares it as a group; the two inline ones cannot, since their group says
7
+ * where they may stand, so they are named here and asked for by name everywhere else.
8
+ */
9
+ export declare function isPreservedNode(node: PMNode): boolean;
10
+ /** Visit preserved XML in the order the writer places it around editable content. */
11
+ export declare function visitPreservedFragments(doc: PMNode, visit: (node: PMNode, pos: number, xml: string | null, attr?: MarkerAttr) => void): void;
12
+ export {};
@@ -0,0 +1,38 @@
1
+ // src/schema/preservedFragments.ts
2
+ function isPreservedNode(node) {
3
+ return node.type.name === "rawInline" || node.type.name === "rawRunContent" || node.type.isInGroup("preserved");
4
+ }
5
+ function visitPreservedFragments(doc, visit) {
6
+ const visitAttr = (node, pos, attr) => {
7
+ const xml = node.attrs[attr];
8
+ if (typeof xml === "string" && xml.length > 0) {
9
+ visit(node, pos, xml, attr);
10
+ }
11
+ };
12
+ const walk = (node, pos) => {
13
+ if (isPreservedNode(node)) {
14
+ visit(
15
+ node,
16
+ pos,
17
+ typeof node.attrs.xml === "string" ? node.attrs.xml : null
18
+ );
19
+ }
20
+ if (node.type.name === "table" || node.type.name === "tableRow") {
21
+ visitAttr(node, pos, "leadingXml");
22
+ }
23
+ node.forEach((child, offset) => {
24
+ walk(child, pos + 1 + offset);
25
+ });
26
+ if (node.type.name === "tableRow" || node.type.name === "tableCell") {
27
+ visitAttr(node, pos, "trailingXml");
28
+ }
29
+ };
30
+ doc.descendants((node, pos) => {
31
+ walk(node, pos);
32
+ return false;
33
+ });
34
+ }
35
+ export {
36
+ isPreservedNode,
37
+ visitPreservedFragments
38
+ };
@@ -1,11 +1,13 @@
1
1
  /**
2
- * The guards over what a document is opened with and the editor only preserves: the two ends of a
3
- * bookmark range, the reference standing where a footnote or an endnote is called, and the
4
- * paragraph that ends a section.
2
+ * The guards over what a document is opened with and the editor only preserves: the fragments a
3
+ * file falls apart without, the reference standing where a footnote or an endnote is called, and
4
+ * the paragraph that ends a section.
5
5
  *
6
- * None of it is content the editor writes, so nothing in it can put such a thing back once it is
7
- * gone, and a document that lost one of a bookmark's two ends cannot be written back as a file at
8
- * all (`docx/exportDocx` refuses it). What the first two guards hold is therefore the whole list of
6
+ * The first of those is the two ends of a bookmark range, the two ends of a permission or move
7
+ * range, and the pieces of a field, which say what they say only in the order they stand in. None
8
+ * of it is content the editor writes, so nothing in it can put such a thing back once it is gone,
9
+ * and a document that lost one of a bookmark's two ends cannot be written back as a file at all
10
+ * (`docx/exportDocx` refuses it). What the first two guards hold is therefore the whole list of
9
11
  * the nodes they answer for, in the order the document carries them; a section break, which an
10
12
  * edit may legitimately move from one paragraph to another, is held by its number instead.
11
13
  */
@@ -27,7 +29,7 @@ type Signature = (node: PMNode) => string;
27
29
  * was.
28
30
  */
29
31
  export declare function preservedNodeGuard(name: EditGuardName, holds: (node: PMNode) => boolean, signature: Signature): ChangeGuard;
30
- export declare const bookmarkGuard: ChangeGuard;
32
+ export declare const preservedGuard: ChangeGuard;
31
33
  export declare const noteGuard: ChangeGuard;
32
34
  /**
33
35
  * A guard that lets a change through only while it leaves at least as many section breaks standing