@portone/docx-editor 0.4.0 → 0.5.1

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 (180) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/CONTRIBUTING.md +2 -0
  3. package/assets/editor.png +0 -0
  4. package/dist/DocxEditor.d.ts +1 -1
  5. package/dist/DocxEditor.js +44 -25
  6. package/dist/docx/cloning.js +4 -9
  7. package/dist/docx/commentOnlyChange.d.ts +5 -3
  8. package/dist/docx/comments/grammar.d.ts +27 -10
  9. package/dist/docx/comments/grammar.js +18 -67
  10. package/dist/docx/comments/model.d.ts +5 -6
  11. package/dist/docx/comments/model.js +0 -9
  12. package/dist/docx/comments/parts.d.ts +16 -10
  13. package/dist/docx/comments/parts.js +65 -13
  14. package/dist/docx/comments/people.d.ts +11 -3
  15. package/dist/docx/comments/people.js +20 -6
  16. package/dist/docx/comments/policy.js +7 -1
  17. package/dist/docx/comments/reading.d.ts +4 -2
  18. package/dist/docx/comments/reading.js +1 -14
  19. package/dist/docx/comments/writing.js +65 -28
  20. package/dist/docx/exportDocx.js +10 -14
  21. package/dist/docx/exportRefs.d.ts +8 -3
  22. package/dist/docx/exportRefs.js +2 -1
  23. package/dist/docx/fidelity.js +24 -10
  24. package/dist/docx/fields.d.ts +42 -0
  25. package/dist/docx/fields.js +76 -0
  26. package/dist/docx/formatting/styles.d.ts +0 -2
  27. package/dist/docx/formatting/styles.js +2 -15
  28. package/dist/docx/headersFooters.d.ts +89 -20
  29. package/dist/docx/headersFooters.js +163 -172
  30. package/dist/docx/identities.js +17 -26
  31. package/dist/docx/importDocx.d.ts +1 -1
  32. package/dist/docx/importDocx.js +100 -48
  33. package/dist/docx/importParagraph.d.ts +18 -5
  34. package/dist/docx/importParagraph.js +75 -128
  35. package/dist/docx/importPolicy.d.ts +70 -0
  36. package/dist/docx/importPolicy.js +195 -0
  37. package/dist/docx/importPreserved.d.ts +30 -0
  38. package/dist/docx/importPreserved.js +54 -0
  39. package/dist/docx/importTable.js +56 -12
  40. package/dist/docx/invariants.js +16 -18
  41. package/dist/docx/notes.d.ts +6 -1
  42. package/dist/docx/notes.js +6 -16
  43. package/dist/docx/pageGeometry.d.ts +5 -14
  44. package/dist/docx/pageGeometry.js +0 -12
  45. package/dist/docx/protectionPolicy.d.ts +13 -5
  46. package/dist/docx/protectionPolicy.js +32 -22
  47. package/dist/docx/scan.d.ts +18 -8
  48. package/dist/docx/scan.js +17 -11
  49. package/dist/docx/sections.d.ts +126 -0
  50. package/dist/docx/sections.js +207 -0
  51. package/dist/docx/serializeBlock.d.ts +13 -4
  52. package/dist/docx/serializeBlock.js +16 -18
  53. package/dist/docx/serializeParagraph.d.ts +9 -4
  54. package/dist/docx/serializeParagraph.js +23 -58
  55. package/dist/docx/serializePreserved.d.ts +14 -0
  56. package/dist/docx/serializePreserved.js +24 -0
  57. package/dist/docx/serializeStory.d.ts +17 -0
  58. package/dist/docx/serializeStory.js +17 -0
  59. package/dist/docx/serializeTable.js +10 -13
  60. package/dist/docx/session.d.ts +23 -6
  61. package/dist/docx/session.js +26 -5
  62. package/dist/docx/story.d.ts +140 -0
  63. package/dist/docx/story.js +237 -0
  64. package/dist/docx/storyProjection.js +6 -1
  65. package/dist/docx/tableTemplate.d.ts +25 -0
  66. package/dist/docx/tableTemplate.js +30 -16
  67. package/dist/docx/wrappers.d.ts +67 -0
  68. package/dist/docx/wrappers.js +117 -0
  69. package/dist/editor/clipboard/blockReaders.d.ts +74 -0
  70. package/dist/editor/clipboard/blockReaders.js +193 -0
  71. package/dist/editor/clipboard/htmlReader.d.ts +26 -0
  72. package/dist/editor/clipboard/htmlReader.js +376 -0
  73. package/dist/editor/clipboard/inlineFormatting.d.ts +0 -7
  74. package/dist/editor/clipboard/inlineFormatting.js +1 -7
  75. package/dist/editor/clipboard/internalChannel.d.ts +57 -0
  76. package/dist/editor/clipboard/internalChannel.js +58 -0
  77. package/dist/editor/clipboard/normalizers.d.ts +99 -0
  78. package/dist/editor/clipboard/normalizers.js +199 -0
  79. package/dist/editor/clipboard/parser.d.ts +34 -0
  80. package/dist/editor/clipboard/parser.js +58 -0
  81. package/dist/editor/clipboard/plugin.d.ts +21 -0
  82. package/dist/editor/clipboard/plugin.js +188 -0
  83. package/dist/editor/clipboard/readContext.d.ts +34 -0
  84. package/dist/editor/clipboard/readContext.js +25 -0
  85. package/dist/editor/clipboard/readers.d.ts +33 -0
  86. package/dist/editor/clipboard/readers.js +37 -0
  87. package/dist/editor/clipboard/source.d.ts +12 -0
  88. package/dist/editor/clipboard/source.js +21 -0
  89. package/dist/editor/commands/comments/editing.d.ts +26 -6
  90. package/dist/editor/commands/comments/editing.js +177 -106
  91. package/dist/editor/commands/comments/model.d.ts +34 -17
  92. package/dist/editor/commands/comments/model.js +11 -0
  93. package/dist/editor/commands/comments/reading.d.ts +5 -3
  94. package/dist/editor/commands/comments/reading.js +8 -58
  95. package/dist/editor/commands/index.d.ts +2 -2
  96. package/dist/editor/commands/index.js +2 -0
  97. package/dist/editor/commands/linkCommands.js +20 -5
  98. package/dist/editor/commands/listCommands.d.ts +6 -0
  99. package/dist/editor/commands/listCommands.js +3 -2
  100. package/dist/editor/commands/lockCommands.js +59 -16
  101. package/dist/editor/commands/noteQueries.d.ts +22 -5
  102. package/dist/editor/commands/noteQueries.js +34 -6
  103. package/dist/editor/commands/paragraphCommands.js +2 -2
  104. package/dist/editor/createEditor.js +13 -3
  105. package/dist/editor/documentStyles.d.ts +26 -4
  106. package/dist/editor/documentStyles.js +9 -4
  107. package/dist/editor/imageFiles.d.ts +2 -2
  108. package/dist/editor/imageFiles.js +2 -0
  109. package/dist/editor/insertTable.d.ts +1 -1
  110. package/dist/editor/insertTable.js +2 -2
  111. package/dist/editor/plainText.d.ts +12 -0
  112. package/dist/editor/plainText.js +2 -1
  113. package/dist/editor/plugins/columnResize.js +6 -3
  114. package/dist/editor/plugins/commentComposer.d.ts +28 -0
  115. package/dist/editor/plugins/commentComposer.js +46 -0
  116. package/dist/editor/plugins/commentDecorations.d.ts +29 -2
  117. package/dist/editor/plugins/commentDecorations.js +89 -15
  118. package/dist/editor/plugins/documentProjection.d.ts +31 -0
  119. package/dist/editor/plugins/documentProjection.js +22 -0
  120. package/dist/editor/plugins/imagePaste.js +15 -8
  121. package/dist/editor/plugins/linkPanel.d.ts +7 -3
  122. package/dist/editor/plugins/linkPanel.js +13 -27
  123. package/dist/editor/plugins/panelState.d.ts +67 -0
  124. package/dist/editor/plugins/panelState.js +54 -0
  125. package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
  126. package/dist/editor/plugins/tableContextMenu.js +30 -47
  127. package/dist/editor/plugins/textContextMenu.d.ts +1 -1
  128. package/dist/editor/plugins/textContextMenu.js +30 -47
  129. package/dist/ooxml/fragment.d.ts +11 -0
  130. package/dist/ooxml/fragment.js +14 -4
  131. package/dist/ooxml/image.d.ts +3 -4
  132. package/dist/ooxml/image.js +50 -5
  133. package/dist/ooxml/names.d.ts +6 -0
  134. package/dist/ooxml/names.js +2 -0
  135. package/dist/ooxml/props.d.ts +10 -0
  136. package/dist/ooxml/props.js +28 -4
  137. package/dist/ooxml/rangeMarkers.d.ts +5 -0
  138. package/dist/ooxml/rangeMarkers.js +24 -0
  139. package/dist/page/PageGuides.d.ts +7 -3
  140. package/dist/page/PageGuides.js +13 -24
  141. package/dist/page/pageLayout.d.ts +64 -5
  142. package/dist/page/pageLayout.js +90 -29
  143. package/dist/page/usePageLayout.d.ts +14 -4
  144. package/dist/page/usePageLayout.js +19 -20
  145. package/dist/schema/attrRoles.js +46 -23
  146. package/dist/schema/clipboard.d.ts +72 -0
  147. package/dist/schema/clipboard.js +288 -0
  148. package/dist/schema/docxSchema.d.ts +9 -1
  149. package/dist/schema/docxSchema.js +217 -165
  150. package/dist/schema/editGuard.d.ts +1 -1
  151. package/dist/schema/guards.js +2 -2
  152. package/dist/schema/index.d.ts +1 -0
  153. package/dist/schema/locks.d.ts +3 -0
  154. package/dist/schema/locks.js +29 -12
  155. package/dist/schema/preservedFragments.d.ts +12 -0
  156. package/dist/schema/preservedFragments.js +38 -0
  157. package/dist/schema/preservedGuards.d.ts +9 -7
  158. package/dist/schema/preservedGuards.js +112 -11
  159. package/dist/schema/protection.d.ts +34 -5
  160. package/dist/schema/protection.js +42 -14
  161. package/dist/schema/rendering.d.ts +14 -0
  162. package/dist/schema/rendering.js +27 -12
  163. package/dist/schema/stories.d.ts +49 -0
  164. package/dist/schema/stories.js +78 -0
  165. package/dist/schema/wrappers.d.ts +58 -0
  166. package/dist/schema/wrappers.js +77 -0
  167. package/dist/styles/classNames.d.ts +3 -5
  168. package/dist/styles/classNames.js +3 -5
  169. package/dist/styles.css +74 -12
  170. package/dist/table/resize.d.ts +5 -3
  171. package/dist/ui/CommentsPanel.d.ts +1 -3
  172. package/dist/ui/CommentsPanel.js +24 -15
  173. package/dist/ui/NotesPanel.js +2 -2
  174. package/dist/ui/TextMenu.d.ts +1 -3
  175. package/dist/ui/TextMenu.js +10 -6
  176. package/dist/ui/comments/CommentComposer.d.ts +6 -1
  177. package/dist/ui/comments/CommentComposer.js +26 -3
  178. package/package.json +5 -4
  179. package/dist/editor/externalClipboard.d.ts +0 -22
  180. package/dist/editor/externalClipboard.js +0 -486
package/CHANGELOG.md CHANGED
@@ -1,5 +1,206 @@
1
1
  # @portone/docx-editor
2
2
 
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#125](https://github.com/portone-io/docx-editor/pull/125) [`88ff33a`](https://github.com/portone-io/docx-editor/commit/88ff33abac21cd56d87723af60d95ab597ccff39) Thanks [@Deea222](https://github.com/Deea222)! - Pasting from Word, Google Docs or LibreOffice now keeps tables as tables and Word lists as lists.
8
+
9
+ A table on the clipboard used to arrive as a single paragraph holding the text of every cell run
10
+ together. It now arrives as a table: one paragraph for each block a cell held, the columns and rows
11
+ a cell reached across kept, and the same width and lines a table inserted here has.
12
+
13
+ Word writes list items as ordinary paragraphs that name the list they belong to and draw their own
14
+ bullet or number, so pasting one used to give unnumbered paragraphs each beginning with a stray
15
+ marker. The items of one Word list now join one list, at the level Word gives them, counted when
16
+ the marker Word drew counts.
17
+
18
+ A table's caption is kept as the paragraph above it, a table inside a list item is read as a table
19
+ rather than as the item's own text, and a table too large for the editor to hold is read as one
20
+ paragraph per row instead of as a single run-on one.
21
+
22
+ A copy wrapped in a single element, which is how Google Docs writes one, no longer has its
23
+ paragraphs pressed into one.
24
+
25
+ - [#123](https://github.com/portone-io/docx-editor/pull/123) [`3d6b0a5`](https://github.com/portone-io/docx-editor/commit/3d6b0a58ea9e0a5ca651a5579d5dd0a8b4646ff6) Thanks [@Deea222](https://github.com/Deea222)! - Copied HTML and text now follow one declared outbound shape per node and mark, so what leaves the editor is decided beside the schema rather than filtered out of the page's own drawing afterwards. A copy pasted into another application carries paragraphs, styles, runs, links, images at the size they were drawn, and tables as tables, and none of the document's internals. A comment marker, a bookmark, and the placeholder standing for content the editor could not model now leave nothing behind them, where before they left an element carrying the editor's own name for what stood there.
26
+
27
+ Copied plain text reads as the page does. A table pasted into a spreadsheet keeps one row per row, with everything a cell holds on the one line that cell stands on, and a block the editor only kept no longer opens a blank line where it stood. A kept line break and a kept character, such as a no-break hyphen, travel with the text they stand in, and so does the text a placeholder draws: a field's cached result and the words of a tracked insertion read in a copy as they read on the page.
28
+
29
+ - [#124](https://github.com/portone-io/docx-editor/pull/124) [`936b2a0`](https://github.com/portone-io/docx-editor/commit/936b2a08d0d26b1a9b77cf39c718bb85b70a834c) Thanks [@Deea222](https://github.com/Deea222)! - Hyperlinks that hold content controls, and nested controls, open as editable text.
30
+
31
+ Only one of the arrangements used to be readable - a control holding a link. A link holding a
32
+ control, or a control holding a control, brought the inner one in as a small box naming the element
33
+ it stood for, and the text it held could not be typed in. Any depth and any order of the two is now
34
+ ordinary text wearing a wrapper each, and goes back out nested as it came. A hyperlink inside a
35
+ hyperlink is the one arrangement still kept whole, since a link marks the text it covers once.
36
+
37
+ A link made on text inside a control is written inside that control, and a lock put on a stretch
38
+ that sits wholly inside a link is written inside that link, instead of either wrapper being split
39
+ around the other. Where a control holds a control, locking shuts the outer one and lifting a lock
40
+ opens the inner one the selection stands in.
41
+
42
+ Plugins reading the drawn page will find `data-key` and `data-depth` on a control and a link in
43
+ place of `data-sdt-key` and `data-link-key`.
44
+
45
+ ## 0.5.0
46
+
47
+ ### Minor Changes
48
+
49
+ - [#105](https://github.com/portone-io/docx-editor/pull/105) [`efe7dbf`](https://github.com/portone-io/docx-editor/commit/efe7dbf6f2cb260ee912dbaed4c518c11b3527b7) Thanks [@Deea222](https://github.com/Deea222)! - Keep markup the editor cannot model where it stood, instead of standing the paragraph around it down.
50
+
51
+ Opening a document used to walk a handful of element names and give up on the rest, and giving up
52
+ travelled outward until a whole paragraph became an "Unsupported content" placeholder. A word
53
+ processor writes a `w:lastRenderedPageBreak` on every page it lays out, so a real document opened
54
+ with one locked paragraph per page.
55
+
56
+ Every level of the document now has a preservation rule of its own. A run child stays inside its
57
+ run, a paragraph child beside the runs, a block stays a block, and only a marker or a wrapper
58
+ standing between the rows or the cells of a table - where there is no node to keep it in - still
59
+ stands the table down. What is left shows as a small box naming the element it stands for, or as
60
+ nothing where the file drew nothing, and the paragraph around it stays editable.
61
+
62
+ - A field code and its drawn result, a tracked insertion or deletion, a symbol, a positional tab
63
+ and a content control the editor could not take apart are visible where they stand.
64
+ - Bookmarks, permission ranges, the markers of a moved passage and the pieces of a field cannot be
65
+ removed by an edit; a tracked-change container, a simple field and a symbol can be selected and
66
+ deleted whole.
67
+ - `documentFidelity` and the `notes` of `importDocx` now report `preserved-run-content`, and no
68
+ longer report `paragraph-demoted` for a document being opened.
69
+ - New node `rawRunContent`, and `rawInline` gains the attrs `element`, `display`, `text` and
70
+ `guarded`. A plugin reading the document model sees both.
71
+ - A footnote body, a comment body and a header now read a `w:cr` and a `w:noBreakHyphen` the way
72
+ the document body does.
73
+
74
+ - [#106](https://github.com/portone-io/docx-editor/pull/106) [`f2871cd`](https://github.com/portone-io/docx-editor/commit/f2871cd5535780c10574906c9dceb2b3ecc4715c) Thanks [@Deea222](https://github.com/Deea222)! - The comment composer keeps the text it was opened over while the document is edited elsewhere, and closes when that text is deleted or the mode turns read-only. The comment is written on that text rather than on whatever is selected when the writer submits.
75
+
76
+ Opening the composer no longer scrolls the document back to its first page.
77
+
78
+ `addComment` and `canAddComment` take an optional `{ from, to }` range as their last argument, which a composer of your own can hold across edits the same way; the current selection is still used when none is given.
79
+
80
+ - [#116](https://github.com/portone-io/docx-editor/pull/116) [`4d9964d`](https://github.com/portone-io/docx-editor/commit/4d9964d59ce66ce52dac06b86968b8ee20dc4f2d) Thanks [@Deea222](https://github.com/Deea222)! - Comment bodies keep their formatting when edited, and `setCommentBody` accepts a formatted body.
81
+
82
+ **Breaking:** the `commentReference` node loses its `text`, `commentXml` and `imported` attrs and
83
+ the `noteReference` node loses its `text` attr, together with the `data-comment-text`,
84
+ `data-comment-xml`, `data-comment-imported` and `data-note-text` attributes that carried them into
85
+ the DOM. A plugin that matched any of those reads what a comment or a note says through
86
+ `documentComments` and `documentNotes` instead, which answer as they always did.
87
+
88
+ **Breaking:** `DocumentComment`, `DocumentCommentReply` and `DocumentNote` declare every field
89
+ `readonly`. They are the editor's own records, worked out once per edit and handed out rather than
90
+ copied, so a caller that was writing into one copies it first.
91
+
92
+ A side story - a comment's body, a footnote's - is now read the way the document body is: the same
93
+ block readers, the same verbatim slices, one document of the editor's own schema per story. It
94
+ stands on the document node under `doc.attrs.stories`, so an edit to it rides a transaction and
95
+ lands in the history, and everything the edit did not touch is written back as the bytes it arrived
96
+ as. Editing a comment used to flatten its body to plain text and rewrite it as one run, which lost
97
+ its bold, its paragraph style and its second paragraph.
98
+
99
+ `setCommentBody(id, body)` is a new command on `./commands`. It takes a `doc` node of `docxSchema`,
100
+ which is what a composer of your own builds a formatted body as; `updateComment(id, text)` stays
101
+ what it was for a body that is only text.
102
+
103
+ Dropping those attrs is what the story replaces them with: what a comment or a note says is no
104
+ longer written on the node marking where it stands, so nothing has to keep the two in step, a copy
105
+ of the node no longer carries the body along with it, and neither reaches the clipboard.
106
+
107
+ - [#108](https://github.com/portone-io/docx-editor/pull/108) [`d783e24`](https://github.com/portone-io/docx-editor/commit/d783e2485eaf6bbbacc3498202019ab053f4e7da) Thanks [@Deea222](https://github.com/Deea222)! - Keep a table whose markers stand between its rows or its cells, and stand every block the editor
108
+ cannot model as one kind of placeholder.
109
+
110
+ **Breaking:** the `docxRaw` and `bookmarkBlock` node types no longer exist. Both were placeholders
111
+ for a block nobody could read, differing only in where the block stood and whether anything of it
112
+ was drawn, and `rawBlock` is now the one node for all of them. A plugin that matched either name
113
+ matches `rawBlock` instead and reads `display` (`"chip"` for a placeholder box, `"hidden"` for a
114
+ marker that draws nothing) and `guarded` to tell them apart. The class names
115
+ `docx-editor-bookmark-block`, `docx-editor-raw-xml` and `docx-editor-table` are gone with them;
116
+ every placeholder now draws as `docx-editor-raw-block`.
117
+
118
+ - A bookmark spanning a table column stands under the row rather than inside a cell, and used to
119
+ cost the whole table its structure. Such a table now opens as a table, and the marker goes back
120
+ exactly where it stood. A marker following a cell that only continues a vertical merge is the
121
+ remaining exception, since that cell is created fresh on export.
122
+ - `documentFidelity` and the `notes` of `importDocx` no longer report `table-demoted` for a table
123
+ whose only unread markup is a marker.
124
+ - A placeholder can now be moved between the body and a table cell and still export.
125
+ - `table` and `tableRow` gain a `leadingXml` attr, `tableRow` and `tableCell` a `trailingXml` attr:
126
+ the markers each carries between its children.
127
+
128
+ Table markers remain protected against deletion, duplication and reordering, including markers
129
+ whose ranges cross into a cell paragraph. Export checks read those fragments in document order.
130
+
131
+ ### Patch Changes
132
+
133
+ - [#109](https://github.com/portone-io/docx-editor/pull/109) [`05e9431`](https://github.com/portone-io/docx-editor/commit/05e9431c5ba773fa9f751860cbf34760b191d91a) Thanks [@Deea222](https://github.com/Deea222)! - Cache comment and note lists between document edits. Public readers continue to return independent comment, reply, and note records, so changes to their results cannot alter the editor's cached data.
134
+
135
+ - [#118](https://github.com/portone-io/docx-editor/pull/118) [`7de8870`](https://github.com/portone-io/docx-editor/commit/7de8870596bf4b311ab4b9fe4f19fb1291d37dbb) Thanks [@Deea222](https://github.com/Deea222)! - Every section now previews the headers and footers it names. A document whose second section
136
+ selects a header of its own used to draw the first section's on every page, because only the first
137
+ section's references were ever resolved.
138
+
139
+ A header and a footer body is read the way the document body is: the same block readers, the same
140
+ verbatim slices, one story per part on the document node beside the comment and footnote stories.
141
+ The plain-text walker each of them used to have is gone, so a `w:cr`, a no-break hyphen and a field
142
+ read the same in a header as they do anywhere else, and a `PAGE` or `NUMPAGES` field is now found by
143
+ pairing the field characters it is written between rather than by scanning the text they surround.
144
+
145
+ A header story the editor rewrote is written back into its own part, around the two ends that part
146
+ arrived with, and every part nobody rewrote is repacked as the bytes it came as. Editing a header is
147
+ not offered in the editor yet; this is the model and the export path underneath it.
148
+
149
+ - [#115](https://github.com/portone-io/docx-editor/pull/115) [`9c5331d`](https://github.com/portone-io/docx-editor/commit/9c5331d543b3238f9859c60f5821e7b94fe52bba) Thanks [@Deea222](https://github.com/Deea222)! - Copying and pasting inside the same document now keeps what it was copied as: paragraph and character formatting, tabs, breaks, images, links, and whole tables, instead of the part an HTML reader could make out. Copying a grid of cells and pasting it over selected cells fills them in place. Comment markers, bookmarks, and note references are not copied, so cutting and pasting them back drops them. A pasted list whose numbering the document no longer defines is given a number that it does, keeping the bullets or numbers it was copied with; a list arriving from another document or application is renumbered by the reader that reads it. Content copied from another document, including a second editor on the same page, still comes in through that reader.
150
+
151
+ - [#119](https://github.com/portone-io/docx-editor/pull/119) [`9b4b3fc`](https://github.com/portone-io/docx-editor/commit/9b4b3fc72ca51b89adf81063ca7fb2fb2c126e3f) Thanks [@Deea222](https://github.com/Deea222)! - Page boundaries and table widths now follow the paper of the section a block sits in. A document
152
+ whose second section is landscape used to be paginated as though the whole of it were the first
153
+ section's portrait paper, and a table put in that section was divided into the upright body width
154
+ and could not be widened past it.
155
+
156
+ Each page is now as tall as the body its own section leaves, a section break starts a new page
157
+ unless it is marked continuous, and a table is fitted to the body width of the section it is
158
+ inserted or dragged in. An image pasted into a section is fitted to the height that section's paper
159
+ leaves. A page number still carries on across a section boundary rather than restarting where the
160
+ section asks.
161
+
162
+ One sheet is still drawn at one width, the first section's, because nothing draws two paper widths
163
+ on one sheet. A landscape section is therefore paginated on landscape pages but drawn on the
164
+ sheet's width, and a table wider than the sheet's body is drawn shrunk to fit it while the document
165
+ keeps the width it was given. An even-page or odd-page start is preserved and exported untouched
166
+ but drawn as an ordinary new page.
167
+
168
+ - [#110](https://github.com/portone-io/docx-editor/pull/110) [`272cd5c`](https://github.com/portone-io/docx-editor/commit/272cd5c55098ef4c62e352fc9837b3db807c1439) Thanks [@Deea222](https://github.com/Deea222)! - Keep a picture's extension lists intact when the image is resized.
169
+
170
+ A picture written by Word often carries extension lists - the small records a word processor
171
+ attaches to a picture to remember things the format has no field for, such as which application
172
+ created it or what resolution to keep it at. Resizing such an image used to overwrite the name of
173
+ any empty record with the image's new size, which left the document with content Word reads as
174
+ invalid. Only the two sizes an image is drawn from are rewritten now, and every extension record
175
+ goes back out exactly as it came in.
176
+
177
+ Size updates follow the picture’s own XML paths, so an extension’s nested transforms and size
178
+ records stay unchanged too. Size attributes retain their quoting and surrounding markup.
179
+
180
+ - [#112](https://github.com/portone-io/docx-editor/pull/112) [`0327d4d`](https://github.com/portone-io/docx-editor/commit/0327d4dabe1de5814d440bf21f89cf5e5d1308b9) Thanks [@Deea222](https://github.com/Deea222)! - Give a document's sections a model of their own. Every `w:sectPr` is now read once - its paper and
181
+ margins, the header and footer stories it selects, whether the first page differs, and the page
182
+ number it starts at - instead of being read twice by two readers that looked for it differently.
183
+
184
+ The section that closes the body rides on the document node as `sectPr` rather than in the
185
+ preserved tail. It is written straight back out, so an untouched document still exports byte for
186
+ byte, and a submission that rewrites the paper it is written on is refused by
187
+ `onlyCommentsChangedBy` as it was before.
188
+
189
+ Pressing Enter in the last paragraph of a section continues to leave the break on the later half,
190
+ now on the same reading of `w:pPr` the rest of the package uses.
191
+
192
+ - [#111](https://github.com/portone-io/docx-editor/pull/111) [`4357635`](https://github.com/portone-io/docx-editor/commit/435763591d456ec26f6466b5e69ecc226a20f743) Thanks [@Deea222](https://github.com/Deea222)! - Take a comment added to a document whose author shares your display name, instead of refusing the whole save.
193
+ A document an older Word wrote has no people part, so its comments name an author and nobody in particular.
194
+ Adding a comment under one of those names no longer records a person for it, which had handed the comments already there to whoever commented next, and `onlyCommentsChangedBy` now takes such a comment back rather than reading it as a forged author.
195
+ Every comment carrying a shared name, the new one included, stays everyone's to edit.
196
+ This also protects comments preserved outside the editable story: their authors are not assigned to a new commenter, and the verifier rejects a people record that would claim them.
197
+
198
+ - [#113](https://github.com/portone-io/docx-editor/pull/113) [`e61185e`](https://github.com/portone-io/docx-editor/commit/e61185e645b06ce5091e59b08c5926fcaf71da16) Thanks [@Deea222](https://github.com/Deea222)! - Pasting text over selected table cells now fills every selected cell instead of only the first. Pasting the clipboard's plain text (Ctrl/Cmd+Shift+V) ignores accompanying HTML and images. Right-click Paste passes the clipboard's HTML and text through the same handlers as Ctrl/Cmd+V, including image loading and text fallback. Unreadable clipboard content leaves the selection intact when there is no usable text fallback.
199
+
200
+ - [#117](https://github.com/portone-io/docx-editor/pull/117) [`faf4081`](https://github.com/portone-io/docx-editor/commit/faf4081b43cbfd111985b1aa124e0e90b4136fc9) Thanks [@Deea222](https://github.com/Deea222)! - A hyperlink whose text is already underlined, which is how a word processor writes one, is drawn with one line under it rather than two. The editor's own link line is still drawn under a link the document leaves unstyled.
201
+
202
+ - [#106](https://github.com/portone-io/docx-editor/pull/106) [`f2871cd`](https://github.com/portone-io/docx-editor/commit/f2871cd5535780c10574906c9dceb2b3ecc4715c) Thanks [@Deea222](https://github.com/Deea222)! - Reject invalid comment range positions without throwing or adding a comment at a rounded position.
203
+
3
204
  ## 0.4.0
4
205
 
5
206
  ### Minor Changes
package/CONTRIBUTING.md CHANGED
@@ -63,6 +63,8 @@ Those are `minor` while the package is below 1.0.
63
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
64
  Commit the regenerated report in the same commit as the changeset.
65
65
 
66
+ The changesets that have landed become a release the way [Releasing](https://github.com/portone-io/docx-editor/blob/main/docs/releasing.md) describes.
67
+
66
68
  By contributing, you agree that your contribution is licensed under the Apache License 2.0 used by this project.
67
69
 
68
70
  ## Commit messages
package/assets/editor.png CHANGED
Binary file
@@ -12,7 +12,7 @@ import type { EditorView } from "prosemirror-view";
12
12
  import { type CSSProperties, type ReactNode } from "react";
13
13
  import { type DocxSource } from "./docx/importDocx";
14
14
  import { type ExportProblem } from "./docx/invariants";
15
- import { type CommentAuthor } from "./editor/commands/commentCommands";
15
+ import type { CommentAuthor } from "./editor/commands/commentCommands";
16
16
  import { DocxImportError } from "./ooxml/errors";
17
17
  import type { EditableComments } from "./schema/protection";
18
18
  import type { FontFallbacks } from "./styles/fontStack";
@@ -9,22 +9,27 @@ import {
9
9
  useState
10
10
  } from "react";
11
11
  import { exportDocx } from "./docx/exportDocx.js";
12
+ import { variantsFor } from "./docx/headersFooters.js";
12
13
  import { importDocx } from "./docx/importDocx.js";
13
14
  import { exportProblems } from "./docx/invariants.js";
14
- import {
15
- documentComments
16
- } from "./editor/commands/commentCommands.js";
15
+ import { sectionIn, sectionsOf } from "./docx/sections.js";
17
16
  import { activeLinkSpan } from "./editor/commands/linkCommands.js";
18
17
  import { createEditorView, editorStateForSession } from "./editor/createEditor.js";
18
+ import {
19
+ closeCommentComposer,
20
+ isCommentComposerOpen
21
+ } from "./editor/plugins/commentComposer.js";
22
+ import { commentProjection } from "./editor/plugins/commentDecorations.js";
19
23
  import { setProtection } from "./editor/plugins/documentProtection.js";
20
24
  import { isLinkPanelOpen } from "./editor/plugins/linkPanel.js";
21
25
  import { tableMenuAnchor } from "./editor/plugins/tableContextMenu.js";
22
26
  import { textMenuAnchor } from "./editor/plugins/textContextMenu.js";
23
27
  import { DocxImportError } from "./ooxml/errors.js";
24
28
  import { PageGuides } from "./page/PageGuides.js";
25
- import { A4_PAGE_PIXELS, pagePixels } from "./page/pageLayout.js";
29
+ import { A4_PAGE_PIXELS, pagePixels, sectionPixels } from "./page/pageLayout.js";
26
30
  import { usePageLayout } from "./page/usePageLayout.js";
27
31
  import { editingProtection, protectionOf } from "./schema/protectionState.js";
32
+ import { storyNodeOf } from "./schema/stories.js";
28
33
  import { editorClassNames } from "./styles/classNames.js";
29
34
  import { CommentsPanel } from "./ui/CommentsPanel.js";
30
35
  import { LinkCard } from "./ui/LinkCard.js";
@@ -36,6 +41,9 @@ import { Toolbar } from "./ui/Toolbar.js";
36
41
  import { useFitWidthZoom } from "./ui/useFitWidthZoom.js";
37
42
  import { normalizeZoom } from "./ui/zoom.js";
38
43
  import { jsx, jsxs } from "react/jsx-runtime";
44
+ function runOn(view, command) {
45
+ command(view.state, (transaction) => view.dispatch(transaction));
46
+ }
39
47
  function affordancesOf(mode) {
40
48
  switch (mode.kind) {
41
49
  case "readOnly":
@@ -161,7 +169,6 @@ function DocxEditorSurface({
161
169
  );
162
170
  const viewRef = useRef(null);
163
171
  const [live, setLive] = useState(null);
164
- const [commentComposerOpen, setCommentComposerOpen] = useState(false);
165
172
  const [commentsOpen, setCommentsOpen] = useState(false);
166
173
  const [uncontrolledZoom, setUncontrolledZoom] = useState(
167
174
  () => normalizeZoom(defaultZoom)
@@ -217,7 +224,6 @@ function DocxEditorSurface({
217
224
  const authorId = author?.id ?? null;
218
225
  const mounted = live !== null;
219
226
  useLayoutEffect(() => {
220
- if (protection === "readOnly") setCommentComposerOpen(false);
221
227
  const view = viewRef.current;
222
228
  if (!view || !mounted) return;
223
229
  const held = protectionOf(view.state);
@@ -231,40 +237,58 @@ function DocxEditorSurface({
231
237
  useImperativeHandle(ref, () => {
232
238
  const view = viewRef.current;
233
239
  if (!view || opened?.status !== "opened") return null;
234
- const session = opened.session;
240
+ const session2 = opened.session;
235
241
  return {
236
242
  view,
237
- exportBytes: () => exportDocx(view.state.doc, session),
238
- exportProblems: () => exportProblems(view.state.doc, session)
243
+ exportBytes: () => exportDocx(view.state.doc, session2),
244
+ exportProblems: () => exportProblems(view.state.doc, session2)
239
245
  };
240
246
  }, [opened]);
247
+ const doc = live?.state.doc ?? null;
248
+ const session = opened?.status === "opened" ? opened.session : null;
249
+ const sections = useMemo(
250
+ () => doc === null ? null : sectionsOf(doc),
251
+ [doc]
252
+ );
253
+ const sectionPapers = useMemo(
254
+ () => sections === null ? void 0 : sectionPixels(sections),
255
+ [sections]
256
+ );
241
257
  const overlay = usePageLayout({
242
258
  view: live?.view ?? null,
243
259
  layer: layerRef,
244
260
  enabled: showPageGuides,
245
261
  revision: live?.state.doc,
246
- geometry: opened?.status === "opened" ? opened.session.geometry : void 0
262
+ sections: sectionPapers
247
263
  });
264
+ const headersFootersFor = useMemo(() => {
265
+ if (doc === null || session === null || sections === null) return void 0;
266
+ const shown = sections.map(
267
+ (section) => variantsFor(
268
+ section,
269
+ session.headerFooterStories,
270
+ (key) => storyNodeOf(doc, key)
271
+ )
272
+ );
273
+ return (face) => shown[sectionIn(sections, doc, face.pos).index] ?? null;
274
+ }, [doc, session, sections]);
248
275
  if (opened?.status === "rejected") {
249
276
  return renderImportError ? renderImportError(opened.error) : /* @__PURE__ */ jsx(ImportRejection, { error: opened.error });
250
277
  }
251
278
  const bodyOpen = live !== null && editingProtection(live.state) === "none";
252
- const commentsOpenToWrite = live !== null && editingProtection(live.state) !== "readOnly";
253
279
  const textAnchor = live ? textMenuAnchor(live.state) : null;
254
280
  const tableAnchor = live && !textAnchor ? tableMenuAnchor(live.state) : null;
255
281
  const linkAtCursor = live && !live.view.composing && !isLinkPanelOpen(live.state) ? activeLinkSpan(live.state) : null;
256
- const comments = live?.state === void 0 ? [] : documentComments(live.state);
282
+ const comments = live?.state === void 0 ? [] : commentProjection.read(live.state).comments;
257
283
  const hasUnresolvedComments = comments.some((comment) => !comment.resolved);
258
- const effectiveComposerOpen = commentComposerOpen && commentsOpenToWrite;
259
- const showComments = live !== null && (commentsOpen || effectiveComposerOpen || hasUnresolvedComments);
284
+ const composerOpen = live !== null && isCommentComposerOpen(live.state);
285
+ const showComments = live !== null && (commentsOpen || composerOpen || hasUnresolvedComments);
260
286
  const commentsPanel = live && showComments && /* @__PURE__ */ jsx(
261
287
  CommentsPanel,
262
288
  {
263
289
  view: live.view,
264
290
  state: live.state,
265
- composerOpen: effectiveComposerOpen,
266
291
  author,
267
- closeComposer: () => setCommentComposerOpen(false),
268
292
  scrollContainer: rootRef.current,
269
293
  allCommentsOpen: commentsOpen
270
294
  }
@@ -284,10 +308,8 @@ function DocxEditorSurface({
284
308
  presets,
285
309
  commentsOpen,
286
310
  onToggleComments: () => {
287
- setCommentsOpen((open) => {
288
- if (open) setCommentComposerOpen(false);
289
- return !open;
290
- });
311
+ if (commentsOpen) runOn(live.view, closeCommentComposer);
312
+ setCommentsOpen(!commentsOpen);
291
313
  },
292
314
  zoom: selectedZoom,
293
315
  onZoomChange: changeZoom
@@ -323,7 +345,7 @@ function DocxEditorSurface({
323
345
  PageGuides,
324
346
  {
325
347
  overlay,
326
- headersFooters: opened?.status === "opened" ? opened.session.headersFooters : void 0
348
+ headersFootersFor
327
349
  }
328
350
  ),
329
351
  live && opened?.status === "opened" && /* @__PURE__ */ jsx(NotesPanel, { state: live.state, pageWidth: page.pageWidth })
@@ -352,10 +374,7 @@ function DocxEditorSurface({
352
374
  view: live.view,
353
375
  state: live.state,
354
376
  anchor: textAnchor,
355
- allowLocking: locking,
356
- onAddComment: () => {
357
- setCommentComposerOpen(true);
358
- }
377
+ allowLocking: locking
359
378
  }
360
379
  ),
361
380
  live && tableAnchor && /* @__PURE__ */ jsx(
@@ -1,7 +1,7 @@
1
1
  // src/docx/cloning.ts
2
2
  import { attrsText, withoutAttrs } from "../ooxml/element.js";
3
- import { parseProps, renderProps, setChild } from "../ooxml/props.js";
4
3
  import { parseAttrs } from "../ooxml/tagScan.js";
4
+ import { withoutSectionBreak } from "./sections.js";
5
5
  var carried = (value) => value;
6
6
  var defaulted = () => void 0;
7
7
  var PARAGRAPH_ID_ATTRS = ["paraId", "textId"];
@@ -13,13 +13,8 @@ function withoutParagraphIds(value) {
13
13
  if (kept.length === attrs.length) return value;
14
14
  return kept.length === 0 ? null : attrsText(kept);
15
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;
16
+ function droppingSectionBreak(value) {
17
+ return typeof value === "string" ? withoutSectionBreak(value) : value;
23
18
  }
24
19
  var INHERITED_CELL_ATTRS = ["tcAttrs", "tcPr", "tcW", "format"];
25
20
  var INHERITED_ROW_ATTRS = ["trAttrs", "trPr", "format"];
@@ -48,7 +43,7 @@ var CLONE_POLICIES = {
48
43
  // A paragraph-level `w:sectPr` ends the section it stands in, so it belongs to whichever
49
44
  // paragraph ends up last in that section: the second half of a split, and no half of a copy,
50
45
  // which is lifted out of the section it was taken from.
51
- pPr: (value, side) => side === "after" ? value : withoutSectionBreak(value),
46
+ pPr: (value, side) => side === "after" ? value : droppingSectionBreak(value),
52
47
  // What names the block this paragraph was opened from. Only the half still standing where
53
48
  // that block stood may go on claiming it; `docx/exportDocx` writes the original bytes back
54
49
  // for whatever claims one.
@@ -34,9 +34,11 @@ export type CommentOnlyVerdict = {
34
34
  * has to read as it did, comments aside. Those three parts are read entry by entry instead
35
35
  * (`./comments/policy`), since a comment edit is free to rewrite them and something has to say
36
36
  * what it may have written there. A comment carrying no recorded identity is everyone's to
37
- * edit here as it is in the editor (`schema/protection`), while a comment that appeared has to
38
- * carry this identity: a file can claim any author, and the editor's own hand in writing it is
39
- * not there to vouch for it. An identity already recorded is nobody's to rewrite.
37
+ * edit here as it is in the editor (`schema/protection`). A comment that appeared carries this
38
+ * identity, or none under a name an original comment already carried unattributed, including a
39
+ * preserved comment outside the editable story. A file can claim any author, so an addition under
40
+ * any other name must carry the requester's identity. An identity already recorded is nobody's
41
+ * to rewrite, and a new identity cannot claim an original unattributed comment.
40
42
  *
41
43
  * `editableComments: "all"` judges the file of an editor opened for a moderator, where every
42
44
  * comment was theirs to edit; an identity is nobody's to rewrite under either setting.
@@ -22,13 +22,38 @@ export declare const COMMENT_EX_ATTRIBUTES: ReadonlySet<string>;
22
22
  * everything it writes.
23
23
  */
24
24
  export declare function declarationWritten(attr: Attr): boolean;
25
+ /**
26
+ * Whether this element and everything inside it declares only namespaces this package writes.
27
+ *
28
+ * A declaration decides what every name under it means, so a block rebinding a prefix says one
29
+ * thing to this reader and another to Word, and is a place to put bytes nothing looks at.
30
+ */
31
+ export declare function declarationsWritten(el: Element): boolean;
25
32
  /**
26
33
  * Whether the element carries no attribute outside this set, and declares no namespace outside
27
34
  * what the writer declares.
28
35
  */
29
36
  export declare function attributesWithin(el: Element, allowed: ReadonlySet<string>): boolean;
30
- /** The body of a comment, as a paragraph of one run holding the text and the breaks in it */
31
- export declare function renderCommentBody(text: string, paraId: string | null): string;
37
+ /**
38
+ * Whether the element holds elements and nothing else.
39
+ *
40
+ * The writer puts no whitespace, comment or stray text between the pieces of a body, so an entry
41
+ * carrying any is one it did not write, and a reading that passed over them would leave a place to
42
+ * put bytes nothing looks at.
43
+ */
44
+ export declare function holdsElementsOnly(el: Element): boolean;
45
+ /** XML layout whitespace, which is the one text carrying no payload wherever it stands */
46
+ export declare function isLayoutText(node: Node): boolean;
47
+ /**
48
+ * Whether the element holds elements and the whitespace a producer laid them out over.
49
+ *
50
+ * A block is sliced with the whitespace standing ahead of it (`docx/scan`), so the writer hands a
51
+ * block it left untouched back with the layout it arrived in, and an entry from a part written
52
+ * over several lines carries that layout between its blocks. Whitespace is the only text this
53
+ * passes over: a comment, a processing instruction or a stretch of real text is a place to put
54
+ * bytes nothing looks at, and none of them is anything the writer puts out.
55
+ */
56
+ export declare function holdsElementsAndLayout(el: Element): boolean;
32
57
  /**
33
58
  * The paragraph a thread key belongs on, which is the last one of the body.
34
59
  *
@@ -61,20 +86,12 @@ export declare function arrivedEntries(partXml: string | null): ReadonlyMap<stri
61
86
  * declares it on its root along with the compatibility markup that goes with it (`./writing`).
62
87
  */
63
88
  export declare function withThreadKey(commentXml: string, paraId: string, arrived: Element | null): string;
64
- /**
65
- * The text of a comment written in that shape, and null for an entry holding anything else: a
66
- * field, a second run, markup a producer wrapped it in, an attribute this editor does not write.
67
- *
68
- * An entry that arrived and was not edited is compared as it stands rather than read here, so
69
- * saying no to a shape this editor would not have written turns down only a rewrite.
70
- */
71
89
  /** Whether this editor's writer could have put out this thread state */
72
90
  export declare function wellFormedCommentExtension(entry: Element): boolean;
73
91
  /** Whether this editor's writer could have put out this recorded identity */
74
92
  export declare function wellFormedPerson(entry: Element): boolean;
75
93
  /** The identity a recorded person stands for, and null for an entry recording none */
76
94
  export declare function recordedIdentity(person: Element): string | null;
77
- export declare function readStrictCommentBody(comment: Element): string | null;
78
95
  /** The thread state of one comment, as this editor writes it into the extended part */
79
96
  export declare function renderCommentExtension(comment: CommentReferenceData | CommentReplyData): string;
80
97
  /**