@stll/folio-core 0.64.1 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +31 -9
- package/dist/ai-edits/headless.js +3 -3
- package/dist/compare/reproducible-package.d.ts +5 -0
- package/dist/compare/reproducible-package.js +10 -23
- package/dist/docx/archiveInflation.d.ts +8 -103
- package/dist/docx/archiveInflation.js +8 -145
- package/dist/docx/attributeRemainder.js +1 -1
- package/dist/docx/canonicalStoryRepack.js +2 -2
- package/dist/docx/documentSectionFacts.js +1 -1
- package/dist/docx/ensureParaIds.js +9 -24
- package/dist/docx/importedIdCollisions.js +1 -1
- package/dist/docx/mathToMathml.js +1 -1
- package/dist/docx/metadataPrivacy.js +38 -48
- package/dist/docx/numericIdNormalization.js +2 -2
- package/dist/docx/packageParts.d.ts +4 -4
- package/dist/docx/packageParts.js +17 -24
- package/dist/docx/parser.js +5 -6
- package/dist/docx/removeHeaderFooterParts.d.ts +2 -2
- package/dist/docx/removeHeaderFooterParts.js +19 -36
- package/dist/docx/revisionIdNormalization.js +2 -2
- package/dist/docx/rezip.d.ts +7 -7
- package/dist/docx/rezip.js +144 -532
- package/dist/docx/sdtProperties.js +1 -2
- package/dist/docx/selectedXmlIdentityScan.d.ts +1 -1
- package/dist/docx/selectedXmlIdentityScan.js +1 -1
- package/dist/docx/selectiveSave.js +37 -41
- package/dist/docx/selectiveSaveFlags.d.ts +1 -1
- package/dist/docx/selectiveSaveTripwire.js +5 -11
- package/dist/docx/selectiveXmlPatch.js +2 -2
- package/dist/docx/server/applyDocxXmlPatchProposal.js +20 -41
- package/dist/docx/server/boundedArchive.d.ts +2 -3
- package/dist/docx/server/boundedArchive.js +78 -99
- package/dist/docx/server/inspectDocxPackage.d.ts +1 -1
- package/dist/docx/server/inspectDocxPackage.js +1 -1
- package/dist/docx/server/validateDocxConformance.js +1 -1
- package/dist/docx/shapeAlternateContent.js +1 -1
- package/dist/docx/storyBlockReplay.js +1 -1
- package/dist/docx/unzip.d.ts +2 -3
- package/dist/docx/unzip.js +127 -155
- package/dist/docx/verbatimCapture.js +1 -1
- package/dist/docx/xmlParser.d.ts +5 -36
- package/dist/docx/xmlParser.js +14 -78
- package/dist/docx/xmlSafety.js +5 -5
- package/dist/layout-painter/renderPage.js +4 -2
- package/dist/markdown/countParagraphList.d.ts +18 -0
- package/dist/markdown/countParagraphList.js +90 -0
- package/dist/markdown/renderParagraph.js +2 -62
- package/dist/markdown/renderRuns.js +4 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +5 -3
- package/dist/prosemirror/conversion/toProseDoc.js +4 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +6 -0
- package/dist/prosemirror/replacedAnnotations.js +30 -8
- package/dist/prosemirror/schema/nodes.d.ts +7 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +9 -1
- package/package.json +3 -5
- package/dist/docx/streamingXmlParser.d.ts +0 -60
- package/dist/docx/streamingXmlParser.js +0 -406
- package/dist/docx/xmlNamespaceContext.d.ts +0 -6
- package/dist/docx/xmlNamespaceContext.js +0 -53
- package/dist/docx/xmlResourceLimits.d.ts +0 -107
- package/dist/docx/xmlResourceLimits.js +0 -194
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { RenderContext } from "./types.js";
|
|
3
|
+
export type CountedParagraphList = {
|
|
4
|
+
/**
|
|
5
|
+
* The list the paragraph shows: its own numbering's, or, while a pending
|
|
6
|
+
* property change removed it, the one that numbering showed.
|
|
7
|
+
*/
|
|
8
|
+
visibleList: document_d_exports.ListRendering | undefined;
|
|
9
|
+
/** The label the counter gave the paragraph; absent when it shows none. */
|
|
10
|
+
label: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
export declare function countParagraphList(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, para: document_d_exports.Paragraph): CountedParagraphList;
|
|
13
|
+
/**
|
|
14
|
+
* Count the paragraphs of a text box Markdown does not write, cells
|
|
15
|
+
* included, in document order. A text box holds no text box, so the walk
|
|
16
|
+
* stops at each paragraph.
|
|
17
|
+
*/
|
|
18
|
+
export declare function countTextBoxLists(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, shape: document_d_exports.Shape): void;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
2
|
+
import { BLOCK_TREE_DESCENT, visitBlockTreeRecords } from "../docx/paragraphTraversal.js";
|
|
3
|
+
import { listLabelAttrsFromRendering } from "../prosemirror/listLabels.js";
|
|
4
|
+
import { effectiveParagraphNumberingReference, paragraphNumberingAttr } from "../prosemirror/numberingAttr.js";
|
|
5
|
+
import { resolveParagraphList } from "../prosemirror/paragraphList.js";
|
|
6
|
+
// src/markdown/countParagraphList.ts
|
|
7
|
+
/**
|
|
8
|
+
* Advance the Markdown walk's list counter past one paragraph, as the page
|
|
9
|
+
* counts it. Every paragraph the page counts, Markdown counts, whatever it
|
|
10
|
+
* renders as and whether or not it writes it: the items after a numbered
|
|
11
|
+
* heading continue from its number, and the paragraphs of a text box count
|
|
12
|
+
* right after the paragraph the box is drawn in (where the page and the
|
|
13
|
+
* content reader count them), although Markdown writes no text box.
|
|
14
|
+
*/
|
|
15
|
+
/** Explicit projection carried by canonical models that are not attached to a package. */
|
|
16
|
+
const standaloneListRendering = (para) => para.listRendering;
|
|
17
|
+
function countParagraphList(ctx, pkg, para) {
|
|
18
|
+
const inherited = ctx.paragraphNumbering(para.formatting?.styleId);
|
|
19
|
+
const inheritedReference = effectiveParagraphNumberingReference({
|
|
20
|
+
numPr: void 0,
|
|
21
|
+
numPrFromStyle: inherited
|
|
22
|
+
});
|
|
23
|
+
const sources = {
|
|
24
|
+
numPr: para.formatting?.numPr,
|
|
25
|
+
numPrFromStyle: para.formatting?.numPrFromStyle
|
|
26
|
+
};
|
|
27
|
+
const numbering = pkg?.numbering ? getCachedNumberingMap(pkg.numbering) : null;
|
|
28
|
+
const resolvedList = resolveParagraphList({
|
|
29
|
+
...sources,
|
|
30
|
+
inheritedReference: inheritedReference ?? null
|
|
31
|
+
}, numbering, standaloneListRendering(para));
|
|
32
|
+
const resolvedRendering = resolvedList.kind === "marker" ? resolvedList.rendering : void 0;
|
|
33
|
+
const counterAttrs = listLabelAttrsFromRendering(resolvedRendering, {
|
|
34
|
+
...sources,
|
|
35
|
+
numPrFromStyle: resolvedList.kind === "none" ? void 0 : inherited ?? sources.numPrFromStyle
|
|
36
|
+
});
|
|
37
|
+
if (sources.numPr === void 0 && sources.numPrFromStyle !== void 0 && inheritedReference === void 0 && para.propertyChanges !== void 0 && para.propertyChanges.length > 0) counterAttrs.numPr = paragraphNumberingAttr({ kind: "none" });
|
|
38
|
+
if (para.pPrMark) counterAttrs.pPrMark = para.pPrMark;
|
|
39
|
+
let originalList;
|
|
40
|
+
if (para.propertyChanges && ctx.opts.trackedChanges !== "clean") counterAttrs._propertyChanges = para.propertyChanges.map(({ previousFormatting, ...change }) => {
|
|
41
|
+
if (!previousFormatting) return change;
|
|
42
|
+
const previousInherited = ctx.paragraphNumbering(previousFormatting.styleId);
|
|
43
|
+
const previousInheritedReference = effectiveParagraphNumberingReference({
|
|
44
|
+
numPr: void 0,
|
|
45
|
+
numPrFromStyle: previousInherited
|
|
46
|
+
});
|
|
47
|
+
const previousSources = {
|
|
48
|
+
numPr: previousFormatting.numPr,
|
|
49
|
+
numPrFromStyle: previousFormatting.numPrFromStyle
|
|
50
|
+
};
|
|
51
|
+
const previousList = resolveParagraphList({
|
|
52
|
+
...previousSources,
|
|
53
|
+
inheritedReference: previousInheritedReference ?? null
|
|
54
|
+
}, numbering);
|
|
55
|
+
const previousRendering = previousList.kind === "marker" ? previousList.rendering : void 0;
|
|
56
|
+
originalList ??= previousRendering;
|
|
57
|
+
const previousAttrs = listLabelAttrsFromRendering(previousRendering, {
|
|
58
|
+
...previousSources,
|
|
59
|
+
numPrFromStyle: previousList.kind === "none" ? void 0 : previousInherited ?? previousSources.numPrFromStyle
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
...change,
|
|
63
|
+
previousFormatting: {
|
|
64
|
+
...previousFormatting,
|
|
65
|
+
...previousAttrs,
|
|
66
|
+
...previousAttrs.numPr === void 0 ? { numPr: null } : { numPr: previousAttrs.numPr },
|
|
67
|
+
...previousAttrs.numPrFromStyle === void 0 ? { numPrFromStyle: null } : { numPrFromStyle: previousAttrs.numPrFromStyle }
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
const countedList = ctx.nextListLabel(counterAttrs, resolvedRendering);
|
|
72
|
+
return {
|
|
73
|
+
visibleList: resolvedRendering ?? (sources.numPr?.kind === "levelOnly" ? void 0 : originalList),
|
|
74
|
+
label: countedList.kind === "marker" ? countedList.label : void 0
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Count the paragraphs of a text box Markdown does not write, cells
|
|
79
|
+
* included, in document order. A text box holds no text box, so the walk
|
|
80
|
+
* stops at each paragraph.
|
|
81
|
+
*/
|
|
82
|
+
function countTextBoxLists(ctx, pkg, shape) {
|
|
83
|
+
if (shape.textBody === void 0) return;
|
|
84
|
+
visitBlockTreeRecords(shape.textBody.content, (record) => {
|
|
85
|
+
if (record.type !== "paragraph") return BLOCK_TREE_DESCENT.descend;
|
|
86
|
+
countParagraphList(ctx, pkg, record);
|
|
87
|
+
return BLOCK_TREE_DESCENT.prune;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export { countParagraphList, countTextBoxLists };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { isQuoteStyle, resolveHeadingLevel } from "../docx/builtInStyles.js";
|
|
2
|
-
import {
|
|
3
|
-
import { listLabelAttrsFromRendering } from "../prosemirror/listLabels.js";
|
|
4
|
-
import { effectiveParagraphNumberingReference, paragraphNumberingAttr } from "../prosemirror/numberingAttr.js";
|
|
5
|
-
import { resolveParagraphList } from "../prosemirror/paragraphList.js";
|
|
2
|
+
import { countParagraphList } from "./countParagraphList.js";
|
|
6
3
|
import { inlineBreaksToHtml } from "./escape.js";
|
|
7
4
|
import { renderParagraphInline } from "./renderRuns.js";
|
|
8
5
|
// src/markdown/renderParagraph.ts
|
|
@@ -21,8 +18,6 @@ import { renderParagraphInline } from "./renderRuns.js";
|
|
|
21
18
|
*/
|
|
22
19
|
/** Markdown has six heading levels; Word has nine. */
|
|
23
20
|
const MAX_MARKDOWN_HEADING_LEVEL = 6;
|
|
24
|
-
/** Explicit projection carried by canonical models that are not attached to a package. */
|
|
25
|
-
const standaloneListRendering = (para) => para.listRendering;
|
|
26
21
|
/**
|
|
27
22
|
* Render a paragraph and return the block text. No surrounding blank line: the
|
|
28
23
|
* caller joins blocks.
|
|
@@ -37,61 +32,7 @@ function renderParagraph(ctx, pkg, para) {
|
|
|
37
32
|
* does not define as prose, as every other reader reads them.
|
|
38
33
|
*/
|
|
39
34
|
function renderParagraphBlock(ctx, pkg, para) {
|
|
40
|
-
const
|
|
41
|
-
const inheritedReference = effectiveParagraphNumberingReference({
|
|
42
|
-
numPr: void 0,
|
|
43
|
-
numPrFromStyle: inherited
|
|
44
|
-
});
|
|
45
|
-
const sources = {
|
|
46
|
-
numPr: para.formatting?.numPr,
|
|
47
|
-
numPrFromStyle: para.formatting?.numPrFromStyle
|
|
48
|
-
};
|
|
49
|
-
const numbering = pkg?.numbering ? getCachedNumberingMap(pkg.numbering) : null;
|
|
50
|
-
const resolvedList = resolveParagraphList({
|
|
51
|
-
...sources,
|
|
52
|
-
inheritedReference: inheritedReference ?? null
|
|
53
|
-
}, numbering, standaloneListRendering(para));
|
|
54
|
-
const resolvedRendering = resolvedList.kind === "marker" ? resolvedList.rendering : void 0;
|
|
55
|
-
const counterAttrs = listLabelAttrsFromRendering(resolvedRendering, {
|
|
56
|
-
...sources,
|
|
57
|
-
numPrFromStyle: resolvedList.kind === "none" ? void 0 : inherited ?? sources.numPrFromStyle
|
|
58
|
-
});
|
|
59
|
-
if (sources.numPr === void 0 && sources.numPrFromStyle !== void 0 && inheritedReference === void 0 && para.propertyChanges !== void 0 && para.propertyChanges.length > 0) counterAttrs.numPr = paragraphNumberingAttr({ kind: "none" });
|
|
60
|
-
if (para.pPrMark) counterAttrs.pPrMark = para.pPrMark;
|
|
61
|
-
let originalList;
|
|
62
|
-
if (para.propertyChanges && ctx.opts.trackedChanges !== "clean") counterAttrs._propertyChanges = para.propertyChanges.map(({ previousFormatting, ...change }) => {
|
|
63
|
-
if (!previousFormatting) return change;
|
|
64
|
-
const previousInherited = ctx.paragraphNumbering(previousFormatting.styleId);
|
|
65
|
-
const previousInheritedReference = effectiveParagraphNumberingReference({
|
|
66
|
-
numPr: void 0,
|
|
67
|
-
numPrFromStyle: previousInherited
|
|
68
|
-
});
|
|
69
|
-
const previousSources = {
|
|
70
|
-
numPr: previousFormatting.numPr,
|
|
71
|
-
numPrFromStyle: previousFormatting.numPrFromStyle
|
|
72
|
-
};
|
|
73
|
-
const previousList = resolveParagraphList({
|
|
74
|
-
...previousSources,
|
|
75
|
-
inheritedReference: previousInheritedReference ?? null
|
|
76
|
-
}, numbering);
|
|
77
|
-
const previousRendering = previousList.kind === "marker" ? previousList.rendering : void 0;
|
|
78
|
-
originalList ??= previousRendering;
|
|
79
|
-
const previousAttrs = listLabelAttrsFromRendering(previousRendering, {
|
|
80
|
-
...previousSources,
|
|
81
|
-
numPrFromStyle: previousList.kind === "none" ? void 0 : previousInherited ?? previousSources.numPrFromStyle
|
|
82
|
-
});
|
|
83
|
-
return {
|
|
84
|
-
...change,
|
|
85
|
-
previousFormatting: {
|
|
86
|
-
...previousFormatting,
|
|
87
|
-
...previousAttrs,
|
|
88
|
-
...previousAttrs.numPr === void 0 ? { numPr: null } : { numPr: previousAttrs.numPr },
|
|
89
|
-
...previousAttrs.numPrFromStyle === void 0 ? { numPrFromStyle: null } : { numPrFromStyle: previousAttrs.numPrFromStyle }
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
|
-
const countedList = ctx.nextListLabel(counterAttrs, resolvedRendering);
|
|
94
|
-
const label = countedList.kind === "marker" ? countedList.label : void 0;
|
|
35
|
+
const { visibleList, label } = countParagraphList(ctx, pkg, para);
|
|
95
36
|
const inline = renderParagraphInline(ctx, pkg, para.content, para.paraId);
|
|
96
37
|
const styleId = para.formatting?.styleId;
|
|
97
38
|
const headingLevel = markdownHeadingLevel(ctx, para);
|
|
@@ -108,7 +49,6 @@ function renderParagraphBlock(ctx, pkg, para) {
|
|
|
108
49
|
type: "paragraph"
|
|
109
50
|
};
|
|
110
51
|
}
|
|
111
|
-
const visibleList = resolvedRendering ?? (sources.numPr?.kind === "levelOnly" ? void 0 : originalList);
|
|
112
52
|
if (visibleList && label !== void 0) return renderListItem(ctx, visibleList, label, inline);
|
|
113
53
|
ctx.listIndentWidths = [];
|
|
114
54
|
if (isQuoteStyle(styleId, ctx.builtInStyles)) return {
|
|
@@ -2,6 +2,7 @@ import { getHyperlinkRuns } from "../docx/hyperlinkParser.js";
|
|
|
2
2
|
import { RELATIONSHIP_TYPES, resolveRelationshipIdOfType, resolveRelativePath } from "../docx/relsParser.js";
|
|
3
3
|
import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
|
|
4
4
|
import { wrapComment, wrapDeletion, wrapInsertion, wrapMoveFrom, wrapMoveTo } from "./annotations.js";
|
|
5
|
+
import { countTextBoxLists } from "./countParagraphList.js";
|
|
5
6
|
import { escapeAltText, escapeInline, escapeLinkUrl } from "./escape.js";
|
|
6
7
|
import { registerImage } from "./images.js";
|
|
7
8
|
import { numberNoteReference, pushWarning } from "./internals.js";
|
|
@@ -251,7 +252,9 @@ function renderRunContent(ctx, pkg, content, paraId) {
|
|
|
251
252
|
pushWarning(ctx, `image rId=${item.image.rId ?? "(absent)"} not resolvable`);
|
|
252
253
|
break;
|
|
253
254
|
}
|
|
254
|
-
case "shape":
|
|
255
|
+
case "shape":
|
|
256
|
+
countTextBoxLists(ctx, pkg, item.shape);
|
|
257
|
+
pushWarning(ctx, "shape not representable in markdown");
|
|
255
258
|
}
|
|
256
259
|
return out;
|
|
257
260
|
}
|
|
@@ -1972,7 +1972,7 @@ function extractParagraphContent(originalParagraph, _documentCounts, emptyHyperl
|
|
|
1972
1972
|
currentTrackedChange = void 0;
|
|
1973
1973
|
return;
|
|
1974
1974
|
}
|
|
1975
|
-
const { anchorId } = expectTextBoxAnchorAttrs(node);
|
|
1975
|
+
const { anchorId, _docxRunFormatting: carried } = expectTextBoxAnchorAttrs(node);
|
|
1976
1976
|
if (textBoxAnchorMarkers.has(anchorId)) {
|
|
1977
1977
|
flushCurrentInline();
|
|
1978
1978
|
currentTrackedChange = void 0;
|
|
@@ -1986,8 +1986,10 @@ function extractParagraphContent(originalParagraph, _documentCounts, emptyHyperl
|
|
|
1986
1986
|
text: ""
|
|
1987
1987
|
}]
|
|
1988
1988
|
};
|
|
1989
|
-
|
|
1990
|
-
|
|
1989
|
+
if (carried === void 0) {
|
|
1990
|
+
const formatting = getAtomRunFormattingFromMarks(node.marks, formattingContext);
|
|
1991
|
+
if (formatting) marker.formatting = formatting;
|
|
1992
|
+
} else if (Object.keys(carried).length > 0) marker.formatting = structuredClone(carried);
|
|
1991
1993
|
restoreRunRecord(marker, node.marks);
|
|
1992
1994
|
textBoxAnchorMarkers.set(anchorId, marker);
|
|
1993
1995
|
anchorRun = marker;
|
|
@@ -2180,7 +2180,10 @@ function convertRunContent({ content, marks, formatting, textBoxAnchors, authore
|
|
|
2180
2180
|
const shp = content.shape;
|
|
2181
2181
|
if (shp.textBody) {
|
|
2182
2182
|
const anchorId = textBoxAnchors?.get(shp);
|
|
2183
|
-
return anchorId ? [schema.node("textBoxAnchor", {
|
|
2183
|
+
return anchorId ? [schema.node("textBoxAnchor", {
|
|
2184
|
+
anchorId,
|
|
2185
|
+
_docxRunFormatting: { ...authoredFormatting }
|
|
2186
|
+
}).mark(marks)] : [];
|
|
2184
2187
|
}
|
|
2185
2188
|
return [withRunBoundaryMarks(withAlternateContent(convertShape(shp, carriedRunFormatting(authoredFormatting)), replayableShapeAlternateContent(content), content.alternateContent), marks)];
|
|
2186
2189
|
}
|
|
@@ -19,7 +19,10 @@ const TextBoxAnchorExtension = createNodeExtension({
|
|
|
19
19
|
marks: "_",
|
|
20
20
|
atom: true,
|
|
21
21
|
selectable: false,
|
|
22
|
-
attrs: {
|
|
22
|
+
attrs: {
|
|
23
|
+
anchorId: {},
|
|
24
|
+
_docxRunFormatting: { default: null }
|
|
25
|
+
},
|
|
23
26
|
parseDOM: [{
|
|
24
27
|
tag: "span[data-docx-textbox-anchor]",
|
|
25
28
|
getAttrs(dom) {
|
|
@@ -48,6 +48,12 @@ export declare const hasReplacedAnnotations: ({ carried, leading, trailing }: Re
|
|
|
48
48
|
* replacement either.
|
|
49
49
|
*/
|
|
50
50
|
export declare const surveyReplacedAnnotations: (doc: Node, from: number, to: number) => ReplacedAnnotations;
|
|
51
|
+
/**
|
|
52
|
+
* {@link surveyReplacedAnnotations} over the span as it reads once accepted:
|
|
53
|
+
* what a replacement of it carries, which a direct edit of the same text
|
|
54
|
+
* carries too.
|
|
55
|
+
*/
|
|
56
|
+
export declare const surveyAcceptedReplacedAnnotations: (doc: Node, from: number, to: number) => ReplacedAnnotations;
|
|
51
57
|
type AnnotatedReplacementOptions = {
|
|
52
58
|
annotations: ReplacedAnnotations;
|
|
53
59
|
/** The replacement's own inline content, already carrying its formatting. */
|
|
@@ -197,19 +197,21 @@ const NOTHING_REPLACED = {
|
|
|
197
197
|
trailing: []
|
|
198
198
|
};
|
|
199
199
|
const hasReplacedAnnotations = ({ carried, leading, trailing }) => carried.length > 0 || leading.length > 0 || trailing.length > 0;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
200
|
+
const lendsMarks = (node, content) => {
|
|
201
|
+
switch (content) {
|
|
202
|
+
case "asWritten": return true;
|
|
203
|
+
case "asAccepted": return !node.marks.some(({ type }) => type.name === "deletion");
|
|
204
|
+
default: throw new Error(`Unhandled replaced content ${String(content)}`);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const surveyAnnotations = (doc, { from, to, content }) => {
|
|
206
208
|
if (to <= from) return NOTHING_REPLACED;
|
|
207
209
|
const carried = [];
|
|
208
210
|
const leading = [];
|
|
209
211
|
const trailing = [];
|
|
210
212
|
doc.nodesBetween(from, to, (node, position) => {
|
|
211
213
|
if (!node.isInline) return true;
|
|
212
|
-
for (const mark of node.marks) if (isCarried(mark) && !alreadyCarried(mark, carried)) carried.push(mark);
|
|
214
|
+
for (const mark of lendsMarks(node, content) ? node.marks : []) if (isCarried(mark) && !alreadyCarried(mark, carried)) carried.push(mark);
|
|
213
215
|
const placement = anchorPlacement(node);
|
|
214
216
|
if (placement === void 0 || position < from || position + node.nodeSize > to) return false;
|
|
215
217
|
(opensRange(node, placement) ? leading : trailing).push(node);
|
|
@@ -222,6 +224,26 @@ const surveyReplacedAnnotations = (doc, from, to) => {
|
|
|
222
224
|
};
|
|
223
225
|
};
|
|
224
226
|
/**
|
|
227
|
+
* The annotations carried by `[from, to)`. An anchor counts only when the
|
|
228
|
+
* range covers it whole: a partially covered atom is not deleted by the
|
|
229
|
+
* replacement either.
|
|
230
|
+
*/
|
|
231
|
+
const surveyReplacedAnnotations = (doc, from, to) => surveyAnnotations(doc, {
|
|
232
|
+
from,
|
|
233
|
+
to,
|
|
234
|
+
content: "asWritten"
|
|
235
|
+
});
|
|
236
|
+
/**
|
|
237
|
+
* {@link surveyReplacedAnnotations} over the span as it reads once accepted:
|
|
238
|
+
* what a replacement of it carries, which a direct edit of the same text
|
|
239
|
+
* carries too.
|
|
240
|
+
*/
|
|
241
|
+
const surveyAcceptedReplacedAnnotations = (doc, from, to) => surveyAnnotations(doc, {
|
|
242
|
+
from,
|
|
243
|
+
to,
|
|
244
|
+
content: "asAccepted"
|
|
245
|
+
});
|
|
246
|
+
/**
|
|
225
247
|
* Whether the span already contributed this mark, or one the mark would evict.
|
|
226
248
|
* Comments do not exclude each other, so every comment is collected; a
|
|
227
249
|
* hyperlink excludes its own type, so the first address in the span wins
|
|
@@ -476,4 +498,4 @@ const inheritedReplacementMarks = (doc, from, to) => {
|
|
|
476
498
|
const $from = doc.resolve(from);
|
|
477
499
|
return (to === from ? $from.marks() : $from.marksAcross(doc.resolve(to))) ?? Mark.none;
|
|
478
500
|
};
|
|
479
|
-
export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, joinCommentStretches, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyReplacedAnnotations };
|
|
501
|
+
export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, joinCommentStretches, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyAcceptedReplacedAnnotations, surveyReplacedAnnotations };
|
|
@@ -969,6 +969,13 @@ export type TextBoxAttrs = {
|
|
|
969
969
|
/** Internal inline position marker for an extracted text box block. */
|
|
970
970
|
export type TextBoxAnchorAttrs = {
|
|
971
971
|
anchorId: string;
|
|
972
|
+
/**
|
|
973
|
+
* The `w:rPr` of the run the text box came from, possibly empty. The
|
|
974
|
+
* anchor's formatting marks are rendered in the paragraph's style cascade
|
|
975
|
+
* and are not rebased when the paragraph is restyled, so the save writes
|
|
976
|
+
* this instead. Absent on an anchor not read from a package.
|
|
977
|
+
*/
|
|
978
|
+
_docxRunFormatting?: document_d_exports.TextFormatting;
|
|
972
979
|
};
|
|
973
980
|
/**
|
|
974
981
|
* Table node attributes
|
|
@@ -12,13 +12,21 @@ const readTextBoxAnchorAttrs = (node) => {
|
|
|
12
12
|
path: "textBoxAnchor.attrs.anchorId",
|
|
13
13
|
message: "Expected a non-empty string."
|
|
14
14
|
});
|
|
15
|
+
const runFormatting = node.attrs["_docxRunFormatting"];
|
|
16
|
+
if (runFormatting !== null && runFormatting !== void 0 && (typeof runFormatting !== "object" || Array.isArray(runFormatting))) issues.push({
|
|
17
|
+
path: "textBoxAnchor.attrs._docxRunFormatting",
|
|
18
|
+
message: "Expected run formatting or null."
|
|
19
|
+
});
|
|
15
20
|
if (issues.length > 0 || typeof anchorId !== "string") return {
|
|
16
21
|
ok: false,
|
|
17
22
|
issues
|
|
18
23
|
};
|
|
19
24
|
return {
|
|
20
25
|
ok: true,
|
|
21
|
-
value: { anchorId }
|
|
26
|
+
value: runFormatting === null || runFormatting === void 0 ? { anchorId } : {
|
|
27
|
+
anchorId,
|
|
28
|
+
_docxRunFormatting: runFormatting
|
|
29
|
+
}
|
|
22
30
|
};
|
|
23
31
|
};
|
|
24
32
|
const expectTextBoxAnchorAttrs = (node) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.0",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|
|
@@ -171,16 +171,14 @@
|
|
|
171
171
|
"perf": "bun scripts/profile-editor.ts"
|
|
172
172
|
},
|
|
173
173
|
"dependencies": {
|
|
174
|
-
"@stll/docx-core": "^0.
|
|
174
|
+
"@stll/docx-core": "^0.41.0",
|
|
175
175
|
"@stll/docx-utils": "^0.2.0",
|
|
176
176
|
"@stll/template-conditions": "^0.6.2",
|
|
177
|
-
"@stll/utf8-boundaries": "0.
|
|
177
|
+
"@stll/utf8-boundaries": "0.2.0",
|
|
178
178
|
"better-result": "3.0.1",
|
|
179
179
|
"csstype": "^3.1.3",
|
|
180
180
|
"dompurify": "^3.4.17",
|
|
181
|
-
"fast-xml-parser": "^5.11.2",
|
|
182
181
|
"hyphen": "1.14.1",
|
|
183
|
-
"jszip": "3.10.2",
|
|
184
182
|
"utif2": "^4.1.0",
|
|
185
183
|
"valibot": "1.5.0"
|
|
186
184
|
},
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { XmlElement, XmlNamespaceScope } from "./xmlParser.js";
|
|
2
|
-
type ParseXmlResult = {
|
|
3
|
-
status: "parsed";
|
|
4
|
-
value: XmlElement;
|
|
5
|
-
} | {
|
|
6
|
-
status: "unsupported";
|
|
7
|
-
};
|
|
8
|
-
type XmlSourceRange = {
|
|
9
|
-
readonly start: number;
|
|
10
|
-
readonly end: number;
|
|
11
|
-
};
|
|
12
|
-
/** Exact element source ranges, available only from the opt-in source-range reader. */
|
|
13
|
-
export declare const getXmlSourceRange: (element: XmlElement) => XmlSourceRange | undefined;
|
|
14
|
-
/**
|
|
15
|
-
* Parse ordinary OOXML into Folio's existing element representation in one
|
|
16
|
-
* pass. Unsupported or malformed constructs return a sentinel so callers can
|
|
17
|
-
* retain the general-purpose parser as a compatibility fallback.
|
|
18
|
-
*/
|
|
19
|
-
type AttributeValueSpan = {
|
|
20
|
-
start: number;
|
|
21
|
-
end: number;
|
|
22
|
-
};
|
|
23
|
-
type OpenTagVisitor = (element: XmlElement, attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>) => ReadonlyMap<string, string> | null;
|
|
24
|
-
type OpenTagScanVisitor = (...args: Parameters<OpenTagVisitor>) => null;
|
|
25
|
-
type ElementScanOptions = {
|
|
26
|
-
element: XmlElement;
|
|
27
|
-
attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>;
|
|
28
|
-
nameEnd: number;
|
|
29
|
-
parent: XmlElement | undefined;
|
|
30
|
-
};
|
|
31
|
-
type ElementScanVisitor = (options: ElementScanOptions) => void;
|
|
32
|
-
/**
|
|
33
|
-
* @param inheritedNamespaceScope the bindings in scope around `xml`, for a
|
|
34
|
-
* fragment captured out of a part whose root declared them.
|
|
35
|
-
*/
|
|
36
|
-
export declare const parseStreamingXml: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
|
|
37
|
-
/** Retain source spans for every element without changing the parsed namespace tree. */
|
|
38
|
-
export declare const parseStreamingXmlWithSourceRanges: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
|
|
39
|
-
/** Retain the body tree while collecting identities for import repair. */
|
|
40
|
-
export declare const parseStreamingXmlWithIdentityVisitor: (xml: string, visitOpenTag: OpenTagScanVisitor) => ParseXmlResult;
|
|
41
|
-
/** Rewrite selected decimal attribute values while preserving every other source byte. */
|
|
42
|
-
export declare const rewriteStreamingXmlDecimalAttributes: (xml: string, visitOpenTag: OpenTagVisitor) => {
|
|
43
|
-
status: "rewritten";
|
|
44
|
-
value: string;
|
|
45
|
-
} | {
|
|
46
|
-
status: "unsupported";
|
|
47
|
-
};
|
|
48
|
-
/** Visit identity attributes and namespace bindings without building an XML tree. */
|
|
49
|
-
export declare const scanStreamingXmlNumericIdAttributes: (xml: string, visitOpenTag: OpenTagScanVisitor) => {
|
|
50
|
-
status: "unsupported";
|
|
51
|
-
} | {
|
|
52
|
-
status: "scanned";
|
|
53
|
-
};
|
|
54
|
-
/** Visit namespace-resolved start tags and source spans without building a tree. */
|
|
55
|
-
export declare const scanStreamingXmlElements: (xml: string, visitElement: ElementScanVisitor) => {
|
|
56
|
-
status: "unsupported";
|
|
57
|
-
} | {
|
|
58
|
-
status: "scanned";
|
|
59
|
-
};
|
|
60
|
-
export {};
|