@stll/folio-core 0.36.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -1
- package/dist/ai-edits/apply.js +689 -367
- package/dist/ai-edits/block-identity.d.ts +4 -1
- package/dist/ai-edits/block-identity.js +4 -1
- package/dist/ai-edits/blockRange.js +14 -14
- package/dist/ai-edits/clean-text.d.ts +40 -1
- package/dist/ai-edits/clean-text.js +52 -2
- package/dist/ai-edits/headless.d.ts +3 -1
- package/dist/ai-edits/headless.js +133 -71
- package/dist/ai-edits/index.d.ts +3 -3
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.js +38 -20
- package/dist/ai-edits/snapshot.d.ts +14 -6
- package/dist/ai-edits/snapshot.js +99 -19
- package/dist/ai-edits/table-cell-mutations.js +1 -1
- package/dist/ai-edits/table-row-column-mutations.js +7 -0
- package/dist/ai-edits/table-template.d.ts +11 -1
- package/dist/ai-edits/table-template.js +49 -17
- package/dist/ai-edits/types.d.ts +60 -57
- package/dist/ai-suggestions/conflict.js +1 -4
- package/dist/ai-suggestions/text-positions.d.ts +8 -0
- package/dist/ai-suggestions/text-positions.js +27 -5
- package/dist/compare/__fixtures__/body-sequence.js +3 -4
- package/dist/compare/column-alignment.d.ts +13 -13
- package/dist/compare/column-alignment.js +78 -39
- package/dist/compare/compare.d.ts +4 -3
- package/dist/compare/compare.js +41 -5
- package/dist/compare/content-alignment.d.ts +95 -0
- package/dist/compare/content-alignment.js +1450 -0
- package/dist/compare/content-types.d.ts +100 -0
- package/dist/compare/content-types.js +0 -0
- package/dist/compare/content.d.ts +209 -0
- package/dist/compare/content.js +885 -0
- package/dist/compare/formatting.d.ts +5 -5
- package/dist/compare/formatting.js +6 -4
- package/dist/compare/plan.d.ts +6 -1
- package/dist/compare/plan.js +201 -569
- package/dist/compare/scenario.d.ts +2 -2
- package/dist/compare/types.d.ts +3 -3
- package/dist/compare/verification.d.ts +2 -2
- package/dist/compare/verification.js +25 -8
- package/dist/compat/eigenpal.d.ts +5 -3
- package/dist/compat/eigenpal.js +3 -2
- package/dist/controller/headerFooterEditorManager.js +14 -6
- package/dist/controller/hiddenEditorManager.d.ts +7 -2
- package/dist/controller/hiddenEditorManager.js +15 -6
- package/dist/controller/layoutPipeline.js +5 -3
- package/dist/controller/noteEditorManager.js +12 -7
- package/dist/display-list/build/headerFooterPrimitives.js +1 -0
- package/dist/display-list/build/storyPrimitives.d.ts +5 -2
- package/dist/display-list/build/storyPrimitives.js +60 -3
- package/dist/display-list/build/tablePrimitives.js +30 -0
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/document-operations.d.ts +8 -6
- package/dist/document-operations.js +121 -26
- package/dist/docx/blockContentParser.js +6 -27
- package/dist/docx/ensureParaIds.js +9 -4
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +4 -1
- package/dist/docx/paragraphParser.js +7 -6
- package/dist/docx/paragraphTraversal.js +30 -34
- package/dist/docx/rezip.d.ts +0 -8
- package/dist/docx/rezip.js +41 -16
- package/dist/docx/runParser.js +4 -2
- package/dist/docx/serializer/paragraphSerializer.js +2 -0
- package/dist/docx/serializer/runSerializer.js +5 -3
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +3 -1
- package/dist/docx/serializer/trackedChangeAttributes.js +11 -1
- package/dist/docx/server/build.d.ts +1 -1
- package/dist/headless-layout.js +5 -4
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/internal/headlessRevisionResolution.d.ts +23 -0
- package/dist/internal/headlessRevisionResolution.js +176 -0
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
- package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
- package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
- package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
- package/dist/internal/pageBreakRunPartition.d.ts +26 -0
- package/dist/internal/pageBreakRunPartition.js +35 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
- package/dist/internal/sectionEndpointResolution.d.ts +32 -0
- package/dist/internal/sectionEndpointResolution.js +38 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
- package/dist/layout-bridge/convert/headerFooterLayout.js +48 -5
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +379 -75
- package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
- package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
- package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
- package/dist/layout-bridge/dom/textStreamDom.js +75 -0
- package/dist/layout-bridge/headerFooterLayout.js +16 -30
- package/dist/layout-engine/index.js +12 -3
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +7 -0
- package/dist/layout-painter/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderTable.js +20 -0
- package/dist/model.d.ts +1 -1
- package/dist/paged-layout/rangeProjection.js +6 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
- package/dist/paged-layout/transactionDirtyRange.js +23 -1
- package/dist/prosemirror/attrs/index.d.ts +7 -3
- package/dist/prosemirror/attrs/index.js +113 -7
- package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
- package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
- package/dist/prosemirror/commands/comments.d.ts +10 -1
- package/dist/prosemirror/commands/comments.js +167 -59
- package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
- package/dist/prosemirror/commands/formatPainter.js +319 -63
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
- package/dist/prosemirror/commands/pageBreak.js +18 -36
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +7 -5
- package/dist/prosemirror/commands/propertyChangeScope.js +27 -18
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +7 -2
- package/dist/prosemirror/conversion/fromProseDoc.js +809 -209
- package/dist/prosemirror/conversion/runShadingMark.js +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
- package/dist/prosemirror/conversion/toProseDoc.js +416 -283
- package/dist/prosemirror/extensions/StarterKit.js +4 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -8
- package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
- package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
- package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
- package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
- package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
- package/dist/prosemirror/findReplaceSelection.js +8 -3
- package/dist/prosemirror/index.d.ts +3 -3
- package/dist/prosemirror/listMarker.d.ts +26 -1
- package/dist/prosemirror/listMarker.js +108 -14
- package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
- package/dist/prosemirror/pageBreakRunProjection.js +47 -0
- package/dist/prosemirror/paraText.js +6 -0
- package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
- package/dist/prosemirror/paragraphSpacing.js +125 -0
- package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
- package/dist/prosemirror/plugins/documentNumbering.js +18 -0
- package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
- package/dist/prosemirror/plugins/pmTextScan.js +14 -4
- package/dist/prosemirror/plugins/suggestionMode.js +8 -10
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
- package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
- package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
- package/dist/prosemirror/runFormattingProvenance.js +48 -0
- package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
- package/dist/prosemirror/runFormattingReconciliation.js +54 -0
- package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
- package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
- package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
- package/dist/prosemirror/runStyleFormatting.js +105 -0
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +82 -9
- package/dist/prosemirror/schema/marks.js +73 -1
- package/dist/prosemirror/schema/nodes.d.ts +18 -5
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +44 -23
- package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
- package/dist/prosemirror/styles/styleResolver.js +3 -3
- package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
- package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
- package/dist/prosemirror/tableGridMutation.d.ts +20 -0
- package/dist/prosemirror/tableGridMutation.js +30 -0
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
- package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
- package/dist/prosemirror/validation.js +23 -4
- package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
- package/dist/render-dom/RenderedDomContext.js +17 -34
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/style-engine/styleEngine.d.ts +1 -1
- package/dist/utils/clipboard.js +1 -1
- package/dist/utils/findReplace.js +38 -21
- package/dist/version-comparison.d.ts +38 -44
- package/dist/version-comparison.js +270 -401
- package/package.json +3 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { expectParagraphAttrs } from "./attrs/index.js";
|
|
2
|
+
//#region src/prosemirror/pageBreakRunProjection.ts
|
|
3
|
+
const PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES = {
|
|
4
|
+
borders: "A bordered paragraph containing an explicit page-break run cannot be projected",
|
|
5
|
+
frame: "A framed paragraph containing an explicit page-break run cannot be projected",
|
|
6
|
+
outline: "An outline paragraph containing an explicit page-break run cannot be projected",
|
|
7
|
+
textBoxAnchor: "A paragraph containing both an explicit page-break run and a text-box anchor cannot be projected"
|
|
8
|
+
};
|
|
9
|
+
/** Keep source-import and ProseMirror-layout ownership decisions on one predicate. */
|
|
10
|
+
const pageBreakRunParagraphProjectionDispositionForFeatures = ({ attrs, effectiveFrame, hasTextBoxAnchor }) => {
|
|
11
|
+
if (effectiveFrame !== void 0 && effectiveFrame.dropCap !== "drop" && effectiveFrame.dropCap !== "margin") return {
|
|
12
|
+
status: "unsupported",
|
|
13
|
+
reason: "frame",
|
|
14
|
+
message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.frame
|
|
15
|
+
};
|
|
16
|
+
if (attrs.outlineLevel !== void 0) return {
|
|
17
|
+
status: "unsupported",
|
|
18
|
+
reason: "outline",
|
|
19
|
+
message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.outline
|
|
20
|
+
};
|
|
21
|
+
if (attrs.borders !== void 0 && Object.values(attrs.borders).some((border) => border !== void 0)) return {
|
|
22
|
+
status: "unsupported",
|
|
23
|
+
reason: "borders",
|
|
24
|
+
message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.borders
|
|
25
|
+
};
|
|
26
|
+
if (hasTextBoxAnchor) return {
|
|
27
|
+
status: "unsupported",
|
|
28
|
+
reason: "textBoxAnchor",
|
|
29
|
+
message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.textBoxAnchor
|
|
30
|
+
};
|
|
31
|
+
return { status: "supported" };
|
|
32
|
+
};
|
|
33
|
+
const pageBreakRunParagraphProjectionDisposition = (paragraph) => {
|
|
34
|
+
const attrs = expectParagraphAttrs(paragraph);
|
|
35
|
+
let hasTextBoxAnchor = false;
|
|
36
|
+
paragraph.descendants((descendant) => {
|
|
37
|
+
hasTextBoxAnchor ||= descendant.type.name === "textBoxAnchor";
|
|
38
|
+
return !hasTextBoxAnchor;
|
|
39
|
+
});
|
|
40
|
+
return pageBreakRunParagraphProjectionDispositionForFeatures({
|
|
41
|
+
attrs,
|
|
42
|
+
effectiveFrame: attrs._originalFormatting?.frame,
|
|
43
|
+
hasTextBoxAnchor
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { pageBreakRunParagraphProjectionDisposition, pageBreakRunParagraphProjectionDispositionForFeatures };
|
|
@@ -53,7 +53,12 @@ function findTextInPmParagraph(doc, paragraphFrom, paragraphTo, searchText) {
|
|
|
53
53
|
if (!searchText) return null;
|
|
54
54
|
let fullText = "";
|
|
55
55
|
const textPositions = [];
|
|
56
|
+
const structuralBoundaryOffsets = [];
|
|
56
57
|
doc.nodesBetween(paragraphFrom, paragraphTo, (node, pos) => {
|
|
58
|
+
if (node.type.name === "pageBreakRun") {
|
|
59
|
+
structuralBoundaryOffsets.push(fullText.length);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
57
62
|
if (node.marks.some((m) => m.type.name === "insertion")) return false;
|
|
58
63
|
if (node.isText && node.text) {
|
|
59
64
|
textPositions.push({
|
|
@@ -81,6 +86,7 @@ function findTextInPmParagraph(doc, paragraphFrom, paragraphTo, searchText) {
|
|
|
81
86
|
if (firstMatch === -1) return null;
|
|
82
87
|
if (fullText.indexOf(searchText, firstMatch + 1) !== -1) return null;
|
|
83
88
|
const matchEnd = firstMatch + searchText.length;
|
|
89
|
+
if (structuralBoundaryOffsets.some((offset) => offset > firstMatch && offset < matchEnd)) return null;
|
|
84
90
|
let segmentStart = 0;
|
|
85
91
|
for (const position of textPositions) {
|
|
86
92
|
const segmentEnd = segmentStart + position.text.length;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { FolioAIParagraphSpacing } from "../ai-edits/types.js";
|
|
3
|
+
import { ParagraphAttrs } from "./schema/nodes.js";
|
|
4
|
+
//#region src/prosemirror/paragraphSpacing.d.ts
|
|
5
|
+
/** The complete modeled attribute set of one direct `w:pPr/w:spacing`. */
|
|
6
|
+
declare const DIRECT_PARAGRAPH_SPACING_KEYS: readonly ["spaceBefore", "spaceAfter", "lineSpacing", "lineSpacingRule", "beforeAutospacing", "afterAutospacing"];
|
|
7
|
+
type DirectParagraphSpacing = FolioAIParagraphSpacing;
|
|
8
|
+
type ExactLineSpacingProvenance = Exclude<NonNullable<ParagraphAttrs["lineSpacingExplicit"]>, boolean>;
|
|
9
|
+
declare const lineSpacingProvenanceFromSpacing: (spacing: DirectParagraphSpacing | null | undefined) => ExactLineSpacingProvenance | undefined;
|
|
10
|
+
/** Project only authored `w:spacing` attributes, preserving explicit zero and false. */
|
|
11
|
+
declare const paragraphSpacingFromFormatting: (formatting: DirectParagraphSpacing | null | undefined) => DirectParagraphSpacing | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Read direct spacing independently of the effective values used for layout.
|
|
14
|
+
* Imported and command-authored paragraphs keep their source in
|
|
15
|
+
* `_originalFormatting`; explicit PM attrs cover newly created content.
|
|
16
|
+
*/
|
|
17
|
+
declare const directParagraphSpacing: (attrs: ParagraphAttrs) => DirectParagraphSpacing | undefined;
|
|
18
|
+
/** Structural equality for the canonical, fixed-order spacing projection. */
|
|
19
|
+
declare const paragraphSpacingEqual: (left: DirectParagraphSpacing | null | undefined, right: DirectParagraphSpacing | null | undefined) => boolean;
|
|
20
|
+
/** Replace the complete direct `w:spacing` cluster in canonical formatting. */
|
|
21
|
+
declare const withDirectParagraphSpacing: (formatting: document_d_exports.ParagraphFormatting | null | undefined, spacing: DirectParagraphSpacing | null | undefined) => document_d_exports.ParagraphFormatting | undefined;
|
|
22
|
+
type ParagraphSpacingAttrPatchOptions = {
|
|
23
|
+
direct: DirectParagraphSpacing | null | undefined;
|
|
24
|
+
inherited: DirectParagraphSpacing | null | undefined;
|
|
25
|
+
};
|
|
26
|
+
/** Project direct-over-inherited spacing into the attrs consumed by layout. */
|
|
27
|
+
declare const paragraphSpacingAttrPatch: ({ direct, inherited }: ParagraphSpacingAttrPatchOptions) => Record<string, unknown>;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { DIRECT_PARAGRAPH_SPACING_KEYS, DirectParagraphSpacing, directParagraphSpacing, lineSpacingProvenanceFromSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { autospacingMatchesBase, hasAutospacingBaseSide, setAutospacingBaseValue } from "./autospacingBase.js";
|
|
2
|
+
//#region src/prosemirror/paragraphSpacing.ts
|
|
3
|
+
/** The complete modeled attribute set of one direct `w:pPr/w:spacing`. */
|
|
4
|
+
const DIRECT_PARAGRAPH_SPACING_KEYS = [
|
|
5
|
+
"spaceBefore",
|
|
6
|
+
"spaceAfter",
|
|
7
|
+
"lineSpacing",
|
|
8
|
+
"lineSpacingRule",
|
|
9
|
+
"beforeAutospacing",
|
|
10
|
+
"afterAutospacing"
|
|
11
|
+
];
|
|
12
|
+
const lineSpacingProvenanceFromSpacing = (spacing) => {
|
|
13
|
+
const hasValue = spacing?.lineSpacing !== void 0;
|
|
14
|
+
const hasRule = spacing?.lineSpacingRule !== void 0;
|
|
15
|
+
if (hasValue && hasRule) return "both";
|
|
16
|
+
if (hasValue) return "value";
|
|
17
|
+
return hasRule ? "rule" : void 0;
|
|
18
|
+
};
|
|
19
|
+
/** Resolve exact provenance, including editor states written with the former boolean marker. */
|
|
20
|
+
const lineSpacingProvenance = (attrs) => {
|
|
21
|
+
if (typeof attrs.lineSpacingExplicit === "string") return attrs.lineSpacingExplicit;
|
|
22
|
+
const original = paragraphSpacingFromFormatting(attrs._originalFormatting);
|
|
23
|
+
const originalProvenance = lineSpacingProvenanceFromSpacing(original);
|
|
24
|
+
if (originalProvenance !== void 0 || attrs.lineSpacingExplicit !== true) return originalProvenance;
|
|
25
|
+
return lineSpacingProvenanceFromSpacing({
|
|
26
|
+
...typeof attrs.lineSpacing === "number" ? { lineSpacing: attrs.lineSpacing } : {},
|
|
27
|
+
...attrs.lineSpacingRule !== void 0 ? { lineSpacingRule: attrs.lineSpacingRule } : {}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
/** Project only authored `w:spacing` attributes, preserving explicit zero and false. */
|
|
31
|
+
const paragraphSpacingFromFormatting = (formatting) => {
|
|
32
|
+
if (!formatting) return;
|
|
33
|
+
const spacing = {
|
|
34
|
+
...formatting.spaceBefore !== void 0 ? { spaceBefore: formatting.spaceBefore } : {},
|
|
35
|
+
...formatting.spaceAfter !== void 0 ? { spaceAfter: formatting.spaceAfter } : {},
|
|
36
|
+
...formatting.lineSpacing !== void 0 ? { lineSpacing: formatting.lineSpacing } : {},
|
|
37
|
+
...formatting.lineSpacingRule !== void 0 ? { lineSpacingRule: formatting.lineSpacingRule } : {},
|
|
38
|
+
...formatting.beforeAutospacing !== void 0 ? { beforeAutospacing: formatting.beforeAutospacing } : {},
|
|
39
|
+
...formatting.afterAutospacing !== void 0 ? { afterAutospacing: formatting.afterAutospacing } : {}
|
|
40
|
+
};
|
|
41
|
+
return Object.keys(spacing).length > 0 ? spacing : void 0;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Read direct spacing independently of the effective values used for layout.
|
|
45
|
+
* Imported and command-authored paragraphs keep their source in
|
|
46
|
+
* `_originalFormatting`; explicit PM attrs cover newly created content.
|
|
47
|
+
*/
|
|
48
|
+
const directParagraphSpacing = (attrs) => {
|
|
49
|
+
const spacing = paragraphSpacingFromFormatting(attrs._originalFormatting) ?? {};
|
|
50
|
+
const lineProvenance = lineSpacingProvenance(attrs);
|
|
51
|
+
const spaceBefore = attrs.spaceBefore;
|
|
52
|
+
const spaceAfter = attrs.spaceAfter;
|
|
53
|
+
const beforeHasBase = hasAutospacingBaseSide(attrs._autospacingBase, "before");
|
|
54
|
+
const afterHasBase = hasAutospacingBaseSide(attrs._autospacingBase, "after");
|
|
55
|
+
const beforeAutospacingEdited = beforeHasBase ? !autospacingMatchesBase(attrs._autospacingBase, "before", spaceBefore) : spacing.beforeAutospacing === true && attrs._autospacingBase == null;
|
|
56
|
+
const afterAutospacingEdited = afterHasBase ? !autospacingMatchesBase(attrs._autospacingBase, "after", spaceAfter) : spacing.afterAutospacing === true && attrs._autospacingBase == null;
|
|
57
|
+
if (beforeAutospacingEdited) {
|
|
58
|
+
spacing.beforeAutospacing = false;
|
|
59
|
+
if (typeof spaceBefore === "number") spacing.spaceBefore = spaceBefore;
|
|
60
|
+
else Reflect.deleteProperty(spacing, "spaceBefore");
|
|
61
|
+
}
|
|
62
|
+
if (afterAutospacingEdited) {
|
|
63
|
+
spacing.afterAutospacing = false;
|
|
64
|
+
if (typeof spaceAfter === "number") spacing.spaceAfter = spaceAfter;
|
|
65
|
+
else Reflect.deleteProperty(spacing, "spaceAfter");
|
|
66
|
+
}
|
|
67
|
+
if (spacing.spaceBefore !== void 0 || attrs.spacingExplicit?.before === true) if (typeof spaceBefore === "number") spacing.spaceBefore = spaceBefore;
|
|
68
|
+
else Reflect.deleteProperty(spacing, "spaceBefore");
|
|
69
|
+
if (spacing.spaceAfter !== void 0 || attrs.spacingExplicit?.after === true) if (typeof spaceAfter === "number") spacing.spaceAfter = spaceAfter;
|
|
70
|
+
else Reflect.deleteProperty(spacing, "spaceAfter");
|
|
71
|
+
if (lineProvenance === "value" || lineProvenance === "both") if (typeof attrs.lineSpacing === "number") spacing.lineSpacing = attrs.lineSpacing;
|
|
72
|
+
else Reflect.deleteProperty(spacing, "lineSpacing");
|
|
73
|
+
else Reflect.deleteProperty(spacing, "lineSpacing");
|
|
74
|
+
if (lineProvenance === "rule" || lineProvenance === "both") if (attrs.lineSpacingRule !== void 0) spacing.lineSpacingRule = attrs.lineSpacingRule;
|
|
75
|
+
else Reflect.deleteProperty(spacing, "lineSpacingRule");
|
|
76
|
+
else Reflect.deleteProperty(spacing, "lineSpacingRule");
|
|
77
|
+
return Object.keys(spacing).length > 0 ? spacing : void 0;
|
|
78
|
+
};
|
|
79
|
+
/** Structural equality for the canonical, fixed-order spacing projection. */
|
|
80
|
+
const paragraphSpacingEqual = (left, right) => (left?.spaceBefore ?? null) === (right?.spaceBefore ?? null) && (left?.spaceAfter ?? null) === (right?.spaceAfter ?? null) && (left?.lineSpacing ?? null) === (right?.lineSpacing ?? null) && (left?.lineSpacingRule ?? null) === (right?.lineSpacingRule ?? null) && (left?.beforeAutospacing ?? null) === (right?.beforeAutospacing ?? null) && (left?.afterAutospacing ?? null) === (right?.afterAutospacing ?? null);
|
|
81
|
+
/** Replace the complete direct `w:spacing` cluster in canonical formatting. */
|
|
82
|
+
const withDirectParagraphSpacing = (formatting, spacing) => {
|
|
83
|
+
const result = { ...formatting };
|
|
84
|
+
for (const key of DIRECT_PARAGRAPH_SPACING_KEYS) Reflect.deleteProperty(result, key);
|
|
85
|
+
Reflect.deleteProperty(result, "spacingExplicit");
|
|
86
|
+
if (spacing) {
|
|
87
|
+
Object.assign(result, spacing);
|
|
88
|
+
const spacingExplicit = {
|
|
89
|
+
...spacing.spaceBefore !== void 0 ? { before: true } : {},
|
|
90
|
+
...spacing.spaceAfter !== void 0 ? { after: true } : {}
|
|
91
|
+
};
|
|
92
|
+
if (Object.keys(spacingExplicit).length > 0) result.spacingExplicit = spacingExplicit;
|
|
93
|
+
}
|
|
94
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
95
|
+
};
|
|
96
|
+
/** Project direct-over-inherited spacing into the attrs consumed by layout. */
|
|
97
|
+
const paragraphSpacingAttrPatch = ({ direct, inherited }) => {
|
|
98
|
+
const effective = {
|
|
99
|
+
...inherited,
|
|
100
|
+
...direct
|
|
101
|
+
};
|
|
102
|
+
const spacingExplicit = {
|
|
103
|
+
...direct?.spaceBefore !== void 0 ? { before: true } : {},
|
|
104
|
+
...direct?.spaceAfter !== void 0 ? { after: true } : {}
|
|
105
|
+
};
|
|
106
|
+
const spacingFromStyle = {
|
|
107
|
+
...direct?.spaceBefore === void 0 && inherited?.spaceBefore !== void 0 ? { before: true } : {},
|
|
108
|
+
...direct?.spaceAfter === void 0 && inherited?.spaceAfter !== void 0 ? { after: true } : {}
|
|
109
|
+
};
|
|
110
|
+
const autospacingBase = {};
|
|
111
|
+
if (effective.beforeAutospacing === true) setAutospacingBaseValue(autospacingBase, "before", effective.spaceBefore);
|
|
112
|
+
if (effective.afterAutospacing === true) setAutospacingBaseValue(autospacingBase, "after", effective.spaceAfter);
|
|
113
|
+
return {
|
|
114
|
+
spaceBefore: effective.spaceBefore ?? null,
|
|
115
|
+
spaceAfter: effective.spaceAfter ?? null,
|
|
116
|
+
lineSpacing: effective.lineSpacing ?? null,
|
|
117
|
+
lineSpacingRule: effective.lineSpacingRule ?? null,
|
|
118
|
+
lineSpacingExplicit: lineSpacingProvenanceFromSpacing(direct) ?? null,
|
|
119
|
+
spacingExplicit: Object.keys(spacingExplicit).length > 0 ? spacingExplicit : null,
|
|
120
|
+
spacingFromImplicitDefaultStyle: Object.keys(spacingFromStyle).length > 0 ? spacingFromStyle : null,
|
|
121
|
+
_autospacingBase: Object.keys(autospacingBase).length > 0 ? autospacingBase : null
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
//#endregion
|
|
125
|
+
export { DIRECT_PARAGRAPH_SPACING_KEYS, directParagraphSpacing, lineSpacingProvenanceFromSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { NumberingMap } from "../../docx/numberingParser.js";
|
|
3
|
+
import { EditorState, Plugin } from "prosemirror-state";
|
|
4
|
+
//#region src/prosemirror/plugins/documentNumbering.d.ts
|
|
5
|
+
declare const createDocumentNumberingPlugin: (definitions: document_d_exports.NumberingDefinitions | null | undefined) => Plugin;
|
|
6
|
+
declare const getDocumentNumbering: (state: EditorState) => NumberingMap | null;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { createDocumentNumberingPlugin, getDocumentNumbering };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getCachedNumberingMap } from "../../docx/numberingParser.js";
|
|
2
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
3
|
+
//#region src/prosemirror/plugins/documentNumbering.ts
|
|
4
|
+
/** Document-scoped numbering definitions for list-editing commands. */
|
|
5
|
+
const documentNumberingKey = new PluginKey("documentNumbering");
|
|
6
|
+
const createDocumentNumberingPlugin = (definitions) => {
|
|
7
|
+
const numbering = definitions ? getCachedNumberingMap(definitions) : null;
|
|
8
|
+
return new Plugin({
|
|
9
|
+
key: documentNumberingKey,
|
|
10
|
+
state: {
|
|
11
|
+
init: () => numbering,
|
|
12
|
+
apply: (_transaction, value) => value
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
const getDocumentNumbering = (state) => documentNumberingKey.getState(state) ?? null;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { createDocumentNumberingPlugin, getDocumentNumbering };
|
|
@@ -9,8 +9,9 @@ type TextChunk = {
|
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
11
|
* Collect every block-level node's text content as an array of
|
|
12
|
-
* chunks (one per PM text node), grouped per
|
|
13
|
-
*
|
|
12
|
+
* chunks (one per PM text node), grouped per uninterrupted scan segment.
|
|
13
|
+
* An explicit page-break run ends a segment: it is zero-width in the joined
|
|
14
|
+
* text, but a text-shaped overlay must not select across that structural atom.
|
|
14
15
|
*/
|
|
15
16
|
declare const collectBlockChunks: (doc: Node) => TextChunk[][];
|
|
16
17
|
/** Map a joined-string offset back to its PM doc position. */
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
//#region src/prosemirror/plugins/pmTextScan.ts
|
|
2
2
|
/**
|
|
3
3
|
* Collect every block-level node's text content as an array of
|
|
4
|
-
* chunks (one per PM text node), grouped per
|
|
5
|
-
*
|
|
4
|
+
* chunks (one per PM text node), grouped per uninterrupted scan segment.
|
|
5
|
+
* An explicit page-break run ends a segment: it is zero-width in the joined
|
|
6
|
+
* text, but a text-shaped overlay must not select across that structural atom.
|
|
6
7
|
*/
|
|
7
8
|
const collectBlockChunks = (doc) => {
|
|
8
9
|
const blocks = [];
|
|
9
10
|
doc.descendants((node, pos) => {
|
|
10
11
|
if (node.isTextblock) {
|
|
11
|
-
|
|
12
|
+
let chunks = [];
|
|
13
|
+
const flushSegment = () => {
|
|
14
|
+
if (chunks.length === 0) return;
|
|
15
|
+
blocks.push(chunks);
|
|
16
|
+
chunks = [];
|
|
17
|
+
};
|
|
12
18
|
node.descendants((child, offset) => {
|
|
19
|
+
if (child.type.name === "pageBreakRun") {
|
|
20
|
+
flushSegment();
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
13
23
|
if (child.isText && child.text !== void 0) {
|
|
14
24
|
const start = pos + 1 + offset;
|
|
15
25
|
chunks.push({
|
|
@@ -30,7 +40,7 @@ const collectBlockChunks = (doc) => {
|
|
|
30
40
|
}
|
|
31
41
|
return true;
|
|
32
42
|
});
|
|
33
|
-
|
|
43
|
+
flushSegment();
|
|
34
44
|
return false;
|
|
35
45
|
}
|
|
36
46
|
return true;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
|
|
2
|
+
import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
|
|
2
3
|
import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
|
|
3
4
|
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
4
5
|
import { isHistoryTransaction } from "prosemirror-history";
|
|
@@ -71,7 +72,7 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
|
|
|
71
72
|
function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState) {
|
|
72
73
|
const ranges = [];
|
|
73
74
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
74
|
-
if (!
|
|
75
|
+
if (!canCarryTrackedRunMark(node)) return;
|
|
75
76
|
const start = Math.max(pos, from);
|
|
76
77
|
const end = Math.min(pos + node.nodeSize, to);
|
|
77
78
|
if (start >= end) return;
|
|
@@ -92,16 +93,13 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
/**
|
|
95
|
-
* Add the insertion mark to every
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* `allowsMarkType` is false, hence the explicit `isText` arm; content already
|
|
99
|
-
* carrying an insertion/deletion is left untouched so another author's revision
|
|
100
|
-
* isn't overwritten. eigenpal/docx-editor#784.
|
|
96
|
+
* Add the insertion mark to every run carrier in `[from, to)` that does not
|
|
97
|
+
* already carry a tracked-change mark. Content already carrying a revision is
|
|
98
|
+
* left untouched so another author's attribution is not overwritten.
|
|
101
99
|
*/
|
|
102
100
|
function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, attrs) {
|
|
103
101
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
104
|
-
if (!
|
|
102
|
+
if (!canCarryTrackedRunMark(node)) return;
|
|
105
103
|
if (node.marks.some((m) => m.type === insertionType || m.type === deletionType)) return;
|
|
106
104
|
const start = Math.max(pos, from);
|
|
107
105
|
const end = Math.min(pos + node.nodeSize, to);
|
|
@@ -289,7 +287,7 @@ function handleSuggestionDelete(state, dispatch, direction) {
|
|
|
289
287
|
const deleteEnd = isBackward ? $from.pos : $from.pos + 1;
|
|
290
288
|
if (deletePos < 0 || deleteEnd > state.doc.content.size) return true;
|
|
291
289
|
const nodeAfter = state.doc.resolve(deletePos).nodeAfter;
|
|
292
|
-
if (!nodeAfter || !(nodeAfter
|
|
290
|
+
if (!nodeAfter || !canCarryTrackedRunMark(nodeAfter)) return false;
|
|
293
291
|
const hasOwnInsertion = nodeAfter.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
|
|
294
292
|
if (nodeAfter.marks.some((m) => m.type === deletionType)) {
|
|
295
293
|
const newPos = isBackward ? deletePos : deleteEnd;
|
|
@@ -449,7 +447,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
449
447
|
const deletionType = newState.schema.marks["deletion"];
|
|
450
448
|
for (const step of userTr.steps) step.getMap().forEach((_oldFrom, _oldTo, newFrom, newTo) => {
|
|
451
449
|
if (newTo > newFrom) newState.doc.nodesBetween(newFrom, newTo, (node, pos) => {
|
|
452
|
-
if (!
|
|
450
|
+
if (!canCarryTrackedRunMark(node)) return;
|
|
453
451
|
if (!node.marks.some((m) => m.type === insertionType || deletionType && m.type === deletionType)) {
|
|
454
452
|
const nodeStart = Math.max(pos, newFrom);
|
|
455
453
|
const nodeEnd = Math.min(pos + node.nodeSize, newTo);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RunStyleResolver } from "./runStyleFormatting.js";
|
|
2
|
+
import { Transaction } from "prosemirror-state";
|
|
3
|
+
//#region src/prosemirror/rebaseParagraphRunFormatting.d.ts
|
|
4
|
+
type RebaseParagraphRunFormattingOptions = {
|
|
5
|
+
nextAttrs: Record<string, unknown>;
|
|
6
|
+
paragraphPosition: number;
|
|
7
|
+
styleResolver: RunStyleResolver;
|
|
8
|
+
tr: Transaction;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Change paragraph attrs and re-resolve inherited run marks in the new style
|
|
12
|
+
* context without turning the old rendered style into direct run formatting.
|
|
13
|
+
*/
|
|
14
|
+
declare const setParagraphAttrsWithRebasedRunFormatting: ({ nextAttrs, paragraphPosition, styleResolver, tr }: RebaseParagraphRunFormattingOptions) => Transaction;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { setParagraphAttrsWithRebasedRunFormatting };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { expandRunFormattingCarrier } from "./runFormattingInlineCarriers.js";
|
|
2
|
+
import { RUN_FORMATTING_MARK_NAMES } from "./runFormattingMarkNames.js";
|
|
3
|
+
import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "./runFormattingReconciliation.js";
|
|
4
|
+
import { paragraphRunStyleContext } from "./runStyleFormatting.js";
|
|
5
|
+
import { panic } from "better-result";
|
|
6
|
+
import { Mark } from "prosemirror-model";
|
|
7
|
+
//#region src/prosemirror/rebaseParagraphRunFormatting.ts
|
|
8
|
+
/**
|
|
9
|
+
* Change paragraph attrs and re-resolve inherited run marks in the new style
|
|
10
|
+
* context without turning the old rendered style into direct run formatting.
|
|
11
|
+
*/
|
|
12
|
+
const setParagraphAttrsWithRebasedRunFormatting = ({ nextAttrs, paragraphPosition, styleResolver, tr }) => {
|
|
13
|
+
const paragraph = tr.doc.nodeAt(paragraphPosition);
|
|
14
|
+
if (!paragraph || paragraph.type.name !== "paragraph") return panic("Cannot rebase run formatting outside a paragraph", {
|
|
15
|
+
nodeType: paragraph?.type.name,
|
|
16
|
+
paragraphPosition
|
|
17
|
+
});
|
|
18
|
+
const previousContext = paragraphRunStyleContext(paragraph, styleResolver);
|
|
19
|
+
const nextContext = paragraphRunStyleContext(paragraph.type.create(nextAttrs, paragraph.content, paragraph.marks), styleResolver);
|
|
20
|
+
const changes = [];
|
|
21
|
+
const collectRebasedRepresentation = ({ node, position }) => {
|
|
22
|
+
const nextMarks = reconcileRunFormattingMarks({
|
|
23
|
+
authoredFormatting: readAuthoredRunFormatting({
|
|
24
|
+
context: previousContext,
|
|
25
|
+
marks: node.marks,
|
|
26
|
+
styleResolver
|
|
27
|
+
}),
|
|
28
|
+
context: nextContext,
|
|
29
|
+
node,
|
|
30
|
+
styleResolver
|
|
31
|
+
});
|
|
32
|
+
if (Mark.sameSet(node.marks, nextMarks)) return;
|
|
33
|
+
changes.push({
|
|
34
|
+
attrs: node.attrs,
|
|
35
|
+
currentFormattingMarks: node.marks.filter(({ type }) => RUN_FORMATTING_MARK_NAMES.has(type.name)),
|
|
36
|
+
from: position,
|
|
37
|
+
isText: node.isText,
|
|
38
|
+
marks: nextMarks,
|
|
39
|
+
to: position + node.nodeSize
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
paragraph.descendants((node, relativePosition) => {
|
|
43
|
+
if (!node.isInline) return true;
|
|
44
|
+
const carrier = expandRunFormattingCarrier(node, paragraphPosition + 1 + relativePosition);
|
|
45
|
+
if (!carrier) return !node.isAtom;
|
|
46
|
+
for (const representation of carrier.representations) collectRebasedRepresentation(representation);
|
|
47
|
+
return false;
|
|
48
|
+
});
|
|
49
|
+
tr = tr.setNodeMarkup(paragraphPosition, void 0, nextAttrs);
|
|
50
|
+
for (const { attrs, currentFormattingMarks, from, isText, marks, to } of changes) {
|
|
51
|
+
if (!isText) {
|
|
52
|
+
tr = tr.setNodeMarkup(from, void 0, attrs, marks);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
for (const mark of currentFormattingMarks) tr = tr.removeMark(from, to, mark.type);
|
|
56
|
+
for (const mark of marks) if (RUN_FORMATTING_MARK_NAMES.has(mark.type.name)) tr = tr.addMark(from, to, mark);
|
|
57
|
+
}
|
|
58
|
+
return tr;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { setParagraphAttrsWithRebasedRunFormatting };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/prosemirror/runFormattingInlineCarriers.d.ts
|
|
3
|
+
type RunFormattingInlineAtomDisposition = "break-run" | "field-run" | "not-a-run" | "page-break-carrier" | "structured-field" | "symbol-run" | "tab-run" | "text-run";
|
|
4
|
+
/**
|
|
5
|
+
* How every inline atom maps to a serialized run-formatting carrier.
|
|
6
|
+
*
|
|
7
|
+
* This is intentionally separate from tracked wrapper ownership: drawings and
|
|
8
|
+
* bookmarks can sit inside an insertion/deletion wrapper, but their editor
|
|
9
|
+
* marks do not serialize as run properties. The runtime fixed-point test binds
|
|
10
|
+
* this map to the complete schema atom set.
|
|
11
|
+
*/
|
|
12
|
+
declare const RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS: Readonly<{
|
|
13
|
+
readonly bookmarkBoundary: "not-a-run";
|
|
14
|
+
readonly field: "field-run";
|
|
15
|
+
readonly hardBreak: "break-run";
|
|
16
|
+
readonly image: "not-a-run";
|
|
17
|
+
readonly math: "not-a-run";
|
|
18
|
+
readonly pageBreakRun: "page-break-carrier";
|
|
19
|
+
readonly renderedPageBreak: "not-a-run";
|
|
20
|
+
readonly shape: "not-a-run";
|
|
21
|
+
readonly structuredField: "structured-field";
|
|
22
|
+
readonly symbol: "symbol-run";
|
|
23
|
+
readonly tab: "tab-run";
|
|
24
|
+
readonly text: "text-run";
|
|
25
|
+
readonly textBoxAnchor: "not-a-run";
|
|
26
|
+
}>;
|
|
27
|
+
declare const runFormattingInlineAtomDisposition: (node: Node) => RunFormattingInlineAtomDisposition | null;
|
|
28
|
+
type RunFormattingCarrierRepresentation = {
|
|
29
|
+
node: Node;
|
|
30
|
+
position: number;
|
|
31
|
+
role: "owner" | "serialized-result";
|
|
32
|
+
};
|
|
33
|
+
type RunFormattingCarrier = {
|
|
34
|
+
disposition: Exclude<RunFormattingInlineAtomDisposition, "not-a-run">;
|
|
35
|
+
node: Node;
|
|
36
|
+
position: number;
|
|
37
|
+
representations: readonly RunFormattingCarrierRepresentation[];
|
|
38
|
+
};
|
|
39
|
+
type SelectedRunFormattingCarrierRepresentation = RunFormattingCarrierRepresentation & {
|
|
40
|
+
carrierDisposition: RunFormattingCarrier["disposition"];
|
|
41
|
+
from: number;
|
|
42
|
+
to: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Expand one logical carrier into every editor node that materializes its run
|
|
46
|
+
* properties. A structured field is owned by its outer atom, while its result
|
|
47
|
+
* runs are derived representations needed by the package serializer.
|
|
48
|
+
*/
|
|
49
|
+
declare const expandRunFormattingCarrier: (node: Node, position: number) => RunFormattingCarrier | null;
|
|
50
|
+
type SelectRunFormattingCarrierRepresentationsOptions = {
|
|
51
|
+
doc: Node;
|
|
52
|
+
from: number;
|
|
53
|
+
to: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a document range to the exact editor nodes that serialize run
|
|
57
|
+
* properties. The result is shared by every formatting mutation so preflight,
|
|
58
|
+
* direct edits, and tracked edits cannot disagree about ownership.
|
|
59
|
+
*/
|
|
60
|
+
declare const selectRunFormattingCarrierRepresentations: ({ doc, from, to }: SelectRunFormattingCarrierRepresentationsOptions) => SelectedRunFormattingCarrierRepresentation[];
|
|
61
|
+
/** Deterministic visible text for one logical formatting revision carrier. */
|
|
62
|
+
declare const runFormattingCarrierReviewText: (carrier: RunFormattingCarrier) => string;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS, RunFormattingCarrier, RunFormattingCarrierRepresentation, RunFormattingInlineAtomDisposition, SelectedRunFormattingCarrierRepresentation, expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition, selectRunFormattingCarrierRepresentations };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
|
|
2
|
+
import { expectFieldAttrs, expectSymbolAttrs } from "./attrs/index.js";
|
|
3
|
+
import { panic } from "better-result";
|
|
4
|
+
//#region src/prosemirror/runFormattingInlineCarriers.ts
|
|
5
|
+
/**
|
|
6
|
+
* How every inline atom maps to a serialized run-formatting carrier.
|
|
7
|
+
*
|
|
8
|
+
* This is intentionally separate from tracked wrapper ownership: drawings and
|
|
9
|
+
* bookmarks can sit inside an insertion/deletion wrapper, but their editor
|
|
10
|
+
* marks do not serialize as run properties. The runtime fixed-point test binds
|
|
11
|
+
* this map to the complete schema atom set.
|
|
12
|
+
*/
|
|
13
|
+
const RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS = Object.freeze({
|
|
14
|
+
bookmarkBoundary: "not-a-run",
|
|
15
|
+
field: "field-run",
|
|
16
|
+
hardBreak: "break-run",
|
|
17
|
+
image: "not-a-run",
|
|
18
|
+
math: "not-a-run",
|
|
19
|
+
pageBreakRun: "page-break-carrier",
|
|
20
|
+
renderedPageBreak: "not-a-run",
|
|
21
|
+
shape: "not-a-run",
|
|
22
|
+
structuredField: "structured-field",
|
|
23
|
+
symbol: "symbol-run",
|
|
24
|
+
tab: "tab-run",
|
|
25
|
+
text: "text-run",
|
|
26
|
+
textBoxAnchor: "not-a-run"
|
|
27
|
+
});
|
|
28
|
+
const dispositionsByName = new Map(Object.entries(RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS));
|
|
29
|
+
const runFormattingInlineAtomDisposition = (node) => {
|
|
30
|
+
if (!node.isInline || !node.isAtom) return null;
|
|
31
|
+
const disposition = dispositionsByName.get(node.type.name);
|
|
32
|
+
if (!disposition) return panic(`Inline atom ${JSON.stringify(node.type.name)} has no run-formatting disposition`);
|
|
33
|
+
return disposition;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Expand one logical carrier into every editor node that materializes its run
|
|
37
|
+
* properties. A structured field is owned by its outer atom, while its result
|
|
38
|
+
* runs are derived representations needed by the package serializer.
|
|
39
|
+
*/
|
|
40
|
+
const expandRunFormattingCarrier = (node, position) => {
|
|
41
|
+
const disposition = runFormattingInlineAtomDisposition(node);
|
|
42
|
+
if (disposition === null || disposition === "not-a-run") return null;
|
|
43
|
+
if (disposition !== "structured-field") return {
|
|
44
|
+
disposition,
|
|
45
|
+
node,
|
|
46
|
+
position,
|
|
47
|
+
representations: [{
|
|
48
|
+
node,
|
|
49
|
+
position,
|
|
50
|
+
role: "owner"
|
|
51
|
+
}]
|
|
52
|
+
};
|
|
53
|
+
const representations = [{
|
|
54
|
+
node,
|
|
55
|
+
position,
|
|
56
|
+
role: "owner"
|
|
57
|
+
}];
|
|
58
|
+
node.descendants((child, relativePosition) => {
|
|
59
|
+
const childDisposition = runFormattingInlineAtomDisposition(child);
|
|
60
|
+
if (childDisposition === null) return true;
|
|
61
|
+
if (childDisposition === "structured-field") return panic("A structured field cannot contain another structured field");
|
|
62
|
+
if (childDisposition !== "not-a-run") representations.push({
|
|
63
|
+
node: child,
|
|
64
|
+
position: position + 1 + relativePosition,
|
|
65
|
+
role: "serialized-result"
|
|
66
|
+
});
|
|
67
|
+
return false;
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
disposition,
|
|
71
|
+
node,
|
|
72
|
+
position,
|
|
73
|
+
representations
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Resolve a document range to the exact editor nodes that serialize run
|
|
78
|
+
* properties. The result is shared by every formatting mutation so preflight,
|
|
79
|
+
* direct edits, and tracked edits cannot disagree about ownership.
|
|
80
|
+
*/
|
|
81
|
+
const selectRunFormattingCarrierRepresentations = ({ doc, from, to }) => {
|
|
82
|
+
const selected = [];
|
|
83
|
+
doc.nodesBetween(from, to, (node, position) => {
|
|
84
|
+
if (!node.isInline) return true;
|
|
85
|
+
const carrier = expandRunFormattingCarrier(node, position);
|
|
86
|
+
if (!carrier) return !node.isAtom;
|
|
87
|
+
if (carrier.disposition === "page-break-carrier") return false;
|
|
88
|
+
for (const representation of carrier.representations) {
|
|
89
|
+
if (runFormattingInlineAtomDisposition(representation.node) === "page-break-carrier") continue;
|
|
90
|
+
const representationFrom = Math.max(from, representation.position);
|
|
91
|
+
const representationTo = Math.min(to, representation.position + representation.node.nodeSize);
|
|
92
|
+
if (representationFrom >= representationTo) continue;
|
|
93
|
+
if (carrier.disposition === "structured-field" && representation.role === "owner" && (representationFrom !== representation.position || representationTo !== representation.position + representation.node.nodeSize)) continue;
|
|
94
|
+
selected.push({
|
|
95
|
+
...representation,
|
|
96
|
+
carrierDisposition: carrier.disposition,
|
|
97
|
+
from: representationFrom,
|
|
98
|
+
to: representationTo
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
});
|
|
103
|
+
return selected;
|
|
104
|
+
};
|
|
105
|
+
/** Deterministic visible text for one logical formatting revision carrier. */
|
|
106
|
+
const runFormattingCarrierReviewText = (carrier) => {
|
|
107
|
+
switch (carrier.disposition) {
|
|
108
|
+
case "text-run": return carrier.node.text ?? "";
|
|
109
|
+
case "tab-run": return " ";
|
|
110
|
+
case "break-run": return "\n";
|
|
111
|
+
case "page-break-carrier": return "";
|
|
112
|
+
case "symbol-run": {
|
|
113
|
+
const { char } = expectSymbolAttrs(carrier.node);
|
|
114
|
+
return decodeOoxmlSymbolCharacter(char) ?? "�";
|
|
115
|
+
}
|
|
116
|
+
case "field-run":
|
|
117
|
+
case "structured-field": return expectFieldAttrs(carrier.node).displayText ?? carrier.node.textContent;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
121
|
+
export { RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS, expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition, selectRunFormattingCarrierRepresentations };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { RunFormattingOverrideAttrs } from "./schema/marks.js";
|
|
3
|
+
//#region src/prosemirror/runFormattingProvenance.d.ts
|
|
4
|
+
declare const hasAuthoredRunFormattingProvenance: ({ _authoredOff, _authoredOn, _authoredValues }: RunFormattingOverrideAttrs) => boolean;
|
|
5
|
+
/** Whether an override mark still carries any rendering or authorship signal. */
|
|
6
|
+
declare const hasRunFormattingOverrideAttrs: (attrs: RunFormattingOverrideAttrs) => boolean;
|
|
7
|
+
/** Reconstruct the last imported/reconciled direct baseline; inherited values never enter here. */
|
|
8
|
+
declare const authoredRunFormattingFromAttrs: (attrs: RunFormattingOverrideAttrs) => document_d_exports.TextFormatting | undefined;
|
|
9
|
+
/** Replace the direct baseline while preserving current rendering attributes. */
|
|
10
|
+
declare const withAuthoredRunFormatting: (attrs: RunFormattingOverrideAttrs, formatting: document_d_exports.TextFormatting | undefined) => RunFormattingOverrideAttrs;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { authoredRunFormattingFromAttrs, hasAuthoredRunFormattingProvenance, hasRunFormattingOverrideAttrs, withAuthoredRunFormatting };
|