@stll/folio-core 0.53.0 → 0.54.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 +390 -166
- package/dist/ai-edits/batch-claims.d.ts +21 -6
- package/dist/ai-edits/batch-claims.js +47 -16
- package/dist/ai-edits/headless.d.ts +20 -0
- package/dist/ai-edits/headless.js +140 -11
- package/dist/ai-edits/pending-suggestions.js +1 -1
- package/dist/ai-edits/read.d.ts +5 -3
- package/dist/ai-edits/read.js +97 -47
- package/dist/ai-edits/revisionStretches.d.ts +17 -0
- package/dist/ai-edits/revisionStretches.js +80 -0
- package/dist/ai-edits/snapshot.js +3 -2
- package/dist/ai-edits/table-cell-mutations.d.ts +3 -1
- package/dist/ai-edits/table-cell-mutations.js +2 -1
- package/dist/ai-edits/table-row-column-mutations.d.ts +6 -1
- package/dist/ai-edits/table-row-column-mutations.js +27 -10
- package/dist/compare/content-alignment.d.ts +5 -1
- package/dist/compare/content-alignment.js +39 -32
- package/dist/compare/inline-provenance.js +1 -1
- package/dist/controller/layoutPipeline.js +3 -2
- package/dist/docx/archiveInflation.d.ts +127 -0
- package/dist/docx/archiveInflation.js +181 -0
- package/dist/docx/metadataPrivacy.js +24 -1
- package/dist/docx/noteReferenceMark.d.ts +15 -0
- package/dist/docx/noteReferenceMark.js +48 -0
- package/dist/docx/paragraphParser.js +5 -3
- package/dist/docx/paragraphPropertySource.d.ts +1 -1
- package/dist/docx/paragraphPropertySource.js +3 -1
- package/dist/docx/parser.js +2 -2
- package/dist/docx/rezip.js +4 -2
- package/dist/docx/selectiveXmlPatch.d.ts +3 -1
- package/dist/docx/selectiveXmlPatch.js +81 -3
- package/dist/docx/server/boundedArchive.d.ts +8 -13
- package/dist/docx/server/boundedArchive.js +70 -59
- package/dist/docx/server/extractDocxText.d.ts +8 -2
- package/dist/docx/server/extractDocxText.js +2 -2
- package/dist/docx/server/validateDocxConformance.d.ts +1 -1
- package/dist/docx/server/validateDocxConformance.js +1 -0
- package/dist/docx/unzip.d.ts +19 -2
- package/dist/docx/unzip.js +135 -24
- package/dist/fonts/embeddedFonts.js +1 -1
- package/dist/i18n/messages/catalogs.gen.d.ts +34 -34
- package/dist/i18n/messages/catalogs.gen.js +34 -34
- package/dist/i18n/messages/messages.gen.d.ts +2 -2
- package/dist/internal/wholeStoryRevisionResolution.js +95 -25
- package/dist/layout-engine/measure/measureParagraph.js +2 -2
- package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
- package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
- package/dist/markdown/escape.d.ts +2 -2
- package/dist/markdown/escape.js +4 -5
- package/dist/markdown/renderBlock.js +91 -25
- package/dist/markdown/renderRuns.d.ts +10 -1
- package/dist/markdown/renderRuns.js +320 -59
- package/dist/prosemirror/clearRunColor.d.ts +6 -0
- package/dist/prosemirror/clearRunColor.js +46 -0
- package/dist/prosemirror/commands/comments.js +56 -21
- package/dist/prosemirror/commands/formatPainter.js +1 -1
- package/dist/prosemirror/commands/hyperlink.js +2 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +24 -3
- package/dist/prosemirror/commands/resolveAllTableChanges.js +46 -41
- package/dist/prosemirror/commands/resolveParagraphProperties.js +20 -6
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -1
- package/dist/prosemirror/commands/tableCellMergeResolution.js +23 -7
- package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
- package/dist/prosemirror/containerFinalParagraph.js +7 -6
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
- package/dist/prosemirror/conversion/fromProseDoc.js +12 -590
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -11
- package/dist/prosemirror/conversion/toProseDoc.js +31 -150
- package/dist/prosemirror/documentSchema.d.ts +8 -0
- package/dist/prosemirror/documentSchema.js +18 -0
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +104 -63
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -1
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +10 -9
- package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +2 -4
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +14 -8
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
- package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
- package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
- package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -6
- package/dist/prosemirror/extensions/marks/markUtils.js +17 -8
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +3 -1
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +12 -1
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
- package/dist/prosemirror/extensions/nodes/TableExtension.js +104 -210
- package/dist/prosemirror/extensions/types.d.ts +7 -0
- package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
- package/dist/prosemirror/hyperlinkRemoval.js +129 -0
- package/dist/prosemirror/index.d.ts +2 -1
- package/dist/prosemirror/index.js +2 -1
- package/dist/prosemirror/listNumbering.d.ts +3 -21
- package/dist/prosemirror/listNumbering.js +10 -25
- package/dist/prosemirror/listRendering.d.ts +27 -0
- package/dist/prosemirror/listRendering.js +27 -0
- package/dist/prosemirror/markupViewNotes.d.ts +8 -0
- package/dist/prosemirror/markupViewNotes.js +49 -0
- package/dist/prosemirror/markupViewProjection.d.ts +3 -1
- package/dist/prosemirror/markupViewProjection.js +7 -2
- package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
- package/dist/prosemirror/noteReferenceReview.js +332 -0
- package/dist/prosemirror/paragraphIndentation.js +5 -2
- package/dist/prosemirror/paragraphMarkJoin.d.ts +6 -4
- package/dist/prosemirror/paragraphMarkJoin.js +21 -19
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
- package/dist/prosemirror/paragraphPropertyCarry.js +158 -0
- package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
- package/dist/prosemirror/plugins/documentStyleState.js +32 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +2 -21
- package/dist/prosemirror/plugins/documentStyles.js +66 -29
- package/dist/prosemirror/plugins/index.d.ts +2 -1
- package/dist/prosemirror/plugins/index.js +2 -1
- package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
- package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
- package/dist/prosemirror/plugins/suggestionMode.d.ts +12 -2
- package/dist/prosemirror/plugins/suggestionMode.js +391 -28
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +22 -2
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
- package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
- package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
- package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
- package/dist/prosemirror/runFormattingFromMarks.js +567 -0
- package/dist/prosemirror/runFormattingReconciliation.js +10 -9
- package/dist/prosemirror/schema/nodes.d.ts +2 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
- package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
- package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
- package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
- package/dist/prosemirror/tableCellPaste.d.ts +74 -0
- package/dist/prosemirror/tableCellPaste.js +525 -0
- package/dist/prosemirror/tableGridMutation.d.ts +53 -14
- package/dist/prosemirror/tableGridMutation.js +166 -18
- package/dist/prosemirror/tableRunIn.d.ts +51 -0
- package/dist/prosemirror/tableRunIn.js +165 -0
- package/dist/prosemirror/textInput.js +11 -2
- package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
- package/dist/prosemirror/trackedRevisionPath.js +32 -0
- package/dist/server.d.ts +2 -2
- package/package.json +5 -5
|
@@ -4,6 +4,19 @@ import { DEFAULT_FONT_FAMILY, buildRunFontStyle } from "./measureHelpers.js";
|
|
|
4
4
|
const DEFAULT_FONT_SIZE = 11;
|
|
5
5
|
const DEFAULT_LINE_HEIGHT_MULTIPLIER = 1;
|
|
6
6
|
const WIDTH_TOLERANCE = .5;
|
|
7
|
+
/** Grid a finished line's width snaps to: 1/1024 px, exact in binary floating point. */
|
|
8
|
+
const LINE_WIDTH_GRID = 1024;
|
|
9
|
+
/**
|
|
10
|
+
* The width a finished line reports. A line's width is a floating-point sum
|
|
11
|
+
* of per-token measurements, and the same text can reach it through different
|
|
12
|
+
* token groupings, e.g. a run split where a hidden revision sat measures
|
|
13
|
+
* "confirm" + "ation" where the resolved text measures "confirmation". Those
|
|
14
|
+
* sums agree only up to the last few bits, so the result snaps to a
|
|
15
|
+
* binary-exact 1/1024 px grid (under 0.0005 px, far below a device pixel):
|
|
16
|
+
* the same text yields the same width whichever grouping summed it, and the
|
|
17
|
+
* alignment offsets derived from it agree too.
|
|
18
|
+
*/
|
|
19
|
+
const canonicalLineWidth = (width) => Math.round(Math.max(0, width) * LINE_WIDTH_GRID) / LINE_WIDTH_GRID;
|
|
7
20
|
/**
|
|
8
21
|
* Extract FontStyle from a run that carries RunFormatting (text, tab, or
|
|
9
22
|
* field). All three share the same formatting shape, so they measure the
|
|
@@ -79,4 +92,4 @@ function isSpaceOrTab(char) {
|
|
|
79
92
|
return char === " " || char === " ";
|
|
80
93
|
}
|
|
81
94
|
//#endregion
|
|
82
|
-
export { DEFAULT_FONT_SIZE, DEFAULT_LINE_HEIGHT_MULTIPLIER, WIDTH_TOLERANCE, fieldMeasureText, isBlockLayoutImageRun, isEmptyTextRun, isFieldRun, isImageRun, isLineBreakRun, isMathRun, isSpaceOrTab, isTabRun, isTextRun, runToFontStyle };
|
|
95
|
+
export { DEFAULT_FONT_SIZE, DEFAULT_LINE_HEIGHT_MULTIPLIER, WIDTH_TOLERANCE, canonicalLineWidth, fieldMeasureText, isBlockLayoutImageRun, isEmptyTextRun, isFieldRun, isImageRun, isLineBreakRun, isMathRun, isSpaceOrTab, isTabRun, isTextRun, runToFontStyle };
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
9
|
* Escape only characters that would change markdown structure mid-text:
|
|
10
|
-
* - backslash, backtick, asterisk, brackets (always)
|
|
11
|
-
* - underscore:
|
|
10
|
+
* - backslash, backtick, asterisk, brackets, tilde (always)
|
|
11
|
+
* - underscore: unless between two letters or digits (emphasis trigger)
|
|
12
12
|
* - angle brackets: only when they look like a tag/autolink
|
|
13
13
|
*
|
|
14
14
|
* Characters like `.` `-` `+` `#` `(` `)` `!` are only meaningful at line
|
package/dist/markdown/escape.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
9
|
* Escape only characters that would change markdown structure mid-text:
|
|
10
|
-
* - backslash, backtick, asterisk, brackets (always)
|
|
11
|
-
* - underscore:
|
|
10
|
+
* - backslash, backtick, asterisk, brackets, tilde (always)
|
|
11
|
+
* - underscore: unless between two letters or digits (emphasis trigger)
|
|
12
12
|
* - angle brackets: only when they look like a tag/autolink
|
|
13
13
|
*
|
|
14
14
|
* Characters like `.` `-` `+` `#` `(` `)` `!` are only meaningful at line
|
|
@@ -16,9 +16,8 @@
|
|
|
16
16
|
* when to escape those.
|
|
17
17
|
*/
|
|
18
18
|
function escapeInline(text) {
|
|
19
|
-
let out = text.replace(/(?<ch>[\\`*[\]])/gu, "\\$<ch>");
|
|
20
|
-
out = out.replace(/(
|
|
21
|
-
out = out.replace(/_(?<post>\s|$)/gu, "\\_$<post>");
|
|
19
|
+
let out = text.replace(/(?<ch>[\\`*[\]~])/gu, "\\$<ch>");
|
|
20
|
+
out = out.replace(/(?<![\p{L}\p{N}])_|_(?![\p{L}\p{N}])/gu, "\\_");
|
|
22
21
|
out = out.replace(/<(?<tag>\/?[A-Za-z][\w-]*)(?=[\s/>])/gu, "\\<$<tag>");
|
|
23
22
|
return out;
|
|
24
23
|
}
|
|
@@ -11,37 +11,103 @@ import { panic } from "better-result";
|
|
|
11
11
|
* of the same list. Ported from eigenpal/docx-editor PR #595.
|
|
12
12
|
*/
|
|
13
13
|
/**
|
|
14
|
-
* In `trackedChanges: "clean"` mode every change is accepted
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
14
|
+
* In `trackedChanges: "clean"` mode every change is accepted, as the editor's
|
|
15
|
+
* accept-all resolves the story (`internal/wholeStoryRevisionResolution.ts`).
|
|
16
|
+
*
|
|
17
|
+
* A paragraph whose mark is pending deletion loses its break on accept and
|
|
18
|
+
* runs on into the paragraph after it, which is the paragraph left: its mark
|
|
19
|
+
* ends the joined text, and a paragraph's properties (style, list) live on its
|
|
20
|
+
* mark. A run of consecutive deletions collapses into that last paragraph.
|
|
21
|
+
*
|
|
22
|
+
* With a table right after it, its words run on into the table's first cell
|
|
23
|
+
* and lead that cell's first paragraph (`prosemirror/tableRunIn.ts`), unless
|
|
24
|
+
* it ends a section, keeps nothing once accepted, or that cell is pending.
|
|
25
|
+
* Otherwise a paragraph that cannot join keeps its place, or goes when it
|
|
26
|
+
* keeps nothing and something follows it.
|
|
25
27
|
*/
|
|
26
28
|
function mergeAcceptedParagraphBreaks(blocks) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
const reversed = [];
|
|
30
|
+
let survivor = null;
|
|
31
|
+
let tableFollows = false;
|
|
32
|
+
let followed = false;
|
|
33
|
+
const flush = () => {
|
|
34
|
+
if (survivor) reversed.push(survivor);
|
|
35
|
+
survivor = null;
|
|
36
|
+
};
|
|
37
|
+
for (let index = blocks.length - 1; index >= 0; index--) {
|
|
38
|
+
const block = blocks[index];
|
|
39
|
+
if (!block) continue;
|
|
40
|
+
if (block.type !== "paragraph") {
|
|
41
|
+
flush();
|
|
42
|
+
reversed.push(block);
|
|
43
|
+
tableFollows = block.type === "table";
|
|
44
|
+
followed ||= tableFollows;
|
|
39
45
|
continue;
|
|
40
46
|
}
|
|
41
|
-
|
|
47
|
+
const goes = block.pPrMark?.kind === "del" || block.pPrMark?.kind === "moveFrom";
|
|
48
|
+
if (goes && survivor) {
|
|
49
|
+
survivor = cloneParagraphWithoutPropertySource(survivor, { content: [...block.content, ...survivor.content] });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const table = reversed.at(-1);
|
|
53
|
+
if (goes && tableFollows && table?.type === "table") {
|
|
54
|
+
const ranIn = tableWithParagraphRunIn(table, block);
|
|
55
|
+
if (ranIn) {
|
|
56
|
+
reversed[reversed.length - 1] = ranIn;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
flush();
|
|
61
|
+
tableFollows = false;
|
|
62
|
+
if (goes && holdsNothingOnAccept(block) && followed && index + reversed.length > 0) continue;
|
|
63
|
+
survivor = block;
|
|
64
|
+
followed = true;
|
|
42
65
|
}
|
|
43
|
-
|
|
66
|
+
flush();
|
|
67
|
+
return reversed.reverse();
|
|
44
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether the cell a paragraph's words would run into is itself pending (its
|
|
71
|
+
* row or the cell, through nested tables' first cells), as
|
|
72
|
+
* `prosemirror/tableRunIn.ts` reads it.
|
|
73
|
+
*/
|
|
74
|
+
const runInCellPending = (table) => {
|
|
75
|
+
const row = table.rows[0];
|
|
76
|
+
const cell = row?.cells[0];
|
|
77
|
+
if (!row || !cell) return false;
|
|
78
|
+
if (row.structuralChange !== void 0 || cell.structuralChange !== void 0) return true;
|
|
79
|
+
const first = cell.content[0];
|
|
80
|
+
return first?.type === "table" && runInCellPending(first);
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* `table` with `paragraph`'s words leading its first cell's first paragraph,
|
|
84
|
+
* descending into nested tables, or null when the paragraph keeps its place.
|
|
85
|
+
*/
|
|
86
|
+
const tableWithParagraphRunIn = (table, paragraph) => {
|
|
87
|
+
if (paragraph.sectionProperties !== void 0 || holdsNothingOnAccept(paragraph)) return null;
|
|
88
|
+
if (runInCellPending(table)) return null;
|
|
89
|
+
const runIn = (current) => {
|
|
90
|
+
const [row, ...rows] = current.rows;
|
|
91
|
+
const [cell, ...cells] = row?.cells ?? [];
|
|
92
|
+
const [first, ...rest] = cell?.content ?? [];
|
|
93
|
+
if (!row || !cell || !first) return null;
|
|
94
|
+
let led = null;
|
|
95
|
+
if (first.type === "paragraph") led = cloneParagraphWithoutPropertySource(first, { content: [...paragraph.content, ...first.content] });
|
|
96
|
+
else if (first.type === "table") led = runIn(first);
|
|
97
|
+
if (!led) return null;
|
|
98
|
+
return {
|
|
99
|
+
...current,
|
|
100
|
+
rows: [{
|
|
101
|
+
...row,
|
|
102
|
+
cells: [{
|
|
103
|
+
...cell,
|
|
104
|
+
content: [led, ...rest]
|
|
105
|
+
}, ...cells]
|
|
106
|
+
}, ...rows]
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
return runIn(table);
|
|
110
|
+
};
|
|
45
111
|
/** Paragraph content that shows nothing: range boundaries and anchors. */
|
|
46
112
|
const ZERO_WIDTH_CONTENT = /* @__PURE__ */ new Set([
|
|
47
113
|
"bookmarkStart",
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { RenderContext } from "./types.js";
|
|
3
3
|
//#region src/markdown/renderRuns.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The character beside a run's delimiters: `""` at the paragraph's edge,
|
|
6
|
+
* `undefined` when it cannot be told (read as a letter, the strictest case).
|
|
7
|
+
*/
|
|
8
|
+
type Neighbor = string | undefined;
|
|
9
|
+
type RunNeighbors = {
|
|
10
|
+
before: Neighbor;
|
|
11
|
+
after: Neighbor;
|
|
12
|
+
};
|
|
4
13
|
/**
|
|
5
14
|
* Render the full inline content of a paragraph, tracking comment-range
|
|
6
15
|
* boundaries to apply the configured wrapper.
|
|
7
16
|
*/
|
|
8
|
-
declare function renderParagraphInline(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, content: readonly document_d_exports.ParagraphContent[], paraId: string | undefined): string;
|
|
17
|
+
declare function renderParagraphInline(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, content: readonly document_d_exports.ParagraphContent[], paraId: string | undefined, edges?: RunNeighbors): string;
|
|
9
18
|
//#endregion
|
|
10
19
|
export { renderParagraphInline };
|
|
@@ -5,6 +5,7 @@ import { wrapComment, wrapDeletion, wrapInsertion, wrapMoveFrom, wrapMoveTo } fr
|
|
|
5
5
|
import { escapeAltText, escapeInline, escapeLinkUrl } from "./escape.js";
|
|
6
6
|
import { registerImage } from "./images.js";
|
|
7
7
|
import { numberNoteReference, pushWarning } from "./internals.js";
|
|
8
|
+
import { compileMarkdownToContent } from "@stll/docx-core";
|
|
8
9
|
//#region src/markdown/renderRuns.ts
|
|
9
10
|
const MARK_DELIMS = {
|
|
10
11
|
bold: "**",
|
|
@@ -32,9 +33,9 @@ function marksFor(run) {
|
|
|
32
33
|
const f = run.formatting;
|
|
33
34
|
if (!f) return [];
|
|
34
35
|
const out = [];
|
|
36
|
+
if (f.strike) out.push("strike");
|
|
35
37
|
if (f.bold) out.push("bold");
|
|
36
38
|
if (f.italic) out.push("italic");
|
|
37
|
-
if (f.strike) out.push("strike");
|
|
38
39
|
const ascii = f.fontFamily?.ascii?.toLowerCase();
|
|
39
40
|
if (ascii && MONOSPACE_FONTS.has(ascii)) out.push("code");
|
|
40
41
|
return out;
|
|
@@ -42,7 +43,7 @@ function marksFor(run) {
|
|
|
42
43
|
function applyMarks(text, marks) {
|
|
43
44
|
if (!text) return text;
|
|
44
45
|
if (marks.includes("code")) {
|
|
45
|
-
const literal = text.replace(/\\(?<char>[\\`*[\]_
|
|
46
|
+
const literal = text.replace(/\\(?<char>[\\`*[\]_<~])/gu, "$<char>");
|
|
46
47
|
let longestRun = 0;
|
|
47
48
|
for (const m of literal.matchAll(/`+/gu)) longestRun = Math.max(longestRun, m[0].length);
|
|
48
49
|
const fence = "`".repeat(longestRun + 1);
|
|
@@ -56,6 +57,133 @@ function applyMarks(text, marks) {
|
|
|
56
57
|
}
|
|
57
58
|
return out;
|
|
58
59
|
}
|
|
60
|
+
const UNKNOWN_NEIGHBORS = {
|
|
61
|
+
before: void 0,
|
|
62
|
+
after: void 0
|
|
63
|
+
};
|
|
64
|
+
const isBlank = (ch) => ch === "" || ch !== void 0 && /\s/u.test(ch);
|
|
65
|
+
/**
|
|
66
|
+
* Which mark wraps a stretch of runs first, outermost first. Strikethrough
|
|
67
|
+
* goes inside emphasis: an emphasis delimiter beside a tilde opens and closes
|
|
68
|
+
* wherever it stands, where the other way round (`~~*`) needs a blank or
|
|
69
|
+
* punctuation outside.
|
|
70
|
+
*/
|
|
71
|
+
const GROUPING_ORDER = [
|
|
72
|
+
"italic",
|
|
73
|
+
"bold",
|
|
74
|
+
"strike"
|
|
75
|
+
];
|
|
76
|
+
/** The stackings tried, in turn, where the reader misreads one. */
|
|
77
|
+
const GROUPING_ORDERS = [GROUPING_ORDER, [
|
|
78
|
+
"bold",
|
|
79
|
+
"italic",
|
|
80
|
+
"strike"
|
|
81
|
+
]];
|
|
82
|
+
/**
|
|
83
|
+
* CommonMark punctuation (Unicode P and S categories) as a delimiter of
|
|
84
|
+
* `mark` meets it. Beside an emphasis delimiter, an asterisk is another
|
|
85
|
+
* delimiter that runs together with it, and a tilde does not let it close
|
|
86
|
+
* (the GFM reader's rule): both count as letters. (Words carry them escaped,
|
|
87
|
+
* as a two-character unit.)
|
|
88
|
+
*/
|
|
89
|
+
const punctuationFor = (mark) => (ch) => {
|
|
90
|
+
if (ch === void 0 || ch === "" || ch === "~") return false;
|
|
91
|
+
if (mark !== "strike" && ch === "*") return false;
|
|
92
|
+
return /[\p{P}\p{S}]/u.test(ch);
|
|
93
|
+
};
|
|
94
|
+
/** One escape pair (`\*`) or one code point of escaped inline text. */
|
|
95
|
+
const INLINE_UNIT = /\\[\s\S]|[\s\S]/gu;
|
|
96
|
+
/**
|
|
97
|
+
* Wrap `inner` in `mark` so a CommonMark reader reads it back. A delimiter
|
|
98
|
+
* opens only before a character that is not blank, and before punctuation
|
|
99
|
+
* only where blank or punctuation precedes it; closing mirrors that. Where
|
|
100
|
+
* the words at an edge would stop a delimiter from opening or closing (a
|
|
101
|
+
* space at the edge, emphasis ending in a full stop right before a letter),
|
|
102
|
+
* those characters are left outside the emphasis rather than written as
|
|
103
|
+
* literal asterisks.
|
|
104
|
+
*/
|
|
105
|
+
function wrapFlanking(inner, mark, { before, after }) {
|
|
106
|
+
const isPunctuation = punctuationFor(mark);
|
|
107
|
+
const opensAfter = (ch) => isBlank(ch) || isPunctuation(ch);
|
|
108
|
+
const units = Array.from(inner.matchAll(INLINE_UNIT), ([unit]) => unit);
|
|
109
|
+
const isPunctuationUnit = (unit) => unit.length === 2 && unit.startsWith("\\") || isPunctuation(unit);
|
|
110
|
+
let start = 0;
|
|
111
|
+
let end = units.length;
|
|
112
|
+
let outside = before;
|
|
113
|
+
for (;;) {
|
|
114
|
+
const unit = units[start];
|
|
115
|
+
if (unit === void 0 || start >= end) break;
|
|
116
|
+
if (!isBlank(unit) && (!isPunctuationUnit(unit) || opensAfter(outside))) break;
|
|
117
|
+
outside = unit.at(-1);
|
|
118
|
+
start++;
|
|
119
|
+
}
|
|
120
|
+
outside = after;
|
|
121
|
+
for (;;) {
|
|
122
|
+
const unit = units[end - 1];
|
|
123
|
+
if (unit === void 0 || end <= start) break;
|
|
124
|
+
if (!isBlank(unit) && (!isPunctuationUnit(unit) || opensAfter(outside))) break;
|
|
125
|
+
outside = unit.at(0);
|
|
126
|
+
end--;
|
|
127
|
+
}
|
|
128
|
+
return units.slice(0, start).join("") + applyMarks(units.slice(start, end).join(""), start < end ? [mark] : []) + units.slice(end).join("");
|
|
129
|
+
}
|
|
130
|
+
/** Paragraph content that writes nothing to the line. */
|
|
131
|
+
const SILENT_MARKERS = /* @__PURE__ */ new Set([
|
|
132
|
+
"bookmarkStart",
|
|
133
|
+
"bookmarkEnd",
|
|
134
|
+
"moveFromRangeStart",
|
|
135
|
+
"moveFromRangeEnd",
|
|
136
|
+
"moveToRangeStart",
|
|
137
|
+
"moveToRangeEnd"
|
|
138
|
+
]);
|
|
139
|
+
/** Comment range markers, which write nothing when comments are stripped. */
|
|
140
|
+
const COMMENT_MARKERS = /* @__PURE__ */ new Set(["commentRangeStart", "commentRangeEnd"]);
|
|
141
|
+
/** The first character `piece` puts on the line, inside the marks `outer` already opened. */
|
|
142
|
+
const pieceStart = (piece, outer, order) => {
|
|
143
|
+
const first = piece.text.match(INLINE_UNIT)?.[0];
|
|
144
|
+
if (first === void 0 || isBlank(first)) return first;
|
|
145
|
+
const opens = order.find((mark) => piece.marks.has(mark) && !outer.has(mark));
|
|
146
|
+
if (opens === void 0) return first;
|
|
147
|
+
return opens === "strike" ? "~" : "*";
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Wrap consecutive pieces in their emphasis, one mark at a time: the pieces
|
|
151
|
+
* sharing the outermost mark are wrapped once, around the rest of their
|
|
152
|
+
* emphasis. Written run by run, the closing delimiter of one would meet the
|
|
153
|
+
* opening one of the next (`*a**b*`), which no reader takes for two
|
|
154
|
+
* emphases.
|
|
155
|
+
*/
|
|
156
|
+
function renderPieces(pieces, outer, edges, order = GROUPING_ORDER) {
|
|
157
|
+
let out = "";
|
|
158
|
+
let index = 0;
|
|
159
|
+
while (index < pieces.length) {
|
|
160
|
+
const piece = pieces[index];
|
|
161
|
+
if (!piece) break;
|
|
162
|
+
const mark = order.find((key) => piece.marks.has(key) && !outer.has(key));
|
|
163
|
+
if (mark === void 0) {
|
|
164
|
+
out += piece.text;
|
|
165
|
+
index++;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
let last = index;
|
|
169
|
+
while (pieces[last + 1]?.marks.has(mark)) last++;
|
|
170
|
+
const next = pieces[last + 1];
|
|
171
|
+
const around = {
|
|
172
|
+
before: out.length > 0 ? out.at(-1) : edges.before,
|
|
173
|
+
after: next === void 0 ? edges.after : pieceStart(next, outer, order)
|
|
174
|
+
};
|
|
175
|
+
const inner = renderPieces(pieces.slice(index, last + 1), /* @__PURE__ */ new Set([...outer, mark]), around, order);
|
|
176
|
+
out += wrapFlanking(inner, mark, around);
|
|
177
|
+
index = last + 1;
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
/** The first character an item that is not a run puts on the line, where it is fixed. */
|
|
182
|
+
function renderedStart(item) {
|
|
183
|
+
if (item?.type !== "hyperlink") return void 0;
|
|
184
|
+
const link = item;
|
|
185
|
+
return link.href || link.anchor ? "[" : void 0;
|
|
186
|
+
}
|
|
59
187
|
/** Render a single run's RunContent array into the inline text fragment. */
|
|
60
188
|
function renderRunContent(ctx, pkg, content, paraId) {
|
|
61
189
|
let out = "";
|
|
@@ -111,21 +239,111 @@ function renderRunContent(ctx, pkg, content, paraId) {
|
|
|
111
239
|
}
|
|
112
240
|
return out;
|
|
113
241
|
}
|
|
114
|
-
/**
|
|
115
|
-
function
|
|
116
|
-
if (run.formatting?.hidden) return
|
|
242
|
+
/** A run's rendered text and the emphasis it still takes; `null` when it writes nothing. */
|
|
243
|
+
function pieceOf(ctx, pkg, run, paraId) {
|
|
244
|
+
if (run.formatting?.hidden) return null;
|
|
117
245
|
const inner = renderRunContent(ctx, pkg, run.content, paraId);
|
|
118
|
-
if (!inner) return
|
|
246
|
+
if (!inner) return null;
|
|
247
|
+
const marks = marksFor(run);
|
|
248
|
+
if (!marks.includes("code")) return {
|
|
249
|
+
text: inner,
|
|
250
|
+
marks: new Set(marks)
|
|
251
|
+
};
|
|
119
252
|
const leadLen = inner.length - inner.trimStart().length;
|
|
120
253
|
const trailLen = inner.length - inner.trimEnd().length;
|
|
121
254
|
const core = inner.slice(leadLen, inner.length - trailLen);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
255
|
+
return {
|
|
256
|
+
text: core ? `${inner.slice(0, leadLen)}${applyMarks(core, marks)}${inner.slice(inner.length - trailLen)}` : inner,
|
|
257
|
+
marks: /* @__PURE__ */ new Set()
|
|
258
|
+
};
|
|
126
259
|
}
|
|
260
|
+
/** What a markdown reader reads from one line: each character with its emphasis. */
|
|
261
|
+
const readLine = (line) => compileMarkdownToContent(line).content.flatMap((block) => block.type === "paragraph" ? block.content : []).flatMap((item) => item.type === "hyperlink" ? item.children : [item]).flatMap((item) => {
|
|
262
|
+
if (item.type !== "run") return [{
|
|
263
|
+
char: "\0",
|
|
264
|
+
marks: /* @__PURE__ */ new Set()
|
|
265
|
+
}];
|
|
266
|
+
const marks = new Set(GROUPING_ORDER.filter((key) => item.formatting?.[key] === true));
|
|
267
|
+
return item.content.flatMap((content) => content.type === "text" ? Array.from(content.text, (char) => ({
|
|
268
|
+
char,
|
|
269
|
+
marks
|
|
270
|
+
})) : [{
|
|
271
|
+
char: "\0",
|
|
272
|
+
marks
|
|
273
|
+
}]);
|
|
274
|
+
});
|
|
275
|
+
const sameMarks = (left, right) => left.size === right.size && [...left].every((key) => right.has(key));
|
|
276
|
+
/**
|
|
277
|
+
* How `rendered`, between `edges`, reads back: `"exact"` with the pieces'
|
|
278
|
+
* text and each letter's emphasis, `"text"` with their text alone (emphasis
|
|
279
|
+
* lost somewhere, none of it read as literal asterisks), or `null`.
|
|
280
|
+
*/
|
|
281
|
+
const readBack = (rendered, pieces, edges) => {
|
|
282
|
+
const side = (ch) => {
|
|
283
|
+
if (ch === void 0) return "x";
|
|
284
|
+
return ch === "" ? " " : ch;
|
|
285
|
+
};
|
|
286
|
+
const line = (inner) => `x ${side(edges.before)}${inner}${side(edges.after)} x`;
|
|
287
|
+
const read = readLine(line(rendered));
|
|
288
|
+
const plain = readLine(line(pieces.map(({ text }) => text).join("")));
|
|
289
|
+
if (read.map(({ char }) => char).join("") !== plain.map(({ char }) => char).join("")) return null;
|
|
290
|
+
const expected = pieces.flatMap(({ text, marks }) => readLine(`x${text}x`).slice(1, -1).map(({ char }) => ({
|
|
291
|
+
char,
|
|
292
|
+
marks
|
|
293
|
+
})));
|
|
294
|
+
const start = Array.from(`x ${side(edges.before)}`).length;
|
|
295
|
+
const end = Array.from(`${side(edges.after)} x`).length;
|
|
296
|
+
if (read.length !== start + expected.length + end) return read.every(({ marks }) => marks.size === 0) ? "text" : null;
|
|
297
|
+
let exact = true;
|
|
298
|
+
for (const [index, { char, marks }] of expected.entries()) {
|
|
299
|
+
const at = read[start + index];
|
|
300
|
+
if (at?.char !== char) return null;
|
|
301
|
+
if ([...at.marks].some((mark) => !marks.has(mark))) return null;
|
|
302
|
+
if (/[\p{L}\p{N}]/u.test(char) && !sameMarks(at.marks, marks)) exact = false;
|
|
303
|
+
}
|
|
304
|
+
return exact ? "exact" : "text";
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Render consecutive runs with their formatting applied. The delimiters are
|
|
308
|
+
* placed by the flanking rules the reader applies, and the result is checked
|
|
309
|
+
* against the reader: a stacking it misreads is tried the other way round,
|
|
310
|
+
* or with more punctuation left outside, and failing all of those, the runs
|
|
311
|
+
* go out without emphasis rather than with stray asterisks.
|
|
312
|
+
*/
|
|
313
|
+
function renderRuns(ctx, pkg, runs, paraId, edges = UNKNOWN_NEIGHBORS) {
|
|
314
|
+
const pieces = runs.flatMap((run) => pieceOf(ctx, pkg, run, paraId) ?? []);
|
|
315
|
+
const plain = pieces.map(({ text }) => text).join("");
|
|
316
|
+
if (!pieces.some(({ marks }) => marks.size > 0)) return plain;
|
|
317
|
+
let fallback;
|
|
318
|
+
for (const split of [pieces, splitAtBlanks(pieces)]) for (const order of GROUPING_ORDERS) for (const neighbors of [edges, UNKNOWN_NEIGHBORS]) {
|
|
319
|
+
const rendered = renderPieces(split, /* @__PURE__ */ new Set(), neighbors, order);
|
|
320
|
+
const read = readBack(rendered, pieces, edges);
|
|
321
|
+
if (read === "exact") return rendered;
|
|
322
|
+
if (read === "text") fallback ??= rendered;
|
|
323
|
+
}
|
|
324
|
+
return fallback ?? plain;
|
|
325
|
+
}
|
|
326
|
+
/** The pieces with every blank a piece of its own, without emphasis. */
|
|
327
|
+
const splitAtBlanks = (pieces) => pieces.flatMap((piece) => {
|
|
328
|
+
if (piece.marks.size === 0) return [piece];
|
|
329
|
+
const parts = [];
|
|
330
|
+
for (const [unit] of piece.text.matchAll(INLINE_UNIT)) {
|
|
331
|
+
const marks = isBlank(unit) ? /* @__PURE__ */ new Set() : piece.marks;
|
|
332
|
+
const last = parts.at(-1);
|
|
333
|
+
if (last && last.marks === marks) last.text += unit;
|
|
334
|
+
else parts.push({
|
|
335
|
+
text: unit,
|
|
336
|
+
marks
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return parts;
|
|
340
|
+
});
|
|
127
341
|
function renderHyperlink(ctx, pkg, link, paraId) {
|
|
128
|
-
const
|
|
342
|
+
const bracketed = Boolean(link.href ?? link.anchor);
|
|
343
|
+
const inner = renderRuns(ctx, pkg, getHyperlinkRuns(link), paraId, bracketed ? {
|
|
344
|
+
before: "[",
|
|
345
|
+
after: "]"
|
|
346
|
+
} : UNKNOWN_NEIGHBORS);
|
|
129
347
|
if (!inner) return "";
|
|
130
348
|
const href = link.href ?? (link.anchor ? `#${link.anchor}` : "");
|
|
131
349
|
if (!href) {
|
|
@@ -145,17 +363,17 @@ function renderHyperlink(ctx, pkg, link, paraId) {
|
|
|
145
363
|
function renderTrackedWrapper(ctx, pkg, wrapper, paraId) {
|
|
146
364
|
const renderChild = (child) => {
|
|
147
365
|
switch (child.type) {
|
|
148
|
-
case "run": return
|
|
366
|
+
case "run": return renderRuns(ctx, pkg, [child], paraId);
|
|
149
367
|
case "hyperlink": return renderHyperlink(ctx, pkg, child, paraId);
|
|
150
368
|
case "mathEquation": return child.plainText ? escapeInline(child.plainText) : "";
|
|
151
369
|
case "inlineWrapper":
|
|
152
|
-
case "inlineSdt": return renderParagraphInline(ctx, pkg, child.content, paraId);
|
|
370
|
+
case "inlineSdt": return renderParagraphInline(ctx, pkg, child.content, paraId, UNKNOWN_NEIGHBORS);
|
|
153
371
|
case "insertion":
|
|
154
372
|
case "deletion":
|
|
155
373
|
case "moveFrom":
|
|
156
374
|
case "moveTo": return renderTrackedWrapper(ctx, pkg, child, paraId);
|
|
157
375
|
case "simpleField":
|
|
158
|
-
case "complexField": return renderParagraphInline(ctx, pkg, [child], paraId);
|
|
376
|
+
case "complexField": return renderParagraphInline(ctx, pkg, [child], paraId, UNKNOWN_NEIGHBORS);
|
|
159
377
|
case "bookmarkStart":
|
|
160
378
|
case "bookmarkEnd":
|
|
161
379
|
case "moveFromRangeStart":
|
|
@@ -166,11 +384,33 @@ function renderTrackedWrapper(ctx, pkg, wrapper, paraId) {
|
|
|
166
384
|
default: return child;
|
|
167
385
|
}
|
|
168
386
|
};
|
|
387
|
+
const renderChildren = () => {
|
|
388
|
+
let rendered = "";
|
|
389
|
+
let runs = [];
|
|
390
|
+
const flush = (after) => {
|
|
391
|
+
if (runs.length === 0) return;
|
|
392
|
+
rendered += renderRuns(ctx, pkg, runs, paraId, {
|
|
393
|
+
before: rendered.length > 0 ? rendered.at(-1) : void 0,
|
|
394
|
+
after
|
|
395
|
+
});
|
|
396
|
+
runs = [];
|
|
397
|
+
};
|
|
398
|
+
for (const child of wrapper.content) {
|
|
399
|
+
if (child.type === "run") {
|
|
400
|
+
runs.push(child);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
flush(renderedStart(child));
|
|
404
|
+
rendered += renderChild(child);
|
|
405
|
+
}
|
|
406
|
+
flush(void 0);
|
|
407
|
+
return rendered;
|
|
408
|
+
};
|
|
169
409
|
if (ctx.opts.trackedChanges === "clean") {
|
|
170
|
-
if (wrapper.type === "insertion" || wrapper.type === "moveTo") return
|
|
410
|
+
if (wrapper.type === "insertion" || wrapper.type === "moveTo") return renderChildren();
|
|
171
411
|
return "";
|
|
172
412
|
}
|
|
173
|
-
const inner =
|
|
413
|
+
const inner = renderChildren();
|
|
174
414
|
switch (wrapper.type) {
|
|
175
415
|
case "insertion": return wrapInsertion(ctx, wrapper.info, inner);
|
|
176
416
|
case "deletion": return wrapDeletion(ctx, wrapper.info, inner);
|
|
@@ -182,55 +422,76 @@ function renderTrackedWrapper(ctx, pkg, wrapper, paraId) {
|
|
|
182
422
|
* Render the full inline content of a paragraph, tracking comment-range
|
|
183
423
|
* boundaries to apply the configured wrapper.
|
|
184
424
|
*/
|
|
185
|
-
function renderParagraphInline(ctx, pkg, content, paraId
|
|
425
|
+
function renderParagraphInline(ctx, pkg, content, paraId, edges = {
|
|
426
|
+
before: "",
|
|
427
|
+
after: ""
|
|
428
|
+
}) {
|
|
186
429
|
let out = "";
|
|
187
430
|
const openComments = [];
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
out = handleCommentEnd(ctx, item, openComments, out);
|
|
206
|
-
break;
|
|
207
|
-
case "commentReference":
|
|
208
|
-
out += renderPointComment(ctx, pkg, item.id);
|
|
209
|
-
break;
|
|
210
|
-
case "simpleField":
|
|
211
|
-
case "complexField": {
|
|
212
|
-
const runs = item.type === "simpleField" ? item.content : item.fieldResult;
|
|
213
|
-
for (const child of runs) {
|
|
214
|
-
if (child.type === "run") {
|
|
215
|
-
out += renderRun(ctx, pkg, child, paraId);
|
|
216
|
-
continue;
|
|
431
|
+
const items = content.filter((item) => !SILENT_MARKERS.has(item.type) && !(ctx.opts.comments === "strip" && COMMENT_MARKERS.has(item.type)));
|
|
432
|
+
const around = (index) => {
|
|
433
|
+
const next = items[index + 1];
|
|
434
|
+
return {
|
|
435
|
+
before: out.length > 0 ? out.at(-1) : edges.before,
|
|
436
|
+
after: next === void 0 ? edges.after : renderedStart(next)
|
|
437
|
+
};
|
|
438
|
+
};
|
|
439
|
+
for (let index = 0; index < items.length; index++) {
|
|
440
|
+
const item = items[index];
|
|
441
|
+
if (item === void 0) break;
|
|
442
|
+
switch (item.type) {
|
|
443
|
+
case "run": {
|
|
444
|
+
const runs = [item];
|
|
445
|
+
while (items[index + 1]?.type === "run") {
|
|
446
|
+
runs.push(items[index + 1]);
|
|
447
|
+
index++;
|
|
217
448
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
449
|
+
out += renderRuns(ctx, pkg, runs, paraId, around(index));
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case "hyperlink":
|
|
453
|
+
out += renderHyperlink(ctx, pkg, item, paraId);
|
|
454
|
+
break;
|
|
455
|
+
case "insertion":
|
|
456
|
+
case "deletion":
|
|
457
|
+
case "moveFrom":
|
|
458
|
+
case "moveTo":
|
|
459
|
+
out += renderTrackedWrapper(ctx, pkg, item, paraId);
|
|
460
|
+
break;
|
|
461
|
+
case "commentRangeStart":
|
|
462
|
+
out += handleCommentStart(ctx, pkg, item, openComments, out.length);
|
|
463
|
+
break;
|
|
464
|
+
case "commentRangeEnd":
|
|
465
|
+
out = handleCommentEnd(ctx, item, openComments, out);
|
|
466
|
+
break;
|
|
467
|
+
case "commentReference":
|
|
468
|
+
out += renderPointComment(ctx, pkg, item.id);
|
|
469
|
+
break;
|
|
470
|
+
case "simpleField":
|
|
471
|
+
case "complexField": {
|
|
472
|
+
const runs = item.type === "simpleField" ? item.content : item.fieldResult;
|
|
473
|
+
for (const child of runs) {
|
|
474
|
+
if (child.type === "run") {
|
|
475
|
+
out += renderRuns(ctx, pkg, [child], paraId);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (child.type === "hyperlink") {
|
|
479
|
+
out += renderHyperlink(ctx, pkg, child, paraId);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
out += child.type === "inlineWrapper" ? renderParagraphInline(ctx, pkg, child.content, paraId, UNKNOWN_NEIGHBORS) : child.text;
|
|
221
483
|
}
|
|
222
|
-
|
|
484
|
+
break;
|
|
223
485
|
}
|
|
224
|
-
|
|
486
|
+
case "inlineSdt":
|
|
487
|
+
case "inlineWrapper":
|
|
488
|
+
out += renderParagraphInline(ctx, pkg, item.content, paraId, around(index));
|
|
489
|
+
break;
|
|
490
|
+
case "mathEquation":
|
|
491
|
+
if (item.plainText) out += escapeInline(item.plainText);
|
|
492
|
+
break;
|
|
493
|
+
default: break;
|
|
225
494
|
}
|
|
226
|
-
case "inlineSdt":
|
|
227
|
-
case "inlineWrapper":
|
|
228
|
-
out += renderParagraphInline(ctx, pkg, item.content, paraId);
|
|
229
|
-
break;
|
|
230
|
-
case "mathEquation":
|
|
231
|
-
if (item.plainText) out += escapeInline(item.plainText);
|
|
232
|
-
break;
|
|
233
|
-
default: break;
|
|
234
495
|
}
|
|
235
496
|
while (openComments.length) {
|
|
236
497
|
const slot = openComments.pop();
|