@stll/folio-core 0.53.0 → 0.54.1

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 (155) hide show
  1. package/dist/ai-edits/apply.js +415 -166
  2. package/dist/ai-edits/batch-claims.d.ts +21 -6
  3. package/dist/ai-edits/batch-claims.js +47 -16
  4. package/dist/ai-edits/headless.d.ts +20 -0
  5. package/dist/ai-edits/headless.js +140 -11
  6. package/dist/ai-edits/pending-suggestions.js +1 -1
  7. package/dist/ai-edits/read.d.ts +5 -3
  8. package/dist/ai-edits/read.js +97 -47
  9. package/dist/ai-edits/revisionStretches.d.ts +17 -0
  10. package/dist/ai-edits/revisionStretches.js +80 -0
  11. package/dist/ai-edits/snapshot.js +3 -2
  12. package/dist/ai-edits/table-cell-mutations.d.ts +3 -1
  13. package/dist/ai-edits/table-cell-mutations.js +2 -1
  14. package/dist/ai-edits/table-row-column-mutations.d.ts +6 -1
  15. package/dist/ai-edits/table-row-column-mutations.js +27 -10
  16. package/dist/compare/content-alignment.d.ts +5 -1
  17. package/dist/compare/content-alignment.js +39 -32
  18. package/dist/compare/inline-provenance.js +1 -1
  19. package/dist/controller/layoutPipeline.js +3 -2
  20. package/dist/docx/archiveInflation.d.ts +127 -0
  21. package/dist/docx/archiveInflation.js +181 -0
  22. package/dist/docx/metadataPrivacy.js +24 -1
  23. package/dist/docx/noteReferenceMark.d.ts +15 -0
  24. package/dist/docx/noteReferenceMark.js +48 -0
  25. package/dist/docx/paragraphParser.js +5 -3
  26. package/dist/docx/paragraphPropertySource.d.ts +1 -1
  27. package/dist/docx/paragraphPropertySource.js +4 -2
  28. package/dist/docx/parser.js +2 -2
  29. package/dist/docx/rezip.js +4 -2
  30. package/dist/docx/selectiveXmlPatch.d.ts +3 -1
  31. package/dist/docx/selectiveXmlPatch.js +81 -3
  32. package/dist/docx/server/boundedArchive.d.ts +8 -13
  33. package/dist/docx/server/boundedArchive.js +70 -59
  34. package/dist/docx/server/extractDocxText.d.ts +8 -2
  35. package/dist/docx/server/extractDocxText.js +2 -2
  36. package/dist/docx/server/validateDocxConformance.d.ts +1 -1
  37. package/dist/docx/server/validateDocxConformance.js +1 -0
  38. package/dist/docx/unzip.d.ts +19 -2
  39. package/dist/docx/unzip.js +135 -24
  40. package/dist/docx/verticalMergeProjection.d.ts +17 -0
  41. package/dist/docx/verticalMergeProjection.js +120 -0
  42. package/dist/fonts/embeddedFonts.js +1 -1
  43. package/dist/i18n/messages/catalogs.gen.d.ts +34 -34
  44. package/dist/i18n/messages/catalogs.gen.js +34 -34
  45. package/dist/i18n/messages/messages.gen.d.ts +2 -2
  46. package/dist/internal/wholeStoryRevisionResolution.js +95 -25
  47. package/dist/layout-engine/measure/measureParagraph.js +2 -2
  48. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
  49. package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
  50. package/dist/markdown/escape.d.ts +2 -2
  51. package/dist/markdown/escape.js +4 -5
  52. package/dist/markdown/renderBlock.js +91 -25
  53. package/dist/markdown/renderRuns.d.ts +10 -1
  54. package/dist/markdown/renderRuns.js +320 -59
  55. package/dist/prosemirror/clearRunColor.d.ts +6 -0
  56. package/dist/prosemirror/clearRunColor.js +46 -0
  57. package/dist/prosemirror/commands/comments.js +62 -22
  58. package/dist/prosemirror/commands/formatPainter.js +1 -1
  59. package/dist/prosemirror/commands/hyperlink.js +2 -1
  60. package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
  61. package/dist/prosemirror/commands/propertyChangeScope.js +28 -4
  62. package/dist/prosemirror/commands/resolveAllTableChanges.js +40 -41
  63. package/dist/prosemirror/commands/resolveParagraphProperties.js +33 -8
  64. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +7 -1
  65. package/dist/prosemirror/commands/tableCellMergeResolution.js +39 -20
  66. package/dist/prosemirror/commands/tableMergeFoldDecisions.d.ts +61 -0
  67. package/dist/prosemirror/commands/tableMergeFoldDecisions.js +79 -0
  68. package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
  69. package/dist/prosemirror/containerFinalParagraph.js +7 -6
  70. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  71. package/dist/prosemirror/conversion/fromProseDoc.js +12 -590
  72. package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -13
  73. package/dist/prosemirror/conversion/toProseDoc.js +28 -262
  74. package/dist/prosemirror/documentSchema.d.ts +8 -0
  75. package/dist/prosemirror/documentSchema.js +18 -0
  76. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  77. package/dist/prosemirror/extensions/core/ParagraphExtension.js +104 -63
  78. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -1
  79. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
  80. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
  81. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
  82. package/dist/prosemirror/extensions/features/ListExtension.js +10 -9
  83. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
  84. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +2 -4
  85. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +14 -8
  86. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
  87. package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
  88. package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
  89. package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
  90. package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
  91. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -6
  92. package/dist/prosemirror/extensions/marks/markUtils.js +17 -8
  93. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +3 -1
  94. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +12 -1
  95. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
  96. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
  97. package/dist/prosemirror/extensions/nodes/TableExtension.js +104 -210
  98. package/dist/prosemirror/extensions/types.d.ts +7 -0
  99. package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
  100. package/dist/prosemirror/hyperlinkRemoval.js +129 -0
  101. package/dist/prosemirror/index.d.ts +2 -1
  102. package/dist/prosemirror/index.js +2 -1
  103. package/dist/prosemirror/listNumbering.d.ts +3 -21
  104. package/dist/prosemirror/listNumbering.js +24 -39
  105. package/dist/prosemirror/listRendering.d.ts +29 -0
  106. package/dist/prosemirror/listRendering.js +26 -0
  107. package/dist/prosemirror/markupViewNotes.d.ts +8 -0
  108. package/dist/prosemirror/markupViewNotes.js +49 -0
  109. package/dist/prosemirror/markupViewProjection.d.ts +3 -1
  110. package/dist/prosemirror/markupViewProjection.js +7 -2
  111. package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
  112. package/dist/prosemirror/noteReferenceReview.js +332 -0
  113. package/dist/prosemirror/paragraphIndentation.js +5 -2
  114. package/dist/prosemirror/paragraphMarkJoin.d.ts +6 -4
  115. package/dist/prosemirror/paragraphMarkJoin.js +21 -19
  116. package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
  117. package/dist/prosemirror/paragraphPropertyCarry.js +155 -0
  118. package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
  119. package/dist/prosemirror/plugins/documentStyleState.js +32 -0
  120. package/dist/prosemirror/plugins/documentStyles.d.ts +2 -21
  121. package/dist/prosemirror/plugins/documentStyles.js +66 -29
  122. package/dist/prosemirror/plugins/index.d.ts +2 -1
  123. package/dist/prosemirror/plugins/index.js +2 -1
  124. package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
  125. package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
  126. package/dist/prosemirror/plugins/suggestionMode.d.ts +12 -2
  127. package/dist/prosemirror/plugins/suggestionMode.js +387 -28
  128. package/dist/prosemirror/plugins/templateDirectives.d.ts +6 -0
  129. package/dist/prosemirror/plugins/templateDirectives.js +21 -7
  130. package/dist/prosemirror/plugins/templatePreviewValues.js +14 -21
  131. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +22 -2
  132. package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
  133. package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
  134. package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
  135. package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
  136. package/dist/prosemirror/runFormattingFromMarks.js +567 -0
  137. package/dist/prosemirror/runFormattingReconciliation.js +10 -9
  138. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  139. package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
  140. package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
  141. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
  142. package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
  143. package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
  144. package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
  145. package/dist/prosemirror/tableCellPaste.d.ts +74 -0
  146. package/dist/prosemirror/tableCellPaste.js +525 -0
  147. package/dist/prosemirror/tableGridMutation.d.ts +53 -14
  148. package/dist/prosemirror/tableGridMutation.js +166 -18
  149. package/dist/prosemirror/tableRunIn.d.ts +51 -0
  150. package/dist/prosemirror/tableRunIn.js +165 -0
  151. package/dist/prosemirror/textInput.js +11 -2
  152. package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
  153. package/dist/prosemirror/trackedRevisionPath.js +32 -0
  154. package/dist/server.d.ts +2 -2
  155. package/package.json +6 -6
@@ -7,21 +7,23 @@ import { expectFieldAttrs, expectParagraphAttrs, expectRunPropertyChangeMarkAttr
7
7
  import { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer } from "../containerFinalParagraph.js";
8
8
  import { enclosingRevisionIds, resolutionRemovesControl, withoutResolvedEnclosures } from "../contentControlRevisions.js";
9
9
  import { resolveEmptyFieldResultRuns } from "../emptyFieldResultRuns.js";
10
+ import { JOINED_RUNS_RESTYLED_META } from "../extensions/features/JoinedRunStyleExtension.js";
10
11
  import { markChangedParagraphRanges, markStructuralChange, markTrackedSectionEndpointRemoval } from "../extensions/features/ParagraphChangeTrackerExtension.js";
11
12
  import "../extensions/nodes/SdtExtension.js";
12
13
  import { resolveStateStory } from "../markupViewProjection.js";
13
14
  import { joinAtParagraphMark } from "../paragraphMarkJoin.js";
14
15
  import { getDocumentNumbering } from "../plugins/documentNumbering.js";
15
- import { getDocumentStyleResolver } from "../plugins/documentStyles.js";
16
- import { setParagraphAttrsWithRebasedRunFormatting } from "../rebaseParagraphRunFormatting.js";
16
+ import { getDocumentStyleResolver } from "../plugins/documentStyleState.js";
17
+ import { rebaseParagraphRuns, setParagraphAttrsWithRebasedRunFormatting } from "../rebaseParagraphRunFormatting.js";
17
18
  import { rejoinRunsAt } from "../rejoinRunCarriers.js";
18
19
  import { getFolioNodeRevisionCarriers, nodePropertyRevisionSites } from "../revisionCarriers.js";
19
20
  import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
20
21
  import { reconstructRejectedRunFormattingMarks } from "../runPropertyChangeResolution.js";
21
- import { paragraphRunStyleContextAt } from "../runStyleFormatting.js";
22
+ import { paragraphRunStyleContext, paragraphRunStyleContextAt } from "../runStyleFormatting.js";
22
23
  import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
23
24
  import { isTableCellRetainedInReviewView } from "../tableCellRevisionVisibility.js";
24
- import { reconcileTableGridAfterColumnRemoval, removeRowsWithoutCells, removeTableRow } from "../tableGridMutation.js";
25
+ import { markRowEmptiedInBatch, reconcileTableGridAfterColumnRemoval, removeRowsWithoutCellsAfterBatch, removeTableRow } from "../tableGridMutation.js";
26
+ import { runInCellPending, runParagraphIntoTable } from "../tableRunIn.js";
25
27
  import { holdsNoContent } from "../zeroWidthAnchors.js";
26
28
  import { inlineBookmarksForParagraphJoin } from "./paragraphBookmarkJoin.js";
27
29
  import { hasSerializableParagraphPropertyChange, paragraphPropertiesSnapshot } from "./propertyChangeScope.js";
@@ -106,6 +108,7 @@ function resolveChange(from, to, mode, revisionIds) {
106
108
  const matchesRevision = (mark) => revisionSet === null || typeof mark.attrs["revisionId"] === "number" && revisionSet.has(mark.attrs["revisionId"]);
107
109
  if (dispatch) {
108
110
  const tr = state.tr;
111
+ tr.setMeta(JOINED_RUNS_RESTYLED_META, true);
109
112
  const deleteRanges = [];
110
113
  /** Where resolved inline content began and ended, in `state.doc`. */
111
114
  const resolvedBoundaries = [];
@@ -278,13 +281,18 @@ function resolveChange(from, to, mode, revisionIds) {
278
281
  tableCellStructuralOps.sort((left, right) => right.cellPos - left.cellPos);
279
282
  let resolvedTableCellStructure = false;
280
283
  let failedTableCellMergeResolution = false;
281
- const emptiedTables = [];
284
+ const emptiedTables = { pending: false };
282
285
  for (const op of tableCellStructuralOps) {
283
286
  const mappedPos = tr.mapping.map(op.cellPos);
284
287
  const cell = tr.doc.nodeAt(mappedPos);
285
288
  if (!cell || cell.type.name !== "tableCell" && cell.type.name !== "tableHeader") continue;
286
289
  if (op.type === "merge") {
287
- const resolved = op.source === "collapsed" ? resolveCollapsedTableCellMerge(tr, mappedPos, op.mode, op.revisionSet) : resolveVisibleTableCellMerge(tr, mappedPos, op.mode);
290
+ const resolved = op.source === "collapsed" ? resolveCollapsedTableCellMerge(tr, mappedPos, op.mode, op.revisionSet) : resolveVisibleTableCellMerge({
291
+ tr,
292
+ cellPos: mappedPos,
293
+ mode: op.mode,
294
+ revisionSet: op.revisionSet
295
+ });
288
296
  if (!resolved) {
289
297
  failedTableCellMergeResolution = true;
290
298
  break;
@@ -301,9 +309,15 @@ function resolveChange(from, to, mode, revisionIds) {
301
309
  resolvedTableCellStructure = true;
302
310
  }
303
311
  if (failedTableCellMergeResolution) return false;
304
- for (const { position, mapFrom } of emptiedTables) removeRowsWithoutCells(tr, tr.mapping.slice(mapFrom).map(position));
312
+ removeRowsWithoutCellsAfterBatch(tr, emptiedTables);
305
313
  if (resolvedTableCellStructure) markStructuralChange(tr);
306
314
  pPrMarkOps.sort((a, b) => b.paragraphPos - a.paragraphPos);
315
+ const pendingTables = [];
316
+ state.doc.descendants((node, position) => {
317
+ if (node.type.spec["tableRole"] === "table" && runInCellPending(node)) pendingTables.push(position);
318
+ return !node.isTextblock;
319
+ });
320
+ const joinSeams = [];
307
321
  for (const op of pPrMarkOps) {
308
322
  const mappedPos = tr.mapping.map(op.paragraphPos);
309
323
  const paragraph = tr.doc.nodeAt(mappedPos);
@@ -323,7 +337,18 @@ function resolveChange(from, to, mode, revisionIds) {
323
337
  nextPos += boundary.nodeSize;
324
338
  }
325
339
  const nextNode = nextPos < tr.doc.content.size ? tr.doc.nodeAt(nextPos) : null;
326
- if (!(nextNode?.type.name === paragraph.type.name)) {
340
+ const joinable = nextNode?.type.name === paragraph.type.name;
341
+ const ranIn = nextNode?.type.spec["tableRole"] === "table" && boundaries.length === 0 && !pendingTables.some((position) => tr.mapping.map(position) === nextPos) ? runParagraphIntoTable({
342
+ tr,
343
+ paragraphPos: mappedPos,
344
+ styleResolver
345
+ }) : null;
346
+ if (ranIn) {
347
+ tr.setMeta(JOINED_RUNS_RESTYLED_META, true);
348
+ markParagraphPropertySourceTransfers(tr, [ranIn.transfer]);
349
+ continue;
350
+ }
351
+ if (!joinable) {
327
352
  const resolved = tr.doc.resolve(mappedPos);
328
353
  const endsItsContainer = paragraphEndsItsContainer(resolved, paragraph.type.name);
329
354
  const canGo = op.markWasAdded || !endsItsContainer;
@@ -340,12 +365,6 @@ function resolveChange(from, to, mode, revisionIds) {
340
365
  }
341
366
  try {
342
367
  if (boundaries.length > 0) {
343
- const emptyFirstParagraph = holdsNoContent(paragraph);
344
- const joinedAttrs = {
345
- ...(emptyFirstParagraph ? nextNode : paragraph).attrs,
346
- pPrMark: nextNode.attrs["pPrMark"],
347
- _sectionProperties: nextNode.attrs["_sectionProperties"]
348
- };
349
368
  const leftToken = getProseParagraphPropertySourceToken(paragraph);
350
369
  const rightToken = getProseParagraphPropertySourceToken(nextNode);
351
370
  const inlineBookmarks = inlineBookmarksForParagraphJoin({
@@ -355,16 +374,31 @@ function resolveChange(from, to, mode, revisionIds) {
355
374
  });
356
375
  if (!inlineBookmarks) continue;
357
376
  tr.replaceWith(joinPos - 1, nextPos + 1, inlineBookmarks);
358
- tr.setNodeMarkup(mappedPos, void 0, joinedAttrs);
359
- if (emptyFirstParagraph) markParagraphPropertySourceTransfers(tr, [{
377
+ tr.setNodeMarkup(mappedPos, void 0, nextNode.attrs);
378
+ markParagraphPropertySourceTransfers(tr, [{
360
379
  displacedToken: typeof leftToken === "string" ? leftToken : null,
361
380
  selectedToken: typeof rightToken === "string" ? rightToken : null
362
381
  }]);
382
+ if (styleResolver && paragraph.content.size > 0) rebaseParagraphRuns({
383
+ previousContext: paragraphRunStyleContext(paragraph, styleResolver),
384
+ paragraphPosition: mappedPos,
385
+ range: {
386
+ from: 0,
387
+ to: paragraph.content.size
388
+ },
389
+ styleResolver,
390
+ tr
391
+ });
363
392
  } else joinAtParagraphMark({
364
393
  tr,
365
394
  paragraphPos: mappedPos,
366
395
  paragraph,
367
- next: nextNode
396
+ next: nextNode,
397
+ styleResolver
398
+ });
399
+ joinSeams.push({
400
+ position: mappedPos + 1 + paragraph.content.size,
401
+ step: tr.steps.length
368
402
  });
369
403
  if (ownsSectionEndpoint(paragraph)) {
370
404
  removedSectionEndpointCount++;
@@ -378,9 +412,10 @@ function resolveChange(from, to, mode, revisionIds) {
378
412
  removedReferences: removedSectionReferences
379
413
  });
380
414
  if (!terminalTableDeletionIsPending(tr)) resolveTerminalTableReviewCarrier(tr, mode);
415
+ const seams = new Set(joinSeams.map(({ position, step }) => tr.mapping.slice(step).map(position)));
381
416
  rejoinRunsAt({
382
417
  tr,
383
- boundaries: resolvedBoundaries.map((position) => tr.mapping.map(position)),
418
+ boundaries: resolvedBoundaries.map((position) => tr.mapping.map(position)).filter((position) => !seams.has(position)),
384
419
  styleResolver
385
420
  });
386
421
  if (tr.steps.length > 0) dispatch(tr);
@@ -559,6 +594,7 @@ function isTableCellRevisionAttr(value) {
559
594
  const info = value.info;
560
595
  return typeof info === "object" && info !== null && "revisionId" in info && typeof info.revisionId === "number";
561
596
  }
597
+ /** Record rows for a final sweep after every cell revision has resolved. */
562
598
  function deleteTableCellAt(tr, cellPos, emptied) {
563
599
  const cell = tr.doc.nodeAt(cellPos);
564
600
  if (!cell || cell.type.name !== "tableCell" && cell.type.name !== "tableHeader") return;
@@ -573,9 +609,10 @@ function deleteTableCellAt(tr, cellPos, emptied) {
573
609
  const removedColumn = TableMap.get(table).findCell(cellOffset).left;
574
610
  const spansDown = Number(cell.attrs["rowspan"]) > 1;
575
611
  if (row.childCount > 1 || spansDown) {
576
- if (row.childCount === 1) emptied.push({
577
- position: tablePosition,
578
- mapFrom: tr.mapping.maps.length
612
+ if (row.childCount === 1) markRowEmptiedInBatch({
613
+ tr,
614
+ rowPosition: resolved.start() - 1,
615
+ emptied
579
616
  });
580
617
  tr.delete(cellPos, cellPos + cell.nodeSize);
581
618
  reconcileTableGridAfterColumnRemoval({
@@ -1336,7 +1373,10 @@ const shiftAddedParagraphBreakBefore = ({ tr, paragraphPosition, mark, fallbackA
1336
1373
  const paragraph = tr.doc.nodeAt(paragraphPosition);
1337
1374
  if (!paragraph || paragraph.type.name !== "paragraph") return false;
1338
1375
  const resolved = tr.doc.resolve(paragraphPosition);
1339
- const carrier = addedBreakCarrierBefore(resolved, paragraph.type.name);
1376
+ const carrier = addedBreakCarrierBefore({
1377
+ at: resolved,
1378
+ paragraphTypeName: paragraph.type.name
1379
+ });
1340
1380
  if (!carrier) return false;
1341
1381
  const path = [{
1342
1382
  node: paragraph,
@@ -1,6 +1,6 @@
1
1
  import { mergeTextFormatting } from "../../utils/textFormattingMerge.js";
2
2
  import { expectCharacterStyleMarkAttrs } from "../attrs/index.js";
3
- import { getDocumentStyleResolver } from "../plugins/documentStyles.js";
3
+ import { getDocumentStyleResolver } from "../plugins/documentStyleState.js";
4
4
  import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../runFormattingReconciliation.js";
5
5
  import { paragraphFormattingForRun, paragraphRunStyleContext, paragraphRunStyleContextAt, resolveEffectiveRunStyleFormatting } from "../runStyleFormatting.js";
6
6
  //#region src/prosemirror/commands/formatPainter.ts
@@ -1,5 +1,6 @@
1
1
  import { normalizeUserUrl } from "../../utils/urlSecurity.js";
2
2
  import { expectHyperlinkMarkAttrs } from "../attrs/index.js";
3
+ import { removeHyperlinkInRange } from "../hyperlinkRemoval.js";
3
4
  //#region src/prosemirror/commands/hyperlink.ts
4
5
  const hyperlinkHref = (mark) => expectHyperlinkMarkAttrs(mark).href;
5
6
  /**
@@ -87,7 +88,7 @@ const removeHyperlinkAtCursor = (view, { popupHref }) => {
87
88
  const ranges = collectHyperlinkRanges($from.parent, $from.start(), hlType, hyperlinkHref(linkMark));
88
89
  const targetRange = rangeAtCursor(ranges, $from.pos);
89
90
  if (!targetRange) return false;
90
- const tr = view.state.tr.removeMark(targetRange.start, targetRange.end, hlType);
91
+ const tr = removeHyperlinkInRange(view.state, view.state.tr, targetRange.start, targetRange.end);
91
92
  view.dispatch(tr.scrollIntoView());
92
93
  return true;
93
94
  };
@@ -48,8 +48,9 @@ declare const PPR_CHANGE_SCOPED_ATTR_KEYS: readonly ["styleId", "numPr", "alignm
48
48
  declare const paragraphPropertiesSnapshot: (node: Node) => ParagraphPropertySnapshot;
49
49
  /**
50
50
  * Build the attr patch that rejecting one pPrChange applies to a paragraph:
51
- * every in-scope key set to the stored previous value, or reset to `null`
52
- * when the stored old pPr does not carry it. Keys the record captured beyond
51
+ * every in-scope key set to the stored previous value, or to what the style
52
+ * (`inheritedFormatting`) supplies when the stored old pPr does not carry it,
53
+ * `null` when neither does. Keys the record captured beyond
53
54
  * the scoped set (editor-created list suggestions snapshot list-rendering
54
55
  * bookkeeping attrs) merge through 1:1 so their pre-change values restore too.
55
56
  */
@@ -1,8 +1,11 @@
1
+ import { isStyleSourcedParagraphNumbering } from "../../internal/paragraphFormattingSerialization.js";
1
2
  import { expectParagraphAttrs } from "../attrs/index.js";
2
3
  import { readParagraphNumberingAttr } from "../numberingAttr.js";
3
4
  import { directParagraphAlignment } from "../paragraphAlignment.js";
4
5
  import { directionFromBidi } from "../paragraphDirection.js";
6
+ import { PARAGRAPH_FORMATTING_WRITE_BACK, STYLE_RESOLVED_PARAGRAPH_FIELDS } from "../paragraphFormattingProvenance.js";
5
7
  import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingFromFormatting } from "../paragraphSpacing.js";
8
+ import { sameFormattingValue } from "../runFormattingFromMarks.js";
6
9
  //#region src/prosemirror/commands/propertyChangeScope.ts
7
10
  /** Editor-only suggestions are rebased away before save; every other entry emits `w:pPrChange`. */
8
11
  const hasSerializableParagraphPropertyChange = (changes) => changes?.some(({ info }) => info.provenance !== "suggested") === true;
@@ -106,12 +109,29 @@ const PPR_SPACING_ATTR_KEYS = /* @__PURE__ */ new Set([
106
109
  "spacingExplicit",
107
110
  "_autospacingBase"
108
111
  ]);
112
+ /**
113
+ * Whether the attr holds only what the paragraph's style resolves to: the
114
+ * paragraph does not state it (its source `w:pPr` has no such field) and the
115
+ * value is the style's. The same test a save applies before writing it.
116
+ */
117
+ const styleSuppliedParagraphValue = (attrs, key) => {
118
+ if (!STYLE_RESOLVED_ATTR_FIELDS.has(key)) return false;
119
+ const field = key;
120
+ const inherited = attrs._resolvedFormatting?.[field];
121
+ return attrs._originalFormatting?.[field] === void 0 && inherited !== void 0 && inherited !== null && sameFormattingValue(Reflect.get(attrs, key), inherited);
122
+ };
123
+ /** Fields whose attr carries the style-resolved value under the field's own name. */
124
+ const STYLE_RESOLVED_ATTR_FIELDS = new Set(STYLE_RESOLVED_PARAGRAPH_FIELDS.filter((field) => PARAGRAPH_FORMATTING_WRITE_BACK[field].attr === field));
109
125
  /** The in-scope paragraph properties as they stand, for a `w:pPrChange` record. */
110
126
  const paragraphPropertiesSnapshot = (node) => {
111
127
  const attrs = expectParagraphAttrs(node);
112
128
  const snapshot = {};
113
129
  for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) {
114
- if (key === "alignment" || PPR_SPACING_ATTR_KEYS.has(key)) continue;
130
+ if (key === "alignment" || PPR_SPACING_ATTR_KEYS.has(key) || key === "numPr" && isStyleSourcedParagraphNumbering(attrs.numPr, attrs.numPrFromStyle)) continue;
131
+ if (styleSuppliedParagraphValue(attrs, key)) continue;
132
+ if (key === "hangingIndent" && styleSuppliedParagraphValue(attrs, "indentFirstLine")) continue;
133
+ if (styleSuppliedParagraphValue(attrs, key)) continue;
134
+ if (key === "hangingIndent" && styleSuppliedParagraphValue(attrs, "indentFirstLine")) continue;
115
135
  const value = attrs[key];
116
136
  if (key === "hangingIndent" && value === false && attrs._originalFormatting?.hangingIndent === void 0) continue;
117
137
  if (value !== null && value !== void 0) snapshot[key] = value;
@@ -177,15 +197,19 @@ const PPR_CHANGE_SCOPED_FORMATTING_KEYS = [
177
197
  ];
178
198
  /**
179
199
  * Build the attr patch that rejecting one pPrChange applies to a paragraph:
180
- * every in-scope key set to the stored previous value, or reset to `null`
181
- * when the stored old pPr does not carry it. Keys the record captured beyond
200
+ * every in-scope key set to the stored previous value, or to what the style
201
+ * (`inheritedFormatting`) supplies when the stored old pPr does not carry it,
202
+ * `null` when neither does. Keys the record captured beyond
182
203
  * the scoped set (editor-created list suggestions snapshot list-rendering
183
204
  * bookkeeping attrs) merge through 1:1 so their pre-change values restore too.
184
205
  */
185
206
  function paragraphRejectAttrPatch(previousFormatting, inheritedFormatting) {
186
207
  const prev = previousFormatting ?? {};
187
208
  const patch = {};
188
- for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) patch[key] = Object.hasOwn(prev, key) ? prev[key] ?? null : null;
209
+ for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) if (Object.hasOwn(prev, key)) patch[key] = prev[key] ?? null;
210
+ else if (STYLE_RESOLVED_ATTR_FIELDS.has(key)) patch[key] = Reflect.get(inheritedFormatting ?? {}, key) ?? null;
211
+ else patch[key] = null;
212
+ if (!Object.hasOwn(prev, "indentFirstLine") && !Object.hasOwn(prev, "hangingIndent")) patch["hangingIndent"] = inheritedFormatting?.hangingIndent ?? null;
189
213
  if (!Object.hasOwn(prev, "alignment")) patch["alignment"] = inheritedFormatting?.alignment ?? null;
190
214
  patch["alignmentFromStyle"] = inheritedFormatting?.alignment;
191
215
  const directSpacing = paragraphSpacingFromFormatting(prev);
@@ -6,6 +6,7 @@ import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
6
6
  import { isTableCellRetainedInReviewView } from "../tableCellRevisionVisibility.js";
7
7
  import { resolveAllNodePropertyChangeAttrs } from "./resolveNodePropertyChangeAttrs.js";
8
8
  import { createRestoredTableCell, hasMatchingCollapsedTableCellMerge, tableCellContinuationCells, tableCellContinuationFromNode, tableCellContinuationPayload } from "./tableCellMergeResolution.js";
9
+ import { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions } from "./tableMergeFoldDecisions.js";
9
10
  import { panic } from "better-result";
10
11
  import { TableMap } from "prosemirror-tables";
11
12
  import { StepMap } from "prosemirror-transform";
@@ -131,10 +132,12 @@ const cellRevisionMarker = (cell) => {
131
132
  const marker = cell.attrs["cellMarker"];
132
133
  if (typeof marker !== "object" || marker === null || !("kind" in marker) || marker.kind !== "ins" && marker.kind !== "del" && marker.kind !== "merge" || !("info" in marker) || typeof marker.info !== "object" || marker.info === null || !("revisionId" in marker.info) || typeof marker.info.revisionId !== "number") return null;
133
134
  const verticalMergeOriginal = "verticalMergeOriginal" in marker && isTableCellMergeRevisionValue(marker.verticalMergeOriginal) ? marker.verticalMergeOriginal : void 0;
135
+ const verticalMerge = "verticalMerge" in marker && isTableCellMergeRevisionValue(marker.verticalMerge) ? marker.verticalMerge : void 0;
134
136
  return {
135
137
  kind: marker.kind,
136
138
  info: { revisionId: marker.info.revisionId },
137
- ...verticalMergeOriginal ? { verticalMergeOriginal } : {}
139
+ ...verticalMergeOriginal ? { verticalMergeOriginal } : {},
140
+ ...verticalMerge ? { verticalMerge } : {}
138
141
  };
139
142
  };
140
143
  /**
@@ -356,6 +359,12 @@ const compactColumnWidths = (widths, removals) => {
356
359
  /** Resolve all cell edits in reverse order against one grid and mutable slots. */
357
360
  const resolvePureTableMerges = ({ table, mode, rowMetadata, tablePropertyTouched, sourceTable }) => {
358
361
  const map = TableMap.get(table);
362
+ const mergeDecisions = tableMergeFoldDecisions({
363
+ table,
364
+ mode,
365
+ revisionSet: null
366
+ });
367
+ if (mergeDecisions.invalid.size > 0) return failedTableResolution(table);
359
368
  const cellRectangle = indexedTableCells(map);
360
369
  const rows = Array.from({ length: table.childCount }, (_, index) => table.child(index));
361
370
  const rowCells = rows.map(() => /* @__PURE__ */ new Map());
@@ -381,7 +390,7 @@ const resolvePureTableMerges = ({ table, mode, rowMetadata, tablePropertyTouched
381
390
  for (let coveredRow = rectangle.top; coveredRow < rectangle.bottom; coveredRow++) rowOccupancy[coveredRow] = elementAt(rowOccupancy, coveredRow) + (rectangle.right - rectangle.left);
382
391
  const initial = elementAt(elementAt(rowMetadata, rowIndex).cells, cellIndex);
383
392
  const entry = {
384
- node,
393
+ node: mergeDecisions.origins.has(cellOffset) ? node.type.create(resolvedTableMergeOriginAttrs(node), node.content, node.marks) : node,
385
394
  position: cellOffset,
386
395
  left,
387
396
  row: rowIndex,
@@ -435,46 +444,36 @@ const resolvePureTableMerges = ({ table, mode, rowMetadata, tablePropertyTouched
435
444
  count: previousWidth - currentWidth
436
445
  });
437
446
  }
438
- } else if (marker?.kind === "merge") if (mode === "accept") {
439
- entry.touched = true;
440
- entry.node = entry.node.type.create({
441
- ...entry.node.attrs,
442
- cellMarker: null
443
- }, entry.node.content, entry.node.marks);
444
- } else if (!isTableCellMergeRevisionContinuation(marker.verticalMergeOriginal)) {
445
- entry.touched = true;
446
- const original = entry.node.attrs["_originalFormatting"];
447
- let formatting = original;
448
- if (typeof original === "object" && original !== null) {
449
- const copy = { ...original };
450
- delete copy["vMerge"];
451
- formatting = copy;
447
+ } else if (marker?.kind === "merge") {
448
+ const joinsAbove = mergeDecisions.folds.get(entry.position);
449
+ if (joinsAbove === void 0) return panic("Merge resolution lost its cell");
450
+ if (mode === "reject" && isTableCellMergeRevisionContinuation(marker.verticalMergeOriginal) && cellRectangle(entry.position).top === 0) return failedTableResolution(table);
451
+ if (!joinsAbove) entry.node = entry.node.type.create(resolvedVisibleTableCellMergeAttrs({
452
+ cell: entry.node,
453
+ mode,
454
+ origin: mergeDecisions.origins.has(entry.position)
455
+ }), entry.node.content, entry.node.marks);
456
+ else {
457
+ const rectangle = cellRectangle(entry.position);
458
+ if (rectangle.top === 0) return failedTableResolution(table);
459
+ const abovePosition = map.map[(rectangle.top - 1) * map.width + rectangle.left];
460
+ if (abovePosition === void 0) return failedTableResolution(table);
461
+ const aboveRectangle = cellRectangle(abovePosition);
462
+ const above = byPosition.get(abovePosition);
463
+ if (!above || !elementAt(rowCells, above.row).has(above.left) || aboveRectangle.left !== rectangle.left || aboveRectangle.right !== rectangle.right || aboveRectangle.bottom !== rectangle.top) return failedTableResolution(table);
464
+ const aboveRowspan = above.node.attrs["rowspan"];
465
+ const cellRowspan = entry.node.attrs["rowspan"];
466
+ if (typeof aboveRowspan !== "number" || typeof cellRowspan !== "number" || aboveRowspan < 1 || cellRowspan < 1) return failedTableResolution(table);
467
+ above.continuation ??= continuationSequence(tableCellContinuationCells(above.node, aboveRowspan));
468
+ appendContinuation(above.continuation, tableCellContinuationFromNode(entry.node));
469
+ const nested = entry.continuation ?? continuationSequence(tableCellContinuationPayload(entry.node)?.cells ?? []);
470
+ appendContinuationSequence(above.continuation, nested);
471
+ above.node = above.node.type.create({
472
+ ...above.node.attrs,
473
+ rowspan: aboveRowspan + cellRowspan
474
+ }, above.node.content, above.node.marks);
475
+ elementAt(rowCells, entry.row).delete(entry.left);
452
476
  }
453
- entry.node = entry.node.type.create({
454
- ...entry.node.attrs,
455
- cellMarker: null,
456
- _originalFormatting: formatting
457
- }, entry.node.content, entry.node.marks);
458
- } else {
459
- const rectangle = cellRectangle(entry.position);
460
- if (rectangle.top === 0) return failedTableResolution(table);
461
- const abovePosition = map.map[(rectangle.top - 1) * map.width + rectangle.left];
462
- if (abovePosition === void 0) return failedTableResolution(table);
463
- const aboveRectangle = cellRectangle(abovePosition);
464
- const above = byPosition.get(abovePosition);
465
- if (!above || !elementAt(rowCells, above.row).has(above.left) || aboveRectangle.left !== rectangle.left || aboveRectangle.right !== rectangle.right || aboveRectangle.bottom !== rectangle.top) return failedTableResolution(table);
466
- const aboveRowspan = above.node.attrs["rowspan"];
467
- const cellRowspan = entry.node.attrs["rowspan"];
468
- if (typeof aboveRowspan !== "number" || typeof cellRowspan !== "number" || aboveRowspan < 1 || cellRowspan < 1) return failedTableResolution(table);
469
- above.continuation ??= continuationSequence(tableCellContinuationCells(above.node, aboveRowspan));
470
- appendContinuation(above.continuation, tableCellContinuationFromNode(entry.node));
471
- const nested = entry.continuation ?? continuationSequence(tableCellContinuationPayload(entry.node)?.cells ?? []);
472
- appendContinuationSequence(above.continuation, nested);
473
- above.node = above.node.type.create({
474
- ...above.node.attrs,
475
- rowspan: aboveRowspan + cellRowspan
476
- }, above.node.content, above.node.marks);
477
- elementAt(rowCells, entry.row).delete(entry.left);
478
477
  }
479
478
  if (!elementAt(rowCells, entry.row).has(entry.left)) continue;
480
479
  if (!tableCellContinuationPayload(entry.node)?.cells.some((cell) => getTableCellMergeChange(cell) !== null)) continue;
@@ -1,6 +1,9 @@
1
+ import { resolveParagraphNumbering } from "../../docx/numberingReference.js";
1
2
  import { expectParagraphAttrs } from "../attrs/index.js";
2
- import { resolveParagraphDefaultTextFormatting } from "../conversion/toProseDoc.js";
3
- import { rejectedListRenderingPatch } from "../listNumbering.js";
3
+ import { rejectedListRenderingPatch } from "../listRendering.js";
4
+ import { paragraphNumberingAttr } from "../numberingAttr.js";
5
+ import { styleResolvedParagraphFormatting } from "../paragraphFormattingProvenance.js";
6
+ import { resolveParagraphDefaultTextFormatting } from "../styles/paragraphStyleCascade.js";
4
7
  import { paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, removeParagraphPropertyChanges, sectionRejectProperties } from "./propertyChangeScope.js";
5
8
  //#region src/prosemirror/commands/resolveParagraphProperties.ts
6
9
  /** Resolve paragraph and section property records before resolving their inline content. */
@@ -20,15 +23,37 @@ const resolveParagraphChangeAttrs = ({ node, boundaryCovered, mode, revisionSet,
20
23
  _propertyChanges: remaining.length > 0 ? remaining : null
21
24
  };
22
25
  if (rejection?.type === "restore-previous") {
23
- const inheritedAlignment = expectParagraphAttrs(node).alignmentFromStyle;
26
+ const currentAttrs = expectParagraphAttrs(node);
27
+ const inheritedAlignment = currentAttrs.alignmentFromStyle;
28
+ const restoredStyleId = rejection.previousFormatting?.styleId ?? void 0;
29
+ const sameStyle = restoredStyleId === (currentAttrs.styleId ?? void 0);
24
30
  let previousFormattingFromStyle;
25
- if (styleResolver) previousFormattingFromStyle = styleResolver.resolveParagraphStyle(rejection.previousFormatting?.styleId).paragraphFormatting;
26
- else if (inheritedAlignment !== void 0) previousFormattingFromStyle = { alignment: inheritedAlignment };
27
- Object.assign(nextAttrs, paragraphRejectAttrPatch(rejection.previousFormatting, previousFormattingFromStyle), rejectedListRenderingPatch({
31
+ let resolvedFromStyle = sameStyle ? currentAttrs._resolvedFormatting : void 0;
32
+ if (styleResolver) {
33
+ previousFormattingFromStyle = styleResolver.resolveParagraphStyle(restoredStyleId).paragraphFormatting;
34
+ if (!sameStyle) {
35
+ resolvedFromStyle = styleResolvedParagraphFormatting(previousFormattingFromStyle);
36
+ nextAttrs["_resolvedFormatting"] = resolvedFromStyle;
37
+ }
38
+ } else if (inheritedAlignment !== void 0) previousFormattingFromStyle = { alignment: inheritedAlignment };
39
+ const inheritedFormatting = previousFormattingFromStyle === void 0 && resolvedFromStyle === void 0 ? void 0 : {
40
+ ...previousFormattingFromStyle,
41
+ ...resolvedFromStyle
42
+ };
43
+ let styleNumbering = previousFormattingFromStyle?.numPr;
44
+ if (!styleResolver && sameStyle) styleNumbering = currentAttrs.numPrFromStyle ?? void 0;
45
+ const inheritedNumbering = styleNumbering == null ? null : paragraphNumberingAttr(resolveParagraphNumbering(styleNumbering));
46
+ const recordedNumbering = rejection.previousFormatting?.numPr;
47
+ const restoredNumbering = recordedNumbering ?? inheritedNumbering;
48
+ Object.assign(nextAttrs, paragraphRejectAttrPatch(rejection.previousFormatting, inheritedFormatting), rejectedListRenderingPatch({
28
49
  current: expectParagraphAttrs(node),
29
50
  previousFormatting: rejection.previousFormatting,
30
- numbering
31
- }));
51
+ numbering,
52
+ restoredNumbering
53
+ }), {
54
+ numPr: restoredNumbering,
55
+ numPrFromStyle: recordedNumbering == null ? inheritedNumbering : null
56
+ });
32
57
  const restoredFormatting = paragraphRejectOriginalFormatting(rejection.previousFormatting, node.attrs["_originalFormatting"]);
33
58
  nextAttrs["_originalFormatting"] = restoredFormatting;
34
59
  if (styleResolver) nextAttrs["defaultTextFormatting"] = resolveParagraphDefaultTextFormatting(rejection.previousFormatting?.styleId, restoredFormatting ?? void 0, styleResolver) ?? null;
@@ -5,7 +5,13 @@ import { Transaction } from "prosemirror-state";
5
5
  //#region src/prosemirror/commands/tableCellMergeResolution.d.ts
6
6
  declare const tableCellContinuationPayload: (node: Node) => DecodedTableCellParagraphSourcePayload | null;
7
7
  declare const hasMatchingCollapsedTableCellMerge: (node: Node, revisionSet: Set<number> | null) => boolean;
8
- declare const resolveVisibleTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject") => boolean;
8
+ type ResolveVisibleTableCellMergeOptions = {
9
+ tr: Transaction;
10
+ cellPos: number;
11
+ mode: "accept" | "reject";
12
+ revisionSet: ReadonlySet<number> | null;
13
+ };
14
+ declare const resolveVisibleTableCellMerge: ({ tr, cellPos, mode, revisionSet }: ResolveVisibleTableCellMergeOptions) => boolean;
9
15
  declare const tableCellContinuationCells: (cell: Node, rowspan: number) => document_d_exports.TableCell[];
10
16
  declare const tableCellContinuationFromNode: (cell: Node) => document_d_exports.TableCell;
11
17
  declare const resolveCollapsedTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject", revisionSet: Set<number> | null) => boolean;
@@ -1,8 +1,11 @@
1
1
  import { decodeTableCellParagraphSourcePayload, restoreTableCellsWithParagraphPropertySources, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
2
+ import { isTableCellMergeRevisionContinuation } from "../../docx/tableParser.js";
2
3
  import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
3
4
  import { standaloneTableCellToProseMirror } from "../conversion/toProseDoc.js";
4
5
  import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
5
- import { Result } from "better-result";
6
+ import { removeRowsWithoutCellsInRange } from "../tableGridMutation.js";
7
+ import { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions } from "./tableMergeFoldDecisions.js";
8
+ import { Result, panic } from "better-result";
6
9
  import { TableMap } from "prosemirror-tables";
7
10
  //#region src/prosemirror/commands/tableCellMergeResolution.ts
8
11
  const tableCellContinuationPayload = (node) => {
@@ -35,26 +38,35 @@ const tableCellContext = (doc, cellPos) => {
35
38
  }
36
39
  return null;
37
40
  };
38
- const resolveVisibleTableCellMerge = (tr, cellPos, mode) => {
41
+ const resolveVisibleTableCellMerge = ({ tr, cellPos, mode, revisionSet }) => {
39
42
  const cell = tr.doc.nodeAt(cellPos);
40
43
  const marker = cell?.attrs["cellMarker"];
41
44
  if (!cell || !isTableCellMergeRevisionAttr(marker)) return false;
42
- if (mode === "accept") {
43
- tr.setNodeAttribute(cellPos, "cellMarker", null);
44
- return true;
45
- }
46
- if ((marker.verticalMergeOriginal ?? "rest") === "continue") return mergeTableCellWithCellAbove(tr, cellPos);
47
- const originalFormatting = cell.attrs["_originalFormatting"];
48
- let nextOriginalFormatting = originalFormatting;
49
- if (typeof originalFormatting === "object" && originalFormatting !== null) {
50
- nextOriginalFormatting = { ...originalFormatting };
51
- delete nextOriginalFormatting.vMerge;
52
- }
53
- tr.setNodeMarkup(cellPos, void 0, {
54
- ...cell.attrs,
55
- cellMarker: null,
56
- _originalFormatting: nextOriginalFormatting
45
+ const context = tableCellContext(tr.doc, cellPos);
46
+ if (!context) return false;
47
+ const rectangle = TableMap.get(context.table).findCell(context.relativeCellPos);
48
+ if (mode === "reject" && isTableCellMergeRevisionContinuation(marker.verticalMergeOriginal) && rectangle.top === 0) return false;
49
+ const decisions = tableMergeFoldDecisions({
50
+ table: context.table,
51
+ mode,
52
+ revisionSet
57
53
  });
54
+ if (decisions.invalid.has(context.relativeCellPos)) return false;
55
+ const folds = decisions.folds.get(context.relativeCellPos);
56
+ if (folds === void 0) return panic("Merge resolution lost its cell");
57
+ for (const origin of decisions.origins) {
58
+ if (origin === context.relativeCellPos) continue;
59
+ const originPos = context.tableStart + origin;
60
+ const originCell = tr.doc.nodeAt(originPos);
61
+ if (!originCell) return panic("Merge resolution lost its origin");
62
+ tr.setNodeMarkup(originPos, void 0, resolvedTableMergeOriginAttrs(originCell));
63
+ }
64
+ if (folds) return mergeTableCellWithCellAbove(tr, cellPos);
65
+ tr.setNodeMarkup(cellPos, void 0, resolvedVisibleTableCellMergeAttrs({
66
+ cell,
67
+ mode,
68
+ origin: decisions.origins.has(context.relativeCellPos)
69
+ }));
58
70
  return true;
59
71
  };
60
72
  const mergeTableCellWithCellAbove = (tr, cellPos) => {
@@ -73,11 +85,18 @@ const mergeTableCellWithCellAbove = (tr, cellPos) => {
73
85
  const aboveRowspan = aboveCell?.attrs["rowspan"];
74
86
  const cellRowspan = cell.attrs["rowspan"];
75
87
  if (!aboveCell || typeof aboveRowspan !== "number" || typeof cellRowspan !== "number" || aboveRowspan < 1 || cellRowspan < 1) return false;
88
+ removeRowsWithoutCellsInRange({
89
+ tr,
90
+ from: cellPos + 1,
91
+ to: cellPos + cell.nodeSize - 1
92
+ });
93
+ const capturedCell = tr.doc.nodeAt(cellPos);
94
+ if (!capturedCell) panic("Nested table cleanup removed its owning cell");
76
95
  const continuationCells = tableCellContinuationCells(aboveCell, aboveRowspan);
77
- continuationCells.push(tableCellContinuationFromNode(cell));
78
- const nestedPayload = tableCellContinuationPayload(cell);
96
+ continuationCells.push(tableCellContinuationFromNode(capturedCell));
97
+ const nestedPayload = tableCellContinuationPayload(capturedCell);
79
98
  if (nestedPayload) continuationCells.push(...nestedPayload.cells);
80
- tr.delete(cellPos, cellPos + cell.nodeSize);
99
+ tr.delete(cellPos, cellPos + capturedCell.nodeSize);
81
100
  tr.setNodeMarkup(abovePos, void 0, {
82
101
  ...aboveCell.attrs,
83
102
  rowspan: aboveRowspan + cellRowspan,
@@ -0,0 +1,61 @@
1
+ import { Node } from "prosemirror-model";
2
+ //#region src/prosemirror/commands/tableMergeFoldDecisions.d.ts
3
+ type TableMergeFoldDecisionsOptions = {
4
+ table: Node;
5
+ mode: "accept" | "reject";
6
+ revisionSet: ReadonlySet<number> | null;
7
+ };
8
+ /** Resolve raw merge states without converting unrelated table revisions. */
9
+ declare const tableMergeFoldDecisions: ({ table, mode, revisionSet }: TableMergeFoldDecisionsOptions) => {
10
+ folds: Map<number, boolean>;
11
+ origins: Set<number>;
12
+ invalid: Set<number>;
13
+ };
14
+ /** Persist an implicit merge origin even when reader rules retain its continuation. */
15
+ declare const resolvedTableMergeOriginAttrs: (cell: Node) => {
16
+ _originalFormatting: {
17
+ vMerge: string;
18
+ width?: import("@stll/docx-core/model").TableMeasurement;
19
+ borders?: import("@stll/docx-core/model").TableCellBorders;
20
+ margins?: import("@stll/docx-core/model").CellMargins;
21
+ shading?: import("@stll/docx-core/model").ShadingProperties;
22
+ verticalAlign?: "top" | "center" | "bottom";
23
+ textDirection?: import("@stll/docx-core/model").TextDirection;
24
+ gridSpan?: number;
25
+ fitText?: boolean;
26
+ noWrap?: boolean;
27
+ hideMark?: boolean;
28
+ conditionalFormat?: import("@stll/docx-core/model").ConditionalFormatStyle;
29
+ preserved?: import("@stll/docx-core/model").PreservedMarkup;
30
+ sourceXml?: string;
31
+ };
32
+ _preserveVMergeRestart: boolean;
33
+ };
34
+ type ResolvedVisibleTableCellMergeOptions = {
35
+ cell: Node;
36
+ mode: "accept" | "reject";
37
+ origin: boolean;
38
+ };
39
+ /** A retained continuation must save the merge state the reviewer resolved. */
40
+ declare const resolvedVisibleTableCellMergeAttrs: ({ cell, mode, origin }: ResolvedVisibleTableCellMergeOptions) => {
41
+ cellMarker: null;
42
+ _originalFormatting: {
43
+ width?: import("@stll/docx-core/model").TableMeasurement;
44
+ borders?: import("@stll/docx-core/model").TableCellBorders;
45
+ margins?: import("@stll/docx-core/model").CellMargins;
46
+ shading?: import("@stll/docx-core/model").ShadingProperties;
47
+ verticalAlign?: "top" | "center" | "bottom";
48
+ textDirection?: import("@stll/docx-core/model").TextDirection;
49
+ gridSpan?: number;
50
+ vMerge?: "restart" | "continue";
51
+ fitText?: boolean;
52
+ noWrap?: boolean;
53
+ hideMark?: boolean;
54
+ conditionalFormat?: import("@stll/docx-core/model").ConditionalFormatStyle;
55
+ preserved?: import("@stll/docx-core/model").PreservedMarkup;
56
+ sourceXml?: string;
57
+ };
58
+ _preserveVMergeRestart: boolean;
59
+ };
60
+ //#endregion
61
+ export { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions };