@stll/folio-core 0.66.1 → 0.66.2
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.
- package/dist/ai-edits/apply.d.ts +13 -1
- package/dist/ai-edits/apply.js +533 -136
- package/dist/ai-edits/batch-claims.d.ts +26 -0
- package/dist/ai-edits/batch-claims.js +30 -5
- package/dist/ai-edits/headless.d.ts +6 -0
- package/dist/ai-edits/headless.js +25 -3
- package/dist/ai-edits/newListNumbering.d.ts +4 -1
- package/dist/ai-edits/newListNumbering.js +4 -2
- package/dist/ai-edits/types.d.ts +1 -1
- package/dist/document-operations.d.ts +13 -1
- package/dist/document-operations.js +11 -4
- package/dist/docx/listNumberingInstances.d.ts +6 -4
- package/dist/docx/listNumberingInstances.js +12 -11
- package/dist/docx/numberingIds.d.ts +10 -0
- package/dist/docx/numberingIds.js +5 -1
- package/dist/docx/paragraphParser.js +16 -3
- package/dist/docx/paragraphProperties.js +2 -1
- package/dist/docx/serializer/paragraphSerializer.js +86 -66
- package/dist/i18n/messages/ar.gen.js +1 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/cs.gen.js +1 -0
- package/dist/i18n/messages/de.gen.js +1 -0
- package/dist/i18n/messages/en.gen.js +1 -0
- package/dist/i18n/messages/es.gen.js +1 -0
- package/dist/i18n/messages/et.gen.js +1 -0
- package/dist/i18n/messages/fr.gen.js +1 -0
- package/dist/i18n/messages/he.gen.js +1 -0
- package/dist/i18n/messages/hi.gen.js +1 -0
- package/dist/i18n/messages/hu.gen.js +1 -0
- package/dist/i18n/messages/lt.gen.js +1 -0
- package/dist/i18n/messages/lv.gen.js +1 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/i18n/messages/pl.gen.js +1 -0
- package/dist/i18n/messages/pt-BR.gen.js +1 -0
- package/dist/i18n/messages/sk.gen.js +1 -0
- package/dist/i18n/messages/tr.gen.js +1 -0
- package/dist/i18n/messages/zh-CN.gen.js +1 -0
- package/dist/internal/wholeStoryRevisionResolution.d.ts +1 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -9
- package/dist/layout-bridge/convert/runMarkFormatting.js +1 -0
- package/dist/markdown/renderRuns.js +68 -12
- package/dist/prosemirror/commands/comments.js +2 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.js +15 -2
- package/dist/prosemirror/conversion/fromProseDoc.js +55 -18
- package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +23 -1
- package/dist/prosemirror/conversion/hyphenTextCarriers.js +46 -1
- package/dist/prosemirror/conversion/toProseDoc.js +15 -9
- package/dist/prosemirror/extensions/StarterKit.js +1 -1
- package/dist/prosemirror/extensions/features/pasteCleanup.js +6 -0
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +2 -1
- package/dist/prosemirror/extensions/markRegistry.d.ts +3 -1
- package/dist/prosemirror/extensions/markRegistry.js +3 -0
- package/dist/prosemirror/extensions/marks/HyphenElementExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/marks/HyphenElementExtension.js +54 -0
- package/dist/prosemirror/fieldRepresentation.js +2 -1
- package/dist/prosemirror/hyphenElementAttrs.d.ts +10 -0
- package/dist/prosemirror/hyphenElementAttrs.js +32 -0
- package/dist/prosemirror/keptRangeBoundaries.d.ts +2 -0
- package/dist/prosemirror/keptRangeBoundaries.js +1 -1
- package/dist/prosemirror/listNumbering.js +3 -2
- package/dist/prosemirror/paragraphIndentation.js +2 -1
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +1 -14
- package/dist/prosemirror/paragraphPropertyCarry.js +4 -28
- package/dist/prosemirror/plugins/suggestionMode.js +19 -6
- package/dist/prosemirror/replacedAnnotations.d.ts +1 -0
- package/dist/prosemirror/replacedAnnotations.js +1 -0
- package/dist/prosemirror/sectionCarrier.d.ts +8 -0
- package/dist/prosemirror/sectionCarrier.js +29 -1
- package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +10 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.js +12 -2
- package/dist/prosemirror/tableRunIn.d.ts +24 -9
- package/dist/prosemirror/tableRunIn.js +107 -21
- package/dist/prosemirror/textInput.js +3 -1
- package/dist/prosemirror/validation.js +6 -0
- package/dist/utils/createDocument.js +11 -8
- package/dist/utils/formatToStyle.js +2 -2
- package/dist/utils/units.d.ts +5 -0
- package/dist/utils/units.js +6 -1
- package/package.json +3 -3
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NO_NUMBERING_IDS_IN_USE } from "../docx/numberingIds.js";
|
|
1
2
|
import { isNumberingReference } from "../docx/numberingReference.js";
|
|
2
3
|
import { formattingEquals } from "../docx/runConsolidator.js";
|
|
3
4
|
import { isStyleSourcedParagraphNumbering } from "../internal/paragraphFormattingSerialization.js";
|
|
@@ -20,7 +21,7 @@ import { directParagraphAlignment } from "../prosemirror/paragraphAlignment.js";
|
|
|
20
21
|
import { styleResolvedParagraphFormatting } from "../prosemirror/paragraphFormattingProvenance.js";
|
|
21
22
|
import { directParagraphIndentation, paragraphIndentationAttrPatch, paragraphIndentationEqual, paragraphIndentationFromFormatting, withDirectParagraphIndentation } from "../prosemirror/paragraphIndentation.js";
|
|
22
23
|
import { joinAtParagraphMark } from "../prosemirror/paragraphMarkJoin.js";
|
|
23
|
-
import {
|
|
24
|
+
import { carryParagraphProperties, paragraphLeftAfter, recordReplacedParagraphProperties } from "../prosemirror/paragraphPropertyCarry.js";
|
|
24
25
|
import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing } from "../prosemirror/paragraphSpacing.js";
|
|
25
26
|
import { getDocumentNumbering, getDocumentNumberingInstanceIds } from "../prosemirror/plugins/documentNumbering.js";
|
|
26
27
|
import { getDocumentStyleDefinitions, getDocumentStyleResolver } from "../prosemirror/plugins/documentStyleState.js";
|
|
@@ -34,6 +35,7 @@ import { paragraphRunStyleContext, paragraphRunStyleContextAt, resolveParagraphB
|
|
|
34
35
|
import { sectionPropertiesOf } from "../prosemirror/sectionCarrier.js";
|
|
35
36
|
import { listAttrsFromNumbering, listLevelAttrPatch, listLevelIndentRemovalPatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
|
|
36
37
|
import { removeRowsWithoutCellsAfterBatch } from "../prosemirror/tableGridMutation.js";
|
|
38
|
+
import { runInCellPending } from "../prosemirror/tableRunIn.js";
|
|
37
39
|
import { isZeroWidthAnchor } from "../prosemirror/zeroWidthAnchors.js";
|
|
38
40
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
39
41
|
import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
|
|
@@ -59,7 +61,7 @@ import { panic } from "better-result";
|
|
|
59
61
|
import { Fragment, Mark } from "prosemirror-model";
|
|
60
62
|
import { TableMap } from "prosemirror-tables";
|
|
61
63
|
import { canSplit } from "prosemirror-transform";
|
|
62
|
-
import { contentRank, groupInsertionRuns, orderBatchOperations } from "@stll/docx-core/ops";
|
|
64
|
+
import { contentRank, deletionRemovesBlock, groupInsertionRuns, orderAddressingBeforeRemoval, orderBatchOperations } from "@stll/docx-core/ops";
|
|
63
65
|
// src/ai-edits/apply.ts
|
|
64
66
|
const coarseWordDiff = (before, after) => {
|
|
65
67
|
if (before === after) return before.length === 0 ? [] : [{
|
|
@@ -377,8 +379,8 @@ const withoutRevisionsOnZeroWidthAnchors = (tr, batchRevisionIds, deletedTextBox
|
|
|
377
379
|
* text box's anchor is the exception: it stands for the box's drawing run,
|
|
378
380
|
* which is the paragraph's content, and the box follows it.
|
|
379
381
|
*/
|
|
380
|
-
const undeletedContentAtomRanges = (tr, blockFrom) => {
|
|
381
|
-
const position = tr.mapping.map(blockFrom);
|
|
382
|
+
const undeletedContentAtomRanges = (tr, blockFrom, readAt) => {
|
|
383
|
+
const position = tr.mapping.slice(readAt).map(blockFrom);
|
|
382
384
|
const block = tr.doc.nodeAt(position);
|
|
383
385
|
if (!block?.isTextblock) return [];
|
|
384
386
|
const ranges = [];
|
|
@@ -567,7 +569,7 @@ const applyReplaceBlockStyleId = ({ item, tr, styleResolver, revisionInfo }) =>
|
|
|
567
569
|
revisionId: null,
|
|
568
570
|
revisionIds: []
|
|
569
571
|
};
|
|
570
|
-
const blockPosition = tr.
|
|
572
|
+
const blockPosition = sinceRead(tr, item).map(item.blockFrom, -1);
|
|
571
573
|
const block = tr.doc.nodeAt(blockPosition);
|
|
572
574
|
if (!block) return {
|
|
573
575
|
tr,
|
|
@@ -1039,6 +1041,28 @@ const foldedBlankCellRanges = (doc, { tablePosition, rectangle }) => {
|
|
|
1039
1041
|
}] : [];
|
|
1040
1042
|
});
|
|
1041
1043
|
};
|
|
1044
|
+
/**
|
|
1045
|
+
* The paragraphs before `paragraph`, which ends its container, nearest first,
|
|
1046
|
+
* up to the container's start or another block, past those that go whole, as
|
|
1047
|
+
* `finalParagraphPredecessor` looks past them. See the `deleteBlock` claim's
|
|
1048
|
+
* `paragraphsBefore`.
|
|
1049
|
+
*/
|
|
1050
|
+
const paragraphsBeforeFinal = ($paragraph, paragraph) => {
|
|
1051
|
+
const paragraphs = [];
|
|
1052
|
+
let position = $paragraph.pos;
|
|
1053
|
+
for (let index = $paragraph.index() - 1; index >= 0; index--) {
|
|
1054
|
+
const node = $paragraph.parent.child(index);
|
|
1055
|
+
position -= node.nodeSize;
|
|
1056
|
+
if (node.type !== paragraph.type) break;
|
|
1057
|
+
const mark = node.attrs["pPrMark"];
|
|
1058
|
+
if (isPlainDeletedPPrMark(mark) && holdsOnlyDeletedContent(node)) continue;
|
|
1059
|
+
paragraphs.push({
|
|
1060
|
+
block: position,
|
|
1061
|
+
keepsBreak: mark == null
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
return paragraphs;
|
|
1065
|
+
};
|
|
1042
1066
|
/** What `item` claims of the document its batch resolved against; see `batch-claims.ts`. */
|
|
1043
1067
|
const batchClaimOf = ({ item, doc, producesTrackedChanges }) => {
|
|
1044
1068
|
const block = item.blockFrom;
|
|
@@ -1090,7 +1114,9 @@ const batchClaimOf = ({ item, doc, producesTrackedChanges }) => {
|
|
|
1090
1114
|
type: "deleteBlock",
|
|
1091
1115
|
block,
|
|
1092
1116
|
end: item.blockTo,
|
|
1093
|
-
keepsParagraph
|
|
1117
|
+
keepsParagraph,
|
|
1118
|
+
endsContainer: endsItsContainer,
|
|
1119
|
+
paragraphsBefore: producesTrackedChanges && endsItsContainer ? paragraphsBeforeFinal(at, item.blockNode) : []
|
|
1094
1120
|
};
|
|
1095
1121
|
}
|
|
1096
1122
|
case "insertAfterBlock":
|
|
@@ -1308,22 +1334,253 @@ const rebuiltBlockContent = ({ doc, blockFrom, blockTo, content }) => annotatedR
|
|
|
1308
1334
|
}).fragment;
|
|
1309
1335
|
const isBatchableParagraphInsertion = (item, mode) => {
|
|
1310
1336
|
if (item.operation.type !== "insertAfterBlock" && item.operation.type !== "insertBeforeBlock") return false;
|
|
1337
|
+
if (item.joinSide !== void 0) return false;
|
|
1311
1338
|
if (item.commentText !== void 0) return false;
|
|
1312
1339
|
const insertTexts = item.insertTexts ?? [""];
|
|
1313
1340
|
const isEmptyInsert = insertTexts.length === 1 && insertTexts[0]?.length === 0;
|
|
1314
1341
|
return !(mode === "tracked-changes" && item.operation.pageBreakBefore === true && isEmptyInsert);
|
|
1315
1342
|
};
|
|
1316
|
-
const insertionWithLiveAnchor = (item, tr) => {
|
|
1343
|
+
const insertionWithLiveAnchor = (item, tr, joinedInto) => {
|
|
1317
1344
|
if (item.operation.type !== "insertAfterBlock" && item.operation.type !== "insertBeforeBlock") panic("Only paragraph insertions have insertion anchors", { operationId: item.operation.id });
|
|
1318
|
-
const mapped = tr
|
|
1345
|
+
const mapped = liveBlockResult(tr, item, 1);
|
|
1319
1346
|
const anchor = mapped.deletedAcross ? null : tr.doc.nodeAt(mapped.pos);
|
|
1320
|
-
|
|
1347
|
+
let paraId = item.blockNode.attrs["paraId"];
|
|
1348
|
+
while (typeof paraId === "string" && joinedInto.has(paraId)) paraId = joinedInto.get(paraId);
|
|
1321
1349
|
if (!anchor?.isTextblock || anchor.type !== item.blockNode.type || typeof paraId === "string" && anchor.attrs["paraId"] !== paraId) return null;
|
|
1322
1350
|
return {
|
|
1323
1351
|
...item,
|
|
1324
1352
|
blockNode: anchor
|
|
1325
1353
|
};
|
|
1326
1354
|
};
|
|
1355
|
+
/** Operations that place a block in a gap between blocks. */
|
|
1356
|
+
const BLOCK_INSERTION_TYPES = /* @__PURE__ */ new Set([
|
|
1357
|
+
"insertAfterBlock",
|
|
1358
|
+
"insertBeforeBlock",
|
|
1359
|
+
"insertTable",
|
|
1360
|
+
"insertSignatureTable"
|
|
1361
|
+
]);
|
|
1362
|
+
/** The steps of `tr` since `item` read its positions. */
|
|
1363
|
+
const sinceRead = (tr, item) => tr.mapping.slice(item.readAt ?? 0);
|
|
1364
|
+
/**
|
|
1365
|
+
* Where the block `item` names stands now, mapped from the document the
|
|
1366
|
+
* batch read, so the result also says whether an earlier step deleted it.
|
|
1367
|
+
*/
|
|
1368
|
+
const liveBlockResult = (tr, item, assoc) => tr.mapping.mapResult(item.batchBlockFrom ?? item.blockFrom, assoc);
|
|
1369
|
+
/**
|
|
1370
|
+
* `item` with its positions read in the document as it stands now. A batch
|
|
1371
|
+
* runs its operations in the shared order (`orderAddressingBeforeRemoval`),
|
|
1372
|
+
* which can run an edit before one further left; every operation therefore
|
|
1373
|
+
* reads its positions through the steps already taken rather than the
|
|
1374
|
+
* document the batch read. A block's start stays with its content and its
|
|
1375
|
+
* end with what precedes it.
|
|
1376
|
+
*/
|
|
1377
|
+
const readNow = (item, tr) => {
|
|
1378
|
+
const steps = tr.mapping.maps.length;
|
|
1379
|
+
if ((item.readAt ?? 0) === steps) return item;
|
|
1380
|
+
const mapping = sinceRead(tr, item);
|
|
1381
|
+
const blockFrom = mapping.map(item.blockFrom, 1);
|
|
1382
|
+
const blockTo = Math.max(blockFrom, mapping.map(item.blockTo, -1));
|
|
1383
|
+
let from;
|
|
1384
|
+
let to;
|
|
1385
|
+
if (item.from === item.to) {
|
|
1386
|
+
const side = item.from === item.blockTo || BLOCK_INSERTION_TYPES.has(item.operation.type) ? -1 : 1;
|
|
1387
|
+
from = mapping.map(item.from, side);
|
|
1388
|
+
to = from;
|
|
1389
|
+
} else {
|
|
1390
|
+
from = mapping.map(item.from, 1);
|
|
1391
|
+
to = Math.max(from, mapping.map(item.to, -1));
|
|
1392
|
+
}
|
|
1393
|
+
return {
|
|
1394
|
+
...item,
|
|
1395
|
+
from,
|
|
1396
|
+
to,
|
|
1397
|
+
blockFrom,
|
|
1398
|
+
blockTo,
|
|
1399
|
+
readAt: steps
|
|
1400
|
+
};
|
|
1401
|
+
};
|
|
1402
|
+
/**
|
|
1403
|
+
* The paragraph a pending join makes of the one at `paragraphPos`: where its
|
|
1404
|
+
* break is pending removal while its words stay, accepting runs them into the
|
|
1405
|
+
* paragraph the join leaves, which already carries the joined paragraph's
|
|
1406
|
+
* properties (see `carryParagraphProperties`). Null when the paragraph stands
|
|
1407
|
+
* alone, or goes whole as a pending deletion.
|
|
1408
|
+
*/
|
|
1409
|
+
const pendingJoinSurvivor = ({ doc, paragraphPos, cleanText, noteReferences }) => {
|
|
1410
|
+
const paragraph = doc.nodeAt(paragraphPos);
|
|
1411
|
+
if (paragraph?.type.name !== "paragraph" || !isDeletedPPrMark(paragraph.attrs["pPrMark"]) || isPendingDeletion(doc, paragraphPos, paragraph, cleanText)) return null;
|
|
1412
|
+
const survivor = paragraphLeftAfter({
|
|
1413
|
+
doc,
|
|
1414
|
+
paragraphPos
|
|
1415
|
+
});
|
|
1416
|
+
const left = survivor === null ? null : doc.nodeAt(survivor);
|
|
1417
|
+
if (survivor === null || left?.type !== paragraph.type) return null;
|
|
1418
|
+
return isDeletedPPrMark(left.attrs["pPrMark"]) && isPendingDeletion(doc, survivor, left, buildCleanBlockText(left, survivor, {
|
|
1419
|
+
fieldResults: "text",
|
|
1420
|
+
noteReferences
|
|
1421
|
+
}).text) ? null : survivor;
|
|
1422
|
+
};
|
|
1423
|
+
/**
|
|
1424
|
+
* The insertion with the paragraph its new paragraphs copy properties from as
|
|
1425
|
+
* its anchor: a paragraph a pending join runs into another reads as that one,
|
|
1426
|
+
* as the joined paragraph does when the merge is applied directly. A
|
|
1427
|
+
* suggestion is read against the document as it stands, where the join has
|
|
1428
|
+
* not run anything together yet, so it copies the paragraph it names.
|
|
1429
|
+
*/
|
|
1430
|
+
const insertionFormattingAnchor = ({ item, tr, mode, noteReferences }) => {
|
|
1431
|
+
if (mode === "suggested") return item;
|
|
1432
|
+
const position = sinceRead(tr, item).map(item.blockFrom, 1);
|
|
1433
|
+
const anchor = tr.doc.nodeAt(position);
|
|
1434
|
+
if (anchor?.type !== item.blockNode.type) return item;
|
|
1435
|
+
const survivor = pendingJoinSurvivor({
|
|
1436
|
+
doc: tr.doc,
|
|
1437
|
+
paragraphPos: position,
|
|
1438
|
+
cleanText: buildCleanBlockText(anchor, position, {
|
|
1439
|
+
fieldResults: "text",
|
|
1440
|
+
noteReferences
|
|
1441
|
+
}).text,
|
|
1442
|
+
noteReferences
|
|
1443
|
+
});
|
|
1444
|
+
const source = survivor === null ? null : tr.doc.nodeAt(survivor);
|
|
1445
|
+
return source ? {
|
|
1446
|
+
...item,
|
|
1447
|
+
blockNode: source
|
|
1448
|
+
} : item;
|
|
1449
|
+
};
|
|
1450
|
+
const keepsWords = ({ doc, paragraphPos, noteReferences }) => {
|
|
1451
|
+
const paragraph = doc.nodeAt(paragraphPos);
|
|
1452
|
+
if (paragraph?.type.name !== "paragraph") return false;
|
|
1453
|
+
const cleanText = buildCleanBlockText(paragraph, paragraphPos, {
|
|
1454
|
+
fieldResults: "text",
|
|
1455
|
+
noteReferences
|
|
1456
|
+
}).text;
|
|
1457
|
+
return cleanText.length > 0 && !isPendingDeletion(doc, paragraphPos, paragraph, cleanText);
|
|
1458
|
+
};
|
|
1459
|
+
/**
|
|
1460
|
+
* Whether accepting runs the paragraph into the one its pending join leaves:
|
|
1461
|
+
* it keeps words, or it is empty. One whose words are all pending deletion
|
|
1462
|
+
* goes whole instead.
|
|
1463
|
+
*/
|
|
1464
|
+
const joinsIn = (options) => options.doc.nodeAt(options.paragraphPos)?.content.size === 0 || keepsWords(options);
|
|
1465
|
+
/**
|
|
1466
|
+
* Whether a pending join runs earlier words on to `paragraphPos`, the start of
|
|
1467
|
+
* a paragraph or a place between blocks: the paragraphs before it, back to one
|
|
1468
|
+
* that keeps words, all have their breaks pending removal.
|
|
1469
|
+
*/
|
|
1470
|
+
const joinRunsWordsInto = ({ doc, paragraphPos, noteReferences }) => {
|
|
1471
|
+
const $place = doc.resolve(paragraphPos);
|
|
1472
|
+
let position = paragraphPos;
|
|
1473
|
+
for (let index = $place.index() - 1; index >= 0; index--) {
|
|
1474
|
+
const previous = $place.parent.child(index);
|
|
1475
|
+
position -= previous.nodeSize;
|
|
1476
|
+
if (previous.type.name !== "paragraph" || !isDeletedPPrMark(previous.attrs["pPrMark"])) return false;
|
|
1477
|
+
if (keepsWords({
|
|
1478
|
+
doc,
|
|
1479
|
+
paragraphPos: position,
|
|
1480
|
+
noteReferences
|
|
1481
|
+
})) return true;
|
|
1482
|
+
}
|
|
1483
|
+
return false;
|
|
1484
|
+
};
|
|
1485
|
+
/**
|
|
1486
|
+
* Whether a pending join runs earlier words on to `paragraphPos`, right before
|
|
1487
|
+
* a table. A paragraph put there cannot stand apart once accepted: keeping its
|
|
1488
|
+
* own break, it takes the words, and taking the deleted break from them, it
|
|
1489
|
+
* runs into the table's first cell. Where that cell is itself pending nothing
|
|
1490
|
+
* runs into it, so a paragraph right after the words can take their break and
|
|
1491
|
+
* stand; so can one with a paragraph going whole between it and the table.
|
|
1492
|
+
*/
|
|
1493
|
+
const joinStopsAtTableAfter = (options) => {
|
|
1494
|
+
if (!joinRunsWordsInto(options)) return false;
|
|
1495
|
+
const $place = options.doc.resolve(options.paragraphPos);
|
|
1496
|
+
for (let index = $place.index(); index < $place.parent.childCount; index++) {
|
|
1497
|
+
const next = $place.parent.child(index);
|
|
1498
|
+
if (next.type.name === "paragraph") return false;
|
|
1499
|
+
if (next.type.spec["tableRole"] === "table") {
|
|
1500
|
+
const before = $place.nodeBefore;
|
|
1501
|
+
return !runInCellPending(next) || before !== null && holdsOnlyDeletedContent(before);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
return false;
|
|
1505
|
+
};
|
|
1506
|
+
/**
|
|
1507
|
+
* Where a block goes before and after the paragraph at `paragraphPos`, read
|
|
1508
|
+
* as the joined paragraph a pending join makes of it. A join runs the words
|
|
1509
|
+
* of each paragraph whose break is pending removal into the paragraph it
|
|
1510
|
+
* leaves, so a block put between them would take those words once accepted,
|
|
1511
|
+
* as the same edits applied directly never do. Where a paragraph runs into
|
|
1512
|
+
* one the join leaves that keeps words of its own, a block after any
|
|
1513
|
+
* paragraph of the join goes after the paragraph it leaves, and a block
|
|
1514
|
+
* before that one goes before the first paragraph the join takes in, empty
|
|
1515
|
+
* ones included. A join into a paragraph with no words left (an emptied
|
|
1516
|
+
* final carrier, or one going whole) runs nothing together, nor does a
|
|
1517
|
+
* paragraph going whole with no join running through it: blocks keep their
|
|
1518
|
+
* places.
|
|
1519
|
+
*/
|
|
1520
|
+
const pendingJoinBounds = (options) => {
|
|
1521
|
+
const { doc, paragraphPos, noteReferences } = options;
|
|
1522
|
+
const paragraph = doc.nodeAt(paragraphPos);
|
|
1523
|
+
let after = paragraphPos;
|
|
1524
|
+
if (paragraph?.type.name === "paragraph" && isDeletedPPrMark(paragraph.attrs["pPrMark"]) && (joinsIn(options) || joinRunsWordsInto(options))) {
|
|
1525
|
+
const survivor = paragraphLeftAfter({
|
|
1526
|
+
doc,
|
|
1527
|
+
paragraphPos
|
|
1528
|
+
});
|
|
1529
|
+
if (survivor !== null && keepsWords({
|
|
1530
|
+
doc,
|
|
1531
|
+
paragraphPos: survivor,
|
|
1532
|
+
noteReferences
|
|
1533
|
+
})) after = survivor;
|
|
1534
|
+
}
|
|
1535
|
+
let before = paragraphPos;
|
|
1536
|
+
if (paragraph?.type.name === "paragraph" && keepsWords(options)) {
|
|
1537
|
+
let start = paragraphPos;
|
|
1538
|
+
for (;;) {
|
|
1539
|
+
const previous = doc.resolve(start).nodeBefore;
|
|
1540
|
+
if (previous?.type !== paragraph.type || !isDeletedPPrMark(previous.attrs["pPrMark"])) break;
|
|
1541
|
+
start -= previous.nodeSize;
|
|
1542
|
+
if (joinsIn({
|
|
1543
|
+
doc,
|
|
1544
|
+
paragraphPos: start,
|
|
1545
|
+
noteReferences
|
|
1546
|
+
})) before = start;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return {
|
|
1550
|
+
before,
|
|
1551
|
+
after: endOfParagraphWithItsTextBoxes(doc, after)
|
|
1552
|
+
};
|
|
1553
|
+
};
|
|
1554
|
+
/** Where a block insertion lands as it runs (see `joinSide`). */
|
|
1555
|
+
const insertionAt = ({ item, tr, noteReferences }) => {
|
|
1556
|
+
if (item.joinSide === void 0) return item.from;
|
|
1557
|
+
const position = sinceRead(tr, item).map(item.blockFrom, 1);
|
|
1558
|
+
if (tr.doc.nodeAt(position)?.type.name !== "paragraph") return item.from;
|
|
1559
|
+
return pendingJoinBounds({
|
|
1560
|
+
doc: tr.doc,
|
|
1561
|
+
paragraphPos: position,
|
|
1562
|
+
noteReferences
|
|
1563
|
+
})[item.joinSide];
|
|
1564
|
+
};
|
|
1565
|
+
/**
|
|
1566
|
+
* Where a block insertion lands as it runs, with the comments running across
|
|
1567
|
+
* that boundary. They are the ones the batch read there, so an edit that runs
|
|
1568
|
+
* first cannot move a comment's end out from around the block; but where an
|
|
1569
|
+
* earlier edit moved the join the block follows, the block lands at another
|
|
1570
|
+
* boundary, and carries the comments across that one.
|
|
1571
|
+
*/
|
|
1572
|
+
const insertionPlacement = (options) => {
|
|
1573
|
+
const { item, tr } = options;
|
|
1574
|
+
const at = insertionAt(options);
|
|
1575
|
+
if (item.joinAt === void 0) return {
|
|
1576
|
+
at,
|
|
1577
|
+
comments: item.boundaryComments
|
|
1578
|
+
};
|
|
1579
|
+
return {
|
|
1580
|
+
at,
|
|
1581
|
+
comments: at === tr.mapping.map(item.joinAt, item.joinSide === "after" ? -1 : 1) ? item.boundaryComments : commentsAcrossBlockBoundary(tr.doc, at)
|
|
1582
|
+
};
|
|
1583
|
+
};
|
|
1327
1584
|
/** The comments running across both edges of the table at `tablePosition`: over all of it. */
|
|
1328
1585
|
const commentsAcrossTable = (doc, tablePosition) => {
|
|
1329
1586
|
const table = doc.nodeAt(tablePosition);
|
|
@@ -1351,6 +1608,10 @@ const isInsertedPPrMark = (value) => typeof value === "object" && value !== null
|
|
|
1351
1608
|
const isSuggestedParagraphInsert = (paragraph) => typeof paragraph.attrs["_suggestedInsert"] === "object" && paragraph.attrs["_suggestedInsert"] !== null;
|
|
1352
1609
|
/** A paragraph mark that is a pending deletion, or a relocation's source. */
|
|
1353
1610
|
const isDeletedPPrMark = (value) => typeof value === "object" && value !== null && "kind" in value && (value.kind === "del" || value.kind === "moveFrom");
|
|
1611
|
+
const pendingJoinInsertionRefusal = {
|
|
1612
|
+
reason: "pendingParagraphMarkDeletion",
|
|
1613
|
+
message: "Accepting the pending paragraph mark deletion before this place would run the paragraph together with the words before it or the table after it. Resolve that change first, or insert elsewhere."
|
|
1614
|
+
};
|
|
1354
1615
|
const pendingParagraphMarkPropertyRefusal = {
|
|
1355
1616
|
reason: "pendingParagraphMarkDeletion",
|
|
1356
1617
|
canonicalRefusal: { gap: CANONICAL_GAP.publicPendingParagraphMarkProperties },
|
|
@@ -1614,6 +1875,20 @@ const deletedFinalParagraphAfter = (at, paragraphTypeName, insertedBy) => {
|
|
|
1614
1875
|
}
|
|
1615
1876
|
return null;
|
|
1616
1877
|
};
|
|
1878
|
+
/** Whether every row of `node` is a table row one insertion revision of this batch added. */
|
|
1879
|
+
const isWhollyInsertedTable = (node, batchRevisionIds) => {
|
|
1880
|
+
if (node.type.spec["tableRole"] !== "table" || node.childCount === 0) return false;
|
|
1881
|
+
let revisionId;
|
|
1882
|
+
for (let index = 0; index < node.childCount; index++) {
|
|
1883
|
+
const row = node.child(index);
|
|
1884
|
+
if (row.type.spec["tableRole"] !== "row") return false;
|
|
1885
|
+
const { trIns, trDel } = expectTableRowAttrs(row);
|
|
1886
|
+
if (!trIns || trDel || !batchRevisionIds.has(trIns.revisionId)) return false;
|
|
1887
|
+
revisionId ??= trIns.revisionId;
|
|
1888
|
+
if (trIns.revisionId !== revisionId) return false;
|
|
1889
|
+
}
|
|
1890
|
+
return true;
|
|
1891
|
+
};
|
|
1617
1892
|
/**
|
|
1618
1893
|
* Where the added break of the final paragraph at `at` rotates to: the free
|
|
1619
1894
|
* mark `addedBreakCarrierBefore` finds, or else a paragraph the batch deletes
|
|
@@ -1628,19 +1903,7 @@ const finalBreakCarrierBefore = (at, paragraphTypeName, batchRevisionIds) => {
|
|
|
1628
1903
|
const free = addedBreakCarrierBefore({
|
|
1629
1904
|
at,
|
|
1630
1905
|
paragraphTypeName,
|
|
1631
|
-
canCrossTable: (table) =>
|
|
1632
|
-
if (table.childCount === 0) return false;
|
|
1633
|
-
let revisionId;
|
|
1634
|
-
for (let index = 0; index < table.childCount; index++) {
|
|
1635
|
-
const row = table.child(index);
|
|
1636
|
-
if (row.type.spec["tableRole"] !== "row") return false;
|
|
1637
|
-
const { trIns, trDel } = expectTableRowAttrs(row);
|
|
1638
|
-
if (!trIns || trDel || !batchRevisionIds.has(trIns.revisionId)) return false;
|
|
1639
|
-
revisionId ??= trIns.revisionId;
|
|
1640
|
-
if (trIns.revisionId !== revisionId) return false;
|
|
1641
|
-
}
|
|
1642
|
-
return true;
|
|
1643
|
-
}
|
|
1906
|
+
canCrossTable: (table) => isWhollyInsertedTable(table, batchRevisionIds)
|
|
1644
1907
|
});
|
|
1645
1908
|
if (free) return {
|
|
1646
1909
|
...free,
|
|
@@ -1746,22 +2009,6 @@ const collapsedFinalRunSource = (doc, at, batchRevisionIds) => {
|
|
|
1746
2009
|
}
|
|
1747
2010
|
return source;
|
|
1748
2011
|
};
|
|
1749
|
-
/**
|
|
1750
|
-
* Whether a sibling between a final carrier's predecessor and the carrier
|
|
1751
|
-
* has its break deleted by this batch.
|
|
1752
|
-
*/
|
|
1753
|
-
const crossesBatchDeletion = ({ doc, from, to, batchRevisionIds }) => {
|
|
1754
|
-
const $to = doc.resolve(to);
|
|
1755
|
-
let position = to;
|
|
1756
|
-
for (let index = $to.index() - 1; index >= 0 && position > from; index--) {
|
|
1757
|
-
const node = $to.parent.child(index);
|
|
1758
|
-
position -= node.nodeSize;
|
|
1759
|
-
const mark = node.attrs["pPrMark"];
|
|
1760
|
-
const revisionId = isPlainDeletedPPrMark(mark) ? addedOrDeletedBreakRevisionId(mark) : null;
|
|
1761
|
-
if (revisionId !== null && batchRevisionIds.has(revisionId)) return true;
|
|
1762
|
-
}
|
|
1763
|
-
return false;
|
|
1764
|
-
};
|
|
1765
2012
|
const canRetractFinalBreak = ({ predecessor, target }) => {
|
|
1766
2013
|
if (target.type !== predecessor.type) return false;
|
|
1767
2014
|
return !(JSON.stringify(paragraphPropertiesBeforePendingChanges(target)) !== JSON.stringify(paragraphPropertiesSnapshot(predecessor)) && hasSerializableParagraphPropertyChange(expectParagraphAttrs(predecessor)._propertyChanges));
|
|
@@ -1778,8 +2025,8 @@ const pendingRemovedFinalBreakPositions = (tr, deletions) => {
|
|
|
1778
2025
|
return positions;
|
|
1779
2026
|
};
|
|
1780
2027
|
/** An unoccupied final carrier takes admissible writes from the finished batch. */
|
|
1781
|
-
const finalParagraphCarry = ({
|
|
1782
|
-
const { node: previous
|
|
2028
|
+
const finalParagraphCarry = ({ target, predecessor, batchRevisionIds, styleResolver, numbering }) => {
|
|
2029
|
+
const { node: previous } = predecessor;
|
|
1783
2030
|
const beforeBatch = hasSerializableParagraphPropertyChange(expectParagraphAttrs(target)._propertyChanges) ? resolveParagraphChangeAttrs({
|
|
1784
2031
|
node: previous,
|
|
1785
2032
|
mode: "reject",
|
|
@@ -1788,15 +2035,7 @@ const finalParagraphCarry = ({ tr, at, target, predecessor, batchRevisionIds, st
|
|
|
1788
2035
|
styleResolver,
|
|
1789
2036
|
numbering
|
|
1790
2037
|
}) : null;
|
|
1791
|
-
return {
|
|
1792
|
-
source: beforeBatch ? previous.type.create(beforeBatch, previous.content, previous.marks) : previous,
|
|
1793
|
-
keep: crossesBatchDeletion({
|
|
1794
|
-
doc: tr.doc,
|
|
1795
|
-
from: position + previous.nodeSize,
|
|
1796
|
-
to: at,
|
|
1797
|
-
batchRevisionIds
|
|
1798
|
-
}) ? /* @__PURE__ */ new Set() : propertiesSetInBatch(target, batchRevisionIds)
|
|
1799
|
-
};
|
|
2038
|
+
return { source: beforeBatch ? previous.type.create(beforeBatch, previous.content, previous.marks) : previous };
|
|
1800
2039
|
};
|
|
1801
2040
|
/** Deferred retirement still owns a slot before a later explicit formatting merge. */
|
|
1802
2041
|
const hasDeferredParagraphPropertyWrite = ({ tr, deletions, targets, mergedPropertyTargets, batchRevisionIds, styleResolver, numbering }) => {
|
|
@@ -1819,26 +2058,19 @@ const hasDeferredParagraphPropertyWrite = ({ tr, deletions, targets, mergedPrope
|
|
|
1819
2058
|
}))) continue;
|
|
1820
2059
|
const transfer = mark == null ? finalParagraphCarry({
|
|
1821
2060
|
tr,
|
|
1822
|
-
at,
|
|
1823
2061
|
target,
|
|
1824
2062
|
predecessor,
|
|
1825
2063
|
batchRevisionIds,
|
|
1826
2064
|
styleResolver,
|
|
1827
2065
|
numbering
|
|
1828
|
-
}) : {
|
|
1829
|
-
|
|
1830
|
-
keep: propertiesSetInBatch(writeTarget, batchRevisionIds)
|
|
1831
|
-
};
|
|
1832
|
-
if (JSON.stringify(paragraphPropertiesSnapshot(writeTarget)) !== JSON.stringify(carriedParagraphProperties({
|
|
1833
|
-
...transfer,
|
|
1834
|
-
target: writeTarget
|
|
1835
|
-
}))) return true;
|
|
2066
|
+
}) : { source: predecessor.node };
|
|
2067
|
+
if (JSON.stringify(paragraphPropertiesSnapshot(writeTarget)) !== JSON.stringify(paragraphPropertiesSnapshot(transfer.source))) return true;
|
|
1836
2068
|
}
|
|
1837
2069
|
return false;
|
|
1838
2070
|
};
|
|
1839
2071
|
/** The paragraph a merge leaves owns its properties, even across pending deleted breaks. */
|
|
1840
2072
|
const paragraphPropertyRevisionTargets = ({ item, tr, deletedFinalParagraphs }) => {
|
|
1841
|
-
const position = tr.
|
|
2073
|
+
const position = sinceRead(tr, item).map(item.blockFrom, item.operation.type === "replaceBlock" ? -1 : 1);
|
|
1842
2074
|
const source = tr.doc.nodeAt(position) ?? item.blockNode;
|
|
1843
2075
|
const targets = source.type.name === "paragraph" ? [source] : [];
|
|
1844
2076
|
if (item.operation.type !== "mergeBlockWithNext") return targets;
|
|
@@ -1915,8 +2147,7 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1915
2147
|
author,
|
|
1916
2148
|
date,
|
|
1917
2149
|
...revisionExtras
|
|
1918
|
-
}
|
|
1919
|
-
keep: /* @__PURE__ */ new Set()
|
|
2150
|
+
}
|
|
1920
2151
|
});
|
|
1921
2152
|
if (carried.tracked) {
|
|
1922
2153
|
nextRevisionId++;
|
|
@@ -1950,7 +2181,6 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1950
2181
|
position: at,
|
|
1951
2182
|
...finalParagraphCarry({
|
|
1952
2183
|
tr,
|
|
1953
|
-
at,
|
|
1954
2184
|
target: emptied,
|
|
1955
2185
|
predecessor,
|
|
1956
2186
|
batchRevisionIds,
|
|
@@ -1995,7 +2225,6 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
1995
2225
|
doc: tr.doc,
|
|
1996
2226
|
paragraphPos: position
|
|
1997
2227
|
}) ?? position : position;
|
|
1998
|
-
const carryTarget = tr.doc.nodeAt(carryPosition) ?? following;
|
|
1999
2228
|
const revisionId = nextRevisionId;
|
|
2000
2229
|
if (carryParagraphProperties({
|
|
2001
2230
|
tr,
|
|
@@ -2008,8 +2237,7 @@ const withRetiredFinalParagraphs = ({ tr, deletions, mergedPropertyTargets, appl
|
|
|
2008
2237
|
author,
|
|
2009
2238
|
date,
|
|
2010
2239
|
...revisionExtras
|
|
2011
|
-
}
|
|
2012
|
-
keep: propertiesSetInBatch(carryTarget, batchRevisionIds)
|
|
2240
|
+
}
|
|
2013
2241
|
}).tracked) {
|
|
2014
2242
|
nextRevisionId++;
|
|
2015
2243
|
addedRevisions.push({
|
|
@@ -2069,6 +2297,7 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
2069
2297
|
let carriedDeletedFormatting = null;
|
|
2070
2298
|
parent.forEach((node, offset) => {
|
|
2071
2299
|
const position = contentStart + offset;
|
|
2300
|
+
if (isWhollyInsertedTable(node, batchRevisionIds)) return;
|
|
2072
2301
|
if (node.type.name !== "paragraph") {
|
|
2073
2302
|
previous = null;
|
|
2074
2303
|
carriedDeletedMark = null;
|
|
@@ -2215,25 +2444,38 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
2215
2444
|
const liveCurrent = next.doc.nodeAt(current.position);
|
|
2216
2445
|
if (!liveCurrent || liveCurrent.type.name !== final.type.name) panic("A final-mark rotation lost its paragraph", { position: current.position });
|
|
2217
2446
|
const existing = expectParagraphAttrs(liveCurrent)._propertyChanges;
|
|
2218
|
-
if (hasSerializableParagraphPropertyChange(existing))
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
info: {
|
|
2225
|
-
id: revisionId,
|
|
2226
|
-
author,
|
|
2227
|
-
date,
|
|
2228
|
-
...initials ? { initials } : {}
|
|
2229
|
-
},
|
|
2447
|
+
if (hasSerializableParagraphPropertyChange(existing)) {
|
|
2448
|
+
const serialized = (existing ?? []).filter(({ info }) => info.provenance !== "suggested");
|
|
2449
|
+
if (!serialized.every(({ info }) => batchRevisionIds.has(info.id))) panic("A final-mark rotation met a w:pPrChange from outside", { position: current.position });
|
|
2450
|
+
const restated = [];
|
|
2451
|
+
for (const change of existing ?? []) restated.push(serialized.includes(change) ? {
|
|
2452
|
+
...change,
|
|
2230
2453
|
previousFormatting
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
}
|
|
2454
|
+
} : change);
|
|
2455
|
+
next = next.setNodeMarkup(current.position, void 0, {
|
|
2456
|
+
...liveCurrent.attrs,
|
|
2457
|
+
_propertyChanges: restated
|
|
2458
|
+
});
|
|
2459
|
+
} else {
|
|
2460
|
+
const revisionId = nextRevisionId++;
|
|
2461
|
+
next = next.setNodeMarkup(current.position, void 0, {
|
|
2462
|
+
...liveCurrent.attrs,
|
|
2463
|
+
_propertyChanges: [...Array.isArray(existing) ? existing : [], {
|
|
2464
|
+
type: "paragraphPropertyChange",
|
|
2465
|
+
info: {
|
|
2466
|
+
id: revisionId,
|
|
2467
|
+
author,
|
|
2468
|
+
date,
|
|
2469
|
+
...initials ? { initials } : {}
|
|
2470
|
+
},
|
|
2471
|
+
previousFormatting
|
|
2472
|
+
}]
|
|
2473
|
+
});
|
|
2474
|
+
synthesizedRevisions.push({
|
|
2475
|
+
ownerRevisionId,
|
|
2476
|
+
revisionId
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2237
2479
|
}
|
|
2238
2480
|
if (cancels) {
|
|
2239
2481
|
const deleted = next.doc.nodeAt(previous.position);
|
|
@@ -2490,7 +2732,7 @@ const buildInsertedParagraphs = ({ item, schema, formattingFromStyle, mode, auth
|
|
|
2490
2732
|
nextRevisionId
|
|
2491
2733
|
};
|
|
2492
2734
|
};
|
|
2493
|
-
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: requestedOperations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates, replacementBackground = "clear", undefinedReferences }) => {
|
|
2735
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: requestedOperations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates, replacementBackground = "clear", undefinedReferences, numberingIdsElsewhere }) => {
|
|
2494
2736
|
seedRevisionIdsFromDoc(view.state.doc);
|
|
2495
2737
|
const applied = [];
|
|
2496
2738
|
const skipped = [];
|
|
@@ -2571,7 +2813,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2571
2813
|
liveBlocks,
|
|
2572
2814
|
liveBlocksByParaId,
|
|
2573
2815
|
noteReferences,
|
|
2574
|
-
doc: view.state.doc
|
|
2816
|
+
doc: view.state.doc,
|
|
2817
|
+
mode
|
|
2575
2818
|
});
|
|
2576
2819
|
if (resolution.type === "skip") {
|
|
2577
2820
|
skipped.push({
|
|
@@ -2652,7 +2895,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2652
2895
|
"splitTableCell"
|
|
2653
2896
|
].includes(operation.type) ? removedBlocksBy.get(targetBlockId) : void 0;
|
|
2654
2897
|
const continuesMergeChain = operation.type === "mergeBlockWithNext" && removedBy?.type === "mergeBlockWithNext";
|
|
2655
|
-
|
|
2898
|
+
const formatsBeforeRemoval = operation.type === "setBlockParagraphProperties" && (removedBy?.type === "mergeBlockWithNext" || removedBy?.type === "deleteBlock");
|
|
2899
|
+
if (removedBy !== void 0 && !continuesMergeChain && !formatsBeforeRemoval) {
|
|
2656
2900
|
skipped.push({
|
|
2657
2901
|
id: operation.id,
|
|
2658
2902
|
reason: "overlappingOperation",
|
|
@@ -2692,6 +2936,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2692
2936
|
});
|
|
2693
2937
|
resolved.push({
|
|
2694
2938
|
...resolution.operation,
|
|
2939
|
+
batchBlockFrom: resolution.operation.blockFrom,
|
|
2695
2940
|
originalIndex: index,
|
|
2696
2941
|
...commentText !== void 0 && { commentText }
|
|
2697
2942
|
});
|
|
@@ -2813,18 +3058,71 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2813
3058
|
inputIndex: item.originalIndex
|
|
2814
3059
|
};
|
|
2815
3060
|
};
|
|
2816
|
-
const
|
|
3061
|
+
const batchDeletions = new Set(executableResolved.flatMap((item) => item.operation.type === "deleteBlock" ? [item.blockFrom] : []));
|
|
3062
|
+
const blockAccess = (item) => {
|
|
3063
|
+
const doc = view.state.doc;
|
|
3064
|
+
if (item.operation.type === "mergeBlockWithNext") {
|
|
3065
|
+
const next = paragraphJoinAfter(doc, item.blockTo);
|
|
3066
|
+
return {
|
|
3067
|
+
addresses: [item.blockFrom],
|
|
3068
|
+
removes: next ? [next.nextPosition] : []
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
if (item.operation.type === "deleteTableRow" || item.operation.type === "deleteTable") return {
|
|
3072
|
+
addresses: [],
|
|
3073
|
+
removes: [item.blockFrom]
|
|
3074
|
+
};
|
|
3075
|
+
if (item.tableColumnInsertion ?? item.tableColumnDeletion ?? item.tableCellMerge ?? item.tableCellSplit) return {
|
|
3076
|
+
addresses: [],
|
|
3077
|
+
removes: []
|
|
3078
|
+
};
|
|
3079
|
+
if (item.operation.type !== "deleteBlock") return {
|
|
3080
|
+
addresses: [item.blockFrom],
|
|
3081
|
+
removes: []
|
|
3082
|
+
};
|
|
3083
|
+
const at = doc.resolve(item.blockFrom);
|
|
3084
|
+
let hasKeptParagraphBefore = false;
|
|
3085
|
+
let position = at.pos;
|
|
3086
|
+
for (let index = at.index() - 1; index >= 0; index -= 1) {
|
|
3087
|
+
const previous = at.parent.child(index);
|
|
3088
|
+
position -= previous.nodeSize;
|
|
3089
|
+
if (previous.type !== item.blockNode.type) break;
|
|
3090
|
+
if (!batchDeletions.has(position)) {
|
|
3091
|
+
hasKeptParagraphBefore = true;
|
|
3092
|
+
break;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
const appended = executableResolved.some((other) => other.blockFrom === item.blockFrom && (other.operation.type === "insertAfterBlock" || other.operation.type === "insertTable" && other.operation.position !== "before"));
|
|
3096
|
+
return {
|
|
3097
|
+
addresses: [],
|
|
3098
|
+
removes: deletionRemovesBlock({
|
|
3099
|
+
endsContainer: !appended && paragraphEndsItsContainer(at, item.blockNode.type.name),
|
|
3100
|
+
hasKeptParagraphBefore
|
|
3101
|
+
}) ? [item.blockFrom] : []
|
|
3102
|
+
};
|
|
3103
|
+
};
|
|
3104
|
+
const groupRuns = (items) => groupInsertionRuns(items, (candidate) => isBatchableParagraphInsertion(candidate, mode) ? candidate.from : null, (candidate) => candidate.operation.type === "setBlockParagraphProperties");
|
|
3105
|
+
const executionOrder = orderAddressingBeforeRemoval(groupRuns(orderBatchOperations(executableResolved, batchAddress)), (run) => {
|
|
3106
|
+
const access = run.map(blockAccess);
|
|
3107
|
+
return {
|
|
3108
|
+
addresses: access.flatMap(({ addresses }) => addresses),
|
|
3109
|
+
removes: access.flatMap(({ removes }) => removes)
|
|
3110
|
+
};
|
|
3111
|
+
}).flat();
|
|
2817
3112
|
const allocateNewLists = ({ items }) => {
|
|
2818
|
-
const allocated = resolveNewListOperations(items.map(({ operation }) => operation), numbering);
|
|
3113
|
+
const allocated = resolveNewListOperations(items.map(({ operation }) => operation), numbering, numberingIdsElsewhere);
|
|
2819
3114
|
numbering = allocated.numbering;
|
|
2820
3115
|
for (const [index, candidate] of items.entries()) candidate.operation = allocated.operations[index] ?? panic("A will-apply operation lost its new-list allocation", { id: candidate.operation.id });
|
|
2821
3116
|
};
|
|
2822
|
-
|
|
3117
|
+
/** Paragraphs a retracted break of this batch joined, to the one it left. */
|
|
3118
|
+
const joinedInto = /* @__PURE__ */ new Map();
|
|
3119
|
+
const executionGroups = groupRuns(executionOrder);
|
|
2823
3120
|
for (const executionGroup of executionGroups) {
|
|
2824
3121
|
const insertionState = tr;
|
|
2825
|
-
const insertionRun = executionGroup.flatMap((
|
|
3122
|
+
const insertionRun = executionGroup.flatMap((resolvedCandidate) => {
|
|
3123
|
+
const candidate = readNow(resolvedCandidate, insertionState);
|
|
2826
3124
|
if (candidate.operation.type !== "insertAfterBlock" && candidate.operation.type !== "insertBeforeBlock") return [candidate];
|
|
2827
|
-
const liveCandidate = insertionWithLiveAnchor(candidate, insertionState);
|
|
3125
|
+
const liveCandidate = insertionWithLiveAnchor(candidate, insertionState, joinedInto);
|
|
2828
3126
|
if (!liveCandidate) {
|
|
2829
3127
|
if (!isSuggested) return [candidate];
|
|
2830
3128
|
if (candidate.operation.moveId !== void 0) refusedMoveIds.add(candidate.operation.moveId);
|
|
@@ -2835,7 +3133,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2835
3133
|
return [];
|
|
2836
3134
|
}
|
|
2837
3135
|
if (isSuggested) {
|
|
2838
|
-
const blockFrom = insertionState.
|
|
3136
|
+
const blockFrom = sinceRead(insertionState, candidate).map(candidate.blockFrom, 1);
|
|
2839
3137
|
if (isPendingDeletion(insertionState.doc, blockFrom, liveCandidate.blockNode, buildCleanBlockText(liveCandidate.blockNode, blockFrom, {
|
|
2840
3138
|
fieldResults: "text",
|
|
2841
3139
|
noteReferences
|
|
@@ -2887,10 +3185,16 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2887
3185
|
const runApplied = [];
|
|
2888
3186
|
for (const insertion of insertionRun) {
|
|
2889
3187
|
const insertionSuggestionId = isSuggested ? insertion.operation.suggestionId ?? insertion.operation.id : null;
|
|
2890
|
-
const
|
|
3188
|
+
const formatted = insertionFormattingAnchor({
|
|
2891
3189
|
item: insertion,
|
|
3190
|
+
tr,
|
|
3191
|
+
mode,
|
|
3192
|
+
noteReferences
|
|
3193
|
+
});
|
|
3194
|
+
const built = buildInsertedParagraphs({
|
|
3195
|
+
item: formatted,
|
|
2892
3196
|
schema: view.state.schema,
|
|
2893
|
-
formattingFromStyle: formattingFromStyleForInsertion(
|
|
3197
|
+
formattingFromStyle: formattingFromStyleForInsertion(formatted),
|
|
2894
3198
|
mode,
|
|
2895
3199
|
author,
|
|
2896
3200
|
date,
|
|
@@ -2918,7 +3222,11 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
2918
3222
|
for (const group of nodeGroups.toReversed()) for (const node of group) nodes.push(node);
|
|
2919
3223
|
tr = insertBlocksInsideComments({
|
|
2920
3224
|
tr,
|
|
2921
|
-
at:
|
|
3225
|
+
at: insertionAt({
|
|
3226
|
+
item,
|
|
3227
|
+
tr,
|
|
3228
|
+
noteReferences
|
|
3229
|
+
}),
|
|
2922
3230
|
blocks: nodes,
|
|
2923
3231
|
...item.boundaryComments !== void 0 && { carriedComments: item.boundaryComments }
|
|
2924
3232
|
});
|
|
@@ -3115,7 +3423,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3115
3423
|
case "replaceBlock": {
|
|
3116
3424
|
if (!isResolvedReplaceBlockOperation(item)) panic("Resolved replaceBlock operation lost its impact discriminator");
|
|
3117
3425
|
const { changesStyle, changesText } = REPLACE_BLOCK_IMPACT[item.replaceBlockImpact];
|
|
3118
|
-
if (changesStyle && isDeletedPPrMark(tr.doc.nodeAt(tr.
|
|
3426
|
+
if (changesStyle && isDeletedPPrMark(tr.doc.nodeAt(sinceRead(tr, item).map(item.blockFrom, -1))?.attrs["pPrMark"])) {
|
|
3119
3427
|
skipped.push({
|
|
3120
3428
|
id: item.operation.id,
|
|
3121
3429
|
...pendingParagraphMarkPropertyRefusal
|
|
@@ -3286,10 +3594,16 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3286
3594
|
items: [item]
|
|
3287
3595
|
});
|
|
3288
3596
|
assertNewListsAllocated(item.operation);
|
|
3289
|
-
const
|
|
3597
|
+
const formatted = insertionFormattingAnchor({
|
|
3290
3598
|
item,
|
|
3599
|
+
tr,
|
|
3600
|
+
mode,
|
|
3601
|
+
noteReferences
|
|
3602
|
+
});
|
|
3603
|
+
const built = buildInsertedParagraphs({
|
|
3604
|
+
item: formatted,
|
|
3291
3605
|
schema: view.state.schema,
|
|
3292
|
-
formattingFromStyle: formattingFromStyleForInsertion(
|
|
3606
|
+
formattingFromStyle: formattingFromStyleForInsertion(formatted),
|
|
3293
3607
|
mode,
|
|
3294
3608
|
author,
|
|
3295
3609
|
date,
|
|
@@ -3303,11 +3617,16 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3303
3617
|
});
|
|
3304
3618
|
operationRevisionSeed = built.nextRevisionId;
|
|
3305
3619
|
if (built.revisionIds.length > 0) appliedRevisionIds = built.revisionIds;
|
|
3620
|
+
const placement = insertionPlacement({
|
|
3621
|
+
item,
|
|
3622
|
+
tr,
|
|
3623
|
+
noteReferences
|
|
3624
|
+
});
|
|
3306
3625
|
tr = insertBlocksInsideComments({
|
|
3307
3626
|
tr,
|
|
3308
|
-
at:
|
|
3627
|
+
at: placement.at,
|
|
3309
3628
|
blocks: built.nodes,
|
|
3310
|
-
...
|
|
3629
|
+
...placement.comments !== void 0 && { carriedComments: placement.comments }
|
|
3311
3630
|
});
|
|
3312
3631
|
break;
|
|
3313
3632
|
}
|
|
@@ -3345,7 +3664,11 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3345
3664
|
}, signatureTable.content, signatureTable.marks);
|
|
3346
3665
|
appliedRevisionIds = [revisionId];
|
|
3347
3666
|
}
|
|
3348
|
-
tr = tr.insert(
|
|
3667
|
+
tr = tr.insert(insertionAt({
|
|
3668
|
+
item,
|
|
3669
|
+
tr,
|
|
3670
|
+
noteReferences
|
|
3671
|
+
}), node);
|
|
3349
3672
|
break;
|
|
3350
3673
|
}
|
|
3351
3674
|
case "insertTableRow": {
|
|
@@ -3617,7 +3940,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3617
3940
|
to: item.to,
|
|
3618
3941
|
mark: deletionMark
|
|
3619
3942
|
});
|
|
3620
|
-
const atomRanges = undeletedContentAtomRanges(tr, item.blockFrom);
|
|
3943
|
+
const atomRanges = undeletedContentAtomRanges(tr, item.blockFrom, item.readAt ?? 0);
|
|
3621
3944
|
for (const { from, to } of atomRanges) addTrackedDeletionMark({
|
|
3622
3945
|
insertionPolicy: "preserve-pending",
|
|
3623
3946
|
tr,
|
|
@@ -3625,7 +3948,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3625
3948
|
to,
|
|
3626
3949
|
mark: deletionMark
|
|
3627
3950
|
});
|
|
3628
|
-
for (const anchorId of textBoxAnchorIdsBetween(tr.doc, tr.
|
|
3951
|
+
for (const anchorId of textBoxAnchorIdsBetween(tr.doc, sinceRead(tr, item).map(item.blockFrom), sinceRead(tr, item).map(item.blockTo))) deletedTextBoxAnchorIds.add(anchorId);
|
|
3629
3952
|
encloseWholeControls({
|
|
3630
3953
|
tr,
|
|
3631
3954
|
from: item.blockFrom,
|
|
@@ -3634,7 +3957,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3634
3957
|
});
|
|
3635
3958
|
const inlineRevisionApplied = item.from < item.to || atomRanges.length > 0;
|
|
3636
3959
|
appliedRevisionIds = inlineRevisionApplied ? [revisionId] : [];
|
|
3637
|
-
let markPosition = tr.
|
|
3960
|
+
let markPosition = sinceRead(tr, item).map(item.blockFrom);
|
|
3638
3961
|
const markOwner = tr.doc.nodeAt(markPosition);
|
|
3639
3962
|
const across = markOwner && !isPairedMove(item.operation.moveId) && addedBreakRevisionId(markOwner.attrs["pPrMark"]) !== null ? paragraphAcrossVanishingTables(tr.doc, markPosition) : null;
|
|
3640
3963
|
if (across !== null) markPosition = moveParagraphPastTables(tr, markPosition, across);
|
|
@@ -3729,10 +4052,15 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3729
4052
|
continue;
|
|
3730
4053
|
}
|
|
3731
4054
|
if (producesTrackedChanges) appliedRevisionIds = [operationRevisionSeed++];
|
|
3732
|
-
const carriedComments = tableAfterDeletedSplit.get(item.blockFrom);
|
|
3733
|
-
const usesRetractedSplit = carriedComments !== void 0 && item.from === item.blockTo;
|
|
3734
|
-
|
|
3735
|
-
|
|
4055
|
+
const carriedComments = tableAfterDeletedSplit.get(item.batchBlockFrom ?? item.blockFrom);
|
|
4056
|
+
const usesRetractedSplit = carriedComments !== void 0 && item.from === item.blockTo && liveBlockResult(tr, item, 1).deletedAcross;
|
|
4057
|
+
const placement = insertionPlacement({
|
|
4058
|
+
item,
|
|
4059
|
+
tr,
|
|
4060
|
+
noteReferences
|
|
4061
|
+
});
|
|
4062
|
+
let at = usesRetractedSplit ? sinceRead(tr, item).map(item.blockFrom, -1) : placement.at;
|
|
4063
|
+
let insertionComments = carriedComments ?? placement.comments;
|
|
3736
4064
|
if (!usesRetractedSplit) {
|
|
3737
4065
|
const boundary = tr.doc.resolve(at);
|
|
3738
4066
|
const before = boundary.nodeBefore;
|
|
@@ -3857,7 +4185,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3857
4185
|
break;
|
|
3858
4186
|
}
|
|
3859
4187
|
case "setBlockParagraphProperties": {
|
|
3860
|
-
const mapped = tr
|
|
4188
|
+
const mapped = liveBlockResult(tr, item);
|
|
3861
4189
|
const liveBlock = mapped.deletedAcross ? null : tr.doc.nodeAt(mapped.pos);
|
|
3862
4190
|
const paraId = item.blockNode.attrs["paraId"];
|
|
3863
4191
|
if (!liveBlock || liveBlock.type !== item.blockNode.type || typeof paraId === "string" && liveBlock.attrs["paraId"] !== paraId) {
|
|
@@ -3954,6 +4282,19 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3954
4282
|
kind: "ins",
|
|
3955
4283
|
info
|
|
3956
4284
|
});
|
|
4285
|
+
const second = tr.doc.nodeAt(item.from + 1);
|
|
4286
|
+
const survivor = mode === "tracked-changes" && isDeletedPPrMark(second?.attrs["pPrMark"]) ? paragraphLeftAfter({
|
|
4287
|
+
doc: tr.doc,
|
|
4288
|
+
paragraphPos: item.from + 1
|
|
4289
|
+
}) : null;
|
|
4290
|
+
const source = survivor === null ? null : tr.doc.nodeAt(survivor);
|
|
4291
|
+
if (source) tr = carryParagraphProperties({
|
|
4292
|
+
tr,
|
|
4293
|
+
position: item.blockFrom,
|
|
4294
|
+
source,
|
|
4295
|
+
styleResolver,
|
|
4296
|
+
numbering
|
|
4297
|
+
}).tr;
|
|
3957
4298
|
}
|
|
3958
4299
|
const allocateSplitParagraphPropertyRevisionInfo = () => ({
|
|
3959
4300
|
id: operationRevisionSeed++,
|
|
@@ -3987,7 +4328,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
3987
4328
|
break;
|
|
3988
4329
|
}
|
|
3989
4330
|
case "mergeBlockWithNext": {
|
|
3990
|
-
const mapped = tr
|
|
4331
|
+
const mapped = liveBlockResult(tr, item, 1);
|
|
3991
4332
|
const live = mapped.deletedAcross ? null : tr.doc.nodeAt(mapped.pos);
|
|
3992
4333
|
const paraId = item.blockNode.attrs["paraId"];
|
|
3993
4334
|
if (!live || live.type !== item.blockNode.type || typeof paraId === "string" && live.attrs["paraId"] !== paraId) {
|
|
@@ -4102,7 +4443,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
4102
4443
|
const nextPosition = join.nextPosition + separator.length;
|
|
4103
4444
|
const next = tr.doc.nodeAt(nextPosition);
|
|
4104
4445
|
if (!paragraph || !next) panic("A resolved merge lost its paragraphs");
|
|
4105
|
-
if (
|
|
4446
|
+
if (addedBreakRevisionId(paragraph.attrs["pPrMark"]) !== null) {
|
|
4106
4447
|
joinAtParagraphMark({
|
|
4107
4448
|
tr,
|
|
4108
4449
|
paragraphPos: blockFrom,
|
|
@@ -4110,6 +4451,9 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
4110
4451
|
next,
|
|
4111
4452
|
styleResolver
|
|
4112
4453
|
});
|
|
4454
|
+
const firstId = paragraph.attrs["paraId"];
|
|
4455
|
+
const nextId = next.attrs["paraId"];
|
|
4456
|
+
if (typeof firstId === "string" && typeof nextId === "string") joinedInto.set(firstId, nextId);
|
|
4113
4457
|
const joinedMark = tr.doc.nodeAt(blockFrom)?.attrs["pPrMark"];
|
|
4114
4458
|
const removedBreakPositions = pendingRemovedFinalBreakPositions(tr, deletedFinalParagraphs);
|
|
4115
4459
|
const carryPos = isDeletedPPrMark(joinedMark) || removedBreakPositions.has(blockFrom) ? paragraphLeftAfter({
|
|
@@ -4348,8 +4692,16 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations: reques
|
|
|
4348
4692
|
nextRevisionId: revisionSeed
|
|
4349
4693
|
};
|
|
4350
4694
|
};
|
|
4351
|
-
const applyFolioAIEditOperations = (options) => applyFolioAIEditOperationsInternal(
|
|
4352
|
-
|
|
4695
|
+
const applyFolioAIEditOperations = (options) => applyFolioAIEditOperationsInternal({
|
|
4696
|
+
...options,
|
|
4697
|
+
numberingIdsElsewhere: NO_NUMBERING_IDS_IN_USE
|
|
4698
|
+
});
|
|
4699
|
+
/** {@link applyFolioAIEditOperations} for a caller holding every story. Internal. */
|
|
4700
|
+
const applyFolioAIEditOperationsInPackage = (options) => applyFolioAIEditOperationsInternal(options);
|
|
4701
|
+
const previewFolioAIEditOperations = (options) => previewFolioAIEditOperationsWithResult({
|
|
4702
|
+
...options,
|
|
4703
|
+
numberingIdsElsewhere: NO_NUMBERING_IDS_IN_USE
|
|
4704
|
+
}).outcome;
|
|
4353
4705
|
/**
|
|
4354
4706
|
* A preview together with the document it would leave and the provisional
|
|
4355
4707
|
* comment ids it placed, for a caller that checks the result before reporting
|
|
@@ -5228,7 +5580,7 @@ const refuseNoteReferenceEdit = (item, noteReferences) => {
|
|
|
5228
5580
|
reason: "protectedReference"
|
|
5229
5581
|
} : null;
|
|
5230
5582
|
};
|
|
5231
|
-
const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByParaId, noteReferences, doc }) => {
|
|
5583
|
+
const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByParaId, noteReferences, doc, mode }) => {
|
|
5232
5584
|
const blockId = operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange" ? operation.range.blockId : operation.blockId;
|
|
5233
5585
|
const primaryBlock = resolveStableBlock({
|
|
5234
5586
|
snapshot,
|
|
@@ -5243,6 +5595,28 @@ const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByP
|
|
|
5243
5595
|
type: "skip",
|
|
5244
5596
|
reason: "preconditionFailed"
|
|
5245
5597
|
};
|
|
5598
|
+
const joinBounds = () => mode === "suggested" ? {
|
|
5599
|
+
before: blockFrom,
|
|
5600
|
+
after: endOfParagraphWithItsTextBoxes(doc, blockFrom)
|
|
5601
|
+
} : pendingJoinBounds({
|
|
5602
|
+
doc,
|
|
5603
|
+
paragraphPos: blockFrom,
|
|
5604
|
+
noteReferences
|
|
5605
|
+
});
|
|
5606
|
+
const beside = (side) => {
|
|
5607
|
+
const plain = side === "after" ? endOfParagraphWithItsTextBoxes(doc, blockFrom) : blockFrom;
|
|
5608
|
+
const at = joinBounds()[side];
|
|
5609
|
+
const from = side === "after" ? plain : at;
|
|
5610
|
+
return at === plain ? {
|
|
5611
|
+
from,
|
|
5612
|
+
at
|
|
5613
|
+
} : {
|
|
5614
|
+
from,
|
|
5615
|
+
at,
|
|
5616
|
+
joinSide: side,
|
|
5617
|
+
joinAt: at
|
|
5618
|
+
};
|
|
5619
|
+
};
|
|
5246
5620
|
if (operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange") {
|
|
5247
5621
|
const { startOffset, endOffset, selectedTextHash } = operation.range;
|
|
5248
5622
|
if (cleanBlock.offsets[startOffset] === void 0 || cleanBlock.offsets[endOffset] === void 0) return {
|
|
@@ -5291,11 +5665,20 @@ const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByP
|
|
|
5291
5665
|
}
|
|
5292
5666
|
if (operation.type === "insertAfterBlock" || operation.type === "insertBeforeBlock") {
|
|
5293
5667
|
const tableBoundary = findOutermostTableBoundary(doc, blockFrom);
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5668
|
+
const side = operation.type === "insertAfterBlock" ? "after" : "before";
|
|
5669
|
+
const placed = tableBoundary ? {
|
|
5670
|
+
from: tableBoundary[side],
|
|
5671
|
+
at: tableBoundary[side]
|
|
5672
|
+
} : beside(side);
|
|
5673
|
+
if (joinStopsAtTableAfter({
|
|
5674
|
+
doc,
|
|
5675
|
+
paragraphPos: placed.at,
|
|
5676
|
+
noteReferences
|
|
5677
|
+
})) return {
|
|
5678
|
+
type: "skip",
|
|
5679
|
+
...pendingJoinInsertionRefusal
|
|
5680
|
+
};
|
|
5681
|
+
const insertFrom = placed.from;
|
|
5299
5682
|
return {
|
|
5300
5683
|
type: "resolved",
|
|
5301
5684
|
operation: {
|
|
@@ -5305,8 +5688,12 @@ const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByP
|
|
|
5305
5688
|
blockFrom,
|
|
5306
5689
|
blockTo,
|
|
5307
5690
|
blockNode,
|
|
5308
|
-
insertTexts,
|
|
5309
|
-
boundaryComments: commentsAcrossBlockBoundary(doc,
|
|
5691
|
+
insertTexts: operation.lineBreakMode !== "inline" && LINE_BREAK_PATTERN.test(operation.text) ? splitInsertParagraphTexts(operation.text) : [operation.text],
|
|
5692
|
+
boundaryComments: commentsAcrossBlockBoundary(doc, placed.at),
|
|
5693
|
+
...placed.joinSide !== void 0 && {
|
|
5694
|
+
joinSide: placed.joinSide,
|
|
5695
|
+
joinAt: placed.joinAt
|
|
5696
|
+
}
|
|
5310
5697
|
}
|
|
5311
5698
|
};
|
|
5312
5699
|
}
|
|
@@ -5317,18 +5704,23 @@ const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByP
|
|
|
5317
5704
|
};
|
|
5318
5705
|
const position = operation.position ?? "after";
|
|
5319
5706
|
const tableBoundary = findOutermostTableBoundary(doc, blockFrom);
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5707
|
+
const placed = tableBoundary ? {
|
|
5708
|
+
from: tableBoundary[position],
|
|
5709
|
+
at: tableBoundary[position]
|
|
5710
|
+
} : beside(position);
|
|
5323
5711
|
return {
|
|
5324
5712
|
type: "resolved",
|
|
5325
5713
|
operation: {
|
|
5326
5714
|
operation,
|
|
5327
|
-
from:
|
|
5328
|
-
to:
|
|
5715
|
+
from: placed.from,
|
|
5716
|
+
to: placed.from,
|
|
5329
5717
|
blockFrom,
|
|
5330
5718
|
blockTo,
|
|
5331
|
-
blockNode
|
|
5719
|
+
blockNode,
|
|
5720
|
+
...placed.joinSide !== void 0 && {
|
|
5721
|
+
joinSide: placed.joinSide,
|
|
5722
|
+
joinAt: placed.joinAt
|
|
5723
|
+
}
|
|
5332
5724
|
}
|
|
5333
5725
|
};
|
|
5334
5726
|
}
|
|
@@ -5549,21 +5941,26 @@ const resolveOperationTarget = ({ snapshot, operation, liveBlocks, liveBlocksByP
|
|
|
5549
5941
|
};
|
|
5550
5942
|
}
|
|
5551
5943
|
if (operation.type === "insertTable") {
|
|
5552
|
-
const
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5944
|
+
const side = (operation.position ?? "after") === "after" ? "after" : "before";
|
|
5945
|
+
const tableBoundary = findOutermostTableBoundary(doc, blockFrom);
|
|
5946
|
+
const placed = tableBoundary ? {
|
|
5947
|
+
from: tableBoundary[side],
|
|
5948
|
+
at: tableBoundary[side]
|
|
5949
|
+
} : beside(side);
|
|
5557
5950
|
return {
|
|
5558
5951
|
type: "resolved",
|
|
5559
5952
|
operation: {
|
|
5560
5953
|
operation,
|
|
5561
|
-
from:
|
|
5562
|
-
to:
|
|
5954
|
+
from: placed.from,
|
|
5955
|
+
to: placed.from,
|
|
5563
5956
|
blockFrom,
|
|
5564
5957
|
blockTo,
|
|
5565
5958
|
blockNode,
|
|
5566
|
-
boundaryComments: commentsAcrossBlockBoundary(doc,
|
|
5959
|
+
boundaryComments: commentsAcrossBlockBoundary(doc, placed.at),
|
|
5960
|
+
...placed.joinSide !== void 0 && {
|
|
5961
|
+
joinSide: placed.joinSide,
|
|
5962
|
+
joinAt: placed.joinAt
|
|
5963
|
+
}
|
|
5567
5964
|
}
|
|
5568
5965
|
};
|
|
5569
5966
|
}
|
|
@@ -5947,4 +6344,4 @@ const getOperationQuote = (operation) => {
|
|
|
5947
6344
|
if (operation.type === "replaceInBlock") return operation.find;
|
|
5948
6345
|
if (operation.type === "commentOnBlock") return operation.quote;
|
|
5949
6346
|
};
|
|
5950
|
-
export { applyFolioAIEditOperations, insertCleanText, previewFolioAIEditOperations, previewFolioAIEditOperationsWithResult };
|
|
6347
|
+
export { applyFolioAIEditOperations, applyFolioAIEditOperationsInPackage, insertCleanText, previewFolioAIEditOperations, previewFolioAIEditOperationsWithResult };
|