@portone/docx-editor 0.2.1 → 0.3.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 (225) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/CONTRIBUTING.md +5 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +12 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +12 -7
  10. package/dist/docx/commentOnlyChange.js +9 -156
  11. package/dist/docx/comments/constants.d.ts +0 -1
  12. package/dist/docx/comments/constants.js +0 -2
  13. package/dist/docx/comments/grammar.d.ts +10 -4
  14. package/dist/docx/comments/grammar.js +12 -2
  15. package/dist/docx/comments/{verifying.d.ts → parts.d.ts} +26 -18
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +16 -92
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.js +8 -10
  22. package/dist/docx/comments/writing.d.ts +19 -7
  23. package/dist/docx/comments/writing.js +52 -111
  24. package/dist/docx/documentSettings.d.ts +7 -0
  25. package/dist/docx/documentSettings.js +10 -2
  26. package/dist/docx/exportDocx.d.ts +40 -5
  27. package/dist/docx/exportDocx.js +69 -71
  28. package/dist/docx/exportRefs.d.ts +5 -2
  29. package/dist/docx/exportRefs.js +3 -1
  30. package/dist/docx/fidelity.d.ts +43 -0
  31. package/dist/docx/fidelity.js +64 -0
  32. package/dist/docx/formatting/attrs.d.ts +27 -0
  33. package/dist/docx/formatting/attrs.js +31 -0
  34. package/dist/docx/formatting/context.d.ts +31 -0
  35. package/dist/docx/formatting/context.js +42 -0
  36. package/dist/docx/formatting/direct.d.ts +6 -5
  37. package/dist/docx/formatting/direct.js +52 -78
  38. package/dist/docx/formatting/resolve.d.ts +45 -0
  39. package/dist/docx/formatting/resolve.js +140 -0
  40. package/dist/docx/formatting/runProperties.d.ts +93 -0
  41. package/dist/docx/formatting/runProperties.js +316 -0
  42. package/dist/docx/formatting/styles.js +3 -3
  43. package/dist/docx/formatting/tabStops.js +7 -22
  44. package/dist/docx/formatting.d.ts +4 -1
  45. package/dist/docx/formatting.js +4 -1
  46. package/dist/docx/headersFooters.js +6 -13
  47. package/dist/docx/identities.d.ts +67 -0
  48. package/dist/docx/identities.js +174 -0
  49. package/dist/docx/importDocx.d.ts +12 -1
  50. package/dist/docx/importDocx.js +72 -79
  51. package/dist/docx/importParagraph.d.ts +1 -1
  52. package/dist/docx/importParagraph.js +4 -3
  53. package/dist/docx/importTable.d.ts +1 -1
  54. package/dist/docx/importTable.js +17 -1
  55. package/dist/docx/invariants.d.ts +33 -0
  56. package/dist/docx/invariants.js +256 -0
  57. package/dist/docx/media.d.ts +6 -4
  58. package/dist/docx/media.js +6 -37
  59. package/dist/docx/newLists.d.ts +20 -0
  60. package/dist/docx/newLists.js +36 -0
  61. package/dist/docx/notes.js +3 -7
  62. package/dist/docx/numberingPlanner.d.ts +8 -0
  63. package/dist/docx/numberingPlanner.js +19 -0
  64. package/dist/docx/packageParts.d.ts +42 -0
  65. package/dist/docx/packageParts.js +135 -0
  66. package/dist/docx/pageGeometry.d.ts +2 -0
  67. package/dist/docx/pageGeometry.js +18 -9
  68. package/dist/docx/paraProps.d.ts +10 -22
  69. package/dist/docx/paraProps.js +47 -76
  70. package/dist/docx/partPlan.d.ts +36 -0
  71. package/dist/docx/partPlan.js +59 -0
  72. package/dist/docx/protectionPolicy.d.ts +127 -0
  73. package/dist/docx/protectionPolicy.js +169 -0
  74. package/dist/docx/relationships.d.ts +1 -1
  75. package/dist/docx/relationships.js +8 -13
  76. package/dist/docx/runProps.d.ts +9 -22
  77. package/dist/docx/runProps.js +15 -168
  78. package/dist/docx/scan.js +20 -52
  79. package/dist/docx/sdt.js +9 -34
  80. package/dist/docx/sdtProps.d.ts +8 -1
  81. package/dist/docx/sdtProps.js +10 -0
  82. package/dist/docx/serializeBlock.d.ts +2 -0
  83. package/dist/docx/serializeBlock.js +8 -5
  84. package/dist/docx/serializeTable.js +32 -15
  85. package/dist/docx/session.d.ts +42 -14
  86. package/dist/docx/session.js +43 -17
  87. package/dist/docx/tableFormatting/editing.js +108 -139
  88. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  89. package/dist/docx/tableFormatting/reading.js +40 -20
  90. package/dist/docx/tableTemplate.js +18 -7
  91. package/dist/download.d.ts +8 -5
  92. package/dist/download.js +2 -0
  93. package/dist/editor/clipboard/inlineFormatting.js +19 -30
  94. package/dist/editor/commands/comments/editing.d.ts +6 -2
  95. package/dist/editor/commands/comments/editing.js +19 -31
  96. package/dist/editor/commands/exportQueries.d.ts +15 -0
  97. package/dist/editor/commands/exportQueries.js +14 -0
  98. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  99. package/dist/editor/commands/fidelityQueries.js +8 -0
  100. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  101. package/dist/editor/commands/formatting/editing.js +26 -108
  102. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  103. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  104. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  105. package/dist/editor/commands/formatting/shared.js +5 -2
  106. package/dist/editor/commands/indentCommands.js +5 -4
  107. package/dist/editor/commands/index.d.ts +11 -0
  108. package/dist/editor/commands/index.js +5 -0
  109. package/dist/editor/commands/linkCommands.js +5 -6
  110. package/dist/editor/commands/listCommands.js +8 -10
  111. package/dist/editor/commands/lockCommands.d.ts +7 -1
  112. package/dist/editor/commands/lockCommands.js +35 -46
  113. package/dist/editor/commands/paragraphCommands.js +28 -31
  114. package/dist/editor/commands/spacingCommands.js +1 -1
  115. package/dist/editor/createEditor.d.ts +17 -29
  116. package/dist/editor/createEditor.js +58 -58
  117. package/dist/editor/documentStyles.d.ts +11 -35
  118. package/dist/editor/documentStyles.js +9 -52
  119. package/dist/editor/editorDocument.d.ts +56 -0
  120. package/dist/editor/editorDocument.js +74 -0
  121. package/dist/editor/externalClipboard.js +16 -45
  122. package/dist/editor/insertTable.js +4 -3
  123. package/dist/editor/paragraphEdits.d.ts +11 -18
  124. package/dist/editor/paragraphEdits.js +7 -16
  125. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  126. package/dist/editor/plugins/displayDerivation.js +78 -0
  127. package/dist/editor/plugins/imagePaste.js +4 -3
  128. package/dist/editor/plugins/keymap.js +18 -3
  129. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  130. package/dist/editor/plugins/numberingDecorations.js +7 -17
  131. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  132. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  133. package/dist/editor/plugins/tabLayout.js +1 -1
  134. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  135. package/dist/editor/plugins/tableDisplay.js +16 -0
  136. package/dist/index.d.ts +2 -0
  137. package/dist/model/format.d.ts +30 -8
  138. package/dist/model/format.js +35 -22
  139. package/dist/model/tabStops.d.ts +9 -0
  140. package/dist/model/tabStops.js +18 -0
  141. package/dist/numbering/listTemplate.js +24 -7
  142. package/dist/numbering/parseNumbering.d.ts +14 -1
  143. package/dist/numbering/parseNumbering.js +35 -22
  144. package/dist/numbering/writeNumbering.d.ts +3 -4
  145. package/dist/numbering/writeNumbering.js +14 -25
  146. package/dist/ooxml/childOrder.d.ts +34 -0
  147. package/dist/ooxml/childOrder.js +496 -0
  148. package/dist/ooxml/element.d.ts +24 -11
  149. package/dist/ooxml/element.js +31 -12
  150. package/dist/ooxml/errors.d.ts +3 -2
  151. package/dist/ooxml/image.d.ts +4 -0
  152. package/dist/ooxml/image.js +2 -5
  153. package/dist/ooxml/partSplice.d.ts +67 -0
  154. package/dist/ooxml/partSplice.js +169 -0
  155. package/dist/ooxml/props.d.ts +112 -0
  156. package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
  157. package/dist/ooxml/simpleTypes.d.ts +103 -0
  158. package/dist/ooxml/simpleTypes.js +182 -0
  159. package/dist/ooxml/tabStops.js +8 -27
  160. package/dist/ooxml/tagScan.d.ts +34 -0
  161. package/dist/ooxml/tagScan.js +108 -0
  162. package/dist/ooxml/units.d.ts +25 -13
  163. package/dist/ooxml/units.js +65 -31
  164. package/dist/ooxml/xml.d.ts +27 -6
  165. package/dist/ooxml/xml.js +37 -5
  166. package/dist/page/blockKinds.d.ts +61 -0
  167. package/dist/page/blockKinds.js +11 -0
  168. package/dist/page/kinds/index.d.ts +6 -0
  169. package/dist/page/kinds/index.js +10 -0
  170. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  171. package/dist/page/kinds/paragraphKind.js +71 -0
  172. package/dist/page/kinds/tableKind.d.ts +10 -0
  173. package/dist/page/kinds/tableKind.js +178 -0
  174. package/dist/page/measureBlocks.d.ts +3 -14
  175. package/dist/page/measureBlocks.js +24 -45
  176. package/dist/page/pageDecorations.d.ts +27 -44
  177. package/dist/page/pageDecorations.js +70 -152
  178. package/dist/page/pageLayout.d.ts +2 -19
  179. package/dist/page/pageLayout.js +36 -6
  180. package/dist/page/usePageLayout.d.ts +2 -17
  181. package/dist/page/usePageLayout.js +3 -39
  182. package/dist/schema/attrRoles.d.ts +25 -18
  183. package/dist/schema/attrRoles.js +102 -67
  184. package/dist/schema/displayDerivation.d.ts +82 -0
  185. package/dist/schema/displayDerivation.js +130 -0
  186. package/dist/schema/docxSchema.d.ts +3 -0
  187. package/dist/schema/docxSchema.js +26 -14
  188. package/dist/schema/editGuard.d.ts +1 -1
  189. package/dist/schema/guards.d.ts +37 -14
  190. package/dist/schema/guards.js +29 -3
  191. package/dist/schema/index.d.ts +2 -0
  192. package/dist/schema/index.js +2 -0
  193. package/dist/schema/locks.d.ts +11 -44
  194. package/dist/schema/locks.js +0 -9
  195. package/dist/schema/preservedGuards.d.ts +26 -6
  196. package/dist/schema/preservedGuards.js +31 -1
  197. package/dist/schema/protection.d.ts +7 -1
  198. package/dist/schema/protection.js +2 -1
  199. package/dist/schema/sourceEquality.d.ts +1 -9
  200. package/dist/schema/sourceEquality.js +1 -27
  201. package/dist/styles/inlineStyle.js +15 -6
  202. package/dist/table/cellFormatting.d.ts +8 -1
  203. package/dist/table/cellFormatting.js +10 -18
  204. package/dist/table/commands.js +20 -18
  205. package/dist/table/format.d.ts +3 -0
  206. package/dist/table/format.js +3 -9
  207. package/dist/table/gridBorders.d.ts +14 -16
  208. package/dist/table/gridBorders.js +3 -41
  209. package/dist/table/merge.d.ts +2 -6
  210. package/dist/table/merge.js +5 -5
  211. package/package.json +7 -3
  212. package/dist/docx/comments/contentTypes.d.ts +0 -7
  213. package/dist/docx/comments/contentTypes.js +0 -38
  214. package/dist/docx/comments/verifying.js +0 -206
  215. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  216. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  217. package/dist/docx/propsXml.d.ts +0 -67
  218. package/dist/docx/uniqueControls.d.ts +0 -14
  219. package/dist/docx/uniqueControls.js +0 -62
  220. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  221. package/dist/editor/plugins/commentReservations.js +0 -26
  222. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  223. package/dist/editor/plugins/styledParagraphs.js +0 -65
  224. package/dist/page/tableMeasurements.d.ts +0 -18
  225. package/dist/page/tableMeasurements.js +0 -119
@@ -2,105 +2,140 @@
2
2
  import { NodeType } from "prosemirror-model";
3
3
  var NODE_ATTR_ROLES = {
4
4
  paragraph: {
5
- srcId: "session",
6
- pAttrs: "source",
7
- pPr: "source",
8
- format: "display",
9
- styleRun: "display"
5
+ srcId: { role: "session", class: "identity" },
6
+ pAttrs: { role: "source", class: "preserved" },
7
+ pPr: { role: "source", class: "preserved" },
8
+ format: { role: "display", class: "derived" },
9
+ styleRun: { role: "display", class: "derived" }
10
10
  },
11
11
  table: {
12
- srcId: "session",
13
- tblAttrs: "source",
14
- tblPr: "source",
15
- tblW: "source",
16
- gridCols: "source",
17
- format: "display",
18
- styleInside: "display",
19
- styleCellMargins: "display"
12
+ srcId: { role: "session", class: "identity" },
13
+ tblAttrs: { role: "source", class: "preserved" },
14
+ tblPr: { role: "source", class: "preserved" },
15
+ tblW: { role: "source", class: "model" },
16
+ gridCols: { role: "source", class: "model" },
17
+ // The grid the table had before it was last revised, carried as it arrived
18
+ gridChange: { role: "source", class: "preserved" },
19
+ format: { role: "display", class: "derived" },
20
+ styleInside: { role: "display", class: "derived" },
21
+ styleCellMargins: { role: "display", class: "derived" }
20
22
  },
21
23
  tableRow: {
22
- trAttrs: "source",
23
- tblPrEx: "source",
24
- trPr: "source",
25
- format: "display"
24
+ trAttrs: { role: "source", class: "preserved" },
25
+ tblPrEx: { role: "source", class: "preserved" },
26
+ trPr: { role: "source", class: "preserved" },
27
+ format: { role: "display", class: "derived" }
26
28
  },
27
29
  tableCell: {
28
- colspan: "source",
29
- rowspan: "source",
30
+ colspan: { role: "source", class: "model" },
31
+ rowspan: { role: "source", class: "model" },
30
32
  // prosemirror-tables' own attr. Import writes null, the writer never reads it, and the table
31
33
  // commands only carry it from one cell to another; a real column resize moves `gridCols`
32
- colwidth: "display",
33
- tcAttrs: "source",
34
- tcPr: "source",
35
- tcW: "source",
36
- format: "display",
34
+ colwidth: { role: "display", class: "derived" },
35
+ tcAttrs: { role: "source", class: "preserved" },
36
+ tcPr: { role: "source", class: "preserved" },
37
+ tcW: { role: "source", class: "model" },
38
+ format: { role: "display", class: "derived" },
37
39
  // Read from `sdtPrefix` rather than from the file, but a lock is not a display value: leaving
38
40
  // it out of the comparison would let a step that unlocks a cell pass as a re-derivation
39
- sdtPrefix: "source",
40
- sdtContentsLocked: "source",
41
- sdtDeletionLocked: "source"
41
+ sdtPrefix: { role: "source", class: "preserved" },
42
+ sdtContentsLocked: { role: "source", class: "derived" },
43
+ sdtDeletionLocked: { role: "source", class: "derived" }
44
+ },
45
+ rawBlock: {
46
+ xml: { role: "source", class: "preserved" },
47
+ name: { role: "source", class: "identity" }
48
+ },
49
+ docxRaw: {
50
+ srcId: { role: "session", class: "identity" },
51
+ name: { role: "session", class: "identity" }
52
+ },
53
+ bookmarkBlock: {
54
+ srcId: { role: "session", class: "identity" },
55
+ name: { role: "session", class: "identity" }
56
+ },
57
+ hardBreak: { brAttrs: { role: "source", class: "preserved" } },
58
+ image: {
59
+ // The bytes themselves, as a data URL the editor makes when an image is inserted
60
+ src: { role: "source", class: "model" },
61
+ extent: { role: "source", class: "model" },
62
+ alt: { role: "source", class: "model" },
63
+ xml: { role: "source", class: "preserved" }
64
+ },
65
+ commentStart: {
66
+ id: { role: "source", class: "identity" },
67
+ xml: { role: "source", class: "preserved" }
68
+ },
69
+ commentEnd: {
70
+ id: { role: "source", class: "identity" },
71
+ xml: { role: "source", class: "preserved" }
42
72
  },
43
- rawBlock: { xml: "source", name: "source" },
44
- docxRaw: { srcId: "session", name: "session" },
45
- bookmarkBlock: { srcId: "session", name: "session" },
46
- hardBreak: { brAttrs: "source" },
47
- image: { src: "source", extent: "source", alt: "source", xml: "source" },
48
- commentStart: { id: "source", xml: "source" },
49
- commentEnd: { id: "source", xml: "source" },
50
73
  commentReference: {
51
- id: "source",
52
- referenceXml: "source",
53
- author: "source",
54
- authorId: "source",
55
- initials: "source",
56
- date: "source",
57
- text: "source",
58
- commentXml: "source",
59
- paraId: "source",
60
- resolved: "source",
61
- extensionXml: "source",
62
- replies: "source",
74
+ id: { role: "source", class: "identity" },
75
+ referenceXml: { role: "source", class: "preserved" },
76
+ author: { role: "source", class: "model" },
77
+ authorId: { role: "source", class: "model" },
78
+ initials: { role: "source", class: "model" },
79
+ date: { role: "source", class: "model" },
80
+ text: { role: "source", class: "model" },
81
+ commentXml: { role: "source", class: "preserved" },
82
+ paraId: { role: "source", class: "identity" },
83
+ resolved: { role: "source", class: "model" },
84
+ extensionXml: { role: "source", class: "preserved" },
85
+ replies: { role: "source", class: "model" },
63
86
  // Read to decide whether the comment parts are rewritten at all; neither is written
64
- imported: "session",
65
- threadImported: "session"
87
+ imported: { role: "session", class: "derived" },
88
+ threadImported: { role: "session", class: "derived" }
66
89
  },
67
90
  noteReference: {
68
- kind: "source",
69
- id: "source",
70
- customMarkFollows: "source",
71
- referenceXml: "source",
91
+ kind: { role: "source", class: "model" },
92
+ id: { role: "source", class: "identity" },
93
+ customMarkFollows: { role: "source", class: "model" },
94
+ referenceXml: { role: "source", class: "preserved" },
72
95
  // Both come from the notes part as the document was opened, and the writer puts back the
73
96
  // reference alone: a note renumbered around an edit is the same reference it was
74
- label: "display",
75
- text: "display"
97
+ label: { role: "display", class: "derived" },
98
+ text: { role: "display", class: "derived" }
76
99
  },
77
- rawInline: { xml: "source" }
100
+ rawInline: { xml: { role: "source", class: "preserved" } }
78
101
  };
79
102
  var MARK_ATTR_ROLES = {
80
- run: { rPr: "source", rAttrs: "source", format: "display" },
103
+ run: {
104
+ rPr: { role: "source", class: "preserved" },
105
+ rAttrs: { role: "source", class: "preserved" },
106
+ format: { role: "display", class: "derived" }
107
+ },
81
108
  sdt: {
82
- sdtPrefix: "source",
109
+ sdtPrefix: { role: "source", class: "preserved" },
83
110
  // Counted through the document as it was opened, to tell one control from the next
84
- sdtKey: "session",
85
- contentsLocked: "source",
86
- deletionLocked: "source"
111
+ sdtKey: { role: "session", class: "identity" },
112
+ contentsLocked: { role: "source", class: "derived" },
113
+ deletionLocked: { role: "source", class: "derived" }
87
114
  },
88
- link: { linkPrefix: "source", href: "source", linkKey: "session" },
89
- tab: { tabAttrs: "source" }
115
+ link: {
116
+ linkPrefix: { role: "source", class: "preserved" },
117
+ href: { role: "source", class: "model" },
118
+ linkKey: { role: "session", class: "identity" }
119
+ },
120
+ tab: { tabAttrs: { role: "source", class: "preserved" } }
90
121
  };
91
- function rolesOf(type) {
92
- const roles = type instanceof NodeType ? NODE_ATTR_ROLES[type.name] : MARK_ATTR_ROLES[type.name];
93
- return roles ?? {};
122
+ function factsOf(type) {
123
+ const facts = type instanceof NodeType ? NODE_ATTR_ROLES[type.name] : MARK_ATTR_ROLES[type.name];
124
+ return facts ?? {};
94
125
  }
95
126
  function attrRole(type, name) {
96
- return rolesOf(type)[name] ?? "source";
127
+ return factsOf(type)[name]?.role ?? "source";
97
128
  }
98
129
  function displayAttrsOf(type) {
99
- return Object.entries(rolesOf(type)).filter(([, role]) => role === "display").map(([name]) => name);
130
+ return Object.entries(factsOf(type)).filter(([, facts]) => facts.role === "display").map(([name]) => name);
131
+ }
132
+ function attrsOfClass(type, cls) {
133
+ return Object.entries(factsOf(type)).filter(([, facts]) => facts.class === cls).map(([name]) => name);
100
134
  }
101
135
  export {
102
136
  MARK_ATTR_ROLES,
103
137
  NODE_ATTR_ROLES,
104
138
  attrRole,
139
+ attrsOfClass,
105
140
  displayAttrsOf
106
141
  };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * How a display value gets onto a node: who says what it should be, and why the transaction that
3
+ * writes it is no edit.
4
+ *
5
+ * What a node draws with is worked out from its source and the formatting around it
6
+ * (`./attrRoles`), and has to be worked out again whenever either moves: the lines of a table's
7
+ * cells once a row is added, the style values of a paragraph once its own properties change, and
8
+ * every value at once when the formatting the document is resolved against is replaced. A deriver
9
+ * says, for the node types it answers for, what the display attrs of one such node should be, and
10
+ * `deriveDisplay` walks the blocks of a document, asks each owner in turn, and writes one step per
11
+ * node whose attrs would change. Only the display attrs of what a deriver hands back are read, so
12
+ * a deriver cannot write a source attr, and the walk is by construction the kind of transaction
13
+ * `changesOnlyDisplayAttrs` describes.
14
+ *
15
+ * The interface stands here, below `docx` and `editor`, so that a module of either can write a
16
+ * deriver against it. What a deriver resolves against is the caller's own `Context`, which this
17
+ * module never reads; `editor/plugins/displayDerivation` registers the derivers and runs the walk
18
+ * after every edit.
19
+ *
20
+ * A transaction carrying the `displayOnly` pass goes through the guard list whole (`./guards`),
21
+ * provided every step of it is judged, off the role table alone, to change display attrs and
22
+ * nothing else. The pass is a claim rather than a key: a step that rewrites a source attr, a lock
23
+ * flag among them, or that puts content anywhere, fails the claim, and the transaction is then
24
+ * judged as any other edit.
25
+ */
26
+ import { type Attrs, type MarkType, type Node as PMNode, type Schema } from "prosemirror-model";
27
+ import { PluginKey } from "prosemirror-state";
28
+ import { type Step, type Transform } from "prosemirror-transform";
29
+ import type { docxSchema } from "./docxSchema";
30
+ /** The name of a node type of the document schema, which is what a deriver answers for by */
31
+ export type NodeName = typeof docxSchema extends Schema<infer Nodes, string> ? Nodes : never;
32
+ /** The display attrs one node should carry, by the position the node stands at in the document handed to the deriver */
33
+ export interface DisplayAttrs {
34
+ readonly pos: number;
35
+ readonly attrs: Attrs;
36
+ /** Omitted for node attrs. Otherwise updates an existing mark on this inline node. */
37
+ readonly mark?: {
38
+ readonly type: MarkType;
39
+ readonly to: number;
40
+ };
41
+ }
42
+ /**
43
+ * What says, for the node types it answers for, what one node's display attrs should be.
44
+ *
45
+ * `derive` is asked once per such node, in document order, with the node as the derivers before it
46
+ * left it. It hands back the display attrs of that node and of any node inside it, each by
47
+ * position - a table's deriver answers for the cells - or nothing where nothing changes; a source
48
+ * or session attr among what it hands back is not written. `previous` is the node this one maps
49
+ * back to in the document before the change, or null when there is none, or when every value is
50
+ * being worked out again from nothing.
51
+ */
52
+ export interface DisplayDeriver<Context> {
53
+ readonly name: string;
54
+ /**
55
+ * The block node types this deriver answers for. The walk runs over the blocks and never into
56
+ * the inline content of a textblock, since it runs after every keystroke, so an inline type
57
+ * cannot be answered for.
58
+ */
59
+ readonly nodeTypes: readonly NodeName[];
60
+ derive(node: PMNode, pos: number, doc: PMNode, context: Context, previous: PMNode | null): readonly DisplayAttrs[];
61
+ }
62
+ /**
63
+ * Writes the display attrs the derivers hand back, one `setNodeMarkup` per node whose attrs would
64
+ * change, onto the transform. It holds no state of its own: what a node maps back to comes from
65
+ * the caller, who knows what changed.
66
+ *
67
+ * Every step keeps the document the same size, so the positions gathered before the first write
68
+ * still hold, and a deriver asked after another reads the node as that one left it.
69
+ */
70
+ export declare function deriveDisplay<Context>(transform: Transform, context: Context, derivers: readonly DisplayDeriver<Context>[], previousOf: (node: PMNode, pos: number) => PMNode | null): void;
71
+ /**
72
+ * The pass a re-derivation carries. A plugin key is used as the name so that it cannot collide
73
+ * with a consumer's own metadata.
74
+ */
75
+ export declare const displayOnly: PluginKey<boolean>;
76
+ /**
77
+ * Whether the step changes display attrs and nothing else, judged off the role table alone.
78
+ *
79
+ * Node attrs and existing mark attrs may change only their display fields. Adding a mark where
80
+ * that type was absent, changing source attrs, and all other step shapes remain edits.
81
+ */
82
+ export declare function changesOnlyDisplayAttrs(step: Step, doc: PMNode): boolean;
@@ -0,0 +1,130 @@
1
+ // src/schema/displayDerivation.ts
2
+ import {
3
+ Mark
4
+ } from "prosemirror-model";
5
+ import { PluginKey } from "prosemirror-state";
6
+ import {
7
+ AddMarkStep,
8
+ AttrStep,
9
+ ReplaceAroundStep
10
+ } from "prosemirror-transform";
11
+ import { attrRole } from "./attrRoles.js";
12
+ function ownersByType(schema, derivers) {
13
+ const owners = /* @__PURE__ */ new Map();
14
+ for (const deriver of derivers) {
15
+ for (const name of deriver.nodeTypes) {
16
+ const type = schema.nodes[name];
17
+ if (type === void 0 || type.isInline) {
18
+ throw new Error(
19
+ `${deriver.name} answers for ${name}, which the walk over the blocks never reaches`
20
+ );
21
+ }
22
+ const owning = owners.get(name) ?? [];
23
+ owning.push(deriver);
24
+ owners.set(name, owning);
25
+ }
26
+ }
27
+ return owners;
28
+ }
29
+ function displayLaidOver(standing, derived) {
30
+ const attrs = { ...standing.attrs };
31
+ for (const [name, value] of Object.entries(derived)) {
32
+ if (attrRole(standing.type, name) === "display") attrs[name] = value;
33
+ }
34
+ return attrs;
35
+ }
36
+ function nodeStanding(doc, pos) {
37
+ const node = doc.nodeAt(pos);
38
+ if (node === null) throw new Error(`no node stands at ${pos}`);
39
+ return node;
40
+ }
41
+ function deriveDisplay(transform, context, derivers, previousOf) {
42
+ const owners = ownersByType(transform.doc.type.schema, derivers);
43
+ const spots = [];
44
+ transform.doc.descendants((node, pos) => {
45
+ const owning = owners.get(node.type.name);
46
+ if (owning !== void 0) spots.push({ pos, owners: owning });
47
+ return !node.isTextblock;
48
+ });
49
+ for (const spot of spots) {
50
+ for (const deriver of spot.owners) {
51
+ const node = nodeStanding(transform.doc, spot.pos);
52
+ const derived = deriver.derive(
53
+ node,
54
+ spot.pos,
55
+ transform.doc,
56
+ context,
57
+ previousOf(node, spot.pos)
58
+ );
59
+ for (const { pos, attrs, mark: target } of derived) {
60
+ const standing = nodeStanding(transform.doc, pos);
61
+ if (target !== void 0) {
62
+ const mark = target.type.isInSet(standing.marks);
63
+ if (!standing.isInline || mark === void 0) continue;
64
+ const end = pos + standing.nodeSize - transform.doc.resolve(pos).textOffset;
65
+ if (target.to <= pos || target.to > end) {
66
+ throw new Error(
67
+ "a display mark update must stay within its inline node"
68
+ );
69
+ }
70
+ const next2 = target.type.create(displayLaidOver(mark, attrs));
71
+ if (!mark.eq(next2)) {
72
+ transform.step(new AddMarkStep(pos, target.to, next2));
73
+ }
74
+ continue;
75
+ }
76
+ const next = displayLaidOver(standing, attrs);
77
+ if (!standing.hasMarkup(standing.type, next, standing.marks)) {
78
+ transform.setNodeMarkup(pos, null, next);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ var displayOnly = new PluginKey("docxEditorDisplayOnly");
85
+ function sameOutsideDisplay(type, was, now) {
86
+ const names = /* @__PURE__ */ new Set([...Object.keys(was), ...Object.keys(now)]);
87
+ for (const name of names) {
88
+ if (attrRole(type, name) === "display") continue;
89
+ if (was[name] !== now[name]) return false;
90
+ }
91
+ return true;
92
+ }
93
+ function rewrittenNode(step, doc) {
94
+ const was = doc.nodeAt(step.from);
95
+ const now = step.slice.content.firstChild;
96
+ if (was === null || now === null || step.slice.content.childCount !== 1 || step.slice.openStart !== 0 || step.slice.openEnd !== 0 || now.content.size !== 0 || step.insert !== 1 || step.gapFrom !== step.from + 1 || step.to !== step.from + was.nodeSize || step.gapTo !== step.to - 1) {
97
+ return null;
98
+ }
99
+ return { was, now };
100
+ }
101
+ function changesOnlyDisplayAttrs(step, doc) {
102
+ if (step instanceof AddMarkStep) {
103
+ let touched = false;
104
+ let allowed = true;
105
+ doc.nodesBetween(step.from, step.to, (node, _pos, parent) => {
106
+ if (!node.isInline) return true;
107
+ if (!parent?.type.allowsMarkType(step.mark.type)) return false;
108
+ touched = true;
109
+ const previous = step.mark.type.isInSet(node.marks);
110
+ if (!previous || !sameOutsideDisplay(step.mark.type, previous.attrs, step.mark.attrs))
111
+ allowed = false;
112
+ return false;
113
+ });
114
+ return touched && allowed;
115
+ }
116
+ if (step instanceof AttrStep) {
117
+ const node = doc.nodeAt(step.pos);
118
+ return node !== null && attrRole(node.type, step.attr) === "display";
119
+ }
120
+ if (!(step instanceof ReplaceAroundStep)) return false;
121
+ const rewritten = rewrittenNode(step, doc);
122
+ if (rewritten === null) return false;
123
+ const { was, now } = rewritten;
124
+ return was.type === now.type && Mark.sameSet(was.marks, now.marks) && sameOutsideDisplay(was.type, was.attrs, now.attrs);
125
+ }
126
+ export {
127
+ changesOnlyDisplayAttrs,
128
+ deriveDisplay,
129
+ displayOnly
130
+ };
@@ -7,6 +7,9 @@
7
7
  * goes back out as the XML it arrived as. Which of the two a block is decides how it is written
8
8
  * (`docx/serializeBlock`) and how a submitted file is compared against the original
9
9
  * (`docx/storyProjection`), so a new block node has to name one of them.
10
+ *
11
+ * `./attrRoles` declares each attr's provenance and comparison role; `attrClasses.test.ts`
12
+ * checks coverage. The plugin guide defines the supported public surface.
10
13
  */
11
14
  import { Schema } from "prosemirror-model";
12
15
  export { imageNodeSpec, runMarkSpec } from "./rendering";
@@ -69,9 +69,7 @@ function isPageBreak(brAttrs) {
69
69
  }
70
70
  function srcIdOf(dom) {
71
71
  const raw = dom.getAttribute("data-src");
72
- if (raw === null) return null;
73
- const parsed = Number.parseInt(raw, 10);
74
- return Number.isNaN(parsed) ? null : parsed;
72
+ return raw === null || raw === "" ? null : raw;
75
73
  }
76
74
  function repliesHoldTheirXml(value) {
77
75
  if (!Array.isArray(value)) return true;
@@ -126,13 +124,11 @@ var docxSchema = new Schema({
126
124
  pAttrs: { default: null },
127
125
  /** The whole `<w:pPr>...</w:pPr>` XML. null when there is none */
128
126
  pPr: { default: null },
129
- /** The display values derived from reading pPr */
127
+ /** Derived paragraph formatting; see `./attrRoles`. */
130
128
  format: { default: null },
131
129
  /**
132
- * The character formatting the style this paragraph wears lays down, drawn as the
133
- * paragraph's own CSS so that text carrying no run of its own inherits it.
134
- * It is derived from the style table the same way `format` is, and like `format` it never
135
- * goes back into the document.
130
+ * Derived character formatting drawn on the paragraph so unmarked text inherits it.
131
+ * Its comparison role and provenance are declared in `./attrRoles`.
136
132
  */
137
133
  styleRun: { default: null }
138
134
  },
@@ -144,7 +140,7 @@ var docxSchema = new Schema({
144
140
  {
145
141
  class: editorClassNames.paragraph,
146
142
  style: paragraphStyle(format, styleRun),
147
- "data-src": node.attrs.srcId === null ? void 0 : `${node.attrs.srcId}`,
143
+ "data-src": text(node.attrs.srcId),
148
144
  "data-pattrs": text(node.attrs.pAttrs),
149
145
  "data-ppr": text(node.attrs.pPr),
150
146
  "data-fmt": formatJson(format),
@@ -195,6 +191,13 @@ var docxSchema = new Schema({
195
191
  tblW: { default: null },
196
192
  /** The `w:gridCol` widths (dxa) in order */
197
193
  gridCols: { default: [] },
194
+ /**
195
+ * The whole `<w:tblGridChange>...</w:tblGridChange>` XML, the record of the grid this
196
+ * table had before it was last revised. Carried as it arrived because the grid around it
197
+ * is rebuilt from `gridCols`, and CT_TblGrid takes it after the columns however wide
198
+ * those turn out to be (ECMA-376 Part 1 17.4.48)
199
+ */
200
+ gridChange: { default: null },
198
201
  format: { default: null },
199
202
  /** The lines between cells the table style laid down, so an edit can derive them again */
200
203
  styleInside: { default: null },
@@ -208,11 +211,12 @@ var docxSchema = new Schema({
208
211
  const attrs = {
209
212
  class: editorClassNames.table,
210
213
  style: tableStyle(format, width),
211
- "data-src": node.attrs.srcId === null ? void 0 : `${node.attrs.srcId}`,
214
+ "data-src": text(node.attrs.srcId),
212
215
  "data-tblattrs": text(node.attrs.tblAttrs),
213
216
  "data-tblpr": text(node.attrs.tblPr),
214
217
  "data-tblw": formatJson(width),
215
218
  "data-cols": numberListText(gridCols),
219
+ "data-gridchange": text(node.attrs.gridChange),
216
220
  "data-fmt": formatJson(format),
217
221
  "data-style-inside": formatJson(
218
222
  toInsideBorders(node.attrs.styleInside)
@@ -237,13 +241,21 @@ var docxSchema = new Schema({
237
241
  getAttrs: (dom) => {
238
242
  const tblAttrs = rawXml(dom, "data-tblattrs", ATTRIBUTES);
239
243
  const tblPr = rawXml(dom, "data-tblpr", ELEMENT("tblPr"));
240
- if (tblAttrs === false || tblPr === false) return false;
244
+ const gridChange = rawXml(
245
+ dom,
246
+ "data-gridchange",
247
+ ELEMENT("tblGridChange")
248
+ );
249
+ if (tblAttrs === false || tblPr === false || gridChange === false) {
250
+ return false;
251
+ }
241
252
  return {
242
253
  srcId: srcIdOf(dom),
243
254
  tblAttrs,
244
255
  tblPr,
245
256
  tblW: toTableWidth(parseJson(dom.getAttribute("data-tblw"))),
246
257
  gridCols: parseNumberList(dom.getAttribute("data-cols")),
258
+ gridChange,
247
259
  format: toTableFormat(parseJson(dom.getAttribute("data-fmt"))),
248
260
  styleInside: toInsideBorders(
249
261
  parseJson(dom.getAttribute("data-style-inside"))
@@ -439,7 +451,7 @@ var docxSchema = new Schema({
439
451
  "div",
440
452
  {
441
453
  class: className,
442
- "data-src": node.attrs.srcId === null ? "" : `${node.attrs.srcId}`,
454
+ "data-src": text(node.attrs.srcId) ?? "",
443
455
  "data-name": text(node.attrs.name)
444
456
  },
445
457
  label
@@ -470,7 +482,7 @@ var docxSchema = new Schema({
470
482
  "div",
471
483
  {
472
484
  class: editorClassNames.bookmarkBlock,
473
- "data-src": node.attrs.srcId === null ? "" : `${node.attrs.srcId}`,
485
+ "data-src": text(node.attrs.srcId) ?? "",
474
486
  "data-name": text(node.attrs.name),
475
487
  hidden: "hidden"
476
488
  }
@@ -923,7 +935,7 @@ var docxSchema = new Schema({
923
935
  attrs: {
924
936
  rPr: { default: null },
925
937
  rAttrs: { default: null },
926
- /** The display values derived from reading rPr */
938
+ /** Derived run formatting; see `./attrRoles`. */
927
939
  format: { default: null }
928
940
  },
929
941
  toDOM(mark) {
@@ -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";
49
+ export type EditGuardName = "protection" | "lock" | "bookmark" | "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;
@@ -12,14 +12,16 @@
12
12
  * `./editGuard` so that a module writing a guard need not read this one, and is handed on from
13
13
  * here so that a caller has one door to the whole seam.
14
14
  *
15
- * `editShut` is not yet what every command asks. Only `editor/commands/breakCommands`,
16
- * `editor/commands/tabCommands` and `editor/insertImage` ask it today; the rest still compose
17
- * `editsShut` with a lock predicate of their own (`./locks`), and each decides for itself whether
18
- * a stretch a guard shuts is trimmed out of the edit or refuses the whole of it. Until they move
19
- * over, a guard added to the list below reaches those commands through `transactionAllowed` alone,
20
- * which refuses the transaction they built rather than telling them not to build it.
15
+ * A command decides nothing of its own about any of this. It takes one of the two shapes an edit
16
+ * comes in - `guardedCommand`, which builds the whole edit and is refused whole, and
17
+ * `openStretches`, which leaves the shut stretches out and applies to the rest - and both ask the
18
+ * list below. A guard registered here therefore reaches every command by being registered, rather
19
+ * than by each command being taught about it.
20
+ *
21
+ * `editsShut` (`./protectionState`) stays what it was, the view-level question of whether the body
22
+ * is open at all, which is a question about the editor rather than about an edit.
21
23
  */
22
- import type { EditorState, Selection, Transaction } from "prosemirror-state";
24
+ import type { Command, EditorState, Selection, Transaction } from "prosemirror-state";
23
25
  import { type EditGuard, type EditIntent } from "./editGuard";
24
26
  import { type ProtectionState } from "./protection";
25
27
  export type { EditGuard, EditGuardName, EditIntent } from "./editGuard";
@@ -47,9 +49,10 @@ export declare const EDIT_GUARDS: readonly EditGuard[];
47
49
  * decision stands apart from it and every caller building an edit asks this rather than handing
48
50
  * the transaction to a state.
49
51
  *
50
- * The whole-change judgements come first and take no pass, since a pass lifts one guard's reading
51
- * of a step rather than another guard's reading of the change. What is left is judged step by
52
- * step, each step over the document it was built against.
52
+ * A re-derivation of display values is no edit and is let through before any guard is asked. The
53
+ * whole-change judgements then come first and take no pass, since a pass lifts one guard's
54
+ * reading of a step rather than another guard's reading of the change. What is left is judged
55
+ * step by step, each step over the document it was built against.
53
56
  */
54
57
  export declare function transactionAllowed(tr: Transaction, state: EditorState): boolean;
55
58
  /**
@@ -59,10 +62,30 @@ export declare function transactionAllowed(tr: Transaction, state: EditorState):
59
62
  * being refused by the guard draws a live control that swallows the click.
60
63
  */
61
64
  export declare function editShut(state: EditorState, intent: EditIntent): boolean;
65
+ /** What a command doing this to whatever is selected means to do, one intent per selected stretch */
66
+ export declare function selectionIntents(selection: Selection, kind: "mark" | "replace"): EditIntent[];
62
67
  /**
63
- * What a command doing this to whatever is selected means to do, one intent per selected stretch.
68
+ * Build, guard, dispatch: the one shape of a command that is refused whole.
64
69
  *
65
- * A stretch of no length holds nothing to mark or to put away, so whatever the command would do to
66
- * a stretch it is an insertion there.
70
+ * A structural edit has no smaller piece to fall back on - half a row cannot be deleted, and half a
71
+ * block of cells cannot be merged into one - so the whole transaction is built, handed to the whole
72
+ * guard list, and dispatched only if it comes back allowed. A character or a paragraph edit does
73
+ * the opposite and leaves the shut stretches out (`openStretches`).
74
+ *
75
+ * The answer is the same whether or not `dispatch` was passed. The transaction is built before
76
+ * either way, so asking the guard costs nothing more, and a command reporting one thing to a button
77
+ * and doing another would be worse than the button being wrong.
67
78
  */
68
- export declare function selectionIntents(selection: Selection, kind: "mark" | "replace"): EditIntent[];
79
+ export declare function guardedCommand(build: (state: EditorState) => Transaction | null): Command;
80
+ /**
81
+ * The stretches the guards leave open, which is the one shape of a command that is trimmed.
82
+ *
83
+ * A shut stretch is left out rather than the whole edit refused: a guard turns down the whole
84
+ * transaction, so asking for the shut stretch as well would leave the rest of the selection
85
+ * unedited too. A selection the guards leave nothing of edits nothing, and the command reports that
86
+ * of its own accord, which is the disabled state of the control that runs it.
87
+ */
88
+ export declare function openStretches<S extends {
89
+ from: number;
90
+ to: number;
91
+ }>(state: EditorState, stretches: readonly S[], kind: "mark" | "replace"): S[];