@stll/folio-core 0.58.3 → 0.58.5
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/controller/canonicalListRendering.d.ts +10 -0
- package/dist/controller/canonicalListRendering.js +169 -0
- package/dist/controller/canonicalSession.js +26 -6
- package/dist/docx/blockContentParser.d.ts +1 -5
- package/dist/docx/blockContentParser.js +1 -74
- package/dist/docx/foldedListNumberFields.d.ts +3 -1
- package/dist/docx/foldedListNumberFields.js +4 -2
- package/dist/docx/listMarkerComputation.d.ts +18 -0
- package/dist/docx/listMarkerComputation.js +77 -0
- package/dist/docx/paragraphPropertySource.d.ts +9 -1
- package/dist/docx/paragraphPropertySource.js +26 -2
- package/dist/generated/text_shaper_bg.wasm +0 -0
- package/dist/internal/paragraphFormattingSerialization.js +5 -2
- package/dist/markdown/renderBlock.js +48 -44
- package/dist/markdown/renderParagraph.d.ts +6 -2
- package/dist/markdown/renderParagraph.js +21 -15
- package/package.json +2 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { DocumentOp } from "@stll/docx-core/ops";
|
|
3
|
+
//#region src/controller/canonicalListRendering.d.ts
|
|
4
|
+
type CanonicalListNormalization = {
|
|
5
|
+
document: document_d_exports.Document;
|
|
6
|
+
inverse: DocumentOp[];
|
|
7
|
+
};
|
|
8
|
+
declare const normalizeCanonicalListRendering: (document: document_d_exports.Document) => CanonicalListNormalization;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { normalizeCanonicalListRendering };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { foldListNumberFields, planParagraphListNumberFold, unfoldedListNumberContent } from "../docx/foldedListNumberFields.js";
|
|
2
|
+
import { computeListMarker } from "../docx/listMarkerComputation.js";
|
|
3
|
+
import { listRenderingDefinitionsMatch } from "../docx/listRendering.js";
|
|
4
|
+
import { computeListRendering, getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
5
|
+
import { mergeParagraphNumbering, paragraphNumberingReferenceId, sameStatedParagraphNumbering } from "../docx/numberingReference.js";
|
|
6
|
+
import { assignParagraphIndentationProjection, cloneParagraphWithPropertySource, paragraphFormattingWithAuthoredIndentation, sameParagraphIndentationProjection } from "../docx/paragraphPropertySource.js";
|
|
7
|
+
import { paragraphIndentationFromFormatting } from "../prosemirror/paragraphIndentation.js";
|
|
8
|
+
import { listIndentationProvenancePatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
|
|
9
|
+
import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
|
|
10
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
11
|
+
import { panic } from "better-result";
|
|
12
|
+
import { DOCUMENT_OP_TYPES, EMPTY_PROPERTY_SETS, OP_STORIES, registerParagraphPropertySnapshot, withBodyContent } from "@stll/docx-core/ops";
|
|
13
|
+
//#region src/controller/canonicalListRendering.ts
|
|
14
|
+
/** Canonical edits retain authored fields; list rendering follows the current numbering. */
|
|
15
|
+
const normalizeCanonicalListRendering = (document) => {
|
|
16
|
+
const inverse = [];
|
|
17
|
+
const styles = createStyleResolver(document.package.styles);
|
|
18
|
+
const numbering = document.package.numbering === void 0 ? null : getCachedNumberingMap(document.package.numbering);
|
|
19
|
+
const normalizeBody = (body, story) => {
|
|
20
|
+
const counters = {
|
|
21
|
+
numbering,
|
|
22
|
+
listCounters: /* @__PURE__ */ new Map(),
|
|
23
|
+
abstractCounters: /* @__PURE__ */ new Map(),
|
|
24
|
+
restartedNumIds: /* @__PURE__ */ new Set(),
|
|
25
|
+
previousList: {
|
|
26
|
+
abstractNumId: null,
|
|
27
|
+
fromStyle: false,
|
|
28
|
+
numId: null
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const content = body.content.map((paragraph) => {
|
|
32
|
+
if (paragraph.type !== "paragraph") return paragraph;
|
|
33
|
+
registerParagraphPropertySnapshot({
|
|
34
|
+
paragraph,
|
|
35
|
+
snapshot: paragraphFormattingWithAuthoredIndentation(paragraph)
|
|
36
|
+
});
|
|
37
|
+
const formatting = paragraph.formatting;
|
|
38
|
+
const numPr = mergeParagraphNumbering(formatting?.numPrFromStyle, formatting?.numPr);
|
|
39
|
+
if (paragraph.listRendering === void 0 && formatting?.numPrFromStyle === void 0 && (numPr?.kind !== "reference" || numbering === null)) {
|
|
40
|
+
computeListMarker(paragraph, counters);
|
|
41
|
+
return paragraph;
|
|
42
|
+
}
|
|
43
|
+
const definition = numPr?.kind === "reference" && numbering !== null ? computeListRendering(numPr, numbering) : null;
|
|
44
|
+
const cached = paragraph.listRendering;
|
|
45
|
+
const rendering = definition !== null && cached !== void 0 && listRenderingDefinitionsMatch(cached, definition) ? { ...cached } : definition;
|
|
46
|
+
const next = cloneParagraphWithPropertySource(paragraph, {});
|
|
47
|
+
if (formatting?.numPrFromStyle !== void 0 && formatting.numPr !== void 0 && paragraphNumberingReferenceId(numPr) !== paragraphNumberingReferenceId(formatting.numPrFromStyle)) {
|
|
48
|
+
next.formatting = { ...formatting };
|
|
49
|
+
delete next.formatting.numPrFromStyle;
|
|
50
|
+
inverse.push({
|
|
51
|
+
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
52
|
+
story,
|
|
53
|
+
blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
|
|
54
|
+
patch: { numPrFromStyle: formatting.numPrFromStyle }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (formatting?.numPrFromStyle !== void 0 && numPr?.kind === "reference" && !sameStatedParagraphNumbering(formatting.numPr, numPr)) {
|
|
58
|
+
next.formatting = {
|
|
59
|
+
...next.formatting,
|
|
60
|
+
numPr
|
|
61
|
+
};
|
|
62
|
+
inverse.push({
|
|
63
|
+
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
64
|
+
story,
|
|
65
|
+
blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
|
|
66
|
+
patch: { numPr: formatting.numPr ?? null }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const authoredFormatting = paragraphFormattingWithAuthoredIndentation(next);
|
|
70
|
+
assignParagraphIndentationProjection({
|
|
71
|
+
paragraph: next,
|
|
72
|
+
authored: authoredFormatting,
|
|
73
|
+
inherited: paragraphIndentationFromFormatting(listIndentationProvenancePatch({
|
|
74
|
+
direct: paragraphIndentationFromFormatting(authoredFormatting),
|
|
75
|
+
styleFormatting: styles.resolveParagraphStyle(next.formatting?.styleId).paragraphFormatting,
|
|
76
|
+
numberingSource: next.formatting?.numPrFromStyle === void 0 ? "paragraph" : "style",
|
|
77
|
+
numPr: numPr?.kind === "reference" ? {
|
|
78
|
+
numId: numPr.numId,
|
|
79
|
+
ilvl: numPr.ilvl ?? 0
|
|
80
|
+
} : void 0,
|
|
81
|
+
numbering
|
|
82
|
+
})._resolvedFormatting) ?? {}
|
|
83
|
+
});
|
|
84
|
+
if (canonicalJson(paragraphIndentationFromFormatting(next.formatting)) !== canonicalJson(paragraphIndentationFromFormatting(formatting))) inverse.unshift({
|
|
85
|
+
type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
|
|
86
|
+
story,
|
|
87
|
+
blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
|
|
88
|
+
patch: {
|
|
89
|
+
indentLeft: formatting?.indentLeft ?? null,
|
|
90
|
+
indentRight: formatting?.indentRight ?? null,
|
|
91
|
+
indentFirstLine: formatting?.indentFirstLine ?? null,
|
|
92
|
+
hangingIndent: formatting?.hangingIndent ?? null
|
|
93
|
+
},
|
|
94
|
+
whenEmpty: formatting === void 0 ? EMPTY_PROPERTY_SETS.OMIT : EMPTY_PROPERTY_SETS.KEEP
|
|
95
|
+
});
|
|
96
|
+
if (rendering === null) delete next.listRendering;
|
|
97
|
+
else {
|
|
98
|
+
const fields = foldListNumberFields(paragraph.content.map(unfoldedListNumberContent), () => void 0).fieldCount;
|
|
99
|
+
if (fields === 0) delete rendering.implicitChildLevelAdvances;
|
|
100
|
+
else rendering.implicitChildLevelAdvances = fields;
|
|
101
|
+
const template = rendering.markerTemplate ?? rendering.marker;
|
|
102
|
+
const fold = planParagraphListNumberFold(paragraph.content, !rendering.isBullet && template !== "" && !template.includes(" "));
|
|
103
|
+
if (fold.suffix === void 0) delete rendering.foldedMarkerSuffix;
|
|
104
|
+
else rendering.foldedMarkerSuffix = fold.suffix;
|
|
105
|
+
const child = numbering?.getLevel(rendering.numId, rendering.level + 1)?.pPr;
|
|
106
|
+
if (fold.suffix !== void 0 && child?.hangingIndent === true && child.indentFirstLine !== void 0 && child.indentFirstLine < 0) rendering.markerSecondSlotOffsetTwips = -child.indentFirstLine;
|
|
107
|
+
else delete rendering.markerSecondSlotOffsetTwips;
|
|
108
|
+
rendering.marker = rendering.foldedMarkerSuffix !== void 0 && !template.includes(" ") ? `${template}\t${rendering.foldedMarkerSuffix}` : template;
|
|
109
|
+
next.listRendering = rendering;
|
|
110
|
+
}
|
|
111
|
+
computeListMarker(next, counters);
|
|
112
|
+
registerParagraphPropertySnapshot({
|
|
113
|
+
paragraph: next,
|
|
114
|
+
snapshot: authoredFormatting
|
|
115
|
+
});
|
|
116
|
+
if (sameParagraphIndentationProjection({
|
|
117
|
+
left: next,
|
|
118
|
+
right: paragraph
|
|
119
|
+
}) && canonicalJson(next.formatting) === canonicalJson(formatting) && canonicalJson(next.listRendering) === canonicalJson(cached)) return paragraph;
|
|
120
|
+
return next;
|
|
121
|
+
});
|
|
122
|
+
return content.every((paragraph, index) => paragraph === body.content[index]) ? body : {
|
|
123
|
+
...body,
|
|
124
|
+
...withBodyContent(body, content)
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
const pkg = document.package;
|
|
128
|
+
const body = normalizeBody(pkg.document, OP_STORIES.MAIN);
|
|
129
|
+
const normalizeParts = (parts, kind) => {
|
|
130
|
+
if (parts === void 0) return parts;
|
|
131
|
+
const normalized = new Map([...parts].map(([id, part]) => [id, normalizeBody(part, {
|
|
132
|
+
kind,
|
|
133
|
+
rId: id
|
|
134
|
+
})]));
|
|
135
|
+
return [...normalized].every(([id, part]) => part === parts.get(id)) ? parts : normalized;
|
|
136
|
+
};
|
|
137
|
+
const normalizeNotes = (notes, kind) => {
|
|
138
|
+
if (notes === void 0) return notes;
|
|
139
|
+
const normalized = notes.map((note) => normalizeBody(note, {
|
|
140
|
+
kind,
|
|
141
|
+
id: note.id
|
|
142
|
+
}));
|
|
143
|
+
return normalized.every((note, index) => note === notes[index]) ? notes : normalized;
|
|
144
|
+
};
|
|
145
|
+
const headers = normalizeParts(pkg.headers, "header");
|
|
146
|
+
const footers = normalizeParts(pkg.footers, "footer");
|
|
147
|
+
const footnotes = normalizeNotes(pkg.footnotes, "footnote");
|
|
148
|
+
const endnotes = normalizeNotes(pkg.endnotes, "endnote");
|
|
149
|
+
if (body === pkg.document && headers === pkg.headers && footers === pkg.footers && footnotes === pkg.footnotes && endnotes === pkg.endnotes) return {
|
|
150
|
+
document,
|
|
151
|
+
inverse
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
inverse,
|
|
155
|
+
document: {
|
|
156
|
+
...document,
|
|
157
|
+
package: {
|
|
158
|
+
...pkg,
|
|
159
|
+
document: body,
|
|
160
|
+
...headers === void 0 ? {} : { headers },
|
|
161
|
+
...footers === void 0 ? {} : { footers },
|
|
162
|
+
...footnotes === void 0 ? {} : { footnotes },
|
|
163
|
+
...endnotes === void 0 ? {} : { endnotes }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
//#endregion
|
|
169
|
+
export { normalizeCanonicalListRendering };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { splitsGraphemeCluster, splitsSurrogatePair } from "../ai-edits/character-boundaries.js";
|
|
2
2
|
import { rangedCommentIds, visitCommentMarkers } from "../docx/commentAnchorIndex.js";
|
|
3
|
-
import { cloneDocumentWithParagraphPropertySources, copyDocumentParagraphPropertySourceContract, copyParagraphPropertySource } from "../docx/paragraphPropertySource.js";
|
|
3
|
+
import { cloneDocumentWithParagraphPropertySources, copyDocumentParagraphPropertySourceContract, copyParagraphPropertySource, restoreParagraphIndentationProjection } from "../docx/paragraphPropertySource.js";
|
|
4
4
|
import { canonicalSelectionRange } from "../prosemirror/canonicalSelectionRange.js";
|
|
5
5
|
import { collectPairedBookmarkIds, footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
6
6
|
import { markPackageChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
7
7
|
import { runFormattingPatchFromMarks } from "../prosemirror/runFormattingFromMarks.js";
|
|
8
8
|
import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
|
|
9
9
|
import { canonicalInlineSourceIssue, canonicalNativeCells, projectCanonicalInline } from "./canonicalInlineProjection.js";
|
|
10
|
+
import { normalizeCanonicalListRendering } from "./canonicalListRendering.js";
|
|
10
11
|
import { restoreCanonicalSelection } from "./canonicalSelection.js";
|
|
11
12
|
import { mapTocSelection } from "./canonicalTocSelection.js";
|
|
12
13
|
import { Result, TaggedError, panic } from "better-result";
|
|
@@ -237,7 +238,7 @@ const changedStories = ({ document, previous }) => {
|
|
|
237
238
|
}
|
|
238
239
|
return stories;
|
|
239
240
|
};
|
|
240
|
-
const preservePropertySources = ({ target, source, stories = documentStories(source) }) => {
|
|
241
|
+
const preservePropertySources = ({ target, source, stories = documentStories(source), indentation = "derive" }) => {
|
|
241
242
|
for (const story of stories) {
|
|
242
243
|
const originals = findStoryBody(source, story)?.content ?? [];
|
|
243
244
|
const derived = findStoryBody(target, story)?.content ?? [];
|
|
@@ -246,7 +247,13 @@ const preservePropertySources = ({ target, source, stories = documentStories(sou
|
|
|
246
247
|
for (const [index, original] of originals.entries()) {
|
|
247
248
|
if (original.type !== "paragraph") continue;
|
|
248
249
|
const next = original.paraId === void 0 ? derived.at(index) : paragraphs.get(original.paraId);
|
|
249
|
-
if (next?.type === "paragraph" && next !== original)
|
|
250
|
+
if (next?.type === "paragraph" && next !== original) {
|
|
251
|
+
copyParagraphPropertySource(next, original);
|
|
252
|
+
if (indentation === "restore") restoreParagraphIndentationProjection({
|
|
253
|
+
target: next,
|
|
254
|
+
source: original
|
|
255
|
+
});
|
|
256
|
+
}
|
|
250
257
|
}
|
|
251
258
|
}
|
|
252
259
|
copyDocumentParagraphPropertySourceContract(target, source);
|
|
@@ -998,6 +1005,14 @@ var CanonicalSession = class {
|
|
|
998
1005
|
const applied = stagedApplied === void 0 ? applyDocumentOps(this.currentDocument, ops) : Result.ok(stagedApplied);
|
|
999
1006
|
if (applied.isErr()) return refuse(applied.error.message);
|
|
1000
1007
|
if (applied.value.inverse.length === 0) return noChange("The intent makes no document change.");
|
|
1008
|
+
if (propertySourceDocument !== void 0) preservePropertySources({
|
|
1009
|
+
target: applied.value.document,
|
|
1010
|
+
source: propertySourceDocument,
|
|
1011
|
+
indentation: "restore"
|
|
1012
|
+
});
|
|
1013
|
+
const normalized = normalizeCanonicalListRendering(applied.value.document);
|
|
1014
|
+
applied.value.document = normalized.document;
|
|
1015
|
+
applied.value.inverse = normalized.inverse.concat(applied.value.inverse);
|
|
1001
1016
|
const changed = changedStories({
|
|
1002
1017
|
document: applied.value.document,
|
|
1003
1018
|
previous: this.currentDocument
|
|
@@ -1138,13 +1153,18 @@ const createCanonicalSession = (document, styles) => {
|
|
|
1138
1153
|
const unsupported = unsupportedSeedReason(document);
|
|
1139
1154
|
if (unsupported !== null) return refuse(unsupported);
|
|
1140
1155
|
const owned = cloneDocumentWithParagraphPropertySources(document);
|
|
1141
|
-
const
|
|
1156
|
+
const identified = normalizeForOps(owned);
|
|
1142
1157
|
preservePropertySources({
|
|
1143
|
-
target:
|
|
1158
|
+
target: identified,
|
|
1144
1159
|
source: owned
|
|
1145
1160
|
});
|
|
1146
|
-
const validated = validateOpsDocument(
|
|
1161
|
+
const validated = validateOpsDocument(identified);
|
|
1147
1162
|
if (validated.isErr()) return refuse(validated.error.message);
|
|
1163
|
+
const normalized = normalizeCanonicalListRendering(identified).document;
|
|
1164
|
+
preservePropertySources({
|
|
1165
|
+
target: normalized,
|
|
1166
|
+
source: owned
|
|
1167
|
+
});
|
|
1148
1168
|
const projected = project({
|
|
1149
1169
|
document: normalized,
|
|
1150
1170
|
styles
|
|
@@ -3,6 +3,7 @@ import { NumberingMap } from "./numberingParser.js";
|
|
|
3
3
|
import { ParseContext } from "./parseContext.js";
|
|
4
4
|
import { XmlElement } from "./xmlParser.js";
|
|
5
5
|
import { OwnedElsewhere } from "./containerChildren.js";
|
|
6
|
+
import { PreviousListState } from "./listMarkerComputation.js";
|
|
6
7
|
import { PreviewLedger } from "./previewBudget.js";
|
|
7
8
|
import { StyleMap } from "./styleParser.js";
|
|
8
9
|
//#region src/docx/blockContentParser.d.ts
|
|
@@ -16,11 +17,6 @@ type ParseBlockContentOptions = {
|
|
|
16
17
|
type BlockContentScope = ParseBlockContentOptions & {
|
|
17
18
|
previews: PreviewLedger;
|
|
18
19
|
};
|
|
19
|
-
type PreviousListState = {
|
|
20
|
-
abstractNumId: number | null;
|
|
21
|
-
fromStyle: boolean;
|
|
22
|
-
numId: number | null;
|
|
23
|
-
};
|
|
24
20
|
type ParseBlockContentState = {
|
|
25
21
|
listCounters: Map<number, number[]>;
|
|
26
22
|
abstractCounters: Map<number, number[]>;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { blockCustomXmlShell } from "./blockCustomXmlShell.js";
|
|
2
2
|
import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
|
|
3
|
-
import { bulletMarkerFontName, convertBulletToUnicode } from "./bulletMarkers.js";
|
|
4
3
|
import { CAPTURE, dispatchChildrenWithContext, ownedElsewhere, withPreservedChildren } from "./containerChildren.js";
|
|
5
|
-
import {
|
|
6
|
-
import { formatOoxmlCounter } from "./ooxmlCounterFormatter.js";
|
|
4
|
+
import { computeListMarker } from "./listMarkerComputation.js";
|
|
7
5
|
import { parseParagraph } from "./paragraphParser.js";
|
|
8
6
|
import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
|
|
9
7
|
import { standalonePreviewLedger } from "./previewBudget.js";
|
|
@@ -11,77 +9,6 @@ import { captureSdtSiblingMarkers, parseSdtProperties } from "./sdtProperties.js
|
|
|
11
9
|
import { parseTable } from "./tableParser.js";
|
|
12
10
|
import { findWordprocessingChild, getLocalName, mergeXmlnsDeclarations, selectAlternateContentBranch } from "./xmlParser.js";
|
|
13
11
|
//#region src/docx/blockContentParser.ts
|
|
14
|
-
const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
|
|
15
|
-
const listRendering = paragraph.listRendering;
|
|
16
|
-
if (!listRendering || !numbering) {
|
|
17
|
-
previousList.abstractNumId = null;
|
|
18
|
-
previousList.fromStyle = false;
|
|
19
|
-
previousList.numId = null;
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const { numId, level } = listRendering;
|
|
23
|
-
if (!isNumberingReference(numId)) {
|
|
24
|
-
previousList.abstractNumId = null;
|
|
25
|
-
previousList.fromStyle = false;
|
|
26
|
-
previousList.numId = null;
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const firstEncounter = !listCounters.has(numId);
|
|
30
|
-
if (firstEncounter) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
|
|
31
|
-
let counters = listCounters.get(numId);
|
|
32
|
-
if (!counters) return;
|
|
33
|
-
const abstractNumId = numbering.getAbstractNumId(numId);
|
|
34
|
-
const styleNumbering = paragraph.formatting?.numPrFromStyle;
|
|
35
|
-
const resumesRestartedInstance = firstEncounter && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.numId !== null && previousList.numId !== numId && restartedNumIds.has(previousList.numId);
|
|
36
|
-
const resumesStyleInstance = firstEncounter && !styleNumbering && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.fromStyle === true;
|
|
37
|
-
if (abstractNumId !== null && (styleNumbering || resumesRestartedInstance || resumesStyleInstance)) {
|
|
38
|
-
const latestAbstractCounters = abstractCounters.get(abstractNumId);
|
|
39
|
-
if (latestAbstractCounters) {
|
|
40
|
-
counters = latestAbstractCounters;
|
|
41
|
-
listCounters.set(numId, counters);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (listRendering.startOverride !== void 0 || resumesRestartedInstance || previousList.numId === numId && restartedNumIds.has(numId)) restartedNumIds.add(numId);
|
|
45
|
-
if (abstractNumId !== null && level > 0) {
|
|
46
|
-
const latestAbstractCounters = abstractCounters.get(abstractNumId);
|
|
47
|
-
if (counters.slice(0, level).every(Number.isNaN)) for (let i = 0; i < level; i += 1) {
|
|
48
|
-
const latestCounter = latestAbstractCounters?.[i];
|
|
49
|
-
counters[i] = latestCounter !== void 0 && !Number.isNaN(latestCounter) ? latestCounter : numbering.getLevel(numId, i)?.start ?? 1;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (Number.isNaN(counters[level])) counters[level] = (numbering.getLevel(numId, level)?.start ?? 1) - 1;
|
|
53
|
-
counters[level] = (counters[level] ?? 0) + 1;
|
|
54
|
-
for (let i = level + 1; i < counters.length; i += 1) counters[i] = NaN;
|
|
55
|
-
const childAdvances = listRendering.implicitChildLevelAdvances ?? 0;
|
|
56
|
-
if (childAdvances > 0 && level + 1 < counters.length) {
|
|
57
|
-
const childCounter = counters[level + 1];
|
|
58
|
-
counters[level + 1] = (childCounter === void 0 || Number.isNaN(childCounter) ? 0 : childCounter) + childAdvances;
|
|
59
|
-
}
|
|
60
|
-
if (abstractNumId !== null) abstractCounters.set(abstractNumId, counters);
|
|
61
|
-
previousList.abstractNumId = abstractNumId;
|
|
62
|
-
previousList.fromStyle = Boolean(styleNumbering);
|
|
63
|
-
previousList.numId = numId;
|
|
64
|
-
const pattern = listRendering.marker;
|
|
65
|
-
if (listRendering.isBullet) {
|
|
66
|
-
listRendering.marker = convertBulletToUnicode(pattern || "", bulletMarkerFontName(listRendering.markerFormatting));
|
|
67
|
-
previousList.abstractNumId = null;
|
|
68
|
-
previousList.fromStyle = false;
|
|
69
|
-
previousList.numId = null;
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
let computedMarker = pattern;
|
|
73
|
-
const useLegalNumbering = numbering.getLevel(numId, level)?.isLgl === true || listRendering.isLegal === true;
|
|
74
|
-
for (let lvl = 0; lvl <= level; lvl += 1) {
|
|
75
|
-
const placeholder = `%${lvl + 1}`;
|
|
76
|
-
if (computedMarker.includes(placeholder)) {
|
|
77
|
-
const value = counters[lvl] ?? 0;
|
|
78
|
-
const levelInfo = numbering.getLevel(numId, lvl);
|
|
79
|
-
const formatted = formatOoxmlCounter(value, useLegalNumbering ? "decimal" : levelInfo?.numFmt || "decimal");
|
|
80
|
-
computedMarker = computedMarker.replaceAll(placeholder, formatted);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
listRendering.marker = computedMarker;
|
|
84
|
-
};
|
|
85
12
|
const withContainerXmlns = (state, element) => ({
|
|
86
13
|
...state,
|
|
87
14
|
options: {
|
|
@@ -72,6 +72,8 @@ type ListNumberFoldPlan = {
|
|
|
72
72
|
* further in is on the line.
|
|
73
73
|
*/
|
|
74
74
|
declare const planListNumberFold: (items: readonly ListNumberFoldItem[], markerShowsFields: boolean) => ListNumberFoldPlan;
|
|
75
|
+
/** Derive the marker fold from current captures, never from cached rendering. */
|
|
76
|
+
declare const planParagraphListNumberFold: (content: readonly document_d_exports.ParagraphContent[], showsFields: boolean) => ListNumberFoldPlan;
|
|
75
77
|
/** The item a capture stands for, which shows on the line; any other item is itself. */
|
|
76
78
|
declare const unfoldedListNumberContent: (content: document_d_exports.ParagraphContent) => document_d_exports.ParagraphContent;
|
|
77
79
|
/**
|
|
@@ -82,4 +84,4 @@ declare const unfoldedListNumberContent: (content: document_d_exports.ParagraphC
|
|
|
82
84
|
*/
|
|
83
85
|
declare const normalizeFoldedListNumbers: (paragraph: document_d_exports.Paragraph) => void;
|
|
84
86
|
//#endregion
|
|
85
|
-
export { ListNumberFieldFold, ListNumberFoldItem, ListNumberFoldPlan, foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, unfoldedListNumberContent };
|
|
87
|
+
export { ListNumberFieldFold, ListNumberFoldItem, ListNumberFoldPlan, foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, planParagraphListNumberFold, unfoldedListNumberContent };
|
|
@@ -202,6 +202,8 @@ const foldItemOf = (content) => {
|
|
|
202
202
|
} : { kind: "tab" };
|
|
203
203
|
return isZeroWidth(content) ? { kind: "hidden" } : { kind: "shown" };
|
|
204
204
|
};
|
|
205
|
+
/** Derive the marker fold from current captures, never from cached rendering. */
|
|
206
|
+
const planParagraphListNumberFold = (content, showsFields) => planListNumberFold(content.map(foldItemOf), showsFields);
|
|
205
207
|
/** The item a capture stands for, which shows on the line; any other item is itself. */
|
|
206
208
|
const unfoldedListNumberContent = (content) => {
|
|
207
209
|
const folded = foldedListNumberOf(content);
|
|
@@ -253,7 +255,7 @@ const normalizeFoldedListNumbers = (paragraph) => {
|
|
|
253
255
|
});
|
|
254
256
|
const content = paragraph.content;
|
|
255
257
|
if (!state.showsFields && !content.some(isFoldedListNumberCapture)) return;
|
|
256
|
-
const plan =
|
|
258
|
+
const plan = planParagraphListNumberFold(content, state.showsFields);
|
|
257
259
|
const next = [];
|
|
258
260
|
for (const index of plan.order) {
|
|
259
261
|
const item = content[index];
|
|
@@ -264,4 +266,4 @@ const normalizeFoldedListNumbers = (paragraph) => {
|
|
|
264
266
|
if (rendering && state.showsFields) rendering.marker = listMarkerWithFold(state, plan.suffix);
|
|
265
267
|
};
|
|
266
268
|
//#endregion
|
|
267
|
-
export { foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, unfoldedListNumberContent };
|
|
269
|
+
export { foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, planParagraphListNumberFold, unfoldedListNumberContent };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { NumberingMap } from "./numberingParser.js";
|
|
3
|
+
//#region src/docx/listMarkerComputation.d.ts
|
|
4
|
+
type PreviousListState = {
|
|
5
|
+
abstractNumId: number | null;
|
|
6
|
+
fromStyle: boolean;
|
|
7
|
+
numId: number | null;
|
|
8
|
+
};
|
|
9
|
+
type ComputeListMarkerOptions = {
|
|
10
|
+
numbering: NumberingMap | null;
|
|
11
|
+
listCounters: Map<number, number[]>;
|
|
12
|
+
abstractCounters: Map<number, number[]>;
|
|
13
|
+
restartedNumIds: Set<number>;
|
|
14
|
+
previousList: PreviousListState;
|
|
15
|
+
};
|
|
16
|
+
declare const computeListMarker: (paragraph: document_d_exports.Paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }: ComputeListMarkerOptions) => void;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ComputeListMarkerOptions, PreviousListState, computeListMarker };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { bulletMarkerFontName, convertBulletToUnicode } from "./bulletMarkers.js";
|
|
2
|
+
import { isNumberingReference } from "./numberingReference.js";
|
|
3
|
+
import { formatOoxmlCounter } from "./ooxmlCounterFormatter.js";
|
|
4
|
+
//#region src/docx/listMarkerComputation.ts
|
|
5
|
+
const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
|
|
6
|
+
const listRendering = paragraph.listRendering;
|
|
7
|
+
if (!listRendering || !numbering) {
|
|
8
|
+
previousList.abstractNumId = null;
|
|
9
|
+
previousList.fromStyle = false;
|
|
10
|
+
previousList.numId = null;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const { numId, level } = listRendering;
|
|
14
|
+
if (!isNumberingReference(numId)) {
|
|
15
|
+
previousList.abstractNumId = null;
|
|
16
|
+
previousList.fromStyle = false;
|
|
17
|
+
previousList.numId = null;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const firstEncounter = !listCounters.has(numId);
|
|
21
|
+
if (firstEncounter) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
|
|
22
|
+
let counters = listCounters.get(numId);
|
|
23
|
+
if (!counters) return;
|
|
24
|
+
const abstractNumId = numbering.getAbstractNumId(numId);
|
|
25
|
+
const styleNumbering = paragraph.formatting?.numPrFromStyle;
|
|
26
|
+
const resumesRestartedInstance = firstEncounter && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.numId !== null && previousList.numId !== numId && restartedNumIds.has(previousList.numId);
|
|
27
|
+
const resumesStyleInstance = firstEncounter && !styleNumbering && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.fromStyle === true;
|
|
28
|
+
if (abstractNumId !== null && (styleNumbering || resumesRestartedInstance || resumesStyleInstance)) {
|
|
29
|
+
const latestAbstractCounters = abstractCounters.get(abstractNumId);
|
|
30
|
+
if (latestAbstractCounters) {
|
|
31
|
+
counters = latestAbstractCounters;
|
|
32
|
+
listCounters.set(numId, counters);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (listRendering.startOverride !== void 0 || resumesRestartedInstance || previousList.numId === numId && restartedNumIds.has(numId)) restartedNumIds.add(numId);
|
|
36
|
+
if (abstractNumId !== null && level > 0) {
|
|
37
|
+
const latestAbstractCounters = abstractCounters.get(abstractNumId);
|
|
38
|
+
if (counters.slice(0, level).every(Number.isNaN)) for (let i = 0; i < level; i += 1) {
|
|
39
|
+
const latestCounter = latestAbstractCounters?.[i];
|
|
40
|
+
counters[i] = latestCounter !== void 0 && !Number.isNaN(latestCounter) ? latestCounter : numbering.getLevel(numId, i)?.start ?? 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (Number.isNaN(counters[level])) counters[level] = (numbering.getLevel(numId, level)?.start ?? 1) - 1;
|
|
44
|
+
counters[level] = (counters[level] ?? 0) + 1;
|
|
45
|
+
for (let i = level + 1; i < counters.length; i += 1) counters[i] = NaN;
|
|
46
|
+
const childAdvances = listRendering.implicitChildLevelAdvances ?? 0;
|
|
47
|
+
if (childAdvances > 0 && level + 1 < counters.length) {
|
|
48
|
+
const childCounter = counters[level + 1];
|
|
49
|
+
counters[level + 1] = (childCounter === void 0 || Number.isNaN(childCounter) ? 0 : childCounter) + childAdvances;
|
|
50
|
+
}
|
|
51
|
+
if (abstractNumId !== null) abstractCounters.set(abstractNumId, counters);
|
|
52
|
+
previousList.abstractNumId = abstractNumId;
|
|
53
|
+
previousList.fromStyle = Boolean(styleNumbering);
|
|
54
|
+
previousList.numId = numId;
|
|
55
|
+
const pattern = listRendering.marker;
|
|
56
|
+
if (listRendering.isBullet) {
|
|
57
|
+
listRendering.marker = convertBulletToUnicode(pattern || "", bulletMarkerFontName(listRendering.markerFormatting));
|
|
58
|
+
previousList.abstractNumId = null;
|
|
59
|
+
previousList.fromStyle = false;
|
|
60
|
+
previousList.numId = null;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
let computedMarker = pattern;
|
|
64
|
+
const useLegalNumbering = numbering.getLevel(numId, level)?.isLgl === true || listRendering.isLegal === true;
|
|
65
|
+
for (let lvl = 0; lvl <= level; lvl += 1) {
|
|
66
|
+
const placeholder = `%${lvl + 1}`;
|
|
67
|
+
if (computedMarker.includes(placeholder)) {
|
|
68
|
+
const value = counters[lvl] ?? 0;
|
|
69
|
+
const levelInfo = numbering.getLevel(numId, lvl);
|
|
70
|
+
const formatted = formatOoxmlCounter(value, useLegalNumbering ? "decimal" : levelInfo?.numFmt || "decimal");
|
|
71
|
+
computedMarker = computedMarker.replaceAll(placeholder, formatted);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
listRendering.marker = computedMarker;
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { computeListMarker };
|
|
@@ -12,12 +12,20 @@ type ParagraphPropertySource = Readonly<{
|
|
|
12
12
|
[paragraphPropertySourceIndentationBaseline]: Readonly<Pick<document_d_exports.ParagraphFormatting, (typeof INDENTATION_FIELDS)[number]>>;
|
|
13
13
|
}>;
|
|
14
14
|
type IndentationFormatting = Pick<document_d_exports.ParagraphFormatting, (typeof INDENTATION_FIELDS)[number]>;
|
|
15
|
+
type SameParagraphIndentationProjectionOptions = {
|
|
16
|
+
left: document_d_exports.Paragraph;
|
|
17
|
+
right: document_d_exports.Paragraph;
|
|
18
|
+
};
|
|
19
|
+
/** Fixed points retain both authored indentation and its effective baseline. */
|
|
20
|
+
declare const sameParagraphIndentationProjection: ({ left, right }: SameParagraphIndentationProjectionOptions) => boolean;
|
|
15
21
|
type CopyParagraphIndentationProjectionOptions = {
|
|
16
22
|
target: document_d_exports.Paragraph;
|
|
17
23
|
source: document_d_exports.Paragraph;
|
|
18
24
|
};
|
|
19
25
|
/** Retain effective-value provenance without copying a durable source identity. */
|
|
20
26
|
declare const copyParagraphIndentationProjection: ({ target, source }: CopyParagraphIndentationProjectionOptions) => void;
|
|
27
|
+
/** History restores its captured authored/effective baseline before normalization. */
|
|
28
|
+
declare const restoreParagraphIndentationProjection: ({ target, source }: CopyParagraphIndentationProjectionOptions) => void;
|
|
21
29
|
type AssignParagraphIndentationProjectionOptions = {
|
|
22
30
|
paragraph: document_d_exports.Paragraph;
|
|
23
31
|
authored: document_d_exports.ParagraphFormatting | undefined;
|
|
@@ -171,4 +179,4 @@ declare const linkParagraphPropertySourceCandidate: (target: document_d_exports.
|
|
|
171
179
|
declare const getParagraphPropertySourceCandidate: (paragraph: document_d_exports.Paragraph) => document_d_exports.Paragraph | undefined;
|
|
172
180
|
declare const getParagraphPropertySourceTransferId: (paragraph: document_d_exports.Paragraph) => string | undefined;
|
|
173
181
|
//#endregion
|
|
174
|
-
export { DecodedTableCellParagraphSourcePayload, PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceTransfer, ParagraphPropertySourceValidationCode, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, TableCellParagraphPropertySourceBinding, TableCellParagraphSourcePayloadErrorClassification, assignDocumentParagraphPropertySourceContract, assignParagraphIndentationProjection, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphIndentationProjection, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, markParagraphPropertySourceTransfers, paragraphFormattingWithAuthoredIndentation, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesAuthoredIndentation, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
|
|
182
|
+
export { DecodedTableCellParagraphSourcePayload, PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceTransfer, ParagraphPropertySourceValidationCode, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, TableCellParagraphPropertySourceBinding, TableCellParagraphSourcePayloadErrorClassification, assignDocumentParagraphPropertySourceContract, assignParagraphIndentationProjection, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphIndentationProjection, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, markParagraphPropertySourceTransfers, paragraphFormattingWithAuthoredIndentation, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesAuthoredIndentation, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreParagraphIndentationProjection, restoreTableCellsWithParagraphPropertySources, sameParagraphIndentationProjection, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
|
|
@@ -18,16 +18,34 @@ const INDENTATION_FIELDS = [
|
|
|
18
18
|
const paragraphPropertyCapture = Symbol("paragraphPropertyCapture");
|
|
19
19
|
const paragraphIndentationProjection = Symbol("paragraphIndentationProjection");
|
|
20
20
|
const indentationProjections = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
const assignedIndentationProjections = /* @__PURE__ */ new WeakSet();
|
|
21
22
|
const indentationProjectionFor = (paragraph) => {
|
|
22
23
|
const handle = Object.getOwnPropertyDescriptor(paragraph, paragraphIndentationProjection)?.value;
|
|
23
24
|
return typeof handle === "object" && handle !== null ? indentationProjections.get(handle) : void 0;
|
|
24
25
|
};
|
|
26
|
+
/** Fixed points retain both authored indentation and its effective baseline. */
|
|
27
|
+
const sameParagraphIndentationProjection = ({ left, right }) => {
|
|
28
|
+
const a = indentationProjectionFor(left);
|
|
29
|
+
const b = indentationProjectionFor(right);
|
|
30
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
31
|
+
return INDENTATION_FIELDS.every((key) => a.effective[key] === b.effective[key] && a.authored[key] === b.authored[key]);
|
|
32
|
+
};
|
|
25
33
|
/** Retain effective-value provenance without copying a durable source identity. */
|
|
26
34
|
const copyParagraphIndentationProjection = ({ target, source }) => {
|
|
27
|
-
if (Object.hasOwn(target, paragraphIndentationProjection)) return;
|
|
35
|
+
if (assignedIndentationProjections.has(target) || Object.hasOwn(target, paragraphIndentationProjection)) return;
|
|
28
36
|
const descriptor = Object.getOwnPropertyDescriptor(source, paragraphIndentationProjection);
|
|
29
37
|
if (descriptor && indentationProjectionFor(source)) Object.defineProperty(target, paragraphIndentationProjection, descriptor);
|
|
30
38
|
};
|
|
39
|
+
/** History restores its captured authored/effective baseline before normalization. */
|
|
40
|
+
const restoreParagraphIndentationProjection = ({ target, source }) => {
|
|
41
|
+
assignedIndentationProjections.delete(target);
|
|
42
|
+
Reflect.deleteProperty(target, paragraphIndentationProjection);
|
|
43
|
+
copyParagraphIndentationProjection({
|
|
44
|
+
target,
|
|
45
|
+
source
|
|
46
|
+
});
|
|
47
|
+
assignedIndentationProjections.add(target);
|
|
48
|
+
};
|
|
31
49
|
const indentationFields = (formatting) => ({
|
|
32
50
|
indentLeft: formatting?.indentLeft,
|
|
33
51
|
indentRight: formatting?.indentRight,
|
|
@@ -36,6 +54,7 @@ const indentationFields = (formatting) => ({
|
|
|
36
54
|
});
|
|
37
55
|
/** Keep the model's effective indentation without making inherited values authored. */
|
|
38
56
|
const assignParagraphIndentationProjection = ({ paragraph, authored, inherited }) => {
|
|
57
|
+
assignedIndentationProjections.add(paragraph);
|
|
39
58
|
const effective = {
|
|
40
59
|
...inherited,
|
|
41
60
|
...indentationFields(authored)
|
|
@@ -50,6 +69,11 @@ const assignParagraphIndentationProjection = ({ paragraph, authored, inherited }
|
|
|
50
69
|
...authored,
|
|
51
70
|
...effective
|
|
52
71
|
};
|
|
72
|
+
if (Object.keys(paragraph.formatting).length === 0) delete paragraph.formatting;
|
|
73
|
+
if (Object.values(effective).every((value) => value === void 0) || getParagraphPropertySource(paragraph) === void 0 && INDENTATION_FIELDS.every((key) => effective[key] === authored?.[key])) {
|
|
74
|
+
Reflect.deleteProperty(paragraph, paragraphIndentationProjection);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
53
77
|
const handle = Object.freeze({});
|
|
54
78
|
indentationProjections.set(handle, Object.freeze({
|
|
55
79
|
effective: Object.freeze(indentationFields(paragraph.formatting)),
|
|
@@ -872,4 +896,4 @@ const linkParagraphPropertySourceCandidate = (target, source) => {
|
|
|
872
896
|
const getParagraphPropertySourceCandidate = (paragraph) => paragraphPropertySourceCandidates.get(paragraph);
|
|
873
897
|
const getParagraphPropertySourceTransferId = (paragraph) => paragraphPropertySourceTransferIds.get(paragraph);
|
|
874
898
|
//#endregion
|
|
875
|
-
export { PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, assignDocumentParagraphPropertySourceContract, assignParagraphIndentationProjection, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphIndentationProjection, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, markParagraphPropertySourceTransfers, paragraphFormattingWithAuthoredIndentation, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesAuthoredIndentation, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
|
|
899
|
+
export { PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, assignDocumentParagraphPropertySourceContract, assignParagraphIndentationProjection, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphIndentationProjection, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, markParagraphPropertySourceTransfers, paragraphFormattingWithAuthoredIndentation, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesAuthoredIndentation, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreParagraphIndentationProjection, restoreTableCellsWithParagraphPropertySources, sameParagraphIndentationProjection, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
|
|
Binary file
|
|
@@ -6,7 +6,7 @@ import { TRANSITIONAL_NAME_BY_STRICT_NAME } from "../docx/strictNames.gen.js";
|
|
|
6
6
|
import { createCapturedXmlSanitizer } from "../docx/verbatimCapture.js";
|
|
7
7
|
import { NAMESPACES, OOXML_NAMESPACE_SCOPE } from "../docx/xmlParser.js";
|
|
8
8
|
import { escapeXmlAttribute, serializeOnOffElement } from "@stll/docx-core";
|
|
9
|
-
import { outlineLevelStatedValue, paragraphNumberingSlots, sameEffectiveParagraphNumbering } from "@stll/docx-core/model";
|
|
9
|
+
import { mergeParagraphNumbering, outlineLevelStatedValue, paragraphNumberingLevel, paragraphNumberingSlots, sameEffectiveParagraphNumbering } from "@stll/docx-core/model";
|
|
10
10
|
import { serializeSequenceChildren } from "@stll/docx-core/schema";
|
|
11
11
|
//#region src/internal/paragraphFormattingSerialization.ts
|
|
12
12
|
/**
|
|
@@ -167,7 +167,10 @@ const modelParagraphFormattingEmission = (input) => {
|
|
|
167
167
|
numberingChangeXml,
|
|
168
168
|
numberingInsertionXml
|
|
169
169
|
}) : serializeNumbering({
|
|
170
|
-
numPr
|
|
170
|
+
numPr: numPr?.kind === "reference" && numPrFromStyle?.kind === "reference" && numPr.numId === numPrFromStyle.numId ? {
|
|
171
|
+
kind: "levelOnly",
|
|
172
|
+
ilvl: paragraphNumberingLevel(mergeParagraphNumbering(numPrFromStyle, numPr)) ?? 0
|
|
173
|
+
} : numPr,
|
|
171
174
|
numberingChangeXml,
|
|
172
175
|
numberingInsertionXml
|
|
173
176
|
})],
|
|
@@ -19,56 +19,60 @@ function renderBlocks(ctx, pkg, blocks) {
|
|
|
19
19
|
const projection = ctx.opts.trackedChanges === "clean" ? projectAcceptedBlocks(blocks, pkg) : null;
|
|
20
20
|
if (projection?.completeness === "partial") pushWarning(ctx, "Some tracked structural changes could not be resolved.");
|
|
21
21
|
const ordered = projection?.blocks ?? blocks;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const renderBlock = (block) => {
|
|
23
|
+
switch (block.type) {
|
|
24
|
+
case "paragraph": return renderParagraphBlock(ctx, pkg, block);
|
|
25
|
+
case "table": {
|
|
26
|
+
ctx.listIndentWidths = [];
|
|
27
|
+
const md = renderTable(ctx, pkg, block);
|
|
28
|
+
ctx.listIndentWidths = [];
|
|
29
|
+
return {
|
|
30
|
+
type: "paragraph",
|
|
31
|
+
markdown: md
|
|
32
|
+
};
|
|
28
33
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
ctx.listIndentWidths = [];
|
|
39
|
-
if (md) {
|
|
40
|
-
if (out.length) out.push("");
|
|
41
|
-
out.push(md);
|
|
34
|
+
case "blockSdt":
|
|
35
|
+
case "blockCustomXml": {
|
|
36
|
+
ctx.listIndentWidths = [];
|
|
37
|
+
const nested = renderBlocks(ctx, pkg, block.content);
|
|
38
|
+
ctx.listIndentWidths = [];
|
|
39
|
+
return {
|
|
40
|
+
type: "paragraph",
|
|
41
|
+
markdown: nested
|
|
42
|
+
};
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
case "preservedBlock": {
|
|
45
|
+
let markdown;
|
|
46
|
+
if (isAltChunkMarkup(block.xml)) markdown = block.readerText === void 0 ? ALT_CHUNK_READER_DIAGNOSTIC : `${ALT_CHUNK_READER_DIAGNOSTIC}\n\n${block.readerText}`;
|
|
47
|
+
else if (opaqueRevisionCarrierName(block.xml) !== void 0) markdown = OPAQUE_REVISION_CARRIER_READER_DIAGNOSTIC;
|
|
48
|
+
else if (isOpaqueNestedRowMarkup(block.xml)) markdown = "[Unsupported nested w:tr content]";
|
|
49
|
+
else return { type: "delimiter" };
|
|
50
|
+
ctx.listIndentWidths = [];
|
|
51
|
+
return {
|
|
52
|
+
type: "paragraph",
|
|
53
|
+
markdown
|
|
54
|
+
};
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
case "bookmarkStart":
|
|
57
|
+
case "bookmarkEnd": return { type: "delimiter" };
|
|
58
|
+
default: panic(`Unsupported block content in markdown: ${JSON.stringify(block)}`);
|
|
57
59
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
};
|
|
61
|
+
for (const block of ordered) {
|
|
62
|
+
const rendered = renderBlock(block);
|
|
63
|
+
switch (rendered.type) {
|
|
64
|
+
case "delimiter": continue;
|
|
65
|
+
case "paragraph":
|
|
66
|
+
if (rendered.markdown && out.length) out.push("");
|
|
65
67
|
prevWasListItem = false;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
break;
|
|
69
|
+
case "listItem":
|
|
70
|
+
if (out.length && (!prevWasListItem || rendered.separation === "loose")) out.push("");
|
|
71
|
+
prevWasListItem = true;
|
|
72
|
+
break;
|
|
73
|
+
default: panic(`Unsupported rendered block: ${JSON.stringify(rendered)}`);
|
|
68
74
|
}
|
|
69
|
-
|
|
70
|
-
case "bookmarkEnd": break;
|
|
71
|
-
default: panic(`Unsupported block content in markdown: ${JSON.stringify(block)}`);
|
|
75
|
+
if (rendered.markdown) out.push(rendered.markdown);
|
|
72
76
|
}
|
|
73
77
|
ctx.listIndentWidths = outerListIndentWidths;
|
|
74
78
|
return out.join("\n");
|
|
@@ -6,10 +6,14 @@ import { RenderContext } from "./types.js";
|
|
|
6
6
|
* caller joins blocks.
|
|
7
7
|
*/
|
|
8
8
|
declare function renderParagraph(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, para: document_d_exports.Paragraph): string;
|
|
9
|
-
/**
|
|
9
|
+
/** List items declare the boundary needed to preserve their Markdown structure. */
|
|
10
10
|
type RenderedParagraph = {
|
|
11
|
+
type: "paragraph";
|
|
11
12
|
markdown: string;
|
|
12
|
-
|
|
13
|
+
} | {
|
|
14
|
+
type: "listItem";
|
|
15
|
+
markdown: string;
|
|
16
|
+
separation: "tight" | "loose";
|
|
13
17
|
};
|
|
14
18
|
/**
|
|
15
19
|
* Render a paragraph. A list item is a paragraph that shows a marker, which is
|
|
@@ -47,28 +47,25 @@ function renderParagraphBlock(ctx, pkg, para) {
|
|
|
47
47
|
if (headingLevel !== void 0) {
|
|
48
48
|
ctx.listIndentWidths = [];
|
|
49
49
|
if (!inline || !inline.trim() && label === void 0) return {
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
type: "paragraph",
|
|
51
|
+
markdown: ""
|
|
52
52
|
};
|
|
53
53
|
const hashes = "#".repeat(Math.min(MAX_MARKDOWN_HEADING_LEVEL, headingLevel + 1));
|
|
54
54
|
const headingInline = inlineBreaksToHtml(inline);
|
|
55
55
|
return {
|
|
56
56
|
markdown: label ? `${hashes} ${label} ${headingInline}` : `${hashes} ${headingInline}`,
|
|
57
|
-
|
|
57
|
+
type: "paragraph"
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
if (list && label !== void 0) return
|
|
61
|
-
markdown: renderListItem(ctx, list, label, inline),
|
|
62
|
-
isListItem: true
|
|
63
|
-
};
|
|
60
|
+
if (list && label !== void 0) return renderListItem(ctx, list, label, inline);
|
|
64
61
|
ctx.listIndentWidths = [];
|
|
65
62
|
if (isQuoteStyle(styleId, ctx.builtInStyles)) return {
|
|
66
63
|
markdown: inline.split("\n").map((line) => `> ${line}`).join("\n"),
|
|
67
|
-
|
|
64
|
+
type: "paragraph"
|
|
68
65
|
};
|
|
69
66
|
return {
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
type: "paragraph",
|
|
68
|
+
markdown: escapeLeadingBlockMarker(inline)
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
71
|
/** The paragraph's 0-based heading level, as `builtInStyles` classifies it. */
|
|
@@ -86,7 +83,7 @@ const UNNUMBERED = Object.freeze({});
|
|
|
86
83
|
* style/list metadata. Escape the leading marker so literal text round-trips.
|
|
87
84
|
*/
|
|
88
85
|
function escapeLeadingBlockMarker(text) {
|
|
89
|
-
return text.replace(/^(?<ws>[ \t]*)(?<marker>[
|
|
86
|
+
return text.replace(/^(?<ws>[ \t]*)(?<marker>[#>=])/gmu, "$<ws>\\$<marker>").replace(/^(?<ws>[ \t]*)(?<marker>[-+*])(?=[ \t]|$)/gmu, "$<ws>\\$<marker>").replace(/^(?<ws>[ \t]*)-(?=[- \t]*$)/gmu, "$<ws>\\-").replace(/^(?<ws>[ \t]*)(?<num>\d{1,9})(?<marker>[.)])(?<after>[ \t])/gmu, "$<ws>$<num>\\$<marker>$<after>");
|
|
90
87
|
}
|
|
91
88
|
/**
|
|
92
89
|
* Only emitted Markdown ancestors contribute columns. OOXML levels may skip
|
|
@@ -110,13 +107,22 @@ function listIndentWidth(ctx, level) {
|
|
|
110
107
|
* a following, deeper item can indent correctly under it.
|
|
111
108
|
*/
|
|
112
109
|
function renderListItem(ctx, list, label, inline) {
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
if (!(list.isBullet || /^\d{1,9}[.)]$/u.test(label))) {
|
|
111
|
+
ctx.listIndentWidths = [];
|
|
112
|
+
return {
|
|
113
|
+
type: "paragraph",
|
|
114
|
+
markdown: escapeLeadingBlockMarker(`${label} ${inline}`).trimEnd()
|
|
115
|
+
};
|
|
116
|
+
}
|
|
115
117
|
ctx.listIndentWidths.length = list.level;
|
|
116
118
|
const indent = " ".repeat(listIndentWidth(ctx, list.level));
|
|
117
119
|
const marker = list.isBullet ? "- " : `${label} `;
|
|
118
|
-
|
|
119
|
-
return
|
|
120
|
+
ctx.listIndentWidths[list.level] = marker.length;
|
|
121
|
+
return {
|
|
122
|
+
type: "listItem",
|
|
123
|
+
markdown: `${indent}${marker}${escapeLeadingBlockMarker(inline)}`.trimEnd(),
|
|
124
|
+
separation: inline.trim().length === 0 ? "loose" : "tight"
|
|
125
|
+
};
|
|
120
126
|
}
|
|
121
127
|
//#endregion
|
|
122
128
|
export { renderParagraph, renderParagraphBlock };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.58.
|
|
3
|
+
"version": "0.58.5",
|
|
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",
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"perf": "bun scripts/profile-editor.ts"
|
|
147
147
|
},
|
|
148
148
|
"dependencies": {
|
|
149
|
-
"@stll/docx-core": "^0.36.
|
|
149
|
+
"@stll/docx-core": "^0.36.2",
|
|
150
150
|
"@stll/docx-utils": "^0.1.0",
|
|
151
151
|
"@stll/template-conditions": "^0.6.2",
|
|
152
152
|
"better-result": "3.0.1",
|