@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
@@ -10,6 +10,8 @@
10
10
  * document breaks at Letter's height and fits its tables to Letter's width.
11
11
  */
12
12
  import { type PageGeometry } from "../docx/pageGeometry";
13
+ import type { MeasuredBlock, PageCut } from "./blockKinds";
14
+ export type { MeasuredBlock };
13
15
  /** The paper measured in the pixels the sheet is drawn with */
14
16
  export interface PagePixels {
15
17
  /** The whole sheet, padding included */
@@ -45,52 +47,6 @@ export declare const A4_PAGE_PIXELS: PagePixels;
45
47
  * moment before a document is opened.
46
48
  */
47
49
  export declare function pageGeometryStyle(page: PagePixels): string;
48
- /** One body block as drawn on screen */
49
- export interface MeasuredBlock {
50
- /** The position where this block starts in the document */
51
- pos: number;
52
- /** The height that naturally opens up between the previous block and this one */
53
- gap: number;
54
- height: number;
55
- /** Set when the document records that a new page starts at this block */
56
- breakBefore: boolean;
57
- /**
58
- * Every page break inside the block, in document order, each measured from the block's own top
59
- * so that the block can be laid out wherever it lands.
60
- * That top is the one the block would be drawn at with no space in it, so a space opened at one
61
- * break shifts the ones after it.
62
- */
63
- breaks: readonly number[];
64
- /** Row boundaries available when this block is an editable table */
65
- table?: MeasuredTable;
66
- }
67
- /** A row that may start the continued part of a table */
68
- export interface TableBoundary {
69
- /** The row's document position */
70
- pos: number;
71
- /** Its top measured from the table's natural top */
72
- offset: number;
73
- }
74
- /** The measurements needed to continue a table without changing the document */
75
- export interface MeasuredTable {
76
- boundaries: readonly TableBoundary[];
77
- /** The smallest useful first piece: headers followed by one body row group */
78
- firstPageMinimum: number;
79
- repeatHeaderHeight: number;
80
- /** Document positions of the contiguous header rows at the start of the table */
81
- headerRows: readonly number[];
82
- /** Changes when any projected header content or formatting changes */
83
- headerSignature: string;
84
- columns: number;
85
- }
86
- /** The space opened up at one page break, so that what follows it starts the next page */
87
- export interface BreakSpace {
88
- /** The position where the block holding the break starts */
89
- pos: number;
90
- /** Which break inside that block, counted in document order */
91
- index: number;
92
- height: number;
93
- }
94
50
  /** One block to be moved down to the next page */
95
51
  export interface BlockPush {
96
52
  pos: number;
@@ -99,17 +55,6 @@ export interface BlockPush {
99
55
  /** Of that, the extra amount pushed for the page's sake */
100
56
  push: number;
101
57
  }
102
- /** A display-only row inserted before the row that continues on the next page */
103
- export interface TableContinuation {
104
- /** The position of the first original row on the next page */
105
- pos: number;
106
- /** Space from the previous page's row boundary to the next page body */
107
- height: number;
108
- /** Header rows to project immediately after the space */
109
- headerRows: readonly number[];
110
- headerSignature: string;
111
- columns: number;
112
- }
113
58
  /** A place where one page parts from the next */
114
59
  export interface PageSplit {
115
60
  /** Where the previous page's body ends, measured from the top of the body */
@@ -148,8 +93,8 @@ export interface PageStart {
148
93
  export interface PageLayout {
149
94
  /** Only the blocks to be moved down to the next page */
150
95
  pushes: BlockPush[];
151
- spaces: BreakSpace[];
152
- tableContinuations: TableContinuation[];
96
+ /** Where the layout parted a block, and the space it opened before the continued piece */
97
+ cuts: PageCut[];
153
98
  splits: PageSplit[];
154
99
  /** One per page, the first page included */
155
100
  pages: PageStart[];
@@ -169,8 +114,11 @@ export interface PageLayoutInput {
169
114
  * blocks below it move down by the same amount.
170
115
  * A block taller than one page cannot be pushed, so it is left where it is and only the
171
116
  * places it crosses are reported.
172
- * A page break inside a block is filled out to the end of the page it falls on, which carries
173
- * the rest of that block - the rest of the sentence, the rest of the list item - to the top of
174
- * the next page while the block itself stays whole.
117
+ * A block is parted only where its measurer offered a candidate. A forced one - a page break
118
+ * written into the text - is filled out to the end of the page it falls on, which carries the rest
119
+ * of that block to the top of the next page while the block itself stays whole. An optional one -
120
+ * a row boundary in a table - is taken only where the piece after it would run off the page.
121
+ * A block kept with the next one is pushed together with what it is kept with: the blocks kept
122
+ * after it and the first piece of the block the keeps end at.
175
123
  */
176
124
  export declare function pageLayout({ blocks, pageBodyHeight, pageStep, }: PageLayoutInput): PageLayout;
@@ -41,14 +41,41 @@ var TOLERANCE_PX = 0.5;
41
41
  function round(value) {
42
42
  return Math.round(value * 100) / 100;
43
43
  }
44
+ function keepsWithNext(block, next) {
45
+ return block.keepWithNext && block.candidates.length === 0 && !block.breakAfter && !next.breakBefore;
46
+ }
47
+ function keptExtents(blocks, fitsPage) {
48
+ const extents = /* @__PURE__ */ new Map();
49
+ let run = [];
50
+ const settle = () => {
51
+ const head = run.at(-1);
52
+ if (head && fitsPage(head.extent)) {
53
+ for (const { index, extent } of run) {
54
+ extents.set(index, extent);
55
+ }
56
+ }
57
+ run = [];
58
+ };
59
+ for (let index = blocks.length - 2; index >= 0; index -= 1) {
60
+ const block = blocks[index];
61
+ const next = blocks[index + 1];
62
+ if (block && next && keepsWithNext(block, next)) {
63
+ const below = run.at(-1)?.extent ?? next.minFirstPiece;
64
+ run.push({ index, extent: block.height + next.gap + below });
65
+ } else {
66
+ settle();
67
+ }
68
+ }
69
+ settle();
70
+ return extents;
71
+ }
44
72
  function pageLayout({
45
73
  blocks,
46
74
  pageBodyHeight,
47
75
  pageStep
48
76
  }) {
49
77
  const pushes = [];
50
- const spaces = [];
51
- const tableContinuations = [];
78
+ const cuts = [];
52
79
  const splits = [];
53
80
  const firstPage = {
54
81
  page: 1,
@@ -58,14 +85,7 @@ function pageLayout({
58
85
  };
59
86
  const pages = [firstPage];
60
87
  if (!(pageBodyHeight > 0)) {
61
- return {
62
- pushes,
63
- spaces,
64
- tableContinuations,
65
- splits,
66
- pages,
67
- bodyHeight: 0
68
- };
88
+ return { pushes, cuts, splits, pages, bodyHeight: 0 };
69
89
  }
70
90
  let pageStart = 0;
71
91
  let cursor = 0;
@@ -82,13 +102,16 @@ function pageLayout({
82
102
  split(pageStart + pageBodyHeight, true, false);
83
103
  }
84
104
  };
85
- for (const block of blocks) {
105
+ const fitsPage = (extent) => extent <= pageBodyHeight + TOLERANCE_PX;
106
+ const kept = keptExtents(blocks, fitsPage);
107
+ let breakAfterPrevious = false;
108
+ for (const [index, block] of blocks.entries()) {
86
109
  const pageEnd = pageStart + pageBodyHeight;
87
110
  const top = cursor + block.gap;
88
- const startsPage = block.breakBefore && top > pageStart + TOLERANCE_PX;
89
- const first = block.breaks[0] ?? block.table?.firstPageMinimum ?? block.height;
111
+ const startsPage = (block.breakBefore || breakAfterPrevious) && top > pageStart + TOLERANCE_PX;
112
+ const first = kept.get(index) ?? block.minFirstPiece;
90
113
  const overflows = top + first > pageEnd + TOLERANCE_PX;
91
- const fits = first <= pageBodyHeight + TOLERANCE_PX;
114
+ const fits = fitsPage(first);
92
115
  const push = startsPage || overflows && fits ? Math.max(0, pageEnd + pageStep - top) : 0;
93
116
  if (push > 0 || startsPage) split(pageEnd, false, startsPage);
94
117
  if (push > 0) {
@@ -98,51 +121,35 @@ function pageLayout({
98
121
  push: round(push)
99
122
  });
100
123
  }
101
- let contentTop = top + push;
102
- for (const [index, offset] of block.breaks.entries()) {
103
- const breakY = contentTop + offset;
104
- crossTo(breakY);
105
- split(pageStart + pageBodyHeight, false, true);
106
- const height = Math.max(0, pageStart - breakY);
107
- spaces.push({ pos: block.pos, index, height: round(height) });
108
- contentTop += height;
109
- }
110
- let tableAdded = 0;
111
- if (block.table) {
112
- let segmentStart = 0;
113
- for (let index = 0; index <= block.table.boundaries.length; index += 1) {
114
- const segmentEnd = block.table.boundaries[index]?.offset ?? block.height;
115
- const segmentTop = contentTop + segmentStart + tableAdded;
116
- let segmentBottom = contentTop + segmentEnd + tableAdded;
117
- const pageEnd2 = pageStart + pageBodyHeight;
118
- const overflows2 = segmentBottom > pageEnd2 + TOLERANCE_PX;
119
- const boundary = index === 0 ? null : block.table.boundaries[index - 1];
120
- const segmentHeight = segmentEnd - segmentStart;
121
- if (overflows2 && boundary && segmentTop > pageStart + TOLERANCE_PX && segmentHeight <= pageBodyHeight + TOLERANCE_PX) {
122
- split(pageEnd2, false, false);
123
- const height = Math.max(0, pageStart - segmentTop);
124
- tableContinuations.push({
125
- pos: boundary.pos,
126
- height: round(height),
127
- headerRows: block.table.headerRows,
128
- headerSignature: block.table.headerSignature,
129
- columns: block.table.columns
130
- });
131
- tableAdded += height + block.table.repeatHeaderHeight;
132
- segmentBottom = contentTop + segmentEnd + tableAdded;
124
+ const contentTop = top + push;
125
+ let added = 0;
126
+ let pieceStart = 0;
127
+ for (let index2 = 0; index2 <= block.candidates.length; index2 += 1) {
128
+ const cut = index2 === 0 ? null : block.candidates[index2 - 1];
129
+ const pieceEnd = block.candidates[index2]?.offset ?? block.height;
130
+ const pieceTop = contentTop + pieceStart + added;
131
+ let pieceBottom = contentTop + pieceEnd + added;
132
+ if (cut) {
133
+ if (cut.forced) crossTo(pieceTop);
134
+ const pageBottom = pageStart + pageBodyHeight;
135
+ const cutHere = cut.forced || pieceBottom > pageBottom + TOLERANCE_PX && pieceTop > pageStart + TOLERANCE_PX && pieceEnd - pieceStart <= pageBodyHeight + TOLERANCE_PX;
136
+ if (cutHere) {
137
+ split(pageBottom, false, cut.forced);
138
+ const height = Math.max(0, pageStart - pieceTop);
139
+ cuts.push({ at: cut.at, height: round(height) });
140
+ added += height + cut.repeatHeight;
141
+ pieceBottom += height + cut.repeatHeight;
133
142
  }
134
- crossTo(segmentBottom);
135
- segmentStart = segmentEnd;
136
143
  }
144
+ crossTo(pieceBottom);
145
+ pieceStart = pieceEnd;
137
146
  }
138
- const bottom = contentTop + block.height + tableAdded;
139
- crossTo(bottom);
140
- cursor = bottom;
147
+ cursor = contentTop + block.height + added;
148
+ breakAfterPrevious = block.breakAfter;
141
149
  }
142
150
  return {
143
151
  pushes,
144
- spaces,
145
- tableContinuations,
152
+ cuts,
146
153
  splits,
147
154
  pages,
148
155
  bodyHeight: round(pageStart + pageBodyHeight)
@@ -3,8 +3,8 @@
3
3
  * page-boundary positions over to the view. It remeasures whenever the text changes or the
4
4
  * box is resized.
5
5
  *
6
- * A measurement hands new pushes and break spaces to the view (`./pageDecorations`), which is a
7
- * decoration transaction against the very paragraph an IME may be composing in, and a composition
6
+ * A measurement hands its marks to the view (`./pageDecorations`) in one decoration transaction
7
+ * against the very paragraph an IME may be composing in, and a composition
8
8
  * in Japanese or Chinese stays open across a whole clause while the text grows line by line. So no
9
9
  * measurement is taken while a composition is open: the frame is taken again until it is over.
10
10
  */
@@ -8,11 +8,7 @@ import {
8
8
  } from "react";
9
9
  import { editorCssVariables } from "../styles/classNames.js";
10
10
  import { measureSheet } from "./measureBlocks.js";
11
- import {
12
- setPageBreakSpaces,
13
- setPagePushes,
14
- setTableContinuations
15
- } from "./pageDecorations.js";
11
+ import { setPageMarks } from "./pageDecorations.js";
16
12
  import {
17
13
  A4_PAGE_PIXELS,
18
14
  PAGE_SPLIT_PX,
@@ -74,9 +70,7 @@ function usePageLayout({
74
70
  pageBodyHeight: page.bodyHeight,
75
71
  pageStep: page.pageStep
76
72
  });
77
- setPagePushes(view, layout.pushes);
78
- setPageBreakSpaces(view, layout.spaces);
79
- setTableContinuations(view, layout.tableContinuations);
73
+ setPageMarks(view, { pushes: layout.pushes, cuts: layout.cuts });
80
74
  const sheetHeight = measured.contentTop + layout.bodyHeight + measured.contentBottom;
81
75
  box.style.setProperty(editorCssVariables.sheetHeight, `${sheetHeight}px`);
82
76
  const next = {
@@ -120,9 +114,7 @@ function usePageLayout({
120
114
  if (enabled) return;
121
115
  layer.current?.style.removeProperty(editorCssVariables.sheetHeight);
122
116
  if (!view) return;
123
- setPagePushes(view, []);
124
- setPageBreakSpaces(view, []);
125
- setTableContinuations(view, []);
117
+ setPageMarks(view, { pushes: [], cuts: [] });
126
118
  }, [enabled, layer, view]);
127
119
  useEffect(() => {
128
120
  const box = layer.current;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Each attr has two independent classifications. Its role controls source comparison: `source`
3
+ * values are what the writer writes a block from; `display` values are worked out from the source
4
+ * and the formatting around it, and are never written into a block's XML (`docx/newLists` reads a
5
+ * list reference off one to decide what numbering.xml needs); `session` values are what export
6
+ * reads but neither writes nor derives again - which block, control or link this is, whether a
7
+ * comment came in with the file, the name of a preserved element. `sourceEquality` compares source
8
+ * and session values so a display refresh does not rebuild an untouched block.
9
+ *
10
+ * Its class records provenance: `preserved` holds XML, `derived` is calculated from other data,
11
+ * `identity` identifies content (imported or allocated here), and `model` holds editable values.
12
+ * Classification alone does not promise a public API; the plugin guide defines that boundary.
13
+ *
14
+ * Lock flags are derived from control XML but have the source role: ignoring them in a comparison
15
+ * would let an unlock pass as a display refresh. The imported flags are derived with the session
16
+ * role because export reads them to decide whether to rewrite comment parts.
17
+ * Both columns are checked against the schema by the adjacent tests.
18
+ */
19
+ import { type MarkType, NodeType } from "prosemirror-model";
20
+ export type AttrRole = "source" | "display" | "session";
21
+ export type AttrClass = "preserved" | "derived" | "identity" | "model";
22
+ /** What is declared about one attr: what a comparison does with it, and where its value comes from */
23
+ export type AttrFacts = {
24
+ readonly role: AttrRole;
25
+ readonly class: AttrClass;
26
+ };
27
+ type AttrTable = Readonly<Record<string, Readonly<Record<string, AttrFacts>>>>;
28
+ export declare const NODE_ATTR_ROLES: AttrTable;
29
+ export declare const MARK_ATTR_ROLES: AttrTable;
30
+ /**
31
+ * What this attr of this node or mark is for.
32
+ *
33
+ * An attr with no role declared is read as `source`: a judgement then compares it, which is the
34
+ * answer that reports a change rather than hiding one. `attrRoles.test.ts` is what keeps the case
35
+ * from arising.
36
+ */
37
+ export declare function attrRole(type: NodeType | MarkType, name: string): AttrRole;
38
+ /** The attrs of this node or mark that are worked out rather than written */
39
+ export declare function displayAttrsOf(type: NodeType | MarkType): readonly string[];
40
+ /** The attrs of this node or mark that the given class covers */
41
+ export declare function attrsOfClass(type: NodeType | MarkType, cls: AttrClass): readonly string[];
42
+ export {};
@@ -0,0 +1,141 @@
1
+ // src/schema/attrRoles.ts
2
+ import { NodeType } from "prosemirror-model";
3
+ var NODE_ATTR_ROLES = {
4
+ paragraph: {
5
+ srcId: { role: "session", class: "identity" },
6
+ pAttrs: { role: "source", class: "preserved" },
7
+ pPr: { role: "source", class: "preserved" },
8
+ format: { role: "display", class: "derived" },
9
+ styleRun: { role: "display", class: "derived" }
10
+ },
11
+ table: {
12
+ srcId: { role: "session", class: "identity" },
13
+ tblAttrs: { role: "source", class: "preserved" },
14
+ tblPr: { role: "source", class: "preserved" },
15
+ tblW: { role: "source", class: "model" },
16
+ gridCols: { role: "source", class: "model" },
17
+ // The grid the table had before it was last revised, carried as it arrived
18
+ gridChange: { role: "source", class: "preserved" },
19
+ format: { role: "display", class: "derived" },
20
+ styleInside: { role: "display", class: "derived" },
21
+ styleCellMargins: { role: "display", class: "derived" }
22
+ },
23
+ tableRow: {
24
+ trAttrs: { role: "source", class: "preserved" },
25
+ tblPrEx: { role: "source", class: "preserved" },
26
+ trPr: { role: "source", class: "preserved" },
27
+ format: { role: "display", class: "derived" }
28
+ },
29
+ tableCell: {
30
+ colspan: { role: "source", class: "model" },
31
+ rowspan: { role: "source", class: "model" },
32
+ // prosemirror-tables' own attr. Import writes null, the writer never reads it, and the table
33
+ // commands only carry it from one cell to another; a real column resize moves `gridCols`
34
+ colwidth: { role: "display", class: "derived" },
35
+ tcAttrs: { role: "source", class: "preserved" },
36
+ tcPr: { role: "source", class: "preserved" },
37
+ tcW: { role: "source", class: "model" },
38
+ format: { role: "display", class: "derived" },
39
+ // Read from `sdtPrefix` rather than from the file, but a lock is not a display value: leaving
40
+ // it out of the comparison would let a step that unlocks a cell pass as a re-derivation
41
+ sdtPrefix: { role: "source", class: "preserved" },
42
+ sdtContentsLocked: { role: "source", class: "derived" },
43
+ sdtDeletionLocked: { role: "source", class: "derived" }
44
+ },
45
+ rawBlock: {
46
+ xml: { role: "source", class: "preserved" },
47
+ name: { role: "source", class: "identity" }
48
+ },
49
+ docxRaw: {
50
+ srcId: { role: "session", class: "identity" },
51
+ name: { role: "session", class: "identity" }
52
+ },
53
+ bookmarkBlock: {
54
+ srcId: { role: "session", class: "identity" },
55
+ name: { role: "session", class: "identity" }
56
+ },
57
+ hardBreak: { brAttrs: { role: "source", class: "preserved" } },
58
+ image: {
59
+ // The bytes themselves, as a data URL the editor makes when an image is inserted
60
+ src: { role: "source", class: "model" },
61
+ extent: { role: "source", class: "model" },
62
+ alt: { role: "source", class: "model" },
63
+ xml: { role: "source", class: "preserved" }
64
+ },
65
+ commentStart: {
66
+ id: { role: "source", class: "identity" },
67
+ xml: { role: "source", class: "preserved" }
68
+ },
69
+ commentEnd: {
70
+ id: { role: "source", class: "identity" },
71
+ xml: { role: "source", class: "preserved" }
72
+ },
73
+ commentReference: {
74
+ id: { role: "source", class: "identity" },
75
+ referenceXml: { role: "source", class: "preserved" },
76
+ author: { role: "source", class: "model" },
77
+ authorId: { role: "source", class: "model" },
78
+ initials: { role: "source", class: "model" },
79
+ date: { role: "source", class: "model" },
80
+ text: { role: "source", class: "model" },
81
+ commentXml: { role: "source", class: "preserved" },
82
+ paraId: { role: "source", class: "identity" },
83
+ resolved: { role: "source", class: "model" },
84
+ extensionXml: { role: "source", class: "preserved" },
85
+ replies: { role: "source", class: "model" },
86
+ // Read to decide whether the comment parts are rewritten at all; neither is written
87
+ imported: { role: "session", class: "derived" },
88
+ threadImported: { role: "session", class: "derived" }
89
+ },
90
+ noteReference: {
91
+ kind: { role: "source", class: "model" },
92
+ id: { role: "source", class: "identity" },
93
+ customMarkFollows: { role: "source", class: "model" },
94
+ referenceXml: { role: "source", class: "preserved" },
95
+ // Both come from the notes part as the document was opened, and the writer puts back the
96
+ // reference alone: a note renumbered around an edit is the same reference it was
97
+ label: { role: "display", class: "derived" },
98
+ text: { role: "display", class: "derived" }
99
+ },
100
+ rawInline: { xml: { role: "source", class: "preserved" } }
101
+ };
102
+ var MARK_ATTR_ROLES = {
103
+ run: {
104
+ rPr: { role: "source", class: "preserved" },
105
+ rAttrs: { role: "source", class: "preserved" },
106
+ format: { role: "display", class: "derived" }
107
+ },
108
+ sdt: {
109
+ sdtPrefix: { role: "source", class: "preserved" },
110
+ // Counted through the document as it was opened, to tell one control from the next
111
+ sdtKey: { role: "session", class: "identity" },
112
+ contentsLocked: { role: "source", class: "derived" },
113
+ deletionLocked: { role: "source", class: "derived" }
114
+ },
115
+ link: {
116
+ linkPrefix: { role: "source", class: "preserved" },
117
+ href: { role: "source", class: "model" },
118
+ linkKey: { role: "session", class: "identity" }
119
+ },
120
+ tab: { tabAttrs: { role: "source", class: "preserved" } }
121
+ };
122
+ function factsOf(type) {
123
+ const facts = type instanceof NodeType ? NODE_ATTR_ROLES[type.name] : MARK_ATTR_ROLES[type.name];
124
+ return facts ?? {};
125
+ }
126
+ function attrRole(type, name) {
127
+ return factsOf(type)[name]?.role ?? "source";
128
+ }
129
+ function displayAttrsOf(type) {
130
+ return Object.entries(factsOf(type)).filter(([, facts]) => facts.role === "display").map(([name]) => name);
131
+ }
132
+ function attrsOfClass(type, cls) {
133
+ return Object.entries(factsOf(type)).filter(([, facts]) => facts.class === cls).map(([name]) => name);
134
+ }
135
+ export {
136
+ MARK_ATTR_ROLES,
137
+ NODE_ATTR_ROLES,
138
+ attrRole,
139
+ attrsOfClass,
140
+ displayAttrsOf
141
+ };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * How a display value gets onto a node: who says what it should be, and why the transaction that
3
+ * writes it is no edit.
4
+ *
5
+ * What a node draws with is worked out from its source and the formatting around it
6
+ * (`./attrRoles`), and has to be worked out again whenever either moves: the lines of a table's
7
+ * cells once a row is added, the style values of a paragraph once its own properties change, and
8
+ * every value at once when the formatting the document is resolved against is replaced. A deriver
9
+ * says, for the node types it answers for, what the display attrs of one such node should be, and
10
+ * `deriveDisplay` walks the blocks of a document, asks each owner in turn, and writes one step per
11
+ * node whose attrs would change. Only the display attrs of what a deriver hands back are read, so
12
+ * a deriver cannot write a source attr, and the walk is by construction the kind of transaction
13
+ * `changesOnlyDisplayAttrs` describes.
14
+ *
15
+ * The interface stands here, below `docx` and `editor`, so that a module of either can write a
16
+ * deriver against it. What a deriver resolves against is the caller's own `Context`, which this
17
+ * module never reads; `editor/plugins/displayDerivation` registers the derivers and runs the walk
18
+ * after every edit.
19
+ *
20
+ * A transaction carrying the `displayOnly` pass goes through the guard list whole (`./guards`),
21
+ * provided every step of it is judged, off the role table alone, to change display attrs and
22
+ * nothing else. The pass is a claim rather than a key: a step that rewrites a source attr, a lock
23
+ * flag among them, or that puts content anywhere, fails the claim, and the transaction is then
24
+ * judged as any other edit.
25
+ */
26
+ import { type Attrs, type MarkType, type Node as PMNode, type Schema } from "prosemirror-model";
27
+ import { PluginKey } from "prosemirror-state";
28
+ import { type Step, type Transform } from "prosemirror-transform";
29
+ import type { docxSchema } from "./docxSchema";
30
+ /** The name of a node type of the document schema, which is what a deriver answers for by */
31
+ export type NodeName = typeof docxSchema extends Schema<infer Nodes, string> ? Nodes : never;
32
+ /** The display attrs one node should carry, by the position the node stands at in the document handed to the deriver */
33
+ export interface DisplayAttrs {
34
+ readonly pos: number;
35
+ readonly attrs: Attrs;
36
+ /** Omitted for node attrs. Otherwise updates an existing mark on this inline node. */
37
+ readonly mark?: {
38
+ readonly type: MarkType;
39
+ readonly to: number;
40
+ };
41
+ }
42
+ /**
43
+ * What says, for the node types it answers for, what one node's display attrs should be.
44
+ *
45
+ * `derive` is asked once per such node, in document order, with the node as the derivers before it
46
+ * left it. It hands back the display attrs of that node and of any node inside it, each by
47
+ * position - a table's deriver answers for the cells - or nothing where nothing changes; a source
48
+ * or session attr among what it hands back is not written. `previous` is the node this one maps
49
+ * back to in the document before the change, or null when there is none, or when every value is
50
+ * being worked out again from nothing.
51
+ */
52
+ export interface DisplayDeriver<Context> {
53
+ readonly name: string;
54
+ /**
55
+ * The block node types this deriver answers for. The walk runs over the blocks and never into
56
+ * the inline content of a textblock, since it runs after every keystroke, so an inline type
57
+ * cannot be answered for.
58
+ */
59
+ readonly nodeTypes: readonly NodeName[];
60
+ derive(node: PMNode, pos: number, doc: PMNode, context: Context, previous: PMNode | null): readonly DisplayAttrs[];
61
+ }
62
+ /**
63
+ * Writes the display attrs the derivers hand back, one `setNodeMarkup` per node whose attrs would
64
+ * change, onto the transform. It holds no state of its own: what a node maps back to comes from
65
+ * the caller, who knows what changed.
66
+ *
67
+ * Every step keeps the document the same size, so the positions gathered before the first write
68
+ * still hold, and a deriver asked after another reads the node as that one left it.
69
+ */
70
+ export declare function deriveDisplay<Context>(transform: Transform, context: Context, derivers: readonly DisplayDeriver<Context>[], previousOf: (node: PMNode, pos: number) => PMNode | null): void;
71
+ /**
72
+ * The pass a re-derivation carries. A plugin key is used as the name so that it cannot collide
73
+ * with a consumer's own metadata.
74
+ */
75
+ export declare const displayOnly: PluginKey<boolean>;
76
+ /**
77
+ * Whether the step changes display attrs and nothing else, judged off the role table alone.
78
+ *
79
+ * Node attrs and existing mark attrs may change only their display fields. Adding a mark where
80
+ * that type was absent, changing source attrs, and all other step shapes remain edits.
81
+ */
82
+ export declare function changesOnlyDisplayAttrs(step: Step, doc: PMNode): boolean;