@stll/folio-core 0.60.0 → 0.62.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 (173) hide show
  1. package/README.md +13 -0
  2. package/dist/ai-edits/apply.js +139 -32
  3. package/dist/ai-edits/headless.d.ts +3 -0
  4. package/dist/ai-edits/headless.js +70 -75
  5. package/dist/ai-edits/note-story-inventory.d.ts +7 -0
  6. package/dist/ai-edits/note-story-inventory.js +11 -0
  7. package/dist/ai-edits/read.js +16 -9
  8. package/dist/ai-edits/revisionStretches.js +52 -1
  9. package/dist/ai-edits/table-row-column-mutations.js +6 -1
  10. package/dist/compare/reproducible-package.js +2 -1
  11. package/dist/compare/verification.js +1 -0
  12. package/dist/compat/eigenpal.d.ts +3 -1
  13. package/dist/controller/canonicalClipboard.js +2 -1
  14. package/dist/controller/canonicalListRendering.d.ts +4 -6
  15. package/dist/controller/canonicalListRendering.js +3 -157
  16. package/dist/controller/headerFooterEditorManager.d.ts +1 -0
  17. package/dist/controller/headerFooterEditorManager.js +11 -0
  18. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  19. package/dist/controller/hiddenEditorManager.js +3 -1
  20. package/dist/controller/layoutPipeline.d.ts +3 -1
  21. package/dist/controller/layoutPipeline.js +13 -4
  22. package/dist/controller/layoutSession.d.ts +2 -1
  23. package/dist/controller/noteEditorManager.d.ts +1 -1
  24. package/dist/display-list/build/imagePrimitives.js +28 -12
  25. package/dist/display-list/build/paragraphPrimitives.js +15 -2
  26. package/dist/display-list/dom/renderDisplayListToDom.js +8 -5
  27. package/dist/display-list/types.d.ts +7 -3
  28. package/dist/docx/canonicalStoryRepack.js +3 -2
  29. package/dist/docx/commentTextParagraph.d.ts +6 -0
  30. package/dist/docx/commentTextParagraph.js +15 -0
  31. package/dist/docx/conformance.js +6 -1
  32. package/dist/docx/diagramPreview.js +2 -1
  33. package/dist/docx/documentParser.d.ts +13 -1
  34. package/dist/docx/documentParser.js +1 -1
  35. package/dist/docx/encryption/compoundFile.d.ts +0 -8
  36. package/dist/docx/encryption/compoundFile.js +2 -1
  37. package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
  38. package/dist/docx/encryption/cryptoBytes.js +3 -18
  39. package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
  40. package/dist/docx/encryption/encryptionInfo.js +2 -1
  41. package/dist/docx/ensureParaIds.js +2 -1
  42. package/dist/docx/groupDrawingParser.js +28 -13
  43. package/dist/docx/listModelNormalization.d.ts +10 -0
  44. package/dist/docx/listModelNormalization.js +166 -0
  45. package/dist/docx/metadataPrivacy.js +3 -2
  46. package/dist/docx/metafileRaster.js +2 -1
  47. package/dist/docx/packageParts.js +6 -5
  48. package/dist/docx/paragraphParser.js +6 -4
  49. package/dist/docx/parser.js +5 -4
  50. package/dist/docx/preservedRunContent.js +11 -2
  51. package/dist/docx/removeHeaderFooterParts.js +8 -7
  52. package/dist/docx/replyToComment.js +3 -13
  53. package/dist/docx/rezip.js +88 -89
  54. package/dist/docx/runParser.js +12 -6
  55. package/dist/docx/runPropertyPresence.d.ts +11 -0
  56. package/dist/docx/runPropertyPresence.js +14 -0
  57. package/dist/docx/selectiveSave.js +15 -14
  58. package/dist/docx/serializer/runSerializer.js +3 -2
  59. package/dist/docx/serializer/tableSerializer.js +2 -1
  60. package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
  61. package/dist/docx/server/boundedArchive.js +2 -1
  62. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
  63. package/dist/docx/server/inspectDocxPackage.js +5 -1
  64. package/dist/docx/server/validateDocxConformance.js +2 -1
  65. package/dist/docx/tableParser.js +7 -4
  66. package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
  67. package/dist/docx/tableRowPropertyPresence.js +15 -0
  68. package/dist/docx/unzip.js +2 -1
  69. package/dist/docx/xmlEncoding.js +6 -5
  70. package/dist/fonts/fontsourceFaces.js +2 -1
  71. package/dist/fonts/sfnt/parse.js +6 -9
  72. package/dist/fonts/sfnt/subset.js +2 -1
  73. package/dist/fonts/sfnt/tables.js +2 -3
  74. package/dist/fonts/sfnt/woff.js +2 -1
  75. package/dist/i18n/messages/ar.gen.js +1 -0
  76. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  77. package/dist/i18n/messages/catalogs.gen.js +17 -0
  78. package/dist/i18n/messages/cs.gen.js +1 -0
  79. package/dist/i18n/messages/de.gen.js +1 -0
  80. package/dist/i18n/messages/en.gen.js +1 -0
  81. package/dist/i18n/messages/es.gen.js +1 -0
  82. package/dist/i18n/messages/et.gen.js +1 -0
  83. package/dist/i18n/messages/fr.gen.js +1 -0
  84. package/dist/i18n/messages/he.gen.js +1 -0
  85. package/dist/i18n/messages/hi.gen.js +1 -0
  86. package/dist/i18n/messages/hu.gen.js +1 -0
  87. package/dist/i18n/messages/lt.gen.js +1 -0
  88. package/dist/i18n/messages/lv.gen.js +1 -0
  89. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  90. package/dist/i18n/messages/pl.gen.js +1 -0
  91. package/dist/i18n/messages/pt-BR.gen.js +1 -0
  92. package/dist/i18n/messages/sk.gen.js +1 -0
  93. package/dist/i18n/messages/tr.gen.js +1 -0
  94. package/dist/i18n/messages/zh-CN.gen.js +1 -0
  95. package/dist/index.d.ts +3 -1
  96. package/dist/internal/revisionResolutionInline.d.ts +2 -1
  97. package/dist/internal/revisionResolutionInline.js +22 -1
  98. package/dist/internal/wholeStoryRevisionResolution.js +24 -7
  99. package/dist/layout-bridge/convert/endnoteLayout.d.ts +1 -1
  100. package/dist/layout-bridge/convert/endnoteLayout.js +1 -3
  101. package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
  102. package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
  103. package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
  104. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
  105. package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
  106. package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
  107. package/dist/layout-engine/index.js +2 -2
  108. package/dist/layout-engine/measure/crossRunWords.js +12 -2
  109. package/dist/layout-engine/noteAreaFlow.d.ts +4 -4
  110. package/dist/layout-engine/noteAreaFlow.js +12 -6
  111. package/dist/layout-engine/types.d.ts +5 -2
  112. package/dist/layout-painter/index.d.ts +1 -1
  113. package/dist/managers/AutoSaveManager.js +2 -1
  114. package/dist/managers/EditorModeManager.d.ts +10 -1
  115. package/dist/managers/EditorModeManager.js +15 -1
  116. package/dist/managers/FindReplaceManager.d.ts +13 -6
  117. package/dist/managers/FindReplaceManager.js +15 -9
  118. package/dist/managers/TableSelectionManager.d.ts +1 -1
  119. package/dist/managers/autoSaveCodec.js +2 -6
  120. package/dist/managers/reviewDisplayMode.d.ts +8 -0
  121. package/dist/managers/reviewDisplayMode.js +15 -0
  122. package/dist/paged-layout/editorScrollRoot.d.ts +3 -1
  123. package/dist/paged-layout/editorScrollRoot.js +2 -2
  124. package/dist/paged-layout/pageText.d.ts +11 -2
  125. package/dist/paged-layout/pageText.js +8 -3
  126. package/dist/pdf/fonts.js +5 -14
  127. package/dist/pdf/images.js +4 -2
  128. package/dist/pdf/objects.d.ts +0 -11
  129. package/dist/pdf/objects.js +7 -9
  130. package/dist/pdf/writePdf.js +2 -1
  131. package/dist/prosemirror/commands/comments.js +51 -13
  132. package/dist/prosemirror/commentRangeInlineAtoms.d.ts +40 -0
  133. package/dist/prosemirror/commentRangeInlineAtoms.js +33 -0
  134. package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
  135. package/dist/prosemirror/containerFinalParagraph.js +8 -8
  136. package/dist/prosemirror/conversion/fromProseDoc.js +14 -8
  137. package/dist/prosemirror/conversion/toProseDoc.js +16 -23
  138. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
  139. package/dist/prosemirror/fieldRepresentation.js +4 -2
  140. package/dist/prosemirror/findReplaceSelection.d.ts +48 -2
  141. package/dist/prosemirror/findReplaceSelection.js +153 -38
  142. package/dist/prosemirror/inlineDisplayText.d.ts +10 -0
  143. package/dist/prosemirror/inlineDisplayText.js +29 -0
  144. package/dist/prosemirror/markupViewProjection.d.ts +10 -1
  145. package/dist/prosemirror/markupViewProjection.js +21 -3
  146. package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
  147. package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
  148. package/dist/prosemirror/replacedAnnotations.js +71 -12
  149. package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
  150. package/dist/prosemirror/runFormattingReconciliation.js +10 -2
  151. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
  152. package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
  153. package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
  154. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
  155. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
  156. package/dist/utils/base64.d.ts +1 -31
  157. package/dist/utils/base64.js +19 -33
  158. package/dist/utils/clipboard.js +5 -4
  159. package/dist/utils/colorResolver.js +11 -2
  160. package/dist/utils/findMatchView.d.ts +32 -0
  161. package/dist/utils/findMatchView.js +406 -0
  162. package/dist/utils/findReplace.d.ts +67 -4
  163. package/dist/utils/findReplace.js +359 -37
  164. package/dist/utils/findSearchKey.d.ts +29 -0
  165. package/dist/utils/findSearchKey.js +34 -0
  166. package/dist/utils/replaceText.js +138 -94
  167. package/dist/utils/sliceText.d.ts +2 -0
  168. package/dist/utils/sliceText.js +2 -0
  169. package/dist/utils/textEncoding.d.ts +2 -0
  170. package/dist/utils/textEncoding.js +2 -0
  171. package/dist/utils/tiffConverter.d.ts +0 -9
  172. package/dist/utils/tiffConverter.js +2 -4
  173. package/package.json +51 -25
package/README.md CHANGED
@@ -42,6 +42,19 @@ the same runtime. The command above includes the complete editor peer closure.
42
42
  | `@stll/folio-core/redline` | Compare two `.docx` buffers and generate a native Word redline |
43
43
  | `@stll/folio-core/*` | the source-mirrored module tree (e.g. `@stll/folio-core/types/document`, `@stll/folio-core/prosemirror/schema`) for adapters that need lower-level building blocks |
44
44
 
45
+ ## Review display configuration
46
+
47
+ `ReviewDisplayMode` is `all-markup | final | original`.
48
+ `reviewDisplayModeToDisplayMode` in `managers/reviewDisplayMode` maps it to the
49
+ layout pipeline's `markupView` input. The shared projection reuses the
50
+ accept-all/reject-all resolver without changing editor state. Layout and
51
+ search consume that projection; saved DOCX always uses the authored state.
52
+ `getEditorReadOnlyReason` in `managers/EditorModeManager` makes projected review
53
+ views read-only. Both adapters expose the resulting `EditorReadOnlyReason`
54
+ through `getReadOnlyReason()` and `onReadonlyEditAttempt(reason)`.
55
+ `showCommentHighlights` on the layout pipeline defaults to `true`; final
56
+ (`no-markup`) and original suppress comment highlights regardless of it.
57
+
45
58
  ## New documents and reusable style sets
46
59
 
47
60
  Create a legal-shaped blank document with the built-in preset:
@@ -11,6 +11,7 @@ import { encloseWholeControls } from "../prosemirror/contentControlRevisions.js"
11
11
  import { JOINED_RUNS_RESTYLED_META } from "../prosemirror/extensions/features/JoinedRunStyleExtension.js";
12
12
  import { requestDeterministicParaIds } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
13
13
  import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
14
+ import "../prosemirror/extensions/nodes/RangeAnchorExtension.js";
14
15
  import "../prosemirror/extensions/nodes/TextBoxAnchorExtension.js";
15
16
  import { CLEARED_LIST_RENDERING_ATTRS } from "../prosemirror/listMarker.js";
16
17
  import { effectiveParagraphNumbering, paragraphNumberingAttr, paragraphNumberingSourcesFromBoundary, readParagraphNumberingAttr, removedNumberingAttr } from "../prosemirror/numberingAttr.js";
@@ -23,11 +24,12 @@ import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqua
23
24
  import { getDocumentNumbering, getDocumentNumberingInstanceIds } from "../prosemirror/plugins/documentNumbering.js";
24
25
  import { getDocumentStyleDefinitions, getDocumentStyleResolver } from "../prosemirror/plugins/documentStyleState.js";
25
26
  import { claimRevisionIds, nextRevisionId, nextRevisionIdRange, reserveRevisionIds, seedRevisionIdsFromDoc } from "../prosemirror/plugins/revisionIds.js";
27
+ import { readRangeAnchorAttrs } from "../prosemirror/rangeAnchorAttrs.js";
26
28
  import { rebaseParagraphRuns } from "../prosemirror/rebaseParagraphRuns.js";
27
- import { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, placeTrackedInsertion, surveyReplacedAnnotations } from "../prosemirror/replacedAnnotations.js";
29
+ import { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, placeTrackedInsertion, surveyReplacedAnnotations } from "../prosemirror/replacedAnnotations.js";
28
30
  import { applyMarksToRunFormattingRepresentation, runFormattingInlineControlNodeName, selectRunFormattingCarrierRepresentations } from "../prosemirror/runFormattingInlineCarriers.js";
29
31
  import { RUN_FORMATTING_MARK_NAMES } from "../prosemirror/runFormattingMarkNames.js";
30
- import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
32
+ import { readAuthoredRunFormatting, readAuthoredRunPropertySnapshot, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
31
33
  import { paragraphRunStyleContext, paragraphRunStyleContextAt, resolveParagraphBodyRunFormatting } from "../prosemirror/runStyleFormatting.js";
32
34
  import { sectionPropertiesOf } from "../prosemirror/sectionCarrier.js";
33
35
  import { listAttrsFromNumbering, listLevelAttrPatch, listLevelIndentRemovalPatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
@@ -788,12 +790,12 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
788
790
  pos: representation.from,
789
791
  ...styleResolver !== void 0 ? { styleResolver } : {}
790
792
  });
791
- const previousFormatting = readAuthoredRunFormatting({
793
+ const previousFormatting = readAuthoredRunPropertySnapshot({
792
794
  context: styleContext,
793
795
  marks: node.marks,
794
796
  ...styleResolver !== void 0 ? { styleResolver } : {}
795
797
  });
796
- const includedProperties = formattingChangesForAuthoredFormatting(previousFormatting, formatting);
798
+ const includedProperties = formattingChangesForAuthoredFormatting(previousFormatting ?? {}, formatting);
797
799
  if (includedProperties.length === 0) continue;
798
800
  const existingMark = node.marks.find((mark) => mark.type === propertyChangeType);
799
801
  if (existingMark && expectRunPropertyChangeMarkAttrs(existingMark).changes.length > 0) {
@@ -802,7 +804,7 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
802
804
  }
803
805
  const formattingMarks = reconcileRunFormattingMarks({
804
806
  authoredFormatting: patchAuthoredRunFormatting({
805
- authoredFormatting: previousFormatting,
807
+ authoredFormatting: previousFormatting ?? {},
806
808
  formatting,
807
809
  includedProperties
808
810
  }),
@@ -845,7 +847,7 @@ const applyTrackedInlineFormattingToRanges = ({ tr, schema, doc = tr.doc, ranges
845
847
  date,
846
848
  ...initials ? { initials } : {}
847
849
  },
848
- ...Object.keys(segment.previousFormatting).length > 0 ? { previousFormatting: segment.previousFormatting } : {}
850
+ ...segment.previousFormatting !== void 0 ? { previousFormatting: segment.previousFormatting } : {}
849
851
  };
850
852
  const marks = propertyChangeType.create({
851
853
  changes: [change],
@@ -1353,12 +1355,12 @@ const isPendingDeletion = (doc, blockFrom, paragraph, cleanText) => {
1353
1355
  return false;
1354
1356
  };
1355
1357
  /** Whether every piece of the paragraph's content is pending inserted text. */
1356
- const holdsOnlyInsertedContent = (paragraph) => {
1358
+ const holdsOnlyInsertedContent = (paragraph, revisions) => {
1357
1359
  let whollyInserted = true;
1358
1360
  paragraph.descendants((node) => {
1359
1361
  if (!whollyInserted) return false;
1360
1362
  if (!node.isInline || isZeroWidthAnchor(node)) return true;
1361
- whollyInserted = node.marks.some((mark) => mark.type.name === "insertion" && mark.attrs["moveKind"] !== "moveTo");
1363
+ whollyInserted = node.marks.some((mark) => mark.type.name === "insertion" && (revisions === "including-moves" || mark.attrs["moveKind"] !== "moveTo"));
1362
1364
  return false;
1363
1365
  });
1364
1366
  return whollyInserted;
@@ -1376,11 +1378,16 @@ const holdsOnlyInsertedContent = (paragraph) => {
1376
1378
  */
1377
1379
  const pendingParagraphRetraction = (doc, position) => {
1378
1380
  const paragraph = doc.nodeAt(position);
1379
- if (paragraph?.type.name !== "paragraph" || !holdsOnlyInsertedContent(paragraph)) return null;
1381
+ if (paragraph?.type.name !== "paragraph") return null;
1380
1382
  const at = doc.resolve(position);
1381
1383
  const before = at.nodeBefore;
1382
1384
  const endsItsContainer = paragraphEndsItsContainer(at, paragraph.type.name);
1383
1385
  if (at.parent.childCount < 2 || endsItsContainer && before?.type.name !== paragraph.type.name) return null;
1386
+ let followingIndex = at.index() + 1;
1387
+ while (at.parent.maybeChild(followingIndex)?.type.name === "blockBookmarkBoundary") followingIndex++;
1388
+ const following = at.parent.maybeChild(followingIndex);
1389
+ if (expectParagraphAttrs(paragraph).pPrMark?.kind === "moveTo" && following?.type.spec["tableRole"] === "table" && holdsOnlyInsertedContent(paragraph, "including-moves")) return { clearBreakAt: null };
1390
+ if (!holdsOnlyInsertedContent(paragraph, "ordinary")) return null;
1384
1391
  if (isInsertedPPrMark(paragraph.attrs["pPrMark"]) || isSuggestedParagraphInsert(paragraph)) return { clearBreakAt: null };
1385
1392
  if (endsItsContainer && before && paragraph.attrs["pPrMark"] == null) return isInsertedPPrMark(before.attrs["pPrMark"]) ? { clearBreakAt: position - before.nodeSize } : null;
1386
1393
  return null;
@@ -1596,7 +1603,7 @@ const hasDeferredParagraphPropertyWrite = ({ tr, deletions, targets, mergedPrope
1596
1603
  })) continue;
1597
1604
  const writeTarget = mark == null ? target : tr.doc.nodeAt(predecessor.position + predecessor.node.nodeSize);
1598
1605
  if (!writeTarget || !targets.includes(writeTarget)) continue;
1599
- if (mark != null && !(isInsertedPPrMark(mark) && canRetractFinalBreak({
1606
+ if (mark != null && !(addedBreakRevisionId(mark) !== null && canRetractFinalBreak({
1600
1607
  predecessor: predecessor.node,
1601
1608
  target: writeTarget
1602
1609
  }))) continue;
@@ -1629,7 +1636,7 @@ const paragraphPropertyRevisionTargets = ({ item, tr, deletedFinalParagraphs })
1629
1636
  for (const deletion of deletedFinalParagraphs) {
1630
1637
  const at = tr.mapping.slice(deletion.mappedFrom).map(deletion.position);
1631
1638
  const predecessor = finalParagraphPredecessor(tr.doc, at);
1632
- if (!predecessor || !isInsertedPPrMark(predecessor.node.attrs["pPrMark"])) continue;
1639
+ if (!predecessor || addedBreakRevisionId(predecessor.node.attrs["pPrMark"]) === null) continue;
1633
1640
  const target = tr.doc.nodeAt(predecessor.position + predecessor.node.nodeSize);
1634
1641
  if (!target) continue;
1635
1642
  if (canRetractFinalBreak({
@@ -1727,7 +1734,7 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
1727
1734
  });
1728
1735
  continue;
1729
1736
  }
1730
- if (isInsertedPPrMark(mark)) {
1737
+ if (addedBreakRevisionId(mark) !== null) {
1731
1738
  const following = tr.doc.nodeAt(position + previous.nodeSize);
1732
1739
  if (!following || !canRetractFinalBreak({
1733
1740
  predecessor: previous,
@@ -1777,10 +1784,10 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
1777
1784
  /**
1778
1785
  * Move an ADDED paragraph break off every paragraph its container ends with.
1779
1786
  *
1780
- * A container's final mark carries no revision in either direction: nothing
1781
- * follows it, so "join this paragraph with the one after it" and its mirror
1782
- * both state an edit that cannot be carried out, and a reader is left with a
1783
- * revision neither accepting nor rejecting everything can clear.
1787
+ * Prefer a preceding carrier so rejection can join the inserted content back
1788
+ * over its added boundary. When no carrier is free, the insertion keeps its
1789
+ * own mark: the resolver can remove that whole paragraph when its text is
1790
+ * rejected, while a markless empty paragraph would have no insertion owner.
1784
1791
  *
1785
1792
  * Appending at a container's end therefore rotates exactly as removing from it
1786
1793
  * does. The first inserted break moves onto the paragraph the run was appended
@@ -1806,7 +1813,9 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
1806
1813
  * paragraph's words run into the one after it, which ends with the free mark,
1807
1814
  * and neither break is left to resolve.
1808
1815
  */
1809
- const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials, styleResolver }) => {
1816
+ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials, styleResolver, numbering }) => {
1817
+ let nextRevisionId = revisionSeed;
1818
+ const synthesizedRevisions = [];
1810
1819
  const supersededDeletedBreaks = [];
1811
1820
  const collectSupersededDeletedBreaks = (parent, contentStart) => {
1812
1821
  let previous = null;
@@ -1824,7 +1833,7 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
1824
1833
  const trackedInsertion = insertedRevisionId !== null && batchRevisionIds.has(insertedRevisionId);
1825
1834
  const suggestedInsertion = isSuggestedParagraphInsert(node);
1826
1835
  const deletedBreakRevisionId = addedOrDeletedBreakRevisionId(deletedMark);
1827
- if (isPlainDeletedPPrMark(deletedMark) && deletedBreakRevisionId !== null && !batchRevisionIds.has(deletedBreakRevisionId) && !holdsOnlyDeletedContent(previous.node) && (trackedInsertion || suggestedInsertion) && holdsOnlyInsertedContent(node)) supersededDeletedBreaks.push({
1836
+ if (isPlainDeletedPPrMark(deletedMark) && deletedBreakRevisionId !== null && !batchRevisionIds.has(deletedBreakRevisionId) && !holdsOnlyDeletedContent(previous.node) && (trackedInsertion || suggestedInsertion) && holdsOnlyInsertedContent(node, "ordinary")) supersededDeletedBreaks.push({
1828
1837
  preceding: previous.position,
1829
1838
  inserted: position,
1830
1839
  deletedMark,
@@ -1859,6 +1868,33 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
1859
1868
  }
1860
1869
  tr.setNodeAttribute(preceding, "pPrMark", precedingMark);
1861
1870
  tr.setNodeAttribute(inserted, "pPrMark", deletedMark);
1871
+ if (insertionRevisionId === null) continue;
1872
+ const source = tr.doc.nodeAt(inserted);
1873
+ const survivor = paragraphLeftAfter({
1874
+ doc: tr.doc,
1875
+ paragraphPos: inserted
1876
+ });
1877
+ if (!source || survivor === null) continue;
1878
+ const revisionId = nextRevisionId;
1879
+ if (carryParagraphProperties({
1880
+ tr,
1881
+ position: survivor,
1882
+ source,
1883
+ styleResolver,
1884
+ numbering,
1885
+ revision: {
1886
+ id: revisionId,
1887
+ author,
1888
+ date,
1889
+ ...initials ? { initials } : {}
1890
+ }
1891
+ }).tracked) {
1892
+ nextRevisionId++;
1893
+ synthesizedRevisions.push({
1894
+ ownerRevisionId: insertionRevisionId,
1895
+ revisionId
1896
+ });
1897
+ }
1862
1898
  }
1863
1899
  const paragraphTypeName = tr.doc.type.schema.nodes["paragraph"]?.name ?? "paragraph";
1864
1900
  const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).flatMap(({ node, position }) => {
@@ -1872,16 +1908,11 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
1872
1908
  }] : [];
1873
1909
  }).toSorted((left, right) => right.position - left.position);
1874
1910
  let next = tr;
1875
- let nextRevisionId = revisionSeed;
1876
- const synthesizedRevisions = [];
1877
1911
  const retractedRevisionIds = [];
1878
1912
  for (const { position: finalPosition, node: final, mark: finalMark } of rotations) {
1879
1913
  const resolved = next.doc.resolve(finalPosition);
1880
1914
  const carrier = finalBreakCarrierBefore(resolved, final.type.name, batchRevisionIds);
1881
- if (!carrier) {
1882
- next = next.setNodeAttribute(finalPosition, "pPrMark", null);
1883
- continue;
1884
- }
1915
+ if (!carrier) continue;
1885
1916
  const path = [{
1886
1917
  node: final,
1887
1918
  position: finalPosition
@@ -1897,10 +1928,10 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
1897
1928
  });
1898
1929
  if (position === carrier.position) break;
1899
1930
  }
1900
- if (path.at(-1)?.position !== carrier.position) {
1901
- next = next.setNodeAttribute(finalPosition, "pPrMark", null);
1902
- continue;
1903
- }
1931
+ if (path.at(-1)?.position !== carrier.position) panic("A final-mark rotation lost its carrier", {
1932
+ finalPosition,
1933
+ carrier: carrier.position
1934
+ });
1904
1935
  if (path.slice(0, -1).some(({ node }) => {
1905
1936
  const revisionId = addedBreakRevisionId(node.attrs["pPrMark"]);
1906
1937
  return revisionId === null || !batchRevisionIds.has(revisionId);
@@ -2581,6 +2612,17 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
2581
2612
  date,
2582
2613
  ...trackedRevisionExtras
2583
2614
  } : null;
2615
+ const commentSource = tr.doc;
2616
+ const commentStepsBefore = tr.steps.length;
2617
+ const commentReplacement = replacementText(item.operation);
2618
+ const commentPlan = mode === "direct" && commentReplacement !== null && surveyReplacedAnnotations(commentSource, item.from, item.to).carried.some((mark) => mark.type.name === "comment") ? planDocumentReplacement({
2619
+ doc: commentSource,
2620
+ item,
2621
+ replacement: stripInlineEmphasisMarkers(commentReplacement),
2622
+ noteReferences,
2623
+ planChanges: (before, after) => changesFromSegments(diffText(before, after)),
2624
+ keepFieldsOf: planTextChanges
2625
+ }) : null;
2584
2626
  switch (item.operation.type) {
2585
2627
  case "replaceInBlock":
2586
2628
  case "replaceRange": {
@@ -3259,8 +3301,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
3259
3301
  const markPosition = tr.mapping.map(item.blockFrom);
3260
3302
  const endsItsContainer = paragraphEndsItsContainer(tr.doc.resolve(markPosition), item.blockNode.type.name);
3261
3303
  const deleted = tr.doc.nodeAt(markPosition);
3262
- const following = deleted && tr.doc.nodeAt(markPosition + deleted.nodeSize);
3263
- if (deleted && following?.type === deleted.type && !isPairedMove(item.operation.moveId) && isInsertedPPrMark(deleted.attrs["pPrMark"])) {
3304
+ const following = deleted && paragraphJoinAfter(tr.doc, markPosition + deleted.nodeSize)?.next;
3305
+ if (deleted && following?.type === deleted.type && !isPairedMove(item.operation.moveId) && addedBreakRevisionId(deleted.attrs["pPrMark"]) !== null) {
3264
3306
  const restored = paragraphPropertiesOnReject(deleted);
3265
3307
  const followingChanges = expectParagraphAttrs(following)._propertyChanges;
3266
3308
  const recordsRestored = isInsertedPPrMark(following.attrs["pPrMark"]) && !hasSerializableParagraphPropertyChange(followingChanges) && JSON.stringify(restored) !== JSON.stringify(paragraphPropertiesSnapshot(following));
@@ -3784,6 +3826,14 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
3784
3826
  break;
3785
3827
  default: break;
3786
3828
  }
3829
+ if (commentPlan !== null) tr = applyAlignedReplacementComments({
3830
+ tr,
3831
+ source: commentSource,
3832
+ item,
3833
+ plan: commentPlan,
3834
+ stepsBefore: commentStepsBefore,
3835
+ noteReferences
3836
+ });
3787
3837
  if (tr.steps.length === stepsBefore) {
3788
3838
  if (!deletedFinalParagraphs.some(({ operationId }) => operationId === item.operation.id)) {
3789
3839
  skipped.push({
@@ -3838,7 +3888,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
3838
3888
  author,
3839
3889
  date,
3840
3890
  initials,
3841
- styleResolver: styleResolver ?? null
3891
+ styleResolver: styleResolver ?? null,
3892
+ numbering
3842
3893
  });
3843
3894
  tr = rotated.transaction;
3844
3895
  revisionSeed = rotated.nextRevisionId;
@@ -4353,6 +4404,57 @@ const directReplacementMarks = ({ doc, plan, replacementBackground }) => {
4353
4404
  keepUntil(plan.spanEnd - plan.spanStart);
4354
4405
  return allocated;
4355
4406
  };
4407
+ const replacementText = (operation) => {
4408
+ switch (operation.type) {
4409
+ case "replaceBlock": return operation.text;
4410
+ case "replaceInBlock":
4411
+ case "replaceRange": return operation.replace;
4412
+ default: return null;
4413
+ }
4414
+ };
4415
+ /** Both modes project comment coverage through the same text alignment. */
4416
+ const applyAlignedReplacementComments = ({ tr, source, item, plan, stepsBefore, noteReferences }) => {
4417
+ const comments = surveyReplacedAnnotations(source, item.from, item.to).carried.filter((mark) => mark.type.name === "comment");
4418
+ if (comments.length === 0) return tr;
4419
+ const marks = directReplacementMarks({
4420
+ doc: source,
4421
+ plan,
4422
+ replacementBackground: "keep"
4423
+ });
4424
+ const blockFrom = tr.mapping.slice(stepsBefore).map(item.blockFrom, -1);
4425
+ const block = tr.doc.nodeAt(blockFrom);
4426
+ if (!block) panic("Replacement lost its paragraph");
4427
+ const clean = buildCleanBlockText(block, blockFrom, {
4428
+ fieldResults: "text",
4429
+ noteReferences
4430
+ });
4431
+ for (const [index, expected] of marks.entries()) {
4432
+ const from = clean.offsets[plan.spanStart + index];
4433
+ if (from === void 0) panic("Replacement lost an aligned character");
4434
+ const node = tr.doc.nodeAt(from);
4435
+ if (!node?.isText || noteReferenceOf(node) !== null) continue;
4436
+ for (const comment of comments) if (comment.isInSet(expected)) tr.addMark(from, from + 1, comment);
4437
+ else tr.removeMark(from, from + 1, comment);
4438
+ }
4439
+ const visible = /* @__PURE__ */ new Set();
4440
+ const points = /* @__PURE__ */ new Set();
4441
+ tr.doc.descendants((node) => {
4442
+ if (node.type.name === "rangeAnchor") {
4443
+ const attrs = readRangeAnchorAttrs(node);
4444
+ if (attrs.ok && attrs.value.start.type === "commentRangeStart") points.add(attrs.value.start.id);
4445
+ }
4446
+ if (!node.isInline || isZeroWidthAnchor(node) || node.marks.some((mark) => mark.type.name === "deletion")) return;
4447
+ for (const mark of node.marks) if (mark.type.name === "comment") visible.add(mark.attrs["commentId"]);
4448
+ });
4449
+ const at = clean.offsets[plan.spanStart];
4450
+ if (at === void 0) panic("Replacement lost its start gap");
4451
+ for (const comment of comments) {
4452
+ const id = comment.attrs["commentId"];
4453
+ if (visible.has(id) || points.has(id)) continue;
4454
+ tr.insert(at, commentPoint(tr.doc.type.schema, id));
4455
+ }
4456
+ return tr;
4457
+ };
4356
4458
  /**
4357
4459
  * Apply a direct-mode text replacement as the difference between the matched
4358
4460
  * text and its replacement; `minimal-replacement.ts` states the contract.
@@ -4474,11 +4576,16 @@ const applyMinimalTrackedReplacement = ({ tr, item, noteReferences, replacement,
4474
4576
  noteReferences,
4475
4577
  planChanges: planTextChanges
4476
4578
  });
4579
+ const annotationMarks = directReplacementMarks({
4580
+ doc: tr.doc,
4581
+ plan,
4582
+ replacementBackground: "keep"
4583
+ });
4477
4584
  const allocatedMarks = directPlan === null ? null : directReplacementMarks({
4478
4585
  doc: tr.doc,
4479
4586
  plan: directPlan,
4480
4587
  replacementBackground
4481
- });
4588
+ }).map((marks, index) => addCarriedMarks(marks.filter((mark) => mark.type.name !== "comment"), (annotationMarks[index] ?? []).filter((mark) => mark.type.name === "comment")));
4482
4589
  const unifiesFormatting = directPlan === null ? false : replacementUnifiesFormatting(tr.doc, directPlan);
4483
4590
  let nextTr = tr;
4484
4591
  let backgroundRevisionIds = [];
@@ -591,6 +591,8 @@ declare class FolioDocxReviewer {
591
591
  private noteKeysReferencedOutsideBody;
592
592
  private resolveEveryStoryOnce;
593
593
  private acceptOrdinaryChangesWithSuggestions;
594
+ /** Resolve ordinary revisions on a detached in-memory document. */
595
+ toResolvedDocument(decision: "accept" | "reject"): document_d_exports.Document;
594
596
  /** The current document model with edits merged back in. */
595
597
  toDocument(): document_d_exports.Document;
596
598
  private captureReviewerState;
@@ -625,6 +627,7 @@ declare class FolioDocxReviewer {
625
627
  * state for a story nothing edits.
626
628
  */
627
629
  private readSecondaryStoryDoc;
630
+ private secondaryStorySource;
628
631
  private secondaryStoryDoc;
629
632
  private requireEditableStoryState;
630
633
  private setEditableStoryState;