@stll/folio-core 0.56.0 → 0.57.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 (142) hide show
  1. package/dist/ai-edits/apply.js +45 -14
  2. package/dist/ai-edits/batch-claims.d.ts +3 -1
  3. package/dist/ai-edits/batch-claims.js +25 -1
  4. package/dist/ai-edits/table-row-column-mutations.js +17 -3
  5. package/dist/ai-suggestions/apply.js +20 -1
  6. package/dist/compare/inline-atoms.js +40 -16
  7. package/dist/compare/inline-provenance.js +12 -5
  8. package/dist/compat/eigenpal.d.ts +2 -1
  9. package/dist/compat/eigenpal.js +2 -1
  10. package/dist/controller/canonicalClipboard.js +7 -1
  11. package/dist/controller/canonicalComments.d.ts +41 -0
  12. package/dist/controller/canonicalComments.js +152 -0
  13. package/dist/controller/canonicalComposition.d.ts +4 -0
  14. package/dist/controller/canonicalComposition.js +43 -11
  15. package/dist/controller/canonicalInlineProjection.d.ts +6 -1
  16. package/dist/controller/canonicalInlineProjection.js +86 -3
  17. package/dist/controller/canonicalInput.js +10 -1
  18. package/dist/controller/canonicalInputTimer.d.ts +10 -1
  19. package/dist/controller/canonicalInputTimer.js +24 -1
  20. package/dist/controller/canonicalPublicOperations.d.ts +2 -2
  21. package/dist/controller/canonicalPublicOperations.js +107 -15
  22. package/dist/controller/canonicalSelection.d.ts +18 -0
  23. package/dist/controller/canonicalSelection.js +32 -0
  24. package/dist/controller/canonicalSession.d.ts +3 -0
  25. package/dist/controller/canonicalSession.js +58 -24
  26. package/dist/controller/canonicalStoryEditor.d.ts +2 -0
  27. package/dist/controller/canonicalStoryEditor.js +20 -0
  28. package/dist/controller/folioEditor.js +13 -0
  29. package/dist/controller/headerFooterEditorManager.d.ts +1 -1
  30. package/dist/controller/headerFooterEditorManager.js +25 -11
  31. package/dist/controller/hiddenEditorApi.d.ts +13 -2
  32. package/dist/controller/hiddenEditorApi.js +4 -0
  33. package/dist/controller/hiddenEditorManager.d.ts +3 -1
  34. package/dist/controller/hiddenEditorManager.js +166 -13
  35. package/dist/controller/noteEditorManager.d.ts +1 -1
  36. package/dist/controller/noteEditorManager.js +28 -13
  37. package/dist/docx/builtInStyles.js +13 -6
  38. package/dist/docx/containerChildren.gen.d.ts +3 -1
  39. package/dist/docx/containerChildren.gen.js +33 -1
  40. package/dist/docx/fieldParser.js +14 -2
  41. package/dist/docx/hyperlinkParser.d.ts +1 -1
  42. package/dist/docx/hyperlinkParser.js +20 -1
  43. package/dist/docx/inlineWrapperContent.d.ts +4 -4
  44. package/dist/docx/inlineWrapperContent.js +4 -4
  45. package/dist/docx/linkedSdtPreservation.d.ts +10 -0
  46. package/dist/docx/linkedSdtPreservation.js +37 -0
  47. package/dist/docx/paragraphParser.js +88 -67
  48. package/dist/docx/paragraphPropertySource.d.ts +22 -1
  49. package/dist/docx/paragraphPropertySource.js +107 -1
  50. package/dist/docx/preservedRunContent.js +2 -1
  51. package/dist/docx/relsParser.js +26 -2
  52. package/dist/docx/rezip.js +9 -6
  53. package/dist/docx/selectiveSave.js +10 -8
  54. package/dist/docx/serializer/commentSerializer.d.ts +4 -1
  55. package/dist/docx/serializer/commentSerializer.js +7 -3
  56. package/dist/docx/serializer/paragraphSerializer.js +207 -94
  57. package/dist/docx/server/materializeYjsDocx.js +14 -6
  58. package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
  59. package/dist/i18n/messages/ar.gen.js +4 -0
  60. package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
  61. package/dist/i18n/messages/catalogs.gen.js +68 -0
  62. package/dist/i18n/messages/cs.gen.js +4 -0
  63. package/dist/i18n/messages/de.gen.js +4 -0
  64. package/dist/i18n/messages/en.gen.js +4 -0
  65. package/dist/i18n/messages/es.gen.js +4 -0
  66. package/dist/i18n/messages/et.gen.js +4 -0
  67. package/dist/i18n/messages/fr.gen.js +4 -0
  68. package/dist/i18n/messages/he.gen.js +4 -0
  69. package/dist/i18n/messages/hi.gen.js +4 -0
  70. package/dist/i18n/messages/hu.gen.js +4 -0
  71. package/dist/i18n/messages/lt.gen.js +4 -0
  72. package/dist/i18n/messages/lv.gen.js +4 -0
  73. package/dist/i18n/messages/messages.gen.d.ts +4 -0
  74. package/dist/i18n/messages/pl.gen.js +4 -0
  75. package/dist/i18n/messages/pt-BR.gen.js +4 -0
  76. package/dist/i18n/messages/sk.gen.js +4 -0
  77. package/dist/i18n/messages/tr.gen.js +4 -0
  78. package/dist/i18n/messages/zh-CN.gen.js +4 -0
  79. package/dist/index.d.ts +2 -1
  80. package/dist/index.js +2 -1
  81. package/dist/internal/revisionResolutionInline.js +25 -3
  82. package/dist/markdown/renderRuns.js +1 -11
  83. package/dist/panel-layout.d.ts +11 -9
  84. package/dist/panel-layout.js +13 -9
  85. package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
  86. package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
  87. package/dist/prosemirror/attrs/index.js +5 -0
  88. package/dist/prosemirror/commands/comments.js +60 -35
  89. package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
  90. package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
  91. package/dist/prosemirror/commentOps.js +15 -1
  92. package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
  93. package/dist/prosemirror/conversion/toProseDoc.js +119 -18
  94. package/dist/prosemirror/extensions/core/ParagraphExtension.js +22 -5
  95. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
  96. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
  97. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +22 -14
  98. package/dist/prosemirror/extensions/marks/markUtils.js +8 -7
  99. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
  100. package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
  101. package/dist/prosemirror/fieldRepresentation.js +18 -0
  102. package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
  103. package/dist/prosemirror/hyperlinkRemoval.js +28 -9
  104. package/dist/prosemirror/listNumbering.js +11 -5
  105. package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
  106. package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
  107. package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
  108. package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
  109. package/dist/prosemirror/noteReferenceReview.js +14 -1
  110. package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
  111. package/dist/prosemirror/plugins/documentStyles.js +17 -10
  112. package/dist/prosemirror/plugins/suggestionMode.js +47 -49
  113. package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
  114. package/dist/prosemirror/preservedXmlReview.js +35 -0
  115. package/dist/prosemirror/runFormattingFromMarks.js +2 -2
  116. package/dist/prosemirror/runFormattingReconciliation.js +5 -4
  117. package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
  118. package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
  119. package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
  120. package/dist/prosemirror/runStyleFormatting.js +3 -1
  121. package/dist/prosemirror/schema/marks.d.ts +1 -0
  122. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  123. package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
  124. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
  125. package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
  126. package/dist/prosemirror/textInput.d.ts +5 -1
  127. package/dist/prosemirror/textInput.js +3 -3
  128. package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
  129. package/dist/prosemirror/trackedRevisionPath.js +39 -1
  130. package/dist/prosemirror/validation.js +3 -5
  131. package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
  132. package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
  133. package/dist/types/canonicalCapabilities.d.ts +6 -20
  134. package/dist/types/canonicalCapabilities.js +6 -20
  135. package/dist/types/canonicalComments.d.ts +51 -0
  136. package/dist/types/canonicalComments.js +0 -0
  137. package/dist/types/canonicalSections.d.ts +12 -0
  138. package/dist/types/canonicalSections.js +0 -0
  139. package/dist/utils/headingCollector.js +45 -4
  140. package/dist/utils/outlineDepth.d.ts +9 -0
  141. package/dist/utils/outlineDepth.js +9 -0
  142. package/package.json +2 -2
@@ -1,6 +1,7 @@
1
1
  import { paragraphNumberingReference, paragraphNumberingReferenceId } from "../docx/numberingReference.js";
2
2
  import { formattingEquals } from "../docx/runConsolidator.js";
3
3
  import { isStyleSourcedParagraphNumbering } from "../internal/paragraphFormattingSerialization.js";
4
+ import { addTrackedDeletionMark } from "../prosemirror/addTrackedDeletionMark.js";
4
5
  import { deleteTextBoxesAnchoredAt, droppedTextBoxAnchorIds, endOfParagraphWithItsTextBoxes, gatherTextBoxesAfterParagraph, textBoxAnchorIdOf, textBoxAnchorIdsBetween } from "../prosemirror/anchoredTextBoxes.js";
5
6
  import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs, expectTableRowAttrs } from "../prosemirror/attrs/index.js";
6
7
  import { hasSerializableParagraphPropertyChange, paragraphPropertiesSnapshot } from "../prosemirror/commands/propertyChangeScope.js";
@@ -34,7 +35,7 @@ import { removeRowsWithoutCellsAfterBatch } from "../prosemirror/tableGridMutati
34
35
  import { isZeroWidthAnchor } from "../prosemirror/zeroWidthAnchors.js";
35
36
  import { getFolioParaIdFromBlockId } from "../types/block-id.js";
36
37
  import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
37
- import { BatchClaims } from "./batch-claims.js";
38
+ import { BatchClaims, compareBatchTieOrder } from "./batch-claims.js";
38
39
  import { stripBlockIdentityAttrs } from "./block-identity.js";
39
40
  import { describeCharacterSplit } from "./character-boundaries.js";
40
41
  import { buildCleanBlockText, resolveCleanTextRange } from "./clean-text.js";
@@ -2417,7 +2418,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
2417
2418
  if (leftCellShape.tablePosition !== rightCellShape.tablePosition) return rightCellShape.tablePosition - leftCellShape.tablePosition;
2418
2419
  if (leftCellShape.rectangle.bottom !== rightCellShape.rectangle.bottom) return rightCellShape.rectangle.bottom - leftCellShape.rectangle.bottom;
2419
2420
  if (leftCellShape.rectangle.right !== rightCellShape.rectangle.right) return rightCellShape.rectangle.right - leftCellShape.rectangle.right;
2420
- return right.originalIndex - left.originalIndex;
2421
+ return compareBatchTieOrder(left.originalIndex, right.originalIndex);
2421
2422
  }
2422
2423
  const leftColumn = left.tableColumnInsertion ?? left.tableColumnDeletion;
2423
2424
  const rightColumn = right.tableColumnInsertion ?? right.tableColumnDeletion;
@@ -2428,10 +2429,10 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
2428
2429
  if (leftColumn.columnIndex !== rightColumn.columnIndex) return rightColumn.columnIndex - leftColumn.columnIndex;
2429
2430
  const leftIsInsertion = left.tableColumnInsertion !== void 0;
2430
2431
  if (leftIsInsertion !== (right.tableColumnInsertion !== void 0)) return leftIsInsertion ? -1 : 1;
2431
- return right.originalIndex - left.originalIndex;
2432
+ return compareBatchTieOrder(left.originalIndex, right.originalIndex);
2432
2433
  }
2433
2434
  if (left.from !== right.from) return right.from - left.from;
2434
- return right.originalIndex - left.originalIndex;
2435
+ return compareBatchTieOrder(left.originalIndex, right.originalIndex);
2435
2436
  });
2436
2437
  for (let executionIndex = 0; executionIndex < executionOrder.length; executionIndex++) {
2437
2438
  const item = executionOrder[executionIndex];
@@ -3186,9 +3187,21 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
3186
3187
  ...isPairedMove(item.operation.moveId) && { moveKind: "moveFrom" },
3187
3188
  ...trackedRevisionExtras
3188
3189
  });
3189
- tr = tr.addMark(item.from, item.to, deletionMark);
3190
+ addTrackedDeletionMark({
3191
+ insertionPolicy: "preserve-pending",
3192
+ tr,
3193
+ from: item.from,
3194
+ to: item.to,
3195
+ mark: deletionMark
3196
+ });
3190
3197
  const atomRanges = undeletedContentAtomRanges(tr, item.blockFrom);
3191
- for (const { from, to } of atomRanges) tr = tr.addMark(from, to, deletionMark);
3198
+ for (const { from, to } of atomRanges) addTrackedDeletionMark({
3199
+ insertionPolicy: "preserve-pending",
3200
+ tr,
3201
+ from,
3202
+ to,
3203
+ mark: deletionMark
3204
+ });
3192
3205
  for (const anchorId of textBoxAnchorIdsBetween(tr.doc, tr.mapping.map(item.blockFrom), tr.mapping.map(item.blockTo))) deletedTextBoxAnchorIds.add(anchorId);
3193
3206
  encloseWholeControls({
3194
3207
  tr,
@@ -3481,12 +3494,18 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
3481
3494
  appliedRevisionIds = [revisionIdMark];
3482
3495
  if (item.to > item.from && deletionType) {
3483
3496
  const revisionIdSeparator = operationRevisionSeed++;
3484
- tr = tr.addMark(item.from, item.to, deletionType.create({
3485
- revisionId: revisionIdSeparator,
3486
- author,
3487
- date,
3488
- ...trackedRevisionExtras
3489
- }));
3497
+ addTrackedDeletionMark({
3498
+ insertionPolicy: "preserve-pending",
3499
+ tr,
3500
+ from: item.from,
3501
+ to: item.to,
3502
+ mark: deletionType.create({
3503
+ revisionId: revisionIdSeparator,
3504
+ author,
3505
+ date,
3506
+ ...trackedRevisionExtras
3507
+ })
3508
+ });
3490
3509
  appliedRevisionIds.push(revisionIdSeparator);
3491
3510
  }
3492
3511
  tr = tr.split(item.from);
@@ -3982,7 +4001,13 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
3982
4001
  if (commentMark) nextTr = nextTr.addMark(inserted.start, inserted.end, commentMark);
3983
4002
  }
3984
4003
  if (item.to > item.from && deletionType) {
3985
- nextTr = nextTr.addMark(item.from, item.to, deletionType.create(delAttrs));
4004
+ addTrackedDeletionMark({
4005
+ insertionPolicy: "preserve-pending",
4006
+ tr: nextTr,
4007
+ from: item.from,
4008
+ to: item.to,
4009
+ mark: deletionType.create(delAttrs)
4010
+ });
3986
4011
  if (commentMark && replacement.length === 0) nextTr = nextTr.addMark(item.from, item.to, commentMark);
3987
4012
  }
3988
4013
  return nextTr;
@@ -4474,7 +4499,13 @@ const applyMinimalTrackedReplacement = ({ tr, item, noteReferences, replacement,
4474
4499
  removedFrom = first.from;
4475
4500
  marks = addCarriedMarks(inheritedReplacementMarks(doc, styleSource.from, styleSource.to), surveyReplacedAnnotations(doc, first.from, last.to).carried);
4476
4501
  for (const piece of change.pieces) {
4477
- nextTr = nextTr.addMark(piece.from, piece.to, deletion);
4502
+ addTrackedDeletionMark({
4503
+ insertionPolicy: "preserve-pending",
4504
+ tr: nextTr,
4505
+ from: piece.from,
4506
+ to: piece.to,
4507
+ mark: deletion
4508
+ });
4478
4509
  if (commentMark) nextTr = nextTr.addMark(piece.from, piece.to, commentMark);
4479
4510
  }
4480
4511
  }
@@ -21,6 +21,8 @@
21
21
  * Positions are the ones the batch resolved against. A block is keyed by the
22
22
  * position before its node.
23
23
  */
24
+ /** Equal snapshot coordinates execute in reverse input order. */
25
+ declare const compareBatchTieOrder: (leftIndex: number, rightIndex: number) => number;
24
26
  type PositionRange = {
25
27
  from: number;
26
28
  to: number;
@@ -142,4 +144,4 @@ declare class BatchClaims {
142
144
  add(operationId: string, claim: BatchClaim): void;
143
145
  }
144
146
  //#endregion
145
- export { BatchClaim, BatchClaims };
147
+ export { BatchClaim, BatchClaims, compareBatchTieOrder };
@@ -1,4 +1,28 @@
1
1
  //#region src/ai-edits/batch-claims.ts
2
+ /**
3
+ * Which operations of one batch may not share their targets.
4
+ *
5
+ * A batch resolves every operation against the document as it was read and
6
+ * applies them from the end of the document backwards, so an operation never
7
+ * moves the positions an earlier-placed one resolved to. That holds only while
8
+ * the operations' targets are disjoint. Two operations on the same stretch of
9
+ * text, or an operation inside a paragraph another one deletes, rewrites,
10
+ * splits or merges away, are applied against coordinates the first one has
11
+ * already invalidated — and the second then edits whatever now sits there,
12
+ * which can be a paragraph neither operation named.
13
+ *
14
+ * Each operation therefore states what it claims before anything is applied,
15
+ * and the later of two operations whose claims conflict is refused
16
+ * (`overlappingOperation`). The caller re-reads the document after the batch
17
+ * and sends the refused operation again against the result. What remains
18
+ * applies exactly as the same operations applied one at a time, from the end
19
+ * of the document backwards, would.
20
+ *
21
+ * Positions are the ones the batch resolved against. A block is keyed by the
22
+ * position before its node.
23
+ */
24
+ /** Equal snapshot coordinates execute in reverse input order. */
25
+ const compareBatchTieOrder = (leftIndex, rightIndex) => rightIndex - leftIndex;
2
26
  const blockRolesOf = (claim) => {
3
27
  switch (claim.type) {
4
28
  case "text":
@@ -272,4 +296,4 @@ var BatchClaims = class {
272
296
  }
273
297
  };
274
298
  //#endregion
275
- export { BatchClaims };
299
+ export { BatchClaims, compareBatchTieOrder };
@@ -1,3 +1,4 @@
1
+ import { addTrackedDeletionMark } from "../prosemirror/addTrackedDeletionMark.js";
1
2
  import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
2
3
  import { markRowsEmptiedInBatch, reconcileTableGridAfterColumnInsertion, reconcileTableGridAfterColumnRemoval, removeRowsWithoutCells, removeTableRow } from "../prosemirror/tableGridMutation.js";
3
4
  import { stripBlockIdentityAttrs } from "./block-identity.js";
@@ -130,7 +131,7 @@ const markTableRowContent = ({ tr, rowPosition, kind, revision }) => {
130
131
  if (!node.isInline) return true;
131
132
  if (node.marks.some(isTrackedChangeMark)) {
132
133
  wholeRowMarkable = false;
133
- if (kind === "insertion" || node.marks.some(({ type }) => type.name === "deletion")) return false;
134
+ if (kind === "insertion") return false;
134
135
  }
135
136
  const from = contentFrom + offset;
136
137
  markable.push({
@@ -140,10 +141,23 @@ const markTableRowContent = ({ tr, rowPosition, kind, revision }) => {
140
141
  return false;
141
142
  });
142
143
  if (wholeRowMarkable) {
143
- tr.addMark(contentFrom, rowPosition + row.nodeSize - 1, mark);
144
+ if (kind === "deletion") addTrackedDeletionMark({
145
+ insertionPolicy: "preserve-pending",
146
+ tr,
147
+ from: contentFrom,
148
+ to: rowPosition + row.nodeSize - 1,
149
+ mark
150
+ });
151
+ else tr.addMark(contentFrom, rowPosition + row.nodeSize - 1, mark);
144
152
  return;
145
153
  }
146
- for (const range of markable) tr.addMark(range.from, range.to, mark);
154
+ for (const range of markable) if (kind === "deletion") addTrackedDeletionMark({
155
+ insertionPolicy: "preserve-pending",
156
+ tr,
157
+ ...range,
158
+ mark
159
+ });
160
+ else tr.addMark(range.from, range.to, mark);
147
161
  };
148
162
  const isTrackedChangeMark = (mark) => mark.type.name === "insertion" || mark.type.name === "deletion";
149
163
  const isRowSpanningCell = (node) => {
@@ -1,6 +1,19 @@
1
+ import { addTrackedDeletionMark } from "../prosemirror/addTrackedDeletionMark.js";
1
2
  import { mintRevisionId, seedRevisionIdsFromDoc } from "../prosemirror/plugins/revisionIds.js";
2
3
  import { resolveSuggestionAnchor } from "./conflict.js";
3
4
  //#region src/ai-suggestions/apply.ts
5
+ /**
6
+ * Apply AI suggestions to the document.
7
+ *
8
+ * Two modes:
9
+ * - "direct": replace text in place.
10
+ * - "tracked-changes": insertion + deletion marks, attributed to the
11
+ * configured author. Reuses the editor's existing tracked-change
12
+ * schema and resolution commands.
13
+ *
14
+ * Apply is always preceded by anchor resolution; stale suggestions are
15
+ * skipped and reported back to the caller.
16
+ */
4
17
  function applySuggestions(options) {
5
18
  const { view, suggestions, mode, author } = options;
6
19
  const applied = [];
@@ -51,7 +64,13 @@ function applySuggestions(options) {
51
64
  tr.insertText(suggestion.suggestedText, to, to);
52
65
  tr.addMark(to, to + suggestion.suggestedText.length, insertionType.create(attrs));
53
66
  }
54
- if (to > from) tr.addMark(from, to, deletionType.create(attrs));
67
+ if (to > from) addTrackedDeletionMark({
68
+ insertionPolicy: "preserve-pending",
69
+ tr,
70
+ from,
71
+ to,
72
+ mark: deletionType.create(attrs)
73
+ });
55
74
  applied.push(suggestion.id);
56
75
  }
57
76
  }
@@ -2,7 +2,9 @@ import { buildCleanBlockText } from "../ai-edits/clean-text.js";
2
2
  import { noteReferenceOf } from "../ai-edits/note-references.js";
3
3
  import { collectNoteReferenceLabels, sourceDocumentOf } from "../ai-edits/snapshot.js";
4
4
  import { drawingXmlWithoutIdentity } from "../docx/drawingIdNormalization.js";
5
+ import { addTrackedDeletionMark } from "../prosemirror/addTrackedDeletionMark.js";
5
6
  import { resolveAllChangesInHeadlessStateWithMapping } from "../prosemirror/commands/comments.js";
7
+ import { mintNoteReferenceOccurrenceId } from "../prosemirror/noteReferenceOccurrences.js";
6
8
  import { runFormattingInlineAtomResultText } from "../prosemirror/runFormattingInlineCarriers.js";
7
9
  import { canonicalJson } from "../utils/canonicalJson.js";
8
10
  import { prepareTargetInlineAtom } from "./inline-atom-resources.js";
@@ -618,11 +620,17 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
618
620
  const to = transaction.mapping.map(action.to, -1);
619
621
  if (from >= to) return { status: "unalignable" };
620
622
  if (action.textDisposition === "inserted") transaction.delete(from, to);
621
- else transaction.addMark(from, to, deletionType.create({
622
- revisionId: nextRevisionId++,
623
- author,
624
- date: revisionStamp.date
625
- }));
623
+ else addTrackedDeletionMark({
624
+ insertionPolicy: "preserve-pending",
625
+ tr: transaction,
626
+ from,
627
+ to,
628
+ mark: deletionType.create({
629
+ revisionId: nextRevisionId++,
630
+ author,
631
+ date: revisionStamp.date
632
+ })
633
+ });
626
634
  const at = transaction.mapping.map(action.from, -1);
627
635
  transaction.insert(at, action.node.mark(insertionType.create({
628
636
  revisionId: nextRevisionId++,
@@ -638,7 +646,11 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
638
646
  const written = transaction.doc.nodeAt(from);
639
647
  const noteMark = action.node.marks.find(({ type }) => type.name === "footnoteRef");
640
648
  if (from >= to || !written?.isText || !noteMark) return { status: "unalignable" };
641
- transaction.replaceWith(from, to, state.schema.text(action.node.text ?? "", noteMark.addToSet(written.marks)));
649
+ const restoredNoteMark = noteMark.type.create({
650
+ ...noteMark.attrs,
651
+ occurrenceId: mintNoteReferenceOccurrenceId()
652
+ });
653
+ transaction.replaceWith(from, to, state.schema.text(action.node.text ?? "", restoredNoteMark.addToSet(written.marks)));
642
654
  if (action.targetBlockId) changedTargetBlockIds.add(action.targetBlockId);
643
655
  continue;
644
656
  }
@@ -648,11 +660,17 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
648
660
  const node = transaction.doc.nodeAt(from);
649
661
  if (!node || node.nodeSize !== to - from || node.type.name !== "field") return { status: "unalignable" };
650
662
  if (action.atomDisposition === "inserted") transaction.delete(from, to);
651
- else transaction.addMark(from, to, deletionType.create({
652
- revisionId: nextRevisionId++,
653
- author,
654
- date: revisionStamp.date
655
- }));
663
+ else addTrackedDeletionMark({
664
+ insertionPolicy: "preserve-pending",
665
+ tr: transaction,
666
+ from,
667
+ to,
668
+ mark: deletionType.create({
669
+ revisionId: nextRevisionId++,
670
+ author,
671
+ date: revisionStamp.date
672
+ })
673
+ });
656
674
  const at = transaction.mapping.map(action.from, -1);
657
675
  transaction.insert(at, state.schema.text(action.text, insertionType.create({
658
676
  revisionId: nextRevisionId++,
@@ -675,11 +693,17 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
675
693
  continue;
676
694
  }
677
695
  if (node.marks.some(({ type }) => type === deletionType)) return { status: "unalignable" };
678
- transaction.addMark(from, to, deletionType.create({
679
- revisionId: nextRevisionId++,
680
- author,
681
- date: revisionStamp.date
682
- }));
696
+ addTrackedDeletionMark({
697
+ insertionPolicy: "preserve-pending",
698
+ tr: transaction,
699
+ from,
700
+ to,
701
+ mark: deletionType.create({
702
+ revisionId: nextRevisionId++,
703
+ author,
704
+ date: revisionStamp.date
705
+ })
706
+ });
683
707
  if (action.targetBlockId) changedTargetBlockIds.add(action.targetBlockId);
684
708
  }
685
709
  return {
@@ -1,4 +1,5 @@
1
1
  import { sourceDocumentOf, styleResolverOf } from "../ai-edits/snapshot.js";
2
+ import { addTrackedDeletionMark } from "../prosemirror/addTrackedDeletionMark.js";
2
3
  import { expectHyperlinkMarkAttrs, expectRunPropertyChangeMarkAttrs, expectTableCellAttrs } from "../prosemirror/attrs/index.js";
3
4
  import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyleState.js";
4
5
  import { applyMarksToRunFormattingRepresentation, expandRunFormattingCarrier, runFormattingCarrierReviewText } from "../prosemirror/runFormattingInlineCarriers.js";
@@ -252,11 +253,17 @@ const matchInlineProvenance = ({ state, targetSnapshot, revisionStamp, originalR
252
253
  date: revisionStamp.date
253
254
  }).addToSet(marks);
254
255
  const text = state.doc.textBetween(change.live.from, change.live.to);
255
- transaction.addMark(change.live.from, change.live.to, deletionType.create({
256
- revisionId: nextRevisionId++,
257
- author,
258
- date: revisionStamp.date
259
- }));
256
+ addTrackedDeletionMark({
257
+ insertionPolicy: "preserve-pending",
258
+ tr: transaction,
259
+ from: change.live.from,
260
+ to: change.live.to,
261
+ mark: deletionType.create({
262
+ revisionId: nextRevisionId++,
263
+ author,
264
+ date: revisionStamp.date
265
+ })
266
+ });
260
267
  transaction.insert(change.live.to, state.schema.text(text, marks));
261
268
  changedTargetBlockIds.add(change.targetBlockId);
262
269
  continue;
@@ -17,6 +17,7 @@ import { MAX_COMPARE_OPERATIONS, compareDocx } from "../compare/compare.js";
17
17
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocumentPreset, DocumentStyleSet } from "../style-sets/types.js";
18
18
  import { CreateEmptyDocumentOptions, createEmptyDocument } from "../utils/createDocument.js";
19
19
  import { mergeDocumentContent } from "../utils/mergeDocumentContent.js";
20
+ import { DEFAULT_OUTLINE_DEPTH, OutlineDepth, filterHeadingsByDepth } from "../utils/outlineDepth.js";
20
21
  import { DocumentStyleCatalog, DocumentStyleCatalogEntry, ExtractDocumentStyleSetOptions, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, inspectDocumentStyles, inspectDocumentStylesFromDocx } from "../style-sets/extract.js";
21
22
  import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaStyleSet } from "../style-sets/stellaStyle.js";
22
23
  import { DocumentPropertiesOptions, createDocx } from "../docx/rezip.js";
@@ -41,4 +42,4 @@ import { FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioYjsAttrSchemaVersion, FolioYjsAttrS
41
42
  import { DOCX_CONFORMANCE_CLASSES } from "../index.js";
42
43
  type Document = document_d_exports.Document;
43
44
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
44
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareCompatibility, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFolioRequirement, type CompareResult, type CompareRevisionFormat, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentPropertiesOptions, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type DocxDrawingClassification, type DrawingSafetyClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineComparisonResult, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, FolioContentInlinePresentationProjectionError, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewHiddenRange, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
45
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareCompatibility, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFolioRequirement, type CompareResult, type CompareRevisionFormat, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DEFAULT_OUTLINE_DEPTH, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentPropertiesOptions, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type DocxDrawingClassification, type DrawingSafetyClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineComparisonResult, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, FolioContentInlinePresentationProjectionError, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type OutlineDepth, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewHiddenRange, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, filterHeadingsByDepth, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -34,4 +34,5 @@ import { currentFolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioB
34
34
  import { createEmptyDocument } from "../utils/createDocument.js";
35
35
  import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "../utils/fontResolver.js";
36
36
  import { mergeDocumentContent } from "../utils/mergeDocumentContent.js";
37
- export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, FolioYjsAttrSchemaVersionError, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
37
+ import { DEFAULT_OUTLINE_DEPTH, filterHeadingsByDepth } from "../utils/outlineDepth.js";
38
+ export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DEFAULT_OUTLINE_DEPTH, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, FolioYjsAttrSchemaVersionError, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, filterHeadingsByDepth, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -1,4 +1,5 @@
1
1
  import { createNumberingIdAllocator } from "../docx/numberingIds.js";
2
+ import { copyParagraphIndentationProjection } from "../docx/paragraphPropertySource.js";
2
3
  import { visitInlineContentSlots, visitParagraphRuns } from "../docx/paragraphTraversal.js";
3
4
  import { captureVerbatimXml } from "../docx/verbatimCapture.js";
4
5
  import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, findAttributeByNamespaceUri, getLocalName, parseXml } from "../docx/xmlParser.js";
@@ -203,7 +204,12 @@ const prepareCanonicalPaste = ({ session, state, slice, plain = false, moveTarge
203
204
  const paragraphs = [];
204
205
  for (const block of converted.value) {
205
206
  if (block.type !== "paragraph") return refuse("The clipboard contains an unsupported block.");
206
- paragraphs.push(structuredClone(block));
207
+ const paragraph = structuredClone(block);
208
+ copyParagraphIndentationProjection({
209
+ target: paragraph,
210
+ source: block
211
+ });
212
+ paragraphs.push(paragraph);
207
213
  }
208
214
  if (moveTarget === void 0 && (plain || sourceDocument === void 0)) flattenClipboardStyleReferences(paragraphs);
209
215
  const from = session.projection.addressAt(pasteTarget ?? moveSource?.from ?? state.selection.from);
@@ -0,0 +1,41 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
3
+ import { Comment } from "../types/content.js";
4
+ import { CreateCommentOp, DocumentOp } from "@stll/docx-core/ops";
5
+ //#region src/controller/canonicalComments.d.ts
6
+ declare const CanonicalCommentError_base: import("better-result").TaggedErrorClass<"CanonicalCommentError">;
7
+ declare class CanonicalCommentError extends CanonicalCommentError_base<{
8
+ message: string;
9
+ gap: typeof CANONICAL_GAP.comments;
10
+ }> {}
11
+ type CanonicalCommentCommand = {
12
+ type: "create";
13
+ comment: CreateCommentOp["comment"];
14
+ anchor: CreateCommentOp["anchor"];
15
+ } | {
16
+ type: "update";
17
+ id: number;
18
+ content: Comment["content"];
19
+ } | {
20
+ type: "resolve";
21
+ id: number;
22
+ status: "open" | "resolved";
23
+ } | {
24
+ type: "delete";
25
+ id: number;
26
+ } | {
27
+ type: "replace";
28
+ comments: readonly Comment[];
29
+ };
30
+ type CompileCanonicalCommentsOptions = {
31
+ document: document_d_exports.Document;
32
+ command: CanonicalCommentCommand;
33
+ };
34
+ declare const compileCanonicalComments: ({ document, command }: CompileCanonicalCommentsOptions) => import("better-result").Err<never, CanonicalCommentError> | import("better-result").Ok<{
35
+ ops: DocumentOp[];
36
+ document: document_d_exports.Document;
37
+ commentId: number | undefined;
38
+ }, never>;
39
+ declare const canonicalCommentBody: (document: document_d_exports.Document, text: string) => Comment["content"];
40
+ //#endregion
41
+ export { CanonicalCommentCommand, CanonicalCommentError, canonicalCommentBody, compileCanonicalComments };
@@ -0,0 +1,152 @@
1
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
2
+ import { canonicalJson } from "../utils/canonicalJson.js";
3
+ import { Result, TaggedError } from "better-result";
4
+ import { DOCUMENT_OP_TYPES, allocateCommentAnchorIds, applyDocumentOps, packageParagraphIds } from "@stll/docx-core/ops";
5
+ //#region src/controller/canonicalComments.ts
6
+ /** The one semantic compiler for canonical sidebar, controlled and public comments. */
7
+ var CanonicalCommentError = class extends TaggedError("CanonicalCommentError") {};
8
+ const refuse = (message) => Result.err(new CanonicalCommentError({
9
+ message,
10
+ gap: CANONICAL_GAP.comments
11
+ }));
12
+ const compileCanonicalComments = ({ document, command }) => {
13
+ const current = document.package.document.comments ?? [];
14
+ const byId = new Map(current.map((comment) => [comment.id, comment]));
15
+ const ops = [];
16
+ let commentId;
17
+ switch (command.type) {
18
+ case "create":
19
+ ops.push({
20
+ type: DOCUMENT_OP_TYPES.CREATE_COMMENT,
21
+ comment: command.comment,
22
+ anchor: command.anchor
23
+ });
24
+ commentId = command.comment.id;
25
+ break;
26
+ case "update":
27
+ ops.push({
28
+ type: DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT,
29
+ id: command.id,
30
+ content: command.content
31
+ });
32
+ break;
33
+ case "resolve":
34
+ ops.push({
35
+ type: DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION,
36
+ id: command.id,
37
+ status: command.status
38
+ });
39
+ break;
40
+ case "delete": {
41
+ const comment = byId.get(command.id);
42
+ if (!comment) return refuse("The comment no longer exists.");
43
+ ops.push({
44
+ type: DOCUMENT_OP_TYPES.DELETE_COMMENT,
45
+ id: command.id,
46
+ scope: comment.parentId !== void 0 && byId.has(comment.parentId) ? "reply" : "thread"
47
+ });
48
+ break;
49
+ }
50
+ case "replace": {
51
+ const nextById = new Map(command.comments.map((comment) => [comment.id, comment]));
52
+ if (nextById.size !== command.comments.length) return refuse("Comment ids must be unique.");
53
+ for (const comment of current) {
54
+ if (nextById.has(comment.id) && comment.parentId !== void 0 && byId.has(comment.parentId) && !nextById.has(comment.parentId)) return refuse("A retained reply requires its parent thread.");
55
+ if (nextById.has(comment.id)) continue;
56
+ if (comment.parentId !== void 0 && !nextById.has(comment.parentId) && byId.has(comment.parentId)) continue;
57
+ ops.push({
58
+ type: DOCUMENT_OP_TYPES.DELETE_COMMENT,
59
+ id: comment.id,
60
+ scope: comment.parentId !== void 0 && byId.has(comment.parentId) ? "reply" : "thread"
61
+ });
62
+ }
63
+ const pending = command.comments.filter((comment) => !byId.has(comment.id));
64
+ const available = new Set(current.filter((comment) => nextById.has(comment.id)).map((comment) => comment.id));
65
+ while (pending.length > 0) {
66
+ const index = pending.findIndex((comment) => comment.parentId !== void 0 && available.has(comment.parentId));
67
+ if (index < 0) return refuse("New controlled comments require an existing anchored parent without cycles.");
68
+ const next = pending.splice(index, 1).at(0);
69
+ if (next === void 0 || next.parentId === void 0) return refuse("The controlled reply is missing its parent.");
70
+ const { parentId, ...comment } = next;
71
+ ops.push({
72
+ type: DOCUMENT_OP_TYPES.CREATE_COMMENT,
73
+ comment,
74
+ anchor: {
75
+ kind: "reply",
76
+ parentId
77
+ }
78
+ });
79
+ available.add(next.id);
80
+ }
81
+ for (const next of command.comments) {
82
+ const previous = byId.get(next.id);
83
+ if (!previous) continue;
84
+ const { content: priorContent, done: priorDone, author: priorAuthor, initials: priorInitials, date: priorDate, ...priorMetadata } = previous;
85
+ const { content: nextContent, done: nextDone, author: nextAuthor, initials: nextInitials, date: nextDate, ...nextMetadata } = next;
86
+ if (canonicalJson(priorMetadata) !== canonicalJson(nextMetadata)) return refuse("Controlled comment identity and opaque metadata cannot be replaced.");
87
+ const patch = {
88
+ ...priorAuthor === nextAuthor ? {} : { author: nextAuthor },
89
+ ...priorInitials === nextInitials ? {} : { initials: Object.hasOwn(next, "initials") ? nextInitials : null },
90
+ ...priorDate === nextDate ? {} : { date: Object.hasOwn(next, "date") ? nextDate : null }
91
+ };
92
+ if (canonicalJson(priorContent) !== canonicalJson(nextContent) || Object.keys(patch).length > 0) ops.push({
93
+ type: DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT,
94
+ id: next.id,
95
+ content: nextContent,
96
+ patch
97
+ });
98
+ if (priorDone !== nextDone) {
99
+ if (nextDone === void 0) return refuse("Resolution changes must name an open or resolved state.");
100
+ ops.push({
101
+ type: DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION,
102
+ id: next.id,
103
+ status: nextDone ? "resolved" : "open"
104
+ });
105
+ }
106
+ }
107
+ break;
108
+ }
109
+ default: return command;
110
+ }
111
+ const compiled = [];
112
+ let draft = document;
113
+ for (const op of ops) {
114
+ const materialized = (() => {
115
+ if (op.type !== DOCUMENT_OP_TYPES.CREATE_COMMENT) return Result.ok(op);
116
+ const allocation = allocateCommentAnchorIds(draft, op);
117
+ if (allocation.isErr()) return Result.err(allocation.error);
118
+ return Result.ok({
119
+ ...op,
120
+ newIds: allocation.value
121
+ });
122
+ })();
123
+ if (materialized.isErr()) return refuse(materialized.error.message);
124
+ const applied = applyDocumentOps(draft, [materialized.value]);
125
+ if (applied.isErr()) return refuse(applied.error.message);
126
+ compiled.push(materialized.value);
127
+ draft = applied.value.document;
128
+ }
129
+ return Result.ok({
130
+ ops: compiled,
131
+ document: draft,
132
+ commentId
133
+ });
134
+ };
135
+ const canonicalCommentBody = (document, text) => {
136
+ const occupied = new Set(packageParagraphIds(document.package).map((id) => id.toUpperCase()));
137
+ let id = 1;
138
+ while (occupied.has(id.toString(16).padStart(8, "0").toUpperCase())) id += 1;
139
+ return [{
140
+ type: "paragraph",
141
+ paraId: id.toString(16).padStart(8, "0").toUpperCase(),
142
+ content: [{
143
+ type: "run",
144
+ content: [{
145
+ type: "text",
146
+ text
147
+ }]
148
+ }]
149
+ }];
150
+ };
151
+ //#endregion
152
+ export { CanonicalCommentError, canonicalCommentBody, compileCanonicalComments };