@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,3 +1,4 @@
|
|
|
1
|
+
import { isCommentRangeInertAtom } from "../../commentRangeInlineAtoms.js";
|
|
1
2
|
import { readCommentReferenceAttrs } from "../../commentReferenceAttrs.js";
|
|
2
3
|
import { planCommentReferenceRepairs } from "../../commentReferenceIntegrity.js";
|
|
3
4
|
import { readRangeAnchorAttrs } from "../../rangeAnchorAttrs.js";
|
|
@@ -10,7 +11,16 @@ const COMMENT_REFERENCE_NODE_NAME = "commentReference";
|
|
|
10
11
|
const surveyCommentReferences = (doc) => {
|
|
11
12
|
const references = [];
|
|
12
13
|
const markEnds = /* @__PURE__ */ new Map();
|
|
14
|
+
const unanchoredMarks = [];
|
|
13
15
|
doc.descendants((node, position) => {
|
|
16
|
+
const inert = isCommentRangeInertAtom(node);
|
|
17
|
+
if (inert) {
|
|
18
|
+
for (const mark of node.marks) if (mark.type.name === "comment") unanchoredMarks.push({
|
|
19
|
+
position,
|
|
20
|
+
size: node.nodeSize,
|
|
21
|
+
mark
|
|
22
|
+
});
|
|
23
|
+
}
|
|
14
24
|
if (node.type.name === "commentReference") {
|
|
15
25
|
const attrs = readCommentReferenceAttrs(node);
|
|
16
26
|
references.push(attrs.ok ? {
|
|
@@ -33,6 +43,7 @@ const surveyCommentReferences = (doc) => {
|
|
|
33
43
|
}
|
|
34
44
|
return false;
|
|
35
45
|
}
|
|
46
|
+
if (inert) return false;
|
|
36
47
|
if (!node.isInline) return true;
|
|
37
48
|
for (const mark of node.marks) {
|
|
38
49
|
if (mark.type.name !== "comment") continue;
|
|
@@ -44,6 +55,7 @@ const surveyCommentReferences = (doc) => {
|
|
|
44
55
|
});
|
|
45
56
|
return {
|
|
46
57
|
references,
|
|
58
|
+
unanchoredMarks,
|
|
47
59
|
markExtents: [...markEnds].map(([commentId, end]) => ({
|
|
48
60
|
commentId,
|
|
49
61
|
end
|
|
@@ -84,9 +96,11 @@ const CommentReferenceExtension = createNodeExtension({
|
|
|
84
96
|
if (!transactions.some(({ docChanged }) => docChanged)) return null;
|
|
85
97
|
const referenceType = newState.schema.nodes[COMMENT_REFERENCE_NODE_NAME];
|
|
86
98
|
if (!referenceType) return null;
|
|
87
|
-
const
|
|
88
|
-
|
|
99
|
+
const survey = surveyCommentReferences(newState.doc);
|
|
100
|
+
const repairs = planCommentReferenceRepairs(survey);
|
|
101
|
+
if (repairs.length === 0 && survey.unanchoredMarks.length === 0) return null;
|
|
89
102
|
const transaction = newState.tr;
|
|
103
|
+
for (const { position, size, mark } of survey.unanchoredMarks) transaction.removeMark(position, position + size, mark);
|
|
90
104
|
for (const repair of repairs) {
|
|
91
105
|
if (repair.type === "delete") {
|
|
92
106
|
transaction.delete(repair.position, repair.position + repair.size);
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import "./extensions/marks/InlineWrapperExtension.js";
|
|
2
|
+
import { getInlineDisplayText } from "./inlineDisplayText.js";
|
|
2
3
|
//#region src/prosemirror/fieldRepresentation.ts
|
|
3
4
|
/** Current children alone decide whether a field needs editable structural content. */
|
|
4
5
|
const fieldRequiresStructuredContent = (children) => children.some((child) => !child.isText || child.marks.some((mark) => mark.type.name === "hyperlink" || mark.type.name === "inlineWrapper" || mark.type.name === "insertion" || mark.type.name === "deletion"));
|
|
5
6
|
/** Resolution returns text-only fields to the same atom representation as initial conversion. */
|
|
6
7
|
const canonicalFieldNode = (node) => {
|
|
7
8
|
if (node.type.name !== "structuredField") return node;
|
|
9
|
+
const displayText = getInlineDisplayText(node);
|
|
8
10
|
const attrs = {
|
|
9
11
|
...node.attrs,
|
|
10
|
-
displayText
|
|
12
|
+
displayText
|
|
11
13
|
};
|
|
12
|
-
if (fieldRequiresStructuredContent(node.content.content)) return node.attrs["displayText"] ===
|
|
14
|
+
if (fieldRequiresStructuredContent(node.content.content)) return node.attrs["displayText"] === displayText ? node : node.type.create(attrs, node.content, node.marks);
|
|
13
15
|
let marks = node.marks;
|
|
14
16
|
for (const mark of node.firstChild?.marks ?? []) marks = mark.addToSet(marks);
|
|
15
17
|
return node.type.schema.node("field", attrs, void 0, marks);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { DisplayMode } from "../managers/EditorModeManager.js";
|
|
3
|
+
import { FindMatch, FindOptions, FindSearchContext } from "../utils/findReplace.js";
|
|
2
4
|
import { Node } from "prosemirror-model";
|
|
5
|
+
import { EditorState } from "prosemirror-state";
|
|
3
6
|
//#region src/prosemirror/findReplaceSelection.d.ts
|
|
4
7
|
/**
|
|
5
8
|
* Positional fields of a document search match that the range resolver needs.
|
|
@@ -20,11 +23,54 @@ type FindMatchPosition = {
|
|
|
20
23
|
type FindMatchRange = {
|
|
21
24
|
from: number;
|
|
22
25
|
to: number;
|
|
26
|
+
/** Navigation anchors inside a logical match are excluded from decorations. */
|
|
27
|
+
excludedRanges?: {
|
|
28
|
+
from: number;
|
|
29
|
+
to: number;
|
|
30
|
+
}[];
|
|
31
|
+
/** Character subranges inside source atoms, whose text exceeds their PM size. */
|
|
32
|
+
atomicTextRanges?: {
|
|
33
|
+
from: number;
|
|
34
|
+
to: number;
|
|
35
|
+
startOffset: number;
|
|
36
|
+
endOffset: number;
|
|
37
|
+
}[];
|
|
23
38
|
};
|
|
24
39
|
type ProseMirrorFindMatch = FindMatchPosition & FindMatchRange & {
|
|
25
40
|
text: string;
|
|
26
41
|
};
|
|
27
42
|
declare function resolveFindMatchRange(doc: Node, match: FindMatchPosition): FindMatchRange | null;
|
|
43
|
+
/** Resolve a result set in one paragraph traversal, preserving result order. */
|
|
44
|
+
declare const resolveFindMatchRanges: (doc: Node, matches: readonly FindMatchPosition[]) => (FindMatchRange | null)[];
|
|
28
45
|
declare function findInProseMirrorDocument(doc: Node, searchText: string, options: FindOptions): ProseMirrorFindMatch[];
|
|
46
|
+
type FindInMarkupViewOptions = {
|
|
47
|
+
state: EditorState;
|
|
48
|
+
markupView: DisplayMode;
|
|
49
|
+
searchText: string;
|
|
50
|
+
options: FindOptions;
|
|
51
|
+
};
|
|
52
|
+
/** Search the displayed story; returned ranges address the authored editor document. */
|
|
53
|
+
declare const findInMarkupView: ({ state, markupView, searchText, options }: FindInMarkupViewOptions) => ProseMirrorFindMatch[];
|
|
54
|
+
type FindInDisplayedDocumentOptions = {
|
|
55
|
+
document: document_d_exports.Document;
|
|
56
|
+
/**
|
|
57
|
+
* Live body editor state. A review view reads its body from this state's
|
|
58
|
+
* projection, so null defers that search until the body editor mounts.
|
|
59
|
+
* Hosts without an editor state omit it and search the package model.
|
|
60
|
+
*/
|
|
61
|
+
state?: EditorState | null | undefined;
|
|
62
|
+
searchText: string;
|
|
63
|
+
options: FindOptions;
|
|
64
|
+
context?: FindSearchContext | undefined;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Search every story a display mode shows. All Markup searches the package
|
|
68
|
+
* stories. A resolved review view searches its body through the projection the
|
|
69
|
+
* layout paints, so body ranges address the authored editor document; anchored
|
|
70
|
+
* shapes, headers, footers, notes and comments keep the package search owner
|
|
71
|
+
* with the same display mode. Returns null while a review view has no body
|
|
72
|
+
* editor state to project.
|
|
73
|
+
*/
|
|
74
|
+
declare const findInDisplayedDocument: ({ document, state, searchText, options, context }: FindInDisplayedDocumentOptions) => FindMatch[] | null;
|
|
29
75
|
//#endregion
|
|
30
|
-
export { FindMatchPosition, FindMatchRange, ProseMirrorFindMatch, findInProseMirrorDocument, resolveFindMatchRange };
|
|
76
|
+
export { FindMatchPosition, FindMatchRange, ProseMirrorFindMatch, findInDisplayedDocument, findInMarkupView, findInProseMirrorDocument, resolveFindMatchRange, resolveFindMatchRanges };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { findAllMatches } from "../utils/findReplace.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { findAllMatches, findInDocument, isPageDependentSearchField } from "../utils/findReplace.js";
|
|
2
|
+
import { expectFieldAttrs } from "./attrs/index.js";
|
|
3
|
+
import { getInlineDisplayText, getInlineDisplayToken, isNoteReferenceText } from "./inlineDisplayText.js";
|
|
4
|
+
import { projectMarkupView } from "./markupViewProjection.js";
|
|
4
5
|
//#region src/prosemirror/findReplaceSelection.ts
|
|
5
6
|
function resolveFindMatchRange(doc, match) {
|
|
6
7
|
let resolved = null;
|
|
@@ -16,11 +17,29 @@ function resolveFindMatchRange(doc, match) {
|
|
|
16
17
|
});
|
|
17
18
|
return resolved;
|
|
18
19
|
}
|
|
20
|
+
/** Resolve a result set in one paragraph traversal, preserving result order. */
|
|
21
|
+
const resolveFindMatchRanges = (doc, matches) => {
|
|
22
|
+
const paragraphs = /* @__PURE__ */ new Map();
|
|
23
|
+
forEachSearchParagraph(doc, (paragraph, paragraphPos, paragraphIndex) => {
|
|
24
|
+
paragraphs.set(paragraphIndex, {
|
|
25
|
+
paragraph,
|
|
26
|
+
paragraphPos
|
|
27
|
+
});
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
return matches.map((match) => {
|
|
31
|
+
const paragraph = paragraphs.get(match.paragraphIndex);
|
|
32
|
+
return paragraph ? resolveTextRangeInParagraph({
|
|
33
|
+
...paragraph,
|
|
34
|
+
...match
|
|
35
|
+
}) : null;
|
|
36
|
+
});
|
|
37
|
+
};
|
|
19
38
|
function findInProseMirrorDocument(doc, searchText, options) {
|
|
20
39
|
if (!searchText) return [];
|
|
21
40
|
const matches = [];
|
|
22
41
|
forEachSearchParagraph(doc, (paragraph, paragraphPos, paragraphIndex) => {
|
|
23
|
-
const text =
|
|
42
|
+
const text = getInlineDisplayText(paragraph);
|
|
24
43
|
for (const { start, end } of findAllMatches(text, searchText, options)) {
|
|
25
44
|
const range = resolveTextRangeInParagraph({
|
|
26
45
|
paragraph,
|
|
@@ -40,6 +59,100 @@ function findInProseMirrorDocument(doc, searchText, options) {
|
|
|
40
59
|
});
|
|
41
60
|
return matches;
|
|
42
61
|
}
|
|
62
|
+
/** Search the displayed story; returned ranges address the authored editor document. */
|
|
63
|
+
const findInMarkupView = ({ state, markupView, searchText, options }) => {
|
|
64
|
+
const projection = projectMarkupView(state, markupView);
|
|
65
|
+
if (projection.type === "authored") return findInProseMirrorDocument(state.doc, searchText, options);
|
|
66
|
+
return findInProseMirrorDocument(projection.doc, searchText, options).map((match) => toAuthoredFindMatch(projection, match));
|
|
67
|
+
};
|
|
68
|
+
/** Every view-document position a match carries, mapped to the authored editor document. */
|
|
69
|
+
const toAuthoredFindMatch = (projection, match) => {
|
|
70
|
+
const toEditorRange = ({ from, to }) => ({
|
|
71
|
+
from: projection.toEditor(from, 1),
|
|
72
|
+
to: projection.toEditor(to, -1)
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
paragraphIndex: match.paragraphIndex,
|
|
76
|
+
startOffset: match.startOffset,
|
|
77
|
+
endOffset: match.endOffset,
|
|
78
|
+
text: match.text,
|
|
79
|
+
...toEditorRange(match),
|
|
80
|
+
...match.excludedRanges && { excludedRanges: match.excludedRanges.map(toEditorRange) },
|
|
81
|
+
...match.atomicTextRanges && { atomicTextRanges: match.atomicTextRanges.map((atom) => ({
|
|
82
|
+
...toEditorRange(atom),
|
|
83
|
+
startOffset: atom.startOffset,
|
|
84
|
+
endOffset: atom.endOffset
|
|
85
|
+
})) }
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
/** Replacement policy for a projected body match, in the shared eligibility vocabulary. */
|
|
89
|
+
const projectedReplacement = (state, viewMatch, authored) => {
|
|
90
|
+
const atom = authored.atomicTextRanges?.at(0);
|
|
91
|
+
if (atom) return {
|
|
92
|
+
status: "blocked",
|
|
93
|
+
reason: state.doc.nodeAt(atom.from)?.type.name === "field" ? "field-result" : "atomic-content"
|
|
94
|
+
};
|
|
95
|
+
return authored.to - authored.from === viewMatch.to - viewMatch.from ? { status: "allowed" } : {
|
|
96
|
+
status: "blocked",
|
|
97
|
+
reason: "hidden-content"
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Search every story a display mode shows. All Markup searches the package
|
|
102
|
+
* stories. A resolved review view searches its body through the projection the
|
|
103
|
+
* layout paints, so body ranges address the authored editor document; anchored
|
|
104
|
+
* shapes, headers, footers, notes and comments keep the package search owner
|
|
105
|
+
* with the same display mode. Returns null while a review view has no body
|
|
106
|
+
* editor state to project.
|
|
107
|
+
*/
|
|
108
|
+
const findInDisplayedDocument = ({ document, state, searchText, options, context }) => {
|
|
109
|
+
const displayMode = options.displayMode ?? "all-markup";
|
|
110
|
+
if (displayMode === "all-markup" || state === void 0) return findInDocument(document, searchText, options, context);
|
|
111
|
+
if (state === null) return null;
|
|
112
|
+
const packageMatches = findInDocument(document, searchText, options, context);
|
|
113
|
+
const projection = projectMarkupView(state, displayMode);
|
|
114
|
+
if (projection.type === "authored" || !searchText) return packageMatches;
|
|
115
|
+
const bodyMatches = findInProseMirrorDocument(projection.doc, searchText, options).map((viewMatch) => {
|
|
116
|
+
const authored = toAuthoredFindMatch(projection, viewMatch);
|
|
117
|
+
const { from, to, excludedRanges, atomicTextRanges } = authored;
|
|
118
|
+
return {
|
|
119
|
+
paragraphIndex: authored.paragraphIndex,
|
|
120
|
+
contentIndex: 0,
|
|
121
|
+
startOffset: authored.startOffset,
|
|
122
|
+
endOffset: authored.endOffset,
|
|
123
|
+
text: authored.text,
|
|
124
|
+
story: "main",
|
|
125
|
+
replacement: projectedReplacement(state, viewMatch, authored),
|
|
126
|
+
authoredRange: {
|
|
127
|
+
from,
|
|
128
|
+
to,
|
|
129
|
+
...excludedRanges && { excludedRanges },
|
|
130
|
+
...atomicTextRanges && { atomicTextRanges }
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
const anchorEnds = [];
|
|
135
|
+
forEachSearchParagraph(state.doc, (paragraph, paragraphPos, paragraphIndex) => {
|
|
136
|
+
anchorEnds[paragraphIndex] = paragraphPos + paragraph.nodeSize;
|
|
137
|
+
return true;
|
|
138
|
+
});
|
|
139
|
+
const bodyShapeMatches = packageMatches.filter((match) => match.story === "main" && match.textBoxIndex !== void 0);
|
|
140
|
+
const orderedBody = [];
|
|
141
|
+
let shapeIndex = 0;
|
|
142
|
+
const shapeAnchorEnd = (match) => match.anchorParagraphIndex !== void 0 && anchorEnds[match.anchorParagraphIndex] || Number.POSITIVE_INFINITY;
|
|
143
|
+
for (const bodyMatch of bodyMatches) {
|
|
144
|
+
const from = bodyMatch.authoredRange?.from ?? 0;
|
|
145
|
+
while (shapeIndex < bodyShapeMatches.length) {
|
|
146
|
+
const shapeMatch = bodyShapeMatches[shapeIndex];
|
|
147
|
+
if (!shapeMatch || shapeAnchorEnd(shapeMatch) > from) break;
|
|
148
|
+
orderedBody.push(shapeMatch);
|
|
149
|
+
shapeIndex++;
|
|
150
|
+
}
|
|
151
|
+
orderedBody.push(bodyMatch);
|
|
152
|
+
}
|
|
153
|
+
orderedBody.push(...bodyShapeMatches.slice(shapeIndex));
|
|
154
|
+
return [...orderedBody, ...packageMatches.filter((match) => match.story !== "main" || match.textBoxIndex === void 0 && match.commentId !== void 0)];
|
|
155
|
+
};
|
|
43
156
|
function forEachSearchParagraph(doc, visit) {
|
|
44
157
|
let paragraphIndex = 0;
|
|
45
158
|
const walkBlocks = (container, contentStart) => {
|
|
@@ -89,54 +202,56 @@ function resolveTextRangeInParagraph({ paragraph, paragraphPos, startOffset, end
|
|
|
89
202
|
to: null
|
|
90
203
|
};
|
|
91
204
|
const pageBreakOffsets = [];
|
|
205
|
+
const excludedTextRanges = [];
|
|
206
|
+
const excludedRanges = [];
|
|
207
|
+
const atomicTextRanges = [];
|
|
92
208
|
paragraph.descendants((node, pos) => {
|
|
209
|
+
if ((node.type.name === "field" || node.type.name === "structuredField") && isPageDependentSearchField(expectFieldAttrs(node).instruction)) {
|
|
210
|
+
const length = node.type.name === "field" ? expectFieldAttrs(node).displayText.length : getInlineDisplayText(node).length;
|
|
211
|
+
excludedTextRanges.push({
|
|
212
|
+
start: textOffset,
|
|
213
|
+
end: textOffset + length
|
|
214
|
+
});
|
|
215
|
+
}
|
|
93
216
|
if (node.type.name === "pageBreakRun") {
|
|
94
217
|
pageBreakOffsets.push(textOffset);
|
|
95
218
|
return false;
|
|
96
219
|
}
|
|
220
|
+
if (isNoteReferenceText(node)) {
|
|
221
|
+
const from = paragraphPos + 1 + pos;
|
|
222
|
+
excludedRanges.push({
|
|
223
|
+
from,
|
|
224
|
+
to: from + node.nodeSize
|
|
225
|
+
});
|
|
226
|
+
}
|
|
97
227
|
const tokenLength = getSearchTextTokenLength(node);
|
|
98
228
|
if (tokenLength === 0) return true;
|
|
99
229
|
const textStart = textOffset;
|
|
100
230
|
const textEnd = textStart + tokenLength;
|
|
101
231
|
const nodeStart = paragraphPos + 1 + pos;
|
|
102
|
-
|
|
103
|
-
if (
|
|
232
|
+
const atomic = node.type.name === "field" || node.type.name === "preservedXml";
|
|
233
|
+
if (atomic && textStart < endOffset && textEnd > startOffset) atomicTextRanges.push({
|
|
234
|
+
from: nodeStart,
|
|
235
|
+
to: nodeStart + node.nodeSize,
|
|
236
|
+
startOffset: Math.max(0, startOffset - textStart),
|
|
237
|
+
endOffset: Math.min(tokenLength, endOffset - textStart)
|
|
238
|
+
});
|
|
239
|
+
if (range.from === null && startOffset >= textStart && startOffset < textEnd) range.from = atomic ? nodeStart : nodeStart + Math.min(startOffset - textStart, node.nodeSize);
|
|
240
|
+
if (range.to === null && endOffset > textStart && endOffset <= textEnd) range.to = atomic ? nodeStart + node.nodeSize : nodeStart + Math.min(endOffset - textStart, node.nodeSize);
|
|
104
241
|
textOffset = textEnd;
|
|
105
242
|
return range.to === null;
|
|
106
243
|
});
|
|
107
|
-
if (range.from === null || range.to === null || range.from >= range.to || pageBreakOffsets.some((offset) => offset > startOffset && offset < endOffset)) return null;
|
|
244
|
+
if (range.from === null || range.to === null || range.from >= range.to || pageBreakOffsets.some((offset) => offset > startOffset && offset < endOffset) || excludedTextRanges.some(({ start, end }) => start < endOffset && end > startOffset)) return null;
|
|
245
|
+
const from = range.from;
|
|
246
|
+
const to = range.to;
|
|
247
|
+
const visibleExcludedRanges = excludedRanges.filter((anchor) => anchor.from < to && anchor.to > from);
|
|
108
248
|
return {
|
|
109
|
-
from
|
|
110
|
-
to
|
|
249
|
+
from,
|
|
250
|
+
to,
|
|
251
|
+
...atomicTextRanges.length > 0 && { atomicTextRanges },
|
|
252
|
+
...visibleExcludedRanges.length > 0 && { excludedRanges: visibleExcludedRanges }
|
|
111
253
|
};
|
|
112
254
|
}
|
|
113
|
-
|
|
114
|
-
if (node.isText) return node.text?.length ?? 0;
|
|
115
|
-
if (node.type.name === "tab" || node.type.name === "hardBreak" || node.type.name === "symbol") return 1;
|
|
116
|
-
return 0;
|
|
117
|
-
}
|
|
118
|
-
function getSearchableParagraphText(paragraph) {
|
|
119
|
-
let text = "";
|
|
120
|
-
paragraph.descendants((node) => {
|
|
121
|
-
if (node.isText) {
|
|
122
|
-
text += node.text ?? "";
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
if (node.type.name === "tab") {
|
|
126
|
-
text += " ";
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
if (node.type.name === "hardBreak") {
|
|
130
|
-
text += "\n";
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
if (node.type.name === "symbol") {
|
|
134
|
-
text += decodeOoxmlSymbolCharacter(expectSymbolAttrs(node).char) ?? "";
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
return true;
|
|
138
|
-
});
|
|
139
|
-
return text;
|
|
140
|
-
}
|
|
255
|
+
const getSearchTextTokenLength = (node) => getInlineDisplayToken(node)?.length ?? 0;
|
|
141
256
|
//#endregion
|
|
142
|
-
export { findInProseMirrorDocument, resolveFindMatchRange };
|
|
257
|
+
export { findInDisplayedDocument, findInMarkupView, findInProseMirrorDocument, resolveFindMatchRange, resolveFindMatchRanges };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/prosemirror/inlineDisplayText.d.ts
|
|
3
|
+
/** Native note-reference labels are navigation anchors, outside searchable text. */
|
|
4
|
+
declare const isNoteReferenceText: (node: Node) => boolean;
|
|
5
|
+
/** A leaf's logical text; null means a transparent container to descend into. */
|
|
6
|
+
declare const getInlineDisplayToken: (node: Node) => string | null;
|
|
7
|
+
/** Visible inline text, including cached source atoms and native run tokens. */
|
|
8
|
+
declare const getInlineDisplayText: (paragraph: Node) => string;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getInlineDisplayText, getInlineDisplayToken, isNoteReferenceText };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
|
|
2
|
+
import { expectFieldAttrs, expectPreservedXmlAttrs, expectSymbolAttrs } from "./attrs/index.js";
|
|
3
|
+
//#region src/prosemirror/inlineDisplayText.ts
|
|
4
|
+
/** Native note-reference labels are navigation anchors, outside searchable text. */
|
|
5
|
+
const isNoteReferenceText = (node) => node.isText && node.marks.some((mark) => mark.type.name === "footnoteRef");
|
|
6
|
+
/** A leaf's logical text; null means a transparent container to descend into. */
|
|
7
|
+
const getInlineDisplayToken = (node) => {
|
|
8
|
+
if (isNoteReferenceText(node)) return "";
|
|
9
|
+
if (node.type.name === "field") return expectFieldAttrs(node).displayText;
|
|
10
|
+
if (node.type.name === "preservedXml") return expectPreservedXmlAttrs(node).text;
|
|
11
|
+
if (node.isText) return node.text ?? "";
|
|
12
|
+
if (node.type.name === "tab") return " ";
|
|
13
|
+
if (node.type.name === "hardBreak") return "\n";
|
|
14
|
+
if (node.type.name === "symbol") return decodeOoxmlSymbolCharacter(expectSymbolAttrs(node).char) ?? "";
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
/** Visible inline text, including cached source atoms and native run tokens. */
|
|
18
|
+
const getInlineDisplayText = (paragraph) => {
|
|
19
|
+
let text = "";
|
|
20
|
+
paragraph.descendants((node) => {
|
|
21
|
+
const token = getInlineDisplayToken(node);
|
|
22
|
+
if (token === null) return true;
|
|
23
|
+
text += token;
|
|
24
|
+
return false;
|
|
25
|
+
});
|
|
26
|
+
return text;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { getInlineDisplayText, getInlineDisplayToken, isNoteReferenceText };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
1
2
|
import { DisplayMode } from "../managers/EditorModeManager.js";
|
|
2
3
|
import { RemovedSectionReference } from "../internal/sectionEndpointResolution.js";
|
|
3
4
|
import { indexedPositionMap } from "../internal/indexedPositionMap.js";
|
|
@@ -93,5 +94,13 @@ declare const projectMarkupView: (state: EditorState, view: DisplayMode) => Mark
|
|
|
93
94
|
* of text it cannot enter.
|
|
94
95
|
*/
|
|
95
96
|
declare const visibleCaretPosition: (projection: MarkupViewProjection, position: number) => number;
|
|
97
|
+
type ProjectStoryMarkupViewOptions = {
|
|
98
|
+
doc: Node;
|
|
99
|
+
markupView: DisplayMode;
|
|
100
|
+
styles?: document_d_exports.StyleDefinitions | null | undefined;
|
|
101
|
+
numbering?: document_d_exports.NumberingDefinitions | undefined;
|
|
102
|
+
};
|
|
103
|
+
/** Detached stories use the same state-aware projection owner as the body. */
|
|
104
|
+
declare const projectStoryMarkupView: ({ doc, markupView, styles, numbering }: ProjectStoryMarkupViewOptions) => MarkupViewProjection;
|
|
96
105
|
//#endregion
|
|
97
|
-
export { MarkupViewProjection, markupViewResolutionMode, projectMarkupView, resolveStateStory, visibleCaretPosition };
|
|
106
|
+
export { MarkupViewProjection, markupViewResolutionMode, projectMarkupView, projectStoryMarkupView, resolveStateStory, visibleCaretPosition };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { indexedPositionMap } from "../internal/indexedPositionMap.js";
|
|
2
2
|
import { finalRevisionParagraphRanges } from "../internal/revisionResolutionTracking.js";
|
|
3
3
|
import { resolveWholeStory } from "../internal/wholeStoryRevisionResolution.js";
|
|
4
|
-
import { getDocumentNumbering } from "./plugins/documentNumbering.js";
|
|
4
|
+
import { createDocumentNumberingPlugin, getDocumentNumbering } from "./plugins/documentNumbering.js";
|
|
5
5
|
import { getDocumentStyleResolver } from "./plugins/documentStyleState.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createDocumentStyleContextPlugin } from "./plugins/documentStyles.js";
|
|
7
|
+
import { EditorState, Selection } from "prosemirror-state";
|
|
7
8
|
//#region src/prosemirror/markupViewProjection.ts
|
|
8
9
|
/** Resolve every revision in the state's story with the state's own styles and numbering. */
|
|
9
10
|
const resolveStateStory = (state, mode) => resolveWholeStory({
|
|
@@ -84,5 +85,22 @@ const visibleCaretPosition = (projection, position) => {
|
|
|
84
85
|
const near = Selection.near(projection.doc.resolve(bounded), -1);
|
|
85
86
|
return projection.toEditor(near.head, -1);
|
|
86
87
|
};
|
|
88
|
+
const storyProjectionStates = /* @__PURE__ */ new WeakMap();
|
|
89
|
+
/** Detached stories use the same state-aware projection owner as the body. */
|
|
90
|
+
const projectStoryMarkupView = ({ doc, markupView, styles, numbering }) => {
|
|
91
|
+
if (markupView === "all-markup") return AUTHORED;
|
|
92
|
+
const cached = storyProjectionStates.get(doc);
|
|
93
|
+
if (cached && cached.styles === styles && cached.numbering === numbering) return projectMarkupView(cached.state, markupView);
|
|
94
|
+
const state = EditorState.create({
|
|
95
|
+
doc,
|
|
96
|
+
plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
|
|
97
|
+
});
|
|
98
|
+
storyProjectionStates.set(doc, {
|
|
99
|
+
styles,
|
|
100
|
+
numbering,
|
|
101
|
+
state
|
|
102
|
+
});
|
|
103
|
+
return projectMarkupView(state, markupView);
|
|
104
|
+
};
|
|
87
105
|
//#endregion
|
|
88
|
-
export { markupViewResolutionMode, projectMarkupView, resolveStateStory, visibleCaretPosition };
|
|
106
|
+
export { markupViewResolutionMode, projectMarkupView, projectStoryMarkupView, resolveStateStory, visibleCaretPosition };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sliceText } from "../../utils/sliceText.js";
|
|
1
2
|
import { expectParagraphAttrs } from "../attrs/index.js";
|
|
2
3
|
import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "../numberingAttr.js";
|
|
3
4
|
import { createPluginKeyOwner } from "../pluginKeyOwner.js";
|
|
@@ -30,7 +31,11 @@ const MAX_REPORTED_STEPS_LENGTH = 4e3;
|
|
|
30
31
|
const describeSteps = (transactions) => {
|
|
31
32
|
const steps = transactions.flatMap((transaction) => transaction.steps.map((step) => step.toJSON()));
|
|
32
33
|
const json = JSON.stringify(steps);
|
|
33
|
-
return json.length > MAX_REPORTED_STEPS_LENGTH ? `${
|
|
34
|
+
return json.length > MAX_REPORTED_STEPS_LENGTH ? `${sliceText({
|
|
35
|
+
text: json,
|
|
36
|
+
units: "codeUnits",
|
|
37
|
+
end: MAX_REPORTED_STEPS_LENGTH
|
|
38
|
+
})}… (${json.length} characters)` : json;
|
|
34
39
|
};
|
|
35
40
|
var TransactionInvariantError = class extends Error {
|
|
36
41
|
issues;
|
|
@@ -19,6 +19,20 @@ declare const NON_INCLUSIVE_MARK_DISPOSITION: {
|
|
|
19
19
|
};
|
|
20
20
|
/** The mark names in the schema that a replacement must not silently drop. */
|
|
21
21
|
declare const nonInclusiveMarkNames: (schema: Schema) => string[];
|
|
22
|
+
/** One typed point for a comment whose aligned coverage has no content left. */
|
|
23
|
+
declare const commentPoint: (schema: Schema, id: number) => Node;
|
|
24
|
+
type ResolvedCommentPointsOptions = {
|
|
25
|
+
doc: Node;
|
|
26
|
+
removes: (node: Node, position: number) => boolean;
|
|
27
|
+
retiresStructure: (node: Node, position: number) => boolean;
|
|
28
|
+
};
|
|
29
|
+
/** The same collector serves standalone commands and the existing bulk walk. */
|
|
30
|
+
declare const createResolvedCommentPointCollector: ({ removes, retiresStructure }: Omit<ResolvedCommentPointsOptions, "doc">) => {
|
|
31
|
+
visit: (node: Node, position: number, previous?: Node) => boolean;
|
|
32
|
+
finish: () => Map<number, number>;
|
|
33
|
+
};
|
|
34
|
+
/** Point fallback excludes comments belonging to retired blocks and table cells. */
|
|
35
|
+
declare const resolvedCommentPoints: ({ doc, removes, retiresStructure }: ResolvedCommentPointsOptions) => Map<number, number>;
|
|
22
36
|
type ReplacedAnnotations = {
|
|
23
37
|
/** Marks the replacement must carry, in document order. */
|
|
24
38
|
carried: readonly Mark[];
|
|
@@ -88,4 +102,4 @@ declare const annotatedReplacement: ({ annotations: { carried, leading, trailing
|
|
|
88
102
|
*/
|
|
89
103
|
declare const inheritedReplacementMarks: (doc: Node, from: number, to: number) => readonly Mark[];
|
|
90
104
|
//#endregion
|
|
91
|
-
export { NON_INCLUSIVE_MARK_DISPOSITION, ReplacedAnnotations, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, surveyReplacedAnnotations };
|
|
105
|
+
export { NON_INCLUSIVE_MARK_DISPOSITION, ReplacedAnnotations, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyReplacedAnnotations };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { expectParagraphAttrs, expectTableCellAttrs, expectTableRowAttrs } from "./attrs/index.js";
|
|
1
2
|
import { BOOKMARK_BOUNDARY_NODE_NAME } from "./extensions/nodes/BookmarkBoundaryExtension.js";
|
|
2
3
|
import { COMMENT_REFERENCE_NODE_NAME } from "./extensions/nodes/CommentReferenceExtension.js";
|
|
3
4
|
import { MOVE_RANGE_BOUNDARY_NODE_NAME } from "./extensions/nodes/MoveRangeBoundaryExtension.js";
|
|
4
5
|
import { RANGE_ANCHOR_NODE_NAME } from "./extensions/nodes/RangeAnchorExtension.js";
|
|
5
6
|
import { TEXT_BOX_ANCHOR_NODE_NAME } from "./extensions/nodes/TextBoxAnchorExtension.js";
|
|
6
7
|
import { readMoveRangeBoundaryAttrs } from "./moveRangeBoundaryAttrs.js";
|
|
8
|
+
import { readRangeAnchorAttrs } from "./rangeAnchorAttrs.js";
|
|
7
9
|
import { isZeroWidthAnchor } from "./zeroWidthAnchors.js";
|
|
8
10
|
import { Fragment, Mark } from "prosemirror-model";
|
|
9
11
|
//#region src/prosemirror/replacedAnnotations.ts
|
|
@@ -21,15 +23,12 @@ import { Fragment, Mark } from "prosemirror-model";
|
|
|
21
23
|
* hyperlink simply stops being a link. Zero-width anchors fare worse still: a
|
|
22
24
|
* plain replace deletes them outright.
|
|
23
25
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* character it covered is gone. One rule per marker kind, and that is the
|
|
29
|
-
* whole contract:
|
|
26
|
+
* Text replacement preserves a comment's aligned coverage. Unchanged text
|
|
27
|
+
* keeps its ranges; inserted text takes the comments of the replaced aligned
|
|
28
|
+
* span. A range with no remaining content becomes a point at the replacement
|
|
29
|
+
* start. The applier decides alignment once for direct and tracked edits.
|
|
30
30
|
*
|
|
31
|
-
* - `comment` mark:
|
|
32
|
-
* a range that started inside it starts at the replacement instead.
|
|
31
|
+
* - `comment` mark: carried over its aligned replacement span.
|
|
33
32
|
* - `hyperlink` mark: the replacement carries the first link the span held, so
|
|
34
33
|
* replacing a link's display text leaves the link on the new text. A span
|
|
35
34
|
* crossing two links keeps the one it opens in, because the mark excludes
|
|
@@ -66,9 +65,8 @@ import { Fragment, Mark } from "prosemirror-model";
|
|
|
66
65
|
* - every other mark: formatting, and left to boundary inheritance, which
|
|
67
66
|
* already gives the replacement the formatting of the text it replaces.
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* the comment: the one case where a range is meant to disappear.
|
|
68
|
+
* Removing the owning block removes its comment. Replacing its last covered
|
|
69
|
+
* text within a surviving block retains a point rather than dropping it.
|
|
72
70
|
*/
|
|
73
71
|
/**
|
|
74
72
|
* What a replacement does with each mark the schema declares
|
|
@@ -90,6 +88,67 @@ const NON_INCLUSIVE_MARK_DISPOSITION = {
|
|
|
90
88
|
/** The mark names in the schema that a replacement must not silently drop. */
|
|
91
89
|
const nonInclusiveMarkNames = (schema) => Object.entries(schema.marks).filter(([, type]) => type.spec.inclusive === false).map(([name]) => name);
|
|
92
90
|
const isCarried = (mark) => NON_INCLUSIVE_MARK_DISPOSITION[mark.type.name] === "carry";
|
|
91
|
+
/** One typed point for a comment whose aligned coverage has no content left. */
|
|
92
|
+
const commentPoint = (schema, id) => schema.node(RANGE_ANCHOR_NODE_NAME, {
|
|
93
|
+
start: {
|
|
94
|
+
type: "commentRangeStart",
|
|
95
|
+
id
|
|
96
|
+
},
|
|
97
|
+
end: {
|
|
98
|
+
type: "commentRangeEnd",
|
|
99
|
+
id
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
/** The same collector serves standalone commands and the existing bulk walk. */
|
|
103
|
+
const createResolvedCommentPointCollector = ({ removes, retiresStructure }) => {
|
|
104
|
+
const points = /* @__PURE__ */ new Map();
|
|
105
|
+
const retained = /* @__PURE__ */ new Set();
|
|
106
|
+
const visit = (node, position, previous) => {
|
|
107
|
+
if (node.type.name === "paragraph") {
|
|
108
|
+
const mark = expectParagraphAttrs(node).pPrMark;
|
|
109
|
+
const precedingMark = previous?.type.name === "paragraph" ? expectParagraphAttrs(previous).pPrMark : null;
|
|
110
|
+
if ((mark?.kind === "del" || mark?.kind === "moveFrom") && retiresStructure(node, position) || (precedingMark?.kind === "del" || precedingMark?.kind === "moveFrom") && previous !== void 0 && retiresStructure(previous, position - previous.nodeSize)) {
|
|
111
|
+
let survivingContent = false;
|
|
112
|
+
node.descendants((inline, offset) => {
|
|
113
|
+
if (inline.isInline && !isZeroWidthAnchor(inline) && !removes(inline, position + 1 + offset)) survivingContent = true;
|
|
114
|
+
return !survivingContent;
|
|
115
|
+
});
|
|
116
|
+
if (!survivingContent) return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (node.type.name === "tableRow" && expectTableRowAttrs(node).trDel && retiresStructure(node, position)) return false;
|
|
120
|
+
if ((node.type.name === "tableCell" || node.type.name === "tableHeader") && expectTableCellAttrs(node).cellMarker?.kind === "del" && retiresStructure(node, position)) return false;
|
|
121
|
+
if (node.type.name === "rangeAnchor") {
|
|
122
|
+
const attrs = readRangeAnchorAttrs(node);
|
|
123
|
+
if (attrs.ok && attrs.value.start.type === "commentRangeStart") retained.add(attrs.value.start.id);
|
|
124
|
+
}
|
|
125
|
+
if (!node.isInline || isZeroWidthAnchor(node)) return true;
|
|
126
|
+
for (const mark of node.marks) {
|
|
127
|
+
if (mark.type.name !== "comment") continue;
|
|
128
|
+
const id = mark.attrs["commentId"];
|
|
129
|
+
if (!removes(node, position)) retained.add(id);
|
|
130
|
+
else if (!points.has(id)) points.set(id, position);
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
};
|
|
134
|
+
const finish = () => {
|
|
135
|
+
for (const id of retained) points.delete(id);
|
|
136
|
+
return points;
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
visit,
|
|
140
|
+
finish
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
/** Point fallback excludes comments belonging to retired blocks and table cells. */
|
|
144
|
+
const resolvedCommentPoints = ({ doc, removes, retiresStructure }) => {
|
|
145
|
+
const collector = createResolvedCommentPointCollector({
|
|
146
|
+
removes,
|
|
147
|
+
retiresStructure
|
|
148
|
+
});
|
|
149
|
+
doc.descendants((node, position, parent, index) => collector.visit(node, position, index > 0 ? parent?.maybeChild(index - 1) ?? void 0 : void 0));
|
|
150
|
+
return collector.finish();
|
|
151
|
+
};
|
|
93
152
|
/** Anchors an edit must put back, and where relative to the replacement. */
|
|
94
153
|
const ANCHOR_PLACEMENT = {
|
|
95
154
|
[COMMENT_REFERENCE_NODE_NAME]: "after",
|
|
@@ -349,4 +408,4 @@ const inheritedReplacementMarks = (doc, from, to) => {
|
|
|
349
408
|
return (to === from ? $from.marks() : $from.marksAcross(doc.resolve(to))) ?? Mark.none;
|
|
350
409
|
};
|
|
351
410
|
//#endregion
|
|
352
|
-
export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, surveyReplacedAnnotations };
|
|
411
|
+
export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyReplacedAnnotations };
|
|
@@ -9,6 +9,8 @@ type ReadAuthoredRunFormattingOptions = {
|
|
|
9
9
|
};
|
|
10
10
|
/** Read authored run properties without confusing inherited visuals for direct formatting. */
|
|
11
11
|
declare const readAuthoredRunFormatting: ({ context, marks, styleResolver }: ReadAuthoredRunFormattingOptions) => document_d_exports.TextFormatting;
|
|
12
|
+
/** Preserve the authored distinction between an absent and empty property set in revisions. */
|
|
13
|
+
declare const readAuthoredRunPropertySnapshot: (options: ReadAuthoredRunFormattingOptions) => document_d_exports.TextFormatting | undefined;
|
|
12
14
|
type ReconcileRunFormattingMarksOptions = {
|
|
13
15
|
authoredFormatting: document_d_exports.TextFormatting;
|
|
14
16
|
context: ParagraphRunStyleContext;
|
|
@@ -22,4 +24,4 @@ type ReconcileRunFormattingMarksOptions = {
|
|
|
22
24
|
*/
|
|
23
25
|
declare const reconcileRunFormattingMarks: ({ authoredFormatting, context, node, styleResolver }: ReconcileRunFormattingMarksOptions) => readonly Mark[];
|
|
24
26
|
//#endregion
|
|
25
|
-
export { readAuthoredRunFormatting, reconcileRunFormattingMarks };
|
|
27
|
+
export { readAuthoredRunFormatting, readAuthoredRunPropertySnapshot, reconcileRunFormattingMarks };
|