@portone/docx-editor 0.2.1 → 0.4.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 (253) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/CONTRIBUTING.md +5 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +13 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +12 -7
  10. package/dist/docx/commentOnlyChange.js +9 -156
  11. package/dist/docx/comments/constants.d.ts +0 -1
  12. package/dist/docx/comments/constants.js +0 -2
  13. package/dist/docx/comments/grammar.d.ts +10 -4
  14. package/dist/docx/comments/grammar.js +12 -2
  15. package/dist/docx/comments/{verifying.d.ts → parts.d.ts} +26 -18
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +16 -92
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.js +8 -10
  22. package/dist/docx/comments/writing.d.ts +19 -7
  23. package/dist/docx/comments/writing.js +59 -112
  24. package/dist/docx/documentSettings.d.ts +7 -0
  25. package/dist/docx/documentSettings.js +10 -2
  26. package/dist/docx/exportDocx.d.ts +40 -5
  27. package/dist/docx/exportDocx.js +88 -74
  28. package/dist/docx/exportRefs.d.ts +5 -2
  29. package/dist/docx/exportRefs.js +3 -1
  30. package/dist/docx/fidelity.d.ts +43 -0
  31. package/dist/docx/fidelity.js +64 -0
  32. package/dist/docx/formatting/attrs.d.ts +45 -0
  33. package/dist/docx/formatting/attrs.js +86 -0
  34. package/dist/docx/formatting/context.d.ts +39 -0
  35. package/dist/docx/formatting/context.js +60 -0
  36. package/dist/docx/formatting/direct.d.ts +6 -5
  37. package/dist/docx/formatting/direct.js +52 -78
  38. package/dist/docx/formatting/resolve.d.ts +51 -0
  39. package/dist/docx/formatting/resolve.js +160 -0
  40. package/dist/docx/formatting/runProperties.d.ts +93 -0
  41. package/dist/docx/formatting/runProperties.js +316 -0
  42. package/dist/docx/formatting/styles.d.ts +25 -2
  43. package/dist/docx/formatting/styles.js +87 -6
  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 +6 -13
  48. package/dist/docx/hyperlink.d.ts +14 -4
  49. package/dist/docx/hyperlink.js +4 -1
  50. package/dist/docx/identities.d.ts +67 -0
  51. package/dist/docx/identities.js +174 -0
  52. package/dist/docx/importDocx.d.ts +12 -1
  53. package/dist/docx/importDocx.js +90 -88
  54. package/dist/docx/importParagraph.d.ts +1 -1
  55. package/dist/docx/importParagraph.js +4 -3
  56. package/dist/docx/importTable.d.ts +2 -2
  57. package/dist/docx/importTable.js +69 -56
  58. package/dist/docx/invariants.d.ts +33 -0
  59. package/dist/docx/invariants.js +269 -0
  60. package/dist/docx/media.d.ts +6 -4
  61. package/dist/docx/media.js +6 -37
  62. package/dist/docx/newLists.d.ts +50 -0
  63. package/dist/docx/newLists.js +59 -0
  64. package/dist/docx/notes.js +3 -7
  65. package/dist/docx/numberingPlanner.d.ts +11 -0
  66. package/dist/docx/numberingPlanner.js +50 -0
  67. package/dist/docx/packageParts.d.ts +42 -0
  68. package/dist/docx/packageParts.js +135 -0
  69. package/dist/docx/pageGeometry.d.ts +2 -0
  70. package/dist/docx/pageGeometry.js +18 -9
  71. package/dist/docx/paraProps.d.ts +10 -22
  72. package/dist/docx/paraProps.js +47 -76
  73. package/dist/docx/partPlan.d.ts +36 -0
  74. package/dist/docx/partPlan.js +59 -0
  75. package/dist/docx/protectionPolicy.d.ts +127 -0
  76. package/dist/docx/protectionPolicy.js +169 -0
  77. package/dist/docx/relationships.d.ts +1 -1
  78. package/dist/docx/relationships.js +8 -13
  79. package/dist/docx/runProps.d.ts +9 -22
  80. package/dist/docx/runProps.js +15 -168
  81. package/dist/docx/scan.js +20 -52
  82. package/dist/docx/sdt.js +9 -34
  83. package/dist/docx/sdtProps.d.ts +8 -1
  84. package/dist/docx/sdtProps.js +10 -0
  85. package/dist/docx/serializeBlock.d.ts +2 -0
  86. package/dist/docx/serializeBlock.js +8 -5
  87. package/dist/docx/serializeTable.js +32 -15
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +53 -17
  90. package/dist/docx/tableFormatting/conditions.d.ts +76 -0
  91. package/dist/docx/tableFormatting/conditions.js +196 -0
  92. package/dist/docx/tableFormatting/editing.d.ts +7 -3
  93. package/dist/docx/tableFormatting/editing.js +111 -143
  94. package/dist/docx/tableFormatting/reading.d.ts +63 -23
  95. package/dist/docx/tableFormatting/reading.js +132 -38
  96. package/dist/docx/tableFormatting.d.ts +1 -0
  97. package/dist/docx/tableFormatting.js +1 -0
  98. package/dist/docx/tableTemplate.js +30 -19
  99. package/dist/download.d.ts +8 -5
  100. package/dist/download.js +2 -0
  101. package/dist/editor/clipboard/inlineFormatting.js +19 -30
  102. package/dist/editor/commands/comments/editing.d.ts +6 -2
  103. package/dist/editor/commands/comments/editing.js +19 -31
  104. package/dist/editor/commands/exportQueries.d.ts +15 -0
  105. package/dist/editor/commands/exportQueries.js +14 -0
  106. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  107. package/dist/editor/commands/fidelityQueries.js +8 -0
  108. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  109. package/dist/editor/commands/formatting/editing.js +26 -108
  110. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  111. package/dist/editor/commands/formatting/propertyCommands.js +108 -0
  112. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  113. package/dist/editor/commands/formatting/shared.js +5 -2
  114. package/dist/editor/commands/indentCommands.js +5 -4
  115. package/dist/editor/commands/index.d.ts +11 -0
  116. package/dist/editor/commands/index.js +5 -0
  117. package/dist/editor/commands/linkCommands.js +5 -6
  118. package/dist/editor/commands/listCommands.d.ts +6 -3
  119. package/dist/editor/commands/listCommands.js +73 -30
  120. package/dist/editor/commands/lockCommands.d.ts +7 -1
  121. package/dist/editor/commands/lockCommands.js +35 -46
  122. package/dist/editor/commands/paragraphCommands.js +33 -31
  123. package/dist/editor/commands/spacingCommands.js +1 -1
  124. package/dist/editor/createEditor.d.ts +17 -29
  125. package/dist/editor/createEditor.js +58 -58
  126. package/dist/editor/documentStyles.d.ts +11 -35
  127. package/dist/editor/documentStyles.js +9 -52
  128. package/dist/editor/editorDocument.d.ts +57 -0
  129. package/dist/editor/editorDocument.js +86 -0
  130. package/dist/editor/externalClipboard.d.ts +13 -2
  131. package/dist/editor/externalClipboard.js +76 -75
  132. package/dist/editor/insertTable.js +4 -3
  133. package/dist/editor/paragraphEdits.d.ts +27 -19
  134. package/dist/editor/paragraphEdits.js +18 -19
  135. package/dist/editor/paragraphPlacement.d.ts +11 -0
  136. package/dist/editor/paragraphPlacement.js +18 -0
  137. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  138. package/dist/editor/plugins/displayDerivation.js +78 -0
  139. package/dist/editor/plugins/imagePaste.js +11 -7
  140. package/dist/editor/plugins/keymap.js +18 -3
  141. package/dist/editor/plugins/numberingDecorations.d.ts +14 -14
  142. package/dist/editor/plugins/numberingDecorations.js +50 -19
  143. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  144. package/dist/editor/plugins/paragraphDisplay.js +57 -0
  145. package/dist/editor/plugins/tabLayout.js +1 -1
  146. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  147. package/dist/editor/plugins/tableDisplay.js +30 -0
  148. package/dist/index.d.ts +2 -0
  149. package/dist/model/format.d.ts +80 -8
  150. package/dist/model/format.js +100 -22
  151. package/dist/model/tabStops.d.ts +9 -0
  152. package/dist/model/tabStops.js +18 -0
  153. package/dist/numbering/listRegistry.d.ts +37 -0
  154. package/dist/numbering/listRegistry.js +112 -0
  155. package/dist/numbering/listTemplate.d.ts +26 -17
  156. package/dist/numbering/listTemplate.js +88 -25
  157. package/dist/numbering/markers.d.ts +15 -6
  158. package/dist/numbering/markers.js +26 -59
  159. package/dist/numbering/parseNumbering.d.ts +84 -6
  160. package/dist/numbering/parseNumbering.js +131 -44
  161. package/dist/numbering/spellers.d.ts +15 -0
  162. package/dist/numbering/spellers.js +96 -0
  163. package/dist/numbering/writeNumbering.d.ts +8 -7
  164. package/dist/numbering/writeNumbering.js +28 -39
  165. package/dist/ooxml/childOrder.d.ts +34 -0
  166. package/dist/ooxml/childOrder.js +496 -0
  167. package/dist/ooxml/conformance.d.ts +28 -0
  168. package/dist/ooxml/conformance.js +20 -0
  169. package/dist/ooxml/element.d.ts +24 -11
  170. package/dist/ooxml/element.js +31 -12
  171. package/dist/ooxml/errors.d.ts +6 -5
  172. package/dist/ooxml/image.d.ts +4 -0
  173. package/dist/ooxml/image.js +2 -5
  174. package/dist/ooxml/names.d.ts +9 -0
  175. package/dist/ooxml/names.js +8 -1
  176. package/dist/ooxml/partSplice.d.ts +67 -0
  177. package/dist/ooxml/partSplice.js +176 -0
  178. package/dist/ooxml/props.d.ts +112 -0
  179. package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
  180. package/dist/ooxml/simpleTypes.d.ts +103 -0
  181. package/dist/ooxml/simpleTypes.js +182 -0
  182. package/dist/ooxml/tabStops.js +8 -27
  183. package/dist/ooxml/tagScan.d.ts +34 -0
  184. package/dist/ooxml/tagScan.js +108 -0
  185. package/dist/ooxml/units.d.ts +25 -13
  186. package/dist/ooxml/units.js +65 -31
  187. package/dist/ooxml/xml.d.ts +35 -15
  188. package/dist/ooxml/xml.js +43 -11
  189. package/dist/page/PageGuides.d.ts +1 -1
  190. package/dist/page/PageGuides.js +0 -13
  191. package/dist/page/blockKinds.d.ts +61 -0
  192. package/dist/page/blockKinds.js +11 -0
  193. package/dist/page/kinds/index.d.ts +6 -0
  194. package/dist/page/kinds/index.js +10 -0
  195. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  196. package/dist/page/kinds/paragraphKind.js +71 -0
  197. package/dist/page/kinds/tableKind.d.ts +10 -0
  198. package/dist/page/kinds/tableKind.js +178 -0
  199. package/dist/page/measureBlocks.d.ts +3 -14
  200. package/dist/page/measureBlocks.js +24 -45
  201. package/dist/page/pageDecorations.d.ts +27 -44
  202. package/dist/page/pageDecorations.js +70 -152
  203. package/dist/page/pageLayout.d.ts +2 -29
  204. package/dist/page/pageLayout.js +39 -16
  205. package/dist/page/usePageLayout.d.ts +2 -24
  206. package/dist/page/usePageLayout.js +5 -48
  207. package/dist/schema/attrRoles.d.ts +25 -18
  208. package/dist/schema/attrRoles.js +110 -68
  209. package/dist/schema/displayDerivation.d.ts +82 -0
  210. package/dist/schema/displayDerivation.js +130 -0
  211. package/dist/schema/docxSchema.d.ts +3 -0
  212. package/dist/schema/docxSchema.js +58 -17
  213. package/dist/schema/editGuard.d.ts +1 -1
  214. package/dist/schema/guards.d.ts +37 -14
  215. package/dist/schema/guards.js +29 -3
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +11 -44
  219. package/dist/schema/locks.js +0 -9
  220. package/dist/schema/preservedGuards.d.ts +26 -6
  221. package/dist/schema/preservedGuards.js +31 -1
  222. package/dist/schema/protection.d.ts +7 -1
  223. package/dist/schema/protection.js +2 -1
  224. package/dist/schema/sourceEquality.d.ts +1 -9
  225. package/dist/schema/sourceEquality.js +1 -27
  226. package/dist/styles/classNames.d.ts +13 -2
  227. package/dist/styles/classNames.js +13 -2
  228. package/dist/styles/inlineStyle.js +15 -6
  229. package/dist/styles.css +16 -18
  230. package/dist/table/cellFormatting.d.ts +8 -1
  231. package/dist/table/cellFormatting.js +11 -20
  232. package/dist/table/commands.js +20 -18
  233. package/dist/table/format.d.ts +3 -0
  234. package/dist/table/format.js +3 -9
  235. package/dist/table/gridBorders.d.ts +24 -30
  236. package/dist/table/gridBorders.js +35 -59
  237. package/dist/table/merge.d.ts +2 -6
  238. package/dist/table/merge.js +5 -5
  239. package/package.json +7 -3
  240. package/dist/docx/comments/contentTypes.d.ts +0 -7
  241. package/dist/docx/comments/contentTypes.js +0 -38
  242. package/dist/docx/comments/verifying.js +0 -206
  243. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  244. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  245. package/dist/docx/propsXml.d.ts +0 -67
  246. package/dist/docx/uniqueControls.d.ts +0 -14
  247. package/dist/docx/uniqueControls.js +0 -62
  248. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  249. package/dist/editor/plugins/commentReservations.js +0 -26
  250. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  251. package/dist/editor/plugins/styledParagraphs.js +0 -65
  252. package/dist/page/tableMeasurements.d.ts +0 -18
  253. package/dist/page/tableMeasurements.js +0 -119
@@ -1,96 +1,31 @@
1
1
  // src/page/pageDecorations.ts
2
- import { Plugin, PluginKey } from "prosemirror-state";
2
+ import {
3
+ Plugin,
4
+ PluginKey
5
+ } from "prosemirror-state";
3
6
  import { Decoration, DecorationSet } from "prosemirror-view";
4
- import { docxSchema, isPageBreak } from "../schema/index.js";
5
7
  import { editorAttributes } from "../styles/classNames.js";
8
+ import { blockKindFor } from "./blockKinds.js";
9
+ import { DEFAULT_BLOCK_KINDS } from "./kinds/index.js";
6
10
  var marksKey = new PluginKey("docxPageDecorations");
7
11
  function pushStyle(marginTop) {
8
12
  return `margin-block-start:${marginTop}px`;
9
13
  }
10
- function spaceStyle(height) {
11
- return `display:block;height:${height}px`;
12
- }
13
- function isPageBreakNode(node) {
14
- return node?.type === docxSchema.nodes.hardBreak && isPageBreak(node.attrs.brAttrs);
15
- }
16
- function pageBreaksIn(block, blockPos) {
17
- const found = [];
18
- if (block.type !== docxSchema.nodes.paragraph) return found;
19
- block.forEach((child, offset) => {
20
- if (isPageBreakNode(child)) {
21
- found.push({ at: blockPos + 1 + offset, size: child.nodeSize });
22
- }
23
- });
24
- return found;
25
- }
26
- function breakSpaces(block, blockPos, heights, into) {
27
- for (const { at, size } of pageBreaksIn(block, blockPos)) {
28
- const height = heights.get(at) ?? 0;
29
- into.push(
30
- Decoration.inline(at, at + size, {
31
- nodeName: "span",
32
- style: spaceStyle(height),
33
- [editorAttributes.pageBreakSpace]: `${height}`
34
- })
35
- );
14
+ function cutsByBlock(doc, cuts) {
15
+ const byBlock = /* @__PURE__ */ new Map();
16
+ for (const cut of cuts) {
17
+ const $at = doc.resolve(cut.at);
18
+ if ($at.depth === 0) continue;
19
+ const blockPos = $at.before(1);
20
+ const found = byBlock.get(blockPos);
21
+ if (found) found.push(cut);
22
+ else byBlock.set(blockPos, [cut]);
36
23
  }
24
+ return byBlock;
37
25
  }
38
- function spaceMarks(doc, spaces) {
39
- return spaces.flatMap((space) => {
40
- const block = doc.nodeAt(space.pos);
41
- const found = block ? pageBreaksIn(block, space.pos)[space.index] : null;
42
- return found ? [{ at: found.at, height: space.height }] : [];
43
- });
44
- }
45
- function tableSpace(continuation) {
46
- const row = document.createElement("tr");
47
- row.setAttribute(editorAttributes.tablePageSpace, `${continuation.height}`);
48
- row.setAttribute("aria-hidden", "true");
49
- row.setAttribute("contenteditable", "false");
50
- row.style.height = `${continuation.height}px`;
51
- const cell = document.createElement("td");
52
- cell.colSpan = continuation.columns;
53
- cell.style.height = `${continuation.height}px`;
54
- row.append(cell);
55
- return row;
56
- }
57
- function repeatedHeader(view, rowPos) {
58
- const source = view.nodeDOM(rowPos);
59
- const row = source instanceof HTMLElement && source.tagName === "TR" ? source.cloneNode(true) : document.createElement("tr");
60
- row.setAttribute(editorAttributes.tableRepeatedHeader, "");
61
- row.setAttribute("aria-hidden", "true");
62
- row.setAttribute("contenteditable", "false");
63
- row.removeAttribute("id");
64
- row.querySelectorAll("[id]").forEach((element) => {
65
- element.removeAttribute("id");
66
- });
67
- return row;
68
- }
69
- function tableContinuationDecorations(continuations, into) {
70
- continuations.forEach((continuation) => {
71
- into.push(
72
- Decoration.widget(continuation.pos, () => tableSpace(continuation), {
73
- key: `table-page-space-${continuation.pos}-${continuation.height}-${continuation.columns}`,
74
- side: -100
75
- })
76
- );
77
- continuation.headerRows.forEach((rowPos, headerIndex) => {
78
- into.push(
79
- Decoration.widget(
80
- continuation.pos,
81
- (view) => repeatedHeader(view, rowPos),
82
- {
83
- key: `table-repeated-header-${continuation.pos}-${headerIndex}-` + continuation.headerSignature,
84
- side: -90 + headerIndex
85
- }
86
- )
87
- );
88
- });
89
- });
90
- }
91
- function decorationsFor(doc, pushes, spaces, tableContinuations) {
26
+ function decorationsFor(doc, kinds, pushes, cuts) {
92
27
  const byPos = new Map(pushes.map((push) => [push.pos, push]));
93
- const heights = new Map(spaces.map((space) => [space.at, space.height]));
28
+ const byBlock = cutsByBlock(doc, cuts);
94
29
  const decorations = [];
95
30
  doc.forEach((node, offset) => {
96
31
  const push = byPos.get(offset);
@@ -102,65 +37,63 @@ function decorationsFor(doc, pushes, spaces, tableContinuations) {
102
37
  })
103
38
  );
104
39
  }
105
- breakSpaces(node, offset, heights, decorations);
40
+ blockKindFor(kinds, node).decorate(
41
+ offset,
42
+ node,
43
+ byBlock.get(offset) ?? [],
44
+ decorations
45
+ );
106
46
  });
107
- tableContinuationDecorations(tableContinuations, decorations);
108
47
  return DecorationSet.create(doc, decorations);
109
48
  }
110
- function marksFor(doc, pushes, spaces, tableContinuations) {
49
+ function marksFor(doc, kinds, pushes, cuts) {
111
50
  return {
51
+ kinds,
112
52
  pushes,
113
- spaces,
114
- tableContinuations,
115
- decorations: decorationsFor(doc, pushes, spaces, tableContinuations)
53
+ cuts,
54
+ decorations: decorationsFor(doc, kinds, pushes, cuts)
116
55
  };
117
56
  }
118
- function samePushes(a, b) {
119
- return a.length === b.length && a.every((push, index) => {
120
- const other = b[index];
121
- return other !== void 0 && other.pos === push.pos && other.marginTop === push.marginTop;
122
- });
123
- }
124
- function sameSpaces(a, b) {
125
- return a.length === b.length && a.every((space, index) => {
126
- const other = b[index];
127
- return other !== void 0 && other.at === space.at && other.height === space.height;
128
- });
129
- }
130
- function sameTableContinuations(a, b) {
131
- return a.length === b.length && a.every((continuation, index) => {
132
- const other = b[index];
133
- return other !== void 0 && other.pos === continuation.pos && other.height === continuation.height && other.columns === continuation.columns && other.headerSignature === continuation.headerSignature && other.headerRows.length === continuation.headerRows.length && other.headerRows.every(
134
- (rowPos, rowIndex) => rowPos === continuation.headerRows[rowIndex]
135
- );
57
+ function stillCuts(kinds, doc, at) {
58
+ const $at = doc.resolve(at);
59
+ if ($at.depth === 0) return false;
60
+ const block = doc.nodeAt($at.before(1));
61
+ return block !== null && blockKindFor(kinds, block).holdsCut(doc, at);
62
+ }
63
+ function retainsContent(tr, at, mapped) {
64
+ const node = tr.before.nodeAt(at);
65
+ if (!node || node.isLeaf) return false;
66
+ const content = tr.mapping.mapResult(at + 1, -1);
67
+ return !content.deletedAcross && content.pos === mapped + 1;
68
+ }
69
+ function samePageMarks(a, b) {
70
+ return a.pushes.length === b.pushes.length && a.cuts.length === b.cuts.length && a.pushes.every((push, index) => {
71
+ const other = b.pushes[index];
72
+ return other !== void 0 && other.pos === push.pos && other.marginTop === push.marginTop && other.push === push.push;
73
+ }) && a.cuts.every((cut, index) => {
74
+ const other = b.cuts[index];
75
+ return other !== void 0 && other.at === cut.at && other.height === cut.height;
136
76
  });
137
77
  }
138
- function pageDecorations() {
78
+ function pageDecorations(kinds = DEFAULT_BLOCK_KINDS) {
139
79
  return new Plugin({
140
80
  key: marksKey,
141
81
  state: {
142
- init: (_config, state) => marksFor(state.doc, [], [], []),
82
+ init: (_config, state) => marksFor(state.doc, kinds, [], []),
143
83
  apply(tr, value) {
144
84
  const next = tr.getMeta(marksKey);
145
85
  if (next) return next;
146
86
  if (!tr.docChanged) return value;
147
87
  return marksFor(
148
88
  tr.doc,
89
+ value.kinds,
149
90
  value.pushes.map((push) => ({
150
91
  ...push,
151
92
  pos: tr.mapping.map(push.pos)
152
93
  })),
153
- value.spaces.flatMap((space) => {
154
- const at = tr.mapping.map(space.at);
155
- return isPageBreakNode(tr.doc.nodeAt(at)) ? [{ ...space, at }] : [];
156
- }),
157
- value.tableContinuations.flatMap((continuation) => {
158
- const pos = tr.mapping.map(continuation.pos);
159
- if (tr.doc.nodeAt(pos)?.type.spec.tableRole !== "row") return [];
160
- const headerRows = continuation.headerRows.map((rowPos) => tr.mapping.map(rowPos)).filter(
161
- (rowPos) => tr.doc.nodeAt(rowPos)?.type.spec.tableRole === "row"
162
- );
163
- return [{ ...continuation, pos, headerRows }];
94
+ value.cuts.flatMap((cut) => {
95
+ const mapped = tr.mapping.mapResult(cut.at, 1);
96
+ return !stillCuts(value.kinds, tr.doc, mapped.pos) || mapped.deleted && !retainsContent(tr, cut.at, mapped.pos) ? [] : [{ ...cut, at: mapped.pos }];
164
97
  })
165
98
  );
166
99
  }
@@ -170,43 +103,28 @@ function pageDecorations() {
170
103
  }
171
104
  });
172
105
  }
106
+ function blockKindsOf(state) {
107
+ return marksKey.getState(state)?.kinds ?? DEFAULT_BLOCK_KINDS;
108
+ }
173
109
  function current(view) {
174
- return marksKey.getState(view.state) ?? marksFor(view.state.doc, [], [], []);
110
+ return marksKey.getState(view.state) ?? { pushes: [], cuts: [] };
175
111
  }
176
- function dispatch(view, next) {
112
+ function setPageMarks(view, next) {
113
+ if (samePageMarks(current(view), next)) return;
177
114
  view.dispatch(
178
- view.state.tr.setMeta(marksKey, next).setMeta("addToHistory", false)
179
- );
180
- }
181
- function setPagePushes(view, pushes) {
182
- const marks = current(view);
183
- if (samePushes(marks.pushes, pushes)) return;
184
- dispatch(
185
- view,
186
- marksFor(view.state.doc, pushes, marks.spaces, marks.tableContinuations)
187
- );
188
- }
189
- function setPageBreakSpaces(view, spaces) {
190
- const marks = current(view);
191
- const next = spaceMarks(view.state.doc, spaces);
192
- if (sameSpaces(marks.spaces, next)) return;
193
- dispatch(
194
- view,
195
- marksFor(view.state.doc, marks.pushes, next, marks.tableContinuations)
196
- );
197
- }
198
- function setTableContinuations(view, continuations) {
199
- const marks = current(view);
200
- if (sameTableContinuations(marks.tableContinuations, continuations)) return;
201
- dispatch(
202
- view,
203
- marksFor(view.state.doc, marks.pushes, marks.spaces, continuations)
115
+ view.state.tr.setMeta(
116
+ marksKey,
117
+ marksFor(
118
+ view.state.doc,
119
+ blockKindsOf(view.state),
120
+ next.pushes,
121
+ next.cuts
122
+ )
123
+ ).setMeta("addToHistory", false)
204
124
  );
205
125
  }
206
126
  export {
207
- pageBreaksIn,
127
+ blockKindsOf,
208
128
  pageDecorations,
209
- setPageBreakSpaces,
210
- setPagePushes,
211
- setTableContinuations
129
+ setPageMarks
212
130
  };
@@ -47,14 +47,6 @@ export declare const A4_PAGE_PIXELS: PagePixels;
47
47
  * moment before a document is opened.
48
48
  */
49
49
  export declare function pageGeometryStyle(page: PagePixels): string;
50
- /** The space opened up at one page break, so that what follows it starts the next page */
51
- export interface BreakSpace {
52
- /** The position where the block holding the break starts */
53
- pos: number;
54
- /** Which break inside that block, counted in document order */
55
- index: number;
56
- height: number;
57
- }
58
50
  /** One block to be moved down to the next page */
59
51
  export interface BlockPush {
60
52
  pos: number;
@@ -63,17 +55,6 @@ export interface BlockPush {
63
55
  /** Of that, the extra amount pushed for the page's sake */
64
56
  push: number;
65
57
  }
66
- /** A display-only row inserted before the row that continues on the next page */
67
- export interface TableContinuation {
68
- /** The position of the first original row on the next page */
69
- pos: number;
70
- /** Space from the previous page's row boundary to the next page body */
71
- height: number;
72
- /** Header rows to project immediately after the space */
73
- headerRows: readonly number[];
74
- headerSignature: string;
75
- columns: number;
76
- }
77
58
  /** A place where one page parts from the next */
78
59
  export interface PageSplit {
79
60
  /** Where the previous page's body ends, measured from the top of the body */
@@ -82,11 +63,6 @@ export interface PageSplit {
82
63
  page: number;
83
64
  /** Whether the document records a break at this place */
84
65
  forced: boolean;
85
- /**
86
- * Whether every place before this one was recorded by the document, so the number can
87
- * be trusted
88
- */
89
- exactPage: boolean;
90
66
  /**
91
67
  * Whether the block is taller than one page, so it cannot be pushed and the text
92
68
  * crosses this place
@@ -98,11 +74,6 @@ export interface PageStart {
98
74
  page: number;
99
75
  /** Where this page's body starts, measured from the top of the body */
100
76
  bodyStart: number;
101
- /**
102
- * Whether every place before this one was recorded by the document, so the number can
103
- * be trusted
104
- */
105
- exactPage: boolean;
106
77
  /**
107
78
  * Whether text crosses over from the previous page, so this page continues with no top
108
79
  * margin
@@ -137,5 +108,7 @@ export interface PageLayoutInput {
137
108
  * written into the text - is filled out to the end of the page it falls on, which carries the rest
138
109
  * of that block to the top of the next page while the block itself stays whole. An optional one -
139
110
  * a row boundary in a table - is taken only where the piece after it would run off the page.
111
+ * A block kept with the next one is pushed together with what it is kept with: the blocks kept
112
+ * after it and the first piece of the block the keeps end at.
140
113
  */
141
114
  export declare function pageLayout({ blocks, pageBodyHeight, pageStep, }: PageLayoutInput): PageLayout;
@@ -41,6 +41,34 @@ 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,
@@ -49,39 +77,34 @@ function pageLayout({
49
77
  const pushes = [];
50
78
  const cuts = [];
51
79
  const splits = [];
52
- const firstPage = {
53
- page: 1,
54
- bodyStart: 0,
55
- exactPage: true,
56
- crossed: false
57
- };
80
+ const firstPage = { page: 1, bodyStart: 0, crossed: false };
58
81
  const pages = [firstPage];
59
82
  if (!(pageBodyHeight > 0)) {
60
83
  return { pushes, cuts, splits, pages, bodyHeight: 0 };
61
84
  }
62
85
  let pageStart = 0;
63
86
  let cursor = 0;
64
- let exactPage = true;
65
87
  const split = (y, crossed, forced) => {
66
- if (!forced) exactPage = false;
67
88
  const page = splits.length + 2;
68
- splits.push({ y: round(y), page, forced, exactPage, crossed });
89
+ splits.push({ y: round(y), page, forced, crossed });
69
90
  pageStart = crossed ? y : y + pageStep;
70
- pages.push({ page, bodyStart: round(pageStart), exactPage, crossed });
91
+ pages.push({ page, bodyStart: round(pageStart), crossed });
71
92
  };
72
93
  const crossTo = (y) => {
73
94
  while (y > pageStart + pageBodyHeight + TOLERANCE_PX) {
74
95
  split(pageStart + pageBodyHeight, true, false);
75
96
  }
76
97
  };
98
+ const fitsPage = (extent) => extent <= pageBodyHeight + TOLERANCE_PX;
99
+ const kept = keptExtents(blocks, fitsPage);
77
100
  let breakAfterPrevious = false;
78
- for (const block of blocks) {
101
+ for (const [index, block] of blocks.entries()) {
79
102
  const pageEnd = pageStart + pageBodyHeight;
80
103
  const top = cursor + block.gap;
81
104
  const startsPage = (block.breakBefore || breakAfterPrevious) && top > pageStart + TOLERANCE_PX;
82
- const first = block.minFirstPiece;
105
+ const first = kept.get(index) ?? block.minFirstPiece;
83
106
  const overflows = top + first > pageEnd + TOLERANCE_PX;
84
- const fits = first <= pageBodyHeight + TOLERANCE_PX;
107
+ const fits = fitsPage(first);
85
108
  const push = startsPage || overflows && fits ? Math.max(0, pageEnd + pageStep - top) : 0;
86
109
  if (push > 0 || startsPage) split(pageEnd, false, startsPage);
87
110
  if (push > 0) {
@@ -94,9 +117,9 @@ function pageLayout({
94
117
  const contentTop = top + push;
95
118
  let added = 0;
96
119
  let pieceStart = 0;
97
- for (let index = 0; index <= block.candidates.length; index += 1) {
98
- const cut = index === 0 ? null : block.candidates[index - 1];
99
- const pieceEnd = block.candidates[index]?.offset ?? block.height;
120
+ for (let index2 = 0; index2 <= block.candidates.length; index2 += 1) {
121
+ const cut = index2 === 0 ? null : block.candidates[index2 - 1];
122
+ const pieceEnd = block.candidates[index2]?.offset ?? block.height;
100
123
  const pieceTop = contentTop + pieceStart + added;
101
124
  let pieceBottom = contentTop + pieceEnd + added;
102
125
  if (cut) {
@@ -3,18 +3,14 @@
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
  */
11
- import type { Node as PMNode } from "prosemirror-model";
12
11
  import type { EditorView } from "prosemirror-view";
13
12
  import { type RefObject } from "react";
14
13
  import type { PageGeometry } from "../docx/pageGeometry";
15
- import type { PageCut } from "./blockKinds";
16
- import { type TableHeaderProjection } from "./measureBlocks";
17
- import { type BreakSpace, type TableContinuation } from "./pageLayout";
18
14
  /** One place where a page parts from the next. The position is measured on the sheet */
19
15
  export interface PageMark {
20
16
  page: number;
@@ -23,12 +19,6 @@ export interface PageMark {
23
19
  height: number;
24
20
  crossed: boolean;
25
21
  }
26
- /** The number laid on the corner of a page */
27
- export interface PageBadge {
28
- page: number;
29
- top: number;
30
- exactPage: boolean;
31
- }
32
22
  /** The paper area of one visual page, used to place its header and footer stories. */
33
23
  export interface PageFace {
34
24
  page: number;
@@ -45,7 +35,6 @@ export interface PageOverlay {
45
35
  /** The sheet height with the last page filled out in full */
46
36
  sheetHeight: number;
47
37
  marks: PageMark[];
48
- badges: PageBadge[];
49
38
  pages: PageFace[];
50
39
  }
51
40
  interface PageLayoutOptions {
@@ -57,16 +46,5 @@ interface PageLayoutOptions {
57
46
  /** The paper the open document names. A4 where a document names none */
58
47
  geometry?: PageGeometry;
59
48
  }
60
- /**
61
- * The layout's cuts as the two kinds of mark the decorations still take.
62
- *
63
- * A cut names the position the continued piece starts at, and which of the two it is follows from
64
- * what stands there: a page `br` the block it sits in counted, or a table row. This stands in
65
- * until the decorations take the cuts themselves.
66
- */
67
- export declare function cutsToLegacyMarks(cuts: readonly PageCut[], tables: ReadonlyMap<number, TableHeaderProjection>, doc: PMNode): {
68
- spaces: BreakSpace[];
69
- tableContinuations: TableContinuation[];
70
- };
71
49
  export declare function usePageLayout({ view, layer, enabled, revision, geometry, }: PageLayoutOptions): PageOverlay | null;
72
50
  export {};
@@ -8,19 +8,13 @@ import {
8
8
  } from "react";
9
9
  import { editorCssVariables } from "../styles/classNames.js";
10
10
  import { measureSheet } from "./measureBlocks.js";
11
- import {
12
- pageBreaksIn,
13
- setPageBreakSpaces,
14
- setPagePushes,
15
- setTableContinuations
16
- } from "./pageDecorations.js";
11
+ import { setPageMarks } from "./pageDecorations.js";
17
12
  import {
18
13
  A4_PAGE_PIXELS,
19
14
  PAGE_SPLIT_PX,
20
15
  pageLayout,
21
16
  pagePixels
22
17
  } from "./pageLayout.js";
23
- var BADGE_INSET_PX = 8;
24
18
  function useFrameThrottle(run, hold) {
25
19
  const latest = useRef({ run, hold });
26
20
  const frame = useRef(0);
@@ -51,28 +45,7 @@ function useFrameThrottle(run, hold) {
51
45
  }, []);
52
46
  }
53
47
  function sameOverlay(a, b) {
54
- return a !== null && a.left === b.left && a.top === b.top && a.width === b.width && a.sheetHeight === b.sheetHeight && JSON.stringify(a.marks) === JSON.stringify(b.marks) && JSON.stringify(a.badges) === JSON.stringify(b.badges) && JSON.stringify(a.pages) === JSON.stringify(b.pages);
55
- }
56
- function cutsToLegacyMarks(cuts, tables, doc) {
57
- const spaces = [];
58
- const tableContinuations = [];
59
- for (const cut of cuts) {
60
- const pos = doc.resolve(cut.at).before(1);
61
- const block = doc.nodeAt(pos);
62
- if (!block) continue;
63
- const index = pageBreaksIn(block, pos).findIndex(
64
- (found) => found.at === cut.at
65
- );
66
- if (index >= 0) {
67
- spaces.push({ pos, index, height: cut.height });
68
- continue;
69
- }
70
- const headers = tables.get(pos);
71
- if (headers) {
72
- tableContinuations.push({ pos: cut.at, height: cut.height, ...headers });
73
- }
74
- }
75
- return { spaces, tableContinuations };
48
+ return a !== null && a.left === b.left && a.top === b.top && a.width === b.width && a.sheetHeight === b.sheetHeight && JSON.stringify(a.marks) === JSON.stringify(b.marks) && JSON.stringify(a.pages) === JSON.stringify(b.pages);
76
49
  }
77
50
  function usePageLayout({
78
51
  view,
@@ -96,14 +69,7 @@ function usePageLayout({
96
69
  pageBodyHeight: page.bodyHeight,
97
70
  pageStep: page.pageStep
98
71
  });
99
- const marks = cutsToLegacyMarks(
100
- layout.cuts,
101
- measured.tables,
102
- view.state.doc
103
- );
104
- setPagePushes(view, layout.pushes);
105
- setPageBreakSpaces(view, marks.spaces);
106
- setTableContinuations(view, marks.tableContinuations);
72
+ setPageMarks(view, { pushes: layout.pushes, cuts: layout.cuts });
107
73
  const sheetHeight = measured.contentTop + layout.bodyHeight + measured.contentBottom;
108
74
  box.style.setProperty(editorCssVariables.sheetHeight, `${sheetHeight}px`);
109
75
  const next = {
@@ -117,13 +83,7 @@ function usePageLayout({
117
83
  height: split.crossed ? 0 : PAGE_SPLIT_PX,
118
84
  crossed: split.crossed
119
85
  })),
120
- // The number is laid on the page's top corner. A page crossed into has no margin,
121
- // so the place it was split at is its corner
122
- badges: layout.pages.map((start) => ({
123
- page: start.page,
124
- top: measured.contentTop + start.bodyStart - (start.crossed ? 0 : page.marginTop) + BADGE_INSET_PX,
125
- exactPage: start.exactPage
126
- })),
86
+ // A page crossed into has no margin, so the place it was split at is its top corner
127
87
  pages: layout.pages.map((start) => {
128
88
  const paperTop = measured.contentTop + start.bodyStart - (start.crossed ? 0 : page.marginTop);
129
89
  return {
@@ -147,9 +107,7 @@ function usePageLayout({
147
107
  if (enabled) return;
148
108
  layer.current?.style.removeProperty(editorCssVariables.sheetHeight);
149
109
  if (!view) return;
150
- setPagePushes(view, []);
151
- setPageBreakSpaces(view, []);
152
- setTableContinuations(view, []);
110
+ setPageMarks(view, { pushes: [], cuts: [] });
153
111
  }, [enabled, layer, view]);
154
112
  useEffect(() => {
155
113
  const box = layer.current;
@@ -164,6 +122,5 @@ function usePageLayout({
164
122
  return enabled ? overlay : null;
165
123
  }
166
124
  export {
167
- cutsToLegacyMarks,
168
125
  usePageLayout
169
126
  };
@@ -1,27 +1,32 @@
1
1
  /**
2
- * What each attr of the schema is for, so that a judgement about a node can say which of them it
3
- * is judging.
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.
4
9
  *
5
- * An attr is one of three things. A `source` attr is what the exporter writes from: the XML the
6
- * file arrived as, or a value the model owns that replaces part of it. A `display` attr is worked
7
- * out from the source and the formatting around it, is never written, and is free to change the
8
- * moment the surroundings do. A `session` attr says which block, control or link of the open
9
- * document this is, and is never written either.
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.
10
13
  *
11
- * The split exists because a display value changing is not the document changing. Opening a file
12
- * in the editor re-derives a table's cell borders (`table/gridBorders`), and a comparison that
13
- * counted that as an edit would rewrite a table nobody touched, losing the markup the writer does
14
- * not model. `./sourceEquality` leaves display attrs out for that reason, and keeps session attrs
15
- * in: two blocks that came from different places in the file are different blocks.
16
- *
17
- * `attrRoles.test.ts` holds this table against the schema in both directions, so an attr added
18
- * without a role fails there before it can reach a judgement that does not know what to do with it.
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.
19
18
  */
20
19
  import { type MarkType, NodeType } from "prosemirror-model";
21
20
  export type AttrRole = "source" | "display" | "session";
22
- type AttrRoles = Readonly<Record<string, Readonly<Record<string, AttrRole>>>>;
23
- export declare const NODE_ATTR_ROLES: AttrRoles;
24
- export declare const MARK_ATTR_ROLES: AttrRoles;
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;
25
30
  /**
26
31
  * What this attr of this node or mark is for.
27
32
  *
@@ -32,4 +37,6 @@ export declare const MARK_ATTR_ROLES: AttrRoles;
32
37
  export declare function attrRole(type: NodeType | MarkType, name: string): AttrRole;
33
38
  /** The attrs of this node or mark that are worked out rather than written */
34
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[];
35
42
  export {};