@portone/docx-editor 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/CONTRIBUTING.md +5 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +12 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +12 -7
  10. package/dist/docx/commentOnlyChange.js +9 -156
  11. package/dist/docx/comments/constants.d.ts +0 -1
  12. package/dist/docx/comments/constants.js +0 -2
  13. package/dist/docx/comments/grammar.d.ts +10 -4
  14. package/dist/docx/comments/grammar.js +12 -2
  15. package/dist/docx/comments/{verifying.d.ts → parts.d.ts} +26 -18
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +16 -92
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.js +8 -10
  22. package/dist/docx/comments/writing.d.ts +19 -7
  23. package/dist/docx/comments/writing.js +52 -111
  24. package/dist/docx/documentSettings.d.ts +7 -0
  25. package/dist/docx/documentSettings.js +10 -2
  26. package/dist/docx/exportDocx.d.ts +40 -5
  27. package/dist/docx/exportDocx.js +69 -71
  28. package/dist/docx/exportRefs.d.ts +5 -2
  29. package/dist/docx/exportRefs.js +3 -1
  30. package/dist/docx/fidelity.d.ts +43 -0
  31. package/dist/docx/fidelity.js +64 -0
  32. package/dist/docx/formatting/attrs.d.ts +27 -0
  33. package/dist/docx/formatting/attrs.js +31 -0
  34. package/dist/docx/formatting/context.d.ts +31 -0
  35. package/dist/docx/formatting/context.js +42 -0
  36. package/dist/docx/formatting/direct.d.ts +6 -5
  37. package/dist/docx/formatting/direct.js +52 -78
  38. package/dist/docx/formatting/resolve.d.ts +45 -0
  39. package/dist/docx/formatting/resolve.js +140 -0
  40. package/dist/docx/formatting/runProperties.d.ts +93 -0
  41. package/dist/docx/formatting/runProperties.js +316 -0
  42. package/dist/docx/formatting/styles.js +3 -3
  43. package/dist/docx/formatting/tabStops.js +7 -22
  44. package/dist/docx/formatting.d.ts +4 -1
  45. package/dist/docx/formatting.js +4 -1
  46. package/dist/docx/headersFooters.js +6 -13
  47. package/dist/docx/identities.d.ts +67 -0
  48. package/dist/docx/identities.js +174 -0
  49. package/dist/docx/importDocx.d.ts +12 -1
  50. package/dist/docx/importDocx.js +72 -79
  51. package/dist/docx/importParagraph.d.ts +1 -1
  52. package/dist/docx/importParagraph.js +4 -3
  53. package/dist/docx/importTable.d.ts +1 -1
  54. package/dist/docx/importTable.js +17 -1
  55. package/dist/docx/invariants.d.ts +33 -0
  56. package/dist/docx/invariants.js +256 -0
  57. package/dist/docx/media.d.ts +6 -4
  58. package/dist/docx/media.js +6 -37
  59. package/dist/docx/newLists.d.ts +20 -0
  60. package/dist/docx/newLists.js +36 -0
  61. package/dist/docx/notes.js +3 -7
  62. package/dist/docx/numberingPlanner.d.ts +8 -0
  63. package/dist/docx/numberingPlanner.js +19 -0
  64. package/dist/docx/packageParts.d.ts +42 -0
  65. package/dist/docx/packageParts.js +135 -0
  66. package/dist/docx/pageGeometry.d.ts +2 -0
  67. package/dist/docx/pageGeometry.js +18 -9
  68. package/dist/docx/paraProps.d.ts +10 -22
  69. package/dist/docx/paraProps.js +47 -76
  70. package/dist/docx/partPlan.d.ts +36 -0
  71. package/dist/docx/partPlan.js +59 -0
  72. package/dist/docx/protectionPolicy.d.ts +127 -0
  73. package/dist/docx/protectionPolicy.js +169 -0
  74. package/dist/docx/relationships.d.ts +1 -1
  75. package/dist/docx/relationships.js +8 -13
  76. package/dist/docx/runProps.d.ts +9 -22
  77. package/dist/docx/runProps.js +15 -168
  78. package/dist/docx/scan.js +20 -52
  79. package/dist/docx/sdt.js +9 -34
  80. package/dist/docx/sdtProps.d.ts +8 -1
  81. package/dist/docx/sdtProps.js +10 -0
  82. package/dist/docx/serializeBlock.d.ts +2 -0
  83. package/dist/docx/serializeBlock.js +8 -5
  84. package/dist/docx/serializeTable.js +32 -15
  85. package/dist/docx/session.d.ts +42 -14
  86. package/dist/docx/session.js +43 -17
  87. package/dist/docx/tableFormatting/editing.js +108 -139
  88. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  89. package/dist/docx/tableFormatting/reading.js +40 -20
  90. package/dist/docx/tableTemplate.js +18 -7
  91. package/dist/download.d.ts +8 -5
  92. package/dist/download.js +2 -0
  93. package/dist/editor/clipboard/inlineFormatting.js +19 -30
  94. package/dist/editor/commands/comments/editing.d.ts +6 -2
  95. package/dist/editor/commands/comments/editing.js +19 -31
  96. package/dist/editor/commands/exportQueries.d.ts +15 -0
  97. package/dist/editor/commands/exportQueries.js +14 -0
  98. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  99. package/dist/editor/commands/fidelityQueries.js +8 -0
  100. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  101. package/dist/editor/commands/formatting/editing.js +26 -108
  102. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  103. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  104. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  105. package/dist/editor/commands/formatting/shared.js +5 -2
  106. package/dist/editor/commands/indentCommands.js +5 -4
  107. package/dist/editor/commands/index.d.ts +11 -0
  108. package/dist/editor/commands/index.js +5 -0
  109. package/dist/editor/commands/linkCommands.js +5 -6
  110. package/dist/editor/commands/listCommands.js +8 -10
  111. package/dist/editor/commands/lockCommands.d.ts +7 -1
  112. package/dist/editor/commands/lockCommands.js +35 -46
  113. package/dist/editor/commands/paragraphCommands.js +28 -31
  114. package/dist/editor/commands/spacingCommands.js +1 -1
  115. package/dist/editor/createEditor.d.ts +17 -29
  116. package/dist/editor/createEditor.js +58 -58
  117. package/dist/editor/documentStyles.d.ts +11 -35
  118. package/dist/editor/documentStyles.js +9 -52
  119. package/dist/editor/editorDocument.d.ts +56 -0
  120. package/dist/editor/editorDocument.js +74 -0
  121. package/dist/editor/externalClipboard.js +16 -45
  122. package/dist/editor/insertTable.js +4 -3
  123. package/dist/editor/paragraphEdits.d.ts +11 -18
  124. package/dist/editor/paragraphEdits.js +7 -16
  125. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  126. package/dist/editor/plugins/displayDerivation.js +78 -0
  127. package/dist/editor/plugins/imagePaste.js +4 -3
  128. package/dist/editor/plugins/keymap.js +18 -3
  129. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  130. package/dist/editor/plugins/numberingDecorations.js +7 -17
  131. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  132. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  133. package/dist/editor/plugins/tabLayout.js +1 -1
  134. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  135. package/dist/editor/plugins/tableDisplay.js +16 -0
  136. package/dist/index.d.ts +2 -0
  137. package/dist/model/format.d.ts +30 -8
  138. package/dist/model/format.js +35 -22
  139. package/dist/model/tabStops.d.ts +9 -0
  140. package/dist/model/tabStops.js +18 -0
  141. package/dist/numbering/listTemplate.js +24 -7
  142. package/dist/numbering/parseNumbering.d.ts +14 -1
  143. package/dist/numbering/parseNumbering.js +35 -22
  144. package/dist/numbering/writeNumbering.d.ts +3 -4
  145. package/dist/numbering/writeNumbering.js +14 -25
  146. package/dist/ooxml/childOrder.d.ts +34 -0
  147. package/dist/ooxml/childOrder.js +496 -0
  148. package/dist/ooxml/element.d.ts +24 -11
  149. package/dist/ooxml/element.js +31 -12
  150. package/dist/ooxml/errors.d.ts +3 -2
  151. package/dist/ooxml/image.d.ts +4 -0
  152. package/dist/ooxml/image.js +2 -5
  153. package/dist/ooxml/partSplice.d.ts +67 -0
  154. package/dist/ooxml/partSplice.js +169 -0
  155. package/dist/ooxml/props.d.ts +112 -0
  156. package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
  157. package/dist/ooxml/simpleTypes.d.ts +103 -0
  158. package/dist/ooxml/simpleTypes.js +182 -0
  159. package/dist/ooxml/tabStops.js +8 -27
  160. package/dist/ooxml/tagScan.d.ts +34 -0
  161. package/dist/ooxml/tagScan.js +108 -0
  162. package/dist/ooxml/units.d.ts +25 -13
  163. package/dist/ooxml/units.js +65 -31
  164. package/dist/ooxml/xml.d.ts +27 -6
  165. package/dist/ooxml/xml.js +37 -5
  166. package/dist/page/blockKinds.d.ts +61 -0
  167. package/dist/page/blockKinds.js +11 -0
  168. package/dist/page/kinds/index.d.ts +6 -0
  169. package/dist/page/kinds/index.js +10 -0
  170. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  171. package/dist/page/kinds/paragraphKind.js +71 -0
  172. package/dist/page/kinds/tableKind.d.ts +10 -0
  173. package/dist/page/kinds/tableKind.js +178 -0
  174. package/dist/page/measureBlocks.d.ts +3 -14
  175. package/dist/page/measureBlocks.js +24 -45
  176. package/dist/page/pageDecorations.d.ts +27 -44
  177. package/dist/page/pageDecorations.js +70 -152
  178. package/dist/page/pageLayout.d.ts +2 -19
  179. package/dist/page/pageLayout.js +36 -6
  180. package/dist/page/usePageLayout.d.ts +2 -17
  181. package/dist/page/usePageLayout.js +3 -39
  182. package/dist/schema/attrRoles.d.ts +25 -18
  183. package/dist/schema/attrRoles.js +102 -67
  184. package/dist/schema/displayDerivation.d.ts +82 -0
  185. package/dist/schema/displayDerivation.js +130 -0
  186. package/dist/schema/docxSchema.d.ts +3 -0
  187. package/dist/schema/docxSchema.js +26 -14
  188. package/dist/schema/editGuard.d.ts +1 -1
  189. package/dist/schema/guards.d.ts +37 -14
  190. package/dist/schema/guards.js +29 -3
  191. package/dist/schema/index.d.ts +2 -0
  192. package/dist/schema/index.js +2 -0
  193. package/dist/schema/locks.d.ts +11 -44
  194. package/dist/schema/locks.js +0 -9
  195. package/dist/schema/preservedGuards.d.ts +26 -6
  196. package/dist/schema/preservedGuards.js +31 -1
  197. package/dist/schema/protection.d.ts +7 -1
  198. package/dist/schema/protection.js +2 -1
  199. package/dist/schema/sourceEquality.d.ts +1 -9
  200. package/dist/schema/sourceEquality.js +1 -27
  201. package/dist/styles/inlineStyle.js +15 -6
  202. package/dist/table/cellFormatting.d.ts +8 -1
  203. package/dist/table/cellFormatting.js +10 -18
  204. package/dist/table/commands.js +20 -18
  205. package/dist/table/format.d.ts +3 -0
  206. package/dist/table/format.js +3 -9
  207. package/dist/table/gridBorders.d.ts +14 -16
  208. package/dist/table/gridBorders.js +3 -41
  209. package/dist/table/merge.d.ts +2 -6
  210. package/dist/table/merge.js +5 -5
  211. package/package.json +7 -3
  212. package/dist/docx/comments/contentTypes.d.ts +0 -7
  213. package/dist/docx/comments/contentTypes.js +0 -38
  214. package/dist/docx/comments/verifying.js +0 -206
  215. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  216. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  217. package/dist/docx/propsXml.d.ts +0 -67
  218. package/dist/docx/uniqueControls.d.ts +0 -14
  219. package/dist/docx/uniqueControls.js +0 -62
  220. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  221. package/dist/editor/plugins/commentReservations.js +0 -26
  222. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  223. package/dist/editor/plugins/styledParagraphs.js +0 -65
  224. package/dist/page/tableMeasurements.d.ts +0 -18
  225. package/dist/page/tableMeasurements.js +0 -119
@@ -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 */
@@ -137,5 +118,7 @@ export interface PageLayoutInput {
137
118
  * written into the text - is filled out to the end of the page it falls on, which carries the rest
138
119
  * of that block to the top of the next page while the block itself stays whole. An optional one -
139
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.
140
123
  */
141
124
  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,
@@ -74,14 +102,16 @@ function pageLayout({
74
102
  split(pageStart + pageBodyHeight, true, false);
75
103
  }
76
104
  };
105
+ const fitsPage = (extent) => extent <= pageBodyHeight + TOLERANCE_PX;
106
+ const kept = keptExtents(blocks, fitsPage);
77
107
  let breakAfterPrevious = false;
78
- for (const block of blocks) {
108
+ for (const [index, block] of blocks.entries()) {
79
109
  const pageEnd = pageStart + pageBodyHeight;
80
110
  const top = cursor + block.gap;
81
111
  const startsPage = (block.breakBefore || breakAfterPrevious) && top > pageStart + TOLERANCE_PX;
82
- const first = block.minFirstPiece;
112
+ const first = kept.get(index) ?? block.minFirstPiece;
83
113
  const overflows = top + first > pageEnd + TOLERANCE_PX;
84
- const fits = first <= pageBodyHeight + TOLERANCE_PX;
114
+ const fits = fitsPage(first);
85
115
  const push = startsPage || overflows && fits ? Math.max(0, pageEnd + pageStep - top) : 0;
86
116
  if (push > 0 || startsPage) split(pageEnd, false, startsPage);
87
117
  if (push > 0) {
@@ -94,9 +124,9 @@ function pageLayout({
94
124
  const contentTop = top + push;
95
125
  let added = 0;
96
126
  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;
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;
100
130
  const pieceTop = contentTop + pieceStart + added;
101
131
  let pieceBottom = contentTop + pieceEnd + added;
102
132
  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;
@@ -57,16 +53,5 @@ interface PageLayoutOptions {
57
53
  /** The paper the open document names. A4 where a document names none */
58
54
  geometry?: PageGeometry;
59
55
  }
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
56
  export declare function usePageLayout({ view, layer, enabled, revision, geometry, }: PageLayoutOptions): PageOverlay | null;
72
57
  export {};
@@ -8,12 +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
- 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,
@@ -53,27 +48,6 @@ function useFrameThrottle(run, hold) {
53
48
  function sameOverlay(a, b) {
54
49
  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
50
  }
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 };
76
- }
77
51
  function usePageLayout({
78
52
  view,
79
53
  layer,
@@ -96,14 +70,7 @@ function usePageLayout({
96
70
  pageBodyHeight: page.bodyHeight,
97
71
  pageStep: page.pageStep
98
72
  });
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);
73
+ setPageMarks(view, { pushes: layout.pushes, cuts: layout.cuts });
107
74
  const sheetHeight = measured.contentTop + layout.bodyHeight + measured.contentBottom;
108
75
  box.style.setProperty(editorCssVariables.sheetHeight, `${sheetHeight}px`);
109
76
  const next = {
@@ -147,9 +114,7 @@ function usePageLayout({
147
114
  if (enabled) return;
148
115
  layer.current?.style.removeProperty(editorCssVariables.sheetHeight);
149
116
  if (!view) return;
150
- setPagePushes(view, []);
151
- setPageBreakSpaces(view, []);
152
- setTableContinuations(view, []);
117
+ setPageMarks(view, { pushes: [], cuts: [] });
153
118
  }, [enabled, layer, view]);
154
119
  useEffect(() => {
155
120
  const box = layer.current;
@@ -164,6 +129,5 @@ function usePageLayout({
164
129
  return enabled ? overlay : null;
165
130
  }
166
131
  export {
167
- cutsToLegacyMarks,
168
132
  usePageLayout
169
133
  };
@@ -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 {};