@portone/docx-editor 0.2.0 → 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 (256) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/CONTRIBUTING.md +9 -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 +14 -7
  10. package/dist/docx/commentOnlyChange.js +9 -139
  11. package/dist/docx/comments/constants.d.ts +3 -4
  12. package/dist/docx/comments/constants.js +4 -5
  13. package/dist/docx/comments/grammar.d.ts +86 -0
  14. package/dist/docx/comments/grammar.js +223 -0
  15. package/dist/docx/comments/parts.d.ts +52 -0
  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 +28 -106
  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.d.ts +5 -3
  22. package/dist/docx/comments/reading.js +30 -29
  23. package/dist/docx/comments/writing.d.ts +19 -7
  24. package/dist/docx/comments/writing.js +105 -137
  25. package/dist/docx/documentSettings.d.ts +7 -0
  26. package/dist/docx/documentSettings.js +10 -2
  27. package/dist/docx/exportDocx.d.ts +40 -5
  28. package/dist/docx/exportDocx.js +71 -72
  29. package/dist/docx/exportRefs.d.ts +5 -2
  30. package/dist/docx/exportRefs.js +3 -1
  31. package/dist/docx/fidelity.d.ts +43 -0
  32. package/dist/docx/fidelity.js +64 -0
  33. package/dist/docx/formatting/attrs.d.ts +27 -0
  34. package/dist/docx/formatting/attrs.js +31 -0
  35. package/dist/docx/formatting/context.d.ts +31 -0
  36. package/dist/docx/formatting/context.js +42 -0
  37. package/dist/docx/formatting/direct.d.ts +6 -5
  38. package/dist/docx/formatting/direct.js +52 -81
  39. package/dist/docx/formatting/resolve.d.ts +45 -0
  40. package/dist/docx/formatting/resolve.js +140 -0
  41. package/dist/docx/formatting/runProperties.d.ts +93 -0
  42. package/dist/docx/formatting/runProperties.js +316 -0
  43. package/dist/docx/formatting/styles.js +3 -3
  44. package/dist/docx/formatting/tabStops.js +7 -22
  45. package/dist/docx/formatting.d.ts +4 -1
  46. package/dist/docx/formatting.js +4 -1
  47. package/dist/docx/headersFooters.js +13 -22
  48. package/dist/docx/identities.d.ts +67 -0
  49. package/dist/docx/identities.js +174 -0
  50. package/dist/docx/importDocx.d.ts +12 -1
  51. package/dist/docx/importDocx.js +72 -79
  52. package/dist/docx/importParagraph.d.ts +1 -1
  53. package/dist/docx/importParagraph.js +4 -3
  54. package/dist/docx/importTable.d.ts +1 -1
  55. package/dist/docx/importTable.js +17 -1
  56. package/dist/docx/invariants.d.ts +33 -0
  57. package/dist/docx/invariants.js +256 -0
  58. package/dist/docx/media.d.ts +6 -4
  59. package/dist/docx/media.js +6 -37
  60. package/dist/docx/newLists.d.ts +20 -0
  61. package/dist/docx/newLists.js +36 -0
  62. package/dist/docx/notes.js +6 -12
  63. package/dist/docx/numberingPlanner.d.ts +8 -0
  64. package/dist/docx/numberingPlanner.js +19 -0
  65. package/dist/docx/packageParts.d.ts +42 -0
  66. package/dist/docx/packageParts.js +135 -0
  67. package/dist/docx/pageGeometry.d.ts +2 -0
  68. package/dist/docx/pageGeometry.js +18 -9
  69. package/dist/docx/paraProps.d.ts +10 -22
  70. package/dist/docx/paraProps.js +75 -99
  71. package/dist/docx/partPlan.d.ts +36 -0
  72. package/dist/docx/partPlan.js +59 -0
  73. package/dist/docx/protectionPolicy.d.ts +127 -0
  74. package/dist/docx/protectionPolicy.js +169 -0
  75. package/dist/docx/relationships.d.ts +1 -1
  76. package/dist/docx/relationships.js +15 -15
  77. package/dist/docx/runProps.d.ts +9 -22
  78. package/dist/docx/runProps.js +15 -168
  79. package/dist/docx/scan.js +20 -52
  80. package/dist/docx/sdt.js +14 -37
  81. package/dist/docx/sdtProps.d.ts +8 -1
  82. package/dist/docx/sdtProps.js +10 -0
  83. package/dist/docx/serializeBlock.d.ts +2 -0
  84. package/dist/docx/serializeBlock.js +9 -7
  85. package/dist/docx/serializeParagraph.d.ts +2 -1
  86. package/dist/docx/serializeParagraph.js +25 -14
  87. package/dist/docx/serializeTable.js +63 -32
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +43 -17
  90. package/dist/docx/storyProjection.d.ts +36 -0
  91. package/dist/docx/storyProjection.js +23 -0
  92. package/dist/docx/tableFormatting/editing.js +130 -185
  93. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  94. package/dist/docx/tableFormatting/reading.js +40 -20
  95. package/dist/docx/tableTemplate.js +37 -5
  96. package/dist/docx/theme.d.ts +0 -6
  97. package/dist/docx/theme.js +0 -8
  98. package/dist/download.d.ts +8 -5
  99. package/dist/download.js +2 -0
  100. package/dist/editor/clipboard/images.js +17 -3
  101. package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
  102. package/dist/editor/clipboard/inlineFormatting.js +20 -30
  103. package/dist/editor/commands/breakCommands.js +4 -3
  104. package/dist/editor/commands/canRunCommand.d.ts +2 -2
  105. package/dist/editor/commands/canRunCommand.js +1 -1
  106. package/dist/editor/commands/comments/editing.d.ts +6 -2
  107. package/dist/editor/commands/comments/editing.js +21 -36
  108. package/dist/editor/commands/exportQueries.d.ts +15 -0
  109. package/dist/editor/commands/exportQueries.js +14 -0
  110. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  111. package/dist/editor/commands/fidelityQueries.js +8 -0
  112. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  113. package/dist/editor/commands/formatting/editing.js +26 -108
  114. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  115. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  116. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  117. package/dist/editor/commands/formatting/shared.js +5 -2
  118. package/dist/editor/commands/historyCommands.js +2 -1
  119. package/dist/editor/commands/indentCommands.js +5 -4
  120. package/dist/editor/commands/index.d.ts +11 -0
  121. package/dist/editor/commands/index.js +5 -0
  122. package/dist/editor/commands/linkCommands.js +5 -6
  123. package/dist/editor/commands/listCommands.js +8 -10
  124. package/dist/editor/commands/lockCommands.d.ts +7 -1
  125. package/dist/editor/commands/lockCommands.js +35 -37
  126. package/dist/editor/commands/paragraphCommands.js +28 -31
  127. package/dist/editor/commands/spacingCommands.js +1 -1
  128. package/dist/editor/commands/tabCommands.js +4 -3
  129. package/dist/editor/createEditor.d.ts +17 -29
  130. package/dist/editor/createEditor.js +61 -67
  131. package/dist/editor/documentStyles.d.ts +11 -35
  132. package/dist/editor/documentStyles.js +9 -52
  133. package/dist/editor/editorDocument.d.ts +56 -0
  134. package/dist/editor/editorDocument.js +74 -0
  135. package/dist/editor/externalClipboard.js +166 -33
  136. package/dist/editor/insertImage.js +4 -3
  137. package/dist/editor/insertTable.js +4 -3
  138. package/dist/editor/paragraphEdits.d.ts +11 -18
  139. package/dist/editor/paragraphEdits.js +7 -16
  140. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  141. package/dist/editor/plugins/displayDerivation.js +78 -0
  142. package/dist/editor/plugins/documentProtection.d.ts +3 -3
  143. package/dist/editor/plugins/imagePaste.js +4 -3
  144. package/dist/editor/plugins/keymap.js +18 -3
  145. package/dist/editor/plugins/lockedContent.d.ts +4 -4
  146. package/dist/editor/plugins/lockedContent.js +1 -1
  147. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  148. package/dist/editor/plugins/numberingDecorations.js +7 -17
  149. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  150. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  151. package/dist/editor/plugins/tabLayout.js +1 -1
  152. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  153. package/dist/editor/plugins/tableDisplay.js +16 -0
  154. package/dist/index.d.ts +2 -0
  155. package/dist/model/format.d.ts +30 -8
  156. package/dist/model/format.js +35 -22
  157. package/dist/model/tabStops.d.ts +9 -0
  158. package/dist/model/tabStops.js +18 -0
  159. package/dist/numbering/listTemplate.js +49 -10
  160. package/dist/numbering/parseNumbering.d.ts +14 -1
  161. package/dist/numbering/parseNumbering.js +35 -22
  162. package/dist/numbering/writeNumbering.d.ts +3 -4
  163. package/dist/numbering/writeNumbering.js +14 -25
  164. package/dist/ooxml/childOrder.d.ts +34 -0
  165. package/dist/ooxml/childOrder.js +496 -0
  166. package/dist/ooxml/element.d.ts +65 -0
  167. package/dist/ooxml/element.js +68 -0
  168. package/dist/ooxml/errors.d.ts +3 -2
  169. package/dist/ooxml/fragment.d.ts +53 -0
  170. package/dist/ooxml/fragment.js +76 -0
  171. package/dist/ooxml/image.d.ts +4 -0
  172. package/dist/ooxml/image.js +3 -5
  173. package/dist/ooxml/names.d.ts +27 -0
  174. package/dist/ooxml/names.js +29 -0
  175. package/dist/ooxml/partSplice.d.ts +67 -0
  176. package/dist/ooxml/partSplice.js +169 -0
  177. package/dist/ooxml/precedence.d.ts +31 -0
  178. package/dist/ooxml/precedence.js +51 -0
  179. package/dist/ooxml/props.d.ts +112 -0
  180. package/dist/ooxml/props.js +214 -0
  181. package/dist/ooxml/simpleTypes.d.ts +103 -0
  182. package/dist/ooxml/simpleTypes.js +182 -0
  183. package/dist/ooxml/tabStops.js +8 -27
  184. package/dist/ooxml/tagScan.d.ts +34 -0
  185. package/dist/ooxml/tagScan.js +108 -0
  186. package/dist/ooxml/units.d.ts +25 -13
  187. package/dist/ooxml/units.js +65 -31
  188. package/dist/ooxml/xml.d.ts +52 -0
  189. package/dist/ooxml/xml.js +64 -5
  190. package/dist/page/blockKinds.d.ts +103 -0
  191. package/dist/page/blockKinds.js +11 -0
  192. package/dist/page/kinds/index.d.ts +6 -0
  193. package/dist/page/kinds/index.js +10 -0
  194. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  195. package/dist/page/kinds/paragraphKind.js +71 -0
  196. package/dist/page/kinds/tableKind.d.ts +10 -0
  197. package/dist/page/kinds/tableKind.js +178 -0
  198. package/dist/page/measureBlocks.d.ts +3 -1
  199. package/dist/page/measureBlocks.js +24 -25
  200. package/dist/page/pageDecorations.d.ts +27 -28
  201. package/dist/page/pageDecorations.js +70 -151
  202. package/dist/page/pageLayout.d.ts +10 -62
  203. package/dist/page/pageLayout.js +60 -53
  204. package/dist/page/usePageLayout.d.ts +2 -2
  205. package/dist/page/usePageLayout.js +3 -11
  206. package/dist/schema/attrRoles.d.ts +42 -0
  207. package/dist/schema/attrRoles.js +141 -0
  208. package/dist/schema/displayDerivation.d.ts +82 -0
  209. package/dist/schema/displayDerivation.js +130 -0
  210. package/dist/schema/docxSchema.d.ts +9 -0
  211. package/dist/schema/docxSchema.js +237 -114
  212. package/dist/schema/editGuard.d.ts +99 -0
  213. package/dist/schema/editGuard.js +43 -0
  214. package/dist/schema/guards.d.ts +91 -0
  215. package/dist/schema/guards.js +108 -0
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +17 -77
  219. package/dist/schema/locks.js +26 -70
  220. package/dist/schema/preservedGuards.d.ts +50 -0
  221. package/dist/schema/preservedGuards.js +80 -0
  222. package/dist/schema/protection.d.ts +17 -4
  223. package/dist/schema/protection.js +4 -2
  224. package/dist/schema/sourceEquality.d.ts +19 -0
  225. package/dist/schema/sourceEquality.js +39 -0
  226. package/dist/styles/inlineStyle.js +15 -6
  227. package/dist/table/cellFormatting.d.ts +8 -1
  228. package/dist/table/cellFormatting.js +10 -17
  229. package/dist/table/commands.d.ts +2 -2
  230. package/dist/table/commands.js +20 -18
  231. package/dist/table/format.d.ts +3 -0
  232. package/dist/table/format.js +3 -9
  233. package/dist/table/gridBorders.d.ts +14 -16
  234. package/dist/table/gridBorders.js +3 -41
  235. package/dist/table/index.d.ts +2 -2
  236. package/dist/table/merge.d.ts +4 -8
  237. package/dist/table/merge.js +5 -5
  238. package/package.json +10 -3
  239. package/dist/docx/comments/contentTypes.d.ts +0 -7
  240. package/dist/docx/comments/contentTypes.js +0 -38
  241. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  242. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  243. package/dist/docx/propsXml.d.ts +0 -49
  244. package/dist/docx/propsXml.js +0 -302
  245. package/dist/docx/uniqueControls.d.ts +0 -14
  246. package/dist/docx/uniqueControls.js +0 -62
  247. package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
  248. package/dist/editor/plugins/bookmarkProtection.js +0 -28
  249. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  250. package/dist/editor/plugins/commentReservations.js +0 -26
  251. package/dist/editor/plugins/noteProtection.d.ts +0 -3
  252. package/dist/editor/plugins/noteProtection.js +0 -25
  253. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  254. package/dist/editor/plugins/styledParagraphs.js +0 -65
  255. package/dist/page/tableMeasurements.d.ts +0 -10
  256. package/dist/page/tableMeasurements.js +0 -115
package/CHANGELOG.md CHANGED
@@ -1,5 +1,179 @@
1
1
  # @portone/docx-editor
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#62](https://github.com/portone-io/docx-editor/pull/62) [`5c481e7`](https://github.com/portone-io/docx-editor/commit/5c481e7c6e9a27373dad90d866a70cd80919b294) Thanks [@Deea222](https://github.com/Deea222)! - `onlyCommentsChangedBy` answers `comment-markup-rejected` where it used to answer `part-changed` for a comment part entry this editor would not have written for this author, or an entry nothing refers to that changed. `part` still names the comment part the entry sits in.
8
+
9
+ The verdicts themselves are unchanged: every file accepted before is accepted now, and every file refused before is refused now. What moves is the name a server logs, so "a part this file was not supposed to touch" and "markup forged into a comment" no longer read alike. A `switch` over `verdict.reason` needs the new case.
10
+
11
+ - [#77](https://github.com/portone-io/docx-editor/pull/77) [`a0b5cc6`](https://github.com/portone-io/docx-editor/commit/a0b5cc643bbeb6cf3407a0d729e7cc74fec304d6) Thanks [@Deea222](https://github.com/Deea222)! - Ask whether a document can be exported before trying.
12
+
13
+ `exportProblems(doc, session)` on the core entry, and `canExport(state)` with `documentExportProblems(state)` on the commands entry, report known reasons the writer would refuse the document, in the order it would raise them: each under the code and with the message the `DocxExportError` would carry, and with its position where the problem stands in the document. `exportDocx` throws the first entry of the same list, so problems reported by the query are also refused by the write.
14
+
15
+ The editor's handle answers the same list as `exportProblems()` beside `exportBytes()`, and `downloadDocx` asks it first and returns `{ status: "blocked", problems }` instead of throwing. A refusal the list does not foresee, such as a node a plugin stripped of an attribute the writer needs, is still thrown. `DownloadDocxResult` gaining a fourth member is a compile error for a `switch` over `status` written to be exhaustive; add a `blocked` branch that shows the problems.
16
+
17
+ A document whose comments part arrived as an empty element used to refuse its first comment with `malformed-xml`, since the writer looked for a closing tag the element does not have; the part is now opened for the entry, the way an empty extended comments part already was.
18
+
19
+ - [#63](https://github.com/portone-io/docx-editor/pull/63) [`1a4ec92`](https://github.com/portone-io/docx-editor/commit/1a4ec92162477d468fdb14a95a47388befba45f9) Thanks [@Deea222](https://github.com/Deea222)! - Read what a document holds that this editor cannot model. `importDocx` returns a `notes` array alongside the document and the session, the new `exportDocxReport` returns the same list beside the bytes it writes, and the new `documentFidelity(state)` on `./commands` answers the question about the document standing in an editor. A `FidelityNote` says how much of the original survived, what kind of content it was, the part and body block it came from, where in the document it stands, and the original element name, so a host can list what a file lost instead of guessing at it. Destructuring `importDocx` is unaffected.
20
+
21
+ A table now carries its `w:tblGridChange` instead of losing it whenever the table is rebuilt. The grid is still written from the column widths, and the revision markup closes it where CT_TblGrid takes it. `onlyCommentsChangedBy` therefore catches a submission that lost a `w:tblGridChange`, where it used to accept one: a returned file that dropped it now answers `body-changed` rather than `ok`.
22
+
23
+ - [#79](https://github.com/portone-io/docx-editor/pull/79) [`70d4801`](https://github.com/portone-io/docx-editor/commit/70d48015f52a126824f25bbd03a65b5509699de2) Thanks [@Deea222](https://github.com/Deea222)! - Applying a paragraph style keeps the spacing the document defaults lay down, and formatting a style or the document defaults switch on can be switched off: the off is written into the run (`w:b w:val="0"`) and drawn as off, where it used to be dropped and the style's value drawn again.
24
+
25
+ Every display value a paragraph or a run carries is now resolved in the ECMA-376 §17.7.2 order by one resolver, whichever path built the paragraph: opening the document, applying a style, a paragraph edit, a paste, or typing into a fresh paragraph. A character style a run points at (`w:rStyle`) takes its place in that order, a hanging indent's implicit tab stop follows the indent actually drawn, and a document's `w:noTabHangInd` setting switches that stop off.
26
+
27
+ `RunFormat` grows to say so: `bold`, `italic`, `strike` and `smallCaps` are `boolean` (false is a toggle the run switches off outright), and `underline` may be `"none"`. A reader checking `=== true` or a truthy value is unaffected.
28
+
29
+ Supported character defaults now appear in the text and toolbar, so a default bold setting turns off with one press. Clearing a direct font size immediately restores the inherited paragraph or character style in the formatting controls.
30
+
31
+ - [#82](https://github.com/portone-io/docx-editor/pull/82) [`f804ba1`](https://github.com/portone-io/docx-editor/commit/f804ba1eb5e5e6ed5ebf811e1edf2fa1858dc601) Thanks [@Deea222](https://github.com/Deea222)! - A paragraph marked keep with next (`w:keepNext`), by its own properties or by its style, stays on the same page as the start of the block after it in the page guides, as it does in Word. A run of such paragraphs moves together with the first piece of the block the keeps end at; a run no page can hold is laid out as if no keep were set. The document is not changed: the mark is read, never written.
32
+
33
+ `ParagraphFormat` grows `keepNext?: boolean` to say so, beside `pageBreakBefore`.
34
+
35
+ An explicit off (`w:keepNext w:val="0"`) overrides an inherited keep, so a paragraph can opt out of the keep imposed by its style.
36
+
37
+ - [#80](https://github.com/portone-io/docx-editor/pull/80) [`a37b4fe`](https://github.com/portone-io/docx-editor/commit/a37b4fea75bbf6e9c342c830d358ba21119dd17c) Thanks [@Deea222](https://github.com/Deea222)! - Run formatting is read, written and compared through one property table, so the value a control reads, the XML an edit writes and the check that leaves text already in that state alone can no longer drift apart. The XML written is byte for byte what it was.
38
+
39
+ `RunFormat` gains `caps`, `doubleStrike` and `characterSpacingPt`, read off a run's `w:caps`, `w:dstrike` and `w:spacing`. They reach the `format` attr a plugin reads; the editor does not draw or edit them yet.
40
+
41
+ An underline setter now distinguishes underline kinds; the public underline toggle still turns any existing kind off.
42
+
43
+ - [#72](https://github.com/portone-io/docx-editor/pull/72) [`578e73e`](https://github.com/portone-io/docx-editor/commit/578e73ed9444f528231048621a13651511438e79) Thanks [@Deea222](https://github.com/Deea222)! - `srcId` now names the block within the session it was opened in. A preserved block used to carry a bare index into the blocks of whichever document it was exported against, so a block moved or pasted in from another document pointed at this document's block of the same number and went out as that block's XML instead of its own. The attribute is now a string naming the document, the story and the place in it, and a block whose document is not the one being exported is refused with `lost-original` rather than written as something else.
44
+
45
+ A plugin that read `node.attrs.srcId` as a number needs to change: it is a string, and the only thing to do with it is hand it back as it was found. Nothing else about the attribute is public, and no exported type or function signature changed.
46
+
47
+ - [#60](https://github.com/portone-io/docx-editor/pull/60) [`1b5d0fd`](https://github.com/portone-io/docx-editor/commit/1b5d0fd96f13341de0fe34374c2a7dedec57525c) Thanks [@Deea222](https://github.com/Deea222)! - Hand `importDocx`, `exportDocx`, `onlyCommentsChangedBy`, `documentNumbering` and `parseNumbering` an `xmlParser` to read a document on a runtime that has no `DOMParser` global, instead of installing one.
48
+
49
+ A call given neither is refused with `DocxImportError` and the new import code `no-xml-parser`. It used to fail with a bare `ReferenceError`, which a server checking a file a counterparty returned could not tell apart from a document that arrived damaged. Reading a document no longer asks for a `Node` global at all, so `DOMParser`, however it is supplied, is the only thing the core entry needs from a DOM.
50
+
51
+ Each entry point settles its parser as the call comes in, so a runtime holding none is turned down before the bytes are looked at: bytes that are not a docx opened without a parser now report `no-xml-parser` where they reported `not-a-docx`. A parser that answers markup it cannot read by throwing, rather than by handing back a document holding a `parsererror`, is read as `malformed-xml` instead of having its own exception reach the caller.
52
+
53
+ ### Patch Changes
54
+
55
+ - [#75](https://github.com/portone-io/docx-editor/pull/75) [`c79e417`](https://github.com/portone-io/docx-editor/commit/c79e417bc7c2ec9cc3be804a1010533780558954) Thanks [@Deea222](https://github.com/Deea222)! - Document which schema attributes plugins may rely on and which raw OOXML attributes are internal.
56
+ An internal classification now records the provenance of every node and mark attribute without changing import, editing, or export behavior.
57
+
58
+ - [#65](https://github.com/portone-io/docx-editor/pull/65) [`a9a7e6d`](https://github.com/portone-io/docx-editor/commit/a9a7e6d92277f7725d995415da43ae5f3488e079) Thanks [@Deea222](https://github.com/Deea222)! - Content control properties are written in the order the schema lays down. Locking a control that declares what kind of control it is - a date picker, a drop-down, plain text - used to write `w:lock` after that declaration, where CT_SdtPr puts `w:lock` before it, so a validator reading the exported file could refuse a control this editor had rewritten. A control carrying `w:label` or `w:tabIndex` was written the same wrong way round.
59
+
60
+ Untouched documents retain their original XML.
61
+
62
+ - [#73](https://github.com/portone-io/docx-editor/pull/73) [`ddda545`](https://github.com/portone-io/docx-editor/commit/ddda545d43788007cf8c2c0507a33b1326d593ca) Thanks [@Deea222](https://github.com/Deea222)! - Enter no longer duplicates paragraph identifiers or a paragraph-level section break. The continuing paragraph keeps its identifiers, and the section break stays on the last paragraph of the split.
63
+
64
+ Deleting or joining away a section-ending paragraph is refused until section editing is supported. Replacing text within that paragraph remains available.
65
+
66
+ - [#81](https://github.com/portone-io/docx-editor/pull/81) [`94d4ed7`](https://github.com/portone-io/docx-editor/commit/94d4ed748b22f49054acfb0b9cf7abcd83f19f09) Thanks [@Deea222](https://github.com/Deea222)! - The lines of a table's cells and the style values of a paragraph are worked out again by one plugin, which also works every value out again when the document's formatting is replaced under it; no behavior change for a document opened today.
67
+
68
+ Changing the formatting context also refreshes existing run marks, so text does not keep its previous style after the paragraph display values change. These updates preserve the original run XML, including in locked content and protected documents.
69
+
70
+ - [#74](https://github.com/portone-io/docx-editor/pull/74) [`c22f1da`](https://github.com/portone-io/docx-editor/commit/c22f1da7e1aec0a4a5ffbd805eb7b761655ea84f) Thanks [@Deea222](https://github.com/Deea222)! - Commands now share guard helpers so their applicability checks and dispatched edits respect the same rules. Formatting at a caret inside locked content is refused; formatting queries continue to report the selected text's values under document protection.
71
+
72
+ - [#90](https://github.com/portone-io/docx-editor/pull/90) [`3b37fbd`](https://github.com/portone-io/docx-editor/commit/3b37fbd69eb90fd27011af4ecce2dfd47b61b64f) Thanks [@Deea222](https://github.com/Deea222)! - Importing `emuToPx`, `pxToEmu` or `toImageExtent` off `./core`, or the image file helpers off `./commands`, no longer carries the XML naming layer into a consumer's bundle. The picture module read the namespace table at the top of the file, which a bundler keeps as a side effect, so one multiplication cost 817 bytes minified where it costs 182. Nothing written into a document changes.
73
+
74
+ - [#70](https://github.com/portone-io/docx-editor/pull/70) [`9d7f659`](https://github.com/portone-io/docx-editor/commit/9d7f6598c2f010ee53753cbd3c7952e8b4bc7f1e) Thanks [@Deea222](https://github.com/Deea222)! - The editor reads document-level values such as styles, numbering and page geometry from one snapshot; no visible change.
75
+
76
+ - [#69](https://github.com/portone-io/docx-editor/pull/69) [`4f8c94a`](https://github.com/portone-io/docx-editor/commit/4f8c94abf38c4b77eecb632dc783f215d5decb41) Thanks [@Deea222](https://github.com/Deea222)! - A page measurement applies its pushes, break spaces and table continuations in one transaction instead of three. The pages look exactly as they did; what changes is that a single measurement now reaches the editor as a single state change, so anything watching transactions - an `onStateChange` handler, a plugin, a React state hook - sees one rather than three per remeasure.
77
+
78
+ A table's repeated header now also refreshes as soon as its source row is edited, without waiting for the next measurement.
79
+
80
+ Changing a continued row's formatting keeps its page gap until remeasurement. Page pushes also update when their measured contribution changes but the total top margin stays the same.
81
+
82
+ - [#83](https://github.com/portone-io/docx-editor/pull/83) [`14f3b87`](https://github.com/portone-io/docx-editor/commit/14f3b875004ae28c41a3fd0d58c7783fa9731d67) Thanks [@Deea222](https://github.com/Deea222)! - A relationships part whose root carries a namespace prefix, or that arrived as an empty element, is now spliced correctly when the export adds a relationship to it: the entry goes inside the root, spelled under the root's own prefix, where the export used to refuse the file with `malformed-xml` for want of a bare closing tag. A numbering part that arrived as an empty element takes a new list definition the same way instead of being refused.
83
+
84
+ When one export adds several parts, `[Content_Types].xml` now declares them in the order they were added, a media type's `Default` ahead of any `Override`; each writer used to put its own declaration first, so the declarations came out in the reverse order of adding. Every declaration is still placed right after the opening tag, and the rest of the part is left as it arrived.
85
+
86
+ Every XML part the export rewrites is now read back before the file is repacked, so a part that would not open is refused with `malformed-xml` naming the part rather than handed back.
87
+
88
+ New part names and content-type requests recognize names differing only in case, avoiding duplicate package entries and declarations.
89
+
90
+ - [#71](https://github.com/portone-io/docx-editor/pull/71) [`a0bcd87`](https://github.com/portone-io/docx-editor/commit/a0bcd8798a33ecf3aba267bc2cdfbbca2b98d632) Thanks [@Deea222](https://github.com/Deea222)! - Comment-only verification and export now share the definitions for comment package parts. Public types are unchanged.
91
+
92
+ Verification rejects new or altered content around comment entries, including outside the XML root, while accepting annotations preserved from the original and those removed by normal comment-part rewrites. Namespace rebindings under rewritten comment markup are also rejected.
93
+
94
+ - [#76](https://github.com/portone-io/docx-editor/pull/76) [`46c8ba7`](https://github.com/portone-io/docx-editor/commit/46c8ba73cedd06cd85141ea44f09119648315396) Thanks [@Deea222](https://github.com/Deea222)! - Table pagination moves into a block-kind module; no visible change. What the page engine knows about a table - where it may be parted between rows, the spacer and repeated header a continued page is drawn with, and which positions a page cut may stand at - used to be spread over the measurer, the decorations and the plugin state. It is now one module beside the paragraph's, and the engine asks whichever kind claims a block. Pages, page breaks and continued tables look exactly as they did.
95
+
96
+ - [#63](https://github.com/portone-io/docx-editor/pull/63) [`1a4ec92`](https://github.com/portone-io/docx-editor/commit/1a4ec92162477d468fdb14a95a47388befba45f9) Thanks [@Deea222](https://github.com/Deea222)! - Keep namespace bindings declared on a table grid when preserving its revision history, so editing the table can still produce a readable DOCX. Rebuilt tables also retain the required empty table-properties element when no properties are set.
97
+
98
+ - [#78](https://github.com/portone-io/docx-editor/pull/78) [`3bdf81d`](https://github.com/portone-io/docx-editor/commit/3bdf81dc3c6b0a92091e46fc8312907e60cfc67e) Thanks [@Deea222](https://github.com/Deea222)! - A paragraph copied inside the editor no longer exports a duplicate `w14:paraId`. The copy goes out as a paragraph of its own, without the original's identifiers, and the original still goes out as the bytes it arrived as.
99
+
100
+ A preserved block copied twice, a body-level bookmark marker or a section break among them, is refused with `unsupported-content` instead of written twice. `exportProblems` and `canExport` report that refusal ahead of the write, at the place the second copy stands.
101
+
102
+ - [#67](https://github.com/portone-io/docx-editor/pull/67) [`77a468f`](https://github.com/portone-io/docx-editor/commit/77a468f5c4159e619a20b446bd35d14b4833f44a) Thanks [@Deea222](https://github.com/Deea222)! - Universal measures such as `8.5in` are read correctly. A measurement in a document may be written as a length with a unit - `8.5in`, `2.54cm`, `12pt` - as well as a count, and every one of them used to be read as its leading digits alone. A US Letter document whose section says `w:pgSz w:w="8.5in"` was drawn as a page 8.5 twips wide, which is no page at all, so the editor fell back to A4 and showed the wrong paper; a tab stop at `1.5in` landed at 0.08pt and an automatic tab interval of `0.75in` collapsed every tab in that document to no width. Page size and margins, indents, spacing, font sizes, table and cell widths, cell margins, row heights and tab stops are now read as the lengths they name.
103
+
104
+ `on` and `off` spellings of boolean attributes are read as the schema admits. A style marked `w:default="on"` is now recognised as the default style for its kind, as `w:default="1"` already was, so a document that marks its defaults that way is shown with the formatting they lay down.
105
+
106
+ Integer measurements retain support for an explicit `+` sign. Table and cell widths with an explicit `%` follow Word's percentage interpretation even when their width type says otherwise. Rebuilt table widths and grid columns are written in whole units; untouched XML is preserved.
107
+
108
+ Overflowing universal-measure and percentage conversions are rejected during import.
109
+
110
+ - [#65](https://github.com/portone-io/docx-editor/pull/65) [`a9a7e6d`](https://github.com/portone-io/docx-editor/commit/a9a7e6d92277f7725d995415da43ae5f3488e079) Thanks [@Deea222](https://github.com/Deea222)! - An edit to a cell border, a cell shading, a row height, a paragraph indent, or a line spacing now reads and writes the WordprocessingML attribute alone. A producer's own attribute that shares the local name (`x:val` beside `w:val`, declared ignorable) used to be taken for the formatting value, or written over in its place; it is now left as the producer wrote it, and the value Word reads is the one read and written.
111
+
112
+ ## 0.2.1
113
+
114
+ ### Patch Changes
115
+
116
+ - [#53](https://github.com/portone-io/docx-editor/pull/53) [`d264803`](https://github.com/portone-io/docx-editor/commit/d264803381c3272ae31e1af1faa7fd158b95a81f) Thanks [@Deea222](https://github.com/Deea222)! - Stop text copied out of the editor from carrying the document's private data.
117
+
118
+ A copy was drawn the way the editor draws itself, so the HTML it left on the clipboard held the paragraph and run XML, the name and recorded identity of a comment's author, what the comment and its replies say, and the body of a footnote.
119
+ All of it landed in whatever application it was pasted into, and in `comment` mode a reader who may not change the body could take the body out this way.
120
+ A copy now carries only what a reader of it needs, and the paragraph style it names is the style's id rather than the whole `w:pPr`.
121
+ A link goes out as a link, so it can be followed where it lands and comes back as one when it is pasted here again.
122
+
123
+ The plain text beside it says what was copied: a tab is a tab, a line break is a line, a page break is a form feed, and a table's cells stand apart by a tab and its rows by a line, so a table pasted into a spreadsheet arrives as a table.
124
+ A cell holding a line break is the exception, since the line inside it reads as the start of the next row.
125
+
126
+ - [#50](https://github.com/portone-io/docx-editor/pull/50) [`41d82c5`](https://github.com/portone-io/docx-editor/commit/41d82c5bee74597baedd813c43c8dcfcdfe74fbf) Thanks [@Deea222](https://github.com/Deea222)! - Stop `onlyCommentsChangedBy` from excusing a part a submission relates as a comment part.
127
+
128
+ The three comment parts are left out of the byte comparison, and which parts those were came from the submitted file's own relationships.
129
+ A file could relate a second comments, extended comments or people part at any part it liked and have that part go uncompared, so a rewritten styles part, a settings part pointing at a template off the package, replaced image bytes or forged document properties were all reported as a change to nothing but comments.
130
+ The parts left out are now the ones the reader opened, a comment part may be related once, one related for the first time has to be a part the submission brought with it, and a relationship part naming one id twice is turned down.
131
+
132
+ A file that relates an extended comments part but carries no comments part now has that part read, so writing the first comment into it writes that part rather than a second one beside it.
133
+ Two extended parts related at once was a file whose settled threads a reader would lose, since only the first of them is read.
134
+
135
+ This affects 0.2.0. A server that accepted files on this verdict should upgrade and run the check again over what it accepted, where such a file now answers `part-changed` naming the part, or `relationship-changed` naming the relationship part.
136
+
137
+ - [#56](https://github.com/portone-io/docx-editor/pull/56) [`467f6f5`](https://github.com/portone-io/docx-editor/commit/467f6f5781dbf6eb6739dbf3369155c7287ee608) Thanks [@Deea222](https://github.com/Deea222)! - Commands and `canRunCommand` now report false where a bookmark marker or a note reference would be removed, instead of reporting true and changing nothing.
138
+
139
+ A refusal over one of those markers also ends an open IME composition, the way a refusal over a locked control already did.
140
+
141
+ - [#59](https://github.com/portone-io/docx-editor/pull/59) [`6d7b97f`](https://github.com/portone-io/docx-editor/commit/6d7b97fdb89e0aba7ac13057b503ec510a5550c1) Thanks [@Deea222](https://github.com/Deea222)! - Formatting XML is now written through shared primitives that consistently escape attribute values while preserving existing formatting behavior.
142
+
143
+ - [#58](https://github.com/portone-io/docx-editor/pull/58) [`d9edb75`](https://github.com/portone-io/docx-editor/commit/d9edb75fa45e1a6cc81e5741a5242cf500107091) Thanks [@Deea222](https://github.com/Deea222)! - Pagination measures every block as a list of break candidates; no visible change
144
+
145
+ - [#55](https://github.com/portone-io/docx-editor/pull/55) [`7110baa`](https://github.com/portone-io/docx-editor/commit/7110baae68b75323720a39374c6df58db4367896) Thanks [@Deea222](https://github.com/Deea222)! - Turn down a raw OOXML fragment that does not hold its shape as it enters the document, rather than writing it into the exported file.
146
+
147
+ Keep imported formatting when a document uses an inherited default namespace or an alternative WordprocessingML prefix. Reject nested namespace rebinding that could disconnect hyperlinks from their targets on export.
148
+
149
+ The editor draws the original XML of a paragraph, a run, a table, an image or an annotation into the page as a `data-` attribute, so that it can read the live DOM back after an IME composition or a browser edit. A fragment written into one of those attributes from outside, by a consumer plugin or through `view.pasteHTML`, is now held to what that attribute goes back out as: a whole element under an expected name, an opening tag's attributes, or an opening tag the writer closes itself. One that does not hold it is turned down along with the rule reading it, so the content settles one level plainer - a paragraph keeps its text and loses its properties, a run loses its mark, a content control or a hyperlink loses its wrapper - instead of a `data-ppr` reading `</w:p><w:p>...` writing a second paragraph into the exported body, or one that never closed reaching export and failing the whole document with `malformed-xml`.
150
+
151
+ - [#54](https://github.com/portone-io/docx-editor/pull/54) [`8aea477`](https://github.com/portone-io/docx-editor/commit/8aea477dceab5462c15903b25b29c738980aff47) Thanks [@Deea222](https://github.com/Deea222)! - An untouched table opened in the editor is written back byte for byte, its `w:tblGridChange` included.
152
+
153
+ Export decided whether a block was untouched by comparing it with the one import produced, attrs and all.
154
+ Opening a document works some of those attrs out again from the formatting around them - a table's shared cell borders among them - so a table nobody had touched compared unequal and was rebuilt, and the rebuild dropped the markup the writer does not model.
155
+
156
+ Each attr now declares whether the writer writes from it, whether the editor works it out for the screen, or whether it identifies something in the open document, and the comparison ignores the ones worked out for the screen.
157
+ Exporting through `createEditorState` is held to the same byte identity as exporting straight from `importDocx`.
158
+
159
+ - [#48](https://github.com/portone-io/docx-editor/pull/48) [`de84006`](https://github.com/portone-io/docx-editor/commit/de840061dd60f3b652273410bc2d0a85b9451ad3) Thanks [@Deea222](https://github.com/Deea222)! - Stop `onlyCommentsChangedBy` from refusing a comment written into a table cell.
160
+
161
+ A commented table is rebuilt on the way out, so it came back worded the way this editor words it while the original carried the wording its producer chose, and the two were compared word for word.
162
+ The story is now compared as this editor writes it back, so two blocks this editor would write alike are read alike: the attribute order inside a `w:tcW` or a `w:tblW`, a percentage width written as `100%` or as `5000`, runs a producer split that say the same text, and a table's `w:tblGridChange`.
163
+ Formatting properties keep the line breaks, comments and text a producer wrote between them, so rewriting a run's bold, a paragraph's alignment or a cell of a rebuilt table no longer drops them.
164
+ A rebuilt table keeps what stood inside its width and span properties too.
165
+
166
+ - [#52](https://github.com/portone-io/docx-editor/pull/52) [`991ba48`](https://github.com/portone-io/docx-editor/commit/991ba48501e20e60bfc69b3c76862f4a26123b3e) Thanks [@Deea222](https://github.com/Deea222)! - Read the comment parts entry by entry in `onlyCommentsChangedBy`.
167
+
168
+ The three parts a comment is written across are the ones a comment edit may rewrite, so the package comparison passes over their bytes.
169
+ Nothing read them afterwards, which let a submission carry a field pointing at a remote image inside a comment body, a comment attributed to a third author that nothing refers to, or markup wrapped around a body, and still be answered as a file where only comments changed.
170
+ Each entry now has to arrive as it was, or be one this editor writes for an author who could have written it, and an entry nothing refers to has to stay as it was.
171
+ A file that fails is refused as `part-changed` naming the comment part.
172
+
173
+ Settling or replying to a comment that arrived with the file no longer rewrites its entry as plain text.
174
+ The entry keeps what it said, and its last paragraph gains the `w14:paraId` the thread state is written against.
175
+ The extended comments part carries an entry only for a comment that has thread state.
176
+
3
177
  ## 0.2.0
4
178
 
5
179
  ### Minor Changes
package/CONTRIBUTING.md CHANGED
@@ -48,12 +48,20 @@ pnpm test:e2e
48
48
 
49
49
  [Testing](https://github.com/portone-io/docx-editor/blob/main/docs/testing.md) explains the scope and prerequisites of every check.
50
50
 
51
+ A command added to `./commands` or `./table` needs a writer probe in `src/docx/__testing__/writerProbes.ts` with a `check` of its immediate effect, so its export is validated before another command can overwrite it, or an entry in `NOT_A_WRITER` giving the reason it reaches no writer.
52
+
51
53
  ## Changesets
52
54
 
53
55
  If you changed anything under `src/`, run `pnpm changeset` and commit the file it writes.
54
56
  It becomes the CHANGELOG entry, so write it for someone reading release notes rather than the diff.
55
- Choose `patch` unless the change adds to or breaks the public API.
57
+
58
+ The public API is the exported names and types of the four entry points - `.`, `./core`, `./commands`, and `./table` - together with the parts of the document model that [What a plugin may rely on](https://docx-editor.portone.io/docs/custom-controls#what-a-plugin-may-rely-on) calls stable.
59
+ An attr that page calls internal is not part of it, and neither is anything under `src/` that no entry point reaches.
60
+
61
+ Choose `patch` unless the change adds to that surface, takes something out of it, or changes what a part of it means.
56
62
  Those are `minor` while the package is below 1.0.
63
+ A declaration added, removed, or rewritten in `etc/*.api.md` after running `pnpm api:update` is what that looks like; a line that only gains or loses an `(undocumented)` marker is not.
64
+ Commit the regenerated report in the same commit as the changeset.
57
65
 
58
66
  By contributing, you agree that your contribution is licensed under the Apache License 2.0 used by this project.
59
67
 
@@ -11,6 +11,7 @@ import type { Plugin } from "prosemirror-state";
11
11
  import type { EditorView } from "prosemirror-view";
12
12
  import { type CSSProperties, type ReactNode } from "react";
13
13
  import { type DocxSource } from "./docx/importDocx";
14
+ import { type ExportProblem } from "./docx/invariants";
14
15
  import { type CommentAuthor } from "./editor/commands/commentCommands";
15
16
  import { DocxImportError } from "./ooxml/errors";
16
17
  import type { EditableComments } from "./schema/protection";
@@ -21,6 +22,11 @@ export interface DocxEditorHandle {
21
22
  view: EditorView;
22
23
  /** Turns the editor state currently on screen into docx bytes */
23
24
  exportBytes: () => Uint8Array;
25
+ /**
26
+ * Every reason `exportBytes` would refuse the state currently on screen, each under the code
27
+ * the refusal would carry; empty when it would write. `downloadDocx` answers `blocked` with it
28
+ */
29
+ exportProblems: () => readonly ExportProblem[];
24
30
  }
25
31
  /**
26
32
  * What the editor is for, which decides what it offers.
@@ -10,16 +10,16 @@ import {
10
10
  } from "react";
11
11
  import { exportDocx } from "./docx/exportDocx.js";
12
12
  import { importDocx } from "./docx/importDocx.js";
13
+ import { exportProblems } from "./docx/invariants.js";
13
14
  import {
14
15
  documentComments
15
16
  } from "./editor/commands/commentCommands.js";
16
17
  import { activeLinkSpan } from "./editor/commands/linkCommands.js";
17
- import { createEditorState, createEditorView } from "./editor/createEditor.js";
18
+ import { createEditorView, editorStateForSession } from "./editor/createEditor.js";
18
19
  import { setProtection } from "./editor/plugins/documentProtection.js";
19
20
  import { isLinkPanelOpen } from "./editor/plugins/linkPanel.js";
20
21
  import { tableMenuAnchor } from "./editor/plugins/tableContextMenu.js";
21
22
  import { textMenuAnchor } from "./editor/plugins/textContextMenu.js";
22
- import { parseNumbering } from "./numbering/parseNumbering.js";
23
23
  import { DocxImportError } from "./ooxml/errors.js";
24
24
  import { PageGuides } from "./page/PageGuides.js";
25
25
  import { A4_PAGE_PIXELS, pagePixels } from "./page/pageLayout.js";
@@ -71,12 +71,7 @@ function affordancesOf(mode) {
71
71
  function openDocument(bytes) {
72
72
  try {
73
73
  const { doc, session } = importDocx(bytes);
74
- return {
75
- status: "opened",
76
- doc,
77
- session,
78
- numbering: parseNumbering(session.numberingXml)
79
- };
74
+ return { status: "opened", doc, session };
80
75
  } catch (error) {
81
76
  if (error instanceof DocxImportError) return { status: "rejected", error };
82
77
  throw error;
@@ -112,6 +107,9 @@ function useDocumentBytes(source) {
112
107
  return read.bytes;
113
108
  }
114
109
  var IMPORT_REJECTION_REASON = {
110
+ // `DOMParser` is built into the browser rather than loaded with the page, so a browser that
111
+ // reaches this line has no XML reader at all and will not have gained one on a second try
112
+ "no-xml-parser": "This browser cannot read documents.",
115
113
  "not-a-docx": "This file is not a docx document, or it is damaged.",
116
114
  "too-large": "This document is too large to open.",
117
115
  "missing-part": "This document is missing the part that holds its body.",
@@ -184,32 +182,13 @@ function DocxEditorSurface({
184
182
  const kept = keptState.current;
185
183
  const view = createEditorView({
186
184
  mount,
187
- state: kept?.of === opened ? kept.state : createEditorState(opened.doc, {
188
- numbering: opened.numbering,
189
- styles: opened.session.styles,
190
- defaults: opened.session.defaults,
191
- paragraphDefaults: opened.session.paragraphDefaults,
192
- canStartNewList: opened.session.numberingPartPath !== null,
185
+ state: kept?.of === opened ? kept.state : editorStateForSession(opened, {
193
186
  consumerPlugins: mountedPlugins,
194
- paragraphStyles: opened.session.paragraphStyles,
195
187
  contextMenus: mountedContextMenus,
196
- geometry: opened.session.geometry,
197
- defaultTabStopPt: opened.session.defaultTabStopPt,
198
- reservedCommentIds: opened.session.comments.byId.keys(),
199
- reservedCommentParaIds: [
200
- ...opened.session.comments.ordered.flatMap(
201
- (comment) => comment.paraId === null ? [] : [comment.paraId]
202
- ),
203
- ...opened.session.comments.extendedOrdered.map(
204
- (extension) => extension.paraId
205
- )
206
- ],
207
188
  protection,
208
189
  author,
209
190
  editableComments
210
191
  }),
211
- defaults: opened.session.defaults,
212
- geometry: opened.session.geometry,
213
192
  fontFallbacks: mountedFontFallbacks,
214
193
  onStateChange: (state) => {
215
194
  keptState.current = { of: opened, state };
@@ -252,7 +231,11 @@ function DocxEditorSurface({
252
231
  const view = viewRef.current;
253
232
  if (!view || opened?.status !== "opened") return null;
254
233
  const session = opened.session;
255
- return { view, exportBytes: () => exportDocx(view.state.doc, session) };
234
+ return {
235
+ view,
236
+ exportBytes: () => exportDocx(view.state.doc, session),
237
+ exportProblems: () => exportProblems(view.state.doc, session)
238
+ };
256
239
  }, [opened]);
257
240
  const overlay = usePageLayout({
258
241
  view: live?.view ?? null,
package/dist/core.d.ts CHANGED
@@ -1,20 +1,33 @@
1
1
  /**
2
2
  * Framework-free DOCX import and export API. The opaque session preserves package parts between
3
- * calls; server runtimes must provide global `DOMParser` and `Node` implementations.
3
+ * calls.
4
+ *
5
+ * Reading a package needs an XML parser. A browser has one; anywhere else, hand one in as
6
+ * `xmlParser` or install a `DOMParser` global, or the call is refused with the import code
7
+ * `no-xml-parser`.
4
8
  */
5
9
  import type { Node as PMNode } from "prosemirror-model";
6
- import { type DocxBytes } from "./docx/importDocx";
10
+ import type { FidelityNote } from "./docx/fidelity";
11
+ import { type DocxBytes, type ImportOptions } from "./docx/importDocx";
7
12
  import type { DocxSession } from "./docx/session";
8
13
  export type { CommentOnlyVerdict } from "./docx/commentOnlyChange";
9
14
  export { onlyCommentsChangedBy } from "./docx/commentOnlyChange";
10
- export { exportDocx } from "./docx/exportDocx";
15
+ export type { ExportOptions } from "./docx/exportDocx";
16
+ export { exportDocx, exportDocxReport } from "./docx/exportDocx";
17
+ export type { FidelityCode, FidelityNote, FidelitySeverity, } from "./docx/fidelity";
11
18
  export type { ParagraphStyleOption } from "./docx/formatting";
12
- export type { DocxBytes } from "./docx/importDocx";
19
+ export type { DocxBytes, ImportOptions } from "./docx/importDocx";
20
+ /**
21
+ * What the writer would refuse the document over, asked ahead of the write. The first entry is
22
+ * what `exportDocx` throws, so the two cannot disagree.
23
+ */
24
+ export type { ExportProblem } from "./docx/invariants";
25
+ export { exportProblems } from "./docx/invariants";
13
26
  export type { DocxSession } from "./docx/session";
14
27
  export { documentNumbering, documentPartPath } from "./docx/session";
15
28
  export type { CellFormat, CellVerticalAlign, DocumentDefaults, HighlightName, LineSpacing, NumberingRef, ParagraphAlign, ParagraphFormat, RowFormat, RowHeight, RunFormat, TableFormat, TableWidth, TableWidthType, UnderlineKind, VerticalAlign, } from "./model/format";
16
29
  export { toCellFormat, toParagraphFormat, toRowFormat, toRunFormat, toTableFormat, toTableWidth, } from "./model/format";
17
- export type { LevelIndent, NumberFormat, Numbering, NumberingLevel, NumberingList, } from "./numbering/parseNumbering";
30
+ export type { LevelIndent, NumberFormat, Numbering, NumberingLevel, NumberingList, NumberingOptions, } from "./numbering/parseNumbering";
18
31
  export { parseNumbering } from "./numbering/parseNumbering";
19
32
  export type { DocxExportErrorCode, DocxImportErrorCode, } from "./ooxml/errors";
20
33
  export { DocxExportError, DocxImportError } from "./ooxml/errors";
@@ -24,15 +37,17 @@ export { DocxExportError, DocxImportError } from "./ooxml/errors";
24
37
  */
25
38
  export type { ImageExtent } from "./ooxml/image";
26
39
  export { emuToPx, pxToEmu, toImageExtent } from "./ooxml/image";
40
+ export type { XmlParser } from "./ooxml/xml";
27
41
  export { docxSchema } from "./schema";
28
42
  /**
29
- * Opens docx bytes into the document to work on and the session that remembers the file they came
30
- * from.
43
+ * Opens docx bytes into the document to work on, the session that remembers the file they came
44
+ * from, and what the file holds that this editor could not model.
31
45
  *
32
46
  * The engine hands out the store it fills in; this is where it narrows to the opaque session, so
33
47
  * that the original XML behind it stays the exporter's business.
34
48
  */
35
- export declare function importDocx(input: DocxBytes): {
49
+ export declare function importDocx(input: DocxBytes, options?: ImportOptions): {
36
50
  doc: PMNode;
37
51
  session: DocxSession;
52
+ notes: FidelityNote[];
38
53
  };
package/dist/core.js CHANGED
@@ -1,7 +1,10 @@
1
1
  // src/core.ts
2
- import { importDocx as openDocx } from "./docx/importDocx.js";
2
+ import {
3
+ importDocx as openDocx
4
+ } from "./docx/importDocx.js";
3
5
  import { onlyCommentsChangedBy } from "./docx/commentOnlyChange.js";
4
- import { exportDocx } from "./docx/exportDocx.js";
6
+ import { exportDocx, exportDocxReport } from "./docx/exportDocx.js";
7
+ import { exportProblems } from "./docx/invariants.js";
5
8
  import { documentNumbering, documentPartPath } from "./docx/session.js";
6
9
  import {
7
10
  toCellFormat,
@@ -15,8 +18,8 @@ import { parseNumbering } from "./numbering/parseNumbering.js";
15
18
  import { DocxExportError, DocxImportError } from "./ooxml/errors.js";
16
19
  import { emuToPx, pxToEmu, toImageExtent } from "./ooxml/image.js";
17
20
  import { docxSchema } from "./schema/index.js";
18
- function importDocx(input) {
19
- return openDocx(input);
21
+ function importDocx(input, options) {
22
+ return openDocx(input, options);
20
23
  }
21
24
  export {
22
25
  DocxExportError,
@@ -26,6 +29,8 @@ export {
26
29
  docxSchema,
27
30
  emuToPx,
28
31
  exportDocx,
32
+ exportDocxReport,
33
+ exportProblems,
29
34
  importDocx,
30
35
  onlyCommentsChangedBy,
31
36
  parseNumbering,
@@ -0,0 +1,38 @@
1
+ /**
2
+ * What a node an edit makes out of another node carries over from it.
3
+ *
4
+ * A paragraph built from another one - either half of the two Enter leaves behind, or a copy - used
5
+ * to arrive holding every attr of the original. Two paragraphs then claimed the same `w14:paraId`,
6
+ * and a paragraph-level `w:sectPr` went out twice, which
7
+ * gives the document a section break it never had.
8
+ *
9
+ * `CLONE_POLICIES` is the one place that answers what becomes of each attr, and it answers per
10
+ * side: the half a split leaves standing is not the half it makes, and a free-standing copy is
11
+ * neither. What the answer turns on is whether the attr names the node - an identifier, or the
12
+ * block it was opened from - or describes it. A name may be held by one node only; a description
13
+ * is free to be copied.
14
+ */
15
+ import type { Attrs, Node as PMNode } from "prosemirror-model";
16
+ /** Which half of a split the clone becomes, or a free-standing copy */
17
+ export type CloneSide = "before" | "after" | "copy";
18
+ export interface ClonePolicy {
19
+ /** The attrs a node made from `source` carries when it stands on `side` */
20
+ attrs(source: PMNode, side: CloneSide): Attrs;
21
+ }
22
+ /**
23
+ * The opening tag's attributes without the paragraph identifiers.
24
+ *
25
+ * A paragraph carrying none goes back out spelled exactly as it arrived, and so does one whose
26
+ * attribute text cannot be made out, which is left alone rather than written again from a guess.
27
+ */
28
+ export declare function withoutParagraphIds(value: unknown): unknown;
29
+ /** One policy per node type that an edit can make from another node */
30
+ export declare const CLONE_POLICIES: Readonly<Record<"paragraph" | "tableCell" | "tableRow", ClonePolicy>>;
31
+ /**
32
+ * The two halves Enter leaves behind. `before` keeps the text, `after` keeps the paragraph mark's
33
+ * section break
34
+ */
35
+ export declare function splitParagraphAttrs(parent: PMNode): {
36
+ before: Attrs;
37
+ after: Attrs;
38
+ };
@@ -0,0 +1,74 @@
1
+ // src/docx/cloning.ts
2
+ import { attrsText, withoutAttrs } from "../ooxml/element.js";
3
+ import { parseProps, renderProps, setChild } from "../ooxml/props.js";
4
+ import { parseAttrs } from "../ooxml/tagScan.js";
5
+ var carried = (value) => value;
6
+ var defaulted = () => void 0;
7
+ var PARAGRAPH_ID_ATTRS = ["paraId", "textId"];
8
+ function withoutParagraphIds(value) {
9
+ if (typeof value !== "string") return value;
10
+ const attrs = parseAttrs(value);
11
+ if (attrs === null) return value;
12
+ const kept = withoutAttrs(attrs, PARAGRAPH_ID_ATTRS, "w14");
13
+ if (kept.length === attrs.length) return value;
14
+ return kept.length === 0 ? null : attrsText(kept);
15
+ }
16
+ function withoutSectionBreak(value) {
17
+ if (typeof value !== "string") return value;
18
+ const props = parseProps(value);
19
+ if (props === null) return value;
20
+ const without = setChild(props, "sectPr", null);
21
+ if (without.children.length === props.children.length) return value;
22
+ return renderProps(without) || null;
23
+ }
24
+ var INHERITED_CELL_ATTRS = ["tcAttrs", "tcPr", "tcW", "format"];
25
+ var INHERITED_ROW_ATTRS = ["trAttrs", "trPr", "format"];
26
+ function inherits(names) {
27
+ return Object.fromEntries(names.map((name) => [name, carried]));
28
+ }
29
+ function policy(rules, rest) {
30
+ return {
31
+ attrs(source, side) {
32
+ const attrs = {};
33
+ for (const [name, value] of Object.entries(source.attrs)) {
34
+ const held = (rules[name] ?? rest)(value, side);
35
+ if (held !== void 0) attrs[name] = held;
36
+ }
37
+ return attrs;
38
+ }
39
+ };
40
+ }
41
+ var CLONE_POLICIES = {
42
+ paragraph: policy(
43
+ {
44
+ // The half that carries on where the original stood keeps the original's name. The one the
45
+ // edit made is a new paragraph and goes out with none, so no two ever claim the same
46
+ // paragraph identity.
47
+ pAttrs: (value, side) => side === "before" ? value : withoutParagraphIds(value),
48
+ // A paragraph-level `w:sectPr` ends the section it stands in, so it belongs to whichever
49
+ // paragraph ends up last in that section: the second half of a split, and no half of a copy,
50
+ // which is lifted out of the section it was taken from.
51
+ pPr: (value, side) => side === "after" ? value : withoutSectionBreak(value),
52
+ // What names the block this paragraph was opened from. Only the half still standing where
53
+ // that block stood may go on claiming it; `docx/exportDocx` writes the original bytes back
54
+ // for whatever claims one.
55
+ srcId: (value, side) => side === "before" ? value : void 0
56
+ },
57
+ // `format` and `styleRun` among them: display values `editor/plugins/paragraphDisplay` derives
58
+ // again from the formatting context, which nothing compares and nothing writes out.
59
+ carried
60
+ ),
61
+ tableCell: policy(inherits(INHERITED_CELL_ATTRS), defaulted),
62
+ tableRow: policy(inherits(INHERITED_ROW_ATTRS), defaulted)
63
+ };
64
+ function splitParagraphAttrs(parent) {
65
+ return {
66
+ before: CLONE_POLICIES.paragraph.attrs(parent, "before"),
67
+ after: CLONE_POLICIES.paragraph.attrs(parent, "after")
68
+ };
69
+ }
70
+ export {
71
+ CLONE_POLICIES,
72
+ splitParagraphAttrs,
73
+ withoutParagraphIds
74
+ };
@@ -7,11 +7,13 @@
7
7
  * free to rewrite anything the story does not carry - the paper the document is written on, its
8
8
  * styles, its headers - and a document comparison would see none of it.
9
9
  */
10
- import { type EditableComments } from "../schema/protection";
11
- import { type DocxBytes } from "./importDocx";
10
+ import type { XmlParser } from "../ooxml/xml";
11
+ import type { EditableComments } from "../schema/protection";
12
+ import type { DocxBytes } from "./importDocx";
12
13
  /**
13
- * Why a file is not the one it claims to be. `part-changed` and `relationship-changed` name the
14
- * part they were reached over; the other three are about the document story itself.
14
+ * Why a file is not the one it claims to be. `part-changed`, `relationship-changed` and
15
+ * `comment-markup-rejected` name the part they were reached over; the other three are about the
16
+ * document story itself.
15
17
  */
16
18
  export type CommentOnlyVerdict = {
17
19
  ok: true;
@@ -20,7 +22,7 @@ export type CommentOnlyVerdict = {
20
22
  reason: "body-changed" | "comment-not-owned" | "comment-author-forged";
21
23
  } | {
22
24
  ok: false;
23
- reason: "part-changed" | "relationship-changed";
25
+ reason: "part-changed" | "relationship-changed" | "comment-markup-rejected";
24
26
  part: string;
25
27
  };
26
28
  /**
@@ -29,17 +31,22 @@ export type CommentOnlyVerdict = {
29
31
  *
30
32
  * Every part of the package has to arrive as it left, save for the three a comment is written
31
33
  * across and the relationship and content type they are declared with; the document story itself
32
- * has to read as it did, comments aside. A comment carrying no recorded identity is everyone's to
34
+ * has to read as it did, comments aside. Those three parts are read entry by entry instead
35
+ * (`./comments/policy`), since a comment edit is free to rewrite them and something has to say
36
+ * what it may have written there. A comment carrying no recorded identity is everyone's to
33
37
  * edit here as it is in the editor (`schema/protection`), while a comment that appeared has to
34
38
  * carry this identity: a file can claim any author, and the editor's own hand in writing it is
35
39
  * not there to vouch for it. An identity already recorded is nobody's to rewrite.
36
40
  *
37
41
  * `editableComments: "all"` judges the file of an editor opened for a moderator, where every
38
42
  * comment was theirs to edit; an identity is nobody's to rewrite under either setting.
43
+ * `xmlParser` names the parser both files are read through, for a runtime that holds no
44
+ * `DOMParser` global of its own.
39
45
  *
40
46
  * Bytes that are not a readable docx are turned down the way opening one is, with a
41
47
  * `DocxImportError`, rather than being answered as a file that changed.
42
48
  */
43
- export declare function onlyCommentsChangedBy(original: DocxBytes, submitted: DocxBytes, authorId: string, { editableComments }?: {
49
+ export declare function onlyCommentsChangedBy(original: DocxBytes, submitted: DocxBytes, authorId: string, { editableComments, xmlParser, }?: {
44
50
  editableComments?: EditableComments;
51
+ xmlParser?: XmlParser;
45
52
  }): CommentOnlyVerdict;