@a3s-lab/office 0.50.0 → 0.51.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/README.md
CHANGED
|
@@ -309,6 +309,23 @@ and [CLI reference](./docs/latest/en/cli-reference.md).
|
|
|
309
309
|
|
|
310
310
|
## Current release
|
|
311
311
|
|
|
312
|
+
Version `0.51.0` extends Writer Compare with bounded, WPS-referenced
|
|
313
|
+
cross-paragraph text moves:
|
|
314
|
+
|
|
315
|
+
- A unique lexical range can move between aligned simple text paragraphs or
|
|
316
|
+
headings in the same section and become one paired `move` review item.
|
|
317
|
+
Separators travel with the range; accepting, rejecting, undoing, or reopening
|
|
318
|
+
the DOCX preserves exact source and revised text.
|
|
319
|
+
- The Review ribbon and Changes pane keep one localized **移动** card with
|
|
320
|
+
source/destination marks, destination navigation, author guidance, and
|
|
321
|
+
move-aware toast counts. Duplicate or mark-mismatched candidates, section
|
|
322
|
+
boundaries, rich or relationship-bound content, tables, and over-limit input
|
|
323
|
+
remain fail-closed ordinary revisions or diagnostics.
|
|
324
|
+
- The WPS 12.0 COM/UIA probe observed `CompareDocuments` returning ordinary
|
|
325
|
+
delete/insert records for the tested reorder. A3S therefore documents this
|
|
326
|
+
paired inference as a bounded local enhancement, with no claim that WPS
|
|
327
|
+
exposed a native move type through that API.
|
|
328
|
+
|
|
312
329
|
Version `0.50.0` extends Writer Compare with a bounded, WPS-referenced text
|
|
313
330
|
move workflow:
|
|
314
331
|
|
|
@@ -17496,29 +17496,40 @@ function work_document_compare_moves_appendRevisionUnits(target, units, kind, id
|
|
|
17496
17496
|
]));
|
|
17497
17497
|
}
|
|
17498
17498
|
function inferInlineMovePairs(changes, marksEqual) {
|
|
17499
|
+
return inferInlineMovePairsAcrossScopes([
|
|
17500
|
+
{
|
|
17501
|
+
scope: '',
|
|
17502
|
+
changes
|
|
17503
|
+
}
|
|
17504
|
+
], marksEqual);
|
|
17505
|
+
}
|
|
17506
|
+
function inferInlineMovePairsAcrossScopes(comparisons, marksEqual) {
|
|
17499
17507
|
const fullCandidates = [];
|
|
17500
|
-
for (const [stepIndex, change] of changes.entries()){
|
|
17508
|
+
for (const comparison of comparisons)for (const [stepIndex, change] of comparison.changes.entries()){
|
|
17501
17509
|
if ('equal' === change.kind) continue;
|
|
17502
17510
|
const units = 'delete' === change.kind ? change.left : change.right;
|
|
17503
|
-
const candidate = inlineMoveCandidate(stepIndex, change.kind, units);
|
|
17511
|
+
const candidate = inlineMoveCandidate(stepIndex, change.kind, units, comparison.scope);
|
|
17504
17512
|
if (candidate) fullCandidates.push(candidate);
|
|
17505
17513
|
}
|
|
17506
17514
|
const fullPairs = pairInlineMoveCandidates(fullCandidates, marksEqual);
|
|
17507
17515
|
const consumedSteps = new Set();
|
|
17508
17516
|
for (const pair of fullPairs){
|
|
17509
|
-
consumedSteps.add(pair.deletion
|
|
17510
|
-
consumedSteps.add(pair.insertion
|
|
17517
|
+
consumedSteps.add(moveStepKey(pair.deletion));
|
|
17518
|
+
consumedSteps.add(moveStepKey(pair.insertion));
|
|
17511
17519
|
}
|
|
17512
17520
|
const tokenCandidates = [];
|
|
17513
|
-
for (const [stepIndex, change] of changes.entries()){
|
|
17514
|
-
if ('equal' === change.kind || consumedSteps.has(
|
|
17521
|
+
for (const comparison of comparisons)for (const [stepIndex, change] of comparison.changes.entries()){
|
|
17522
|
+
if ('equal' === change.kind || consumedSteps.has(moveStepKey({
|
|
17523
|
+
scope: comparison.scope,
|
|
17524
|
+
stepIndex
|
|
17525
|
+
}))) continue;
|
|
17515
17526
|
const units = 'delete' === change.kind ? change.left : change.right;
|
|
17516
|
-
tokenCandidates.push(...inlineMoveTokenCandidates(stepIndex, change.kind, units));
|
|
17527
|
+
tokenCandidates.push(...inlineMoveTokenCandidates(stepIndex, change.kind, units, comparison.scope));
|
|
17517
17528
|
}
|
|
17518
17529
|
return [
|
|
17519
17530
|
...fullPairs,
|
|
17520
17531
|
...pairInlineMoveCandidates(tokenCandidates, marksEqual)
|
|
17521
|
-
].sort(
|
|
17532
|
+
].sort(compareMovePairs).slice(0, MAX_INFERRED_MOVES);
|
|
17522
17533
|
}
|
|
17523
17534
|
function appendComparisonRevisionUnits(target, units, kind, schema, factory, stripReviewMarks, appendRevisionUnits, moves) {
|
|
17524
17535
|
if (!moves?.length) return void appendRevisionUnits(target, units, kind, factory.create(kind), schema, stripReviewMarks);
|
|
@@ -17579,9 +17590,9 @@ function pairInlineMoveCandidates(candidates, marksEqual) {
|
|
|
17579
17590
|
return Array.from(grouped.values()).filter(({ deletion, insertion })=>1 === deletion.length && 1 === insertion.length && inlineMoveCandidatesEqual(deletion[0], insertion[0], marksEqual)).map(({ deletion, insertion })=>({
|
|
17580
17591
|
deletion: deletion[0],
|
|
17581
17592
|
insertion: insertion[0]
|
|
17582
|
-
})).sort(
|
|
17593
|
+
})).sort(compareMovePairs).slice(0, MAX_INFERRED_MOVES);
|
|
17583
17594
|
}
|
|
17584
|
-
function inlineMoveCandidate(stepIndex, kind, units) {
|
|
17595
|
+
function inlineMoveCandidate(stepIndex, kind, units, scope = '') {
|
|
17585
17596
|
let start = 0;
|
|
17586
17597
|
while(start < units.length && isWhitespaceUnit(units[start]))start += 1;
|
|
17587
17598
|
let end = units.length;
|
|
@@ -17594,6 +17605,9 @@ function inlineMoveCandidate(stepIndex, kind, units) {
|
|
|
17594
17605
|
const text = units.slice(start, end).map((unit, index, selected)=>unit.text.slice(0 === index ? startOffset : 0, index === selected.length - 1 ? endOffset : unit.text.length)).join('');
|
|
17595
17606
|
if (!text || text.length > MAX_INFERRED_MOVE_TEXT || !/[\p{L}\p{N}]/u.test(text)) return null;
|
|
17596
17607
|
return {
|
|
17608
|
+
...scope ? {
|
|
17609
|
+
scope
|
|
17610
|
+
} : {},
|
|
17597
17611
|
stepIndex,
|
|
17598
17612
|
kind,
|
|
17599
17613
|
units,
|
|
@@ -17604,12 +17618,15 @@ function inlineMoveCandidate(stepIndex, kind, units) {
|
|
|
17604
17618
|
text
|
|
17605
17619
|
};
|
|
17606
17620
|
}
|
|
17607
|
-
function inlineMoveTokenCandidates(stepIndex, kind, units) {
|
|
17621
|
+
function inlineMoveTokenCandidates(stepIndex, kind, units, scope = '') {
|
|
17608
17622
|
const candidates = [];
|
|
17609
17623
|
for (const [index, unit] of units.entries()){
|
|
17610
17624
|
const leadingWhitespace = unit.text.length - unit.text.trimStart().length;
|
|
17611
17625
|
const text = unit.text.slice(leadingWhitespace);
|
|
17612
17626
|
if (text && !(text.length > MAX_INFERRED_MOVE_TEXT) && /[\p{L}\p{N}]/u.test(text)) candidates.push({
|
|
17627
|
+
...scope ? {
|
|
17628
|
+
scope
|
|
17629
|
+
} : {},
|
|
17613
17630
|
stepIndex,
|
|
17614
17631
|
kind,
|
|
17615
17632
|
units,
|
|
@@ -17622,6 +17639,12 @@ function inlineMoveTokenCandidates(stepIndex, kind, units) {
|
|
|
17622
17639
|
}
|
|
17623
17640
|
return candidates;
|
|
17624
17641
|
}
|
|
17642
|
+
function moveStepKey(candidate) {
|
|
17643
|
+
return `${candidate.scope ?? ''}\u0000${candidate.stepIndex}`;
|
|
17644
|
+
}
|
|
17645
|
+
function compareMovePairs(left, right) {
|
|
17646
|
+
return (left.deletion.scope ?? '').localeCompare(right.deletion.scope ?? '') || left.deletion.stepIndex - right.deletion.stepIndex || (left.insertion.scope ?? '').localeCompare(right.insertion.scope ?? '') || left.insertion.stepIndex - right.insertion.stepIndex || left.deletion.start - right.deletion.start;
|
|
17647
|
+
}
|
|
17625
17648
|
function inlineMoveCandidatesEqual(left, right, marksEqual) {
|
|
17626
17649
|
const leftUnits = selectedMoveUnits(left);
|
|
17627
17650
|
const rightUnits = selectedMoveUnits(right);
|
|
@@ -17798,40 +17821,129 @@ function compareSectionBlocks(currentSection, revisedSection, sectionIndex, fact
|
|
|
17798
17821
|
});
|
|
17799
17822
|
return null;
|
|
17800
17823
|
}
|
|
17801
|
-
const
|
|
17824
|
+
const slots = [];
|
|
17802
17825
|
for (const [blockIndex, step] of alignment.entries()){
|
|
17803
17826
|
if ('equal' === step.kind) {
|
|
17804
|
-
|
|
17827
|
+
slots.push({
|
|
17828
|
+
kind: 'node',
|
|
17829
|
+
node: step.left
|
|
17830
|
+
});
|
|
17805
17831
|
continue;
|
|
17806
17832
|
}
|
|
17807
17833
|
if ('delete' === step.kind) {
|
|
17808
17834
|
const deleted = structuralChangeBlock(step.left, 'deletion', factory, `section-${sectionIndex}-delete-${blockIndex}`);
|
|
17809
|
-
if (deleted)
|
|
17835
|
+
if (deleted) slots.push({
|
|
17836
|
+
kind: 'node',
|
|
17837
|
+
node: deleted
|
|
17838
|
+
});
|
|
17810
17839
|
else diagnostics.push(structuralBlockDiagnostic(step.left, sectionIndex, blockIndex));
|
|
17811
17840
|
continue;
|
|
17812
17841
|
}
|
|
17813
17842
|
if ('insert' === step.kind) {
|
|
17814
17843
|
const inserted = structuralChangeBlock(step.right, 'insertion', factory, `section-${sectionIndex}-insert-${blockIndex}`);
|
|
17815
|
-
if (inserted)
|
|
17844
|
+
if (inserted) slots.push({
|
|
17845
|
+
kind: 'node',
|
|
17846
|
+
node: inserted
|
|
17847
|
+
});
|
|
17816
17848
|
else diagnostics.push(structuralBlockDiagnostic(step.right, sectionIndex, blockIndex));
|
|
17817
17849
|
continue;
|
|
17818
17850
|
}
|
|
17819
|
-
const
|
|
17820
|
-
if (
|
|
17821
|
-
|
|
17851
|
+
const prepared = preparePairedBlock(step.left, step.right, `section-${sectionIndex}-block-${blockIndex}`);
|
|
17852
|
+
if (prepared) {
|
|
17853
|
+
slots.push({
|
|
17854
|
+
kind: 'paired',
|
|
17855
|
+
comparison: prepared
|
|
17856
|
+
});
|
|
17822
17857
|
continue;
|
|
17823
17858
|
}
|
|
17824
17859
|
const deleted = structuralChangeBlock(step.left, 'deletion', factory, `section-${sectionIndex}-replace-delete-${blockIndex}`);
|
|
17825
17860
|
const inserted = structuralChangeBlock(step.right, 'insertion', factory, `section-${sectionIndex}-replace-insert-${blockIndex}`);
|
|
17826
|
-
if (deleted
|
|
17827
|
-
|
|
17861
|
+
if (deleted) slots.push({
|
|
17862
|
+
kind: 'node',
|
|
17863
|
+
node: deleted
|
|
17864
|
+
});
|
|
17865
|
+
if (inserted) slots.push({
|
|
17866
|
+
kind: 'node',
|
|
17867
|
+
node: inserted
|
|
17868
|
+
});
|
|
17869
|
+
if (!deleted || !inserted) diagnostics.push(structuralBlockDiagnostic(step.left, sectionIndex, blockIndex));
|
|
17828
17870
|
}
|
|
17829
|
-
|
|
17871
|
+
const pairedSlots = slots.filter((slot)=>'paired' === slot.kind);
|
|
17872
|
+
const initialComparisons = pairedSlots.map(({ comparison })=>({
|
|
17873
|
+
scope: comparison.scope,
|
|
17874
|
+
changes: comparison.inline.variant.changes
|
|
17875
|
+
}));
|
|
17876
|
+
const initialMovePairs = inferInlineMovePairsAcrossScopes(initialComparisons, work_document_compare_marksEqual);
|
|
17877
|
+
const attachedComparisons = pairedSlots.map(({ comparison })=>({
|
|
17878
|
+
scope: comparison.scope,
|
|
17879
|
+
changes: comparison.inline.attached?.changes ?? comparison.inline.variant.changes
|
|
17880
|
+
}));
|
|
17881
|
+
const attachedMovePairs = inferInlineMovePairsAcrossScopes(attachedComparisons, work_document_compare_marksEqual);
|
|
17882
|
+
const pairedByScope = new Map(pairedSlots.map((slot)=>[
|
|
17883
|
+
slot.comparison.scope,
|
|
17884
|
+
slot.comparison
|
|
17885
|
+
]));
|
|
17886
|
+
const attachedScopes = new Set();
|
|
17887
|
+
for (const pair of [
|
|
17888
|
+
...initialMovePairs,
|
|
17889
|
+
...attachedMovePairs
|
|
17890
|
+
]){
|
|
17891
|
+
const deletionScope = pair.deletion.scope ?? '';
|
|
17892
|
+
const insertionScope = pair.insertion.scope ?? '';
|
|
17893
|
+
if (deletionScope === insertionScope) continue;
|
|
17894
|
+
const deletionBlock = pairedByScope.get(deletionScope);
|
|
17895
|
+
const insertionBlock = pairedByScope.get(insertionScope);
|
|
17896
|
+
if (deletionBlock?.inline.attached && insertionBlock?.inline.attached) {
|
|
17897
|
+
attachedScopes.add(deletionScope);
|
|
17898
|
+
attachedScopes.add(insertionScope);
|
|
17899
|
+
}
|
|
17900
|
+
}
|
|
17901
|
+
const selectedComparisons = pairedSlots.map(({ comparison })=>({
|
|
17902
|
+
scope: comparison.scope,
|
|
17903
|
+
changes: (attachedScopes.has(comparison.scope) ? comparison.inline.attached : comparison.inline.variant)?.changes ?? comparison.inline.variant.changes
|
|
17904
|
+
}));
|
|
17905
|
+
const movePairs = inferInlineMovePairsAcrossScopes(selectedComparisons, work_document_compare_marksEqual);
|
|
17906
|
+
const useAttachedVariants = attachedScopes.size > 0 && movePairs.length >= initialMovePairs.length;
|
|
17907
|
+
const movesByScope = new Map();
|
|
17908
|
+
for (const pair of useAttachedVariants ? movePairs : initialMovePairs){
|
|
17909
|
+
const identity = factory.create('move');
|
|
17910
|
+
appendMoveAssignment(movesByScope, pair.deletion, 'from', identity);
|
|
17911
|
+
appendMoveAssignment(movesByScope, pair.insertion, 'to', identity);
|
|
17912
|
+
}
|
|
17913
|
+
return slots.flatMap((slot)=>{
|
|
17914
|
+
if ('node' === slot.kind) return [
|
|
17915
|
+
slot.node
|
|
17916
|
+
];
|
|
17917
|
+
const compared = renderPairedBlock(slot.comparison, factory, movesByScope.get(slot.comparison.scope), useAttachedVariants && attachedScopes.has(slot.comparison.scope) ? slot.comparison.inline.attached ?? slot.comparison.inline.variant : slot.comparison.inline.variant);
|
|
17918
|
+
return compared ? [
|
|
17919
|
+
compared
|
|
17920
|
+
] : [];
|
|
17921
|
+
});
|
|
17922
|
+
}
|
|
17923
|
+
function appendMoveAssignment(target, candidate, role, identity) {
|
|
17924
|
+
const scope = candidate.scope ?? '';
|
|
17925
|
+
const assignments = target.get(scope) ?? [];
|
|
17926
|
+
assignments.push({
|
|
17927
|
+
role,
|
|
17928
|
+
candidate,
|
|
17929
|
+
identity
|
|
17930
|
+
});
|
|
17931
|
+
target.set(scope, assignments);
|
|
17830
17932
|
}
|
|
17831
|
-
function
|
|
17933
|
+
function preparePairedBlock(current, revised, scope) {
|
|
17832
17934
|
if (!isSimpleTextBlock(current) || !isSimpleTextBlock(revised)) return null;
|
|
17833
17935
|
if (current.type !== revised.type || blockStructuralSignature(current) !== blockStructuralSignature(revised)) return null;
|
|
17834
|
-
const
|
|
17936
|
+
const comparison = prepareInlineComparison(current, revised);
|
|
17937
|
+
return comparison ? {
|
|
17938
|
+
current,
|
|
17939
|
+
revised,
|
|
17940
|
+
scope,
|
|
17941
|
+
inline: comparison
|
|
17942
|
+
} : null;
|
|
17943
|
+
}
|
|
17944
|
+
function renderPairedBlock(comparison, factory, moves, variant) {
|
|
17945
|
+
const { current, revised } = comparison;
|
|
17946
|
+
const inline = renderInlineComparison(current, variant, factory, moves);
|
|
17835
17947
|
if (!inline) return null;
|
|
17836
17948
|
const before = serializeDocumentParagraphFormatting(current.attrs);
|
|
17837
17949
|
const after = serializeDocumentParagraphFormatting(revised.attrs);
|
|
@@ -17853,65 +17965,65 @@ function comparePairedBlocks(current, revised, factory) {
|
|
|
17853
17965
|
}
|
|
17854
17966
|
return current.type.create(attributes, inline);
|
|
17855
17967
|
}
|
|
17856
|
-
function
|
|
17857
|
-
|
|
17858
|
-
|
|
17859
|
-
if (!
|
|
17860
|
-
const diff = boundedDocumentSequenceDiff(
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
17871
|
-
|
|
17872
|
-
|
|
17873
|
-
|
|
17968
|
+
function prepareInlineComparison(current, revised) {
|
|
17969
|
+
const baseCurrentUnits = inlineUnits(current);
|
|
17970
|
+
const baseRevisedUnits = inlineUnits(revised);
|
|
17971
|
+
if (!baseCurrentUnits || !baseRevisedUnits) return null;
|
|
17972
|
+
const diff = boundedDocumentSequenceDiff(baseCurrentUnits, baseRevisedUnits, (left, right)=>left.text === right.text, MAX_INLINE_DIFF_CELLS);
|
|
17973
|
+
const baseVariant = {
|
|
17974
|
+
currentUnits: baseCurrentUnits,
|
|
17975
|
+
revisedUnits: baseRevisedUnits,
|
|
17976
|
+
changes: diff ?? [
|
|
17977
|
+
{
|
|
17978
|
+
kind: 'delete',
|
|
17979
|
+
left: baseCurrentUnits,
|
|
17980
|
+
right: []
|
|
17981
|
+
},
|
|
17982
|
+
{
|
|
17983
|
+
kind: 'insert',
|
|
17984
|
+
left: [],
|
|
17985
|
+
right: baseRevisedUnits
|
|
17986
|
+
}
|
|
17987
|
+
]
|
|
17988
|
+
};
|
|
17989
|
+
const variant = baseVariant;
|
|
17990
|
+
const baseMovePairs = inferInlineMovePairs(variant.changes, work_document_compare_marksEqual);
|
|
17874
17991
|
const moveCurrentUnits = inlineUnits(current, true);
|
|
17875
17992
|
const moveRevisedUnits = inlineUnits(revised, true);
|
|
17876
17993
|
if (moveCurrentUnits && moveRevisedUnits && current.textContent.length + revised.textContent.length <= 131072) {
|
|
17877
17994
|
const moveDiff = boundedDocumentSequenceDiff(moveCurrentUnits, moveRevisedUnits, (left, right)=>left.text === right.text, MAX_INLINE_DIFF_CELLS);
|
|
17878
17995
|
if (moveDiff) {
|
|
17879
17996
|
const moveChanges = moveDiff;
|
|
17997
|
+
const moveVariant = {
|
|
17998
|
+
currentUnits: moveCurrentUnits,
|
|
17999
|
+
revisedUnits: moveRevisedUnits,
|
|
18000
|
+
changes: moveChanges
|
|
18001
|
+
};
|
|
17880
18002
|
const movePairs = inferInlineMovePairs(moveChanges, work_document_compare_marksEqual);
|
|
17881
|
-
if (movePairs.length >= baseMovePairs.length && movePairs.length > 0) {
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
18003
|
+
if (movePairs.length >= baseMovePairs.length && movePairs.length > 0) return {
|
|
18004
|
+
variant: moveVariant,
|
|
18005
|
+
attached: moveVariant
|
|
18006
|
+
};
|
|
18007
|
+
return {
|
|
18008
|
+
variant,
|
|
18009
|
+
attached: moveVariant
|
|
18010
|
+
};
|
|
17886
18011
|
}
|
|
17887
18012
|
}
|
|
17888
|
-
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
role: 'from',
|
|
17895
|
-
candidate: pair.deletion,
|
|
17896
|
-
identity
|
|
17897
|
-
});
|
|
17898
|
-
movesByStep.set(pair.deletion.stepIndex, deletion);
|
|
17899
|
-
const insertion = movesByStep.get(pair.insertion.stepIndex) ?? [];
|
|
17900
|
-
insertion.push({
|
|
17901
|
-
role: 'to',
|
|
17902
|
-
candidate: pair.insertion,
|
|
17903
|
-
identity
|
|
17904
|
-
});
|
|
17905
|
-
movesByStep.set(pair.insertion.stepIndex, insertion);
|
|
17906
|
-
}
|
|
18013
|
+
return {
|
|
18014
|
+
variant
|
|
18015
|
+
};
|
|
18016
|
+
}
|
|
18017
|
+
function renderInlineComparison(current, variant, factory, moves) {
|
|
18018
|
+
const { changes } = variant;
|
|
17907
18019
|
const nodes = [];
|
|
17908
18020
|
for (const [stepIndex, change] of changes.entries()){
|
|
17909
18021
|
if ('delete' === change.kind) {
|
|
17910
|
-
appendComparisonRevisionUnits(nodes, change.left, 'deletion', current.type.schema, factory, withoutReviewMarks, work_document_compare_moves_appendRevisionUnits,
|
|
18022
|
+
appendComparisonRevisionUnits(nodes, change.left, 'deletion', current.type.schema, factory, withoutReviewMarks, work_document_compare_moves_appendRevisionUnits, moves?.filter((move)=>move.candidate.stepIndex === stepIndex));
|
|
17911
18023
|
continue;
|
|
17912
18024
|
}
|
|
17913
18025
|
if ('insert' === change.kind) {
|
|
17914
|
-
appendComparisonRevisionUnits(nodes, change.right, 'insertion', current.type.schema, factory, withoutReviewMarks, work_document_compare_moves_appendRevisionUnits,
|
|
18026
|
+
appendComparisonRevisionUnits(nodes, change.right, 'insertion', current.type.schema, factory, withoutReviewMarks, work_document_compare_moves_appendRevisionUnits, moves?.filter((move)=>move.candidate.stepIndex === stepIndex));
|
|
17915
18027
|
continue;
|
|
17916
18028
|
}
|
|
17917
18029
|
let formattingIdentity = null;
|
|
@@ -20,6 +20,8 @@ export type InlineDiffStep = {
|
|
|
20
20
|
right: InlineUnit[];
|
|
21
21
|
};
|
|
22
22
|
export interface InlineMoveCandidate {
|
|
23
|
+
/** Stable comparison scope (for example, the containing paragraph). */
|
|
24
|
+
scope?: string;
|
|
23
25
|
stepIndex: number;
|
|
24
26
|
kind: 'delete' | 'insert';
|
|
25
27
|
units: InlineUnit[];
|
|
@@ -33,6 +35,10 @@ export interface InlineMovePair {
|
|
|
33
35
|
deletion: InlineMoveCandidate;
|
|
34
36
|
insertion: InlineMoveCandidate;
|
|
35
37
|
}
|
|
38
|
+
export interface InlineMoveComparison {
|
|
39
|
+
scope: string;
|
|
40
|
+
changes: readonly InlineDiffStep[];
|
|
41
|
+
}
|
|
36
42
|
export interface InlineMoveAssignment {
|
|
37
43
|
role: WorkDocumentMoveRole;
|
|
38
44
|
candidate: InlineMoveCandidate;
|
|
@@ -47,11 +53,17 @@ type AppendRevisionUnits = (target: ProseMirrorNode[], units: readonly InlineUni
|
|
|
47
53
|
export declare const MAX_INFERRED_MOVE_TEXT = 65536;
|
|
48
54
|
export declare function appendRevisionUnits(target: ProseMirrorNode[], units: readonly InlineUnit[], kind: 'insertion' | 'deletion', identity: WorkDocumentChangeIdentity, schema: Schema, stripReviewMarks: StripReviewMarks): void;
|
|
49
55
|
/**
|
|
50
|
-
* Pairs
|
|
51
|
-
*
|
|
52
|
-
*
|
|
56
|
+
* Pairs deterministic same-text ranges. A comparison may contain several
|
|
57
|
+
* simple blocks, which lets Compare represent a bounded move between
|
|
58
|
+
* paragraphs while still rejecting duplicate or mark-mismatched candidates.
|
|
53
59
|
*/
|
|
54
60
|
export declare function inferInlineMovePairs(changes: readonly InlineDiffStep[], marksEqual: MarksEqual): InlineMovePair[];
|
|
61
|
+
/**
|
|
62
|
+
* Infers moves across a bounded set of simple-block diffs. Candidate scopes
|
|
63
|
+
* are retained on each side so callers can put the resulting marks back into
|
|
64
|
+
* the right paragraph without flattening the document tree.
|
|
65
|
+
*/
|
|
66
|
+
export declare function inferInlineMovePairsAcrossScopes(comparisons: readonly InlineMoveComparison[], marksEqual: MarksEqual): InlineMovePair[];
|
|
55
67
|
/**
|
|
56
68
|
* Splits an ordinary diff step around paired move ranges while retaining the
|
|
57
69
|
* exact whitespace and mark boundaries needed to reconstruct either version.
|
|
@@ -296,16 +296,19 @@ The current and imported TipTap/ProseMirror trees remain immutable while a
|
|
|
296
296
|
deterministic weighted block alignment pairs related paragraphs and headings;
|
|
297
297
|
an LCS token diff then produces insertion and deletion marks, while exact mark
|
|
298
298
|
and paragraph-property snapshots produce character- and paragraph-formatting
|
|
299
|
-
revisions. A bounded
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
revisions. A bounded move pass may pair a lexical range that occurs exactly
|
|
300
|
+
once in a delete chunk and once in an insert chunk when its marks and carried
|
|
301
|
+
separators match. The pass retains a stable paragraph scope, so it can pair
|
|
302
|
+
aligned simple text blocks within one document section without flattening the
|
|
303
|
+
document tree; section boundaries, rich blocks, and structural objects remain
|
|
304
|
+
outside this inference. The result is one `move` identity with source and
|
|
302
305
|
destination roles. Stable semantic signatures seed generated identities, so the
|
|
303
306
|
same inputs and options produce the same ordering and IDs. Only a fully
|
|
304
307
|
admitted plan replaces the mounted document, and that replacement is one
|
|
305
308
|
transaction, one controlled publication, and one Undo record. Duplicate,
|
|
306
|
-
|
|
307
|
-
diagnostics rather than being guessed into moves; native
|
|
308
|
-
reviewable when they already exist.
|
|
309
|
+
mark-mismatched, section-boundary, rich, and over-limit candidates remain
|
|
310
|
+
ordinary revisions or diagnostics rather than being guessed into moves; native
|
|
311
|
+
paired moves remain reviewable when they already exist.
|
|
309
312
|
|
|
310
313
|
Combine validates rather than guesses. It rejects every imported revision on
|
|
311
314
|
an immutable reviewed snapshot and compares the resulting semantic signature
|