@stll/folio-core 0.37.0 → 0.37.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.
- package/dist/ai-edits/apply.js +157 -84
- package/dist/ai-edits/types.d.ts +15 -0
- package/dist/compare/plan.js +32 -2
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/document-operations.d.ts +2 -2
- package/dist/document-operations.js +44 -14
- package/dist/internal/headlessRevisionResolution.d.ts +2 -2
- package/dist/internal/headlessRevisionResolution.js +30 -9
- package/dist/prosemirror/commands/comments.js +2 -25
- package/package.json +1 -1
package/dist/ai-edits/apply.js
CHANGED
|
@@ -135,7 +135,12 @@ const hasRepresentableCommentAnchor = (item, mode) => {
|
|
|
135
135
|
default: return item.from < item.to;
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
|
-
const writesParagraphPropertyChange = (item) =>
|
|
138
|
+
const writesParagraphPropertyChange = (item) => {
|
|
139
|
+
if (item.operation.type === "setBlockParagraphProperties") return true;
|
|
140
|
+
if (item.operation.type === "splitBlock") return item.operation.firstParagraphProperties !== void 0 || item.operation.secondParagraphProperties !== void 0;
|
|
141
|
+
if (item.operation.type === "mergeBlockWithNext") return item.operation.mergedParagraphProperties !== void 0;
|
|
142
|
+
return isResolvedReplaceBlockOperation(item) && REPLACE_BLOCK_IMPACT[item.replaceBlockImpact].changesStyle;
|
|
143
|
+
};
|
|
139
144
|
const resolveReplaceBlockImpact = ({ changesText, changesStyle }) => {
|
|
140
145
|
if (changesText) return changesStyle ? "textAndStyle" : "text";
|
|
141
146
|
if (changesStyle) return "style";
|
|
@@ -266,6 +271,50 @@ const resolveFormattingFromStyle = ({ attrs, styleId, styleResolver }) => {
|
|
|
266
271
|
if (attrs.alignmentFromStyle !== void 0) fallback.alignment = attrs.alignmentFromStyle;
|
|
267
272
|
return Object.keys(fallback).length > 0 ? fallback : void 0;
|
|
268
273
|
};
|
|
274
|
+
/**
|
|
275
|
+
* Apply one paragraph-property patch to a live node. A tracked patch stores
|
|
276
|
+
* the complete previous pPr: a partial snapshot cannot distinguish a property
|
|
277
|
+
* the change left alone from one it explicitly cleared when rejecting it.
|
|
278
|
+
*/
|
|
279
|
+
const applyBlockParagraphProperties = ({ tr, position, node, properties, styleResolver, numbering = null, revisionInfo }) => {
|
|
280
|
+
const attrs = expectParagraphAttrs(node);
|
|
281
|
+
const resolvedFormattingFromStyle = properties.styleId === void 0 ? resolveFormattingFromStyle({
|
|
282
|
+
attrs,
|
|
283
|
+
styleId: attrs.styleId,
|
|
284
|
+
styleResolver
|
|
285
|
+
}) : resolveFormattingFromStyle({
|
|
286
|
+
attrs,
|
|
287
|
+
styleId: properties.styleId,
|
|
288
|
+
styleResolver
|
|
289
|
+
});
|
|
290
|
+
const patch = paragraphPropertiesPatch({
|
|
291
|
+
node,
|
|
292
|
+
properties,
|
|
293
|
+
resolvedFormattingFromStyle,
|
|
294
|
+
numbering
|
|
295
|
+
});
|
|
296
|
+
if (patch === null) return {
|
|
297
|
+
tr,
|
|
298
|
+
changed: false,
|
|
299
|
+
revisionId: null
|
|
300
|
+
};
|
|
301
|
+
const changeInfo = revisionInfo?.();
|
|
302
|
+
const change = changeInfo ? {
|
|
303
|
+
type: "paragraphPropertyChange",
|
|
304
|
+
info: changeInfo,
|
|
305
|
+
previousFormatting: paragraphPropertiesSnapshot(node)
|
|
306
|
+
} : null;
|
|
307
|
+
const existing = attrs._propertyChanges;
|
|
308
|
+
return {
|
|
309
|
+
tr: tr.setNodeMarkup(position, void 0, {
|
|
310
|
+
...node.attrs,
|
|
311
|
+
...patch,
|
|
312
|
+
...change ? { _propertyChanges: [...Array.isArray(existing) ? existing : [], change] } : {}
|
|
313
|
+
}),
|
|
314
|
+
changed: true,
|
|
315
|
+
revisionId: change?.info.id ?? null
|
|
316
|
+
};
|
|
317
|
+
};
|
|
269
318
|
const applyReplaceBlockStyleId = ({ item, tr, styleResolver, revisionInfo }) => {
|
|
270
319
|
if (item.operation.type !== "replaceBlock" || item.operation.styleId === void 0) return {
|
|
271
320
|
tr,
|
|
@@ -1291,7 +1340,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1291
1340
|
const provisionalCommentId = item.commentText !== void 0 ? reserveProvisionalCommentId() : void 0;
|
|
1292
1341
|
const commentMark = provisionalCommentId !== void 0 && commentType ? commentType.create({ commentId: provisionalCommentId }) : null;
|
|
1293
1342
|
const stepsBefore = tr.steps.length;
|
|
1294
|
-
let appliedRevisionIds;
|
|
1343
|
+
let appliedRevisionIds = [];
|
|
1295
1344
|
if (isSuggested && !SUGGESTED_SUPPORTED_OPERATION_TYPES.has(item.operation.type)) {
|
|
1296
1345
|
skipped.push({
|
|
1297
1346
|
id: item.operation.id,
|
|
@@ -1900,85 +1949,89 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1900
1949
|
case "setBlockParagraphProperties": {
|
|
1901
1950
|
const blockPosition = tr.mapping.map(item.blockFrom);
|
|
1902
1951
|
const liveBlock = tr.doc.nodeAt(blockPosition) ?? item.blockNode;
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
styleId: liveAttrs.styleId,
|
|
1907
|
-
styleResolver
|
|
1908
|
-
}) : resolveFormattingFromStyle({
|
|
1909
|
-
attrs: liveAttrs,
|
|
1910
|
-
styleId: item.operation.properties.styleId,
|
|
1911
|
-
styleResolver
|
|
1912
|
-
});
|
|
1913
|
-
const patch = paragraphPropertiesPatch({
|
|
1952
|
+
const appliedProperties = applyBlockParagraphProperties({
|
|
1953
|
+
tr,
|
|
1954
|
+
position: blockPosition,
|
|
1914
1955
|
node: liveBlock,
|
|
1915
1956
|
properties: item.operation.properties,
|
|
1916
|
-
|
|
1917
|
-
numbering
|
|
1957
|
+
styleResolver,
|
|
1958
|
+
numbering,
|
|
1959
|
+
...mode === "direct" ? {} : { revisionInfo: () => ({
|
|
1960
|
+
id: operationRevisionSeed++,
|
|
1961
|
+
author,
|
|
1962
|
+
date,
|
|
1963
|
+
...trackedRevisionExtras
|
|
1964
|
+
}) }
|
|
1918
1965
|
});
|
|
1919
|
-
if (
|
|
1966
|
+
if (!appliedProperties.changed) {
|
|
1920
1967
|
skipped.push({
|
|
1921
1968
|
id: item.operation.id,
|
|
1922
1969
|
reason: "noopOperation"
|
|
1923
1970
|
});
|
|
1924
1971
|
continue;
|
|
1925
1972
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
...liveBlock.attrs,
|
|
1929
|
-
...patch
|
|
1930
|
-
});
|
|
1931
|
-
break;
|
|
1932
|
-
}
|
|
1933
|
-
const revisionId = operationRevisionSeed++;
|
|
1934
|
-
const change = {
|
|
1935
|
-
type: "paragraphPropertyChange",
|
|
1936
|
-
info: {
|
|
1937
|
-
id: revisionId,
|
|
1938
|
-
author,
|
|
1939
|
-
date,
|
|
1940
|
-
...trackedRevisionExtras
|
|
1941
|
-
},
|
|
1942
|
-
previousFormatting: paragraphPropertiesSnapshot(liveBlock)
|
|
1943
|
-
};
|
|
1944
|
-
const existing = expectParagraphAttrs(liveBlock)._propertyChanges;
|
|
1945
|
-
tr = tr.setNodeMarkup(blockPosition, void 0, {
|
|
1946
|
-
...liveBlock.attrs,
|
|
1947
|
-
...patch,
|
|
1948
|
-
_propertyChanges: [...Array.isArray(existing) ? existing : [], change]
|
|
1949
|
-
});
|
|
1950
|
-
appliedRevisionIds = [revisionId];
|
|
1973
|
+
tr = appliedProperties.tr;
|
|
1974
|
+
if (appliedProperties.revisionId !== null) appliedRevisionIds = [appliedProperties.revisionId];
|
|
1951
1975
|
break;
|
|
1952
1976
|
}
|
|
1953
1977
|
case "splitBlock": {
|
|
1954
1978
|
if (mode === "direct") {
|
|
1955
1979
|
if (item.to > item.from) tr = tr.delete(item.from, item.to);
|
|
1956
1980
|
tr = tr.split(item.from);
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
id: revisionIdMark,
|
|
1962
|
-
author,
|
|
1963
|
-
date,
|
|
1964
|
-
...trackedRevisionExtras
|
|
1965
|
-
};
|
|
1966
|
-
appliedRevisionIds = [revisionIdMark];
|
|
1967
|
-
if (item.to > item.from && deletionType) {
|
|
1968
|
-
const revisionIdSeparator = operationRevisionSeed++;
|
|
1969
|
-
tr = tr.addMark(item.from, item.to, deletionType.create({
|
|
1970
|
-
revisionId: revisionIdSeparator,
|
|
1981
|
+
} else {
|
|
1982
|
+
const revisionIdMark = operationRevisionSeed++;
|
|
1983
|
+
const info = {
|
|
1984
|
+
id: revisionIdMark,
|
|
1971
1985
|
author,
|
|
1972
1986
|
date,
|
|
1973
1987
|
...trackedRevisionExtras
|
|
1974
|
-
}
|
|
1975
|
-
appliedRevisionIds = [revisionIdMark
|
|
1988
|
+
};
|
|
1989
|
+
appliedRevisionIds = [revisionIdMark];
|
|
1990
|
+
if (item.to > item.from && deletionType) {
|
|
1991
|
+
const revisionIdSeparator = operationRevisionSeed++;
|
|
1992
|
+
tr = tr.addMark(item.from, item.to, deletionType.create({
|
|
1993
|
+
revisionId: revisionIdSeparator,
|
|
1994
|
+
author,
|
|
1995
|
+
date,
|
|
1996
|
+
...trackedRevisionExtras
|
|
1997
|
+
}));
|
|
1998
|
+
appliedRevisionIds.push(revisionIdSeparator);
|
|
1999
|
+
}
|
|
2000
|
+
tr = tr.split(item.from);
|
|
2001
|
+
tr = tr.setNodeAttribute(item.blockFrom, "pPrMark", {
|
|
2002
|
+
kind: "ins",
|
|
2003
|
+
info
|
|
2004
|
+
});
|
|
1976
2005
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2006
|
+
const allocateSplitParagraphPropertyRevisionInfo = () => ({
|
|
2007
|
+
id: operationRevisionSeed++,
|
|
2008
|
+
author,
|
|
2009
|
+
date,
|
|
2010
|
+
...trackedRevisionExtras
|
|
1981
2011
|
});
|
|
2012
|
+
const paragraphPropertyTargets = [{
|
|
2013
|
+
position: item.blockFrom,
|
|
2014
|
+
properties: item.operation.firstParagraphProperties
|
|
2015
|
+
}, {
|
|
2016
|
+
position: item.from + 1,
|
|
2017
|
+
properties: item.operation.secondParagraphProperties
|
|
2018
|
+
}];
|
|
2019
|
+
for (const { position, properties } of paragraphPropertyTargets) {
|
|
2020
|
+
if (!properties) continue;
|
|
2021
|
+
const paragraph = tr.doc.nodeAt(position);
|
|
2022
|
+
if (!paragraph) panic("A resolved split did not produce two paragraphs");
|
|
2023
|
+
const appliedProperties = applyBlockParagraphProperties({
|
|
2024
|
+
tr,
|
|
2025
|
+
position,
|
|
2026
|
+
node: paragraph,
|
|
2027
|
+
properties,
|
|
2028
|
+
styleResolver,
|
|
2029
|
+
numbering,
|
|
2030
|
+
...mode === "direct" ? {} : { revisionInfo: allocateSplitParagraphPropertyRevisionInfo }
|
|
2031
|
+
});
|
|
2032
|
+
tr = appliedProperties.tr;
|
|
2033
|
+
if (appliedProperties.revisionId !== null) appliedRevisionIds.push(appliedProperties.revisionId);
|
|
2034
|
+
}
|
|
1982
2035
|
break;
|
|
1983
2036
|
}
|
|
1984
2037
|
case "mergeBlockWithNext": {
|
|
@@ -1987,30 +2040,50 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1987
2040
|
if (mode === "direct") {
|
|
1988
2041
|
if (separator.length > 0) tr = tr.insertText(separator, insertAt);
|
|
1989
2042
|
tr = tr.join(item.blockTo + separator.length);
|
|
1990
|
-
|
|
2043
|
+
} else {
|
|
2044
|
+
const revisionIdMark = operationRevisionSeed++;
|
|
2045
|
+
appliedRevisionIds = [revisionIdMark];
|
|
2046
|
+
if (separator.length > 0 && insertionType) {
|
|
2047
|
+
const revisionIdSeparator = operationRevisionSeed++;
|
|
2048
|
+
tr = tr.insertText(separator, insertAt);
|
|
2049
|
+
tr = tr.addMark(insertAt, insertAt + separator.length, insertionType.create({
|
|
2050
|
+
revisionId: revisionIdSeparator,
|
|
2051
|
+
author,
|
|
2052
|
+
date,
|
|
2053
|
+
...trackedRevisionExtras
|
|
2054
|
+
}));
|
|
2055
|
+
appliedRevisionIds.push(revisionIdSeparator);
|
|
2056
|
+
}
|
|
2057
|
+
tr = tr.setNodeAttribute(item.blockFrom, "pPrMark", {
|
|
2058
|
+
kind: "del",
|
|
2059
|
+
info: {
|
|
2060
|
+
id: revisionIdMark,
|
|
2061
|
+
author,
|
|
2062
|
+
date,
|
|
2063
|
+
...trackedRevisionExtras
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
1991
2066
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
const
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2067
|
+
if (item.operation.mergedParagraphProperties) {
|
|
2068
|
+
const mergedParagraph = tr.doc.nodeAt(item.blockFrom);
|
|
2069
|
+
if (!mergedParagraph) panic("A resolved merge did not produce a paragraph");
|
|
2070
|
+
const appliedProperties = applyBlockParagraphProperties({
|
|
2071
|
+
tr,
|
|
2072
|
+
position: item.blockFrom,
|
|
2073
|
+
node: mergedParagraph,
|
|
2074
|
+
properties: item.operation.mergedParagraphProperties,
|
|
2075
|
+
styleResolver,
|
|
2076
|
+
numbering,
|
|
2077
|
+
...mode === "direct" ? {} : { revisionInfo: () => ({
|
|
2078
|
+
id: operationRevisionSeed++,
|
|
2079
|
+
author,
|
|
2080
|
+
date,
|
|
2081
|
+
...trackedRevisionExtras
|
|
2082
|
+
}) }
|
|
2083
|
+
});
|
|
2084
|
+
tr = appliedProperties.tr;
|
|
2085
|
+
if (appliedProperties.revisionId !== null) appliedRevisionIds.push(appliedProperties.revisionId);
|
|
2004
2086
|
}
|
|
2005
|
-
tr = tr.setNodeAttribute(item.blockFrom, "pPrMark", {
|
|
2006
|
-
kind: "del",
|
|
2007
|
-
info: {
|
|
2008
|
-
id: revisionIdMark,
|
|
2009
|
-
author,
|
|
2010
|
-
date,
|
|
2011
|
-
...trackedRevisionExtras
|
|
2012
|
-
}
|
|
2013
|
-
});
|
|
2014
2087
|
break;
|
|
2015
2088
|
}
|
|
2016
2089
|
case "commentOnBlock":
|
|
@@ -2043,7 +2116,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
2043
2116
|
applied.push({
|
|
2044
2117
|
id: item.operation.id,
|
|
2045
2118
|
...committedCommentId !== void 0 && { commentId: committedCommentId },
|
|
2046
|
-
...appliedRevisionIds
|
|
2119
|
+
...appliedRevisionIds[0] !== void 0 && {
|
|
2047
2120
|
revisionId: appliedRevisionIds[0],
|
|
2048
2121
|
revisionIds: appliedRevisionIds
|
|
2049
2122
|
},
|
package/dist/ai-edits/types.d.ts
CHANGED
|
@@ -312,6 +312,16 @@ type FolioAIEditOperation = FolioAIEditReviewMeta & {
|
|
|
312
312
|
*/
|
|
313
313
|
separator?: string;
|
|
314
314
|
blockId: string;
|
|
315
|
+
/**
|
|
316
|
+
* Paragraph properties for the first result. Omitted properties keep
|
|
317
|
+
* the source paragraph's value.
|
|
318
|
+
*/
|
|
319
|
+
firstParagraphProperties?: FolioAIBlockParagraphProperties;
|
|
320
|
+
/**
|
|
321
|
+
* Paragraph properties for the second result. Omitted properties keep
|
|
322
|
+
* the source paragraph's value.
|
|
323
|
+
*/
|
|
324
|
+
secondParagraphProperties?: FolioAIBlockParagraphProperties;
|
|
315
325
|
} |
|
|
316
326
|
/**
|
|
317
327
|
* Add a whole table next to the anchor block, its rows marked inserted in
|
|
@@ -377,6 +387,11 @@ type FolioAIEditOperation = FolioAIEditReviewMeta & {
|
|
|
377
387
|
*/
|
|
378
388
|
separator?: string;
|
|
379
389
|
blockId: string;
|
|
390
|
+
/**
|
|
391
|
+
* Paragraph properties for the joined result. Omitted properties keep
|
|
392
|
+
* the first paragraph's value.
|
|
393
|
+
*/
|
|
394
|
+
mergedParagraphProperties?: FolioAIBlockParagraphProperties;
|
|
380
395
|
} | {
|
|
381
396
|
id: string;
|
|
382
397
|
type: "commentOnBlock";
|
package/dist/compare/plan.js
CHANGED
|
@@ -612,6 +612,10 @@ const planStoryCompare = ({ story, baseSnapshot, targetSnapshot, maxOperations,
|
|
|
612
612
|
const paragraphMarkPlan = paragraphMarkPlans.get(stepIndex);
|
|
613
613
|
if (paragraphMarkPlan?.type === "split") {
|
|
614
614
|
const { baseBlock, revisedBlocks: splitInto, offset, separator } = paragraphMarkPlan;
|
|
615
|
+
const firstParagraphFormatting = changedFolioContentParagraphFormatting(baseBlock, splitInto[0]);
|
|
616
|
+
const secondParagraphFormatting = changedFolioContentParagraphFormatting(baseBlock, splitInto[1]);
|
|
617
|
+
const firstParagraphProperties = firstParagraphFormatting ? toFolioAIBlockParagraphProperties(firstParagraphFormatting) : void 0;
|
|
618
|
+
const secondParagraphProperties = secondParagraphFormatting ? toFolioAIBlockParagraphProperties(secondParagraphFormatting) : void 0;
|
|
615
619
|
changes.push({
|
|
616
620
|
kind: "split",
|
|
617
621
|
location: locationOf(story, baseBlock),
|
|
@@ -619,17 +623,35 @@ const planStoryCompare = ({ story, baseSnapshot, targetSnapshot, maxOperations,
|
|
|
619
623
|
targetBlockIds: splitInto.map(({ id }) => id),
|
|
620
624
|
text: baseBlock.text
|
|
621
625
|
});
|
|
626
|
+
if (firstParagraphProperties) changes.push({
|
|
627
|
+
kind: "paragraph-format",
|
|
628
|
+
location: locationOf(story, baseBlock),
|
|
629
|
+
baseBlockId: baseBlock.id,
|
|
630
|
+
targetBlockId: splitInto[0].id,
|
|
631
|
+
properties: firstParagraphProperties
|
|
632
|
+
});
|
|
633
|
+
if (secondParagraphProperties) changes.push({
|
|
634
|
+
kind: "paragraph-format",
|
|
635
|
+
location: locationOf(story, baseBlock),
|
|
636
|
+
baseBlockId: baseBlock.id,
|
|
637
|
+
targetBlockId: splitInto[1].id,
|
|
638
|
+
properties: secondParagraphProperties
|
|
639
|
+
});
|
|
622
640
|
operations.push({
|
|
623
641
|
id: nextOperationId(),
|
|
624
642
|
type: "splitBlock",
|
|
625
643
|
blockId: baseBlock.id,
|
|
626
644
|
offset,
|
|
627
|
-
...separator.length > 0 && { separator }
|
|
645
|
+
...separator.length > 0 && { separator },
|
|
646
|
+
...firstParagraphProperties && { firstParagraphProperties },
|
|
647
|
+
...secondParagraphProperties && { secondParagraphProperties }
|
|
628
648
|
});
|
|
629
649
|
continue;
|
|
630
650
|
}
|
|
631
651
|
if (paragraphMarkPlan?.type === "merge") {
|
|
632
652
|
const { baseBlocks, revisedBlock: targetBlock, separator } = paragraphMarkPlan;
|
|
653
|
+
const mergedParagraphFormatting = changedFolioContentParagraphFormatting(baseBlocks[0], targetBlock);
|
|
654
|
+
const mergedParagraphProperties = mergedParagraphFormatting ? toFolioAIBlockParagraphProperties(mergedParagraphFormatting) : void 0;
|
|
633
655
|
changes.push({
|
|
634
656
|
kind: "merge",
|
|
635
657
|
location: locationOf(story, baseBlocks[0]),
|
|
@@ -637,11 +659,19 @@ const planStoryCompare = ({ story, baseSnapshot, targetSnapshot, maxOperations,
|
|
|
637
659
|
targetBlockId: targetBlock.id,
|
|
638
660
|
text: targetBlock.text
|
|
639
661
|
});
|
|
662
|
+
if (mergedParagraphProperties) changes.push({
|
|
663
|
+
kind: "paragraph-format",
|
|
664
|
+
location: locationOf(story, baseBlocks[0]),
|
|
665
|
+
baseBlockId: baseBlocks[0].id,
|
|
666
|
+
targetBlockId: targetBlock.id,
|
|
667
|
+
properties: mergedParagraphProperties
|
|
668
|
+
});
|
|
640
669
|
operations.push({
|
|
641
670
|
id: nextOperationId(),
|
|
642
671
|
type: "mergeBlockWithNext",
|
|
643
672
|
blockId: baseBlocks[0].id,
|
|
644
|
-
...separator.length > 0 && { separator }
|
|
673
|
+
...separator.length > 0 && { separator },
|
|
674
|
+
...mergedParagraphProperties && { mergedParagraphProperties }
|
|
645
675
|
});
|
|
646
676
|
continue;
|
|
647
677
|
}
|
|
@@ -15,7 +15,7 @@ import { hasComplexScript, isComplexScriptCodePoint } from "../utils/scriptSegme
|
|
|
15
15
|
* advances rightward from its origin whatever the paragraph does.
|
|
16
16
|
*/
|
|
17
17
|
declare const glyphCellOffsetsPx: (run: DisplayGlyphRun) => readonly number[];
|
|
18
|
-
declare const DISPLAY_PRIMITIVE_KINDS: ("image" | "
|
|
18
|
+
declare const DISPLAY_PRIMITIVE_KINDS: ("image" | "glyphRun" | "rect" | "line" | "clipGroup" | "rotateGroup" | "opacityGroup")[];
|
|
19
19
|
/**
|
|
20
20
|
* Dash geometry of each stroke pattern, as multiples of the stroke thickness.
|
|
21
21
|
*
|
|
@@ -101,8 +101,8 @@ declare const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE: Readonly<{
|
|
|
101
101
|
readonly insertBeforeBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
|
|
102
102
|
readonly replaceBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "preserveFormatting", "styleId", "comment"];
|
|
103
103
|
readonly deleteBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "moveId", "comment"];
|
|
104
|
-
readonly splitBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "offset", "separator"];
|
|
105
|
-
readonly mergeBlockWithNext: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "separator"];
|
|
104
|
+
readonly splitBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "offset", "separator", "firstParagraphProperties", "secondParagraphProperties"];
|
|
105
|
+
readonly mergeBlockWithNext: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "separator", "mergedParagraphProperties"];
|
|
106
106
|
readonly setBlockParagraphProperties: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "properties"];
|
|
107
107
|
readonly insertTable: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "position", "rows"];
|
|
108
108
|
readonly deleteTable: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId"];
|
|
@@ -262,9 +262,9 @@ const readTableRows = (value, path) => {
|
|
|
262
262
|
if (parsed.some((row) => row.length !== width)) return invalidBatch(rowsPath, "expected every row to hold the same number of cells");
|
|
263
263
|
return parsed;
|
|
264
264
|
};
|
|
265
|
-
const readParagraphProperties = (value, path) => {
|
|
266
|
-
const candidate = value[
|
|
267
|
-
const propertiesPath = `${path}
|
|
265
|
+
const readParagraphProperties = ({ value, key, path }) => {
|
|
266
|
+
const candidate = value[key];
|
|
267
|
+
const propertiesPath = `${path}.${key}`;
|
|
268
268
|
if (!isPlainObject(candidate)) return invalidBatch(propertiesPath, "expected an object");
|
|
269
269
|
assertAllowedKeys(candidate, propertiesPath, [
|
|
270
270
|
"styleId",
|
|
@@ -463,9 +463,15 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
|
|
|
463
463
|
splitBlock: [
|
|
464
464
|
...COMMON_OPERATION_KEYS,
|
|
465
465
|
"offset",
|
|
466
|
-
"separator"
|
|
466
|
+
"separator",
|
|
467
|
+
"firstParagraphProperties",
|
|
468
|
+
"secondParagraphProperties"
|
|
469
|
+
],
|
|
470
|
+
mergeBlockWithNext: [
|
|
471
|
+
...COMMON_OPERATION_KEYS,
|
|
472
|
+
"separator",
|
|
473
|
+
"mergedParagraphProperties"
|
|
467
474
|
],
|
|
468
|
-
mergeBlockWithNext: [...COMMON_OPERATION_KEYS, "separator"],
|
|
469
475
|
setBlockParagraphProperties: [...COMMON_OPERATION_KEYS, "properties"],
|
|
470
476
|
insertTable: [
|
|
471
477
|
...COMMON_OPERATION_KEYS,
|
|
@@ -588,24 +594,48 @@ const parseDocumentOperation = (value, index) => {
|
|
|
588
594
|
id,
|
|
589
595
|
type,
|
|
590
596
|
blockId,
|
|
591
|
-
properties: readParagraphProperties(
|
|
597
|
+
properties: readParagraphProperties({
|
|
598
|
+
value,
|
|
599
|
+
key: "properties",
|
|
600
|
+
path
|
|
601
|
+
})
|
|
592
602
|
};
|
|
593
603
|
if (type === "splitBlock" || type === "mergeBlockWithNext") {
|
|
594
604
|
const separator = readOptionalString(value, "separator", path);
|
|
595
|
-
if (type === "mergeBlockWithNext")
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
605
|
+
if (type === "mergeBlockWithNext") {
|
|
606
|
+
const mergedParagraphProperties = value["mergedParagraphProperties"] === void 0 ? void 0 : readParagraphProperties({
|
|
607
|
+
value,
|
|
608
|
+
key: "mergedParagraphProperties",
|
|
609
|
+
path
|
|
610
|
+
});
|
|
611
|
+
return {
|
|
612
|
+
...operationMeta,
|
|
613
|
+
id,
|
|
614
|
+
type,
|
|
615
|
+
blockId,
|
|
616
|
+
...separator !== void 0 && { separator },
|
|
617
|
+
...mergedParagraphProperties !== void 0 && { mergedParagraphProperties }
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const firstParagraphProperties = value["firstParagraphProperties"] === void 0 ? void 0 : readParagraphProperties({
|
|
621
|
+
value,
|
|
622
|
+
key: "firstParagraphProperties",
|
|
623
|
+
path
|
|
624
|
+
});
|
|
625
|
+
const secondParagraphProperties = value["secondParagraphProperties"] === void 0 ? void 0 : readParagraphProperties({
|
|
626
|
+
value,
|
|
627
|
+
key: "secondParagraphProperties",
|
|
628
|
+
path
|
|
629
|
+
});
|
|
602
630
|
return {
|
|
603
631
|
...operationMeta,
|
|
604
632
|
id,
|
|
605
633
|
type,
|
|
606
634
|
blockId,
|
|
607
635
|
offset: readNonNegativeInteger(value, "offset", path),
|
|
608
|
-
...separator !== void 0 && { separator }
|
|
636
|
+
...separator !== void 0 && { separator },
|
|
637
|
+
...firstParagraphProperties !== void 0 && { firstParagraphProperties },
|
|
638
|
+
...secondParagraphProperties !== void 0 && { secondParagraphProperties }
|
|
609
639
|
};
|
|
610
640
|
}
|
|
611
641
|
if (type === "replaceInBlock") return {
|
|
@@ -3,12 +3,12 @@ import { Transaction } from "prosemirror-state";
|
|
|
3
3
|
import { MarkType } from "prosemirror-model";
|
|
4
4
|
//#region src/internal/headlessRevisionResolution.d.ts
|
|
5
5
|
type HeadlessRevisionResolutionMode = "accept" | "reject";
|
|
6
|
-
type
|
|
6
|
+
type HeadlessRange = {
|
|
7
7
|
from: number;
|
|
8
8
|
to: number;
|
|
9
9
|
};
|
|
10
10
|
type HeadlessInlineChangeTracking = {
|
|
11
|
-
ranges: readonly
|
|
11
|
+
ranges: readonly HeadlessRange[];
|
|
12
12
|
mappingFrom: number;
|
|
13
13
|
};
|
|
14
14
|
type AppendHeadlessInlineResolutionOptions = {
|
|
@@ -76,6 +76,7 @@ const resolveInlineNode = ({ node, context, paragraphScope }) => {
|
|
|
76
76
|
};
|
|
77
77
|
const resolveInlineContent = ({ node, position, context, inheritedParagraphScope }) => {
|
|
78
78
|
const paragraphScope = node.type.name === "paragraph" && context.mode === "reject" ? { paragraph: node } : inheritedParagraphScope;
|
|
79
|
+
const replacementRangeStart = context.replacementRanges.length;
|
|
79
80
|
let resolvedNode = node;
|
|
80
81
|
if (node.isInline) {
|
|
81
82
|
const resolved = resolveInlineNode({
|
|
@@ -84,9 +85,10 @@ const resolveInlineContent = ({ node, position, context, inheritedParagraphScope
|
|
|
84
85
|
paragraphScope
|
|
85
86
|
});
|
|
86
87
|
if (resolved === null) {
|
|
87
|
-
context.
|
|
88
|
+
context.replacementRanges.push({
|
|
88
89
|
from: position,
|
|
89
|
-
to: position + node.nodeSize
|
|
90
|
+
to: position + node.nodeSize,
|
|
91
|
+
newSize: 0
|
|
90
92
|
});
|
|
91
93
|
return null;
|
|
92
94
|
}
|
|
@@ -109,17 +111,36 @@ const resolveInlineContent = ({ node, position, context, inheritedParagraphScope
|
|
|
109
111
|
} else contentChanged = true;
|
|
110
112
|
});
|
|
111
113
|
if (!contentChanged) return resolvedNode;
|
|
114
|
+
let resolvedContent = Fragment.fromArray(children);
|
|
115
|
+
if (!resolvedNode.type.validContent(resolvedContent)) {
|
|
116
|
+
const fitted = resolvedNode.type.createAndFill(resolvedNode.attrs, resolvedContent, resolvedNode.marks);
|
|
117
|
+
if (!fitted || !resolvedNode.type.validContent(fitted.content)) {
|
|
118
|
+
if (!resolvedNode.isInline) return panic(`Headless inline resolution invalidated ${resolvedNode.type.name} content`);
|
|
119
|
+
context.replacementRanges.splice(replacementRangeStart, context.replacementRanges.length - replacementRangeStart, {
|
|
120
|
+
from: position,
|
|
121
|
+
to: position + node.nodeSize,
|
|
122
|
+
newSize: 0
|
|
123
|
+
});
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
context.replacementRanges.splice(replacementRangeStart, context.replacementRanges.length - replacementRangeStart, {
|
|
127
|
+
from: contentStart,
|
|
128
|
+
to: contentStart + node.content.size,
|
|
129
|
+
newSize: fitted.content.size
|
|
130
|
+
});
|
|
131
|
+
resolvedContent = fitted.content;
|
|
132
|
+
}
|
|
112
133
|
if (resolvedNode.type.name === "paragraph") context.changedParagraphRanges.push({
|
|
113
134
|
from: position,
|
|
114
135
|
to: position + resolvedNode.nodeSize
|
|
115
136
|
});
|
|
116
|
-
return rebuildNode(node, resolvedNode.attrs,
|
|
137
|
+
return rebuildNode(node, resolvedNode.attrs, resolvedContent, resolvedNode.marks);
|
|
117
138
|
};
|
|
118
|
-
const
|
|
139
|
+
const coalesceReplacementRanges = (replacementRanges) => {
|
|
119
140
|
const coalesced = [];
|
|
120
|
-
for (const range of
|
|
141
|
+
for (const range of replacementRanges) {
|
|
121
142
|
const previous = coalesced.at(-1);
|
|
122
|
-
if (previous && range.from <= previous.to) previous.to = Math.max(previous.to, range.to);
|
|
143
|
+
if (previous && previous.newSize === 0 && range.newSize === 0 && range.from <= previous.to) previous.to = Math.max(previous.to, range.to);
|
|
123
144
|
else coalesced.push({ ...range });
|
|
124
145
|
}
|
|
125
146
|
return coalesced;
|
|
@@ -129,7 +150,7 @@ const appendHeadlessInlineResolution = ({ tr, mode, keepType, removeType, styleR
|
|
|
129
150
|
mode,
|
|
130
151
|
keepType,
|
|
131
152
|
removeType,
|
|
132
|
-
|
|
153
|
+
replacementRanges: [],
|
|
133
154
|
changedParagraphRanges: [],
|
|
134
155
|
styleResolver
|
|
135
156
|
};
|
|
@@ -139,10 +160,10 @@ const appendHeadlessInlineResolution = ({ tr, mode, keepType, removeType, styleR
|
|
|
139
160
|
context
|
|
140
161
|
});
|
|
141
162
|
if (!resolved || resolved.eq(tr.doc)) return null;
|
|
142
|
-
const positionMap = new StepMap(
|
|
163
|
+
const positionMap = new StepMap(coalesceReplacementRanges(context.replacementRanges).flatMap(({ from, to, newSize }) => [
|
|
143
164
|
from,
|
|
144
165
|
to - from,
|
|
145
|
-
|
|
166
|
+
newSize
|
|
146
167
|
]));
|
|
147
168
|
const mappingFrom = tr.steps.length;
|
|
148
169
|
tr.step(new HeadlessInlineResolutionStep(0, tr.doc.content.size, new Slice(resolved.content, 0, 0), positionMap));
|
|
@@ -50,7 +50,6 @@ function removeCommentMark(commentId) {
|
|
|
50
50
|
return true;
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
const BULK_INLINE_RESOLUTION_THRESHOLD = 256;
|
|
54
53
|
/**
|
|
55
54
|
* Resolve a tracked change: accept or reject.
|
|
56
55
|
* - Accept: keep insertions (remove mark), delete deletions (remove text)
|
|
@@ -80,8 +79,6 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
|
|
|
80
79
|
const pPrMarkOps = [];
|
|
81
80
|
const tableRowStructuralOps = [];
|
|
82
81
|
const tableCellStructuralOps = [];
|
|
83
|
-
const deferredRunPropertyChanges = [];
|
|
84
|
-
let bulkInlineCarrierCount = 0;
|
|
85
82
|
let removedSectionEndpointCount = 0;
|
|
86
83
|
const removedSectionReferences = [];
|
|
87
84
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
@@ -156,31 +153,12 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
|
|
|
156
153
|
return true;
|
|
157
154
|
}
|
|
158
155
|
if (!node.isInline) return true;
|
|
156
|
+
if (canBulkInlineResolution) return true;
|
|
159
157
|
const nodeEnd = pos + node.nodeSize;
|
|
160
158
|
const rangeFrom = Math.max(from, pos);
|
|
161
159
|
const rangeTo = Math.min(to, nodeEnd);
|
|
162
160
|
const runPropertyChangeMark = node.marks.find((mark) => mark.type.name === "runPropertyChange");
|
|
163
|
-
const resolvesRunPropertyChange = runPropertyChangeMark !== void 0 && expectRunPropertyChangeMarkAttrs(runPropertyChangeMark).changes.length > 0;
|
|
164
161
|
const removesNode = removeType !== void 0 && node.marks.some((mark) => mark.type === removeType && matchesRevision(mark));
|
|
165
|
-
const removesKeptMark = keepType !== void 0 && node.marks.some((mark) => mark.type === keepType && matchesRevision(mark));
|
|
166
|
-
if (canBulkInlineResolution) {
|
|
167
|
-
if (resolvesRunPropertyChange) deferredRunPropertyChanges.push({
|
|
168
|
-
tr,
|
|
169
|
-
node,
|
|
170
|
-
from: rangeFrom,
|
|
171
|
-
to: rangeTo,
|
|
172
|
-
mark: runPropertyChangeMark,
|
|
173
|
-
mode,
|
|
174
|
-
revisionSet,
|
|
175
|
-
styleResolver
|
|
176
|
-
});
|
|
177
|
-
if (removesNode) deleteRanges.push({
|
|
178
|
-
from: rangeFrom,
|
|
179
|
-
to: rangeTo
|
|
180
|
-
});
|
|
181
|
-
if (resolvesRunPropertyChange || removesNode || removesKeptMark) bulkInlineCarrierCount++;
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
162
|
if (runPropertyChangeMark) resolveRunPropertyChange({
|
|
185
163
|
tr,
|
|
186
164
|
node,
|
|
@@ -201,7 +179,7 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
|
|
|
201
179
|
return true;
|
|
202
180
|
});
|
|
203
181
|
let bulkInlineChangeTracking = null;
|
|
204
|
-
if (canBulkInlineResolution
|
|
182
|
+
if (canBulkInlineResolution) bulkInlineChangeTracking = appendHeadlessInlineResolution({
|
|
205
183
|
tr,
|
|
206
184
|
mode,
|
|
207
185
|
keepType,
|
|
@@ -209,7 +187,6 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
|
|
|
209
187
|
styleResolver
|
|
210
188
|
});
|
|
211
189
|
else {
|
|
212
|
-
for (const deferred of deferredRunPropertyChanges) resolveRunPropertyChange(deferred);
|
|
213
190
|
if (removeKeptMarksInBulk) tr.removeMark(from, to, keepType);
|
|
214
191
|
let rangesToDelete = deleteRanges;
|
|
215
192
|
if (revisionSet === null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|