@stll/folio-core 0.37.0 → 0.37.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.js +157 -84
- package/dist/ai-edits/headless.d.ts +24 -1
- package/dist/ai-edits/headless.js +108 -71
- package/dist/ai-edits/read.d.ts +9 -1
- package/dist/ai-edits/read.js +34 -16
- package/dist/ai-edits/snapshot.d.ts +7 -5
- package/dist/ai-edits/snapshot.js +39 -12
- package/dist/ai-edits/types.d.ts +15 -0
- package/dist/compare/compare.d.ts +2 -3
- package/dist/compare/compare.js +41 -64
- package/dist/compare/content-alignment.js +0 -1
- package/dist/compare/plan.js +32 -2
- package/dist/compare/scenario.d.ts +11 -2
- package/dist/compare/scenario.js +6 -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
|
},
|
|
@@ -122,6 +122,22 @@ type FolioApplyDocumentOperationsToStoryOptions = FolioApplyDocumentOperationsOp
|
|
|
122
122
|
*/
|
|
123
123
|
tableTemplates?: FolioTableTemplates;
|
|
124
124
|
};
|
|
125
|
+
type FolioDocxComparisonProjectionMode = "with-revision-census" | "without-revision-census";
|
|
126
|
+
type FolioDocxComparisonStoryProjection = {
|
|
127
|
+
handle: FolioDocumentStoryHandle;
|
|
128
|
+
snapshot: FolioAIEditSnapshot | null;
|
|
129
|
+
};
|
|
130
|
+
type FolioDocxComparisonProjection = {
|
|
131
|
+
stories: readonly FolioDocxComparisonStoryProjection[];
|
|
132
|
+
revisions: {
|
|
133
|
+
highestId: number;
|
|
134
|
+
present: boolean;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
type FolioDocxComparisonAccess = {
|
|
138
|
+
projectStories: (mode: FolioDocxComparisonProjectionMode) => FolioDocxComparisonProjection;
|
|
139
|
+
snapshotReviewedStory: (options?: FolioReadReviewedStoryOptions) => FolioAIEditSnapshot | null;
|
|
140
|
+
};
|
|
125
141
|
declare const isFolioReviewedView: (value: unknown) => value is FolioReviewedView;
|
|
126
142
|
declare const isFolioResolvedReviewedView: (value: unknown) => value is FolioResolvedReviewedView;
|
|
127
143
|
/** Filter for {@link FolioDocxReviewer.getChanges}. */
|
|
@@ -222,6 +238,8 @@ declare class FolioDocxReviewer {
|
|
|
222
238
|
getDocumentProperties(): Readonly<NonNullable<document_d_exports.Document["package"]["properties"]>> | null;
|
|
223
239
|
/** Snapshot one editable story into stable, operation-ready blocks. */
|
|
224
240
|
snapshotStory(story: FolioEditableDocumentStoryHandle): FolioAIEditSnapshot | null;
|
|
241
|
+
private snapshotReviewedStoryInternal;
|
|
242
|
+
private projectComparisonStoriesInternal;
|
|
225
243
|
/**
|
|
226
244
|
* One story's tables, in the document order {@link snapshotStory} numbers
|
|
227
245
|
* them with, read through a reviewed view.
|
|
@@ -249,6 +267,7 @@ declare class FolioDocxReviewer {
|
|
|
249
267
|
readReviewedStory(options?: FolioReadReviewedStoryOptions): FolioReviewedStory | null;
|
|
250
268
|
/** Resolve one editable story to its original or final state. */
|
|
251
269
|
resolveReviewedStory({ story, view }: FolioResolveReviewedStoryOptions): boolean;
|
|
270
|
+
private resolveReviewedStorySnapshotInternal;
|
|
252
271
|
/**
|
|
253
272
|
* Apply operations against the current state. Reuses the live-editor applier
|
|
254
273
|
* verbatim via a headless `{ state, dispatch }` seam; the resulting state
|
|
@@ -292,6 +311,7 @@ declare class FolioDocxReviewer {
|
|
|
292
311
|
* parts and separator notes are omitted.
|
|
293
312
|
*/
|
|
294
313
|
getNotesAsText(): string;
|
|
314
|
+
private listStoryHandlesInternal;
|
|
295
315
|
/** Discover every readable document story through a typed, serializable handle. */
|
|
296
316
|
listStories(): FolioDocumentStory[];
|
|
297
317
|
/** Read one discovered story; returns null when its handle is no longer present. */
|
|
@@ -386,6 +406,7 @@ declare class FolioDocxReviewer {
|
|
|
386
406
|
private getNoteStory;
|
|
387
407
|
private getHeaderFooterStoryText;
|
|
388
408
|
private getNoteStoryText;
|
|
409
|
+
private getStoryText;
|
|
389
410
|
private mergeEditedSecondaryStories;
|
|
390
411
|
/** Apply any {@link resolveComment} overrides recorded for these comments. */
|
|
391
412
|
private withResolvedOverrides;
|
|
@@ -401,6 +422,8 @@ declare class FolioDocxReviewer {
|
|
|
401
422
|
private runCommand;
|
|
402
423
|
private runStoryCommand;
|
|
403
424
|
}
|
|
425
|
+
/** @internal Comparison-only access to atomic and fresh reviewer projections. */
|
|
426
|
+
declare const getFolioDocxComparisonAccess: (reviewer: FolioDocxReviewer) => FolioDocxComparisonAccess;
|
|
404
427
|
/** Options for {@link applyFolioAIEditsToBuffer}. */
|
|
405
428
|
type ApplyFolioAIEditsToBufferOptions = {
|
|
406
429
|
/** Default author for tracked changes and comments. (default: `"AI"`) */
|
|
@@ -428,4 +451,4 @@ type ApplyFolioAIEditsToBufferResult = FolioAIEditApplyResult & {
|
|
|
428
451
|
*/
|
|
429
452
|
declare const applyFolioAIEditsToBuffer: (buffer: ArrayBuffer, operations: FolioAIEditOperation[], options?: ApplyFolioAIEditsToBufferOptions) => Promise<ApplyFolioAIEditsToBufferResult>;
|
|
430
453
|
//#endregion
|
|
431
|
-
export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioGetContentAsTextOptions, FolioMatchStoryTableGeometryOptions, FolioNumberingLevel, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, type FolioRevisionStamp, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
|
|
454
|
+
export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioGetContentAsTextOptions, FolioMatchStoryTableGeometryOptions, FolioNumberingLevel, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, type FolioRevisionStamp, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, getFolioDocxComparisonAccess, isFolioResolvedReviewedView, isFolioReviewedView };
|
|
@@ -18,10 +18,10 @@ import { createDocumentStylesPlugin, getDocumentStyleResolver } from "../prosemi
|
|
|
18
18
|
import { schema, singletonManager } from "../prosemirror/schema/index.js";
|
|
19
19
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
20
20
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
21
|
-
import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
22
|
-
import { createFolioAIEditSnapshotWithStyleResolver, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
21
|
+
import { getCommentAnchorsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot } from "./read.js";
|
|
22
|
+
import { createFolioAIEditSnapshotWithStyleResolver, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
|
|
23
23
|
import { matchTableGeometry } from "./table-geometry.js";
|
|
24
|
-
import { TaggedError } from "better-result";
|
|
24
|
+
import { TaggedError, panic } from "better-result";
|
|
25
25
|
import { Fragment } from "prosemirror-model";
|
|
26
26
|
import { EditorState } from "prosemirror-state";
|
|
27
27
|
//#region src/ai-edits/headless.ts
|
|
@@ -160,6 +160,7 @@ const resolvedStoryBlockProjection = (block) => {
|
|
|
160
160
|
delete persisted.idStability;
|
|
161
161
|
return persisted;
|
|
162
162
|
};
|
|
163
|
+
const comparisonAccessByReviewer = /* @__PURE__ */ new WeakMap();
|
|
163
164
|
const MAIN_STORY = Object.freeze({ type: "main" });
|
|
164
165
|
const headerFooterStoryKey = ({ type, relationshipId }) => `${type}:${relationshipId}`;
|
|
165
166
|
const noteStoryKey = ({ type, noteId }) => `${type}:${noteId}`;
|
|
@@ -177,12 +178,11 @@ const createHeadlessPlugins = (styles, numbering) => [
|
|
|
177
178
|
createDocumentNumberingPlugin(numbering)
|
|
178
179
|
];
|
|
179
180
|
const createStateSnapshot = (state) => createFolioAIEditSnapshotWithStyleResolver(state.doc, getDocumentStyleResolver(state));
|
|
180
|
-
const
|
|
181
|
-
const snapshot = createStateSnapshot(state);
|
|
181
|
+
const formatStorySnapshotForLLM = (snapshot, annotated) => {
|
|
182
182
|
const blocks = snapshot.blocks.filter(isFolioAIContentBlock);
|
|
183
183
|
if (!annotated) return blocks.map(formatBlockForLLM).join("\n");
|
|
184
184
|
const nodeByStart = /* @__PURE__ */ new Map();
|
|
185
|
-
|
|
185
|
+
sourceDocumentOf(snapshot).descendants((node, pos) => {
|
|
186
186
|
if (!node.isTextblock) return true;
|
|
187
187
|
nodeByStart.set(pos, node);
|
|
188
188
|
return false;
|
|
@@ -264,6 +264,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
264
264
|
this.state = args.state;
|
|
265
265
|
this.author = args.author;
|
|
266
266
|
this.usedCommentIds = new Set((args.baseDocument.package.document.comments ?? []).map(({ id }) => id));
|
|
267
|
+
comparisonAccessByReviewer.set(this, Object.freeze({
|
|
268
|
+
projectStories: (mode) => this.projectComparisonStoriesInternal(mode),
|
|
269
|
+
snapshotReviewedStory: (options) => this.snapshotReviewedStoryInternal(options)
|
|
270
|
+
}));
|
|
267
271
|
}
|
|
268
272
|
/** Parse a `.docx` buffer into a reviewer. */
|
|
269
273
|
static async fromBuffer(buffer, options = {}) {
|
|
@@ -339,6 +343,43 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
339
343
|
const state = this.getEditableStoryState(story);
|
|
340
344
|
return state ? createStateSnapshot(state) : null;
|
|
341
345
|
}
|
|
346
|
+
snapshotReviewedStoryInternal(options = {}) {
|
|
347
|
+
const story = options.story ?? MAIN_STORY;
|
|
348
|
+
const view = options.view ?? "final";
|
|
349
|
+
if (!isFolioReviewedView(view)) throw new UnsupportedFolioReviewedViewError({
|
|
350
|
+
message: "Unsupported reviewed document view.",
|
|
351
|
+
receivedView: view
|
|
352
|
+
});
|
|
353
|
+
const sourceState = this.getEditableStoryState(story);
|
|
354
|
+
return sourceState ? createStateSnapshot(resolveReviewedState(sourceState, view)) : null;
|
|
355
|
+
}
|
|
356
|
+
projectComparisonStoriesInternal(mode) {
|
|
357
|
+
const handles = this.listStoryHandlesInternal();
|
|
358
|
+
let highestId = 0;
|
|
359
|
+
let present = false;
|
|
360
|
+
if (mode === "with-revision-census") for (const handle of handles) {
|
|
361
|
+
const state = this.getEditableStoryState(handle);
|
|
362
|
+
if (!state) continue;
|
|
363
|
+
const stats = getTrackedChangeStatsFromDoc(state.doc);
|
|
364
|
+
highestId = Math.max(highestId, stats.highestId);
|
|
365
|
+
present ||= stats.present;
|
|
366
|
+
}
|
|
367
|
+
const stories = [];
|
|
368
|
+
for (const handle of handles) stories.push({
|
|
369
|
+
handle,
|
|
370
|
+
snapshot: this.resolveReviewedStorySnapshotInternal({
|
|
371
|
+
story: handle,
|
|
372
|
+
view: "final"
|
|
373
|
+
})
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
stories,
|
|
377
|
+
revisions: {
|
|
378
|
+
highestId,
|
|
379
|
+
present
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
342
383
|
/**
|
|
343
384
|
* One story's tables, in the document order {@link snapshotStory} numbers
|
|
344
385
|
* them with, read through a reviewed view.
|
|
@@ -390,37 +431,42 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
390
431
|
readReviewedStory(options = {}) {
|
|
391
432
|
const story = options.story ?? MAIN_STORY;
|
|
392
433
|
const view = options.view ?? "final";
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
434
|
+
const snapshot = this.snapshotReviewedStoryInternal({
|
|
435
|
+
story,
|
|
436
|
+
view
|
|
396
437
|
});
|
|
397
|
-
|
|
398
|
-
if (!sourceState) return null;
|
|
399
|
-
const state = resolveReviewedState(sourceState, view);
|
|
438
|
+
if (!snapshot) return null;
|
|
400
439
|
return {
|
|
401
440
|
story,
|
|
402
441
|
view,
|
|
403
|
-
snapshot
|
|
404
|
-
text:
|
|
405
|
-
changes:
|
|
442
|
+
snapshot,
|
|
443
|
+
text: formatStorySnapshotForLLM(snapshot, view === "current-markup"),
|
|
444
|
+
changes: getTrackedChangesFromSnapshot(snapshot)
|
|
406
445
|
};
|
|
407
446
|
}
|
|
408
447
|
/** Resolve one editable story to its original or final state. */
|
|
409
448
|
resolveReviewedStory({ story = MAIN_STORY, view }) {
|
|
449
|
+
return this.resolveReviewedStorySnapshotInternal({
|
|
450
|
+
story,
|
|
451
|
+
view
|
|
452
|
+
}) !== null;
|
|
453
|
+
}
|
|
454
|
+
resolveReviewedStorySnapshotInternal({ story = MAIN_STORY, view }) {
|
|
410
455
|
if (!isFolioResolvedReviewedView(view)) throw new UnsupportedFolioReviewedViewError({
|
|
411
456
|
message: "Only original and final views can replace editable story state.",
|
|
412
457
|
receivedView: view
|
|
413
458
|
});
|
|
414
459
|
const sourceState = this.getEditableStoryState(story);
|
|
415
|
-
if (!sourceState) return
|
|
460
|
+
if (!sourceState) return null;
|
|
416
461
|
const resolvedState = resolveReviewedState(sourceState, view);
|
|
417
462
|
this.setEditableStoryState(story, resolvedState);
|
|
463
|
+
const snapshot = createStateSnapshot(resolvedState);
|
|
418
464
|
this.resolvedStoryExpectations.set(editableStoryKey(story), {
|
|
419
465
|
story,
|
|
420
|
-
text:
|
|
421
|
-
blocks:
|
|
466
|
+
text: formatStorySnapshotForLLM(snapshot, false),
|
|
467
|
+
blocks: snapshot.blocks.map(resolvedStoryBlockProjection)
|
|
422
468
|
});
|
|
423
|
-
return
|
|
469
|
+
return snapshot;
|
|
424
470
|
}
|
|
425
471
|
/**
|
|
426
472
|
* Apply operations against the current state. Reuses the live-editor applier
|
|
@@ -564,7 +610,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
564
610
|
* (clean) output flattens tracked changes and is unchanged.
|
|
565
611
|
*/
|
|
566
612
|
getContentAsText(options = {}) {
|
|
567
|
-
return
|
|
613
|
+
return formatStorySnapshotForLLM(this.snapshot(), options.annotated === true);
|
|
568
614
|
}
|
|
569
615
|
/**
|
|
570
616
|
* Header / footer and footnote / endnote text as labeled, LLM-ready lines,
|
|
@@ -608,54 +654,33 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
608
654
|
}
|
|
609
655
|
return lines.join("\n");
|
|
610
656
|
}
|
|
657
|
+
listStoryHandlesInternal() {
|
|
658
|
+
const pkg = this.baseDocument.package;
|
|
659
|
+
const handles = [{ type: "main" }];
|
|
660
|
+
for (const relationshipId of pkg.headers?.keys() ?? []) handles.push({
|
|
661
|
+
type: "header",
|
|
662
|
+
relationshipId
|
|
663
|
+
});
|
|
664
|
+
for (const relationshipId of pkg.footers?.keys() ?? []) handles.push({
|
|
665
|
+
type: "footer",
|
|
666
|
+
relationshipId
|
|
667
|
+
});
|
|
668
|
+
for (const footnote of pkg.footnotes ?? []) if (!isSeparatorFootnote(footnote)) handles.push({
|
|
669
|
+
type: "footnote",
|
|
670
|
+
noteId: footnote.id
|
|
671
|
+
});
|
|
672
|
+
for (const endnote of pkg.endnotes ?? []) if (!isSeparatorEndnote(endnote)) handles.push({
|
|
673
|
+
type: "endnote",
|
|
674
|
+
noteId: endnote.id
|
|
675
|
+
});
|
|
676
|
+
return handles;
|
|
677
|
+
}
|
|
611
678
|
/** Discover every readable document story through a typed, serializable handle. */
|
|
612
679
|
listStories() {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}];
|
|
618
|
-
for (const [relationshipId, header] of pkg.headers ?? []) {
|
|
619
|
-
const handle = {
|
|
620
|
-
type: "header",
|
|
621
|
-
relationshipId
|
|
622
|
-
};
|
|
623
|
-
stories.push({
|
|
624
|
-
handle,
|
|
625
|
-
text: this.getHeaderFooterStoryText(handle, header)
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
for (const [relationshipId, footer] of pkg.footers ?? []) {
|
|
629
|
-
const handle = {
|
|
630
|
-
type: "footer",
|
|
631
|
-
relationshipId
|
|
632
|
-
};
|
|
633
|
-
stories.push({
|
|
634
|
-
handle,
|
|
635
|
-
text: this.getHeaderFooterStoryText(handle, footer)
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
for (const footnote of pkg.footnotes ?? []) if (!isSeparatorFootnote(footnote)) {
|
|
639
|
-
const handle = {
|
|
640
|
-
type: "footnote",
|
|
641
|
-
noteId: footnote.id
|
|
642
|
-
};
|
|
643
|
-
stories.push({
|
|
644
|
-
handle,
|
|
645
|
-
text: this.getNoteStoryText(handle, footnote)
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
for (const endnote of pkg.endnotes ?? []) if (!isSeparatorEndnote(endnote)) {
|
|
649
|
-
const handle = {
|
|
650
|
-
type: "endnote",
|
|
651
|
-
noteId: endnote.id
|
|
652
|
-
};
|
|
653
|
-
stories.push({
|
|
654
|
-
handle,
|
|
655
|
-
text: this.getNoteStoryText(handle, endnote)
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
return stories;
|
|
680
|
+
return this.listStoryHandlesInternal().map((handle) => ({
|
|
681
|
+
handle,
|
|
682
|
+
text: this.getStoryText(handle)
|
|
683
|
+
}));
|
|
659
684
|
}
|
|
660
685
|
/** Read one discovered story; returns null when its handle is no longer present. */
|
|
661
686
|
readStory(handle) {
|
|
@@ -887,11 +912,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
887
912
|
story,
|
|
888
913
|
view: "current-markup"
|
|
889
914
|
});
|
|
890
|
-
const
|
|
891
|
-
const
|
|
892
|
-
const serializedBlocks = serializedState ? createStateSnapshot(serializedState).blocks.map(resolvedStoryBlockProjection) : null;
|
|
915
|
+
const serializedText = serialized ? formatStorySnapshotForLLM(serialized.snapshot, false) : null;
|
|
916
|
+
const serializedBlocks = serialized ? serialized.snapshot.blocks.map(resolvedStoryBlockProjection) : null;
|
|
893
917
|
const mismatches = [];
|
|
894
|
-
if (!serialized
|
|
918
|
+
if (!serialized) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.storyMissing);
|
|
895
919
|
else {
|
|
896
920
|
if (serialized.changes.length > 0) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.revisionMarkupRemains);
|
|
897
921
|
if (serializedText !== text) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.textProjection);
|
|
@@ -986,6 +1010,17 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
986
1010
|
const sourceText = source.type === "footnote" ? getFootnoteText(source) : getEndnoteText(source);
|
|
987
1011
|
return normalizeFolioAIBlockText(state?.doc.textContent ?? sourceText);
|
|
988
1012
|
}
|
|
1013
|
+
getStoryText(story) {
|
|
1014
|
+
if (story.type === "main") return this.getContentAsText();
|
|
1015
|
+
if (story.type === "header" || story.type === "footer") {
|
|
1016
|
+
const source = this.getHeaderFooterStory(story);
|
|
1017
|
+
if (!source) return panic("A listed document story no longer exists", { story });
|
|
1018
|
+
return this.getHeaderFooterStoryText(story, source);
|
|
1019
|
+
}
|
|
1020
|
+
const source = this.getNoteStory(story);
|
|
1021
|
+
if (!source) return panic("A listed document story no longer exists", { story });
|
|
1022
|
+
return this.getNoteStoryText(story, source);
|
|
1023
|
+
}
|
|
989
1024
|
mergeEditedSecondaryStories(document, secondaryStoryStates = this.secondaryStoryStates.values()) {
|
|
990
1025
|
let headers;
|
|
991
1026
|
let footers;
|
|
@@ -1087,6 +1122,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1087
1122
|
return handled;
|
|
1088
1123
|
}
|
|
1089
1124
|
};
|
|
1125
|
+
/** @internal Comparison-only access to atomic and fresh reviewer projections. */
|
|
1126
|
+
const getFolioDocxComparisonAccess = (reviewer) => comparisonAccessByReviewer.get(reviewer) ?? panic("Comparison access was requested for an uninitialized DOCX reviewer");
|
|
1090
1127
|
/**
|
|
1091
1128
|
* One-shot headless review: parse `buffer`, apply `operations`, and return the
|
|
1092
1129
|
* reviewed `.docx` buffer alongside the applied / skipped breakdown. Convenience
|
|
@@ -1106,4 +1143,4 @@ const applyFolioAIEditsToBuffer = async (buffer, operations, options = {}) => {
|
|
|
1106
1143
|
};
|
|
1107
1144
|
};
|
|
1108
1145
|
//#endregion
|
|
1109
|
-
export { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, FolioDocxReviewer, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
|
|
1146
|
+
export { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, FolioDocxReviewer, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, getFolioDocxComparisonAccess, isFolioResolvedReviewedView, isFolioReviewedView };
|