@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,9 +1,10 @@
1
1
  // src/schema/guards.ts
2
+ import { changesOnlyDisplayAttrs, displayOnly } from "./displayDerivation.js";
2
3
  import {
3
4
  transactionReaches
4
5
  } from "./editGuard.js";
5
6
  import { lockGuard } from "./locks.js";
6
- import { bookmarkGuard, noteGuard } from "./preservedGuards.js";
7
+ import { bookmarkGuard, noteGuard, sectionGuard } from "./preservedGuards.js";
7
8
  import {
8
9
  isCommentNode,
9
10
  protectionAllows
@@ -36,7 +37,8 @@ var EDIT_GUARDS = [
36
37
  protectionGuard,
37
38
  lockGuard,
38
39
  bookmarkGuard,
39
- noteGuard
40
+ noteGuard,
41
+ sectionGuard
40
42
  ];
41
43
  function judgesSteps(guard) {
42
44
  return guard.step !== void 0;
@@ -44,8 +46,14 @@ function judgesSteps(guard) {
44
46
  function lifted(guard, tr) {
45
47
  return guard.liftedBy?.some((pass) => tr.getMeta(pass) === true) === true;
46
48
  }
49
+ function displayOnlyTransaction(tr, state) {
50
+ return tr.getMeta(displayOnly) === true && tr.steps.every(
51
+ (step, index) => changesOnlyDisplayAttrs(step, tr.docs[index] ?? state.doc)
52
+ );
53
+ }
47
54
  function transactionAllowed(tr, state) {
48
55
  if (!tr.docChanged) return true;
56
+ if (displayOnlyTransaction(tr, state)) return true;
49
57
  if (EDIT_GUARDS.some((guard) => guard.change?.(tr, state) === false)) {
50
58
  return false;
51
59
  }
@@ -66,14 +74,32 @@ function transactionAllowed(tr, state) {
66
74
  function editShut(state, intent) {
67
75
  return EDIT_GUARDS.some((guard) => guard.shuts(intent, state));
68
76
  }
77
+ function intentOver(from, to, kind) {
78
+ return from === to ? { kind: "insert", at: from } : { kind, from, to };
79
+ }
69
80
  function selectionIntents(selection, kind) {
70
81
  return selection.ranges.map(
71
- (range) => range.$from.pos === range.$to.pos ? { kind: "insert", at: range.$from.pos } : { kind, from: range.$from.pos, to: range.$to.pos }
82
+ (range) => intentOver(range.$from.pos, range.$to.pos, kind)
83
+ );
84
+ }
85
+ function guardedCommand(build) {
86
+ return (state, dispatch) => {
87
+ const tr = build(state);
88
+ if (tr === null || !transactionAllowed(tr, state)) return false;
89
+ dispatch?.(tr);
90
+ return true;
91
+ };
92
+ }
93
+ function openStretches(state, stretches, kind) {
94
+ return stretches.filter(
95
+ (stretch) => !editShut(state, intentOver(stretch.from, stretch.to, kind))
72
96
  );
73
97
  }
74
98
  export {
75
99
  EDIT_GUARDS,
76
100
  editShut,
101
+ guardedCommand,
102
+ openStretches,
77
103
  protectionAllowsTransaction,
78
104
  selectionIntents,
79
105
  stepReaches,
@@ -1,2 +1,4 @@
1
+ export type { AttrClass } from "./attrRoles";
2
+ export { attrsOfClass } from "./attrRoles";
1
3
  export { docxSchema, isPageBreak } from "./docxSchema";
2
4
  export { imageNodeSpec, runMarkSpec } from "./rendering";
@@ -1,7 +1,9 @@
1
1
  // src/schema/index.ts
2
+ import { attrsOfClass } from "./attrRoles.js";
2
3
  import { docxSchema, isPageBreak } from "./docxSchema.js";
3
4
  import { imageNodeSpec, runMarkSpec } from "./rendering.js";
4
5
  export {
6
+ attrsOfClass,
5
7
  docxSchema,
6
8
  imageNodeSpec,
7
9
  isPageBreak,
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Evaluates OOXML content and deletion locks for inline controls and whole table cells. Commands
3
- * query these predicates before editing; `editor/plugins/lockedContent` enforces them at runtime.
2
+ * Evaluates OOXML content and deletion locks for inline controls and whole table cells.
4
3
  *
5
- * `lockGuard` is what `./guards` registers all of this as, so that a caller asking the one guard
6
- * list asks the locks along with every other rule an edit is judged by.
4
+ * `lockGuard` is what `./guards` registers all of this as, and it is the only way in: a caller
5
+ * asking about a lock asks the one guard list, which asks the locks along with every other rule an
6
+ * edit is judged by. What a lock says about a whole document or a whole selection - a question
7
+ * about the document rather than about an edit - is exported beside it.
7
8
  */
8
9
  import type { Mark, Node as PMNode } from "prosemirror-model";
9
10
  import { PluginKey, type Selection } from "prosemirror-state";
@@ -58,51 +59,17 @@ export interface ControlSpan extends StepRange {
58
59
  */
59
60
  export declare function controlSpans(block: Textblock): ControlSpan[];
60
61
  /**
61
- * Whether what stands at this position sits inside a cell a control shuts.
62
+ * Whether a lock shuts editing where this selection stands, which is what a menu that would lift a
63
+ * lock is built on (`editor/commands/lockCommands`).
62
64
  *
63
- * This is the question a paragraph edit leaves its locked paragraphs out by
64
- * (`editor/paragraphEdits`), and it is deliberately not `rangeTouchesLocked` over the paragraph:
65
- * a paragraph merely holding a locked control keeps its own alignment and indent.
66
- */
67
- export declare function insideLockedCell(doc: PMNode, pos: number): boolean;
68
- /**
69
- * Whether this stretch reaches contents a lock shuts: text inside a control that shuts its
70
- * contents, or what a locked cell holds, the cell itself included.
71
- *
72
- * This is the question about editing what stands there rather than about taking it away, so every
73
- * control the stretch meets answers with its contents clause. The lock commands and the formatting
74
- * commands ask it of the stretch they are about to mark, so it is exported.
75
- */
76
- export declare function rangeTouchesLocked(doc: PMNode, from: number, to: number): boolean;
77
- /**
78
- * Whether something inserted at this spot would land inside a locked control or a locked cell.
79
- *
80
- * Only a spot with the very same control on both sides is inside one. At either edge the other
81
- * side belongs to a different control or to none, and since the mark is not inclusive what goes
82
- * in there falls outside the control.
83
- */
84
- export declare function insertionInsideLocked(doc: PMNode, pos: number): boolean;
85
- /**
86
- * Whether the guard shuts editing where this selection stands.
87
- *
88
- * This is the question a command that rewrites whatever is selected has to ask before it reports
89
- * that it applies (`editor/insertImage`), and the one a menu offering to lift a lock is built on
90
- * (`editor/commands/lockCommands`). A caret is shut by standing inside a control rather than
91
- * against its edge, which is the same rule an insertion follows.
65
+ * This is a reading of the selection rather than of an edit - the lock guard answers an edit
66
+ * (`./guards`) - and it names the locks alone, which is what the menu says. A caret is shut by
67
+ * standing inside a control rather than against its edge, which is the same rule an insertion
68
+ * follows.
92
69
  * A control locked against deletion alone shuts nothing here: its contents stand open, so editing
93
70
  * where it stands goes through, and only taking the control away is refused.
94
71
  */
95
72
  export declare function selectionShut(selection: Selection, doc: PMNode): boolean;
96
- /**
97
- * Whether the guard would refuse putting something in place of what this selection covers, which
98
- * is what an image insertion does (`editor/insertImage`).
99
- *
100
- * A different question from `selectionShut`: what the selection covers whole goes away rather than
101
- * being edited, so the deletion clause answers for it. A control that may be taken away whole may
102
- * therefore be replaced, and one locked against deletion alone may not, even though editing inside
103
- * it would have gone through.
104
- */
105
- export declare function replacementShut(selection: Selection, doc: PMNode): boolean;
106
73
  /**
107
74
  * The locks the document carries, as `./guards` registers them.
108
75
  *
@@ -127,11 +127,6 @@ function selectionShut(selection, doc) {
127
127
  (range) => markShut(doc, range.$from.pos, range.$to.pos)
128
128
  );
129
129
  }
130
- function replacementShut(selection, doc) {
131
- return selection.ranges.some(
132
- (range) => replaceShut(doc, range.$from.pos, range.$to.pos)
133
- );
134
- }
135
130
  function editedRanges(step) {
136
131
  if (step instanceof ReplaceAroundStep) {
137
132
  return [
@@ -215,13 +210,9 @@ export {
215
210
  carriesLock,
216
211
  controlSpans,
217
212
  historyReplay,
218
- insertionInsideLocked,
219
- insideLockedCell,
220
213
  isLockedCell,
221
214
  lockGuard,
222
215
  lockedMarkOf,
223
- rangeTouchesLocked,
224
- replacementShut,
225
216
  selectionShut,
226
217
  unlockAllowed
227
218
  };
@@ -1,11 +1,13 @@
1
1
  /**
2
- * The guards over the nodes a document is opened with and the editor only preserves: the two ends
3
- * of a bookmark range, and the reference standing where a footnote or an endnote is called.
2
+ * The guards over what a document is opened with and the editor only preserves: the two ends of a
3
+ * bookmark range, the reference standing where a footnote or an endnote is called, and the
4
+ * paragraph that ends a section.
4
5
  *
5
- * Neither is content the editor writes, so nothing in it can put such a node back once it is gone,
6
- * and a document that lost one of a bookmark's two ends cannot be written back as a file at all
7
- * (`docx/exportDocx` refuses it). What each guard holds is therefore the whole list of the nodes it
8
- * answers for, in the order the document carries them.
6
+ * None of it is content the editor writes, so nothing in it can put such a thing back once it is
7
+ * gone, and a document that lost one of a bookmark's two ends cannot be written back as a file at
8
+ * all (`docx/exportDocx` refuses it). What the first two guards hold is therefore the whole list of
9
+ * the nodes they answer for, in the order the document carries them; a section break, which an
10
+ * edit may legitimately move from one paragraph to another, is held by its number instead.
9
11
  */
10
12
  import type { Node as PMNode } from "prosemirror-model";
11
13
  import { type ChangeGuard, type EditGuardName } from "./editGuard";
@@ -27,4 +29,22 @@ type Signature = (node: PMNode) => string;
27
29
  export declare function preservedNodeGuard(name: EditGuardName, holds: (node: PMNode) => boolean, signature: Signature): ChangeGuard;
28
30
  export declare const bookmarkGuard: ChangeGuard;
29
31
  export declare const noteGuard: ChangeGuard;
32
+ /**
33
+ * A guard that lets a change through only while it leaves at least as many section breaks standing
34
+ * as it found.
35
+ *
36
+ * A paragraph-level `w:sectPr` is not content: it is where one section of the document ends, and
37
+ * the section carries the page size, the margins and the headers everything up to it is laid out
38
+ * under. Joining that paragraph into the one above it - a Backspace at its start, a selection run
39
+ * across its boundary - would take the whole section away with it, and nothing the editor writes
40
+ * puts a section back.
41
+ *
42
+ * It counts rather than compares, so splitting such a paragraph is still allowed: Enter leaves the
43
+ * break on the half that ends up last (`docx/cloning`), and one break stands where one stood. An
44
+ * edit that adds a break is no business of this guard's either.
45
+ *
46
+ * The count walks the whole document, so it is reached for only once a step has touched a
47
+ * paragraph that lays a break down.
48
+ */
49
+ export declare const sectionGuard: ChangeGuard;
30
50
  export {};
@@ -1,4 +1,5 @@
1
1
  // src/schema/preservedGuards.ts
2
+ import { parseProps, propsChild } from "../ooxml/props.js";
2
3
  import {
3
4
  rangeHolds,
4
5
  transactionReaches
@@ -43,8 +44,37 @@ var noteGuard = preservedNodeGuard(
43
44
  isNoteReference,
44
45
  (node) => JSON.stringify(node.attrs)
45
46
  );
47
+ function endsASection(node) {
48
+ if (node.type.name !== "paragraph") return false;
49
+ const pPr = node.attrs.pPr;
50
+ if (typeof pPr !== "string") return false;
51
+ const props = parseProps(pPr);
52
+ return props !== null && propsChild(props.children, "sectPr") !== void 0;
53
+ }
54
+ function sectionBreaks(doc) {
55
+ let seen = 0;
56
+ doc.descendants((node) => {
57
+ if (endsASection(node)) seen += 1;
58
+ return true;
59
+ });
60
+ return seen;
61
+ }
62
+ var sectionGuard = {
63
+ name: "section",
64
+ change: (tr) => !transactionReaches(tr, endsASection) || sectionBreaks(tr.doc) >= sectionBreaks(tr.before),
65
+ shuts: (intent, state) => {
66
+ if (intent.kind !== "replace") return false;
67
+ const $from = state.doc.resolve(intent.from);
68
+ const $to = state.doc.resolve(intent.to);
69
+ if ($from.parent.type.name === "paragraph" && $from.sameParent($to)) {
70
+ return false;
71
+ }
72
+ return rangeHolds(state.doc, intent.from, intent.to, endsASection);
73
+ }
74
+ };
46
75
  export {
47
76
  bookmarkGuard,
48
77
  noteGuard,
49
- preservedNodeGuard
78
+ preservedNodeGuard,
79
+ sectionGuard
50
80
  };
@@ -38,7 +38,13 @@ export declare function isCommentNode(node: PMNode): boolean;
38
38
  * reads the same as it did before the comment, and the two compare equal.
39
39
  */
40
40
  export declare function withoutComments(node: PMNode): PMNode;
41
- /** Whether the two documents differ in nothing but their comments */
41
+ /**
42
+ * Whether the two documents differ in nothing but their comments.
43
+ *
44
+ * The comparison is by source (`./sourceEquality`): a display value worked out again beside a
45
+ * comment is no content change, so it neither turns a comment edit into a body edit nor takes the
46
+ * ownership question off it.
47
+ */
42
48
  export declare function changesOnlyComments(before: PMNode, after: PMNode): boolean;
43
49
  /**
44
50
  * Whether a comment written under this identity may be edited or deleted.
@@ -1,5 +1,6 @@
1
1
  // src/schema/protection.ts
2
2
  import { Fragment } from "prosemirror-model";
3
+ import { sameSource } from "./sourceEquality.js";
3
4
  var COMMENT_NODES = /* @__PURE__ */ new Set([
4
5
  "commentStart",
5
6
  "commentEnd",
@@ -17,7 +18,7 @@ function withoutComments(node) {
17
18
  return node.copy(Fragment.fromArray(kept));
18
19
  }
19
20
  function changesOnlyComments(before, after) {
20
- return withoutComments(before).eq(withoutComments(after));
21
+ return sameSource(withoutComments(before), withoutComments(after));
21
22
  }
22
23
  function stringOrNull(value) {
23
24
  return typeof value === "string" ? value : null;
@@ -11,17 +11,9 @@
11
11
  * type, the text, the marks and the source and session attrs all have to agree, and the recursion
12
12
  * runs the same way, so a node that says the same thing in a different shape is still a change.
13
13
  */
14
- import { type Node as PMNode } from "prosemirror-model";
14
+ import type { Node as PMNode } from "prosemirror-model";
15
15
  /**
16
16
  * Whether the two would write the same XML: the same type, text, marks and source attrs, all the
17
17
  * way down. What the editor works out for the screen is not compared.
18
18
  */
19
19
  export declare function sameSource(a: PMNode, b: PMNode): boolean;
20
- /**
21
- * The node with every worked-out attr back at the value the schema starts it on, on itself and on
22
- * everything inside it.
23
- *
24
- * `sameSource` needs no such copy, and takes none. This is for a caller that has to hand a whole
25
- * document to something comparing the ordinary way.
26
- */
27
- export declare function withoutDisplayAttrs(node: PMNode): PMNode;
@@ -1,7 +1,4 @@
1
1
  // src/schema/sourceEquality.ts
2
- import {
3
- Fragment
4
- } from "prosemirror-model";
5
2
  import { displayAttrsOf } from "./attrRoles.js";
6
3
  function sameValue(a, b) {
7
4
  if (a === b) return true;
@@ -37,29 +34,6 @@ function sameSource(a, b) {
37
34
  }
38
35
  return true;
39
36
  }
40
- function withoutDisplay(type, attrs) {
41
- return Object.fromEntries(
42
- Object.entries(attrs).map(
43
- ([name, value]) => displayAttrsOf(type).includes(name) ? [name, type.spec.attrs?.[name]?.default ?? null] : [name, value]
44
- )
45
- );
46
- }
47
- function withoutDisplayMarkAttrs(mark) {
48
- return displayAttrsOf(mark.type).length === 0 ? mark : mark.type.create(withoutDisplay(mark.type, mark.attrs));
49
- }
50
- var childrenOf = (node) => Array.from({ length: node.childCount }, (_, at) => node.child(at));
51
- function withoutDisplayAttrs(node) {
52
- const marks = node.marks.map(withoutDisplayMarkAttrs);
53
- if (node.isText) return node.mark(marks);
54
- return node.type.create(
55
- withoutDisplay(node.type, node.attrs),
56
- Fragment.fromArray(
57
- childrenOf(node).map((child) => withoutDisplayAttrs(child))
58
- ),
59
- marks
60
- );
61
- }
62
37
  export {
63
- sameSource,
64
- withoutDisplayAttrs
38
+ sameSource
65
39
  };
@@ -59,20 +59,29 @@ function lineHeightValue(spacing) {
59
59
  return `max(${pt(spacing.pt)},${SINGLE_LINE_RATIO}em)`;
60
60
  }
61
61
  function textDecoration(format) {
62
+ const underline = format.underline !== void 0 && format.underline !== "none" ? format.underline : void 0;
62
63
  const lines = [];
63
- if (format.underline) lines.push("underline");
64
+ if (underline) lines.push("underline");
64
65
  if (format.strike) lines.push("line-through");
65
- if (lines.length === 0) return [];
66
+ if (lines.length === 0) {
67
+ return format.underline === "none" || format.strike === false ? ["text-decoration-line:none"] : [];
68
+ }
66
69
  const css = [`text-decoration-line:${lines.join(" ")}`];
67
- const style = format.underline ? UNDERLINE_STYLES[format.underline] : void 0;
70
+ const style = underline ? UNDERLINE_STYLES[underline] : void 0;
68
71
  if (style) css.push(`text-decoration-style:${style}`);
69
72
  return css;
70
73
  }
71
74
  function inheritedRunCss(format, fontFallbacks) {
72
75
  const css = [];
73
- if (format.bold) css.push("font-weight:bold");
74
- if (format.italic) css.push("font-style:italic");
75
- if (format.smallCaps) css.push("font-variant:small-caps");
76
+ if (format.bold !== void 0) {
77
+ css.push(`font-weight:${format.bold ? "bold" : "normal"}`);
78
+ }
79
+ if (format.italic !== void 0) {
80
+ css.push(`font-style:${format.italic ? "italic" : "normal"}`);
81
+ }
82
+ if (format.smallCaps !== void 0) {
83
+ css.push(`font-variant:${format.smallCaps ? "small-caps" : "normal"}`);
84
+ }
76
85
  css.push(...textDecoration(format));
77
86
  if (format.fontSizePt !== void 0)
78
87
  css.push(`font-size:${pt(format.fontSizePt)}`);
@@ -41,7 +41,14 @@ export declare function setCellBorders(preset: CellBorderPreset): TableCommand;
41
41
  export declare function setCellBorderColor(hex: string | null): TableCommand;
42
42
  /** Fills the selected cells with `#RRGGBB`. Null takes the fill away */
43
43
  export declare function setCellBackground(hex: string | null): TableCommand;
44
- /** Whether direct cell formatting can act on the whole current cell selection. */
44
+ /**
45
+ * Whether direct cell formatting can act on the whole current cell selection.
46
+ *
47
+ * What a cell records about itself is rewritten around its content, which is the block intent the
48
+ * guards answer (`schema/guards`): a cell a control shuts is refused, and so is every cell while a
49
+ * protection shuts the body. The spot asked about stands inside the cell, since that is where the
50
+ * control wrapping it is an ancestor.
51
+ */
45
52
  export declare function canSetCellFormatting(state: EditorState): boolean;
46
53
  /** The fill shared by the selected cells. Null when they differ or none is filled */
47
54
  export declare function activeCellBackground(state: EditorState): string | null;
@@ -8,9 +8,7 @@ import {
8
8
  NO_FILL,
9
9
  toCellFormat
10
10
  } from "../model/format.js";
11
- import { transactionAllowed } from "../schema/guards.js";
12
- import { isLockedCell } from "../schema/locks.js";
13
- import { editsShut } from "../schema/protectionState.js";
11
+ import { editShut, guardedCommand } from "../schema/guards.js";
14
12
  import { cellDefaultsAt, tableCellSources } from "./gridBorders.js";
15
13
  function text(value) {
16
14
  return typeof value === "string" ? value : null;
@@ -52,19 +50,12 @@ function planChanges(rect, edit) {
52
50
  return changes;
53
51
  }
54
52
  function cellFormatCommand(edit) {
55
- return (state, dispatch) => {
56
- if (!pmIsInTable(state)) return false;
53
+ return guardedCommand((state) => {
54
+ if (!canSetCellFormatting(state)) return null;
57
55
  const rect = selectedRect(state);
58
- if (rect.map.cellsInRect(rect).some((pos) => isLockedCell(rect.table.nodeAt(pos)))) {
59
- return false;
60
- }
61
56
  const changes = planChanges(rect, edit);
62
- if (changes.length === 0) return false;
63
- const transaction = applyChanges(state.tr, rect.tableStart, changes);
64
- if (!transactionAllowed(transaction, state)) return false;
65
- dispatch?.(transaction);
66
- return true;
67
- };
57
+ return changes.length === 0 ? null : applyChanges(state.tr, rect.tableStart, changes);
58
+ });
68
59
  }
69
60
  function shared(values) {
70
61
  const [first, ...rest] = values;
@@ -112,17 +103,18 @@ function selectedCells(state) {
112
103
  const cells = [];
113
104
  for (const pos of rect.map.cellsInRect(rect)) {
114
105
  const cell = rect.table.nodeAt(pos);
115
- if (cell) cells.push(cell);
106
+ if (cell) cells.push({ node: cell, pos: rect.tableStart + pos });
116
107
  }
117
108
  return cells;
118
109
  }
119
110
  function cellFormats(state) {
120
- return selectedCells(state).map((cell) => toCellFormat(cell.attrs.format));
111
+ return selectedCells(state).map(
112
+ ({ node }) => toCellFormat(node.attrs.format)
113
+ );
121
114
  }
122
115
  function canSetCellFormatting(state) {
123
- if (editsShut(state)) return false;
124
116
  const cells = selectedCells(state);
125
- return cells.length > 0 && cells.every((cell) => !isLockedCell(cell));
117
+ return cells.length > 0 && cells.every(({ pos }) => !editShut(state, { kind: "block", at: pos + 1 }));
126
118
  }
127
119
  function sharedValue(values) {
128
120
  const [first, ...rest] = values;
@@ -3,7 +3,7 @@ import {
3
3
  deleteTable as pmDeleteTable,
4
4
  isInTable as pmIsInTable
5
5
  } from "prosemirror-tables";
6
- import { transactionAllowed } from "../schema/guards.js";
6
+ import { guardedCommand } from "../schema/guards.js";
7
7
  import {
8
8
  buildAddColumnAfterTransaction,
9
9
  buildAddColumnBeforeTransaction,
@@ -18,26 +18,22 @@ import {
18
18
  function isInTable(state) {
19
19
  return pmIsInTable(state);
20
20
  }
21
- function toCommand(build) {
22
- return (state, dispatch) => {
23
- const tr = build(state);
24
- if (!tr || !transactionAllowed(tr, state)) return false;
25
- dispatch?.(tr);
26
- return true;
27
- };
28
- }
29
- var addRowBefore = toCommand(
21
+ var addRowBefore = guardedCommand(
30
22
  buildAddRowBeforeTransaction
31
23
  );
32
- var addRowAfter = toCommand(buildAddRowAfterTransaction);
33
- var deleteRow = toCommand(buildDeleteRowTransaction);
34
- var addColumnBefore = toCommand(
24
+ var addRowAfter = guardedCommand(
25
+ buildAddRowAfterTransaction
26
+ );
27
+ var deleteRow = guardedCommand(
28
+ buildDeleteRowTransaction
29
+ );
30
+ var addColumnBefore = guardedCommand(
35
31
  buildAddColumnBeforeTransaction
36
32
  );
37
- var addColumnAfter = toCommand(
33
+ var addColumnAfter = guardedCommand(
38
34
  buildAddColumnAfterTransaction
39
35
  );
40
- var deleteColumn = toCommand(
36
+ var deleteColumn = guardedCommand(
41
37
  buildDeleteColumnTransaction
42
38
  );
43
39
  function buildDeleteTableTransaction(state) {
@@ -46,9 +42,15 @@ function buildDeleteTableTransaction(state) {
46
42
  pmDeleteTable(state, (tr) => captured.push(tr));
47
43
  return captured[0] ?? null;
48
44
  }
49
- var deleteTable = toCommand(buildDeleteTableTransaction);
50
- var mergeCells = toCommand(buildMergeCellsTransaction);
51
- var splitCell = toCommand(buildSplitCellTransaction);
45
+ var deleteTable = guardedCommand(
46
+ buildDeleteTableTransaction
47
+ );
48
+ var mergeCells = guardedCommand(
49
+ buildMergeCellsTransaction
50
+ );
51
+ var splitCell = guardedCommand(
52
+ buildSplitCellTransaction
53
+ );
52
54
  export {
53
55
  addColumnAfter,
54
56
  addColumnBefore,
@@ -2,6 +2,9 @@
2
2
  * Newly created cells and rows inherit only the formatting of the cell or row they
3
3
  * are based on. Values that have to be recomputed from the grid (colspan, rowspan,
4
4
  * colwidth) are not inherited.
5
+ *
6
+ * What that comes to per attr is `docx/cloning`, which answers the same question for a
7
+ * paragraph an edit makes out of another paragraph.
5
8
  */
6
9
  import type { Node as PMNode } from "prosemirror-model";
7
10
  import type { GridRect } from "../docx/tableFormatting";
@@ -1,4 +1,5 @@
1
1
  // src/table/format.ts
2
+ import { CLONE_POLICIES } from "../docx/cloning.js";
2
3
  function isNumberArray(value) {
3
4
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
4
5
  }
@@ -6,18 +7,11 @@ function colwidthOf(cell) {
6
7
  const value = cell.attrs.colwidth;
7
8
  return isNumberArray(value) ? value : null;
8
9
  }
9
- var INHERITED_CELL_ATTRS = ["tcAttrs", "tcPr", "tcW", "format"];
10
10
  function inheritCellAttrs(cell, overrides = {}) {
11
- const attrs = {};
12
- for (const key of INHERITED_CELL_ATTRS) attrs[key] = cell.attrs[key];
13
- return { ...attrs, ...overrides };
11
+ return { ...CLONE_POLICIES.tableCell.attrs(cell, "copy"), ...overrides };
14
12
  }
15
- var INHERITED_ROW_ATTRS = ["trAttrs", "trPr", "format"];
16
13
  function inheritRowAttrs(row) {
17
- if (!row) return null;
18
- const attrs = {};
19
- for (const key of INHERITED_ROW_ATTRS) attrs[key] = row.attrs[key];
20
- return attrs;
14
+ return row ? CLONE_POLICIES.tableRow.attrs(row, "copy") : null;
21
15
  }
22
16
  export {
23
17
  colwidthOf,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The lines a cell draws because of where it sits in the grid, kept true after a structure edit.
2
+ * The lines a cell draws because of where it sits in the grid.
3
3
  *
4
4
  * Every line of a table is drawn by its cells (see `docx/tableFormatting`), so which line a side
5
5
  * falls back on depends on where in the grid the cell sits. A new cell inherits its neighbour's
@@ -9,13 +9,14 @@
9
9
  *
10
10
  * So the cells of a table whose grid moved derive their display values again, along the same path
11
11
  * the import takes. What a cell wrote down itself lives in its `w:tcPr` and is read straight back
12
- * out of it, so only the share that came from the table can change.
12
+ * out of it, so only the share that came from the table can change. `cellFixes` says which cells
13
+ * carry the wrong lines and `sameFormattingInputs` whether anything they depend on moved; the
14
+ * deriver that runs them after an edit is `editor/plugins/tableDisplay`.
13
15
  */
14
16
  import type { Node as PMNode } from "prosemirror-model";
15
- import { Plugin } from "prosemirror-state";
16
17
  import { type CellBorderDefaults } from "../docx/tableFormatting";
17
18
  import { type CellMargins, type InsideBorders, type TableFormat } from "../model/format";
18
- import type { TableGridMap } from "./format";
19
+ import type { NodeAttrs, TableGridMap } from "./format";
19
20
  /**
20
21
  * What a table lays down for its cells to draw.
21
22
  * The values a table style laid down are not in the `tblPr`, so the table carries them separately.
@@ -28,15 +29,12 @@ export interface TableCellSources {
28
29
  export declare function tableCellSources(table: PMNode): TableCellSources;
29
30
  /** The lines the cell at this position falls back on for the sides it draws no border of its own on */
30
31
  export declare function cellDefaultsAt(map: TableGridMap, pos: number, sources: TableCellSources): CellBorderDefaults;
31
- /** Derives shared-border display values before the first editor view is drawn. */
32
- export declare function withDerivedGridBorders(doc: PMNode): PMNode;
33
- /**
34
- * Keeps the derived lines current after a table's grid or border inputs change.
35
- *
36
- * The tables are paired up by the order they stand in. A document that gained or lost a whole table
37
- * pairs the ones after it with the wrong partner, which costs a derivation that changes no cell.
38
- *
39
- * No transaction of its own goes to the history: ProseMirror hands an appended transaction to the
40
- * history as part of the same event, so a single undo takes the edit and this correction back together.
41
- */
42
- export declare function gridBorders(): Plugin;
32
+ export interface CellFix {
33
+ /** The position of the cell within the table's content */
34
+ pos: number;
35
+ attrs: NodeAttrs;
36
+ }
37
+ /** The cells of this table whose display values do not match the spot they now sit in */
38
+ export declare function cellFixes(table: PMNode): CellFix[];
39
+ /** Whether everything the cells' lines are derived from reads the same in both tables */
40
+ export declare function sameFormattingInputs(a: PMNode, b: PMNode): boolean;