@stll/folio-core 0.55.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +214 -52
- package/dist/ai-edits/batch-claims.d.ts +3 -1
- package/dist/ai-edits/batch-claims.js +25 -1
- package/dist/ai-edits/headless.js +2 -2
- package/dist/ai-edits/table-row-column-mutations.js +17 -3
- package/dist/ai-edits/types.d.ts +7 -1
- package/dist/ai-suggestions/apply.js +20 -1
- package/dist/compare/compare.js +1 -0
- package/dist/compare/inline-atoms.js +40 -16
- package/dist/compare/inline-provenance.js +12 -5
- package/dist/compare/reproducible-package.d.ts +0 -17
- package/dist/compare/reproducible-package.js +12 -18
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/controller/canonicalClipboard.d.ts +23 -0
- package/dist/controller/canonicalClipboard.js +482 -0
- package/dist/controller/canonicalClipboardResources.d.ts +21 -0
- package/dist/controller/canonicalClipboardResources.js +382 -0
- package/dist/controller/canonicalComments.d.ts +41 -0
- package/dist/controller/canonicalComments.js +152 -0
- package/dist/controller/canonicalComposition.d.ts +24 -3
- package/dist/controller/canonicalComposition.js +208 -48
- package/dist/controller/canonicalInlineProjection.d.ts +118 -0
- package/dist/controller/canonicalInlineProjection.js +476 -0
- package/dist/controller/canonicalInput.d.ts +13 -4
- package/dist/controller/canonicalInput.js +62 -14
- package/dist/controller/canonicalInputTimer.d.ts +16 -0
- package/dist/controller/canonicalInputTimer.js +34 -0
- package/dist/controller/canonicalPublicOperations.d.ts +48 -0
- package/dist/controller/canonicalPublicOperations.js +648 -0
- package/dist/controller/canonicalSelection.d.ts +18 -0
- package/dist/controller/canonicalSelection.js +32 -0
- package/dist/controller/canonicalSession.d.ts +27 -6
- package/dist/controller/canonicalSession.js +298 -116
- package/dist/controller/canonicalStoryEditor.d.ts +7 -3
- package/dist/controller/canonicalStoryEditor.js +27 -2
- package/dist/controller/canonicalStructure.js +57 -4
- package/dist/controller/canonicalTocSelection.d.ts +11 -0
- package/dist/controller/canonicalTocSelection.js +43 -0
- package/dist/controller/folioEditor.d.ts +2 -9
- package/dist/controller/folioEditor.js +18 -4
- package/dist/controller/headerFooterEditorManager.d.ts +2 -1
- package/dist/controller/headerFooterEditorManager.js +26 -11
- package/dist/controller/hiddenEditorApi.d.ts +24 -3
- package/dist/controller/hiddenEditorApi.js +8 -0
- package/dist/controller/hiddenEditorManager.d.ts +5 -1
- package/dist/controller/hiddenEditorManager.js +378 -43
- package/dist/controller/noteEditorManager.d.ts +2 -1
- package/dist/controller/noteEditorManager.js +29 -13
- package/dist/document-operations.d.ts +8 -1
- package/dist/document-operations.js +6 -4
- package/dist/docx/blockPlainText.d.ts +1 -1
- package/dist/docx/blockPlainText.js +11 -3
- package/dist/docx/builtInStyles.js +13 -6
- package/dist/docx/canonicalResourceSave.d.ts +16 -0
- package/dist/docx/canonicalResourceSave.js +27 -0
- package/dist/docx/canonicalSave.d.ts +29 -0
- package/dist/docx/canonicalSave.js +101 -0
- package/dist/docx/canonicalStoryRepack.d.ts +1 -1
- package/dist/docx/canonicalStoryRepack.js +38 -5
- package/dist/docx/containerChildren.gen.d.ts +3 -1
- package/dist/docx/containerChildren.gen.js +33 -1
- package/dist/docx/documentParser.d.ts +1 -13
- package/dist/docx/documentParser.js +1 -47
- package/dist/docx/ensureParaIds.js +10 -4
- package/dist/docx/fieldParser.js +14 -2
- package/dist/docx/footnoteParser.d.ts +1 -11
- package/dist/docx/footnoteParser.js +1 -16
- package/dist/docx/headerFooterParser.d.ts +1 -9
- package/dist/docx/headerFooterParser.js +1 -12
- package/dist/docx/headerFooterVerbatim.d.ts +26 -1
- package/dist/docx/headerFooterVerbatim.js +81 -3
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/hyperlinkParser.js +20 -1
- package/dist/docx/imageParser.d.ts +13 -1
- package/dist/docx/imageParser.js +20 -11
- package/dist/docx/inlineWrapperContent.d.ts +4 -4
- package/dist/docx/inlineWrapperContent.js +4 -4
- package/dist/docx/linkedSdtPreservation.d.ts +10 -0
- package/dist/docx/linkedSdtPreservation.js +37 -0
- package/dist/docx/metadataPrivacy.js +6 -1
- package/dist/docx/packageParts.js +9 -3
- package/dist/docx/paragraphParser.js +88 -67
- package/dist/docx/paragraphPropertySource.d.ts +22 -1
- package/dist/docx/paragraphPropertySource.js +109 -2
- package/dist/docx/parser.js +2 -1
- package/dist/docx/preservedRunContent.js +2 -1
- package/dist/docx/relsParser.js +26 -2
- package/dist/docx/removeHeaderFooterParts.js +17 -6
- package/dist/docx/rezip.d.ts +49 -5
- package/dist/docx/rezip.js +754 -235
- package/dist/docx/saveDiagnostics.d.ts +11 -0
- package/dist/docx/selectiveSave.d.ts +2 -1
- package/dist/docx/selectiveSave.js +42 -11
- package/dist/docx/selectiveXmlPatch.js +7 -5
- package/dist/docx/serializer/commentSerializer.d.ts +4 -1
- package/dist/docx/serializer/commentSerializer.js +7 -3
- package/dist/docx/serializer/documentSerializer.d.ts +9 -2
- package/dist/docx/serializer/documentSerializer.js +29 -10
- package/dist/docx/serializer/paragraphSerializer.js +207 -94
- package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
- package/dist/docx/server/materializeYjsDocx.js +14 -6
- package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
- package/dist/docx/storyBlockReplay.d.ts +21 -1
- package/dist/docx/storyBlockReplay.js +113 -9
- package/dist/docx/storyPlainText.d.ts +20 -0
- package/dist/docx/storyPlainText.js +23 -0
- package/dist/docx/structuralXmlPatch.js +10 -22
- package/dist/docx/styleParser.d.ts +5 -1
- package/dist/docx/styleParser.js +1 -1
- package/dist/docx/tableParser.d.ts +1 -8
- package/dist/docx/tableParser.js +1 -28
- package/dist/docx/textBoxParser.d.ts +1 -5
- package/dist/docx/textBoxParser.js +1 -20
- package/dist/docx/unzip.js +6 -1
- package/dist/i18n/messages/ar.gen.js +4 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
- package/dist/i18n/messages/catalogs.gen.js +68 -0
- package/dist/i18n/messages/cs.gen.js +4 -0
- package/dist/i18n/messages/de.gen.js +4 -0
- package/dist/i18n/messages/en.gen.js +4 -0
- package/dist/i18n/messages/es.gen.js +4 -0
- package/dist/i18n/messages/et.gen.js +4 -0
- package/dist/i18n/messages/fr.gen.js +4 -0
- package/dist/i18n/messages/he.gen.js +4 -0
- package/dist/i18n/messages/hi.gen.js +4 -0
- package/dist/i18n/messages/hu.gen.js +4 -0
- package/dist/i18n/messages/lt.gen.js +4 -0
- package/dist/i18n/messages/lv.gen.js +4 -0
- package/dist/i18n/messages/messages.gen.d.ts +4 -0
- package/dist/i18n/messages/pl.gen.js +4 -0
- package/dist/i18n/messages/pt-BR.gen.js +4 -0
- package/dist/i18n/messages/sk.gen.js +4 -0
- package/dist/i18n/messages/tr.gen.js +4 -0
- package/dist/i18n/messages/zh-CN.gen.js +4 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/acceptedBlockProjection.d.ts +12 -0
- package/dist/internal/acceptedBlockProjection.js +45 -0
- package/dist/internal/revisionResolutionInline.js +25 -3
- package/dist/managers/DocumentLoaderManager.js +2 -1
- package/dist/markdown/escape.d.ts +3 -1
- package/dist/markdown/escape.js +9 -2
- package/dist/markdown/renderBlock.js +5 -120
- package/dist/markdown/renderParagraph.js +4 -2
- package/dist/markdown/renderRuns.d.ts +7 -1
- package/dist/markdown/renderRuns.js +33 -24
- package/dist/panel-layout.d.ts +11 -9
- package/dist/panel-layout.js +13 -9
- package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
- package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
- package/dist/prosemirror/attrs/index.js +5 -0
- package/dist/prosemirror/canonicalCommands.d.ts +28 -4
- package/dist/prosemirror/canonicalCommands.js +11 -4
- package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
- package/dist/prosemirror/canonicalSelectionRange.js +15 -0
- package/dist/prosemirror/clipboardIntent.d.ts +7 -0
- package/dist/prosemirror/clipboardIntent.js +9 -0
- package/dist/prosemirror/commands/comments.js +60 -35
- package/dist/prosemirror/commands/pageBreak.js +2 -2
- package/dist/prosemirror/commands/pastePlainText.js +2 -1
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
- package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
- package/dist/prosemirror/commentOps.js +15 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
- package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
- package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -1
- package/dist/prosemirror/conversion/toProseDoc.js +124 -22
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +61 -35
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +65 -29
- package/dist/prosemirror/extensions/marks/markUtils.js +11 -8
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
- package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
- package/dist/prosemirror/fieldRepresentation.js +18 -0
- package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
- package/dist/prosemirror/hyperlinkRemoval.js +28 -9
- package/dist/prosemirror/insertOperations.js +2 -1
- package/dist/prosemirror/listNumbering.js +11 -5
- package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
- package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
- package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
- package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
- package/dist/prosemirror/noteReferenceReview.js +14 -1
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
- package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
- package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
- package/dist/prosemirror/plugins/documentStyles.js +17 -10
- package/dist/prosemirror/plugins/suggestionMode.js +48 -48
- package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
- package/dist/prosemirror/preservedXmlReview.js +35 -0
- package/dist/prosemirror/runFormattingFromMarks.js +2 -2
- package/dist/prosemirror/runFormattingReconciliation.js +5 -4
- package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
- package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
- package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
- package/dist/prosemirror/runStyleFormatting.js +3 -1
- package/dist/prosemirror/schema/marks.d.ts +1 -0
- package/dist/prosemirror/schema/nodes.d.ts +2 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
- package/dist/prosemirror/textInput.d.ts +5 -1
- package/dist/prosemirror/textInput.js +3 -3
- package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
- package/dist/prosemirror/trackedRevisionPath.js +39 -1
- package/dist/prosemirror/validation.js +3 -5
- package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
- package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
- package/dist/types/canonicalCapabilities.d.ts +201 -0
- package/dist/types/canonicalCapabilities.js +198 -0
- package/dist/types/canonicalComments.d.ts +51 -0
- package/dist/types/canonicalComments.js +0 -0
- package/dist/types/canonicalSave.d.ts +11 -0
- package/dist/types/canonicalSave.js +0 -0
- package/dist/types/canonicalSections.d.ts +12 -0
- package/dist/types/canonicalSections.js +0 -0
- package/dist/types/docxSerialization.d.ts +14 -0
- package/dist/types/docxSerialization.js +7 -0
- package/dist/utils/headingCollector.js +45 -4
- package/dist/utils/outlineDepth.d.ts +9 -0
- package/dist/utils/outlineDepth.js +9 -0
- package/package.json +4 -4
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { readFootnoteRefMarkAttrs } from "./attrs/index.js";
|
|
2
|
+
import { Result, TaggedError, panic } from "better-result";
|
|
3
|
+
import { Fragment, Mark, Slice } from "prosemirror-model";
|
|
4
|
+
import { Plugin } from "prosemirror-state";
|
|
5
|
+
import { isHistoryTransaction } from "prosemirror-history";
|
|
6
|
+
//#region src/prosemirror/noteReferenceOccurrences.ts
|
|
7
|
+
var NoteReferenceEditRefusal = class extends TaggedError("NoteReferenceEditRefusal") {};
|
|
8
|
+
var NoteReferenceReplayDefect = class extends TaggedError("NoteReferenceReplayDefect") {};
|
|
9
|
+
const NOTE_OCCURRENCE_REPLAY_META = "noteReferenceOccurrenceReplay";
|
|
10
|
+
const markNoteReferenceReplay = (transaction) => {
|
|
11
|
+
transaction.setMeta(NOTE_OCCURRENCE_REPLAY_META, true);
|
|
12
|
+
};
|
|
13
|
+
const NOTE_OCCURRENCE_REPORT_META = "noteReferenceOccurrenceReport";
|
|
14
|
+
const noteReferenceTransactionIssue = (transaction) => {
|
|
15
|
+
const issue = transaction.getMeta(NOTE_OCCURRENCE_REPORT_META);
|
|
16
|
+
return issue instanceof NoteReferenceEditRefusal || issue instanceof NoteReferenceReplayDefect ? issue : void 0;
|
|
17
|
+
};
|
|
18
|
+
const mintNoteReferenceOccurrenceId = () => crypto.randomUUID();
|
|
19
|
+
/** Identity establishes boundaries; text only validates the already bounded unit. */
|
|
20
|
+
const readOccurrences = (paragraph) => {
|
|
21
|
+
const referenceType = paragraph.type.schema.marks["footnoteRef"];
|
|
22
|
+
if (!referenceType || !paragraph.rangeHasMark(0, paragraph.content.size, referenceType)) return Result.ok([]);
|
|
23
|
+
const occurrences = [];
|
|
24
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25
|
+
let previous;
|
|
26
|
+
let issue;
|
|
27
|
+
paragraph.forEach((node, from) => {
|
|
28
|
+
if (issue !== void 0) return;
|
|
29
|
+
const reference = node.marks.find((mark) => mark.type.name === "footnoteRef");
|
|
30
|
+
if (!reference) {
|
|
31
|
+
previous = void 0;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const attrs = readFootnoteRefMarkAttrs(reference);
|
|
35
|
+
if (!attrs.ok || !node.isText) {
|
|
36
|
+
issue = "Note references require attributed text occurrences.";
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const id = attrs.value.occurrenceId;
|
|
40
|
+
if (previous?.id === id) {
|
|
41
|
+
if (!Mark.sameSet(previous.node.marks, node.marks)) {
|
|
42
|
+
issue = "A note reference cannot have different formatting or revision owners within one occurrence.";
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
previous.text += node.text ?? "";
|
|
46
|
+
previous.to = from + node.nodeSize;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (seen.has(id)) {
|
|
50
|
+
issue = "A note-reference occurrence must remain contiguous.";
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
seen.add(id);
|
|
54
|
+
previous = {
|
|
55
|
+
id,
|
|
56
|
+
node,
|
|
57
|
+
text: node.text ?? "",
|
|
58
|
+
from,
|
|
59
|
+
to: from + node.nodeSize
|
|
60
|
+
};
|
|
61
|
+
occurrences.push(previous);
|
|
62
|
+
});
|
|
63
|
+
for (const occurrence of occurrences) {
|
|
64
|
+
const reference = occurrence.node.marks.find((mark) => mark.type.name === "footnoteRef");
|
|
65
|
+
if (!reference) return panic("Missing note-reference mark in an occurrence.");
|
|
66
|
+
const attrs = readFootnoteRefMarkAttrs(reference);
|
|
67
|
+
if (!attrs.ok) return panic("Invalid attributed note-reference occurrence.");
|
|
68
|
+
if (occurrence.text !== String(attrs.value.id)) issue ??= "Edit the whole note reference; its rendered label is indivisible.";
|
|
69
|
+
}
|
|
70
|
+
return issue === void 0 ? Result.ok(occurrences) : Result.err(new NoteReferenceEditRefusal({ message: issue }));
|
|
71
|
+
};
|
|
72
|
+
/** Saving asserts the edit-owner invariant and emits each occurrence once. */
|
|
73
|
+
const coalesceNoteReferenceOccurrences = (paragraph) => {
|
|
74
|
+
if (saveOccurrenceValidator?.cachedIds(paragraph)?.length === 0) return paragraph;
|
|
75
|
+
const referenceType = paragraph.type.schema.marks["footnoteRef"];
|
|
76
|
+
if (!referenceType || !paragraph.rangeHasMark(0, paragraph.content.size, referenceType)) return paragraph;
|
|
77
|
+
const result = readOccurrences(paragraph);
|
|
78
|
+
if (result.isErr()) return panic(result.error.message);
|
|
79
|
+
if (result.value.every(({ node, from, to }) => node.nodeSize === to - from)) return paragraph;
|
|
80
|
+
const byStart = new Map(result.value.map((occurrence) => [occurrence.from, occurrence]));
|
|
81
|
+
const nodes = [];
|
|
82
|
+
let through = -1;
|
|
83
|
+
paragraph.forEach((node, from) => {
|
|
84
|
+
if (from < through) return;
|
|
85
|
+
const occurrence = byStart.get(from);
|
|
86
|
+
if (occurrence) {
|
|
87
|
+
nodes.push(node.type.schema.text(occurrence.text, node.marks));
|
|
88
|
+
through = occurrence.to;
|
|
89
|
+
} else nodes.push(node);
|
|
90
|
+
});
|
|
91
|
+
return paragraph.copy(Fragment.fromArray(nodes));
|
|
92
|
+
};
|
|
93
|
+
const createOccurrenceValidator = () => {
|
|
94
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
95
|
+
const validate = (node) => {
|
|
96
|
+
const cached = cache.get(node);
|
|
97
|
+
if (cached) return cached;
|
|
98
|
+
const ids = /* @__PURE__ */ new Set();
|
|
99
|
+
const include = (id) => {
|
|
100
|
+
if (ids.has(id)) return new NoteReferenceEditRefusal({ message: "Separate note references require distinct occurrence identities." });
|
|
101
|
+
ids.add(id);
|
|
102
|
+
};
|
|
103
|
+
if (node.inlineContent) {
|
|
104
|
+
const own = readOccurrences(node);
|
|
105
|
+
if (own.isErr()) return Result.err(own.error);
|
|
106
|
+
for (const occurrence of own.value) {
|
|
107
|
+
const issue = include(occurrence.id);
|
|
108
|
+
if (issue) return Result.err(issue);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (let index = 0; index < node.childCount; index += 1) {
|
|
112
|
+
const child = node.child(index);
|
|
113
|
+
if (child.isLeaf) continue;
|
|
114
|
+
const nested = validate(child);
|
|
115
|
+
if (nested.isErr()) return nested;
|
|
116
|
+
for (const id of nested.value) {
|
|
117
|
+
const issue = include(id);
|
|
118
|
+
if (issue) return Result.err(issue);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const result = Result.ok([...ids]);
|
|
122
|
+
cache.set(node, result);
|
|
123
|
+
return result;
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
validate,
|
|
127
|
+
cachedIds: (node) => {
|
|
128
|
+
const facts = cache.get(node);
|
|
129
|
+
return facts?.isOk() ? facts.value : void 0;
|
|
130
|
+
},
|
|
131
|
+
validateSave: (doc) => {
|
|
132
|
+
const facts = cache.get(doc);
|
|
133
|
+
if (facts?.isOk() && facts.value.length === 0) return facts;
|
|
134
|
+
cache.delete(doc);
|
|
135
|
+
return validate(doc);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
let saveOccurrenceValidator;
|
|
140
|
+
const assertNoteReferenceOccurrences = (doc) => {
|
|
141
|
+
const result = (saveOccurrenceValidator ??= createOccurrenceValidator()).validateSave(doc);
|
|
142
|
+
if (result.isErr()) panic(result.error.message);
|
|
143
|
+
};
|
|
144
|
+
/** Production edit boundary: refuse before committing an unsaveable occurrence. */
|
|
145
|
+
const noteReferenceOccurrencePlugin = () => {
|
|
146
|
+
const { validate } = createOccurrenceValidator();
|
|
147
|
+
return new Plugin({
|
|
148
|
+
filterTransaction(transaction) {
|
|
149
|
+
if (!transaction.docChanged) return true;
|
|
150
|
+
const result = validate(transaction.doc);
|
|
151
|
+
if (result.isOk()) return true;
|
|
152
|
+
const replay = isHistoryTransaction(transaction) || transaction.getMeta(NOTE_OCCURRENCE_REPLAY_META) === true;
|
|
153
|
+
transaction.setMeta(NOTE_OCCURRENCE_REPORT_META, replay ? new NoteReferenceReplayDefect({ message: result.error.message }) : result.error);
|
|
154
|
+
return replay;
|
|
155
|
+
},
|
|
156
|
+
props: { transformPasted(slice) {
|
|
157
|
+
const identities = /* @__PURE__ */ new Map();
|
|
158
|
+
const remint = (fragment) => {
|
|
159
|
+
const nodes = [];
|
|
160
|
+
fragment.forEach((node) => {
|
|
161
|
+
const marks = node.marks.map((mark) => {
|
|
162
|
+
if (mark.type.name !== "footnoteRef") return mark;
|
|
163
|
+
const attrs = readFootnoteRefMarkAttrs(mark);
|
|
164
|
+
if (!attrs.ok) return mark;
|
|
165
|
+
const id = attrs.value.occurrenceId;
|
|
166
|
+
let replacement = identities.get(id);
|
|
167
|
+
if (replacement === void 0) {
|
|
168
|
+
replacement = mintNoteReferenceOccurrenceId();
|
|
169
|
+
identities.set(id, replacement);
|
|
170
|
+
}
|
|
171
|
+
return mark.type.create({
|
|
172
|
+
...mark.attrs,
|
|
173
|
+
occurrenceId: replacement
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
nodes.push(node.copy(remint(node.content)).mark(marks));
|
|
177
|
+
});
|
|
178
|
+
return Fragment.fromArray(nodes);
|
|
179
|
+
};
|
|
180
|
+
return new Slice(remint(slice.content), slice.openStart, slice.openEnd);
|
|
181
|
+
} }
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
export { NoteReferenceEditRefusal, NoteReferenceReplayDefect, assertNoteReferenceOccurrences, coalesceNoteReferenceOccurrences, markNoteReferenceReplay, mintNoteReferenceOccurrenceId, noteReferenceOccurrencePlugin, noteReferenceTransactionIssue };
|
|
@@ -2,6 +2,7 @@ import { withoutLostCommentThreads } from "../ai-edits/comment-lifecycle.js";
|
|
|
2
2
|
import { visitCommentMarkers } from "../docx/commentAnchorIndex.js";
|
|
3
3
|
import { isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
4
4
|
import { pluginsForHeadlessRevisionResolution } from "../internal/headlessRevisionResolutionGuard.js";
|
|
5
|
+
import { addTrackedDeletionMark } from "./addTrackedDeletionMark.js";
|
|
5
6
|
import { expectFootnoteRefMarkAttrs, expectParagraphAttrs } from "./attrs/index.js";
|
|
6
7
|
import { rejectAIEditRevision } from "./commands/comments.js";
|
|
7
8
|
import { proseDocToBlocks } from "./conversion/fromProseDoc.js";
|
|
@@ -13,6 +14,12 @@ import { schema, singletonManager } from "./schema/index.js";
|
|
|
13
14
|
import { Transform } from "prosemirror-transform";
|
|
14
15
|
import { EditorState } from "prosemirror-state";
|
|
15
16
|
//#region src/prosemirror/noteReferenceReview.ts
|
|
17
|
+
/**
|
|
18
|
+
* A note goes with its reference. Deleting a note's reference while tracking
|
|
19
|
+
* changes deletes the note's text too, so rejecting that deletion must give
|
|
20
|
+
* the note its text back, whichever surface resolves it: the body editor
|
|
21
|
+
* resolves the reference, and this resolves the note's story to match.
|
|
22
|
+
*/
|
|
16
23
|
const noteKey = (kind, id) => `${kind}:${id}`;
|
|
17
24
|
/**
|
|
18
25
|
* The notes a story's text refers to, and whether each reference is pending
|
|
@@ -155,7 +162,13 @@ const deleteNoteWithReference = (transform, reference) => {
|
|
|
155
162
|
kind: "del",
|
|
156
163
|
info: markInfo
|
|
157
164
|
});
|
|
158
|
-
if (node.isInline && !deletion.type.isInSet(node.marks))
|
|
165
|
+
if (node.isInline && !deletion.type.isInSet(node.marks)) addTrackedDeletionMark({
|
|
166
|
+
insertionPolicy: "preserve-pending",
|
|
167
|
+
tr: transform,
|
|
168
|
+
from: position,
|
|
169
|
+
to: position + node.nodeSize,
|
|
170
|
+
mark: deletion
|
|
171
|
+
});
|
|
159
172
|
return !node.isInline;
|
|
160
173
|
});
|
|
161
174
|
return transform;
|
|
@@ -4,6 +4,7 @@ import { NumberingMap } from "../docx/numberingParser.js";
|
|
|
4
4
|
import { Node } from "prosemirror-model";
|
|
5
5
|
import { Transaction } from "prosemirror-state";
|
|
6
6
|
//#region src/prosemirror/paragraphPropertyCarry.d.ts
|
|
7
|
+
type RecordedFormatting = NonNullable<ParagraphPropertyChangeAttrs["previousFormatting"]>;
|
|
7
8
|
type CarryParagraphPropertiesOptions = {
|
|
8
9
|
tr: Transaction;
|
|
9
10
|
/** The paragraph that takes the properties. */
|
|
@@ -17,6 +18,11 @@ type CarryParagraphPropertiesOptions = {
|
|
|
17
18
|
/** Properties the paragraph keeps its own values of: ones its own batch set. */
|
|
18
19
|
keep?: ReadonlySet<string>;
|
|
19
20
|
};
|
|
21
|
+
type CarriedParagraphPropertiesOptions = Pick<CarryParagraphPropertiesOptions, "source" | "keep"> & {
|
|
22
|
+
target: Node;
|
|
23
|
+
};
|
|
24
|
+
/** The exact formatting transfer, shared with pre-mutation capacity checks. */
|
|
25
|
+
declare const carriedParagraphProperties: ({ source, target, keep }: CarriedParagraphPropertiesOptions) => RecordedFormatting;
|
|
20
26
|
/**
|
|
21
27
|
* Give a paragraph another one's properties: the pPr a property change
|
|
22
28
|
* covers. Its identity, mark, mark run properties and section stay its own,
|
|
@@ -61,4 +67,4 @@ declare const recordReplacedParagraphProperties: ({ tr, position, replaced, revi
|
|
|
61
67
|
*/
|
|
62
68
|
declare const propertiesSetInBatch: (node: Node, batchRevisionIds: ReadonlySet<number>) => ReadonlySet<string>;
|
|
63
69
|
//#endregion
|
|
64
|
-
export { carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
|
|
70
|
+
export { carriedParagraphProperties, carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
|
|
@@ -17,6 +17,8 @@ import { panic } from "better-result";
|
|
|
17
17
|
* revision, so accepting reads as the direct edit and rejecting restores the
|
|
18
18
|
* second's own.
|
|
19
19
|
*/
|
|
20
|
+
/** The exact formatting transfer, shared with pre-mutation capacity checks. */
|
|
21
|
+
const carriedParagraphProperties = ({ source, target, keep }) => Object.fromEntries([...Object.entries(paragraphPropertiesSnapshot(source)).filter(([key]) => !keep?.has(key)), ...Object.entries(paragraphPropertiesSnapshot(target)).filter(([key]) => keep?.has(key))]);
|
|
20
22
|
/**
|
|
21
23
|
* Give a paragraph another one's properties: the pPr a property change
|
|
22
24
|
* covers. Its identity, mark, mark run properties and section stay its own,
|
|
@@ -32,8 +34,11 @@ const carryParagraphProperties = ({ tr, position, source, styleResolver, numberi
|
|
|
32
34
|
tracked: false
|
|
33
35
|
};
|
|
34
36
|
const previousFormatting = paragraphPropertiesSnapshot(target);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
+
const formatting = carriedParagraphProperties({
|
|
38
|
+
source,
|
|
39
|
+
target,
|
|
40
|
+
...keep === void 0 ? {} : { keep }
|
|
41
|
+
});
|
|
37
42
|
if (JSON.stringify(previousFormatting) === JSON.stringify(formatting)) return {
|
|
38
43
|
tr,
|
|
39
44
|
changed: false,
|
|
@@ -152,4 +157,4 @@ const propertiesSetInBatch = (node, batchRevisionIds) => {
|
|
|
152
157
|
return new Set([.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(now)])].filter((key) => JSON.stringify(before[key]) !== JSON.stringify(now[key])));
|
|
153
158
|
};
|
|
154
159
|
//#endregion
|
|
155
|
-
export { carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
|
|
160
|
+
export { carriedParagraphProperties, carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
|
|
@@ -3,6 +3,9 @@ import { StyleResolver } from "../styles/styleResolver.js";
|
|
|
3
3
|
import { documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver } from "./documentStyleState.js";
|
|
4
4
|
import { EditorState, Plugin } from "prosemirror-state";
|
|
5
5
|
//#region src/prosemirror/plugins/documentStyles.d.ts
|
|
6
|
+
type DocumentStylesInput = document_d_exports.StyleDefinitions | StyleResolver | null | undefined;
|
|
7
|
+
/** Canonical projections own formatting; expose the resolver without PM repair transactions. */
|
|
8
|
+
declare const createDocumentStyleContextPlugin: (styles: DocumentStylesInput) => Plugin;
|
|
6
9
|
/**
|
|
7
10
|
* Create the plugin holding a StyleResolver for the document's `styles` for
|
|
8
11
|
* the lifetime of the EditorState. The resolver is fixed per document load;
|
|
@@ -14,4 +17,4 @@ declare function createDocumentStylesPlugin(styles: document_d_exports.StyleDefi
|
|
|
14
17
|
/** Reconfigure so ProseMirror replaces the keyed plugin's resolver state. */
|
|
15
18
|
declare const withDocumentStyles: (state: EditorState, styles: document_d_exports.StyleDefinitions | StyleResolver | null | undefined) => EditorState;
|
|
16
19
|
//#endregion
|
|
17
|
-
export { createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
|
|
20
|
+
export { createDocumentStyleContextPlugin, createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
|
|
@@ -76,6 +76,21 @@ const bareResolver = () => {
|
|
|
76
76
|
bareStyleResolver ??= createStyleResolver(void 0);
|
|
77
77
|
return bareStyleResolver;
|
|
78
78
|
};
|
|
79
|
+
const documentStyleContextSpec = (styles) => {
|
|
80
|
+
let resolver;
|
|
81
|
+
if (styles instanceof StyleResolver) resolver = styles;
|
|
82
|
+
else if (styles) resolver = createStyleResolver(styles);
|
|
83
|
+
else resolver = null;
|
|
84
|
+
return {
|
|
85
|
+
key: documentStylesKey,
|
|
86
|
+
state: {
|
|
87
|
+
init: () => resolver,
|
|
88
|
+
apply: (_tr, value) => value
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
/** Canonical projections own formatting; expose the resolver without PM repair transactions. */
|
|
93
|
+
const createDocumentStyleContextPlugin = (styles) => new Plugin(documentStyleContextSpec(styles));
|
|
79
94
|
/**
|
|
80
95
|
* Create the plugin holding a StyleResolver for the document's `styles` for
|
|
81
96
|
* the lifetime of the EditorState. The resolver is fixed per document load;
|
|
@@ -84,16 +99,8 @@ const bareResolver = () => {
|
|
|
84
99
|
* have one.
|
|
85
100
|
*/
|
|
86
101
|
function createDocumentStylesPlugin(styles) {
|
|
87
|
-
let resolver;
|
|
88
|
-
if (styles instanceof StyleResolver) resolver = styles;
|
|
89
|
-
else if (styles) resolver = createStyleResolver(styles);
|
|
90
|
-
else resolver = null;
|
|
91
102
|
return new Plugin({
|
|
92
|
-
|
|
93
|
-
state: {
|
|
94
|
-
init: () => resolver,
|
|
95
|
-
apply: (_tr, value) => value
|
|
96
|
-
},
|
|
103
|
+
...documentStyleContextSpec(styles),
|
|
97
104
|
appendTransaction: (transactions, _oldState, newState) => {
|
|
98
105
|
const styleResolver = documentStylesKey.getState(newState) ?? bareResolver();
|
|
99
106
|
const tr = resolveEditedParagraphStyles(transactions, newState, styleResolver, () => getDocumentNumbering(newState)) ?? newState.tr;
|
|
@@ -109,4 +116,4 @@ const withDocumentStyles = (state, styles) => {
|
|
|
109
116
|
return state.reconfigure({ plugins: retained }).reconfigure({ plugins: [...retained, createDocumentStylesPlugin(styles)] });
|
|
110
117
|
};
|
|
111
118
|
//#endregion
|
|
112
|
-
export { createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
|
|
119
|
+
export { createDocumentStyleContextPlugin, createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addTrackedDeletionMark } from "../addTrackedDeletionMark.js";
|
|
1
2
|
import { expectParagraphAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
|
|
2
3
|
import { clearIndentOnBackspace } from "../commands/clearParagraphIndent.js";
|
|
3
4
|
import { paragraphPropertiesSnapshot } from "../commands/propertyChangeScope.js";
|
|
@@ -117,20 +118,13 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
|
|
|
117
118
|
* Retract the current author's own inserted text.
|
|
118
119
|
* Processes in reverse order to maintain position validity.
|
|
119
120
|
*/
|
|
120
|
-
function markRangeAsDeleted(tr, doc, from, to,
|
|
121
|
-
|
|
121
|
+
function markRangeAsDeleted(tr, doc, from, to, deletionType, pluginState, joinState) {
|
|
122
|
+
let hasMarkableContent = false;
|
|
122
123
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
123
124
|
if (!canCarryTrackedRunMark(node)) return;
|
|
124
|
-
|
|
125
|
-
const end = Math.min(pos + node.nodeSize, to);
|
|
126
|
-
if (start >= end) return;
|
|
127
|
-
const isOwnInsert = node.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
|
|
125
|
+
if (Math.max(pos, from) >= Math.min(pos + node.nodeSize, to)) return;
|
|
128
126
|
if (node.marks.some((m) => m.type === deletionType)) return;
|
|
129
|
-
|
|
130
|
-
from: start,
|
|
131
|
-
to: end,
|
|
132
|
-
isOwnInsert
|
|
133
|
-
});
|
|
127
|
+
hasMarkableContent = true;
|
|
134
128
|
});
|
|
135
129
|
const delAttrs = findAdjacentRevisionForRange(doc, from, to, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
136
130
|
let removedMark = false;
|
|
@@ -165,18 +159,20 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
|
|
|
165
159
|
date: delAttrs.date
|
|
166
160
|
});
|
|
167
161
|
}
|
|
168
|
-
if (
|
|
162
|
+
if (!hasMarkableContent) return;
|
|
169
163
|
encloseWholeControls({
|
|
170
164
|
tr,
|
|
171
165
|
from,
|
|
172
166
|
to,
|
|
173
167
|
revisionId: delAttrs.revisionId
|
|
174
168
|
});
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
addTrackedDeletionMark({
|
|
170
|
+
tr,
|
|
171
|
+
from,
|
|
172
|
+
to,
|
|
173
|
+
mark: deletionType.create(delAttrs),
|
|
174
|
+
insertionPolicy: "retract-own"
|
|
175
|
+
});
|
|
180
176
|
}
|
|
181
177
|
/** Preserve clipboard revisions inside the revision that inserted their content. */
|
|
182
178
|
function enclosePastedRunRevisions({ tr, from, to, revision: insertion }) {
|
|
@@ -298,7 +294,7 @@ function handleSuggestionPaste(view, slice, pluginState) {
|
|
|
298
294
|
const endsContainer = wholeBlocks && paragraphEndsItsContainer(doc.resolve($to.before()), $to.parent.type.name);
|
|
299
295
|
const rotates = endsContainer && slice.content.firstChild?.type === $to.parent.type && slice.content.lastChild?.type === $to.parent.type;
|
|
300
296
|
const tableFirst = endsContainer && !rotates;
|
|
301
|
-
if (!tableFirst) markRangeAsDeleted(tr, doc, from, wholeBlocks && !endsContainer ? $to.after() : to,
|
|
297
|
+
if (!tableFirst) markRangeAsDeleted(tr, doc, from, wholeBlocks && !endsContainer ? $to.after() : to, deletionType, pluginState);
|
|
302
298
|
const carryAcross = () => {
|
|
303
299
|
if (selectsAll || $from.parent.type.name !== "paragraph" || $from.parent === $to.parent || from <= $from.start()) return;
|
|
304
300
|
const firstPos = tr.mapping.map($from.before());
|
|
@@ -332,7 +328,7 @@ function handleSuggestionPaste(view, slice, pluginState) {
|
|
|
332
328
|
let insertTo = placed.length === 0 ? at : Math.max(...placed.map((range) => range.to));
|
|
333
329
|
if (tableFirst) {
|
|
334
330
|
const struck = tr.steps.length;
|
|
335
|
-
markRangeAsDeleted(tr, tr.doc, tr.mapping.map(from), tr.mapping.map(to),
|
|
331
|
+
markRangeAsDeleted(tr, tr.doc, tr.mapping.map(from), tr.mapping.map(to), deletionType, pluginState);
|
|
336
332
|
const since = tr.mapping.slice(struck);
|
|
337
333
|
insertFrom = since.map(insertFrom, -1);
|
|
338
334
|
insertTo = since.map(insertTo, -1);
|
|
@@ -405,7 +401,7 @@ function handleSuggestionTableCellPaste(view, slice, pluginState) {
|
|
|
405
401
|
if (!cell) return;
|
|
406
402
|
const { from, to } = cellPasteRange(cell, cellPos, content);
|
|
407
403
|
const mapFrom = tr.mapping.maps.length;
|
|
408
|
-
markRangeAsDeleted(tr, tr.doc, from, to,
|
|
404
|
+
markRangeAsDeleted(tr, tr.doc, from, to, deletionType, pluginState);
|
|
409
405
|
const insertFrom = tr.mapping.slice(mapFrom).map(to);
|
|
410
406
|
const sizeBefore = tr.doc.content.size;
|
|
411
407
|
tr.replaceRange(insertFrom, insertFrom, content);
|
|
@@ -538,7 +534,7 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
|
|
|
538
534
|
const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
539
535
|
if (from !== to) {
|
|
540
536
|
const deletionType = view.state.schema.marks["deletion"];
|
|
541
|
-
if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to,
|
|
537
|
+
if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, deletionType, pluginState, view.state);
|
|
542
538
|
}
|
|
543
539
|
const insertAt = tr.mapping.map(to);
|
|
544
540
|
tr.insertText(text, insertAt, insertAt);
|
|
@@ -597,7 +593,7 @@ function suggestRangeDeletion(state, tr, from, to) {
|
|
|
597
593
|
const insertionType = state.schema.marks["insertion"];
|
|
598
594
|
const deletionType = state.schema.marks["deletion"];
|
|
599
595
|
if (!pluginState?.active || !insertionType || !deletionType) return false;
|
|
600
|
-
markRangeAsDeleted(tr, tr.doc, from, to,
|
|
596
|
+
markRangeAsDeleted(tr, tr.doc, from, to, deletionType, pluginState);
|
|
601
597
|
return true;
|
|
602
598
|
}
|
|
603
599
|
/**
|
|
@@ -661,21 +657,23 @@ function handleSuggestionEnter(view, pluginState) {
|
|
|
661
657
|
view.dispatch(tr.scrollIntoView());
|
|
662
658
|
return true;
|
|
663
659
|
}
|
|
664
|
-
/** Find the adjacent visible unit without consuming
|
|
660
|
+
/** Find the adjacent visible unit without consuming retained deletions or bookmark markers. */
|
|
665
661
|
function caretDeleteTarget(state, direction) {
|
|
666
662
|
const { $from } = state.selection;
|
|
667
663
|
const backward = direction === "backward";
|
|
668
|
-
let
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (adjacent
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
664
|
+
let cursor = $from.pos;
|
|
665
|
+
while (cursor >= $from.start() && cursor <= $from.end()) {
|
|
666
|
+
const adjacent = backward ? state.doc.resolve(cursor).nodeBefore : state.doc.resolve(cursor).nodeAfter;
|
|
667
|
+
if (!adjacent) return { type: "paragraphEdge" };
|
|
668
|
+
if (adjacent.type.name !== "bookmarkBoundary" && !adjacent.marks.some((mark) => mark.type === state.schema.marks["deletion"])) {
|
|
669
|
+
const size = adjacent.isAtom && !adjacent.isText ? adjacent.nodeSize : 1;
|
|
670
|
+
return {
|
|
671
|
+
type: "inlineUnit",
|
|
672
|
+
from: backward ? cursor - size : cursor,
|
|
673
|
+
to: backward ? cursor : cursor + size
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
cursor += backward ? -adjacent.nodeSize : adjacent.nodeSize;
|
|
679
677
|
}
|
|
680
678
|
return { type: "paragraphEdge" };
|
|
681
679
|
}
|
|
@@ -793,7 +791,7 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
793
791
|
from: rangeFrom.pos,
|
|
794
792
|
to: rangeTo.pos
|
|
795
793
|
}).sort((a, b) => b.from - a.from);
|
|
796
|
-
for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to,
|
|
794
|
+
for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, deletionType, pluginState, state);
|
|
797
795
|
if (state.selection instanceof CellSelection) tr.setSelection(state.selection.map(tr.doc, tr.mapping));
|
|
798
796
|
else {
|
|
799
797
|
const cursorPos = tr.mapping.map($to.pos);
|
|
@@ -821,17 +819,20 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
821
819
|
const rangeTo = noteRange?.to ?? deleteEnd;
|
|
822
820
|
const nodeAfter = state.doc.resolve(rangeFrom).nodeAfter;
|
|
823
821
|
if (!nodeAfter || !canCarryTrackedRunMark(nodeAfter)) return false;
|
|
824
|
-
const
|
|
825
|
-
if (
|
|
826
|
-
|
|
827
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
|
|
828
|
-
} else if (hasOwnInsertion) tr.delete(rangeFrom, rangeTo);
|
|
829
|
-
else {
|
|
822
|
+
const mapFrom = tr.mapping.maps.length;
|
|
823
|
+
if (noteRange) markRangeAsDeleted(tr, state.doc, noteRange.from, noteRange.to, deletionType, pluginState);
|
|
824
|
+
else if (!nodeAfter.marks.some((m) => m.type === deletionType)) {
|
|
830
825
|
const delAttrs = findAdjacentRevisionForRange(state.doc, rangeFrom, rangeTo, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
826
|
+
addTrackedDeletionMark({
|
|
827
|
+
insertionPolicy: "retract-own",
|
|
828
|
+
tr,
|
|
829
|
+
from: rangeFrom,
|
|
830
|
+
to: rangeTo,
|
|
831
|
+
mark: deletionType.create(delAttrs)
|
|
832
|
+
});
|
|
834
833
|
}
|
|
834
|
+
const newPos = tr.mapping.slice(mapFrom).map(isBackward ? rangeFrom : rangeTo);
|
|
835
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
|
|
835
836
|
dispatch(tr.scrollIntoView());
|
|
836
837
|
return true;
|
|
837
838
|
}
|
|
@@ -848,7 +849,7 @@ function markComposedAsInsertion({ view, from, pluginState, replaced, compositio
|
|
|
848
849
|
if (compositionId !== null) tr.setMeta("composition", compositionId);
|
|
849
850
|
if (replaced) {
|
|
850
851
|
tr.replaceRange(from, from, replaced);
|
|
851
|
-
markRangeAsDeleted(tr, tr.doc, from, tr.mapping.map(from, 1),
|
|
852
|
+
markRangeAsDeleted(tr, tr.doc, from, tr.mapping.map(from, 1), deletionType, pluginState);
|
|
852
853
|
const restoredEnd = tr.doc.resolve(tr.mapping.map(from, 1));
|
|
853
854
|
const restoredStart = tr.doc.resolve(from);
|
|
854
855
|
if (restoredStart.parent.type.name === "paragraph" && restoredEnd.parent.type.name === "paragraph" && restoredStart.parent !== restoredEnd.parent) {
|
|
@@ -918,14 +919,14 @@ function replacedInline(before, step) {
|
|
|
918
919
|
* rejecting restores it and accepting removes it. The author's own
|
|
919
920
|
* insertions are retracted instead.
|
|
920
921
|
*/
|
|
921
|
-
function restoreReplacedInline({ tr, replaced,
|
|
922
|
+
function restoreReplacedInline({ tr, replaced, deletionType, pluginState }) {
|
|
922
923
|
for (const { at, content } of replaced) {
|
|
923
924
|
const pos = tr.mapping.map(at, -1);
|
|
924
925
|
const $pos = tr.doc.resolve(pos);
|
|
925
926
|
const index = $pos.index();
|
|
926
927
|
if (!$pos.parent.canReplace(index, index, content)) continue;
|
|
927
928
|
tr.insert(pos, content);
|
|
928
|
-
markRangeAsDeleted(tr, tr.doc, pos, pos + content.size,
|
|
929
|
+
markRangeAsDeleted(tr, tr.doc, pos, pos + content.size, deletionType, pluginState);
|
|
929
930
|
}
|
|
930
931
|
}
|
|
931
932
|
/**
|
|
@@ -1101,7 +1102,6 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
1101
1102
|
if (deletionType) restoreReplacedInline({
|
|
1102
1103
|
tr,
|
|
1103
1104
|
replaced,
|
|
1104
|
-
insertionType,
|
|
1105
1105
|
deletionType,
|
|
1106
1106
|
pluginState
|
|
1107
1107
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Mark, Node } from "prosemirror-model";
|
|
2
|
+
//#region src/prosemirror/preservedXmlReview.d.ts
|
|
3
|
+
/** A retained opaque deleted result becomes ordinary text when its last deletion resolves. */
|
|
4
|
+
declare const resolvedPreservedXmlAttrs: (node: Node, marks: readonly Mark[]) => import("prosemirror-model").Attrs;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { resolvedPreservedXmlAttrs };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { captureVerbatimXml } from "../docx/verbatimCapture.js";
|
|
2
|
+
import { OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, getChildElements, getLocalName, parseXml } from "../docx/xmlParser.js";
|
|
3
|
+
import { expectPreservedXmlAttrs, expectTrackedChangeMarkAttrs } from "./attrs/index.js";
|
|
4
|
+
//#region src/prosemirror/preservedXmlReview.ts
|
|
5
|
+
const restoreDeletedText = (node) => {
|
|
6
|
+
if (node.type !== "element") return node;
|
|
7
|
+
const name = getLocalName(node.name);
|
|
8
|
+
let target;
|
|
9
|
+
if (name === "delText") target = "t";
|
|
10
|
+
if (name === "delInstrText") target = "instrText";
|
|
11
|
+
const renamed = target !== void 0 && WORDPROCESSINGML_NAMESPACE_URIS.has(node.namespaceUri ?? "") ? node.name?.replace(/[^:]+$/u, target) : node.name;
|
|
12
|
+
const elements = node.elements?.map(restoreDeletedText);
|
|
13
|
+
if (renamed === node.name && elements?.every((child, index) => child === node.elements?.[index]) !== false) return node;
|
|
14
|
+
return cloneElement(node, {
|
|
15
|
+
...renamed !== void 0 && renamed !== node.name ? { name: renamed } : {},
|
|
16
|
+
...elements ? { elements } : {}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const hasPendingDeletion = (marks) => marks.some((mark) => {
|
|
20
|
+
if (mark.type.name === "deletion") return true;
|
|
21
|
+
if (mark.type.name !== "insertion") return false;
|
|
22
|
+
return expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors?.some((ancestor) => ancestor.type === "deletion" || ancestor.type === "moveFrom") ?? false;
|
|
23
|
+
});
|
|
24
|
+
/** A retained opaque deleted result becomes ordinary text when its last deletion resolves. */
|
|
25
|
+
const resolvedPreservedXmlAttrs = (node, marks) => {
|
|
26
|
+
if (node.type.name !== "preservedXml" || !hasPendingDeletion(node.marks) || hasPendingDeletion(marks)) return node.attrs;
|
|
27
|
+
const attrs = expectPreservedXmlAttrs(node);
|
|
28
|
+
const xml = getChildElements(parseXml(attrs.xml, OOXML_NAMESPACE_SCOPE)).map((element) => captureVerbatimXml(restoreDeletedText(element))).join("");
|
|
29
|
+
return xml === attrs.xml ? node.attrs : {
|
|
30
|
+
...node.attrs,
|
|
31
|
+
xml
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { resolvedPreservedXmlAttrs };
|
|
@@ -4,7 +4,7 @@ import { expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectE
|
|
|
4
4
|
import { runShadingAttrsToShading, shadingToRunShadingAttrs } from "./conversion/runShadingMark.js";
|
|
5
5
|
import { applyRunFormattingOverrideAttrs, buildRunFormattingOverrideAttrs } from "./extensions/marks/RunFormattingOverrideExtension.js";
|
|
6
6
|
import { authoredRunFormattingFromAttrs, hasAuthoredRunFormattingProvenance } from "./runFormattingProvenance.js";
|
|
7
|
-
import { paragraphFormattingForRun, resolveEffectiveRunStyleFormatting, suppressParagraphMarkFormatting } from "./runStyleFormatting.js";
|
|
7
|
+
import { isParagraphMarkSuppressionEligible, paragraphFormattingForRun, resolveEffectiveRunStyleFormatting, suppressParagraphMarkFormatting } from "./runStyleFormatting.js";
|
|
8
8
|
//#region src/prosemirror/runFormattingFromMarks.ts
|
|
9
9
|
const RUN_FORMATTING_VISUAL_GROUPS = {
|
|
10
10
|
bold: "bold",
|
|
@@ -570,7 +570,7 @@ function marksToTextFormatting(marks, options) {
|
|
|
570
570
|
inheritedFormatting: suppressedInheritedFormatting,
|
|
571
571
|
observedFormatting: formatting
|
|
572
572
|
});
|
|
573
|
-
if (formattingAuthorshipCost(suppressed) < formattingAuthorshipCost(reconciled)) reconciled = suppressed;
|
|
573
|
+
if (isParagraphMarkSuppressionEligible(suppressed) && formattingAuthorshipCost(suppressed) < formattingAuthorshipCost(reconciled)) reconciled = suppressed;
|
|
574
574
|
}
|
|
575
575
|
return {
|
|
576
576
|
...reconciled,
|
|
@@ -3,7 +3,7 @@ import { expectCharacterStyleMarkAttrs } from "./attrs/index.js";
|
|
|
3
3
|
import { textFormattingToMarks } from "./extensions/marks/markUtils.js";
|
|
4
4
|
import { marksToTextFormatting } from "./runFormattingFromMarks.js";
|
|
5
5
|
import { RUN_FORMATTING_MARK_NAMES } from "./runFormattingMarkNames.js";
|
|
6
|
-
import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, paragraphFormattingForRun, resolveEffectiveRunStyleFormatting } from "./runStyleFormatting.js";
|
|
6
|
+
import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, isParagraphMarkSuppressionEligible, paragraphFormattingForRun, resolveEffectiveRunStyleFormatting } from "./runStyleFormatting.js";
|
|
7
7
|
import { Mark } from "prosemirror-model";
|
|
8
8
|
//#region src/prosemirror/runFormattingReconciliation.ts
|
|
9
9
|
/** Read authored run properties without confusing inherited visuals for direct formatting. */
|
|
@@ -37,14 +37,15 @@ const reconcileRunFormattingMarks = ({ authoredFormatting, context, node, styleR
|
|
|
37
37
|
paragraphFormatting,
|
|
38
38
|
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
39
39
|
}), authoredFormatting);
|
|
40
|
-
const overrideFormatting = mergeTextFormatting(getParagraphMarkSuppressionOverrides({
|
|
40
|
+
const overrideFormatting = mergeTextFormatting(isParagraphMarkSuppressionEligible(authoredFormatting) ? getParagraphMarkSuppressionOverrides({
|
|
41
41
|
directFormatting: authoredFormatting,
|
|
42
42
|
paragraphMarkFormatting: context.paragraphMarkFormatting,
|
|
43
43
|
suppressedFormatting: paragraphFormatting
|
|
44
|
-
}), authoredFormatting);
|
|
44
|
+
}) : void 0, authoredFormatting);
|
|
45
45
|
const formattingMarks = textFormattingToMarks(effectiveFormatting, node.type.schema, {
|
|
46
46
|
overrideFormatting,
|
|
47
|
-
directFormatting: authoredFormatting
|
|
47
|
+
directFormatting: authoredFormatting,
|
|
48
|
+
authoredCarrier: "preserve"
|
|
48
49
|
});
|
|
49
50
|
return Mark.setFrom([
|
|
50
51
|
...marks.filter(({ type }) => !RUN_FORMATTING_MARK_NAMES.has(type.name)),
|
|
@@ -14,5 +14,12 @@ type ReconstructRejectedRunFormattingMarksOptions = {
|
|
|
14
14
|
* owner so their reject semantics cannot diverge at the bulk threshold.
|
|
15
15
|
*/
|
|
16
16
|
declare const reconstructRejectedRunFormattingMarks: ({ node, paragraphContext, previousFormatting, styleResolver }: ReconstructRejectedRunFormattingMarksOptions) => readonly Mark[];
|
|
17
|
+
type RestoreHistoricalRunFormattingOptions = {
|
|
18
|
+
node: Node;
|
|
19
|
+
paragraphContext: ParagraphRunStyleContext;
|
|
20
|
+
styleResolver?: RunStyleResolver | null;
|
|
21
|
+
};
|
|
22
|
+
/** Restored deletions inherit the live paragraph cascade without authoring their historical visuals. */
|
|
23
|
+
declare const restoreHistoricalRunFormatting: ({ node, paragraphContext, styleResolver }: RestoreHistoricalRunFormattingOptions) => readonly Mark[];
|
|
17
24
|
//#endregion
|
|
18
|
-
export { reconstructRejectedRunFormattingMarks };
|
|
25
|
+
export { reconstructRejectedRunFormattingMarks, restoreHistoricalRunFormatting };
|