@stll/folio-core 0.47.5 → 0.47.7
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/word-diff.d.ts +25 -6
- package/dist/ai-edits/word-diff.js +210 -38
- package/dist/controller/headerFooterEditorManager.d.ts +2 -1
- package/dist/controller/headerFooterEditorManager.js +2 -1
- package/dist/controller/noteEditorManager.d.ts +2 -1
- package/dist/controller/noteEditorManager.js +2 -1
- package/dist/docx/relsParser.js +9 -1
- package/dist/docx/selectiveSave.js +11 -2
- package/dist/prosemirror/attrs/index.d.ts +3 -2
- package/dist/prosemirror/attrs/index.js +18 -1
- package/dist/prosemirror/commands/comments.js +61 -3
- package/dist/prosemirror/conversion/fromProseDoc.js +107 -37
- package/dist/prosemirror/conversion/toProseDoc.js +33 -13
- package/dist/prosemirror/extensions/markRegistry.d.ts +5 -8
- package/dist/prosemirror/extensions/markRegistry.js +5 -8
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +2 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +32 -7
- package/dist/prosemirror/runIdentity.d.ts +5 -5
- package/dist/prosemirror/runIdentity.js +5 -5
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +17 -2
- package/dist/prosemirror/utils/extractTrackedChanges.js +21 -6
- package/package.json +1 -1
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Diff between two strings, as the segments a redline is drawn from.
|
|
4
4
|
*
|
|
5
|
-
* Tokenises (by default
|
|
6
|
-
* part of each token
|
|
5
|
+
* Tokenises (by default into words and the punctuation marks at their edges,
|
|
6
|
+
* preserving the whitespace as part of each token; see {@link pushRunTokens}),
|
|
7
|
+
* runs an LCS, and returns a left-to-right ordered list of
|
|
7
8
|
* segments where shared runs render as `equal`, removed runs as `del`, and
|
|
8
9
|
* added runs as `ins`. Used by the panel (to render minimal-change redlines),
|
|
9
10
|
* the version comparison, and the apply engine (so tracked changes mark only
|
|
@@ -18,10 +19,25 @@
|
|
|
18
19
|
* same thing and can actually be read. Three rules pull the output back:
|
|
19
20
|
*
|
|
20
21
|
* 1. A match made only of separators is not a match ({@link isSeparatorOnly}).
|
|
21
|
-
* 2. A match too short to carry meaning is dropped
|
|
22
|
-
*
|
|
22
|
+
* 2. A match too short to carry meaning is dropped when changes sit on both
|
|
23
|
+
* sides of it and at least one of them changes words. At either end of the
|
|
24
|
+
* string it is the reader's anchor, and
|
|
25
|
+
* between two punctuation or whitespace edits it is what they were made
|
|
26
|
+
* around; neither is an island.
|
|
23
27
|
* 3. When what survives is still too fragmented for its length, the whole
|
|
24
|
-
* paragraph is one replacement ({@link isTooFragmented}).
|
|
28
|
+
* paragraph is one replacement ({@link isTooFragmented}). Punctuation and
|
|
29
|
+
* whitespace edits alone never are.
|
|
30
|
+
*
|
|
31
|
+
* At word granularity the LCS itself prefers words: one matched word outweighs
|
|
32
|
+
* any number of matched marks, and a unique mark is never an anchor.
|
|
33
|
+
*
|
|
34
|
+
* A word token carries the whitespace before it, so a string's first word has
|
|
35
|
+
* none while the same word inside the other string does. Word tokens are
|
|
36
|
+
* therefore matched on their text alone; the rules above judge that
|
|
37
|
+
* alignment, and a whitespace difference around a matched word is marked
|
|
38
|
+
* afterwards as a change of the whitespace only
|
|
39
|
+
* ({@link separateWhitespaceChanges}). Otherwise prefixing "(1) " would strike
|
|
40
|
+
* through the unchanged first word.
|
|
25
41
|
*
|
|
26
42
|
* Common affixes and unique-token anchors split the input into independent
|
|
27
43
|
* gaps before any quadratic work. The residual gaps share one
|
|
@@ -61,6 +77,9 @@ type WordDiffOptions = {
|
|
|
61
77
|
/** Default: nothing normalized, so both strings reconstruct exactly. */
|
|
62
78
|
normalization?: WordDiffNormalization;
|
|
63
79
|
};
|
|
80
|
+
/** Keep token storage bounded when a run has an unusually long punctuation edge. */
|
|
81
|
+
declare const MAX_EDGE_PUNCTUATION_TOKENS = 64;
|
|
82
|
+
declare const tokenizeWords: (value: string) => string[];
|
|
64
83
|
/**
|
|
65
84
|
* One internal comparison/apply scope. Every diff shares the same quadratic
|
|
66
85
|
* allowance; public standalone calls below still receive a fresh allowance.
|
|
@@ -75,4 +94,4 @@ declare const createScopedWordDiffOptions: <Options extends WordDiffOptions>(opt
|
|
|
75
94
|
declare const wordDiffSessionFromOptions: (options: WordDiffOptions | undefined) => ReturnType<typeof createWordDiffSession>;
|
|
76
95
|
declare const diffWordSegments: (before: string, after: string, options?: WordDiffOptions) => WordDiffSegment[];
|
|
77
96
|
//#endregion
|
|
78
|
-
export { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, createScopedWordDiffOptions, createWordDiffSession, diffWordSegments, wordDiffSessionFromOptions };
|
|
97
|
+
export { MAX_EDGE_PUNCTUATION_TOKENS, WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, createScopedWordDiffOptions, createWordDiffSession, diffWordSegments, tokenizeWords, wordDiffSessionFromOptions };
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
const WORD_DIFF_GRANULARITIES = Object.freeze(["word", "character"]);
|
|
9
9
|
const WHITESPACE = /\s/u;
|
|
10
|
-
/** Punctuation
|
|
11
|
-
const SEPARATOR_ONLY = /^[\s\p{P}
|
|
10
|
+
/** Punctuation and whitespace carry no content except legal section/paragraph signs. */
|
|
11
|
+
const SEPARATOR_ONLY = /^[\s\p{P}]*$/u;
|
|
12
|
+
const CONTENT_PUNCTUATION = /[§¶]/u;
|
|
12
13
|
/**
|
|
13
|
-
* A match of at most this many tokens that carries
|
|
14
|
+
* A match of at most this many tokens that carries only punctuation or space is
|
|
14
15
|
* noise: a lone space, a comma, a stray closing bracket. Matching it splits
|
|
15
16
|
* two rewrites into four.
|
|
16
17
|
*/
|
|
@@ -33,6 +34,59 @@ const MINIMUM_ISOLATED_MATCH_UNITS = 2;
|
|
|
33
34
|
const FRAGMENTATION_SCALE = 32;
|
|
34
35
|
/** Below this length every match is a large fraction of the string, so the floor says nothing. */
|
|
35
36
|
const FRAGMENTATION_MINIMUM_AVERAGE_LENGTH = 8;
|
|
37
|
+
/** Unicode general category P: punctuation marks, dashes, brackets and quotes. */
|
|
38
|
+
const PUNCTUATION = /^\p{P}$/u;
|
|
39
|
+
/** Keep token storage bounded when a run has an unusually long punctuation edge. */
|
|
40
|
+
const MAX_EDGE_PUNCTUATION_TOKENS = 64;
|
|
41
|
+
/** The code point starting at `index`, as a string of one or two UTF-16 units. */
|
|
42
|
+
const codePointAt = (value, index) => String.fromCodePoint(value.codePointAt(index) ?? 0);
|
|
43
|
+
/** The code point ending just before `end`. */
|
|
44
|
+
const codePointBefore = (value, end) => {
|
|
45
|
+
const low = value.charCodeAt(end - 1);
|
|
46
|
+
const high = value.charCodeAt(end - 2);
|
|
47
|
+
return low >= 56320 && low <= 57343 && high >= 55296 && high <= 56319 ? value.slice(end - 2, end) : value.slice(end - 1, end);
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Split one whitespace-free run into its words and punctuation marks.
|
|
51
|
+
*
|
|
52
|
+
* Punctuation marks (general category P) at either edge of the run are
|
|
53
|
+
* tokens of their own, as Word's compare treats them: `jmění.` becoming `jmění,`
|
|
54
|
+
* changes the mark, not the word. Punctuation inside the run stays in the
|
|
55
|
+
* word, so `d.o.o`, `1.1.2026`, `3.5`, `well-known` and `don't` are single
|
|
56
|
+
* tokens; only their edge marks split off (`d.o.o.` is `d.o.o` + `.`, `b)` is
|
|
57
|
+
* `b` + `)`). A run made only of punctuation is split up to the edge cap.
|
|
58
|
+
* `§` and `¶` are content markers despite their Unicode punctuation category;
|
|
59
|
+
* symbols (general category S, such as `€` or `+`) are not punctuation.
|
|
60
|
+
*/
|
|
61
|
+
const pushRunTokens = (tokens, value, run, prefixStart) => {
|
|
62
|
+
let wordStart = run.start;
|
|
63
|
+
let pending = value.slice(prefixStart, run.start);
|
|
64
|
+
let leadingMarks = 0;
|
|
65
|
+
while (wordStart < run.end && leadingMarks < 64) {
|
|
66
|
+
const mark = codePointAt(value, wordStart);
|
|
67
|
+
if (!PUNCTUATION.test(mark) || CONTENT_PUNCTUATION.test(mark)) break;
|
|
68
|
+
tokens.push(pending + mark);
|
|
69
|
+
pending = "";
|
|
70
|
+
wordStart += mark.length;
|
|
71
|
+
leadingMarks++;
|
|
72
|
+
}
|
|
73
|
+
let wordEnd = run.end;
|
|
74
|
+
const trailing = [];
|
|
75
|
+
while (wordEnd > wordStart && trailing.length < 64) {
|
|
76
|
+
const mark = codePointBefore(value, wordEnd);
|
|
77
|
+
if (!PUNCTUATION.test(mark) || CONTENT_PUNCTUATION.test(mark)) break;
|
|
78
|
+
trailing.push(mark);
|
|
79
|
+
wordEnd -= mark.length;
|
|
80
|
+
}
|
|
81
|
+
if (wordStart < wordEnd) {
|
|
82
|
+
tokens.push(pending + value.slice(wordStart, wordEnd));
|
|
83
|
+
pending = "";
|
|
84
|
+
}
|
|
85
|
+
for (let index = trailing.length - 1; index >= 0; index--) {
|
|
86
|
+
tokens.push(pending + (trailing[index] ?? ""));
|
|
87
|
+
pending = "";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
36
90
|
const tokenizeWords = (value) => {
|
|
37
91
|
const tokens = [];
|
|
38
92
|
let tokenStart = 0;
|
|
@@ -40,8 +94,12 @@ const tokenizeWords = (value) => {
|
|
|
40
94
|
while (cursor < value.length) {
|
|
41
95
|
while (cursor < value.length && WHITESPACE.test(value.charAt(cursor))) cursor++;
|
|
42
96
|
if (cursor === value.length) break;
|
|
97
|
+
const runStart = cursor;
|
|
43
98
|
while (cursor < value.length && !WHITESPACE.test(value.charAt(cursor))) cursor++;
|
|
44
|
-
tokens
|
|
99
|
+
pushRunTokens(tokens, value, {
|
|
100
|
+
start: runStart,
|
|
101
|
+
end: cursor
|
|
102
|
+
}, tokenStart);
|
|
45
103
|
tokenStart = cursor;
|
|
46
104
|
}
|
|
47
105
|
const last = tokens.at(-1);
|
|
@@ -61,7 +119,7 @@ const comparisonKey = (token, normalization) => {
|
|
|
61
119
|
const collapsed = normalization.whitespace === true ? token.replaceAll(/\s+/gu, " ").trim() : token;
|
|
62
120
|
return normalization.case === true ? collapsed.toLowerCase() : collapsed;
|
|
63
121
|
};
|
|
64
|
-
const isSeparatorOnly = (text) => SEPARATOR_ONLY.test(text);
|
|
122
|
+
const isSeparatorOnly = (text) => SEPARATOR_ONLY.test(text) && !CONTENT_PUNCTUATION.test(text);
|
|
65
123
|
/**
|
|
66
124
|
* Cell budget shared by the residual LCS gaps inside one comparison or apply
|
|
67
125
|
* scope. `before`/`after` come from attacker-controlled document text (a
|
|
@@ -85,11 +143,21 @@ const MAX_WORD_DIFF_ANCHOR_TOKENS = 16384;
|
|
|
85
143
|
* and case or whitespace normalization creates another string for it.
|
|
86
144
|
*/
|
|
87
145
|
const MAX_WORD_DIFF_COMPARISON_KEY_CODE_UNITS = 1048576;
|
|
146
|
+
/**
|
|
147
|
+
* How the LCS scores a match. `uniform` counts every matched token once;
|
|
148
|
+
* `content-first` (word granularity) never trades a matched word for matched
|
|
149
|
+
* punctuation, so moving a mark across a word cannot strike the word through.
|
|
150
|
+
*/
|
|
151
|
+
const MATCH_WEIGHTING = {
|
|
152
|
+
Uniform: "uniform",
|
|
153
|
+
ContentFirst: "content-first"
|
|
154
|
+
};
|
|
88
155
|
const ALIGNMENT_OPERATION = {
|
|
89
156
|
Equal: 1,
|
|
90
157
|
Delete: 2,
|
|
91
158
|
Insert: 3
|
|
92
159
|
};
|
|
160
|
+
const hasOppositeChange = (runs, direction) => runs.some(({ type }) => type === (direction === "insertion" ? "del" : "ins"));
|
|
93
161
|
const fitsCellBudget = (beforeLength, afterLength, budget) => beforeLength === 0 || afterLength === 0 || beforeLength <= Math.floor(budget.remainingCells / afterLength);
|
|
94
162
|
const pushRun = (runs, run) => {
|
|
95
163
|
const last = runs.at(-1);
|
|
@@ -128,13 +196,7 @@ const pushChangedRange = (runs, before, after, beforeRange, afterRange) => {
|
|
|
128
196
|
text: joinTokenRange(after, afterRange)
|
|
129
197
|
});
|
|
130
198
|
};
|
|
131
|
-
|
|
132
|
-
* Unique tokens common to both ranges, reduced to a monotone subsequence of
|
|
133
|
-
* target positions. Repeated boilerplate is deliberately ineligible: a unique
|
|
134
|
-
* clause number or name is stronger lineage evidence than another occurrence
|
|
135
|
-
* of "the" chosen by an arbitrary LCS tie.
|
|
136
|
-
*/
|
|
137
|
-
const findPatienceAnchors = (beforeKeys, afterKeys, beforeRange, afterRange) => {
|
|
199
|
+
const findPatienceAnchors = ({ beforeKeys, afterKeys, beforeRange, afterRange, weighting }) => {
|
|
138
200
|
const beforeOccurrences = /* @__PURE__ */ new Map();
|
|
139
201
|
for (let index = beforeRange.start; index < beforeRange.end; index++) {
|
|
140
202
|
const key = beforeKeys[index] ?? "";
|
|
@@ -155,7 +217,7 @@ const findPatienceAnchors = (beforeKeys, afterKeys, beforeRange, afterRange) =>
|
|
|
155
217
|
const key = beforeKeys[beforeIndex] ?? "";
|
|
156
218
|
const beforeCount = beforeOccurrences.get(key);
|
|
157
219
|
const afterOccurrence = afterOccurrences.get(key);
|
|
158
|
-
if (beforeCount === 1 && afterOccurrence?.count === 1) candidates.push({
|
|
220
|
+
if ((weighting === MATCH_WEIGHTING.Uniform || !isSeparatorOnly(key)) && beforeCount === 1 && afterOccurrence?.count === 1) candidates.push({
|
|
159
221
|
beforeIndex,
|
|
160
222
|
afterIndex: afterOccurrence.index
|
|
161
223
|
});
|
|
@@ -275,7 +337,7 @@ const alignMonotoneChange = (beforeText, afterText, before, after, normalization
|
|
|
275
337
|
return runs;
|
|
276
338
|
};
|
|
277
339
|
/** Longest-common-subsequence alignment for one residual, bounded gap. */
|
|
278
|
-
const alignDenseGap = ({ before, after, beforeKeys, afterKeys, beforeRange, afterRange, runs, budget }) => {
|
|
340
|
+
const alignDenseGap = ({ before, after, beforeKeys, afterKeys, beforeRange, afterRange, runs, budget, weighting }) => {
|
|
279
341
|
const beforeLength = beforeRange.end - beforeRange.start;
|
|
280
342
|
const afterLength = afterRange.end - afterRange.start;
|
|
281
343
|
if (beforeLength === 0 || afterLength === 0) {
|
|
@@ -289,15 +351,24 @@ const alignDenseGap = ({ before, after, beforeKeys, afterKeys, beforeRange, afte
|
|
|
289
351
|
const cellCount = beforeLength * afterLength;
|
|
290
352
|
budget.remainingCells -= cellCount;
|
|
291
353
|
const lengths = new Uint32Array(cellCount);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
354
|
+
const contentWeight = weighting === MATCH_WEIGHTING.ContentFirst ? Math.min(beforeLength, afterLength) + 1 : 1;
|
|
355
|
+
const matchWeights = new Uint32Array(beforeLength);
|
|
356
|
+
for (let beforeOffset = 0; beforeOffset < beforeLength; beforeOffset++) {
|
|
357
|
+
const key = beforeKeys[beforeRange.start + beforeOffset] ?? "";
|
|
358
|
+
matchWeights[beforeOffset] = isSeparatorOnly(key) ? 1 : contentWeight;
|
|
359
|
+
}
|
|
360
|
+
for (let beforeOffset = 0; beforeOffset < beforeLength; beforeOffset++) {
|
|
361
|
+
const matchWeight = matchWeights[beforeOffset] ?? 1;
|
|
362
|
+
for (let afterOffset = 0; afterOffset < afterLength; afterOffset++) {
|
|
363
|
+
const index = beforeOffset * afterLength + afterOffset;
|
|
364
|
+
if (beforeKeys[beforeRange.start + beforeOffset] === afterKeys[afterRange.start + afterOffset]) {
|
|
365
|
+
lengths[index] = (beforeOffset > 0 && afterOffset > 0 ? lengths[(beforeOffset - 1) * afterLength + afterOffset - 1] ?? 0 : 0) + matchWeight;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const above = beforeOffset > 0 ? lengths[(beforeOffset - 1) * afterLength + afterOffset] ?? 0 : 0;
|
|
369
|
+
const left = afterOffset > 0 ? lengths[index - 1] ?? 0 : 0;
|
|
370
|
+
lengths[index] = Math.max(above, left);
|
|
297
371
|
}
|
|
298
|
-
const above = beforeOffset > 0 ? lengths[(beforeOffset - 1) * afterLength + afterOffset] ?? 0 : 0;
|
|
299
|
-
const left = afterOffset > 0 ? lengths[index - 1] ?? 0 : 0;
|
|
300
|
-
lengths[index] = Math.max(above, left);
|
|
301
372
|
}
|
|
302
373
|
const reversedOperations = new Uint8Array(beforeLength + afterLength);
|
|
303
374
|
let operationCount = 0;
|
|
@@ -404,7 +475,7 @@ const alignGap = (options) => {
|
|
|
404
475
|
* repeated boilerplate from winning a tie over a unique legal term, and makes
|
|
405
476
|
* a small edit inside a long paragraph pay for only its changed gap.
|
|
406
477
|
*/
|
|
407
|
-
const alignTokens = ({ beforeText, afterText, before, after, normalization, budget }) => {
|
|
478
|
+
const alignTokens = ({ beforeText, afterText, before, after, normalization, budget, weighting }) => {
|
|
408
479
|
let commonPrefixLength = 0;
|
|
409
480
|
let beforePrefixLength = 0;
|
|
410
481
|
let afterPrefixLength = 0;
|
|
@@ -491,7 +562,13 @@ const alignTokens = ({ beforeText, afterText, before, after, normalization, budg
|
|
|
491
562
|
start: 0,
|
|
492
563
|
end: afterMiddle.length
|
|
493
564
|
};
|
|
494
|
-
const anchors = findPatienceAnchors(
|
|
565
|
+
const anchors = findPatienceAnchors({
|
|
566
|
+
beforeKeys,
|
|
567
|
+
afterKeys,
|
|
568
|
+
beforeRange: middleRangeBefore,
|
|
569
|
+
afterRange: middleRangeAfter,
|
|
570
|
+
weighting
|
|
571
|
+
});
|
|
495
572
|
if (anchors.length === 0 && fullInputFits && fullInputFitsStorage) {
|
|
496
573
|
alignDenseGap({
|
|
497
574
|
before,
|
|
@@ -507,7 +584,8 @@ const alignTokens = ({ beforeText, afterText, before, after, normalization, budg
|
|
|
507
584
|
end: after.length
|
|
508
585
|
},
|
|
509
586
|
runs,
|
|
510
|
-
budget
|
|
587
|
+
budget,
|
|
588
|
+
weighting
|
|
511
589
|
});
|
|
512
590
|
return {
|
|
513
591
|
runs,
|
|
@@ -538,7 +616,8 @@ const alignTokens = ({ beforeText, afterText, before, after, normalization, budg
|
|
|
538
616
|
end: anchor.afterIndex
|
|
539
617
|
},
|
|
540
618
|
runs,
|
|
541
|
-
budget
|
|
619
|
+
budget,
|
|
620
|
+
weighting
|
|
542
621
|
});
|
|
543
622
|
pushRun(runs, {
|
|
544
623
|
type: "equal",
|
|
@@ -563,7 +642,8 @@ const alignTokens = ({ beforeText, afterText, before, after, normalization, budg
|
|
|
563
642
|
end: afterMiddle.length
|
|
564
643
|
},
|
|
565
644
|
runs,
|
|
566
|
-
budget
|
|
645
|
+
budget,
|
|
646
|
+
weighting
|
|
567
647
|
});
|
|
568
648
|
pushEqualRange(runs, before, after, {
|
|
569
649
|
start: beforeMiddleEnd,
|
|
@@ -587,6 +667,16 @@ const isTooFragmented = (runs, averageLength) => {
|
|
|
587
667
|
for (const run of runs) if (run.type === "equal") sumOfSquares += run.before.length * run.before.length;
|
|
588
668
|
return sumOfSquares * FRAGMENTATION_SCALE < averageLength * averageLength;
|
|
589
669
|
};
|
|
670
|
+
/** True when a run changes words, not only punctuation or whitespace. */
|
|
671
|
+
const isContentChange = (run) => run.type !== "equal" && !isSeparatorOnly(run.text);
|
|
672
|
+
/** True when the change region starting at `from`, walking by `step`, changes words. */
|
|
673
|
+
const changeCarriesContent = (runs, { from, step }) => {
|
|
674
|
+
for (let index = from;; index += step) {
|
|
675
|
+
const run = runs[index];
|
|
676
|
+
if (run === void 0 || run.type === "equal") return false;
|
|
677
|
+
if (isContentChange(run)) return true;
|
|
678
|
+
}
|
|
679
|
+
};
|
|
590
680
|
/**
|
|
591
681
|
* Turn a match the quality rules rejected back into the change it interrupts.
|
|
592
682
|
*
|
|
@@ -602,7 +692,13 @@ const demoteRejectedMatches = (runs) => {
|
|
|
602
692
|
continue;
|
|
603
693
|
}
|
|
604
694
|
const interruptsAChange = runs[index - 1] !== void 0 || runs[index + 1] !== void 0;
|
|
605
|
-
const isIsland = runs[index - 1] !== void 0 && runs[index + 1] !== void 0
|
|
695
|
+
const isIsland = runs[index - 1] !== void 0 && runs[index + 1] !== void 0 && (changeCarriesContent(runs, {
|
|
696
|
+
from: index - 1,
|
|
697
|
+
step: -1
|
|
698
|
+
}) || changeCarriesContent(runs, {
|
|
699
|
+
from: index + 1,
|
|
700
|
+
step: 1
|
|
701
|
+
}));
|
|
606
702
|
if (!(interruptsAChange && run.units <= MAX_SEPARATOR_ONLY_MATCH_UNITS && isSeparatorOnly(run.before) || isIsland && run.units < MINIMUM_ISOLATED_MATCH_UNITS)) {
|
|
607
703
|
kept.push(run);
|
|
608
704
|
continue;
|
|
@@ -617,6 +713,70 @@ const demoteRejectedMatches = (runs) => {
|
|
|
617
713
|
}
|
|
618
714
|
return kept;
|
|
619
715
|
};
|
|
716
|
+
const TOKEN_WHITESPACE = /^(\s*)(.*?)(\s*)$/su;
|
|
717
|
+
const splitTokenWhitespace = (token) => {
|
|
718
|
+
const [, leading = "", text = "", trailing = ""] = TOKEN_WHITESPACE.exec(token) ?? [];
|
|
719
|
+
return {
|
|
720
|
+
leading,
|
|
721
|
+
text,
|
|
722
|
+
trailing
|
|
723
|
+
};
|
|
724
|
+
};
|
|
725
|
+
const pushChangedText = (runs, before, after) => {
|
|
726
|
+
if (before.length > 0) pushRun(runs, {
|
|
727
|
+
type: "del",
|
|
728
|
+
text: before
|
|
729
|
+
});
|
|
730
|
+
if (after.length > 0) pushRun(runs, {
|
|
731
|
+
type: "ins",
|
|
732
|
+
text: after
|
|
733
|
+
});
|
|
734
|
+
};
|
|
735
|
+
const pushWhitespace = (runs, before, after) => {
|
|
736
|
+
if (before !== after) {
|
|
737
|
+
pushChangedText(runs, before, after);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (before.length > 0) pushRun(runs, {
|
|
741
|
+
type: "equal",
|
|
742
|
+
before,
|
|
743
|
+
after,
|
|
744
|
+
units: 0
|
|
745
|
+
});
|
|
746
|
+
};
|
|
747
|
+
/**
|
|
748
|
+
* Mark the whitespace a matched word gained, lost or changed, leaving the word
|
|
749
|
+
* itself equal. Word tokens are aligned on their text alone, so an `equal` run
|
|
750
|
+
* may pair `"Závislá"` with `" Závislá"`; both sides of such a run tokenize
|
|
751
|
+
* to the same number of words. Every piece of both texts is emitted exactly
|
|
752
|
+
* once and in order, so both strings reconstruct whatever the pairing.
|
|
753
|
+
*/
|
|
754
|
+
const separateWhitespaceChanges = (runs) => {
|
|
755
|
+
const separated = [];
|
|
756
|
+
for (const run of runs) {
|
|
757
|
+
if (run.type !== "equal" || run.before === run.after) {
|
|
758
|
+
pushRun(separated, run);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const beforeTokens = tokenizeWords(run.before);
|
|
762
|
+
const afterTokens = tokenizeWords(run.after);
|
|
763
|
+
const tokenCount = Math.max(beforeTokens.length, afterTokens.length);
|
|
764
|
+
for (let index = 0; index < tokenCount; index++) {
|
|
765
|
+
const before = splitTokenWhitespace(beforeTokens[index] ?? "");
|
|
766
|
+
const after = splitTokenWhitespace(afterTokens[index] ?? "");
|
|
767
|
+
pushWhitespace(separated, before.leading, after.leading);
|
|
768
|
+
if (before.text.length > 0 && after.text.length > 0) pushRun(separated, {
|
|
769
|
+
type: "equal",
|
|
770
|
+
before: before.text,
|
|
771
|
+
after: after.text,
|
|
772
|
+
units: 1
|
|
773
|
+
});
|
|
774
|
+
else pushChangedText(separated, before.text, after.text);
|
|
775
|
+
pushWhitespace(separated, before.trailing, after.trailing);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return separated;
|
|
779
|
+
};
|
|
620
780
|
const toSegments = (runs) => {
|
|
621
781
|
const segments = [];
|
|
622
782
|
const push = (type, text) => {
|
|
@@ -694,7 +854,13 @@ const diffWordSegmentsWithBudget = (before, after, options, budget) => {
|
|
|
694
854
|
text: before
|
|
695
855
|
}];
|
|
696
856
|
const granularity = options.granularity ?? "word";
|
|
697
|
-
const
|
|
857
|
+
const requestedNormalization = options.normalization ?? {};
|
|
858
|
+
const normalization = granularity === "word" ? {
|
|
859
|
+
...requestedNormalization,
|
|
860
|
+
whitespace: true
|
|
861
|
+
} : requestedNormalization;
|
|
862
|
+
const whitespaceIsSignificant = granularity === "word" && requestedNormalization.whitespace !== true;
|
|
863
|
+
const weighting = granularity === "word" ? MATCH_WEIGHTING.ContentFirst : MATCH_WEIGHTING.Uniform;
|
|
698
864
|
const beforeTokens = tokenize(before, granularity);
|
|
699
865
|
const afterTokens = tokenize(after, granularity);
|
|
700
866
|
if (beforeTokens.length === 0 && afterTokens.length === 0) return [];
|
|
@@ -705,12 +871,12 @@ const diffWordSegmentsWithBudget = (before, after, options, budget) => {
|
|
|
705
871
|
before: beforeTokens,
|
|
706
872
|
after: afterTokens,
|
|
707
873
|
normalization,
|
|
708
|
-
budget
|
|
874
|
+
budget,
|
|
875
|
+
weighting
|
|
709
876
|
});
|
|
710
877
|
let surviving = demoteRejectedMatches(aligned.runs);
|
|
711
|
-
const inventedOppositeChange = aligned.monotoneDirection === "insertion" && surviving.some(({ type }) => type === "del") || aligned.monotoneDirection === "deletion" && surviving.some(({ type }) => type === "ins");
|
|
712
878
|
const monotoneDirection = aligned.monotoneDirection;
|
|
713
|
-
if (
|
|
879
|
+
if (monotoneDirection !== null && hasOppositeChange(surviving, monotoneDirection)) if (beforeTokens.length + afterTokens.length <= MAX_WORD_DIFF_ANCHOR_TOKENS && before.length + after.length <= MAX_WORD_DIFF_COMPARISON_KEY_CODE_UNITS && fitsCellBudget(beforeTokens.length, afterTokens.length, budget)) {
|
|
714
880
|
const unanchored = [];
|
|
715
881
|
alignDenseGap({
|
|
716
882
|
before: beforeTokens,
|
|
@@ -726,12 +892,18 @@ const diffWordSegmentsWithBudget = (before, after, options, budget) => {
|
|
|
726
892
|
end: afterTokens.length
|
|
727
893
|
},
|
|
728
894
|
runs: unanchored,
|
|
729
|
-
budget
|
|
895
|
+
budget,
|
|
896
|
+
weighting
|
|
730
897
|
});
|
|
731
|
-
surviving = unanchored;
|
|
732
|
-
} else surviving =
|
|
733
|
-
if (aligned.monotoneDirection === null && isTooFragmented(surviving, (before.length + after.length) / 2)) return wholeStringReplacement(before, after);
|
|
734
|
-
|
|
898
|
+
surviving = hasOppositeChange(unanchored, monotoneDirection) ? alignMonotoneChange(before, after, beforeTokens, afterTokens, normalization, monotoneDirection) : unanchored;
|
|
899
|
+
} else surviving = hasOppositeChange(aligned.runs, monotoneDirection) ? alignMonotoneChange(before, after, beforeTokens, afterTokens, normalization, monotoneDirection) : aligned.runs;
|
|
900
|
+
if (aligned.monotoneDirection === null && surviving.some(isContentChange) && isTooFragmented(surviving, (before.length + after.length) / 2)) return wholeStringReplacement(before, after);
|
|
901
|
+
let marked = whitespaceIsSignificant ? separateWhitespaceChanges(surviving) : surviving;
|
|
902
|
+
if (monotoneDirection !== null && hasOppositeChange(marked, monotoneDirection)) {
|
|
903
|
+
const monotoneRuns = alignMonotoneChange(before, after, beforeTokens, afterTokens, normalization, monotoneDirection);
|
|
904
|
+
marked = whitespaceIsSignificant ? separateWhitespaceChanges(monotoneRuns) : monotoneRuns;
|
|
905
|
+
}
|
|
906
|
+
return toSegments(orderDeletionsFirst(marked));
|
|
735
907
|
};
|
|
736
908
|
/**
|
|
737
909
|
* One internal comparison/apply scope. Every diff shares the same quadratic
|
|
@@ -760,4 +932,4 @@ const wordDiffSessionFromOptions = (options) => {
|
|
|
760
932
|
};
|
|
761
933
|
const diffWordSegments = (before, after, options = {}) => diffWordSegmentsWithBudget(before, after, options, { remainingCells: MAX_WORD_DIFF_CELLS });
|
|
762
934
|
//#endregion
|
|
763
|
-
export { WORD_DIFF_GRANULARITIES, createScopedWordDiffOptions, createWordDiffSession, diffWordSegments, wordDiffSessionFromOptions };
|
|
935
|
+
export { MAX_EDGE_PUNCTUATION_TOKENS, WORD_DIFF_GRANULARITIES, createScopedWordDiffOptions, createWordDiffSession, diffWordSegments, tokenizeWords, wordDiffSessionFromOptions };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
-
import { EditorView } from "prosemirror-view";
|
|
2
|
+
import { DirectEditorProps, EditorView } from "prosemirror-view";
|
|
3
3
|
//#region src/controller/headerFooterEditorManager.d.ts
|
|
4
4
|
type HeaderFooterPartKind = "header" | "footer";
|
|
5
5
|
type HeaderFooterPartKey = {
|
|
@@ -14,6 +14,7 @@ type HeaderFooterEditorTransaction = {
|
|
|
14
14
|
view: EditorView;
|
|
15
15
|
};
|
|
16
16
|
type HeaderFooterEditorManagerDeps = {
|
|
17
|
+
createView?: ((mountNode: HTMLElement, props: DirectEditorProps) => EditorView) | undefined;
|
|
17
18
|
getDocument: () => document_d_exports.Document | null;
|
|
18
19
|
getHost: () => HTMLElement | null;
|
|
19
20
|
getStyles: () => document_d_exports.StyleDefinitions | null | undefined;
|
|
@@ -12,6 +12,7 @@ import { schema } from "../prosemirror/schema/index.js";
|
|
|
12
12
|
import { EditorState } from "prosemirror-state";
|
|
13
13
|
import { EditorView } from "prosemirror-view";
|
|
14
14
|
//#region src/controller/headerFooterEditorManager.ts
|
|
15
|
+
const createEditorView = (mountNode, props) => new EditorView(mountNode, props);
|
|
15
16
|
const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
|
|
16
17
|
const headerFooterToProseDocWithDetachedWatermarkHost = (headerFooter, options) => {
|
|
17
18
|
return headerFooterToProseDoc(headerFooter.content.map((block, blockIndex) => {
|
|
@@ -109,7 +110,7 @@ const createHeaderFooterEditorManager = (deps) => {
|
|
|
109
110
|
mountNode.dataset["hfRId"] = part.rId;
|
|
110
111
|
mountNode.dataset["hfKind"] = part.kind;
|
|
111
112
|
host.append(mountNode);
|
|
112
|
-
const view =
|
|
113
|
+
const view = (deps.createView ?? createEditorView)(mountNode, {
|
|
113
114
|
state: buildInitialState(headerFooter, styles, theme, numbering, manager),
|
|
114
115
|
dispatchTransaction(transaction) {
|
|
115
116
|
const nextState = view.state.apply(transaction);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { NoteStoryKey } from "../types/editor-story.js";
|
|
3
3
|
import { Plugin } from "prosemirror-state";
|
|
4
|
-
import { EditorView } from "prosemirror-view";
|
|
4
|
+
import { DirectEditorProps, EditorView } from "prosemirror-view";
|
|
5
5
|
//#region src/controller/noteEditorManager.d.ts
|
|
6
6
|
type NoteStoryKind = NoteStoryKey["kind"];
|
|
7
7
|
type NoteEditorTransaction = NoteStoryKey & {
|
|
@@ -10,6 +10,7 @@ type NoteEditorTransaction = NoteStoryKey & {
|
|
|
10
10
|
view: EditorView;
|
|
11
11
|
};
|
|
12
12
|
type NoteEditorManagerDeps = {
|
|
13
|
+
createView?: ((mountNode: HTMLElement, props: DirectEditorProps) => EditorView) | undefined;
|
|
13
14
|
getDocument: () => document_d_exports.Document | null;
|
|
14
15
|
getHost: () => HTMLElement | null;
|
|
15
16
|
getPlugins?: (() => Plugin[]) | undefined;
|
|
@@ -12,6 +12,7 @@ import { EditorState } from "prosemirror-state";
|
|
|
12
12
|
import { EditorView } from "prosemirror-view";
|
|
13
13
|
//#region src/controller/noteEditorManager.ts
|
|
14
14
|
/** Framework-neutral lifecycle owner for editable footnote and endnote stories. */
|
|
15
|
+
const createEditorView = (mountNode, props) => new EditorView(mountNode, props);
|
|
15
16
|
const EMPTY_PLUGINS = [];
|
|
16
17
|
const storyMapKey = ({ kind, noteId }) => `${kind}:${String(noteId)}`;
|
|
17
18
|
const isNormalFootnote = (note) => !isSeparatorFootnote(note);
|
|
@@ -110,7 +111,7 @@ const createNoteEditorManager = (deps) => {
|
|
|
110
111
|
mountNode.dataset["noteId"] = String(storyKey.noteId);
|
|
111
112
|
host.append(mountNode);
|
|
112
113
|
const proseDocument = noteToProseDocument(note, styles, theme);
|
|
113
|
-
const view =
|
|
114
|
+
const view = (deps.createView ?? createEditorView)(mountNode, {
|
|
114
115
|
state: buildInitialState(proseDocument, styles, numbering, manager, externalPlugins),
|
|
115
116
|
dispatchTransaction(transaction) {
|
|
116
117
|
view.updateState(view.state.apply(transaction));
|
package/dist/docx/relsParser.js
CHANGED
|
@@ -27,6 +27,14 @@ const RELATIONSHIP_TYPES = {
|
|
|
27
27
|
comments: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
|
28
28
|
commentsExtended: "http://schemas.microsoft.com/office/2011/relationships/commentsExtended"
|
|
29
29
|
};
|
|
30
|
+
const STRICT_OFFICE_RELATIONSHIP_PREFIX = "http://purl.oclc.org/ooxml/officeDocument/relationships/";
|
|
31
|
+
const TRANSITIONAL_OFFICE_RELATIONSHIP_PREFIX = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/";
|
|
32
|
+
const KNOWN_RELATIONSHIP_TYPES = new Set(Object.values(RELATIONSHIP_TYPES));
|
|
33
|
+
const normalizeRelationshipType = (type) => {
|
|
34
|
+
if (!type.startsWith(STRICT_OFFICE_RELATIONSHIP_PREFIX)) return type;
|
|
35
|
+
const normalized = TRANSITIONAL_OFFICE_RELATIONSHIP_PREFIX + type.slice(56);
|
|
36
|
+
return KNOWN_RELATIONSHIP_TYPES.has(normalized) ? normalized : type;
|
|
37
|
+
};
|
|
30
38
|
/**
|
|
31
39
|
* Parse a .rels XML file into a RelationshipMap
|
|
32
40
|
*
|
|
@@ -49,7 +57,7 @@ function parseRelationships(relsXml) {
|
|
|
49
57
|
if (!id || !type || !target) continue;
|
|
50
58
|
const relationship = {
|
|
51
59
|
id,
|
|
52
|
-
type,
|
|
60
|
+
type: normalizeRelationshipType(type),
|
|
53
61
|
target
|
|
54
62
|
};
|
|
55
63
|
if (targetMode === "External") relationship.targetMode = "External";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseCommentsExtended } from "./commentParser.js";
|
|
2
2
|
import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
|
|
3
3
|
import { hasUnsynthesizedReplyRanges } from "./commentReplyMarkers.js";
|
|
4
|
+
import { detectDocxConformanceClass } from "./conformance.js";
|
|
4
5
|
import { validateFolioDocumentModel } from "./modelValidation.js";
|
|
5
6
|
import { parseNumbering } from "./numberingParser.js";
|
|
6
7
|
import { isUnsafePackagePath } from "./packageParts.js";
|
|
@@ -13,6 +14,7 @@ import { serializeDocument } from "./serializer/documentSerializer.js";
|
|
|
13
14
|
import { serializeEndnotes, serializeFootnotes } from "./serializer/noteSerializer.js";
|
|
14
15
|
import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
|
|
15
16
|
import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
|
|
17
|
+
import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
|
|
16
18
|
//#region src/docx/selectiveSave.ts
|
|
17
19
|
/**
|
|
18
20
|
* Splice edited footnote/endnote paragraphs into their parts and return the
|
|
@@ -217,8 +219,9 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
217
219
|
}
|
|
218
220
|
const sourceCommentsFile = zip.file("word/comments.xml");
|
|
219
221
|
if (hasComments || sourceCommentsFile) {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
+
const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.async("text") : void 0;
|
|
223
|
+
const commentsXml = serializeComments(commentPlan, sourceCommentsXml === void 0 ? void 0 : readRootNamespaceBindings(sourceCommentsXml));
|
|
224
|
+
if (commentsXml !== sourceCommentsXml) updates.set("word/comments.xml", commentsXml);
|
|
222
225
|
}
|
|
223
226
|
if (hasComments) {
|
|
224
227
|
const ctFile = zip.file("[Content_Types].xml");
|
|
@@ -245,6 +248,12 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
245
248
|
const corePropsXml = await corePropsFile.async("text");
|
|
246
249
|
updates.set("docProps/core.xml", updateCoreProperties(corePropsXml, { updateModifiedDate: true }));
|
|
247
250
|
}
|
|
251
|
+
if ([...updates.keys()].some((path) => {
|
|
252
|
+
const lowerPath = path.toLowerCase();
|
|
253
|
+
return lowerPath.startsWith("word/") && (lowerPath.endsWith(".xml") || lowerPath.endsWith(".rels"));
|
|
254
|
+
})) {
|
|
255
|
+
if (detectDocxConformanceClass(await findZipEntryCaseInsensitive(zip, "word/document.xml")?.async("text") ?? null) === DOCX_CONFORMANCE_CLASSES.STRICT) return null;
|
|
256
|
+
}
|
|
248
257
|
return await applyUpdatesToZip(zip, updates);
|
|
249
258
|
} catch {
|
|
250
259
|
return null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CharacterSpacingAttrs, CharacterStyleAttrs, CommentAttrs, EmphasisMarkAttrs, FontFamilyAttrs, FontSizeAttrs, FootnoteRefAttrs, HighlightAttrs, HyperlinkAttrs, InlineWrapperAttrs, InlineWrapperLayer, LanguageAttrs, RunFormattingOverrideAttrs, RunIdentityMarkAttrs, RunPropertyChangeMarkAttrs, RunShadingAttrs, StrikeAttrs, TextColorAttrs, TextEffectAttrs, TrackedChangeMarkAttrs, UnderlineAttrs } from "../schema/marks.js";
|
|
1
|
+
import { CharacterSpacingAttrs, CharacterStyleAttrs, CommentAttrs, EmphasisMarkAttrs, FontFamilyAttrs, FontSizeAttrs, FootnoteRefAttrs, HighlightAttrs, HyperlinkAttrs, InlineWrapperAttrs, InlineWrapperLayer, LanguageAttrs, RunFormattingOverrideAttrs, RunIdentityMarkAttrs, RunPropertyChangeMarkAttrs, RunShadingAttrs, StrikeAttrs, TextColorAttrs, TextEffectAttrs, TrackedChangeMarkAttrs, TrackedRevisionAncestor, UnderlineAttrs } from "../schema/marks.js";
|
|
2
2
|
import { BlockSdtAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, MathAttrs, PageBreakRunAttrs, ParagraphAttrs, PreservedBlockAttrs, PreservedXmlAttrs, SdtAttrs, ShapeAttrs, SymbolAttrs, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAttrs } from "../schema/nodes.js";
|
|
3
3
|
import "../schema/index.js";
|
|
4
4
|
import { Mark, Node } from "prosemirror-model";
|
|
@@ -76,6 +76,7 @@ declare const readFootnoteRefMarkAttrs: (mark: Mark) => ReadProseMirrorAttrsResu
|
|
|
76
76
|
declare const expectFootnoteRefMarkAttrs: (mark: Mark) => FootnoteRefAttrs;
|
|
77
77
|
declare const readCommentMarkAttrs: (mark: Mark) => ReadProseMirrorAttrsResult<CommentAttrs>;
|
|
78
78
|
declare const expectCommentMarkAttrs: (mark: Mark) => CommentAttrs;
|
|
79
|
+
declare const isTrackedRevisionAncestorArray: (value: unknown) => value is readonly TrackedRevisionAncestor[];
|
|
79
80
|
declare const readTrackedChangeMarkAttrs: (mark: Mark) => ReadProseMirrorAttrsResult<TrackedChangeMarkAttrs>;
|
|
80
81
|
declare const expectTrackedChangeMarkAttrs: (mark: Mark) => TrackedChangeMarkAttrs;
|
|
81
82
|
declare const readRunPropertyChangeMarkAttrs: (mark: Mark) => ReadProseMirrorAttrsResult<RunPropertyChangeMarkAttrs>;
|
|
@@ -118,4 +119,4 @@ declare const mergeTableRowAttrs: (node: Node, patch: NodeAttrPatch<TableRowAttr
|
|
|
118
119
|
*/
|
|
119
120
|
declare const mergeTableCellAttrs: (node: Node, patch: NodeAttrPatch<TableCellAttrs>) => TableCellAttrs;
|
|
120
121
|
//#endregion
|
|
121
|
-
export { ProseMirrorAttrIssue, ReadProseMirrorAttrsResult, expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectInlineWrapperMarkAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectPageBreakRunAttrs, expectParagraphAttrs, expectPreservedBlockAttrs, expectPreservedXmlAttrs, expectRunFormattingOverrideMarkAttrs, expectRunIdentityMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs, mergeImageAttrs, mergeParagraphAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs, readBlockSdtAttrs, readCharacterSpacingMarkAttrs, readCharacterStyleMarkAttrs, readCommentMarkAttrs, readEmphasisMarkAttrs, readFieldAttrs, readFontFamilyMarkAttrs, readFontSizeMarkAttrs, readFootnoteRefMarkAttrs, readHardBreakAttrs, readHighlightMarkAttrs, readHyperlinkMarkAttrs, readImageAttrs, readInlineWrapperMarkAttrs, readInlineWrapperStack, readLanguageMarkAttrs, readMathAttrs, readPageBreakRunAttrs, readParagraphAttrs, readPreservedBlockAttrs, readPreservedXmlAttrs, readRunFormattingOverrideMarkAttrs, readRunIdentityMarkAttrs, readRunPropertyChangeMarkAttrs, readRunShadingMarkAttrs, readSdtAttrs, readShapeAttrs, readStrikeMarkAttrs, readSymbolAttrs, readTabAttrs, readTableAttrs, readTableCellAttrs, readTableRowAttrs, readTextBoxAttrs, readTextColorMarkAttrs, readTextEffectMarkAttrs, readTrackedChangeMarkAttrs, readUnderlineMarkAttrs };
|
|
122
|
+
export { ProseMirrorAttrIssue, ReadProseMirrorAttrsResult, expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectInlineWrapperMarkAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectPageBreakRunAttrs, expectParagraphAttrs, expectPreservedBlockAttrs, expectPreservedXmlAttrs, expectRunFormattingOverrideMarkAttrs, expectRunIdentityMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs, isTrackedRevisionAncestorArray, mergeImageAttrs, mergeParagraphAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs, readBlockSdtAttrs, readCharacterSpacingMarkAttrs, readCharacterStyleMarkAttrs, readCommentMarkAttrs, readEmphasisMarkAttrs, readFieldAttrs, readFontFamilyMarkAttrs, readFontSizeMarkAttrs, readFootnoteRefMarkAttrs, readHardBreakAttrs, readHighlightMarkAttrs, readHyperlinkMarkAttrs, readImageAttrs, readInlineWrapperMarkAttrs, readInlineWrapperStack, readLanguageMarkAttrs, readMathAttrs, readPageBreakRunAttrs, readParagraphAttrs, readPreservedBlockAttrs, readPreservedXmlAttrs, readRunFormattingOverrideMarkAttrs, readRunIdentityMarkAttrs, readRunPropertyChangeMarkAttrs, readRunShadingMarkAttrs, readSdtAttrs, readShapeAttrs, readStrikeMarkAttrs, readSymbolAttrs, readTabAttrs, readTableAttrs, readTableCellAttrs, readTableRowAttrs, readTextBoxAttrs, readTextColorMarkAttrs, readTextEffectMarkAttrs, readTrackedChangeMarkAttrs, readUnderlineMarkAttrs };
|