@stll/folio-core 0.11.0 → 0.12.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 (194) hide show
  1. package/dist/ai-edits/apply.js +602 -50
  2. package/dist/ai-edits/block-identity.d.ts +4 -0
  3. package/dist/ai-edits/block-identity.js +9 -0
  4. package/dist/ai-edits/blockRange.d.ts +28 -1
  5. package/dist/ai-edits/blockRange.js +67 -1
  6. package/dist/ai-edits/headless.d.ts +4 -4
  7. package/dist/ai-edits/headless.js +9 -9
  8. package/dist/ai-edits/index.d.ts +2 -2
  9. package/dist/ai-edits/index.js +2 -2
  10. package/dist/ai-edits/read.d.ts +7 -7
  11. package/dist/ai-edits/read.js +110 -3
  12. package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
  13. package/dist/ai-edits/table-cell-mutations.js +237 -0
  14. package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
  15. package/dist/ai-edits/table-mutation-plan.js +90 -0
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +100 -0
  17. package/dist/ai-edits/table-row-column-mutations.js +300 -0
  18. package/dist/ai-edits/table-targets.d.ts +32 -0
  19. package/dist/ai-edits/table-targets.js +70 -0
  20. package/dist/ai-edits/types.d.ts +34 -0
  21. package/dist/compat/eigenpal.js +1 -1
  22. package/dist/content-controls/mutateContentControls.js +1 -1
  23. package/dist/controller/activeEditorStory.d.ts +36 -0
  24. package/dist/controller/activeEditorStory.js +30 -0
  25. package/dist/controller/contentControlWidgetController.d.ts +55 -0
  26. package/dist/controller/contentControlWidgetController.js +141 -0
  27. package/dist/controller/fontReadiness.js +2 -0
  28. package/dist/controller/headerFooterEditorManager.js +1 -1
  29. package/dist/controller/hiddenEditorApi.js +2 -2
  30. package/dist/controller/hiddenEditorManager.d.ts +12 -1
  31. package/dist/controller/hiddenEditorManager.js +32 -11
  32. package/dist/controller/layoutPipeline.d.ts +2 -0
  33. package/dist/controller/layoutPipeline.js +16 -1
  34. package/dist/controller/noteEditorManager.d.ts +36 -0
  35. package/dist/controller/noteEditorManager.js +210 -0
  36. package/dist/document-operations.d.ts +35 -3
  37. package/dist/document-operations.js +161 -3
  38. package/dist/docx/blockContentParser.js +2 -2
  39. package/dist/docx/commentParser.js +6 -1
  40. package/dist/docx/conformance.js +8 -1
  41. package/dist/docx/documentGrid.d.ts +8 -0
  42. package/dist/docx/documentGrid.js +8 -0
  43. package/dist/docx/drawingUtils.js +1 -1
  44. package/dist/docx/ensureParaIds.js +1 -1
  45. package/dist/docx/fieldParser.js +1 -1
  46. package/dist/docx/notePropertiesParser.js +1 -1
  47. package/dist/docx/numberingParser.js +1 -1
  48. package/dist/docx/paragraphParser.js +63 -58
  49. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  50. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  51. package/dist/docx/paragraphTraversal.js +1 -1
  52. package/dist/docx/parser.js +3 -3
  53. package/dist/docx/rezip.js +1 -1
  54. package/dist/docx/runParser.js +4 -2
  55. package/dist/docx/sdtProperties.js +1 -1
  56. package/dist/docx/sectionParser.js +1 -1
  57. package/dist/docx/selectiveSave.js +1 -1
  58. package/dist/docx/serializer/commentSerializer.js +1 -2
  59. package/dist/docx/serializer/documentSerializer.js +2 -2
  60. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  61. package/dist/docx/serializer/noteSerializer.js +2 -2
  62. package/dist/docx/serializer/paragraphSerializer.js +7 -1
  63. package/dist/docx/serializer/runSerializer.js +4 -2
  64. package/dist/docx/serializer/stylesSerializer.js +1 -1
  65. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  66. package/dist/docx/serializer/tableSerializer.js +17 -13
  67. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  68. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  69. package/dist/docx/server/boundedArchive.d.ts +14 -3
  70. package/dist/docx/server/boundedArchive.js +56 -11
  71. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  72. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  73. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  74. package/dist/docx/server/inspectDocxPackage.js +217 -0
  75. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  76. package/dist/docx/server/validateDocxConformance.js +336 -0
  77. package/dist/docx/settingsParser.js +11 -1
  78. package/dist/docx/shapeParser.js +1 -1
  79. package/dist/docx/styleParser.js +4 -2
  80. package/dist/docx/tableParser.js +18 -7
  81. package/dist/docx/textBoxParser.d.ts +3 -3
  82. package/dist/docx/textBoxParser.js +16 -12
  83. package/dist/docx/xmlSafety.d.ts +5 -0
  84. package/dist/docx/xmlSafety.js +9 -0
  85. package/dist/fields/resolveFieldValues.js +1 -3
  86. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  87. package/dist/i18n/messages/catalogs.gen.js +17 -0
  88. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  89. package/dist/index.js +1 -1
  90. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  91. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  92. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  93. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  94. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  95. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  96. package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
  97. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  98. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  99. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  100. package/dist/layout-engine/index.d.ts +2 -2
  101. package/dist/layout-engine/index.js +43 -28
  102. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  103. package/dist/layout-engine/justificationCompatibility.js +8 -0
  104. package/dist/layout-engine/keep-together.js +11 -1
  105. package/dist/layout-engine/measure/cache.js +21 -5
  106. package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
  107. package/dist/layout-engine/measure/measureBlocks.js +6 -3
  108. package/dist/layout-engine/measure/measureContainer.js +1 -1
  109. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  110. package/dist/layout-engine/measure/measureHelpers.js +3 -3
  111. package/dist/layout-engine/measure/measureParagraph.js +69 -25
  112. package/dist/layout-engine/measure/measureProvider.js +4 -3
  113. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  114. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  115. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  116. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  117. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  118. package/dist/layout-engine/paginator.js +5 -1
  119. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  120. package/dist/layout-engine/paragraphSpacing.js +107 -13
  121. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  122. package/dist/layout-engine/types.d.ts +26 -13
  123. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  124. package/dist/layout-painter/renderPage.d.ts +5 -2
  125. package/dist/layout-painter/renderPage.js +56 -12
  126. package/dist/layout-painter/renderParagraph.js +22 -6
  127. package/dist/layout-painter/renderTable.d.ts +5 -1
  128. package/dist/layout-painter/renderTable.js +5 -2
  129. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  130. package/dist/layout-painter/renderTextBox.js +25 -14
  131. package/dist/managers/DocumentLoaderManager.js +1 -1
  132. package/dist/model.js +1 -1
  133. package/dist/prosemirror/attrs/index.d.ts +4 -2
  134. package/dist/prosemirror/attrs/index.js +154 -18
  135. package/dist/prosemirror/commands/comments.d.ts +3 -3
  136. package/dist/prosemirror/commands/comments.js +248 -12
  137. package/dist/prosemirror/commands/contentControls.js +1 -1
  138. package/dist/prosemirror/commands/index.js +1 -1
  139. package/dist/prosemirror/commands/pageBreak.js +1 -1
  140. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  141. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  142. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  143. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  144. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  145. package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
  146. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  147. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  148. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  149. package/dist/prosemirror/conversion/toProseDoc.js +347 -98
  150. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  151. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
  153. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  154. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  156. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  157. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  158. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  159. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  160. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  161. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
  162. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  163. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  164. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
  166. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  167. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  168. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  169. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  170. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
  171. package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
  172. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  173. package/dist/prosemirror/plugins/suggestionMode.js +1 -1
  174. package/dist/prosemirror/schema/index.d.ts +2 -2
  175. package/dist/prosemirror/schema/marks.d.ts +5 -1
  176. package/dist/prosemirror/schema/nodes.d.ts +64 -7
  177. package/dist/prosemirror/selectionState.js +1 -0
  178. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  179. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  180. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  181. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  182. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  183. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  184. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  185. package/dist/prosemirror/validation.js +9 -1
  186. package/dist/server.d.ts +6 -2
  187. package/dist/server.js +5 -1
  188. package/dist/utils/fontFamilyMerge.js +9 -11
  189. package/dist/utils/fontLoader.js +3 -2
  190. package/dist/utils/fontResolver.js +22 -4
  191. package/dist/utils/fontWeights.d.ts +1 -1
  192. package/dist/utils/fontWeights.js +1 -1
  193. package/dist/utils/paragraphFormattingMerge.js +1 -0
  194. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { panic } from "better-result";
2
- import { keymap } from "prosemirror-keymap";
3
2
  import { Schema } from "prosemirror-model";
3
+ import { keymap } from "prosemirror-keymap";
4
4
  //#region src/prosemirror/extensions/ExtensionManager.ts
5
5
  /**
6
6
  * Extension Manager
@@ -41,7 +41,7 @@ import { SuperscriptExtension } from "./marks/SuperscriptExtension.js";
41
41
  import { TextColorExtension } from "./marks/TextColorExtension.js";
42
42
  import { TextEffectExtension } from "./marks/TextEffectExtension.js";
43
43
  import { EmbossExtension, EmphasisMarkExtension, ImprintExtension, TextOutlineExtension, TextShadowExtension } from "./marks/TextEffectsExtensions.js";
44
- import { DeletionExtension, InsertionExtension } from "./marks/TrackedChangeExtensions.js";
44
+ import { DeletionExtension, InsertionExtension, RunPropertyChangeExtension } from "./marks/TrackedChangeExtensions.js";
45
45
  import { UnderlineExtension } from "./marks/UnderlineExtension.js";
46
46
  import { BlockSdtExtension } from "./nodes/BlockSdtExtension.js";
47
47
  import { FieldExtension } from "./nodes/FieldExtension.js";
@@ -50,10 +50,12 @@ import { HorizontalRuleExtension } from "./nodes/HorizontalRuleExtension.js";
50
50
  import { ImageExtension } from "./nodes/ImageExtension.js";
51
51
  import { MathExtension } from "./nodes/MathExtension.js";
52
52
  import { PageBreakExtension } from "./nodes/PageBreakExtension.js";
53
+ import { RenderedPageBreakExtension } from "./nodes/RenderedPageBreakExtension.js";
53
54
  import { SdtExtension } from "./nodes/SdtExtension.js";
54
55
  import { ShapeExtension } from "./nodes/ShapeExtension.js";
55
56
  import { TabExtension } from "./nodes/TabExtension.js";
56
57
  import { TextBoxExtension } from "./nodes/TextBoxExtension.js";
58
+ import { TextBoxAnchorExtension } from "./nodes/TextBoxAnchorExtension.js";
57
59
  //#region src/prosemirror/extensions/StarterKit.ts
58
60
  /**
59
61
  * Create the full set of extensions for the DOCX editor
@@ -101,10 +103,12 @@ function createStarterKit(options = {}) {
101
103
  add("comment", CommentExtension());
102
104
  add("insertion", InsertionExtension());
103
105
  add("deletion", DeletionExtension());
106
+ add("runPropertyChange", RunPropertyChangeExtension());
104
107
  add("hardBreak", HardBreakExtension());
105
108
  add("tab", TabExtension());
106
109
  add("image", ImageExtension());
107
110
  add("textBox", TextBoxExtension());
111
+ add("textBoxAnchor", TextBoxAnchorExtension());
108
112
  add("shape", ShapeExtension());
109
113
  add("imageDrag", ImageDragExtension());
110
114
  add("imagePaste", ImagePasteExtension());
@@ -112,6 +116,7 @@ function createStarterKit(options = {}) {
112
116
  add("gapCursor", GapCursorExtension());
113
117
  add("horizontalRule", HorizontalRuleExtension());
114
118
  add("pageBreak", PageBreakExtension());
119
+ add("renderedPageBreak", RenderedPageBreakExtension());
115
120
  add("field", FieldExtension());
116
121
  add("sdt", SdtExtension());
117
122
  add("blockSdt", BlockSdtExtension());
@@ -1,6 +1,6 @@
1
- import { autospacingMatchesBase } from "../../autospacingBase.js";
2
1
  import { directionIsRtl } from "../../paragraphDirection.js";
3
2
  import { expectParagraphAttrs } from "../../attrs/index.js";
3
+ import { autospacingMatchesBase } from "../../autospacingBase.js";
4
4
  import { createNodeExtension } from "../create.js";
5
5
  import { paragraphToStyle } from "../../../utils/formatToStyle.js";
6
6
  import { collectHeadings } from "../../../utils/headingCollector.js";
@@ -187,8 +187,10 @@ const paragraphNodeSpec = {
187
187
  spaceAfter: { default: null },
188
188
  lineSpacing: { default: null },
189
189
  lineSpacingRule: { default: null },
190
+ snapToGrid: { default: null },
190
191
  spacingExplicit: { default: null },
191
192
  spacingFromDocDefaults: { default: null },
193
+ spacingFromImplicitDefaultStyle: { default: null },
192
194
  indentLeft: { default: null },
193
195
  indentRight: { default: null },
194
196
  indentFirstLine: { default: null },
@@ -219,6 +221,7 @@ const paragraphNodeSpec = {
219
221
  pageBreakBefore: { default: null },
220
222
  renderedPageBreakBefore: { default: null },
221
223
  _pageBreakCarrier: { default: null },
224
+ _trailingPageBreak: { default: null },
222
225
  keepNext: { default: null },
223
226
  keepLines: { default: null },
224
227
  widowControl: { default: null },
@@ -55,6 +55,7 @@ const INHERITED_PARA_ATTRS = [
55
55
  "styleId",
56
56
  "lineSpacing",
57
57
  "lineSpacingRule",
58
+ "snapToGrid",
58
59
  "spaceAfter",
59
60
  "spaceBefore",
60
61
  "contextualSpacing"
@@ -1,7 +1,7 @@
1
- import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../../commands/propertyChangeScope.js";
2
1
  import { Priority } from "../types.js";
3
2
  import { createExtension } from "../create.js";
4
3
  import { SUGGESTION_META, makeRevisionInfo } from "../../plugins/suggestionMode.js";
4
+ import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../../commands/propertyChangeScope.js";
5
5
  import { goToNextCell, goToPrevCell } from "../nodes/TableExtension.js";
6
6
  //#region src/prosemirror/extensions/features/ListExtension.ts
7
7
  function chainCommands(...commands) {
@@ -1,9 +1,18 @@
1
1
  import { Extension } from "../types.js";
2
2
  import { EditorState, PluginKey } from "prosemirror-state";
3
+ import { Node } from "prosemirror-model";
3
4
 
4
5
  //#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts
5
6
  declare const paraIdAllocatorKey: PluginKey<any>;
7
+ /**
8
+ * Allocate initial paragraph IDs before constructing editor state.
9
+ *
10
+ * Rebuilding only changed branches avoids applying one ProseMirror transaction
11
+ * step per missing paragraph. Live edits still use the transaction-based plugin
12
+ * below so history, mappings, and duplicate ownership retain their semantics.
13
+ */
14
+ declare const ensureParaIdsInDoc: (doc: Node) => Node;
6
15
  declare const ensureParaIdsInState: (state: EditorState) => EditorState;
7
16
  declare const ParaIdAllocatorExtension: (options?: Partial<{}> | undefined) => Extension;
8
17
  //#endregion
9
- export { ParaIdAllocatorExtension, ensureParaIdsInState, paraIdAllocatorKey };
18
+ export { ParaIdAllocatorExtension, ensureParaIdsInDoc, ensureParaIdsInState, paraIdAllocatorKey };
@@ -1,8 +1,46 @@
1
- import { generateHexId } from "../../../utils/hexId.js";
2
1
  import { createExtension } from "../create.js";
3
2
  import { ignoreTrackedChanges } from "./ParagraphChangeTrackerExtension.js";
3
+ import { generateHexId } from "../../../utils/hexId.js";
4
+ import { Fragment } from "prosemirror-model";
4
5
  import { Plugin, PluginKey } from "prosemirror-state";
5
6
  //#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.ts
7
+ /**
8
+ * ParaIdAllocator — assigns a stable `w14:paraId` to every paragraph.
9
+ *
10
+ * Lifted from
11
+ * https://github.com/eigenpal/docx-editor/blob/main/packages/core/src/prosemirror/extensions/features/ParaIdAllocatorExtension.ts
12
+ * (Apache-2.0). Adapted to folio's `createExtension` + import style;
13
+ * keep behaviour in sync upstream. folio divergences: duplicate
14
+ * resolution maps the original paragraph's position through the
15
+ * transaction (see below), and the appended allocation transaction is
16
+ * excluded from paragraph change tracking like `ensureParaIdsInState`.
17
+ *
18
+ * Why: AI tooling, chat citation chips, and the change tracker all
19
+ * anchor on `paraId`. A paragraph with `paraId: null` is invisible
20
+ * to those surfaces; a duplicated paraId (the second half of an
21
+ * Enter-split, or content pasted from another doc) silently desyncs
22
+ * their anchors. This plugin closes both gaps by allocating fresh
23
+ * ids in an `appendTransaction` hook after every doc-changed step.
24
+ *
25
+ * Id lifecycle rules (locked by the co-located tests):
26
+ * - Missing/empty/reserved-zero id: a fresh random id is minted.
27
+ * - Split: the half holding the paragraph's original start position
28
+ * keeps the id; the other half gets a fresh one. Anchors inside the
29
+ * content stay resolvable on the half that carries them.
30
+ * - Merge/join: the surviving paragraph keeps its id; the absorbed id
31
+ * dangles by design (consumers degrade to snapshot fallback).
32
+ * - Paste/duplicate carrying an id already in the doc: the paragraph
33
+ * whose mapped pre-transaction position matches keeps it — pasting a
34
+ * copy above its source no longer steals the source's id. When no
35
+ * occurrence maps back (e.g. both are new), the first in document
36
+ * order keeps it.
37
+ * - Paste of an id unknown to this doc (cross-doc paste, cut-then-
38
+ * paste move): the id is kept, so a moved paragraph stays anchored.
39
+ * - Undo/redo: allocation applies with `addToHistory: false`, and
40
+ * ProseMirror remaps it into the originating history event. Redoing a
41
+ * split therefore restores the same allocated id instead of minting
42
+ * another one.
43
+ */
6
44
  const paraIdAllocatorKey = new PluginKey("paraIdAllocator");
7
45
  const isUsableParaId = (value) => typeof value === "string" && value.length > 0 && value !== "00000000";
8
46
  /**
@@ -76,6 +114,59 @@ const collectParaIdUpdates = (doc, keeperPositions) => {
76
114
  updates.sort((a, b) => a.pos - b.pos);
77
115
  return updates;
78
116
  };
117
+ const collectInitialParaIds = (doc) => {
118
+ let needsRewrite = false;
119
+ const taken = /* @__PURE__ */ new Set();
120
+ doc.descendants((node) => {
121
+ if (node.type.name !== "paragraph") return true;
122
+ const id = node.attrs["paraId"];
123
+ if (!isUsableParaId(id) || taken.has(id)) needsRewrite = true;
124
+ else taken.add(id);
125
+ return false;
126
+ });
127
+ return {
128
+ needsRewrite,
129
+ taken
130
+ };
131
+ };
132
+ const rewriteInitialParaIds = (parent, taken, seen) => {
133
+ let changed = false;
134
+ const children = [];
135
+ parent.forEach((child) => {
136
+ let next = child;
137
+ if (child.type.name === "paragraph") {
138
+ const id = child.attrs["paraId"];
139
+ if (!isUsableParaId(id) || seen.has(id)) {
140
+ let newId = generateHexId();
141
+ while (taken.has(newId)) newId = generateHexId();
142
+ taken.add(newId);
143
+ seen.add(newId);
144
+ next = child.type.create({
145
+ ...child.attrs,
146
+ paraId: newId
147
+ }, child.content, child.marks);
148
+ } else seen.add(id);
149
+ } else if (child.childCount > 0) {
150
+ const content = rewriteInitialParaIds(child, taken, seen);
151
+ if (content !== child.content) next = child.copy(content);
152
+ }
153
+ if (next !== child) changed = true;
154
+ children.push(next);
155
+ });
156
+ return changed ? Fragment.fromArray(children) : parent.content;
157
+ };
158
+ /**
159
+ * Allocate initial paragraph IDs before constructing editor state.
160
+ *
161
+ * Rebuilding only changed branches avoids applying one ProseMirror transaction
162
+ * step per missing paragraph. Live edits still use the transaction-based plugin
163
+ * below so history, mappings, and duplicate ownership retain their semantics.
164
+ */
165
+ const ensureParaIdsInDoc = (doc) => {
166
+ const { needsRewrite, taken } = collectInitialParaIds(doc);
167
+ if (!needsRewrite) return doc;
168
+ return doc.copy(rewriteInitialParaIds(doc, taken, /* @__PURE__ */ new Set()));
169
+ };
79
170
  const ensureParaIdsInState = (state) => {
80
171
  const updates = collectParaIdUpdates(state.doc);
81
172
  if (updates.length === 0) return state;
@@ -109,4 +200,4 @@ const ParaIdAllocatorExtension = createExtension({
109
200
  }
110
201
  });
111
202
  //#endregion
112
- export { ParaIdAllocatorExtension, ensureParaIdsInState, paraIdAllocatorKey };
203
+ export { ParaIdAllocatorExtension, ensureParaIdsInDoc, ensureParaIdsInState, paraIdAllocatorKey };
@@ -30,6 +30,7 @@ declare function hasUntrackedChanges(state: EditorState): boolean;
30
30
  */
31
31
  declare function clearTrackedChanges(state: EditorState): Transaction;
32
32
  declare function ignoreTrackedChanges(tr: Transaction): Transaction;
33
+ declare function markStructuralChange(tr: Transaction): Transaction;
33
34
  declare const ParagraphChangeTrackerExtension: (options?: Partial<{}> | undefined) => Extension;
34
35
  //#endregion
35
- export { ParagraphChangeTrackerExtension, ParagraphChangeTrackerState, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, paragraphChangeTrackerKey };
36
+ export { ParagraphChangeTrackerExtension, ParagraphChangeTrackerState, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, markStructuralChange, paragraphChangeTrackerKey };
@@ -12,6 +12,7 @@ import { AddMarkStep, AddNodeMarkStep, RemoveMarkStep, RemoveNodeMarkStep } from
12
12
  const paragraphChangeTrackerKey = new PluginKey("paragraphChangeTracker");
13
13
  const CLEAR_META = "clear";
14
14
  const IGNORE_META = "ignore";
15
+ const STRUCTURAL_META = "structural";
15
16
  /**
16
17
  * Count paragraph nodes in a ProseMirror document
17
18
  */
@@ -103,7 +104,7 @@ function createParagraphChangeTrackerPlugin() {
103
104
  const newCount = countParagraphs(tr.doc);
104
105
  const newState = {
105
106
  changedParaIds: new Set(prevState.changedParaIds),
106
- structuralChange: prevState.structuralChange,
107
+ structuralChange: prevState.structuralChange || meta === STRUCTURAL_META,
107
108
  hasUntrackedChanges: prevState.hasUntrackedChanges,
108
109
  paragraphCount: newCount
109
110
  };
@@ -177,6 +178,9 @@ function clearTrackedChanges(state) {
177
178
  function ignoreTrackedChanges(tr) {
178
179
  return tr.setMeta(paragraphChangeTrackerKey, IGNORE_META);
179
180
  }
181
+ function markStructuralChange(tr) {
182
+ return tr.setMeta(paragraphChangeTrackerKey, STRUCTURAL_META);
183
+ }
180
184
  const ParagraphChangeTrackerExtension = createExtension({
181
185
  name: "paragraphChangeTracker",
182
186
  defaultOptions: {},
@@ -185,4 +189,4 @@ const ParagraphChangeTrackerExtension = createExtension({
185
189
  }
186
190
  });
187
191
  //#endregion
188
- export { ParagraphChangeTrackerExtension, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, paragraphChangeTrackerKey };
192
+ export { ParagraphChangeTrackerExtension, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, markStructuralChange, paragraphChangeTrackerKey };
@@ -1,5 +1,5 @@
1
- import { formatPx, twipsToPixels } from "../../../utils/units.js";
2
1
  import { expectCharacterSpacingMarkAttrs } from "../../attrs/index.js";
2
+ import { formatPx, twipsToPixels } from "../../../utils/units.js";
3
3
  import { createMarkExtension } from "../create.js";
4
4
  //#region src/prosemirror/extensions/marks/CharacterSpacingExtension.ts
5
5
  /**
@@ -18,5 +18,13 @@ declare const InsertionExtension: (options?: Partial<Record<string, unknown>> |
18
18
  * Renders with per-author colored strikethrough.
19
19
  */
20
20
  declare const DeletionExtension: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
21
+ /**
22
+ * Run property change — formatting changed while review tracking was active.
23
+ *
24
+ * The current formatting remains represented by the normal formatting marks;
25
+ * this mark carries the previous run properties and revision metadata needed
26
+ * to serialize, list, accept, and reject the change.
27
+ */
28
+ declare const RunPropertyChangeExtension: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
21
29
  //#endregion
22
- export { DeletionExtension, InsertionExtension };
30
+ export { DeletionExtension, InsertionExtension, RunPropertyChangeExtension };
@@ -109,5 +109,28 @@ const DeletionExtension = createMarkExtension({
109
109
  }
110
110
  }
111
111
  });
112
+ /**
113
+ * Run property change — formatting changed while review tracking was active.
114
+ *
115
+ * The current formatting remains represented by the normal formatting marks;
116
+ * this mark carries the previous run properties and revision metadata needed
117
+ * to serialize, list, accept, and reject the change.
118
+ */
119
+ const RunPropertyChangeExtension = createMarkExtension({
120
+ name: "runPropertyChange",
121
+ schemaMarkName: "runPropertyChange",
122
+ markSpec: {
123
+ attrs: { changes: { default: [] } },
124
+ inclusive: false,
125
+ parseDOM: [{ tag: "span.docx-run-property-change" }],
126
+ toDOM() {
127
+ return [
128
+ "span",
129
+ { class: "docx-run-property-change" },
130
+ 0
131
+ ];
132
+ }
133
+ }
134
+ });
112
135
  //#endregion
113
- export { DeletionExtension, InsertionExtension };
136
+ export { DeletionExtension, InsertionExtension, RunPropertyChangeExtension };
@@ -1,6 +1,6 @@
1
- import { parseFieldInstruction } from "../../../docx/fieldParser.js";
2
1
  import { expectFieldAttrs } from "../../attrs/index.js";
3
2
  import { createNodeExtension } from "../create.js";
3
+ import { parseFieldInstruction } from "../../../docx/fieldParser.js";
4
4
  //#region src/prosemirror/extensions/nodes/FieldExtension.ts
5
5
  /**
6
6
  * Field Extension — inline field node (PAGE, NUMPAGES, DATE, MERGEFIELD, etc.)
@@ -0,0 +1,7 @@
1
+ import { NodeExtension } from "../types.js";
2
+
3
+ //#region src/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts
4
+ /** Zero-width cached pagination boundary preserved from DOCX. */
5
+ declare const RenderedPageBreakExtension: (options?: Partial<Record<string, unknown>> | undefined) => NodeExtension;
6
+ //#endregion
7
+ export { RenderedPageBreakExtension };
@@ -0,0 +1,22 @@
1
+ import { createNodeExtension } from "../create.js";
2
+ //#region src/prosemirror/extensions/nodes/RenderedPageBreakExtension.ts
3
+ /** Zero-width cached pagination boundary preserved from DOCX. */
4
+ const RenderedPageBreakExtension = createNodeExtension({
5
+ name: "renderedPageBreak",
6
+ schemaNodeName: "renderedPageBreak",
7
+ nodeSpec: {
8
+ inline: true,
9
+ group: "inline",
10
+ atom: true,
11
+ selectable: false,
12
+ parseDOM: [{ tag: "span[data-docx-rendered-page-break]" }],
13
+ toDOM() {
14
+ return ["span", {
15
+ "data-docx-rendered-page-break": "true",
16
+ style: "display: none;"
17
+ }];
18
+ }
19
+ }
20
+ });
21
+ //#endregion
22
+ export { RenderedPageBreakExtension };
@@ -3,8 +3,8 @@ import { expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, mergeTable
3
3
  import { createExtension, createNodeExtension } from "../create.js";
4
4
  import { resolveColor } from "../../../utils/colorResolver.js";
5
5
  import { panic } from "better-result";
6
- import { Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
7
6
  import { CellSelection, TableMap, columnResizing, mergeCells, removeRow, selectedRect, splitCell, tableEditing } from "prosemirror-tables";
7
+ import { Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
8
8
  import { Decoration, DecorationSet } from "prosemirror-view";
9
9
  //#region src/prosemirror/extensions/nodes/TableExtension.ts
10
10
  /**
@@ -190,7 +190,7 @@ const tableSpec = {
190
190
  }
191
191
  };
192
192
  const tableRowSpec = {
193
- content: "(tableCell | tableHeader)+",
193
+ content: "(tableCell | tableHeader)*",
194
194
  tableRole: "row",
195
195
  attrs: {
196
196
  height: { default: null },
@@ -198,7 +198,9 @@ const tableRowSpec = {
198
198
  isHeader: { default: false },
199
199
  hidden: { default: false },
200
200
  _originalFormatting: { default: null },
201
- trPrChange: { default: null }
201
+ trPrChange: { default: null },
202
+ trIns: { default: null },
203
+ trDel: { default: null }
202
204
  },
203
205
  parseDOM: [{ tag: "tr" }],
204
206
  toDOM(node) {
@@ -315,8 +317,10 @@ const tableCellSpec = {
315
317
  margins: { default: null },
316
318
  textDirection: { default: null },
317
319
  noWrap: { default: false },
320
+ hideMark: { default: false },
318
321
  _originalFormatting: { default: null },
319
322
  tcPrChange: { default: null },
323
+ cellMarker: { default: null },
320
324
  _preserveVMergeRestart: { default: null },
321
325
  _docxVMergeContinuationCells: { default: null }
322
326
  },
@@ -369,8 +373,10 @@ const tableHeaderSpec = {
369
373
  margins: { default: null },
370
374
  textDirection: { default: null },
371
375
  noWrap: { default: false },
376
+ hideMark: { default: false },
372
377
  _originalFormatting: { default: null },
373
378
  tcPrChange: { default: null },
379
+ cellMarker: { default: null },
374
380
  _preserveVMergeRestart: { default: null },
375
381
  _docxVMergeContinuationCells: { default: null }
376
382
  },
@@ -0,0 +1,13 @@
1
+ import { NodeExtension } from "../types.js";
2
+
3
+ //#region src/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts
4
+ /**
5
+ * Zero-width inline anchor for a text box extracted to a sibling PM block.
6
+ *
7
+ * Keeping the anchor in the paragraph lets ordinary ProseMirror transactions
8
+ * map its position as surrounding text is edited. The save conversion consumes
9
+ * it when the sibling text box is restored to the DOCX run stream.
10
+ */
11
+ declare const TextBoxAnchorExtension: (options?: Partial<Record<string, unknown>> | undefined) => NodeExtension;
12
+ //#endregion
13
+ export { TextBoxAnchorExtension };
@@ -0,0 +1,41 @@
1
+ import { expectTextBoxAnchorAttrs } from "../../textBoxAnchorAttrs.js";
2
+ import { createNodeExtension } from "../create.js";
3
+ //#region src/prosemirror/extensions/nodes/TextBoxAnchorExtension.ts
4
+ /**
5
+ * Zero-width inline anchor for a text box extracted to a sibling PM block.
6
+ *
7
+ * Keeping the anchor in the paragraph lets ordinary ProseMirror transactions
8
+ * map its position as surrounding text is edited. The save conversion consumes
9
+ * it when the sibling text box is restored to the DOCX run stream.
10
+ */
11
+ const TextBoxAnchorExtension = createNodeExtension({
12
+ name: "textBoxAnchor",
13
+ schemaNodeName: "textBoxAnchor",
14
+ nodeSpec: {
15
+ inline: true,
16
+ group: "inline",
17
+ marks: "_",
18
+ atom: true,
19
+ selectable: false,
20
+ attrs: { anchorId: {} },
21
+ parseDOM: [{
22
+ tag: "span[data-docx-textbox-anchor]",
23
+ getAttrs(dom) {
24
+ if (!(dom instanceof HTMLElement)) return false;
25
+ const anchorId = dom.dataset["docxTextboxAnchor"];
26
+ return anchorId ? { anchorId } : false;
27
+ }
28
+ }],
29
+ toDOM(node) {
30
+ const { anchorId } = expectTextBoxAnchorAttrs(node);
31
+ return ["span", {
32
+ "data-docx-textbox-anchor": anchorId,
33
+ "aria-hidden": "true",
34
+ contenteditable: "false",
35
+ style: "display: inline-block; width: 0; height: 0; overflow: hidden; pointer-events: none;"
36
+ }];
37
+ }
38
+ }
39
+ });
40
+ //#endregion
41
+ export { TextBoxAnchorExtension };
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../../../types/document.js";
2
2
  import { OutlineStyleAttr } from "../../../types/documentEnumValues.js";
3
3
  import { NodeExtension } from "../types.js";
4
- import { ImagePositionAttrs } from "../../schema/nodes.js";
4
+ import { ImagePositionAttrs, TextBoxAttrs as TextBoxAttrs$1 } from "../../schema/nodes.js";
5
5
 
6
6
  //#region src/prosemirror/extensions/nodes/TextBoxExtension.d.ts
7
7
  type TextBoxAttrs = {
@@ -28,7 +28,10 @@ type TextBoxAttrs = {
28
28
  distRight?: number; /** Position for floating/anchored text boxes. */
29
29
  position?: ImagePositionAttrs; /** Original DOCX placement hint for save-path reconstruction. */
30
30
  _docxPlacement?: "standalone" | "inlineWithPrevious"; /** Original DOCX paragraph group for standalone text-box reconstruction. */
31
- _docxGroupId?: string;
31
+ _docxGroupId?: string; /** Inline anchor linking this block node to its source run position. */
32
+ _docxAnchorId?: string; /** Original run-level revision wrapper for save-path reconstruction. */
33
+ _docxTrackedChange?: TextBoxAttrs$1["_docxTrackedChange"]; /** Original inline content-control ancestry for save-path reconstruction. */
34
+ _docxInlineSdts?: TextBoxAttrs$1["_docxInlineSdts"];
32
35
  };
33
36
  declare const TextBoxExtension: (options?: Partial<Record<string, unknown>> | undefined) => NodeExtension;
34
37
  //#endregion
@@ -50,7 +50,10 @@ const TextBoxExtension = createNodeExtension({
50
50
  distRight: { default: null },
51
51
  position: { default: null },
52
52
  _docxPlacement: { default: null },
53
- _docxGroupId: { default: null }
53
+ _docxGroupId: { default: null },
54
+ _docxAnchorId: { default: null },
55
+ _docxTrackedChange: { default: null },
56
+ _docxInlineSdts: { default: null }
54
57
  },
55
58
  parseDOM: [{
56
59
  tag: "div.docx-textbox",
@@ -3,18 +3,24 @@ import { Plugin, PluginKey } from "prosemirror-state";
3
3
  import { EditorView } from "prosemirror-view";
4
4
 
5
5
  //#region src/prosemirror/plugins/contentControlWidgets.d.ts
6
+ type ContentControlWidgetAnchor = {
7
+ getBoundingClientRect: () => {
8
+ bottom: number;
9
+ left: number;
10
+ };
11
+ };
6
12
  type ContentControlWidgetEvent = {
7
13
  kind: "dropdownOpen";
8
14
  tag: string; /** PM position of the clicked SDT instance — disambiguates duplicates. */
9
15
  pmPos: number;
10
16
  sdtType: "dropdown" | "comboBox";
11
- anchor: HTMLElement;
17
+ anchor: ContentControlWidgetAnchor;
12
18
  listItemsJson: string | undefined;
13
19
  } | {
14
20
  kind: "datePick";
15
21
  tag: string;
16
22
  pmPos: number;
17
- anchor: HTMLElement;
23
+ anchor: ContentControlWidgetAnchor;
18
24
  currentValue: string | undefined;
19
25
  } | {
20
26
  /**
@@ -26,21 +32,41 @@ type ContentControlWidgetEvent = {
26
32
  tag: string;
27
33
  pmPos: number;
28
34
  sdtType: string;
29
- anchor: HTMLElement;
35
+ anchor: ContentControlWidgetAnchor;
30
36
  error: ContentControlLockedError | ContentControlTypeError;
31
37
  };
32
38
  type ContentControlWidgetCallback = (event: ContentControlWidgetEvent) => void;
39
+ type ContentControlWidgetClickEvent = {
40
+ preventDefault: () => void;
41
+ target: unknown;
42
+ };
43
+ type ContentControlWidgetView = Pick<EditorView, "dispatch" | "state">;
44
+ type HandleContentControlWidgetClickOptions = {
45
+ event: ContentControlWidgetClickEvent;
46
+ onEvent: ContentControlWidgetCallback;
47
+ view: ContentControlWidgetView;
48
+ };
33
49
  declare const contentControlWidgetsPluginKey: PluginKey<unknown>;
34
50
  /**
35
51
  * CustomEvent name dispatched on the editor view DOM whenever the plugin
36
- * needs to surface a `ContentControlWidgetEvent` to the React shell. The
37
- * shell subscribes via `addEventListener` and renders the matching chrome.
52
+ * needs to surface a `ContentControlWidgetEvent` to an adapter shell. The
53
+ * adapter subscribes via `addEventListener` and renders the matching chrome.
38
54
  *
39
- * Using a CustomEvent keeps the plugin from owning a React reference and
40
- * makes the host's responsibility explicit: receive event → render UI →
55
+ * Using a CustomEvent keeps the plugin framework-neutral and makes the
56
+ * adapter's responsibility explicit: receive event → render UI →
41
57
  * call dispatchDropdownPick / dispatchDatePick.
42
58
  */
43
59
  declare const CONTENT_CONTROL_WIDGET_EVENT_NAME = "folio:content-control-widget";
60
+ /**
61
+ * Interpret one click from either the live ProseMirror DOM or the painted page
62
+ * tree. Both surfaces carry the same `data-sdt-*` projection, so adapters only
63
+ * need to forward the click and render the resulting typed event.
64
+ */
65
+ declare const handleContentControlWidgetClick: ({
66
+ event,
67
+ onEvent,
68
+ view
69
+ }: HandleContentControlWidgetClickOptions) => boolean;
44
70
  declare function createContentControlWidgetsPlugin(onEvent?: ContentControlWidgetCallback): Plugin;
45
71
  /**
46
72
  * Stable resolver helpers exposed for the UI shell so a popover menu or
@@ -53,4 +79,4 @@ declare function createContentControlWidgetsPlugin(onEvent?: ContentControlWidge
53
79
  declare function dispatchDropdownPick(view: EditorView, pmPos: number, value: string): boolean;
54
80
  declare function dispatchDatePick(view: EditorView, pmPos: number, date: string): boolean;
55
81
  //#endregion
56
- export { CONTENT_CONTROL_WIDGET_EVENT_NAME, ContentControlWidgetCallback, ContentControlWidgetEvent, contentControlWidgetsPluginKey, createContentControlWidgetsPlugin, dispatchDatePick, dispatchDropdownPick };
82
+ export { CONTENT_CONTROL_WIDGET_EVENT_NAME, ContentControlWidgetAnchor, ContentControlWidgetCallback, ContentControlWidgetEvent, contentControlWidgetsPluginKey, createContentControlWidgetsPlugin, dispatchDatePick, dispatchDropdownPick, handleContentControlWidgetClick };