@stll/folio-core 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +3 -9
- package/dist/ai-edits/apply.js +94 -14
- package/dist/ai-edits/blockRange.d.ts +33 -0
- package/dist/ai-edits/blockRange.js +58 -0
- package/dist/ai-edits/headless.d.ts +37 -4
- package/dist/ai-edits/headless.js +134 -16
- package/dist/ai-edits/index.d.ts +5 -3
- package/dist/ai-edits/index.js +3 -2
- package/dist/ai-edits/snapshot.d.ts +14 -2
- package/dist/ai-edits/snapshot.js +12 -1
- package/dist/ai-edits/types.d.ts +42 -3
- package/dist/compat/eigenpal.d.ts +3 -2
- package/dist/compat/eigenpal.js +6 -5
- package/dist/controller/layoutPipeline.d.ts +1 -0
- package/dist/controller/layoutPipeline.js +35 -137
- package/dist/controller/layoutScheduler.d.ts +2 -1
- package/dist/controller/layoutScheduler.js +6 -0
- package/dist/document-operations.d.ts +139 -0
- package/dist/document-operations.js +578 -0
- package/dist/docx/blockContentParser.js +17 -7
- package/dist/docx/encryption/agileDecryption.js +2 -1
- package/dist/docx/encryption/compoundFile.js +4 -2
- package/dist/docx/footnoteParser.d.ts +1 -17
- package/dist/docx/footnoteParser.js +1 -27
- package/dist/docx/groupDrawingParser.d.ts +8 -0
- package/dist/docx/groupDrawingParser.js +131 -0
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +8 -2
- package/dist/docx/paragraphParser.js +9 -3
- package/dist/docx/runParser.js +8 -1
- package/dist/docx/selectiveXmlPatch.js +4 -10
- package/dist/docx/serializer/numberingSerializer.js +3 -1
- package/dist/docx/settingsParser.d.ts +6 -3
- package/dist/docx/settingsParser.js +2 -0
- package/dist/docx/xmlParser.js +11 -4
- package/dist/i18n/messages/catalogs.gen.d.ts +1054 -34
- package/dist/i18n/messages/catalogs.gen.js +1122 -68
- package/dist/i18n/messages/messages.gen.d.ts +62 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -5
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +37 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +138 -14
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -7
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -7
- package/dist/layout-bridge/convert/toFlowBlocks.js +56 -19
- package/dist/layout-engine/index.d.ts +6 -5
- package/dist/layout-engine/index.js +143 -30
- package/dist/layout-engine/keep-together.d.ts +13 -8
- package/dist/layout-engine/keep-together.js +40 -25
- package/dist/layout-engine/layoutInstrumentation.d.ts +11 -1
- package/dist/layout-engine/layoutInstrumentation.js +10 -1
- package/dist/layout-engine/measure/measureBlocks.js +50 -18
- package/dist/layout-engine/measure/measureParagraph.js +61 -15
- package/dist/layout-engine/paginator.d.ts +2 -1
- package/dist/layout-engine/paginator.js +9 -3
- package/dist/layout-engine/textBoxGroup.d.ts +7 -0
- package/dist/layout-engine/textBoxGroup.js +8 -0
- package/dist/layout-engine/types.d.ts +30 -9
- package/dist/layout-engine/types.js +20 -2
- package/dist/layout-painter/renderPage.d.ts +15 -1
- package/dist/layout-painter/renderPage.js +27 -11
- package/dist/layout-painter/renderParagraph.js +18 -8
- package/dist/layout-painter/renderTable.js +10 -7
- package/dist/managers/DocumentLoaderManager.js +12 -5
- package/dist/model.js +1 -1
- package/dist/paged-layout/pageText.d.ts +14 -0
- package/dist/paged-layout/pageText.js +21 -0
- package/dist/prosemirror/attrs/index.js +2 -0
- package/dist/prosemirror/commands/comments.js +75 -5
- package/dist/prosemirror/commands/hyperlink.js +4 -2
- package/dist/prosemirror/commands/image.js +2 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +68 -0
- package/dist/prosemirror/commands/propertyChangeScope.js +242 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +14 -3
- package/dist/prosemirror/conversion/toProseDoc.js +29 -8
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +10 -11
- package/dist/prosemirror/findReplaceSelection.js +2 -1
- package/dist/prosemirror/schema/nodes.d.ts +10 -5
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/utils/tabCalculator.js +4 -2
- package/dist/redline.d.ts +20 -0
- package/dist/redline.js +127 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.js +6 -2
- package/dist/symbols.d.ts +35 -0
- package/dist/symbols.js +512 -0
- package/dist/utils/clipboard.js +3 -3
- package/dist/utils/fontLoader.js +3 -0
- package/dist/utils/fontResolver.js +48 -1
- package/dist/utils/zoom.d.ts +21 -0
- package/dist/utils/zoom.js +21 -0
- package/dist/version-comparison.d.ts +92 -0
- package/dist/version-comparison.js +436 -0
- package/package.json +3 -3
- package/dist/paged-layout/headerFooterMargins.d.ts +0 -75
- package/dist/paged-layout/headerFooterMargins.js +0 -122
package/dist/ai-edits/apply.d.ts
CHANGED
|
@@ -23,13 +23,7 @@ type ApplyFolioAIEditOperationsOptions = {
|
|
|
23
23
|
author?: string;
|
|
24
24
|
createCommentId?: (text: string) => number;
|
|
25
25
|
};
|
|
26
|
-
declare const applyFolioAIEditOperations: (
|
|
27
|
-
|
|
28
|
-
snapshot,
|
|
29
|
-
operations,
|
|
30
|
-
mode,
|
|
31
|
-
author,
|
|
32
|
-
createCommentId
|
|
33
|
-
}: ApplyFolioAIEditOperationsOptions) => FolioAIEditApplyResult;
|
|
26
|
+
declare const applyFolioAIEditOperations: (options: ApplyFolioAIEditOperationsOptions) => FolioAIEditApplyResult;
|
|
27
|
+
declare const previewFolioAIEditOperations: (options: ApplyFolioAIEditOperationsOptions) => FolioAIEditApplyResult;
|
|
34
28
|
//#endregion
|
|
35
|
-
export { FolioAIEditView, applyFolioAIEditOperations };
|
|
29
|
+
export { FolioAIEditView, applyFolioAIEditOperations, previewFolioAIEditOperations };
|
package/dist/ai-edits/apply.js
CHANGED
|
@@ -193,7 +193,7 @@ const buildEmphasisInlineContent = (schema, text, baseMarks) => {
|
|
|
193
193
|
}
|
|
194
194
|
return nodes.length > 0 ? nodes : [schema.text(text, [...baseMarks])];
|
|
195
195
|
};
|
|
196
|
-
const
|
|
196
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", createCommentId, revisionIdSeed }) => {
|
|
197
197
|
const applied = [];
|
|
198
198
|
const skipped = [];
|
|
199
199
|
const resolved = [];
|
|
@@ -210,6 +210,13 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
210
210
|
const liveBlocks = collectLiveBlocksByHash(view.state.doc);
|
|
211
211
|
const liveBlocksByParaId = collectLiveBlocksByParaId(view.state.doc);
|
|
212
212
|
for (const [index, operation] of operations.entries()) {
|
|
213
|
+
if (mode === "tracked-changes" && operation.type === "formatRange") {
|
|
214
|
+
skipped.push({
|
|
215
|
+
id: operation.id,
|
|
216
|
+
reason: "unsupportedMode"
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
213
220
|
const commentText = getOperationCommentText(operation);
|
|
214
221
|
if (commentText !== void 0 && (!commentType || createCommentId === void 0)) {
|
|
215
222
|
skipped.push({
|
|
@@ -244,7 +251,7 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
244
251
|
skipped
|
|
245
252
|
};
|
|
246
253
|
let tr = view.state.tr;
|
|
247
|
-
let revisionSeed = nextRevisionSeed(resolved.length);
|
|
254
|
+
let revisionSeed = revisionIdSeed ?? nextRevisionSeed(resolved.length);
|
|
248
255
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
249
256
|
for (const item of resolved.toSorted((left, right) => {
|
|
250
257
|
if (left.from !== right.from) return right.from - left.from;
|
|
@@ -254,7 +261,8 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
254
261
|
const stepsBefore = tr.steps.length;
|
|
255
262
|
let appliedRevisionIds;
|
|
256
263
|
switch (item.operation.type) {
|
|
257
|
-
case "replaceInBlock":
|
|
264
|
+
case "replaceInBlock":
|
|
265
|
+
case "replaceRange": {
|
|
258
266
|
const revisionIdDelete = revisionSeed++;
|
|
259
267
|
const revisionIdInsert = revisionSeed++;
|
|
260
268
|
tr = applyTextReplacement({
|
|
@@ -270,6 +278,17 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
270
278
|
if (mode === "tracked-changes") appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
|
|
271
279
|
break;
|
|
272
280
|
}
|
|
281
|
+
case "commentOnRange":
|
|
282
|
+
if (commentMark) tr = tr.addMark(item.from, item.to, commentMark);
|
|
283
|
+
break;
|
|
284
|
+
case "formatRange":
|
|
285
|
+
for (const [name, enabled] of Object.entries(item.operation.formatting)) {
|
|
286
|
+
const markType = view.state.schema.marks[name];
|
|
287
|
+
if (!markType) continue;
|
|
288
|
+
if (enabled) tr = tr.addMark(item.from, item.to, name === "underline" ? markType.create({ style: "single" }) : markType.create());
|
|
289
|
+
else tr = tr.removeMark(item.from, item.to, markType);
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
273
292
|
case "replaceBlock": {
|
|
274
293
|
const revisionIdDelete = revisionSeed++;
|
|
275
294
|
const revisionIdInsert = revisionSeed++;
|
|
@@ -314,7 +333,7 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
314
333
|
if (mode === "tracked-changes" && item.operation.pageBreakBefore === true && (!item.insertText || item.insertText.length === 0)) {
|
|
315
334
|
skipped.push({
|
|
316
335
|
id: item.operation.id,
|
|
317
|
-
reason: "
|
|
336
|
+
reason: "unsupportedMode"
|
|
318
337
|
});
|
|
319
338
|
continue;
|
|
320
339
|
}
|
|
@@ -338,6 +357,7 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
338
357
|
attrs["listMarker"] = null;
|
|
339
358
|
attrs["listMarkerHidden"] = null;
|
|
340
359
|
attrs["listLevelNumFmts"] = null;
|
|
360
|
+
attrs["listLevelStarts"] = null;
|
|
341
361
|
attrs["listAbstractNumId"] = null;
|
|
342
362
|
attrs["listStartOverride"] = null;
|
|
343
363
|
}
|
|
@@ -350,7 +370,7 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
350
370
|
if (mode === "tracked-changes") {
|
|
351
371
|
skipped.push({
|
|
352
372
|
id: item.operation.id,
|
|
353
|
-
reason: "
|
|
373
|
+
reason: "unsupportedMode"
|
|
354
374
|
});
|
|
355
375
|
continue;
|
|
356
376
|
}
|
|
@@ -411,15 +431,36 @@ const applyFolioAIEditOperations = ({ view, snapshot, operations, mode = "tracke
|
|
|
411
431
|
skipped
|
|
412
432
|
};
|
|
413
433
|
};
|
|
434
|
+
const applyFolioAIEditOperations = (options) => applyFolioAIEditOperationsInternal(options);
|
|
435
|
+
const previewFolioAIEditOperations = (options) => {
|
|
436
|
+
const { view, createCommentId, ...applyOptions } = options;
|
|
437
|
+
let previewCommentId = -1;
|
|
438
|
+
const previewView = {
|
|
439
|
+
state: view.state,
|
|
440
|
+
dispatch: (transaction) => {
|
|
441
|
+
previewView.state = previewView.state.apply(transaction);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const result = applyFolioAIEditOperationsInternal({
|
|
445
|
+
...applyOptions,
|
|
446
|
+
view: previewView,
|
|
447
|
+
...createCommentId !== void 0 && { createCommentId: () => previewCommentId-- },
|
|
448
|
+
revisionIdSeed: -1e9
|
|
449
|
+
});
|
|
450
|
+
return {
|
|
451
|
+
applied: result.applied.map(({ id }) => ({ id })),
|
|
452
|
+
skipped: result.skipped
|
|
453
|
+
};
|
|
454
|
+
};
|
|
414
455
|
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark }) => {
|
|
415
456
|
let nextTr = tr;
|
|
416
457
|
const replacement = stripInlineEmphasisMarkers((() => {
|
|
417
|
-
if (item.operation.type === "replaceInBlock") return item.operation.replace;
|
|
458
|
+
if (item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") return item.operation.replace;
|
|
418
459
|
if (item.operation.type === "replaceBlock") return item.operation.text;
|
|
419
460
|
return "";
|
|
420
461
|
})());
|
|
421
462
|
if (mode === "direct") {
|
|
422
|
-
if (item.operation.type === "replaceInBlock" && hasInlineEmphasis(item.operation.replace)) {
|
|
463
|
+
if ((item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") && hasInlineEmphasis(item.operation.replace)) {
|
|
423
464
|
const content = buildEmphasisInlineContent(nextTr.doc.type.schema, item.operation.replace, commentMark ? [commentMark] : []);
|
|
424
465
|
return nextTr.replaceWith(item.from, item.to, content);
|
|
425
466
|
}
|
|
@@ -447,6 +488,9 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
447
488
|
sourceText = item.operation.find;
|
|
448
489
|
sourceCleanStart = cleanBlock.text.indexOf(item.operation.find);
|
|
449
490
|
if (sourceCleanStart === -1) sourceText = null;
|
|
491
|
+
} else if (item.operation.type === "replaceRange") {
|
|
492
|
+
sourceCleanStart = item.operation.range.startOffset;
|
|
493
|
+
sourceText = cleanBlock.text.slice(sourceCleanStart, item.operation.range.endOffset);
|
|
450
494
|
} else if (item.operation.type === "replaceBlock") {
|
|
451
495
|
sourceText = cleanBlock.text;
|
|
452
496
|
sourceCleanStart = 0;
|
|
@@ -518,12 +562,13 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
518
562
|
return nextTr;
|
|
519
563
|
};
|
|
520
564
|
const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId, doc }) => {
|
|
521
|
-
const
|
|
565
|
+
const blockId = operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange" ? operation.range.blockId : operation.blockId;
|
|
566
|
+
const anchor = snapshot.anchors[blockId];
|
|
522
567
|
if (!anchor) return {
|
|
523
568
|
type: "skip",
|
|
524
569
|
reason: "missingBlock"
|
|
525
570
|
};
|
|
526
|
-
const encodedParaId = getFolioParaIdFromBlockId(
|
|
571
|
+
const encodedParaId = getFolioParaIdFromBlockId(blockId);
|
|
527
572
|
let live;
|
|
528
573
|
if (encodedParaId !== null) {
|
|
529
574
|
live = liveBlocksByParaId.get(encodedParaId);
|
|
@@ -532,7 +577,7 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
532
577
|
reason: "missingBlock"
|
|
533
578
|
};
|
|
534
579
|
} else {
|
|
535
|
-
const ordinal = ordinalAmongSameHash(snapshot,
|
|
580
|
+
const ordinal = ordinalAmongSameHash(snapshot, blockId);
|
|
536
581
|
if (ordinal < 0) return {
|
|
537
582
|
type: "skip",
|
|
538
583
|
reason: "missingBlock"
|
|
@@ -548,10 +593,44 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
548
593
|
const blockTo = live.to;
|
|
549
594
|
const cleanBlock = buildCleanBlockText(blockNode, blockFrom);
|
|
550
595
|
const currentText = cleanBlock.text;
|
|
551
|
-
|
|
596
|
+
const currentTextHash = hashFolioAIBlockText(normalizeFolioAIBlockText(currentText));
|
|
597
|
+
if (operation.precondition !== void 0 && currentTextHash !== operation.precondition.blockTextHash) return {
|
|
598
|
+
type: "skip",
|
|
599
|
+
reason: "preconditionFailed"
|
|
600
|
+
};
|
|
601
|
+
if (currentTextHash !== anchor.textHash) return {
|
|
552
602
|
type: "skip",
|
|
553
603
|
reason: "changedBlock"
|
|
554
604
|
};
|
|
605
|
+
if (operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange") {
|
|
606
|
+
const { startOffset, endOffset, selectedTextHash } = operation.range;
|
|
607
|
+
const from = cleanBlock.offsets[startOffset];
|
|
608
|
+
const to = cleanBlock.offsets[endOffset];
|
|
609
|
+
if (from === void 0 || to === void 0) return {
|
|
610
|
+
type: "skip",
|
|
611
|
+
reason: "staleRange"
|
|
612
|
+
};
|
|
613
|
+
const selectedText = currentText.slice(startOffset, endOffset);
|
|
614
|
+
if (hashFolioAIBlockText(selectedText) !== selectedTextHash) return {
|
|
615
|
+
type: "skip",
|
|
616
|
+
reason: "staleRange"
|
|
617
|
+
};
|
|
618
|
+
if (operation.type === "replaceRange" && selectedText === operation.replace) return {
|
|
619
|
+
type: "skip",
|
|
620
|
+
reason: "noopOperation"
|
|
621
|
+
};
|
|
622
|
+
return {
|
|
623
|
+
type: "resolved",
|
|
624
|
+
operation: {
|
|
625
|
+
operation,
|
|
626
|
+
from,
|
|
627
|
+
to,
|
|
628
|
+
blockFrom,
|
|
629
|
+
blockTo,
|
|
630
|
+
blockNode
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
555
634
|
if (operation.type === "insertAfterBlock" || operation.type === "insertBeforeBlock") {
|
|
556
635
|
if (operation.text.length === 0 && operation.pageBreakBefore !== true) return {
|
|
557
636
|
type: "skip",
|
|
@@ -623,7 +702,8 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
623
702
|
type: "skip",
|
|
624
703
|
reason: "noopOperation"
|
|
625
704
|
};
|
|
626
|
-
const
|
|
705
|
+
const quote = getOperationQuote(operation);
|
|
706
|
+
const range = resolveTextInCleanBlock(cleanBlock, quote || currentText);
|
|
627
707
|
if (range.type !== "resolved") return range;
|
|
628
708
|
return {
|
|
629
709
|
type: "resolved",
|
|
@@ -674,10 +754,10 @@ const getTextRangeFromCleanBlock = (cleanBlock) => {
|
|
|
674
754
|
to
|
|
675
755
|
};
|
|
676
756
|
};
|
|
677
|
-
const getOperationCommentText = (operation) => operation.comment?.text;
|
|
757
|
+
const getOperationCommentText = (operation) => "comment" in operation ? operation.comment?.text : void 0;
|
|
678
758
|
const getOperationQuote = (operation) => {
|
|
679
759
|
if (operation.type === "replaceInBlock") return operation.find;
|
|
680
760
|
if (operation.type === "commentOnBlock") return operation.quote;
|
|
681
761
|
};
|
|
682
762
|
//#endregion
|
|
683
|
-
export { applyFolioAIEditOperations };
|
|
763
|
+
export { applyFolioAIEditOperations, previewFolioAIEditOperations };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FolioAIEditSnapshot } from "./types.js";
|
|
2
|
+
import { Node } from "prosemirror-model";
|
|
3
|
+
|
|
4
|
+
//#region src/ai-edits/blockRange.d.ts
|
|
5
|
+
type DocPositionRange = {
|
|
6
|
+
from: number;
|
|
7
|
+
to: number;
|
|
8
|
+
};
|
|
9
|
+
type ResolveFolioAIBlockRangeOptions = {
|
|
10
|
+
blockId: string;
|
|
11
|
+
doc: Node;
|
|
12
|
+
snapshot?: FolioAIEditSnapshot | null | undefined;
|
|
13
|
+
};
|
|
14
|
+
declare const resolveFolioAIBlockRange: ({
|
|
15
|
+
blockId,
|
|
16
|
+
doc,
|
|
17
|
+
snapshot
|
|
18
|
+
}: ResolveFolioAIBlockRangeOptions) => DocPositionRange | null;
|
|
19
|
+
/**
|
|
20
|
+
* Clamp a `{from, to}` pair so both endpoints fit inside a document of
|
|
21
|
+
* `docSize` (in PM content positions). Block-boundary snapshots and stale
|
|
22
|
+
* range data sometimes produce a `to` one past the last inline position;
|
|
23
|
+
* `view.state.doc.resolve(...)` rejects that with
|
|
24
|
+
* "Position … out of range", and `TextSelection.between` doesn't help — it
|
|
25
|
+
* needs *valid* resolved positions. Clamping before resolution is the cheap
|
|
26
|
+
* defensive step.
|
|
27
|
+
*
|
|
28
|
+
* Order is preserved: if both endpoints exceed `docSize`, the returned
|
|
29
|
+
* `from` may equal `to`, yielding a cursor selection at the doc end.
|
|
30
|
+
*/
|
|
31
|
+
declare function clampRangeToDocSize(docSize: number, range: DocPositionRange): DocPositionRange;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex } from "../types/block-id.js";
|
|
2
|
+
import { createFolioAIEditSnapshot } from "./snapshot.js";
|
|
3
|
+
import { findParagraphByParaId } from "../prosemirror/utils/findParagraphByParaId.js";
|
|
4
|
+
//#region src/ai-edits/blockRange.ts
|
|
5
|
+
const resolveFolioAIBlockRange = ({ blockId, doc, snapshot }) => {
|
|
6
|
+
const paraId = getFolioParaIdFromBlockId(blockId);
|
|
7
|
+
if (paraId !== null) {
|
|
8
|
+
const liveRange = findParagraphByParaId(doc, paraId);
|
|
9
|
+
if (liveRange !== null) return {
|
|
10
|
+
from: liveRange.from,
|
|
11
|
+
to: liveRange.to
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const resolvedSnapshot = snapshot ?? createFolioAIEditSnapshot(doc);
|
|
15
|
+
const anchor = resolvedSnapshot.anchors[blockId] ?? resolveSequentialBlockAnchor(blockId, resolvedSnapshot);
|
|
16
|
+
if (!anchor) return null;
|
|
17
|
+
return clampRangeToDocSize(doc.content.size, anchor);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a `seq-NNNN` fallback id by document position.
|
|
21
|
+
*
|
|
22
|
+
* A sequential id is only minted for a paragraph the source DOCX left
|
|
23
|
+
* without a `w14:paraId`. The live editor's `ParaIdAllocator` fills
|
|
24
|
+
* that gap with a fresh random hex paraId, so a snapshot of the live
|
|
25
|
+
* document keys the block by that hex and never reproduces the
|
|
26
|
+
* server's `seq-NNNN`: the direct anchor lookup misses, and a
|
|
27
|
+
* paraId-based live lookup can't match either (no node carries a
|
|
28
|
+
* `seq-` paraId). The seq number is the block's 1-based position in
|
|
29
|
+
* the same non-empty-block walk the server extractor and
|
|
30
|
+
* `createFolioAIEditSnapshot` share, so it indexes the snapshot's
|
|
31
|
+
* ordered `blocks` directly.
|
|
32
|
+
*/
|
|
33
|
+
const resolveSequentialBlockAnchor = (blockId, snapshot) => {
|
|
34
|
+
const index = getSequentialFolioBlockIdIndex(blockId);
|
|
35
|
+
if (index === null) return;
|
|
36
|
+
const block = snapshot.blocks.at(index - 1);
|
|
37
|
+
return block ? snapshot.anchors[block.id] : void 0;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Clamp a `{from, to}` pair so both endpoints fit inside a document of
|
|
41
|
+
* `docSize` (in PM content positions). Block-boundary snapshots and stale
|
|
42
|
+
* range data sometimes produce a `to` one past the last inline position;
|
|
43
|
+
* `view.state.doc.resolve(...)` rejects that with
|
|
44
|
+
* "Position … out of range", and `TextSelection.between` doesn't help — it
|
|
45
|
+
* needs *valid* resolved positions. Clamping before resolution is the cheap
|
|
46
|
+
* defensive step.
|
|
47
|
+
*
|
|
48
|
+
* Order is preserved: if both endpoints exceed `docSize`, the returned
|
|
49
|
+
* `from` may equal `to`, yielding a cursor selection at the doc end.
|
|
50
|
+
*/
|
|
51
|
+
function clampRangeToDocSize(docSize, range) {
|
|
52
|
+
return {
|
|
53
|
+
from: Math.min(Math.max(range.from, 0), docSize),
|
|
54
|
+
to: Math.min(Math.max(range.to, 0), docSize)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { clampRangeToDocSize, resolveFolioAIBlockRange };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FolioAIBlock, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
|
|
2
2
|
import { document_d_exports } from "../types/document.js";
|
|
3
|
+
import { FolioDocumentOperationBatch, FolioDocumentOperationResult, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult } from "../document-operations.js";
|
|
3
4
|
import { FolioReviewChange, FolioReviewChangeKind } from "./read.js";
|
|
4
5
|
|
|
5
6
|
//#region src/ai-edits/headless.d.ts
|
|
@@ -18,6 +19,8 @@ type FolioApplyOperationsOptions = {
|
|
|
18
19
|
*/
|
|
19
20
|
snapshot?: FolioAIEditSnapshot;
|
|
20
21
|
};
|
|
22
|
+
/** Options for {@link FolioDocxReviewer.applyDocumentOperations}. */
|
|
23
|
+
type FolioApplyDocumentOperationsOptions = Omit<FolioApplyOperationsOptions, "mode">;
|
|
21
24
|
/** Options for {@link FolioDocxReviewer.getContentAsText}. */
|
|
22
25
|
type FolioGetContentAsTextOptions = {
|
|
23
26
|
/**
|
|
@@ -27,6 +30,19 @@ type FolioGetContentAsTextOptions = {
|
|
|
27
30
|
*/
|
|
28
31
|
annotated?: boolean;
|
|
29
32
|
};
|
|
33
|
+
type FolioDocumentStoryHandle = {
|
|
34
|
+
type: "main";
|
|
35
|
+
} | {
|
|
36
|
+
type: "header" | "footer";
|
|
37
|
+
relationshipId: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: "footnote" | "endnote";
|
|
40
|
+
noteId: number;
|
|
41
|
+
};
|
|
42
|
+
type FolioDocumentStory = {
|
|
43
|
+
handle: FolioDocumentStoryHandle;
|
|
44
|
+
text: string;
|
|
45
|
+
};
|
|
30
46
|
/** Filter for {@link FolioDocxReviewer.getChanges}. */
|
|
31
47
|
type FolioReviewChangeFilter = {
|
|
32
48
|
author?: string;
|
|
@@ -82,6 +98,7 @@ declare class FolioDocxReviewer {
|
|
|
82
98
|
private readonly originalBuffer;
|
|
83
99
|
private state;
|
|
84
100
|
private readonly createdComments;
|
|
101
|
+
private readonly documentOperationUndoEntries;
|
|
85
102
|
/**
|
|
86
103
|
* Resolved-state overrides recorded by {@link resolveComment}, keyed by
|
|
87
104
|
* comment id. Applied on read ({@link getComments}) and on write
|
|
@@ -105,6 +122,16 @@ declare class FolioDocxReviewer {
|
|
|
105
122
|
* (including any comments) is retained for {@link toBuffer}.
|
|
106
123
|
*/
|
|
107
124
|
applyOperations(operations: FolioAIEditOperation[], options?: FolioApplyOperationsOptions): FolioAIEditApplyResult;
|
|
125
|
+
/**
|
|
126
|
+
* Apply a versioned document-operation batch against the current state.
|
|
127
|
+
* This is the contract entry point for serialized callers; the legacy
|
|
128
|
+
* {@link applyOperations} method delegates here so both APIs keep identical
|
|
129
|
+
* edit semantics.
|
|
130
|
+
*/
|
|
131
|
+
applyDocumentOperations(batch: FolioDocumentOperationBatch, options?: FolioApplyDocumentOperationsOptions): FolioDocumentOperationResult;
|
|
132
|
+
private applyDocumentOperationsInternal;
|
|
133
|
+
/** Undo the latest unchanged document-operation batch and its created comments. */
|
|
134
|
+
undoDocumentOperations(undoHandle: FolioDocumentOperationUndoHandle): FolioDocumentOperationUndoResult;
|
|
108
135
|
/**
|
|
109
136
|
* The body blocks with their stable ids, in document order — the same
|
|
110
137
|
* `FolioAIBlock` shape {@link snapshot} builds, reused verbatim so the ids
|
|
@@ -131,6 +158,10 @@ declare class FolioDocxReviewer {
|
|
|
131
158
|
* Empty parts and separator notes are omitted.
|
|
132
159
|
*/
|
|
133
160
|
getNotesAsText(): string;
|
|
161
|
+
/** Discover every readable document story through a typed, serializable handle. */
|
|
162
|
+
listStories(): FolioDocumentStory[];
|
|
163
|
+
/** Read one discovered story; returns null when its handle is no longer present. */
|
|
164
|
+
readStory(handle: FolioDocumentStoryHandle): FolioDocumentStory | null;
|
|
134
165
|
/**
|
|
135
166
|
* The tracked changes (insertions and deletions) present in the body, read
|
|
136
167
|
* from the same `insertion` / `deletion` marks the editor renders. Each
|
|
@@ -210,9 +241,11 @@ declare class FolioDocxReviewer {
|
|
|
210
241
|
private trySelectiveSave;
|
|
211
242
|
/**
|
|
212
243
|
* Count every tracked change an accept-all / reject-all sweep resolves: the
|
|
213
|
-
* inline insertion / deletion groups {@link getChanges} enumerates, plus
|
|
214
|
-
*
|
|
215
|
-
* paragraph
|
|
244
|
+
* inline insertion / deletion groups {@link getChanges} enumerates, plus the
|
|
245
|
+
* property-change records living on node attrs rather than inline marks —
|
|
246
|
+
* paragraph-level (`pPrMark`, `_propertyChanges`, the inline sectPr's
|
|
247
|
+
* `propertyChanges`) and table-level (`tblPrChange` / `trPrChange` /
|
|
248
|
+
* `tcPrChange`).
|
|
216
249
|
*/
|
|
217
250
|
private countTrackedChanges;
|
|
218
251
|
/** Apply any {@link resolveComment} overrides recorded for these comments. */
|
|
@@ -250,4 +283,4 @@ type ApplyFolioAIEditsToBufferResult = FolioAIEditApplyResult & {
|
|
|
250
283
|
*/
|
|
251
284
|
declare const applyFolioAIEditsToBuffer: (buffer: ArrayBuffer, operations: FolioAIEditOperation[], options?: ApplyFolioAIEditsToBufferOptions) => Promise<ApplyFolioAIEditsToBufferResult>;
|
|
252
285
|
//#endregion
|
|
253
|
-
export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FolioApplyOperationsOptions, FolioDocxReviewer, FolioDocxReviewerOptions, FolioGetContentAsTextOptions, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, applyFolioAIEditsToBuffer };
|
|
286
|
+
export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FolioApplyDocumentOperationsOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocxReviewer, FolioDocxReviewerOptions, FolioGetContentAsTextOptions, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, applyFolioAIEditsToBuffer };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
2
|
+
import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
3
|
+
import { applyFolioDocumentOperations } from "../document-operations.js";
|
|
1
4
|
import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
2
5
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
3
6
|
import { repackDocx } from "../docx/rezip.js";
|
|
4
|
-
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
5
|
-
import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
6
|
-
import { applyFolioAIEditOperations } from "./apply.js";
|
|
7
7
|
import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
8
8
|
import { createReply } from "../docx/replyToComment.js";
|
|
9
9
|
import { attemptSelectiveSave } from "../docx/selectiveSave.js";
|
|
@@ -44,6 +44,7 @@ import { EditorState } from "prosemirror-state";
|
|
|
44
44
|
* across reviewers created within the same millisecond.
|
|
45
45
|
*/
|
|
46
46
|
let commentIdCursor = Date.now();
|
|
47
|
+
let undoHandleCursor = Date.now();
|
|
47
48
|
/**
|
|
48
49
|
* Build the note-free comment thread the apply layer references by id.
|
|
49
50
|
* Mirrors `commentsHelpers.createComment` (a pure object literal there) so a
|
|
@@ -162,6 +163,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
162
163
|
originalBuffer;
|
|
163
164
|
state;
|
|
164
165
|
createdComments = [];
|
|
166
|
+
documentOperationUndoEntries = [];
|
|
165
167
|
/**
|
|
166
168
|
* Resolved-state overrides recorded by {@link resolveComment}, keyed by
|
|
167
169
|
* comment id. Applied on read ({@link getComments}) and on write
|
|
@@ -210,28 +212,91 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
210
212
|
* (including any comments) is retained for {@link toBuffer}.
|
|
211
213
|
*/
|
|
212
214
|
applyOperations(operations, options = {}) {
|
|
213
|
-
const
|
|
215
|
+
const { applied, skipped } = this.applyDocumentOperationsInternal({
|
|
216
|
+
version: 1,
|
|
217
|
+
operations,
|
|
218
|
+
mode: options.mode ?? "tracked-changes"
|
|
219
|
+
}, { ...options.snapshot !== void 0 && { snapshot: options.snapshot } }, false);
|
|
220
|
+
return {
|
|
221
|
+
applied,
|
|
222
|
+
skipped
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Apply a versioned document-operation batch against the current state.
|
|
227
|
+
* This is the contract entry point for serialized callers; the legacy
|
|
228
|
+
* {@link applyOperations} method delegates here so both APIs keep identical
|
|
229
|
+
* edit semantics.
|
|
230
|
+
*/
|
|
231
|
+
applyDocumentOperations(batch, options = {}) {
|
|
232
|
+
return this.applyDocumentOperationsInternal(batch, options, true);
|
|
233
|
+
}
|
|
234
|
+
applyDocumentOperationsInternal(batch, options, createUndoEntry) {
|
|
235
|
+
const beforeState = this.state;
|
|
236
|
+
const createdCommentsLengthBefore = this.createdComments.length;
|
|
214
237
|
const view = {
|
|
215
238
|
state: this.state,
|
|
216
239
|
dispatch: (transaction) => {
|
|
217
240
|
view.state = view.state.apply(transaction);
|
|
218
241
|
}
|
|
219
242
|
};
|
|
220
|
-
const result =
|
|
243
|
+
const result = applyFolioDocumentOperations({
|
|
221
244
|
view,
|
|
222
|
-
snapshot,
|
|
223
|
-
|
|
224
|
-
mode: options.mode ?? "tracked-changes",
|
|
245
|
+
snapshot: options.snapshot ?? this.snapshot(),
|
|
246
|
+
batch,
|
|
225
247
|
author: this.author,
|
|
226
248
|
createCommentId: (text) => {
|
|
227
249
|
const comment = createReviewerComment(text, this.author);
|
|
228
250
|
this.createdComments.push(comment);
|
|
229
251
|
return comment.id;
|
|
230
|
-
}
|
|
252
|
+
},
|
|
253
|
+
...createUndoEntry && { createUndoHandle: () => ({
|
|
254
|
+
type: "documentOperationUndo",
|
|
255
|
+
id: `headless-${String(undoHandleCursor++)}`
|
|
256
|
+
}) }
|
|
231
257
|
});
|
|
232
258
|
this.state = view.state;
|
|
259
|
+
if (result.undoHandle !== null) this.documentOperationUndoEntries.push({
|
|
260
|
+
undoHandle: result.undoHandle,
|
|
261
|
+
beforeState,
|
|
262
|
+
afterState: this.state,
|
|
263
|
+
createdCommentsLengthBefore,
|
|
264
|
+
createdCommentsLengthAfter: this.createdComments.length
|
|
265
|
+
});
|
|
233
266
|
return result;
|
|
234
267
|
}
|
|
268
|
+
/** Undo the latest unchanged document-operation batch and its created comments. */
|
|
269
|
+
undoDocumentOperations(undoHandle) {
|
|
270
|
+
const entryIndex = this.documentOperationUndoEntries.findIndex((entry) => entry.undoHandle.type === undoHandle.type && entry.undoHandle.id === undoHandle.id);
|
|
271
|
+
if (entryIndex === -1) return {
|
|
272
|
+
status: "rejected",
|
|
273
|
+
undoHandle,
|
|
274
|
+
reason: "unknownHandle"
|
|
275
|
+
};
|
|
276
|
+
if (entryIndex !== this.documentOperationUndoEntries.length - 1) return {
|
|
277
|
+
status: "rejected",
|
|
278
|
+
undoHandle,
|
|
279
|
+
reason: "notLatest"
|
|
280
|
+
};
|
|
281
|
+
const entry = this.documentOperationUndoEntries.at(-1);
|
|
282
|
+
if (!entry) return {
|
|
283
|
+
status: "rejected",
|
|
284
|
+
undoHandle,
|
|
285
|
+
reason: "unknownHandle"
|
|
286
|
+
};
|
|
287
|
+
if (this.state !== entry.afterState || this.createdComments.length !== entry.createdCommentsLengthAfter) return {
|
|
288
|
+
status: "rejected",
|
|
289
|
+
undoHandle,
|
|
290
|
+
reason: "documentChanged"
|
|
291
|
+
};
|
|
292
|
+
this.state = entry.beforeState;
|
|
293
|
+
this.createdComments.length = entry.createdCommentsLengthBefore;
|
|
294
|
+
this.documentOperationUndoEntries.pop();
|
|
295
|
+
return {
|
|
296
|
+
status: "undone",
|
|
297
|
+
undoHandle
|
|
298
|
+
};
|
|
299
|
+
}
|
|
235
300
|
/**
|
|
236
301
|
* The body blocks with their stable ids, in document order — the same
|
|
237
302
|
* `FolioAIBlock` shape {@link snapshot} builds, reused verbatim so the ids
|
|
@@ -259,7 +324,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
259
324
|
return snapshot.blocks.map((block) => {
|
|
260
325
|
const from = startById.get(block.id);
|
|
261
326
|
const node = from === void 0 ? null : this.state.doc.nodeAt(from);
|
|
262
|
-
|
|
327
|
+
const text = node ? buildAnnotatedBlockText(node) : block.text;
|
|
328
|
+
return formatBlockLine(block, text);
|
|
263
329
|
}).join("\n");
|
|
264
330
|
}
|
|
265
331
|
/**
|
|
@@ -293,6 +359,50 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
293
359
|
}
|
|
294
360
|
return lines.join("\n");
|
|
295
361
|
}
|
|
362
|
+
/** Discover every readable document story through a typed, serializable handle. */
|
|
363
|
+
listStories() {
|
|
364
|
+
const pkg = this.baseDocument.package;
|
|
365
|
+
const stories = [{
|
|
366
|
+
handle: { type: "main" },
|
|
367
|
+
text: this.getContentAsText()
|
|
368
|
+
}];
|
|
369
|
+
for (const [relationshipId, header] of pkg.headers ?? []) stories.push({
|
|
370
|
+
handle: {
|
|
371
|
+
type: "header",
|
|
372
|
+
relationshipId
|
|
373
|
+
},
|
|
374
|
+
text: normalizeFolioAIBlockText(getHeaderFooterText(header))
|
|
375
|
+
});
|
|
376
|
+
for (const [relationshipId, footer] of pkg.footers ?? []) stories.push({
|
|
377
|
+
handle: {
|
|
378
|
+
type: "footer",
|
|
379
|
+
relationshipId
|
|
380
|
+
},
|
|
381
|
+
text: normalizeFolioAIBlockText(getHeaderFooterText(footer))
|
|
382
|
+
});
|
|
383
|
+
for (const footnote of pkg.footnotes ?? []) if (!isSeparatorFootnote(footnote)) stories.push({
|
|
384
|
+
handle: {
|
|
385
|
+
type: "footnote",
|
|
386
|
+
noteId: footnote.id
|
|
387
|
+
},
|
|
388
|
+
text: normalizeFolioAIBlockText(getFootnoteText(footnote))
|
|
389
|
+
});
|
|
390
|
+
for (const endnote of pkg.endnotes ?? []) if (!isSeparatorEndnote(endnote)) stories.push({
|
|
391
|
+
handle: {
|
|
392
|
+
type: "endnote",
|
|
393
|
+
noteId: endnote.id
|
|
394
|
+
},
|
|
395
|
+
text: normalizeFolioAIBlockText(getEndnoteText(endnote))
|
|
396
|
+
});
|
|
397
|
+
return stories;
|
|
398
|
+
}
|
|
399
|
+
/** Read one discovered story; returns null when its handle is no longer present. */
|
|
400
|
+
readStory(handle) {
|
|
401
|
+
const stories = this.listStories();
|
|
402
|
+
if (handle.type === "main") return stories.at(0) ?? null;
|
|
403
|
+
if (handle.type === "header" || handle.type === "footer") return stories.find(({ handle: candidate }) => candidate.type === handle.type && candidate.relationshipId === handle.relationshipId) ?? null;
|
|
404
|
+
return stories.find(({ handle: candidate }) => (candidate.type === "footnote" || candidate.type === "endnote") && candidate.type === handle.type && candidate.noteId === handle.noteId) ?? null;
|
|
405
|
+
}
|
|
296
406
|
/**
|
|
297
407
|
* The tracked changes (insertions and deletions) present in the body, read
|
|
298
408
|
* from the same `insertion` / `deletion` marks the editor renders. Each
|
|
@@ -464,18 +574,26 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
464
574
|
}
|
|
465
575
|
/**
|
|
466
576
|
* Count every tracked change an accept-all / reject-all sweep resolves: the
|
|
467
|
-
* inline insertion / deletion groups {@link getChanges} enumerates, plus
|
|
468
|
-
*
|
|
469
|
-
* paragraph
|
|
577
|
+
* inline insertion / deletion groups {@link getChanges} enumerates, plus the
|
|
578
|
+
* property-change records living on node attrs rather than inline marks —
|
|
579
|
+
* paragraph-level (`pPrMark`, `_propertyChanges`, the inline sectPr's
|
|
580
|
+
* `propertyChanges`) and table-level (`tblPrChange` / `trPrChange` /
|
|
581
|
+
* `tcPrChange`).
|
|
470
582
|
*/
|
|
471
583
|
countTrackedChanges() {
|
|
584
|
+
const hasEntries = (value) => Array.isArray(value) && value.length > 0;
|
|
472
585
|
let count = this.getChanges().length;
|
|
473
586
|
this.state.doc.descendants((node) => {
|
|
474
|
-
|
|
587
|
+
const typeName = node.type.name;
|
|
588
|
+
if (typeName === "table" && hasEntries(node.attrs["tblPrChange"])) count += 1;
|
|
589
|
+
if (typeName === "tableRow" && hasEntries(node.attrs["trPrChange"])) count += 1;
|
|
590
|
+
if ((typeName === "tableCell" || typeName === "tableHeader") && hasEntries(node.attrs["tcPrChange"])) count += 1;
|
|
591
|
+
if (typeName !== "paragraph") return;
|
|
475
592
|
const pPrMark = node.attrs["pPrMark"];
|
|
476
593
|
if (pPrMark !== null && pPrMark !== void 0) count += 1;
|
|
477
|
-
|
|
478
|
-
|
|
594
|
+
if (hasEntries(node.attrs["_propertyChanges"])) count += 1;
|
|
595
|
+
const sectionProperties = node.attrs["_sectionProperties"];
|
|
596
|
+
if (hasEntries(sectionProperties?.propertyChanges)) count += 1;
|
|
479
597
|
return false;
|
|
480
598
|
});
|
|
481
599
|
return count;
|
package/dist/ai-edits/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAISignatureParty } from "./types.js";
|
|
1
|
+
import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAISignatureParty, FolioAITextRangeHandle } from "./types.js";
|
|
2
2
|
import { FolioAIEditView, applyFolioAIEditOperations } from "./apply.js";
|
|
3
3
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
4
|
+
import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
|
|
4
5
|
import { FolioCommentAnchor, FolioReviewChange, FolioReviewChangeKind, getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
5
|
-
import {
|
|
6
|
+
import { FolioDocumentStory, FolioDocumentStoryHandle } from "./headless.js";
|
|
7
|
+
import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
6
8
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
7
9
|
import { WordDiffSegment, diffWordSegments } from "./word-diff.js";
|
|
8
|
-
export { type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAISignatureParty, type FolioCommentAnchor, type FolioReviewChange, type FolioReviewChangeKind, type WordDiffSegment, applyFolioAIEditOperations, buildAnnotatedBlockText, createFolioAIEditSnapshot, diffWordSegments, getCommentAnchorsFromDoc, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, normalizeFolioAIBlockText };
|
|
10
|
+
export { type ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineFormatting, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioCommentAnchor, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentStory, type FolioDocumentStoryHandle, type FolioReviewChange, type FolioReviewChangeKind, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch };
|