@stll/folio-core 0.11.0 → 0.13.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.d.ts +2 -1
- package/dist/ai-edits/apply.js +693 -63
- package/dist/ai-edits/block-identity.d.ts +4 -0
- package/dist/ai-edits/block-identity.js +9 -0
- package/dist/ai-edits/blockRange.d.ts +28 -1
- package/dist/ai-edits/blockRange.js +68 -2
- package/dist/ai-edits/clean-text.js +2 -1
- package/dist/ai-edits/headless.d.ts +4 -4
- package/dist/ai-edits/headless.js +9 -9
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.d.ts +7 -7
- package/dist/ai-edits/read.js +110 -3
- package/dist/ai-edits/snapshot.js +21 -3
- package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
- package/dist/ai-edits/table-cell-mutations.js +238 -0
- package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
- package/dist/ai-edits/table-mutation-plan.js +90 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +108 -0
- package/dist/ai-edits/table-row-column-mutations.js +300 -0
- package/dist/ai-edits/table-targets.d.ts +32 -0
- package/dist/ai-edits/table-targets.js +70 -0
- package/dist/ai-edits/types.d.ts +62 -1
- package/dist/ai-edits/word-diff.js +21 -0
- package/dist/compat/eigenpal.d.ts +3 -3
- package/dist/compat/eigenpal.js +3 -3
- package/dist/content-controls/mutateContentControls.js +1 -1
- package/dist/controller/activeEditorStory.d.ts +36 -0
- package/dist/controller/activeEditorStory.js +30 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.js +2 -2
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +12 -1
- package/dist/controller/hiddenEditorManager.js +44 -12
- package/dist/controller/layoutPipeline.d.ts +2 -0
- package/dist/controller/layoutPipeline.js +17 -2
- package/dist/controller/noteEditorManager.d.ts +36 -0
- package/dist/controller/noteEditorManager.js +210 -0
- package/dist/document-operations.d.ts +43 -11
- package/dist/document-operations.js +186 -18
- package/dist/docx/blockContentParser.js +21 -7
- package/dist/docx/commentParser.js +6 -1
- package/dist/docx/commentRangeIntegrity.d.ts +6 -0
- package/dist/docx/commentRangeIntegrity.js +186 -0
- package/dist/docx/conformance.js +62 -1
- package/dist/docx/documentGrid.d.ts +8 -0
- package/dist/docx/documentGrid.js +8 -0
- package/dist/docx/drawingUtils.js +1 -1
- package/dist/docx/encryption/agileDecryption.js +12 -2
- package/dist/docx/encryption/compoundFile.js +6 -1
- package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
- package/dist/docx/encryption/encryptionInfo.js +18 -2
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/ensureParaIds.js +8 -7
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/groupDrawingParser.js +10 -5
- package/dist/docx/hyperlinkParser.js +1 -1
- package/dist/docx/imageParser.js +10 -6
- package/dist/docx/metadataPrivacy.js +17 -3
- package/dist/docx/notePropertiesParser.js +1 -1
- package/dist/docx/numberingParser.js +1 -1
- package/dist/docx/paragraphParser.js +70 -61
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/parser.js +3 -3
- package/dist/docx/rezip.js +20 -9
- package/dist/docx/runParser.js +17 -6
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sectionParser.js +18 -5
- package/dist/docx/selectiveSave.js +5 -1
- package/dist/docx/selectiveXmlPatch.d.ts +22 -1
- package/dist/docx/selectiveXmlPatch.js +61 -1
- package/dist/docx/serializer/blockSdtSerializer.js +3 -2
- package/dist/docx/serializer/commentSerializer.js +5 -6
- package/dist/docx/serializer/documentSerializer.js +2 -2
- package/dist/docx/serializer/headerFooterSerializer.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +2 -2
- package/dist/docx/serializer/paragraphSerializer.js +23 -15
- package/dist/docx/serializer/runSerializer.js +25 -21
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/tableSerializer.d.ts +5 -4
- package/dist/docx/serializer/tableSerializer.js +26 -20
- package/dist/docx/serializer/xmlUtils.d.ts +13 -1
- package/dist/docx/serializer/xmlUtils.js +27 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
- package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
- package/dist/docx/server/boundedArchive.d.ts +14 -3
- package/dist/docx/server/boundedArchive.js +56 -11
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
- package/dist/docx/server/extractDocxText.js +55 -8
- package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
- package/dist/docx/server/inspectDocxPackage.js +217 -0
- package/dist/docx/server/validateDocxConformance.d.ts +41 -0
- package/dist/docx/server/validateDocxConformance.js +336 -0
- package/dist/docx/settingsParser.js +26 -3
- package/dist/docx/shapeParser.js +1 -1
- package/dist/docx/styleParser.js +4 -2
- package/dist/docx/tableParser.d.ts +8 -1
- package/dist/docx/tableParser.js +31 -10
- package/dist/docx/textBoxParser.d.ts +3 -3
- package/dist/docx/textBoxParser.js +16 -12
- package/dist/docx/unzip.js +1 -0
- package/dist/docx/vmlImageParser.js +11 -3
- package/dist/docx/xmlParser.js +11 -1
- package/dist/docx/xmlSafety.d.ts +5 -0
- package/dist/docx/xmlSafety.js +9 -0
- package/dist/fields/resolveFieldValues.js +1 -3
- package/dist/fonts/embeddedFonts.d.ts +33 -5
- package/dist/fonts/embeddedFonts.js +37 -8
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +151 -20
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
- package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
- package/dist/layout-bridge/engine/clickToPosition.js +2 -2
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +58 -30
- package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
- package/dist/layout-engine/justificationCompatibility.js +8 -0
- package/dist/layout-engine/keep-together.js +11 -1
- package/dist/layout-engine/measure/cache.js +23 -5
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +56 -37
- package/dist/layout-engine/measure/measureBlocks.js +15 -4
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +11 -2
- package/dist/layout-engine/measure/measureHelpers.js +15 -4
- package/dist/layout-engine/measure/measureParagraph.js +85 -25
- package/dist/layout-engine/measure/measureProvider.js +4 -3
- package/dist/layout-engine/measure/tableCellFlow.js +5 -2
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
- package/dist/layout-engine/paragraphSpacing.js +107 -13
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +48 -15
- package/dist/layout-painter/registry/modules/textBox.js +5 -1
- package/dist/layout-painter/renderImage.d.ts +12 -1
- package/dist/layout-painter/renderImage.js +21 -4
- package/dist/layout-painter/renderPage.d.ts +5 -2
- package/dist/layout-painter/renderPage.js +59 -14
- package/dist/layout-painter/renderParagraph.js +55 -15
- package/dist/layout-painter/renderTable.d.ts +5 -1
- package/dist/layout-painter/renderTable.js +7 -3
- package/dist/layout-painter/renderTextBox.d.ts +2 -1
- package/dist/layout-painter/renderTextBox.js +25 -14
- package/dist/layout-painter/renderWatermark.d.ts +1 -1
- package/dist/layout-painter/renderWatermark.js +2 -1
- package/dist/managers/DocumentLoaderManager.js +1 -1
- package/dist/markdown/fromMarkdown.js +1 -1
- package/dist/model.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +175 -18
- package/dist/prosemirror/commands/comments.d.ts +79 -4
- package/dist/prosemirror/commands/comments.js +622 -13
- package/dist/prosemirror/commands/contentControls.js +1 -1
- package/dist/prosemirror/commands/hyperlink.js +15 -1
- package/dist/prosemirror/commands/image.js +7 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
- package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
- package/dist/prosemirror/commentIdAllocator.js +13 -3
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +404 -62
- package/dist/prosemirror/conversion/index.js +1 -1
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
- package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +364 -103
- package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
- package/dist/prosemirror/extensions/StarterKit.js +6 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +6 -2
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +70 -11
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +14 -6
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
- package/dist/prosemirror/index.d.ts +1 -1
- package/dist/prosemirror/index.js +1 -1
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +36 -10
- package/dist/prosemirror/plugins/contentControlWidgets.js +128 -94
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
- package/dist/prosemirror/plugins/revisionIds.js +57 -0
- package/dist/prosemirror/plugins/selectionTracker.js +1 -0
- package/dist/prosemirror/plugins/suggestionMode.js +5 -4
- package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
- package/dist/prosemirror/runFormattingMarkNames.js +40 -0
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +30 -3
- package/dist/prosemirror/schema/marks.js +15 -0
- package/dist/prosemirror/schema/nodes.d.ts +111 -6
- package/dist/prosemirror/selectionState.js +1 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
- package/dist/prosemirror/tableCellMergeRevision.js +22 -0
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
- package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
- package/dist/prosemirror/validation.js +9 -1
- package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
- package/dist/server.d.ts +7 -3
- package/dist/server.js +7 -3
- package/dist/utils/clipboard.d.ts +10 -1
- package/dist/utils/clipboard.js +33 -6
- package/dist/utils/colorResolver.d.ts +8 -1
- package/dist/utils/colorResolver.js +35 -5
- package/dist/utils/fontFamilyMerge.js +9 -11
- package/dist/utils/fontLoader.js +3 -2
- package/dist/utils/fontResolver.d.ts +2 -1
- package/dist/utils/fontResolver.js +31 -7
- package/dist/utils/fontWeights.d.ts +1 -1
- package/dist/utils/fontWeights.js +1 -1
- package/dist/utils/hexId.d.ts +11 -1
- package/dist/utils/hexId.js +11 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/dist/utils/sanitizeImageSrc.d.ts +21 -0
- package/dist/utils/sanitizeImageSrc.js +30 -0
- package/dist/utils/stripXmlDeclarations.d.ts +11 -0
- package/dist/utils/stripXmlDeclarations.js +35 -0
- package/dist/version-comparison.d.ts +22 -2
- package/dist/version-comparison.js +41 -12
- package/dist/watermark/index.d.ts +11 -1
- package/dist/watermark/index.js +22 -1
- package/package.json +3 -2
|
@@ -41,7 +41,7 @@ import { SuperscriptExtension } from "./marks/SuperscriptExtension.js";
|
|
|
41
41
|
import { TextColorExtension } from "./marks/TextColorExtension.js";
|
|
42
42
|
import { TextEffectExtension } from "./marks/TextEffectExtension.js";
|
|
43
43
|
import { EmbossExtension, EmphasisMarkExtension, ImprintExtension, TextOutlineExtension, TextShadowExtension } from "./marks/TextEffectsExtensions.js";
|
|
44
|
-
import { DeletionExtension, InsertionExtension } from "./marks/TrackedChangeExtensions.js";
|
|
44
|
+
import { DeletionExtension, InsertionExtension, RunPropertyChangeExtension } from "./marks/TrackedChangeExtensions.js";
|
|
45
45
|
import { UnderlineExtension } from "./marks/UnderlineExtension.js";
|
|
46
46
|
import { BlockSdtExtension } from "./nodes/BlockSdtExtension.js";
|
|
47
47
|
import { FieldExtension } from "./nodes/FieldExtension.js";
|
|
@@ -50,10 +50,12 @@ import { HorizontalRuleExtension } from "./nodes/HorizontalRuleExtension.js";
|
|
|
50
50
|
import { ImageExtension } from "./nodes/ImageExtension.js";
|
|
51
51
|
import { MathExtension } from "./nodes/MathExtension.js";
|
|
52
52
|
import { PageBreakExtension } from "./nodes/PageBreakExtension.js";
|
|
53
|
+
import { RenderedPageBreakExtension } from "./nodes/RenderedPageBreakExtension.js";
|
|
53
54
|
import { SdtExtension } from "./nodes/SdtExtension.js";
|
|
54
55
|
import { ShapeExtension } from "./nodes/ShapeExtension.js";
|
|
55
56
|
import { TabExtension } from "./nodes/TabExtension.js";
|
|
56
57
|
import { TextBoxExtension } from "./nodes/TextBoxExtension.js";
|
|
58
|
+
import { TextBoxAnchorExtension } from "./nodes/TextBoxAnchorExtension.js";
|
|
57
59
|
//#region src/prosemirror/extensions/StarterKit.ts
|
|
58
60
|
/**
|
|
59
61
|
* Create the full set of extensions for the DOCX editor
|
|
@@ -101,10 +103,12 @@ function createStarterKit(options = {}) {
|
|
|
101
103
|
add("comment", CommentExtension());
|
|
102
104
|
add("insertion", InsertionExtension());
|
|
103
105
|
add("deletion", DeletionExtension());
|
|
106
|
+
add("runPropertyChange", RunPropertyChangeExtension());
|
|
104
107
|
add("hardBreak", HardBreakExtension());
|
|
105
108
|
add("tab", TabExtension());
|
|
106
109
|
add("image", ImageExtension());
|
|
107
110
|
add("textBox", TextBoxExtension());
|
|
111
|
+
add("textBoxAnchor", TextBoxAnchorExtension());
|
|
108
112
|
add("shape", ShapeExtension());
|
|
109
113
|
add("imageDrag", ImageDragExtension());
|
|
110
114
|
add("imagePaste", ImagePasteExtension());
|
|
@@ -112,6 +116,7 @@ function createStarterKit(options = {}) {
|
|
|
112
116
|
add("gapCursor", GapCursorExtension());
|
|
113
117
|
add("horizontalRule", HorizontalRuleExtension());
|
|
114
118
|
add("pageBreak", PageBreakExtension());
|
|
119
|
+
add("renderedPageBreak", RenderedPageBreakExtension());
|
|
115
120
|
add("field", FieldExtension());
|
|
116
121
|
add("sdt", SdtExtension());
|
|
117
122
|
add("blockSdt", BlockSdtExtension());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { autospacingMatchesBase } from "../../autospacingBase.js";
|
|
2
1
|
import { directionIsRtl } from "../../paragraphDirection.js";
|
|
3
2
|
import { expectParagraphAttrs } from "../../attrs/index.js";
|
|
3
|
+
import { autospacingMatchesBase } from "../../autospacingBase.js";
|
|
4
4
|
import { createNodeExtension } from "../create.js";
|
|
5
5
|
import { paragraphToStyle } from "../../../utils/formatToStyle.js";
|
|
6
6
|
import { collectHeadings } from "../../../utils/headingCollector.js";
|
|
@@ -187,8 +187,10 @@ const paragraphNodeSpec = {
|
|
|
187
187
|
spaceAfter: { default: null },
|
|
188
188
|
lineSpacing: { default: null },
|
|
189
189
|
lineSpacingRule: { default: null },
|
|
190
|
+
snapToGrid: { default: null },
|
|
190
191
|
spacingExplicit: { default: null },
|
|
191
192
|
spacingFromDocDefaults: { default: null },
|
|
193
|
+
spacingFromImplicitDefaultStyle: { default: null },
|
|
192
194
|
indentLeft: { default: null },
|
|
193
195
|
indentRight: { default: null },
|
|
194
196
|
indentFirstLine: { default: null },
|
|
@@ -219,6 +221,7 @@ const paragraphNodeSpec = {
|
|
|
219
221
|
pageBreakBefore: { default: null },
|
|
220
222
|
renderedPageBreakBefore: { default: null },
|
|
221
223
|
_pageBreakCarrier: { default: null },
|
|
224
|
+
_trailingPageBreak: { default: null },
|
|
222
225
|
keepNext: { default: null },
|
|
223
226
|
keepLines: { default: null },
|
|
224
227
|
widowControl: { default: null },
|
|
@@ -234,7 +237,8 @@ const paragraphNodeSpec = {
|
|
|
234
237
|
_autospacingBase: { default: null },
|
|
235
238
|
_sectionProperties: { default: null },
|
|
236
239
|
_propertyChanges: { default: null },
|
|
237
|
-
pPrMark: { default: null }
|
|
240
|
+
pPrMark: { default: null },
|
|
241
|
+
_suggestedInsert: { default: null }
|
|
238
242
|
},
|
|
239
243
|
parseDOM: [{
|
|
240
244
|
tag: "p",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createExtension } from "../create.js";
|
|
2
|
+
import { sanitizeImageSrc } from "../../../utils/sanitizeImageSrc.js";
|
|
2
3
|
import { getClipboardImageFiles } from "../../../utils/clipboard.js";
|
|
3
4
|
import { isSafeImageFile } from "../../../utils/imageValidation.js";
|
|
4
5
|
import { Plugin, TextSelection } from "prosemirror-state";
|
|
@@ -20,13 +21,18 @@ async function readFileAsDataUrl(file) {
|
|
|
20
21
|
}
|
|
21
22
|
async function loadImageSize(src) {
|
|
22
23
|
return await new Promise((resolve, reject) => {
|
|
24
|
+
const safeSrc = sanitizeImageSrc(src);
|
|
25
|
+
if (!safeSrc) {
|
|
26
|
+
reject(/* @__PURE__ */ new Error("Rejected unsafe image source"));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
23
29
|
const img = new Image();
|
|
24
30
|
img.addEventListener("load", () => resolve({
|
|
25
31
|
width: img.naturalWidth || 1,
|
|
26
32
|
height: img.naturalHeight || 1
|
|
27
33
|
}));
|
|
28
34
|
img.addEventListener("error", () => reject(/* @__PURE__ */ new Error("Failed to load pasted image")));
|
|
29
|
-
img.src =
|
|
35
|
+
img.src = safeSrc;
|
|
30
36
|
});
|
|
31
37
|
}
|
|
32
38
|
async function insertImageFiles(view, files) {
|
|
@@ -37,7 +43,9 @@ async function insertImageFiles(view, files) {
|
|
|
37
43
|
if (!await isSafeImageFile(file)) continue;
|
|
38
44
|
let dataUrl;
|
|
39
45
|
try {
|
|
40
|
-
|
|
46
|
+
const safeSrc = sanitizeImageSrc(await readFileAsDataUrl(file));
|
|
47
|
+
if (!safeSrc) continue;
|
|
48
|
+
dataUrl = safeSrc;
|
|
41
49
|
} catch {
|
|
42
50
|
continue;
|
|
43
51
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../../commands/propertyChangeScope.js";
|
|
2
1
|
import { Priority } from "../types.js";
|
|
3
2
|
import { createExtension } from "../create.js";
|
|
4
3
|
import { SUGGESTION_META, makeRevisionInfo } from "../../plugins/suggestionMode.js";
|
|
4
|
+
import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../../commands/propertyChangeScope.js";
|
|
5
5
|
import { goToNextCell, goToPrevCell } from "../nodes/TableExtension.js";
|
|
6
6
|
//#region src/prosemirror/extensions/features/ListExtension.ts
|
|
7
7
|
function chainCommands(...commands) {
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { Extension } from "../types.js";
|
|
2
2
|
import { EditorState, PluginKey } from "prosemirror-state";
|
|
3
|
+
import { Node } from "prosemirror-model";
|
|
3
4
|
|
|
4
5
|
//#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts
|
|
5
6
|
declare const paraIdAllocatorKey: PluginKey<any>;
|
|
7
|
+
/**
|
|
8
|
+
* Allocate initial paragraph IDs before constructing editor state.
|
|
9
|
+
*
|
|
10
|
+
* Rebuilding only changed branches avoids applying one ProseMirror transaction
|
|
11
|
+
* step per missing paragraph. Live edits still use the transaction-based plugin
|
|
12
|
+
* below so history, mappings, and duplicate ownership retain their semantics.
|
|
13
|
+
*/
|
|
14
|
+
declare const ensureParaIdsInDoc: (doc: Node) => Node;
|
|
6
15
|
declare const ensureParaIdsInState: (state: EditorState) => EditorState;
|
|
7
16
|
declare const ParaIdAllocatorExtension: (options?: Partial<{}> | undefined) => Extension;
|
|
8
17
|
//#endregion
|
|
9
|
-
export { ParaIdAllocatorExtension, ensureParaIdsInState, paraIdAllocatorKey };
|
|
18
|
+
export { ParaIdAllocatorExtension, ensureParaIdsInDoc, ensureParaIdsInState, paraIdAllocatorKey };
|
|
@@ -1,8 +1,46 @@
|
|
|
1
|
-
import { generateHexId } from "../../../utils/hexId.js";
|
|
2
1
|
import { createExtension } from "../create.js";
|
|
3
2
|
import { ignoreTrackedChanges } from "./ParagraphChangeTrackerExtension.js";
|
|
3
|
+
import { generateHexId } from "../../../utils/hexId.js";
|
|
4
|
+
import { Fragment } from "prosemirror-model";
|
|
4
5
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
5
6
|
//#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.ts
|
|
7
|
+
/**
|
|
8
|
+
* ParaIdAllocator — assigns a stable `w14:paraId` to every paragraph.
|
|
9
|
+
*
|
|
10
|
+
* Lifted from
|
|
11
|
+
* https://github.com/eigenpal/docx-editor/blob/main/packages/core/src/prosemirror/extensions/features/ParaIdAllocatorExtension.ts
|
|
12
|
+
* (Apache-2.0). Adapted to folio's `createExtension` + import style;
|
|
13
|
+
* keep behaviour in sync upstream. folio divergences: duplicate
|
|
14
|
+
* resolution maps the original paragraph's position through the
|
|
15
|
+
* transaction (see below), and the appended allocation transaction is
|
|
16
|
+
* excluded from paragraph change tracking like `ensureParaIdsInState`.
|
|
17
|
+
*
|
|
18
|
+
* Why: AI tooling, chat citation chips, and the change tracker all
|
|
19
|
+
* anchor on `paraId`. A paragraph with `paraId: null` is invisible
|
|
20
|
+
* to those surfaces; a duplicated paraId (the second half of an
|
|
21
|
+
* Enter-split, or content pasted from another doc) silently desyncs
|
|
22
|
+
* their anchors. This plugin closes both gaps by allocating fresh
|
|
23
|
+
* ids in an `appendTransaction` hook after every doc-changed step.
|
|
24
|
+
*
|
|
25
|
+
* Id lifecycle rules (locked by the co-located tests):
|
|
26
|
+
* - Missing/empty/reserved-zero id: a fresh random id is minted.
|
|
27
|
+
* - Split: the half holding the paragraph's original start position
|
|
28
|
+
* keeps the id; the other half gets a fresh one. Anchors inside the
|
|
29
|
+
* content stay resolvable on the half that carries them.
|
|
30
|
+
* - Merge/join: the surviving paragraph keeps its id; the absorbed id
|
|
31
|
+
* dangles by design (consumers degrade to snapshot fallback).
|
|
32
|
+
* - Paste/duplicate carrying an id already in the doc: the paragraph
|
|
33
|
+
* whose mapped pre-transaction position matches keeps it — pasting a
|
|
34
|
+
* copy above its source no longer steals the source's id. When no
|
|
35
|
+
* occurrence maps back (e.g. both are new), the first in document
|
|
36
|
+
* order keeps it.
|
|
37
|
+
* - Paste of an id unknown to this doc (cross-doc paste, cut-then-
|
|
38
|
+
* paste move): the id is kept, so a moved paragraph stays anchored.
|
|
39
|
+
* - Undo/redo: allocation applies with `addToHistory: false`, and
|
|
40
|
+
* ProseMirror remaps it into the originating history event. Redoing a
|
|
41
|
+
* split therefore restores the same allocated id instead of minting
|
|
42
|
+
* another one.
|
|
43
|
+
*/
|
|
6
44
|
const paraIdAllocatorKey = new PluginKey("paraIdAllocator");
|
|
7
45
|
const isUsableParaId = (value) => typeof value === "string" && value.length > 0 && value !== "00000000";
|
|
8
46
|
/**
|
|
@@ -76,6 +114,59 @@ const collectParaIdUpdates = (doc, keeperPositions) => {
|
|
|
76
114
|
updates.sort((a, b) => a.pos - b.pos);
|
|
77
115
|
return updates;
|
|
78
116
|
};
|
|
117
|
+
const collectInitialParaIds = (doc) => {
|
|
118
|
+
let needsRewrite = false;
|
|
119
|
+
const taken = /* @__PURE__ */ new Set();
|
|
120
|
+
doc.descendants((node) => {
|
|
121
|
+
if (node.type.name !== "paragraph") return true;
|
|
122
|
+
const id = node.attrs["paraId"];
|
|
123
|
+
if (!isUsableParaId(id) || taken.has(id)) needsRewrite = true;
|
|
124
|
+
else taken.add(id);
|
|
125
|
+
return false;
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
needsRewrite,
|
|
129
|
+
taken
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
const rewriteInitialParaIds = (parent, taken, seen) => {
|
|
133
|
+
let changed = false;
|
|
134
|
+
const children = [];
|
|
135
|
+
parent.forEach((child) => {
|
|
136
|
+
let next = child;
|
|
137
|
+
if (child.type.name === "paragraph") {
|
|
138
|
+
const id = child.attrs["paraId"];
|
|
139
|
+
if (!isUsableParaId(id) || seen.has(id)) {
|
|
140
|
+
let newId = generateHexId();
|
|
141
|
+
while (taken.has(newId)) newId = generateHexId();
|
|
142
|
+
taken.add(newId);
|
|
143
|
+
seen.add(newId);
|
|
144
|
+
next = child.type.create({
|
|
145
|
+
...child.attrs,
|
|
146
|
+
paraId: newId
|
|
147
|
+
}, child.content, child.marks);
|
|
148
|
+
} else seen.add(id);
|
|
149
|
+
} else if (child.childCount > 0) {
|
|
150
|
+
const content = rewriteInitialParaIds(child, taken, seen);
|
|
151
|
+
if (content !== child.content) next = child.copy(content);
|
|
152
|
+
}
|
|
153
|
+
if (next !== child) changed = true;
|
|
154
|
+
children.push(next);
|
|
155
|
+
});
|
|
156
|
+
return changed ? Fragment.fromArray(children) : parent.content;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Allocate initial paragraph IDs before constructing editor state.
|
|
160
|
+
*
|
|
161
|
+
* Rebuilding only changed branches avoids applying one ProseMirror transaction
|
|
162
|
+
* step per missing paragraph. Live edits still use the transaction-based plugin
|
|
163
|
+
* below so history, mappings, and duplicate ownership retain their semantics.
|
|
164
|
+
*/
|
|
165
|
+
const ensureParaIdsInDoc = (doc) => {
|
|
166
|
+
const { needsRewrite, taken } = collectInitialParaIds(doc);
|
|
167
|
+
if (!needsRewrite) return doc;
|
|
168
|
+
return doc.copy(rewriteInitialParaIds(doc, taken, /* @__PURE__ */ new Set()));
|
|
169
|
+
};
|
|
79
170
|
const ensureParaIdsInState = (state) => {
|
|
80
171
|
const updates = collectParaIdUpdates(state.doc);
|
|
81
172
|
if (updates.length === 0) return state;
|
|
@@ -109,4 +200,4 @@ const ParaIdAllocatorExtension = createExtension({
|
|
|
109
200
|
}
|
|
110
201
|
});
|
|
111
202
|
//#endregion
|
|
112
|
-
export { ParaIdAllocatorExtension, ensureParaIdsInState, paraIdAllocatorKey };
|
|
203
|
+
export { ParaIdAllocatorExtension, ensureParaIdsInDoc, ensureParaIdsInState, paraIdAllocatorKey };
|
|
@@ -30,6 +30,7 @@ declare function hasUntrackedChanges(state: EditorState): boolean;
|
|
|
30
30
|
*/
|
|
31
31
|
declare function clearTrackedChanges(state: EditorState): Transaction;
|
|
32
32
|
declare function ignoreTrackedChanges(tr: Transaction): Transaction;
|
|
33
|
+
declare function markStructuralChange(tr: Transaction): Transaction;
|
|
33
34
|
declare const ParagraphChangeTrackerExtension: (options?: Partial<{}> | undefined) => Extension;
|
|
34
35
|
//#endregion
|
|
35
|
-
export { ParagraphChangeTrackerExtension, ParagraphChangeTrackerState, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, paragraphChangeTrackerKey };
|
|
36
|
+
export { ParagraphChangeTrackerExtension, ParagraphChangeTrackerState, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, markStructuralChange, paragraphChangeTrackerKey };
|
|
@@ -12,6 +12,7 @@ import { AddMarkStep, AddNodeMarkStep, RemoveMarkStep, RemoveNodeMarkStep } from
|
|
|
12
12
|
const paragraphChangeTrackerKey = new PluginKey("paragraphChangeTracker");
|
|
13
13
|
const CLEAR_META = "clear";
|
|
14
14
|
const IGNORE_META = "ignore";
|
|
15
|
+
const STRUCTURAL_META = "structural";
|
|
15
16
|
/**
|
|
16
17
|
* Count paragraph nodes in a ProseMirror document
|
|
17
18
|
*/
|
|
@@ -103,7 +104,7 @@ function createParagraphChangeTrackerPlugin() {
|
|
|
103
104
|
const newCount = countParagraphs(tr.doc);
|
|
104
105
|
const newState = {
|
|
105
106
|
changedParaIds: new Set(prevState.changedParaIds),
|
|
106
|
-
structuralChange: prevState.structuralChange,
|
|
107
|
+
structuralChange: prevState.structuralChange || meta === STRUCTURAL_META,
|
|
107
108
|
hasUntrackedChanges: prevState.hasUntrackedChanges,
|
|
108
109
|
paragraphCount: newCount
|
|
109
110
|
};
|
|
@@ -177,6 +178,9 @@ function clearTrackedChanges(state) {
|
|
|
177
178
|
function ignoreTrackedChanges(tr) {
|
|
178
179
|
return tr.setMeta(paragraphChangeTrackerKey, IGNORE_META);
|
|
179
180
|
}
|
|
181
|
+
function markStructuralChange(tr) {
|
|
182
|
+
return tr.setMeta(paragraphChangeTrackerKey, STRUCTURAL_META);
|
|
183
|
+
}
|
|
180
184
|
const ParagraphChangeTrackerExtension = createExtension({
|
|
181
185
|
name: "paragraphChangeTracker",
|
|
182
186
|
defaultOptions: {},
|
|
@@ -185,4 +189,4 @@ const ParagraphChangeTrackerExtension = createExtension({
|
|
|
185
189
|
}
|
|
186
190
|
});
|
|
187
191
|
//#endregion
|
|
188
|
-
export { ParagraphChangeTrackerExtension, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, paragraphChangeTrackerKey };
|
|
192
|
+
export { ParagraphChangeTrackerExtension, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, markStructuralChange, paragraphChangeTrackerKey };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripXmlDeclarations } from "../../../utils/stripXmlDeclarations.js";
|
|
1
2
|
//#region src/prosemirror/extensions/features/pasteCleanup.ts
|
|
2
3
|
/**
|
|
3
4
|
* Office / web paste cleanup
|
|
@@ -112,11 +113,22 @@ function splitStyleDeclarations(value) {
|
|
|
112
113
|
}
|
|
113
114
|
const TAG_TAIL = `(?:"[^"]*"|'[^']*'|[^>"'])*`;
|
|
114
115
|
const NAMESPACED_TAG = new RegExp(`<\\/?(?:[owvm]|st\\d+):${TAG_TAIL}>`, "gi");
|
|
115
|
-
const XML_PROCESSING_INSTRUCTION = new RegExp(`<\\?xml${TAG_TAIL}>`, "gi");
|
|
116
116
|
const STRAY_XML_TAG = new RegExp(`<\\/?xml${TAG_TAIL}>`, "gi");
|
|
117
117
|
const NOISE_TAG = new RegExp(`<\\/?(?:font|meta|link)${TAG_TAIL}>`, "gi");
|
|
118
118
|
const EMPTY_SPAN = new RegExp(`<span(?:\\s${TAG_TAIL})?><\\/span>`, "gi");
|
|
119
119
|
const MAX_EMPTY_SPAN_PASSES = 5;
|
|
120
|
+
const PM_SLICE_MARKER = /\bdata-pm-slice\s*=/i;
|
|
121
|
+
const TEXTBOX_ANCHOR_ATTR = /\s+data-docx-textbox-anchor(?:="[^"]*"|='[^']*')?/gi;
|
|
122
|
+
/**
|
|
123
|
+
* Remove the `data-docx-textbox-anchor` marker from HTML that did not come
|
|
124
|
+
* from a ProseMirror clipboard slice (see {@link PM_SLICE_MARKER}). Internal
|
|
125
|
+
* copy/paste of a real text box carries the marker HTML unmodified so it
|
|
126
|
+
* keeps working; anything else has the marker stripped defensively.
|
|
127
|
+
*/
|
|
128
|
+
function stripForeignTextBoxAnchors(html, originalHtml) {
|
|
129
|
+
if (PM_SLICE_MARKER.test(originalHtml)) return html;
|
|
130
|
+
return html.replace(TEXTBOX_ANCHOR_ATTR, "");
|
|
131
|
+
}
|
|
120
132
|
/**
|
|
121
133
|
* Remove empty spans left behind after stripping `mso-*` styles. Only truly
|
|
122
134
|
* empty spans are removed (whitespace-only spans are kept so word-separating
|
|
@@ -144,12 +156,13 @@ function cleanPastedHtml(html) {
|
|
|
144
156
|
if (!html) return html;
|
|
145
157
|
try {
|
|
146
158
|
let cleaned = stripHtmlComments(html);
|
|
147
|
-
cleaned = cleaned
|
|
159
|
+
cleaned = stripXmlDeclarations(cleaned);
|
|
148
160
|
cleaned = cleaned.replace(NAMESPACED_TAG, "");
|
|
149
161
|
cleaned = cleaned.replace(STRAY_XML_TAG, "");
|
|
150
162
|
cleaned = cleaned.replace(NOISE_TAG, "");
|
|
151
163
|
cleaned = stripMsoStyles(cleaned);
|
|
152
164
|
cleaned = stripEmptySpans(cleaned);
|
|
165
|
+
cleaned = stripForeignTextBoxAnchors(cleaned, html);
|
|
153
166
|
return cleaned.trim();
|
|
154
167
|
} catch {
|
|
155
168
|
return html;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { formatPx, twipsToPixels } from "../../../utils/units.js";
|
|
2
1
|
import { expectCharacterSpacingMarkAttrs } from "../../attrs/index.js";
|
|
2
|
+
import { formatPx, twipsToPixels } from "../../../utils/units.js";
|
|
3
3
|
import { createMarkExtension } from "../create.js";
|
|
4
4
|
//#region src/prosemirror/extensions/marks/CharacterSpacingExtension.ts
|
|
5
5
|
/**
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { expectHyperlinkMarkAttrs } from "../../attrs/index.js";
|
|
2
2
|
import { createMarkExtension } from "../create.js";
|
|
3
3
|
import { isMarkActive } from "./markUtils.js";
|
|
4
|
+
import { normalizeUserUrl, sanitizeExternalUrl } from "../../../utils/urlSecurity.js";
|
|
4
5
|
import { panic } from "better-result";
|
|
5
6
|
//#region src/prosemirror/extensions/marks/HyperlinkExtension.ts
|
|
6
7
|
/**
|
|
7
8
|
* Hyperlink Mark Extension
|
|
8
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* Sanitize an href arriving from (or being emitted to) the DOM: pasted HTML,
|
|
12
|
+
* clipboard content, or a mark attr already stored on the document. Only
|
|
13
|
+
* allow-listed absolute URLs (http/https/mailto/tel) and internal bookmark
|
|
14
|
+
* anchors survive; everything else (javascript:, data:, file:, ...) becomes
|
|
15
|
+
* an empty href, matching how the codebase already signals "no link" for an
|
|
16
|
+
* unresolved hyperlink (see `toProseDoc.ts`'s `hyperlink.href || ""`).
|
|
17
|
+
*/
|
|
18
|
+
function sanitizeStoredHref(rawHref) {
|
|
19
|
+
if (!rawHref) return "";
|
|
20
|
+
const trimmed = rawHref.trim();
|
|
21
|
+
if (trimmed.startsWith("#")) return trimmed;
|
|
22
|
+
return sanitizeExternalUrl(trimmed) ?? "";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalize/sanitize a URL typed by a user (hyperlink dialog/popup) before it
|
|
26
|
+
* is written into a mark. Protocol-less input (e.g. "example.com") is
|
|
27
|
+
* accepted and normalized to https; anything resolving to a disallowed
|
|
28
|
+
* scheme is dropped.
|
|
29
|
+
*/
|
|
30
|
+
function normalizeHyperlinkInput(rawHref) {
|
|
31
|
+
const trimmed = rawHref.trim();
|
|
32
|
+
if (trimmed.startsWith("#")) return trimmed;
|
|
33
|
+
return normalizeUserUrl(trimmed);
|
|
34
|
+
}
|
|
9
35
|
function isHyperlinkActive(state) {
|
|
10
36
|
const hlType = state.schema.marks["hyperlink"];
|
|
11
37
|
if (!hlType) return false;
|
|
@@ -62,14 +88,14 @@ const HyperlinkExtension = createMarkExtension({
|
|
|
62
88
|
parseDOM: [{
|
|
63
89
|
tag: "a[href]",
|
|
64
90
|
getAttrs: (dom) => ({
|
|
65
|
-
href: dom.getAttribute("href") ??
|
|
91
|
+
href: sanitizeStoredHref(dom.getAttribute("href") ?? void 0),
|
|
66
92
|
tooltip: dom.getAttribute("title") ?? void 0
|
|
67
93
|
})
|
|
68
94
|
}],
|
|
69
95
|
toDOM(mark) {
|
|
70
96
|
const { href, tooltip } = expectHyperlinkMarkAttrs(mark);
|
|
71
97
|
const domAttrs = {
|
|
72
|
-
href,
|
|
98
|
+
href: sanitizeStoredHref(href),
|
|
73
99
|
target: "_blank",
|
|
74
100
|
rel: "noopener noreferrer"
|
|
75
101
|
};
|
|
@@ -89,7 +115,7 @@ const HyperlinkExtension = createMarkExtension({
|
|
|
89
115
|
if (empty) return false;
|
|
90
116
|
if (dispatch) {
|
|
91
117
|
const mark = hlType.create({
|
|
92
|
-
href,
|
|
118
|
+
href: normalizeHyperlinkInput(href),
|
|
93
119
|
tooltip: tooltip || null
|
|
94
120
|
});
|
|
95
121
|
let tr = state.tr.addMark(from, to, mark);
|
|
@@ -127,7 +153,7 @@ const HyperlinkExtension = createMarkExtension({
|
|
|
127
153
|
const insertHyperlink = (text, href, tooltip) => (state, dispatch) => {
|
|
128
154
|
if (dispatch) {
|
|
129
155
|
const mark = hlType.create({
|
|
130
|
-
href,
|
|
156
|
+
href: normalizeHyperlinkInput(href),
|
|
131
157
|
tooltip: tooltip || null
|
|
132
158
|
});
|
|
133
159
|
const textNode = state.schema.text(text, [mark]);
|
|
@@ -18,5 +18,13 @@ declare const InsertionExtension: (options?: Partial<Record<string, unknown>> |
|
|
|
18
18
|
* Renders with per-author colored strikethrough.
|
|
19
19
|
*/
|
|
20
20
|
declare const DeletionExtension: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
|
|
21
|
+
/**
|
|
22
|
+
* Run property change — formatting changed while review tracking was active.
|
|
23
|
+
*
|
|
24
|
+
* The current formatting remains represented by the normal formatting marks;
|
|
25
|
+
* this mark carries the previous run properties and revision metadata needed
|
|
26
|
+
* to serialize, list, accept, and reject the change.
|
|
27
|
+
*/
|
|
28
|
+
declare const RunPropertyChangeExtension: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
|
|
21
29
|
//#endregion
|
|
22
|
-
export { DeletionExtension, InsertionExtension };
|
|
30
|
+
export { DeletionExtension, InsertionExtension, RunPropertyChangeExtension };
|
|
@@ -18,6 +18,20 @@ import { AUTHOR_COLORS, getAuthorColorIdx } from "../../../utils/authorColors.js
|
|
|
18
18
|
const insertionStyle = (color) => `color: ${color}; text-decoration: underline; text-decoration-color: ${color};`;
|
|
19
19
|
const deletionStyle = (color) => `color: ${color}; text-decoration: line-through; text-decoration-color: ${color};`;
|
|
20
20
|
/**
|
|
21
|
+
* Suggested (AI-proposed) tracked changes reuse the insertion/deletion visual
|
|
22
|
+
* grammar but with a DOTTED stroke and a dedicated suggestion hue, kept
|
|
23
|
+
* distinct from the per-author redline colors. The hue and tint are driven by
|
|
24
|
+
* the `--suggestion-color` / `--suggestion-bg` CSS custom properties (see
|
|
25
|
+
* editor.css); the inline fallbacks below keep the paged-canvas projection —
|
|
26
|
+
* which reuses this same `toDOM` style string — legible when the stylesheet
|
|
27
|
+
* variables are not in scope.
|
|
28
|
+
*/
|
|
29
|
+
const SUGGESTION_COLOR = "var(--suggestion-color, #6d3bd6)";
|
|
30
|
+
const SUGGESTION_TINT = "var(--suggestion-bg, color-mix(in oklch, #6d3bd6 12%, transparent))";
|
|
31
|
+
const SUGGESTION_TINT_LAYER = `linear-gradient(${SUGGESTION_TINT}, ${SUGGESTION_TINT})`;
|
|
32
|
+
const suggestedInsertionStyle = () => `color: ${SUGGESTION_COLOR}; text-decoration: underline; text-decoration-style: dotted; text-decoration-color: ${SUGGESTION_COLOR}; background-image: ${SUGGESTION_TINT_LAYER};`;
|
|
33
|
+
const suggestedDeletionStyle = () => `color: ${SUGGESTION_COLOR}; text-decoration: line-through; text-decoration-style: dotted; text-decoration-color: ${SUGGESTION_COLOR}; background-image: ${SUGGESTION_TINT_LAYER};`;
|
|
34
|
+
/**
|
|
21
35
|
* Insertion mark — text added in tracked changes
|
|
22
36
|
* Renders with per-author colored underline.
|
|
23
37
|
*/
|
|
@@ -29,7 +43,10 @@ const InsertionExtension = createMarkExtension({
|
|
|
29
43
|
revisionId: { default: 0 },
|
|
30
44
|
author: { default: "" },
|
|
31
45
|
date: { default: null },
|
|
32
|
-
moveKind: { default: null }
|
|
46
|
+
moveKind: { default: null },
|
|
47
|
+
initials: { default: null },
|
|
48
|
+
provenance: { default: "user" },
|
|
49
|
+
suggestionId: { default: null }
|
|
33
50
|
},
|
|
34
51
|
inclusive: false,
|
|
35
52
|
parseDOM: [{
|
|
@@ -43,20 +60,23 @@ const InsertionExtension = createMarkExtension({
|
|
|
43
60
|
}
|
|
44
61
|
}],
|
|
45
62
|
toDOM(mark) {
|
|
46
|
-
const { revisionId, author, date } = expectTrackedChangeMarkAttrs(mark);
|
|
63
|
+
const { revisionId, author, date, provenance, suggestionId } = expectTrackedChangeMarkAttrs(mark);
|
|
47
64
|
const idx = getAuthorColorIdx(author);
|
|
48
65
|
const color = AUTHOR_COLORS[idx] ?? "#000000";
|
|
49
66
|
const titleParts = [author, date ? new Date(date).toLocaleDateString() : ""].filter(Boolean);
|
|
67
|
+
const suggested = provenance === "suggested";
|
|
50
68
|
return [
|
|
51
69
|
"span",
|
|
52
70
|
{
|
|
53
|
-
class: "docx-insertion",
|
|
71
|
+
class: suggested ? "docx-insertion docx-insertion--suggested" : "docx-insertion",
|
|
54
72
|
"data-revision-id": String(revisionId),
|
|
55
73
|
"data-author": author,
|
|
56
74
|
"data-tc-author-idx": String(idx),
|
|
75
|
+
...suggested ? { "data-provenance": "suggested" } : {},
|
|
76
|
+
...suggested && suggestionId ? { "data-suggestion-id": suggestionId } : {},
|
|
57
77
|
...date ? { "data-date": date } : {},
|
|
58
|
-
...titleParts.length > 0 ? { title:
|
|
59
|
-
style: insertionStyle(color)
|
|
78
|
+
...titleParts.length > 0 ? { title: `${suggested ? "Suggested" : "Inserted"}: ${titleParts.join(", ")}` } : {},
|
|
79
|
+
style: suggested ? suggestedInsertionStyle() : insertionStyle(color)
|
|
60
80
|
},
|
|
61
81
|
0
|
|
62
82
|
];
|
|
@@ -75,7 +95,10 @@ const DeletionExtension = createMarkExtension({
|
|
|
75
95
|
revisionId: { default: 0 },
|
|
76
96
|
author: { default: "" },
|
|
77
97
|
date: { default: null },
|
|
78
|
-
moveKind: { default: null }
|
|
98
|
+
moveKind: { default: null },
|
|
99
|
+
initials: { default: null },
|
|
100
|
+
provenance: { default: "user" },
|
|
101
|
+
suggestionId: { default: null }
|
|
79
102
|
},
|
|
80
103
|
inclusive: false,
|
|
81
104
|
parseDOM: [{
|
|
@@ -89,20 +112,56 @@ const DeletionExtension = createMarkExtension({
|
|
|
89
112
|
}
|
|
90
113
|
}],
|
|
91
114
|
toDOM(mark) {
|
|
92
|
-
const { revisionId, author, date } = expectTrackedChangeMarkAttrs(mark);
|
|
115
|
+
const { revisionId, author, date, provenance, suggestionId } = expectTrackedChangeMarkAttrs(mark);
|
|
93
116
|
const idx = getAuthorColorIdx(author);
|
|
94
117
|
const color = AUTHOR_COLORS[idx] ?? "#000000";
|
|
95
118
|
const titleParts = [author, date ? new Date(date).toLocaleDateString() : ""].filter(Boolean);
|
|
119
|
+
const suggested = provenance === "suggested";
|
|
96
120
|
return [
|
|
97
121
|
"span",
|
|
98
122
|
{
|
|
99
|
-
class: "docx-deletion",
|
|
123
|
+
class: suggested ? "docx-deletion docx-deletion--suggested" : "docx-deletion",
|
|
100
124
|
"data-revision-id": String(revisionId),
|
|
101
125
|
"data-author": author,
|
|
102
126
|
"data-tc-author-idx": String(idx),
|
|
127
|
+
...suggested ? { "data-provenance": "suggested" } : {},
|
|
128
|
+
...suggested && suggestionId ? { "data-suggestion-id": suggestionId } : {},
|
|
103
129
|
...date ? { "data-date": date } : {},
|
|
104
|
-
...titleParts.length > 0 ? { title:
|
|
105
|
-
style: deletionStyle(color)
|
|
130
|
+
...titleParts.length > 0 ? { title: `${suggested ? "Suggested deletion" : "Deleted"}: ${titleParts.join(", ")}` } : {},
|
|
131
|
+
style: suggested ? suggestedDeletionStyle() : deletionStyle(color)
|
|
132
|
+
},
|
|
133
|
+
0
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* Run property change — formatting changed while review tracking was active.
|
|
140
|
+
*
|
|
141
|
+
* The current formatting remains represented by the normal formatting marks;
|
|
142
|
+
* this mark carries the previous run properties and revision metadata needed
|
|
143
|
+
* to serialize, list, accept, and reject the change.
|
|
144
|
+
*/
|
|
145
|
+
const RunPropertyChangeExtension = createMarkExtension({
|
|
146
|
+
name: "runPropertyChange",
|
|
147
|
+
schemaMarkName: "runPropertyChange",
|
|
148
|
+
markSpec: {
|
|
149
|
+
attrs: {
|
|
150
|
+
changes: { default: [] },
|
|
151
|
+
provenance: { default: "user" },
|
|
152
|
+
suggestionId: { default: null }
|
|
153
|
+
},
|
|
154
|
+
inclusive: false,
|
|
155
|
+
parseDOM: [{ tag: "span.docx-run-property-change" }],
|
|
156
|
+
toDOM(mark) {
|
|
157
|
+
const suggested = mark.attrs["provenance"] === "suggested";
|
|
158
|
+
const suggestionId = mark.attrs["suggestionId"];
|
|
159
|
+
return [
|
|
160
|
+
"span",
|
|
161
|
+
{
|
|
162
|
+
class: suggested ? "docx-run-property-change docx-run-property-change--suggested" : "docx-run-property-change",
|
|
163
|
+
...suggested ? { "data-provenance": "suggested" } : {},
|
|
164
|
+
...suggested && typeof suggestionId === "string" ? { "data-suggestion-id": suggestionId } : {}
|
|
106
165
|
},
|
|
107
166
|
0
|
|
108
167
|
];
|
|
@@ -110,4 +169,4 @@ const DeletionExtension = createMarkExtension({
|
|
|
110
169
|
}
|
|
111
170
|
});
|
|
112
171
|
//#endregion
|
|
113
|
-
export { DeletionExtension, InsertionExtension };
|
|
172
|
+
export { DeletionExtension, InsertionExtension, RunPropertyChangeExtension };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { parseFieldInstruction } from "../../../docx/fieldParser.js";
|
|
2
1
|
import { expectFieldAttrs } from "../../attrs/index.js";
|
|
3
2
|
import { createNodeExtension } from "../create.js";
|
|
3
|
+
import { parseFieldInstruction } from "../../../docx/fieldParser.js";
|
|
4
4
|
//#region src/prosemirror/extensions/nodes/FieldExtension.ts
|
|
5
5
|
/**
|
|
6
6
|
* Field Extension — inline field node (PAGE, NUMPAGES, DATE, MERGEFIELD, etc.)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NodeExtension } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts
|
|
4
|
+
/** Zero-width cached pagination boundary preserved from DOCX. */
|
|
5
|
+
declare const RenderedPageBreakExtension: (options?: Partial<Record<string, unknown>> | undefined) => NodeExtension;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { RenderedPageBreakExtension };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createNodeExtension } from "../create.js";
|
|
2
|
+
//#region src/prosemirror/extensions/nodes/RenderedPageBreakExtension.ts
|
|
3
|
+
/** Zero-width cached pagination boundary preserved from DOCX. */
|
|
4
|
+
const RenderedPageBreakExtension = createNodeExtension({
|
|
5
|
+
name: "renderedPageBreak",
|
|
6
|
+
schemaNodeName: "renderedPageBreak",
|
|
7
|
+
nodeSpec: {
|
|
8
|
+
inline: true,
|
|
9
|
+
group: "inline",
|
|
10
|
+
atom: true,
|
|
11
|
+
selectable: false,
|
|
12
|
+
parseDOM: [{ tag: "span[data-docx-rendered-page-break]" }],
|
|
13
|
+
toDOM() {
|
|
14
|
+
return ["span", {
|
|
15
|
+
"data-docx-rendered-page-break": "true",
|
|
16
|
+
style: "display: none;"
|
|
17
|
+
}];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
//#endregion
|
|
22
|
+
export { RenderedPageBreakExtension };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AnyExtension, Extension, NodeExtension } from "../types.js";
|
|
2
1
|
import { ColorValue } from "../../../types/colors.js";
|
|
2
|
+
import { AnyExtension, Extension, NodeExtension } from "../types.js";
|
|
3
3
|
import { Command, EditorState } from "prosemirror-state";
|
|
4
4
|
import { Node } from "prosemirror-model";
|
|
5
5
|
|