@stll/folio-core 0.60.0 → 0.62.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 +13 -0
- package/dist/ai-edits/apply.js +139 -32
- package/dist/ai-edits/headless.d.ts +3 -0
- package/dist/ai-edits/headless.js +70 -75
- package/dist/ai-edits/note-story-inventory.d.ts +7 -0
- package/dist/ai-edits/note-story-inventory.js +11 -0
- package/dist/ai-edits/read.js +16 -9
- package/dist/ai-edits/revisionStretches.js +52 -1
- package/dist/ai-edits/table-row-column-mutations.js +6 -1
- package/dist/compare/reproducible-package.js +2 -1
- package/dist/compare/verification.js +1 -0
- package/dist/compat/eigenpal.d.ts +3 -1
- package/dist/controller/canonicalClipboard.js +2 -1
- package/dist/controller/canonicalListRendering.d.ts +4 -6
- package/dist/controller/canonicalListRendering.js +3 -157
- package/dist/controller/headerFooterEditorManager.d.ts +1 -0
- package/dist/controller/headerFooterEditorManager.js +11 -0
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +3 -1
- package/dist/controller/layoutPipeline.d.ts +3 -1
- package/dist/controller/layoutPipeline.js +13 -4
- package/dist/controller/layoutSession.d.ts +2 -1
- package/dist/controller/noteEditorManager.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +28 -12
- package/dist/display-list/build/paragraphPrimitives.js +15 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -5
- package/dist/display-list/types.d.ts +7 -3
- package/dist/docx/canonicalStoryRepack.js +3 -2
- package/dist/docx/commentTextParagraph.d.ts +6 -0
- package/dist/docx/commentTextParagraph.js +15 -0
- package/dist/docx/conformance.js +6 -1
- package/dist/docx/diagramPreview.js +2 -1
- package/dist/docx/documentParser.d.ts +13 -1
- package/dist/docx/documentParser.js +1 -1
- package/dist/docx/encryption/compoundFile.d.ts +0 -8
- package/dist/docx/encryption/compoundFile.js +2 -1
- package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
- package/dist/docx/encryption/cryptoBytes.js +3 -18
- package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
- package/dist/docx/encryption/encryptionInfo.js +2 -1
- package/dist/docx/ensureParaIds.js +2 -1
- package/dist/docx/groupDrawingParser.js +28 -13
- package/dist/docx/listModelNormalization.d.ts +10 -0
- package/dist/docx/listModelNormalization.js +166 -0
- package/dist/docx/metadataPrivacy.js +3 -2
- package/dist/docx/metafileRaster.js +2 -1
- package/dist/docx/packageParts.js +6 -5
- package/dist/docx/paragraphParser.js +6 -4
- package/dist/docx/parser.js +5 -4
- package/dist/docx/preservedRunContent.js +11 -2
- package/dist/docx/removeHeaderFooterParts.js +8 -7
- package/dist/docx/replyToComment.js +3 -13
- package/dist/docx/rezip.js +88 -89
- package/dist/docx/runParser.js +12 -6
- package/dist/docx/runPropertyPresence.d.ts +11 -0
- package/dist/docx/runPropertyPresence.js +14 -0
- package/dist/docx/selectiveSave.js +15 -14
- package/dist/docx/serializer/runSerializer.js +3 -2
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.js +2 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
- package/dist/docx/server/inspectDocxPackage.js +5 -1
- package/dist/docx/server/validateDocxConformance.js +2 -1
- package/dist/docx/tableParser.js +7 -4
- package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
- package/dist/docx/tableRowPropertyPresence.js +15 -0
- package/dist/docx/unzip.js +2 -1
- package/dist/docx/xmlEncoding.js +6 -5
- package/dist/fonts/fontsourceFaces.js +2 -1
- package/dist/fonts/sfnt/parse.js +6 -9
- package/dist/fonts/sfnt/subset.js +2 -1
- package/dist/fonts/sfnt/tables.js +2 -3
- package/dist/fonts/sfnt/woff.js +2 -1
- package/dist/i18n/messages/ar.gen.js +1 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/cs.gen.js +1 -0
- package/dist/i18n/messages/de.gen.js +1 -0
- package/dist/i18n/messages/en.gen.js +1 -0
- package/dist/i18n/messages/es.gen.js +1 -0
- package/dist/i18n/messages/et.gen.js +1 -0
- package/dist/i18n/messages/fr.gen.js +1 -0
- package/dist/i18n/messages/he.gen.js +1 -0
- package/dist/i18n/messages/hi.gen.js +1 -0
- package/dist/i18n/messages/hu.gen.js +1 -0
- package/dist/i18n/messages/lt.gen.js +1 -0
- package/dist/i18n/messages/lv.gen.js +1 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/i18n/messages/pl.gen.js +1 -0
- package/dist/i18n/messages/pt-BR.gen.js +1 -0
- package/dist/i18n/messages/sk.gen.js +1 -0
- package/dist/i18n/messages/tr.gen.js +1 -0
- package/dist/i18n/messages/zh-CN.gen.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/internal/revisionResolutionInline.d.ts +2 -1
- package/dist/internal/revisionResolutionInline.js +22 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -7
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +1 -1
- package/dist/layout-bridge/convert/endnoteLayout.js +1 -3
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
- package/dist/layout-engine/index.js +2 -2
- package/dist/layout-engine/measure/crossRunWords.js +12 -2
- package/dist/layout-engine/noteAreaFlow.d.ts +4 -4
- package/dist/layout-engine/noteAreaFlow.js +12 -6
- package/dist/layout-engine/types.d.ts +5 -2
- package/dist/layout-painter/index.d.ts +1 -1
- package/dist/managers/AutoSaveManager.js +2 -1
- package/dist/managers/EditorModeManager.d.ts +10 -1
- package/dist/managers/EditorModeManager.js +15 -1
- package/dist/managers/FindReplaceManager.d.ts +13 -6
- package/dist/managers/FindReplaceManager.js +15 -9
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.js +2 -6
- package/dist/managers/reviewDisplayMode.d.ts +8 -0
- package/dist/managers/reviewDisplayMode.js +15 -0
- package/dist/paged-layout/editorScrollRoot.d.ts +3 -1
- package/dist/paged-layout/editorScrollRoot.js +2 -2
- package/dist/paged-layout/pageText.d.ts +11 -2
- package/dist/paged-layout/pageText.js +8 -3
- package/dist/pdf/fonts.js +5 -14
- package/dist/pdf/images.js +4 -2
- package/dist/pdf/objects.d.ts +0 -11
- package/dist/pdf/objects.js +7 -9
- package/dist/pdf/writePdf.js +2 -1
- package/dist/prosemirror/commands/comments.js +51 -13
- package/dist/prosemirror/commentRangeInlineAtoms.d.ts +40 -0
- package/dist/prosemirror/commentRangeInlineAtoms.js +33 -0
- package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
- package/dist/prosemirror/containerFinalParagraph.js +8 -8
- package/dist/prosemirror/conversion/fromProseDoc.js +14 -8
- package/dist/prosemirror/conversion/toProseDoc.js +16 -23
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
- package/dist/prosemirror/fieldRepresentation.js +4 -2
- package/dist/prosemirror/findReplaceSelection.d.ts +48 -2
- package/dist/prosemirror/findReplaceSelection.js +153 -38
- package/dist/prosemirror/inlineDisplayText.d.ts +10 -0
- package/dist/prosemirror/inlineDisplayText.js +29 -0
- package/dist/prosemirror/markupViewProjection.d.ts +10 -1
- package/dist/prosemirror/markupViewProjection.js +21 -3
- package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
- package/dist/prosemirror/replacedAnnotations.js +71 -12
- package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
- package/dist/prosemirror/runFormattingReconciliation.js +10 -2
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
- package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
- package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
- package/dist/utils/base64.d.ts +1 -31
- package/dist/utils/base64.js +19 -33
- package/dist/utils/clipboard.js +5 -4
- package/dist/utils/colorResolver.js +11 -2
- package/dist/utils/findMatchView.d.ts +32 -0
- package/dist/utils/findMatchView.js +406 -0
- package/dist/utils/findReplace.d.ts +67 -4
- package/dist/utils/findReplace.js +359 -37
- package/dist/utils/findSearchKey.d.ts +29 -0
- package/dist/utils/findSearchKey.js +34 -0
- package/dist/utils/replaceText.js +138 -94
- package/dist/utils/sliceText.d.ts +2 -0
- package/dist/utils/sliceText.js +2 -0
- package/dist/utils/textEncoding.d.ts +2 -0
- package/dist/utils/textEncoding.js +2 -0
- package/dist/utils/tiffConverter.d.ts +0 -9
- package/dist/utils/tiffConverter.js +2 -4
- package/package.json +51 -25
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import { parseFieldType } from "../docx/fieldParser.js";
|
|
2
|
+
import { HYPHEN_TEXT_CARRIERS } from "../prosemirror/conversion/hyphenTextCarriers.js";
|
|
3
|
+
import { isTableCellRetainedInReviewView } from "../prosemirror/tableCellRevisionVisibility.js";
|
|
4
|
+
import { decodeOoxmlSymbolCharacter } from "./ooxmlSymbol.js";
|
|
1
5
|
import { panic } from "better-result";
|
|
6
|
+
import { documentStories, sameStory, storyBody } from "@stll/docx-core/ops";
|
|
2
7
|
//#region src/utils/findReplace.ts
|
|
8
|
+
/** One replacement policy for every host; searchability does not imply editability. */
|
|
9
|
+
const replaceEligibility = (match) => {
|
|
10
|
+
if (match.story !== void 0 && match.story !== "main" || match.textBoxIndex !== void 0 || match.commentId !== void 0) return {
|
|
11
|
+
status: "blocked",
|
|
12
|
+
reason: "non-body-story"
|
|
13
|
+
};
|
|
14
|
+
return match.replacement;
|
|
15
|
+
};
|
|
3
16
|
const createDefaultFindOptions = () => ({
|
|
4
17
|
matchCase: false,
|
|
5
18
|
matchWholeWord: false,
|
|
@@ -10,7 +23,7 @@ const createSearchPattern = (searchText, options) => {
|
|
|
10
23
|
if (!searchText) return null;
|
|
11
24
|
try {
|
|
12
25
|
const source = options.useRegex ? searchText : escapeRegexString(searchText);
|
|
13
|
-
const pattern = options.matchWholeWord ? `\\b
|
|
26
|
+
const pattern = options.matchWholeWord ? `\\b(?:${source})\\b` : source;
|
|
14
27
|
return new RegExp(pattern, options.matchCase ? "gu" : "giu");
|
|
15
28
|
} catch {
|
|
16
29
|
return null;
|
|
@@ -18,98 +31,385 @@ const createSearchPattern = (searchText, options) => {
|
|
|
18
31
|
};
|
|
19
32
|
const findAllMatches = (content, searchText, options) => {
|
|
20
33
|
if (!content || !searchText) return [];
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const pattern = new RegExp(options.matchWholeWord ? `\\b${source}\\b` : source, options.matchCase ? "gu" : "giu");
|
|
34
|
+
const pattern = createSearchPattern(searchText, options);
|
|
35
|
+
if (!pattern) return [];
|
|
24
36
|
const matches = [];
|
|
25
37
|
let match;
|
|
26
38
|
while ((match = pattern.exec(content)) !== null) {
|
|
39
|
+
if (match[0].length === 0) {
|
|
40
|
+
const codePoint = content.codePointAt(pattern.lastIndex);
|
|
41
|
+
pattern.lastIndex += codePoint !== void 0 && codePoint > 65535 ? 2 : 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
27
44
|
matches.push({
|
|
28
45
|
start: match.index,
|
|
29
46
|
end: match.index + match[0].length
|
|
30
47
|
});
|
|
31
|
-
if (match[0].length === 0) pattern.lastIndex++;
|
|
32
48
|
}
|
|
33
49
|
return matches;
|
|
34
50
|
};
|
|
35
|
-
const findInDocument = (document, searchText, options) => {
|
|
51
|
+
const findInDocument = (document, searchText, options, context) => {
|
|
36
52
|
if (!document || !searchText) return [];
|
|
37
53
|
const body = document.package.document;
|
|
38
54
|
if (!isRecord(body) || !Array.isArray(body.content)) return [];
|
|
39
55
|
const matches = [];
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
for (const story of orderedSearchStories(document)) {
|
|
57
|
+
if (story !== "main" && (story.kind === "header" || story.kind === "footer") && context?.renderedStories && !context.renderedStories.some((rendered) => sameStory(rendered, story))) continue;
|
|
58
|
+
const blocks = storyBody(document, story).content;
|
|
59
|
+
if (story !== "main" && (story.kind === "footnote" || story.kind === "endnote")) {
|
|
60
|
+
const noteType = (story.kind === "footnote" ? document.package.footnotes : document.package.endnotes)?.find(({ id }) => id === story.id)?.noteType;
|
|
61
|
+
if (noteType && noteType !== "normal") continue;
|
|
62
|
+
}
|
|
63
|
+
let textBoxIndex = 0;
|
|
64
|
+
const searchBlocks = (items, shapeAddress, visible = true) => {
|
|
65
|
+
forEachParagraph(items, (paragraph, paragraphIndex, structureVisible) => {
|
|
66
|
+
const paragraphVisible = visible && structureVisible;
|
|
67
|
+
if (paragraphVisible) for (const match of findInParagraph(paragraph, searchText, options, paragraphIndex)) if (shapeAddress) matches.push({
|
|
68
|
+
...match,
|
|
69
|
+
story,
|
|
70
|
+
textBoxIndex: shapeAddress.textBoxIndex,
|
|
71
|
+
anchorParagraphIndex: shapeAddress.anchorParagraphIndex
|
|
72
|
+
});
|
|
73
|
+
else matches.push({
|
|
74
|
+
...match,
|
|
75
|
+
story
|
|
76
|
+
});
|
|
77
|
+
for (const { shape, visible: shapeVisible } of paragraphShapes(paragraph.content, options.displayMode)) {
|
|
78
|
+
if (!shape.textBody) continue;
|
|
79
|
+
searchBlocks(shape.textBody.content, {
|
|
80
|
+
textBoxIndex: textBoxIndex++,
|
|
81
|
+
anchorParagraphIndex: shapeAddress?.anchorParagraphIndex ?? paragraphIndex
|
|
82
|
+
}, paragraphVisible && shapeVisible);
|
|
83
|
+
}
|
|
84
|
+
}, options.displayMode);
|
|
85
|
+
};
|
|
86
|
+
searchBlocks(blocks);
|
|
87
|
+
}
|
|
88
|
+
if (options.includeComments) for (const comment of document.package.document.comments ?? []) forEachParagraph(comment.content, (paragraph, paragraphIndex, visible) => {
|
|
89
|
+
if (!visible) return;
|
|
90
|
+
for (const match of findInParagraph(paragraph, searchText, {
|
|
91
|
+
...options,
|
|
92
|
+
displayMode: "all-markup"
|
|
93
|
+
}, paragraphIndex)) matches.push({
|
|
94
|
+
...match,
|
|
95
|
+
story: "main",
|
|
96
|
+
commentId: comment.id
|
|
97
|
+
});
|
|
42
98
|
});
|
|
99
|
+
for (const match of matches) match.replacement = replaceEligibility(match);
|
|
43
100
|
return matches;
|
|
44
101
|
};
|
|
102
|
+
const orderedSearchStories = (document) => {
|
|
103
|
+
const stories = documentStories(document);
|
|
104
|
+
const ordered = ["main"];
|
|
105
|
+
const seen = /* @__PURE__ */ new Set();
|
|
106
|
+
for (const kind of ["header", "footer"]) {
|
|
107
|
+
const add = (rId) => {
|
|
108
|
+
const key = `${kind}:${rId}`;
|
|
109
|
+
const parts = kind === "header" ? document.package.headers : document.package.footers;
|
|
110
|
+
if (seen.has(key) || !parts?.has(rId)) return;
|
|
111
|
+
seen.add(key);
|
|
112
|
+
ordered.push({
|
|
113
|
+
kind,
|
|
114
|
+
rId
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
for (const section of document.package.document.sections ?? []) {
|
|
118
|
+
const refs = kind === "header" ? section.properties.headerReferences : section.properties.footerReferences;
|
|
119
|
+
for (const variant of [
|
|
120
|
+
"default",
|
|
121
|
+
"first",
|
|
122
|
+
"even"
|
|
123
|
+
]) for (const ref of refs ?? []) if (ref.type === variant) add(ref.rId);
|
|
124
|
+
}
|
|
125
|
+
for (const story of stories) if (story !== "main" && story.kind === kind) add(story.rId);
|
|
126
|
+
}
|
|
127
|
+
for (const story of stories) if (story !== "main" && (story.kind === "footnote" || story.kind === "endnote")) ordered.push(story);
|
|
128
|
+
return ordered;
|
|
129
|
+
};
|
|
130
|
+
const isHiddenRevision = (type, mode) => {
|
|
131
|
+
if (mode === "original") return type === "insertion" || type === "moveTo";
|
|
132
|
+
if (mode === "no-markup" || mode === "simple-markup") return type === "deletion" || type === "moveFrom";
|
|
133
|
+
return false;
|
|
134
|
+
};
|
|
135
|
+
const paragraphShapes = (content, mode) => {
|
|
136
|
+
const shapes = [];
|
|
137
|
+
const visit = (items, visible = true) => {
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const itemVisible = visible && !isHiddenRevision(item.type, mode);
|
|
140
|
+
if (item.type === "run") {
|
|
141
|
+
for (const child of item.content) if (child.type === "shape") shapes.push({
|
|
142
|
+
shape: child.shape,
|
|
143
|
+
visible: itemVisible
|
|
144
|
+
});
|
|
145
|
+
} else if (item.type === "hyperlink") visit(item.children, itemVisible);
|
|
146
|
+
else if (item.type === "complexField") visit(item.fieldResult, itemVisible);
|
|
147
|
+
else if ("content" in item) visit(item.content, itemVisible);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
visit(content);
|
|
151
|
+
return shapes;
|
|
152
|
+
};
|
|
153
|
+
/** Visible characters retain offsets in the authored paragraph, including hidden revisions. */
|
|
154
|
+
const getVisibleParagraphProjection = (paragraph, mode) => {
|
|
155
|
+
if (mode === void 0 || mode === "all-markup") return {
|
|
156
|
+
...joinSearchProjections(paragraph.content.map(getParagraphContentSearchProjection)),
|
|
157
|
+
offsets: [],
|
|
158
|
+
hiddenContentBarriers: []
|
|
159
|
+
};
|
|
160
|
+
let text = "";
|
|
161
|
+
let authoredOffset = 0;
|
|
162
|
+
const offsets = [];
|
|
163
|
+
const hiddenContentBarriers = [];
|
|
164
|
+
const replacementBarriers = [];
|
|
165
|
+
const atomicContentRanges = [];
|
|
166
|
+
const fieldTextRanges = [];
|
|
167
|
+
const excludedTextRanges = [];
|
|
168
|
+
const pageBreakOffsets = [];
|
|
169
|
+
const visit = (items) => {
|
|
170
|
+
for (const item of items) {
|
|
171
|
+
if (isHiddenRevision(item.type, mode)) {
|
|
172
|
+
hiddenContentBarriers.push(text.length);
|
|
173
|
+
authoredOffset += getParagraphContentSearchProjection(item).text.length;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (item.type === "hyperlink") {
|
|
177
|
+
visit(item.children);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (item.type === "complexField" || item.type === "simpleField") {
|
|
181
|
+
const start = text.length;
|
|
182
|
+
visit(item.type === "complexField" ? item.fieldResult : item.content);
|
|
183
|
+
fieldTextRanges.push({
|
|
184
|
+
start,
|
|
185
|
+
end: text.length
|
|
186
|
+
});
|
|
187
|
+
if (isPageDependentSearchField(item.instruction)) excludedTextRanges.push({
|
|
188
|
+
start,
|
|
189
|
+
end: text.length
|
|
190
|
+
});
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (item.type !== "run" && "content" in item) {
|
|
194
|
+
visit(item.content);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const part = getParagraphContentSearchProjection(item);
|
|
198
|
+
for (const offset of part.replacementBarriers ?? []) replacementBarriers.push(text.length + offset);
|
|
199
|
+
for (const range of part.atomicContentRanges ?? []) atomicContentRanges.push({
|
|
200
|
+
start: text.length + range.start,
|
|
201
|
+
end: text.length + range.end
|
|
202
|
+
});
|
|
203
|
+
for (const range of part.fieldTextRanges ?? []) fieldTextRanges.push({
|
|
204
|
+
start: text.length + range.start,
|
|
205
|
+
end: text.length + range.end
|
|
206
|
+
});
|
|
207
|
+
for (const range of part.excludedTextRanges ?? []) excludedTextRanges.push({
|
|
208
|
+
start: text.length + range.start,
|
|
209
|
+
end: text.length + range.end
|
|
210
|
+
});
|
|
211
|
+
for (const offset of part.pageBreakOffsets) pageBreakOffsets.push(text.length + offset);
|
|
212
|
+
for (let index = 0; index < part.text.length; index++) offsets.push(authoredOffset + index);
|
|
213
|
+
text += part.text;
|
|
214
|
+
authoredOffset += part.text.length;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
visit(paragraph.content);
|
|
218
|
+
return {
|
|
219
|
+
text,
|
|
220
|
+
offsets,
|
|
221
|
+
hiddenContentBarriers,
|
|
222
|
+
pageBreakOffsets,
|
|
223
|
+
excludedTextRanges,
|
|
224
|
+
fieldTextRanges,
|
|
225
|
+
atomicContentRanges,
|
|
226
|
+
replacementBarriers
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
/** Authored paragraph text displayed by comment UI, using the search owner. */
|
|
230
|
+
const getFindParagraphText = (paragraph) => getVisibleParagraphProjection(paragraph, void 0).text;
|
|
45
231
|
const findInParagraph = (paragraph, searchText, options, paragraphIndex) => {
|
|
46
|
-
const projection =
|
|
232
|
+
const projection = getVisibleParagraphProjection(paragraph, options.displayMode);
|
|
47
233
|
if (!projection.text) return [];
|
|
48
|
-
return findAllMatches(projection.text, searchText, options).filter(({ start, end }) => !projection.pageBreakOffsets.some((offset) => offset > start && offset < end)).map(({ start, end }) => ({
|
|
234
|
+
return findAllMatches(projection.text, searchText, options).filter(({ start, end }) => !projection.pageBreakOffsets.some((offset) => offset > start && offset < end) && !(projection.excludedTextRanges ?? []).some((range) => range.start < end && range.end > start)).map(({ start, end }) => ({
|
|
49
235
|
paragraphIndex,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
236
|
+
replacement: replacementForProjection({
|
|
237
|
+
projection,
|
|
238
|
+
start,
|
|
239
|
+
end
|
|
240
|
+
}),
|
|
241
|
+
contentIndex: findContentIndexAtOffset(paragraph, projection.offsets.at(start) ?? start),
|
|
242
|
+
startOffset: projection.offsets.at(start) ?? start,
|
|
243
|
+
endOffset: (projection.offsets.at(end - 1) ?? end - 1) + 1,
|
|
53
244
|
text: projection.text.slice(start, end)
|
|
54
245
|
}));
|
|
55
246
|
};
|
|
56
|
-
const
|
|
247
|
+
const replacementForProjection = ({ projection, start, end }) => {
|
|
248
|
+
if (projection.hiddenContentBarriers.some((offset) => offset > start && offset < end)) return {
|
|
249
|
+
status: "blocked",
|
|
250
|
+
reason: "hidden-content"
|
|
251
|
+
};
|
|
252
|
+
if ((projection.fieldTextRanges ?? []).some((range) => range.start < end && range.end > start)) return {
|
|
253
|
+
status: "blocked",
|
|
254
|
+
reason: "field-result"
|
|
255
|
+
};
|
|
256
|
+
if ((projection.atomicContentRanges ?? []).some((range) => range.start < end && range.end > start)) return {
|
|
257
|
+
status: "blocked",
|
|
258
|
+
reason: "atomic-content"
|
|
259
|
+
};
|
|
260
|
+
if ((projection.replacementBarriers ?? []).some((offset) => offset > start && offset < end)) return {
|
|
261
|
+
status: "blocked",
|
|
262
|
+
reason: "atomic-content"
|
|
263
|
+
};
|
|
264
|
+
for (let offset = start + 1; offset < end; offset++) if ((projection.offsets.at(offset) ?? offset) !== (projection.offsets.at(offset - 1) ?? offset - 1) + 1) return {
|
|
265
|
+
status: "blocked",
|
|
266
|
+
reason: "hidden-content"
|
|
267
|
+
};
|
|
268
|
+
return { status: "allowed" };
|
|
269
|
+
};
|
|
270
|
+
const forEachParagraph = (blocks, visit, displayMode) => {
|
|
57
271
|
let paragraphIndex = 0;
|
|
58
|
-
const walkBlocks = (items) => {
|
|
272
|
+
const walkBlocks = (items, visible = true) => {
|
|
59
273
|
for (const block of items) {
|
|
60
274
|
if (isParagraph(block)) {
|
|
61
|
-
visit(block, paragraphIndex);
|
|
275
|
+
visit(block, paragraphIndex, visible);
|
|
62
276
|
paragraphIndex++;
|
|
63
277
|
continue;
|
|
64
278
|
}
|
|
65
279
|
if (isTable(block)) {
|
|
66
|
-
walkTable(block);
|
|
280
|
+
walkTable(block, visible);
|
|
67
281
|
continue;
|
|
68
282
|
}
|
|
69
|
-
if (isBlockContainer(block)) walkBlocks(block.content);
|
|
283
|
+
if (isBlockContainer(block)) walkBlocks(block.content, visible);
|
|
70
284
|
}
|
|
71
285
|
};
|
|
72
|
-
const walkTable = (table) => {
|
|
286
|
+
const walkTable = (table, visible) => {
|
|
73
287
|
for (const row of table.rows) {
|
|
74
288
|
if (!isTableRow(row)) continue;
|
|
75
|
-
for (const cell of row.cells) if (isTableCell(cell)) walkBlocks(cell.content);
|
|
289
|
+
for (const cell of row.cells) if (isTableCell(cell)) walkBlocks(cell.content, visible && isVisibleStructure(row.structuralChange, displayMode) && isVisibleStructure(cell.structuralChange, displayMode));
|
|
76
290
|
}
|
|
77
291
|
};
|
|
78
292
|
walkBlocks(blocks);
|
|
79
293
|
};
|
|
294
|
+
const isVisibleStructure = (change, mode) => {
|
|
295
|
+
if (mode === void 0 || mode === "all-markup" || !change) return true;
|
|
296
|
+
let kind;
|
|
297
|
+
switch (change.type) {
|
|
298
|
+
case "tableRowInsertion":
|
|
299
|
+
case "tableCellInsertion":
|
|
300
|
+
kind = "ins";
|
|
301
|
+
break;
|
|
302
|
+
case "tableRowDeletion":
|
|
303
|
+
case "tableCellDeletion":
|
|
304
|
+
kind = "del";
|
|
305
|
+
break;
|
|
306
|
+
case "tableCellMerge":
|
|
307
|
+
kind = "merge";
|
|
308
|
+
break;
|
|
309
|
+
default: return change;
|
|
310
|
+
}
|
|
311
|
+
return isTableCellRetainedInReviewView(kind, mode === "original" ? "original" : "final");
|
|
312
|
+
};
|
|
313
|
+
const PAGE_DEPENDENT_SEARCH_FIELDS = /* @__PURE__ */ new Set([
|
|
314
|
+
"PAGE",
|
|
315
|
+
"NUMPAGES",
|
|
316
|
+
"SECTIONPAGES",
|
|
317
|
+
"PAGEREF"
|
|
318
|
+
]);
|
|
319
|
+
const isPageDependentSearchField = (instruction) => PAGE_DEPENDENT_SEARCH_FIELDS.has(parseFieldType(instruction));
|
|
80
320
|
const joinSearchProjections = (parts) => {
|
|
81
321
|
let text = "";
|
|
82
322
|
const pageBreakOffsets = [];
|
|
323
|
+
const replacementBarriers = [];
|
|
324
|
+
const atomicContentRanges = [];
|
|
325
|
+
const fieldTextRanges = [];
|
|
326
|
+
const excludedTextRanges = [];
|
|
83
327
|
for (const part of parts) {
|
|
84
328
|
const baseOffset = text.length;
|
|
85
329
|
text += part.text;
|
|
330
|
+
for (const offset of part.replacementBarriers ?? []) replacementBarriers.push(baseOffset + offset);
|
|
331
|
+
for (const range of part.atomicContentRanges ?? []) atomicContentRanges.push({
|
|
332
|
+
start: baseOffset + range.start,
|
|
333
|
+
end: baseOffset + range.end
|
|
334
|
+
});
|
|
335
|
+
for (const range of part.fieldTextRanges ?? []) fieldTextRanges.push({
|
|
336
|
+
start: baseOffset + range.start,
|
|
337
|
+
end: baseOffset + range.end
|
|
338
|
+
});
|
|
339
|
+
for (const range of part.excludedTextRanges ?? []) excludedTextRanges.push({
|
|
340
|
+
start: baseOffset + range.start,
|
|
341
|
+
end: baseOffset + range.end
|
|
342
|
+
});
|
|
86
343
|
for (const offset of part.pageBreakOffsets) pageBreakOffsets.push(baseOffset + offset);
|
|
87
344
|
}
|
|
88
345
|
return {
|
|
89
346
|
text,
|
|
90
|
-
pageBreakOffsets
|
|
347
|
+
pageBreakOffsets,
|
|
348
|
+
excludedTextRanges,
|
|
349
|
+
fieldTextRanges,
|
|
350
|
+
atomicContentRanges,
|
|
351
|
+
replacementBarriers
|
|
91
352
|
};
|
|
92
353
|
};
|
|
93
354
|
const getRunSearchProjection = (run) => {
|
|
94
355
|
let text = "";
|
|
95
356
|
const pageBreakOffsets = [];
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
357
|
+
const replacementBarriers = [];
|
|
358
|
+
const atomicContentRanges = [];
|
|
359
|
+
for (const item of run.content) switch (item.type) {
|
|
360
|
+
case "preservedXml":
|
|
361
|
+
if (!item.text) replacementBarriers.push(text.length);
|
|
362
|
+
atomicContentRanges.push({
|
|
363
|
+
start: text.length,
|
|
364
|
+
end: text.length + item.text.length
|
|
365
|
+
});
|
|
366
|
+
text += item.text;
|
|
367
|
+
break;
|
|
368
|
+
case "text":
|
|
369
|
+
text += item.text;
|
|
370
|
+
break;
|
|
371
|
+
case "symbol": {
|
|
372
|
+
const character = decodeOoxmlSymbolCharacter(item.char);
|
|
373
|
+
if (character) text += character;
|
|
374
|
+
else replacementBarriers.push(text.length);
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case "tab":
|
|
378
|
+
text += " ";
|
|
379
|
+
break;
|
|
380
|
+
case "softHyphen":
|
|
381
|
+
case "noBreakHyphen":
|
|
382
|
+
text += HYPHEN_TEXT_CARRIERS[item.type];
|
|
383
|
+
break;
|
|
384
|
+
case "break":
|
|
385
|
+
if (item.breakType === "page") pageBreakOffsets.push(text.length);
|
|
386
|
+
else text += "\n";
|
|
387
|
+
break;
|
|
388
|
+
case "footnoteRef":
|
|
389
|
+
case "endnoteRef":
|
|
390
|
+
case "drawing":
|
|
391
|
+
case "shape":
|
|
392
|
+
case "noteMarker":
|
|
393
|
+
case "fieldChar":
|
|
394
|
+
case "instrText":
|
|
395
|
+
case "renderedPageBreak":
|
|
396
|
+
replacementBarriers.push(text.length);
|
|
397
|
+
break;
|
|
398
|
+
default: panic(`Unsupported run content: ${JSON.stringify(item)}`);
|
|
399
|
+
}
|
|
100
400
|
return {
|
|
101
401
|
text,
|
|
102
|
-
pageBreakOffsets
|
|
402
|
+
pageBreakOffsets,
|
|
403
|
+
atomicContentRanges,
|
|
404
|
+
replacementBarriers
|
|
103
405
|
};
|
|
104
406
|
};
|
|
105
|
-
const getHyperlinkSearchProjection = (hyperlink) => joinSearchProjections(hyperlink.children.
|
|
106
|
-
if (child.type === "run") return [getRunSearchProjection(child)];
|
|
107
|
-
return child.type === "inlineWrapper" ? [getParagraphContentSearchProjection(child)] : [];
|
|
108
|
-
}));
|
|
407
|
+
const getHyperlinkSearchProjection = (hyperlink) => joinSearchProjections(hyperlink.children.map(getParagraphContentSearchProjection));
|
|
109
408
|
/** Nothing on the line: a marker, or markup with no text under it. */
|
|
110
409
|
const noSearchText = () => ({
|
|
111
410
|
text: "",
|
|
112
|
-
pageBreakOffsets: []
|
|
411
|
+
pageBreakOffsets: [],
|
|
412
|
+
replacementBarriers: [0]
|
|
113
413
|
});
|
|
114
414
|
/**
|
|
115
415
|
* The text one paragraph content item puts on the line, for searching.
|
|
@@ -134,13 +434,37 @@ const getParagraphContentSearchProjection = (content) => {
|
|
|
134
434
|
case "run": return getRunSearchProjection(content);
|
|
135
435
|
case "hyperlink": return getHyperlinkSearchProjection(content);
|
|
136
436
|
case "inlineSdt":
|
|
137
|
-
case "simpleField":
|
|
138
437
|
case "inlineWrapper":
|
|
139
438
|
case "insertion":
|
|
140
439
|
case "deletion":
|
|
141
440
|
case "moveFrom":
|
|
142
441
|
case "moveTo": return joinSearchProjections(content.content.map(getParagraphContentSearchProjection));
|
|
143
|
-
case "
|
|
442
|
+
case "simpleField":
|
|
443
|
+
case "complexField": {
|
|
444
|
+
const projection = joinSearchProjections(content.type === "simpleField" ? content.content.map(getParagraphContentSearchProjection) : content.fieldResult.map(getRunSearchProjection));
|
|
445
|
+
const part = {
|
|
446
|
+
...projection,
|
|
447
|
+
fieldTextRanges: [{
|
|
448
|
+
start: 0,
|
|
449
|
+
end: projection.text.length
|
|
450
|
+
}]
|
|
451
|
+
};
|
|
452
|
+
return isPageDependentSearchField(content.instruction) ? {
|
|
453
|
+
...part,
|
|
454
|
+
excludedTextRanges: [{
|
|
455
|
+
start: 0,
|
|
456
|
+
end: part.text.length
|
|
457
|
+
}]
|
|
458
|
+
} : part;
|
|
459
|
+
}
|
|
460
|
+
case "preservedInline": return {
|
|
461
|
+
text: content.text,
|
|
462
|
+
pageBreakOffsets: [],
|
|
463
|
+
atomicContentRanges: [{
|
|
464
|
+
start: 0,
|
|
465
|
+
end: content.text.length
|
|
466
|
+
}]
|
|
467
|
+
};
|
|
144
468
|
case "bookmarkStart":
|
|
145
469
|
case "bookmarkEnd":
|
|
146
470
|
case "commentRangeStart":
|
|
@@ -150,12 +474,10 @@ const getParagraphContentSearchProjection = (content) => {
|
|
|
150
474
|
case "moveFromRangeEnd":
|
|
151
475
|
case "moveToRangeStart":
|
|
152
476
|
case "moveToRangeEnd":
|
|
153
|
-
case "mathEquation":
|
|
154
|
-
case "preservedInline": return noSearchText();
|
|
477
|
+
case "mathEquation": return noSearchText();
|
|
155
478
|
default: panic(`Unsupported paragraph content: ${JSON.stringify(content)}`);
|
|
156
479
|
}
|
|
157
480
|
};
|
|
158
|
-
const getParagraphSearchProjection = (paragraph) => joinSearchProjections(paragraph.content.map(getParagraphContentSearchProjection));
|
|
159
481
|
const findContentIndexAtOffset = (paragraph, offset) => {
|
|
160
482
|
let currentOffset = 0;
|
|
161
483
|
for (let contentIndex = 0; contentIndex < paragraph.content.length; contentIndex++) {
|
|
@@ -174,4 +496,4 @@ const isTableRow = (value) => isRecord(value) && Array.isArray(value["cells"]);
|
|
|
174
496
|
const isTableCell = (value) => isRecord(value) && Array.isArray(value["content"]);
|
|
175
497
|
const isBlockContainer = (value) => isRecord(value) && (value["type"] === "blockSdt" || value["type"] === "blockCustomXml") && Array.isArray(value["content"]);
|
|
176
498
|
//#endregion
|
|
177
|
-
export { createDefaultFindOptions, createSearchPattern, escapeRegexString, findAllMatches, findInDocument, findInParagraph };
|
|
499
|
+
export { createDefaultFindOptions, createSearchPattern, escapeRegexString, findAllMatches, findInDocument, findInParagraph, getFindParagraphText, isPageDependentSearchField, replaceEligibility };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { FindOptions, FindSearchContext } from "./findReplace.js";
|
|
3
|
+
//#region src/utils/findSearchKey.d.ts
|
|
4
|
+
/** Every matching option must make an explicit decision in the search key. */
|
|
5
|
+
declare const createFindQueryKey: (query: string, options: FindOptions) => string;
|
|
6
|
+
type FindSearchKeyOptions = {
|
|
7
|
+
query: string;
|
|
8
|
+
options: FindOptions;
|
|
9
|
+
document: document_d_exports.Document;
|
|
10
|
+
/** Immutable editor document identity, changed by every body transaction. */
|
|
11
|
+
bodyVersion?: object;
|
|
12
|
+
context?: FindSearchContext;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Getters can return fresh model clones. Keep the immutable body version and
|
|
16
|
+
* snapshot package stories separately: secondary edits do not change that body.
|
|
17
|
+
* Story enumeration also binds this snapshot to the package's story resolver.
|
|
18
|
+
*/
|
|
19
|
+
declare const createFindSearchKey: ({ query, options, document, bodyVersion, context }: FindSearchKeyOptions) => {
|
|
20
|
+
query: string;
|
|
21
|
+
bodyVersion: string | object;
|
|
22
|
+
documentVersion: string;
|
|
23
|
+
storyScope: string;
|
|
24
|
+
};
|
|
25
|
+
declare const sameFindSearchKey: (previous: ReturnType<typeof createFindSearchKey> | null, current: ReturnType<typeof createFindSearchKey>) => boolean;
|
|
26
|
+
/** Secondary edits cannot acknowledge a pending body replacement. */
|
|
27
|
+
declare const sameFindBodyVersion: (previous: ReturnType<typeof createFindSearchKey>, current: ReturnType<typeof createFindSearchKey>) => boolean;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { createFindQueryKey, createFindSearchKey, sameFindBodyVersion, sameFindSearchKey };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { documentStories, storyBody } from "@stll/docx-core/ops";
|
|
2
|
+
//#region src/utils/findSearchKey.ts
|
|
3
|
+
/** Every matching option must make an explicit decision in the search key. */
|
|
4
|
+
const createFindQueryKey = (query, options) => {
|
|
5
|
+
const matchingOptions = {
|
|
6
|
+
matchCase: options.matchCase,
|
|
7
|
+
matchWholeWord: options.matchWholeWord,
|
|
8
|
+
useRegex: options.useRegex ?? false,
|
|
9
|
+
displayMode: options.displayMode ?? "all-markup",
|
|
10
|
+
includeComments: options.includeComments ?? false
|
|
11
|
+
};
|
|
12
|
+
return JSON.stringify([query, matchingOptions]);
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Getters can return fresh model clones. Keep the immutable body version and
|
|
16
|
+
* snapshot package stories separately: secondary edits do not change that body.
|
|
17
|
+
* Story enumeration also binds this snapshot to the package's story resolver.
|
|
18
|
+
*/
|
|
19
|
+
const createFindSearchKey = ({ query, options, document, bodyVersion, context }) => ({
|
|
20
|
+
query: createFindQueryKey(query, options),
|
|
21
|
+
bodyVersion: bodyVersion ?? JSON.stringify(document.package.document.content),
|
|
22
|
+
documentVersion: JSON.stringify([
|
|
23
|
+
documentStories(document).filter((story) => story !== "main").map((story) => [story, storyBody(document, story)]),
|
|
24
|
+
document.package.document.comments ?? [],
|
|
25
|
+
document.package.document.sections?.map(({ properties }) => properties) ?? []
|
|
26
|
+
]),
|
|
27
|
+
storyScope: JSON.stringify(context?.renderedStories?.map((story) => JSON.stringify(story)).toSorted() ?? null)
|
|
28
|
+
});
|
|
29
|
+
const sameFindSearchKey = (previous, current) => previous !== null && previous.query === current.query && sameFindDocumentVersion(previous, current) && previous.storyScope === current.storyScope;
|
|
30
|
+
const sameFindDocumentVersion = (previous, current) => sameFindBodyVersion(previous, current) && previous.documentVersion === current.documentVersion;
|
|
31
|
+
/** Secondary edits cannot acknowledge a pending body replacement. */
|
|
32
|
+
const sameFindBodyVersion = (previous, current) => previous.bodyVersion === current.bodyVersion;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { createFindQueryKey, createFindSearchKey, sameFindBodyVersion, sameFindSearchKey };
|