@beyondwork/docx-react-component 1.0.28 → 1.0.30
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/package.json +26 -37
- package/src/api/public-types.ts +531 -0
- package/src/api/session-state.ts +2 -0
- package/src/core/commands/index.ts +201 -79
- package/src/core/commands/table-structure-commands.ts +138 -5
- package/src/core/state/text-transaction.ts +370 -3
- package/src/index.ts +41 -0
- package/src/io/docx-session.ts +318 -25
- package/src/io/export/serialize-footnotes.ts +41 -46
- package/src/io/export/serialize-headers-footers.ts +36 -40
- package/src/io/export/serialize-main-document.ts +55 -89
- package/src/io/export/serialize-numbering.ts +104 -4
- package/src/io/export/serialize-runtime-revisions.ts +196 -2
- package/src/io/export/split-story-blocks-for-runtime-revisions.ts +252 -0
- package/src/io/export/table-properties-xml.ts +318 -0
- package/src/io/normalize/normalize-text.ts +34 -3
- package/src/io/ooxml/parse-comments.ts +6 -0
- package/src/io/ooxml/parse-footnotes.ts +69 -13
- package/src/io/ooxml/parse-headers-footers.ts +54 -11
- package/src/io/ooxml/parse-main-document.ts +112 -42
- package/src/io/ooxml/parse-numbering.ts +341 -26
- package/src/io/ooxml/parse-revisions.ts +118 -4
- package/src/io/ooxml/parse-styles.ts +176 -0
- package/src/io/ooxml/parse-tables.ts +34 -25
- package/src/io/ooxml/revision-boundaries.ts +127 -3
- package/src/io/ooxml/workflow-payload.ts +544 -0
- package/src/model/canonical-document.ts +91 -1
- package/src/model/snapshot.ts +112 -1
- package/src/preservation/store.ts +73 -3
- package/src/review/store/comment-store.ts +19 -1
- package/src/review/store/revision-actions.ts +29 -0
- package/src/review/store/revision-store.ts +12 -1
- package/src/review/store/revision-types.ts +11 -0
- package/src/runtime/context-analytics.ts +824 -0
- package/src/runtime/document-locations.ts +521 -0
- package/src/runtime/document-navigation.ts +14 -1
- package/src/runtime/document-outline.ts +440 -0
- package/src/runtime/document-runtime.ts +941 -45
- package/src/runtime/event-refresh-hints.ts +137 -0
- package/src/runtime/numbering-prefix.ts +67 -39
- package/src/runtime/page-layout-estimation.ts +100 -7
- package/src/runtime/resolved-numbering-geometry.ts +293 -0
- package/src/runtime/session-capabilities.ts +2 -2
- package/src/runtime/suggestions-snapshot.ts +137 -0
- package/src/runtime/surface-projection.ts +223 -27
- package/src/runtime/table-style-resolver.ts +409 -0
- package/src/runtime/view-state.ts +17 -1
- package/src/runtime/workflow-markup.ts +54 -14
- package/src/ui/WordReviewEditor.tsx +1269 -87
- package/src/ui/editor-command-bag.ts +7 -0
- package/src/ui/editor-runtime-boundary.ts +111 -10
- package/src/ui/editor-shell-view.tsx +17 -15
- package/src/ui/editor-surface-controller.tsx +5 -0
- package/src/ui/headless/selection-tool-context.ts +19 -0
- package/src/ui/headless/selection-tool-resolver.ts +752 -0
- package/src/ui/headless/selection-tool-types.ts +129 -0
- package/src/ui/headless/selection-toolbar-model.ts +10 -33
- package/src/ui/runtime-shortcut-dispatch.ts +365 -0
- package/src/ui-tailwind/chrome/chrome-preset-model.ts +107 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +15 -0
- package/src/ui-tailwind/chrome/review-queue-bar.tsx +97 -0
- package/src/ui-tailwind/chrome/tw-context-analytics-summary.tsx +122 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +1 -9
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +1 -5
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +8 -29
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +23 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +35 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +37 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +298 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +116 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-suggestion.tsx +29 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +27 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +3 -3
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +3 -3
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +86 -14
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +57 -52
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +36 -52
- package/src/ui-tailwind/editor-surface/pm-schema.ts +56 -5
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +87 -24
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +4 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +135 -32
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +74 -7
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +17 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +19 -17
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +10 -10
- package/src/ui-tailwind/status/tw-status-bar.tsx +10 -6
- package/src/ui-tailwind/theme/editor-theme.css +58 -40
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +250 -181
- package/src/ui-tailwind/tw-review-workspace.tsx +323 -280
- package/src/validation/compatibility-engine.ts +246 -2
- package/src/validation/docx-comment-proof.ts +24 -11
|
@@ -8,12 +8,35 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
|
+
CellShading,
|
|
11
12
|
CharacterStyleDefinition,
|
|
12
13
|
LatentStyleDefinition,
|
|
13
14
|
ParagraphStyleDefinition,
|
|
14
15
|
StylesCatalog,
|
|
16
|
+
TableBorders,
|
|
17
|
+
TableCellBorders,
|
|
18
|
+
TableCellMargins,
|
|
19
|
+
TableLook,
|
|
20
|
+
TableStyleConditionalRegion,
|
|
15
21
|
TableStyleDefinition,
|
|
22
|
+
TableStyleFormatting,
|
|
23
|
+
TableWidth,
|
|
16
24
|
} from "../../model/canonical-document.ts";
|
|
25
|
+
import {
|
|
26
|
+
readCellBorders,
|
|
27
|
+
readCellShading,
|
|
28
|
+
readCellVerticalAlign,
|
|
29
|
+
readCellWidth,
|
|
30
|
+
readRowHeight,
|
|
31
|
+
readRowHeightRule,
|
|
32
|
+
readRowIsHeader,
|
|
33
|
+
readTableAlignment,
|
|
34
|
+
readTableBorders,
|
|
35
|
+
readTableCellMargins,
|
|
36
|
+
readTableLook,
|
|
37
|
+
readTableWidth,
|
|
38
|
+
} from "./parse-tables.ts";
|
|
39
|
+
import { toCanonicalNumberingInstanceId } from "./parse-numbering.ts";
|
|
17
40
|
|
|
18
41
|
// ---------------------------------------------------------------------------
|
|
19
42
|
// Inline XML node types (same pattern as parse-numbering.ts)
|
|
@@ -24,11 +47,15 @@ interface XmlElementNode {
|
|
|
24
47
|
name: string;
|
|
25
48
|
attributes: Record<string, string>;
|
|
26
49
|
children: XmlNode[];
|
|
50
|
+
start: number;
|
|
51
|
+
end: number;
|
|
27
52
|
}
|
|
28
53
|
|
|
29
54
|
interface XmlTextNode {
|
|
30
55
|
type: "text";
|
|
31
56
|
text: string;
|
|
57
|
+
start: number;
|
|
58
|
+
end: number;
|
|
32
59
|
}
|
|
33
60
|
|
|
34
61
|
type XmlNode = XmlElementNode | XmlTextNode;
|
|
@@ -107,6 +134,7 @@ export function parseStylesXml(xml: string): ParseStylesResult {
|
|
|
107
134
|
case "paragraph": {
|
|
108
135
|
const nextStyle = readLinkedStyleId(child, "next");
|
|
109
136
|
const outlineLevel = readParagraphStyleOutlineLevel(child);
|
|
137
|
+
const numbering = readParagraphStyleNumbering(child);
|
|
110
138
|
paragraphs[styleId] = {
|
|
111
139
|
styleId,
|
|
112
140
|
displayName,
|
|
@@ -115,6 +143,7 @@ export function parseStylesXml(xml: string): ParseStylesResult {
|
|
|
115
143
|
...(basedOn ? { basedOn } : {}),
|
|
116
144
|
...(nextStyle ? { nextStyle } : {}),
|
|
117
145
|
...(outlineLevel !== undefined ? { outlineLevel } : {}),
|
|
146
|
+
...(numbering ? { numbering } : {}),
|
|
118
147
|
};
|
|
119
148
|
break;
|
|
120
149
|
}
|
|
@@ -129,12 +158,16 @@ export function parseStylesXml(xml: string): ParseStylesResult {
|
|
|
129
158
|
break;
|
|
130
159
|
}
|
|
131
160
|
case "table": {
|
|
161
|
+
const formatting = readTableStyleFormatting(child);
|
|
162
|
+
const conditionalFormatting = readTableConditionalFormatting(child);
|
|
132
163
|
tables[styleId] = {
|
|
133
164
|
styleId,
|
|
134
165
|
displayName,
|
|
135
166
|
kind: "table",
|
|
136
167
|
isDefault,
|
|
137
168
|
...(basedOn ? { basedOn } : {}),
|
|
169
|
+
...(formatting ? { formatting } : {}),
|
|
170
|
+
...(conditionalFormatting ? { conditionalFormatting } : {}),
|
|
138
171
|
};
|
|
139
172
|
break;
|
|
140
173
|
}
|
|
@@ -204,6 +237,140 @@ function readParagraphStyleOutlineLevel(
|
|
|
204
237
|
return Number.isInteger(parsed) && parsed >= 0 ? parsed : undefined;
|
|
205
238
|
}
|
|
206
239
|
|
|
240
|
+
function readParagraphStyleNumbering(
|
|
241
|
+
styleNode: XmlElementNode,
|
|
242
|
+
): ParagraphStyleDefinition["numbering"] | undefined {
|
|
243
|
+
const paragraphProperties = findChildElementOptional(styleNode, "pPr");
|
|
244
|
+
if (!paragraphProperties) {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const numberingProperties = findChildElementOptional(paragraphProperties, "numPr");
|
|
249
|
+
if (!numberingProperties) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const levelNode = findChildElementOptional(numberingProperties, "ilvl");
|
|
254
|
+
const instanceNode = findChildElementOptional(numberingProperties, "numId");
|
|
255
|
+
const rawInstanceId = instanceNode?.attributes["w:val"] ?? instanceNode?.attributes.val;
|
|
256
|
+
if (!rawInstanceId) {
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
if (rawInstanceId === "0") {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const rawLevel = levelNode?.attributes["w:val"] ?? levelNode?.attributes.val;
|
|
264
|
+
const parsedLevel =
|
|
265
|
+
rawLevel !== undefined ? Number.parseInt(rawLevel, 10) : Number.NaN;
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
numberingInstanceId: toCanonicalNumberingInstanceId(rawInstanceId),
|
|
269
|
+
...(Number.isInteger(parsedLevel) && parsedLevel >= 0 ? { level: parsedLevel } : {}),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function readTableStyleFormatting(styleNode: XmlElementNode): TableStyleFormatting | undefined {
|
|
273
|
+
const tableProperties = findChildElementOptional(styleNode, "tblPr");
|
|
274
|
+
const rowProperties = findChildElementOptional(styleNode, "trPr");
|
|
275
|
+
const cellProperties = findChildElementOptional(styleNode, "tcPr");
|
|
276
|
+
const formatting: TableStyleFormatting = {};
|
|
277
|
+
|
|
278
|
+
if (tableProperties) {
|
|
279
|
+
const table: NonNullable<TableStyleFormatting["table"]> = {};
|
|
280
|
+
const width = readTableWidth(tableProperties);
|
|
281
|
+
const alignment = readTableAlignment(tableProperties);
|
|
282
|
+
const borders = readTableBorders(tableProperties);
|
|
283
|
+
const cellMargins = readTableCellMargins(tableProperties);
|
|
284
|
+
const tblLook = readTableLook(tableProperties);
|
|
285
|
+
|
|
286
|
+
if (width) table.width = width as TableWidth;
|
|
287
|
+
if (alignment) table.alignment = alignment;
|
|
288
|
+
if (borders) table.borders = borders as TableBorders;
|
|
289
|
+
if (cellMargins) table.cellMargins = cellMargins as TableCellMargins;
|
|
290
|
+
if (tblLook) table.tblLook = tblLook as TableLook;
|
|
291
|
+
|
|
292
|
+
if (Object.keys(table).length > 0) {
|
|
293
|
+
formatting.table = table;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (rowProperties) {
|
|
298
|
+
const row: NonNullable<TableStyleFormatting["row"]> = {};
|
|
299
|
+
const height = readRowHeight(rowProperties);
|
|
300
|
+
const heightRule = readRowHeightRule(rowProperties);
|
|
301
|
+
const isHeader = readRowIsHeader(rowProperties);
|
|
302
|
+
|
|
303
|
+
if (height !== undefined) row.height = height;
|
|
304
|
+
if (heightRule) row.heightRule = heightRule;
|
|
305
|
+
if (isHeader !== undefined) row.isHeader = isHeader;
|
|
306
|
+
|
|
307
|
+
if (Object.keys(row).length > 0) {
|
|
308
|
+
formatting.row = row;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (cellProperties) {
|
|
313
|
+
const cell: NonNullable<TableStyleFormatting["cell"]> = {};
|
|
314
|
+
const width = readCellWidth(cellProperties);
|
|
315
|
+
const borders = readCellBorders(cellProperties);
|
|
316
|
+
const shading = readCellShading(cellProperties);
|
|
317
|
+
const verticalAlign = readCellVerticalAlign(cellProperties);
|
|
318
|
+
|
|
319
|
+
if (width) cell.width = width as TableWidth;
|
|
320
|
+
if (borders) cell.borders = borders as TableCellBorders;
|
|
321
|
+
if (shading) cell.shading = shading as CellShading;
|
|
322
|
+
if (verticalAlign) cell.verticalAlign = verticalAlign;
|
|
323
|
+
|
|
324
|
+
if (Object.keys(cell).length > 0) {
|
|
325
|
+
formatting.cell = cell;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return Object.keys(formatting).length > 0 ? formatting : undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function readTableConditionalFormatting(
|
|
333
|
+
styleNode: XmlElementNode,
|
|
334
|
+
): Partial<Record<TableStyleConditionalRegion, TableStyleFormatting>> | undefined {
|
|
335
|
+
const conditionalFormatting: Partial<Record<TableStyleConditionalRegion, TableStyleFormatting>> = {};
|
|
336
|
+
|
|
337
|
+
for (const child of styleNode.children) {
|
|
338
|
+
if (child.type !== "element" || localName(child.name) !== "tblStylePr") {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const region = mapTableConditionalRegion(child.attributes["w:type"] ?? child.attributes.type);
|
|
343
|
+
if (!region) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const formatting = readTableStyleFormatting(child);
|
|
348
|
+
if (formatting) {
|
|
349
|
+
conditionalFormatting[region] = formatting;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return Object.keys(conditionalFormatting).length > 0 ? conditionalFormatting : undefined;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function mapTableConditionalRegion(value: string | undefined): TableStyleConditionalRegion | undefined {
|
|
357
|
+
switch (value) {
|
|
358
|
+
case "firstRow":
|
|
359
|
+
case "lastRow":
|
|
360
|
+
case "band1Horz":
|
|
361
|
+
case "band2Horz":
|
|
362
|
+
case "band1Vert":
|
|
363
|
+
case "band2Vert":
|
|
364
|
+
return value;
|
|
365
|
+
case "firstCol":
|
|
366
|
+
return "firstColumn";
|
|
367
|
+
case "lastCol":
|
|
368
|
+
return "lastColumn";
|
|
369
|
+
default:
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
207
374
|
function readLatentStyles(
|
|
208
375
|
latentNode: XmlElementNode,
|
|
209
376
|
out: Record<string, LatentStyleDefinition>,
|
|
@@ -263,6 +430,8 @@ function parseXml(xml: string): XmlElementNode {
|
|
|
263
430
|
name: "__root__",
|
|
264
431
|
attributes: {},
|
|
265
432
|
children: [],
|
|
433
|
+
start: 0,
|
|
434
|
+
end: xml.length,
|
|
266
435
|
};
|
|
267
436
|
const stack: XmlElementNode[] = [root];
|
|
268
437
|
let cursor = 0;
|
|
@@ -286,6 +455,8 @@ function parseXml(xml: string): XmlElementNode {
|
|
|
286
455
|
stack[stack.length - 1]?.children.push({
|
|
287
456
|
type: "text",
|
|
288
457
|
text: xml.slice(cursor + 9, textEnd),
|
|
458
|
+
start: cursor,
|
|
459
|
+
end: end >= 0 ? end + 3 : xml.length,
|
|
289
460
|
});
|
|
290
461
|
cursor = end >= 0 ? end + 3 : xml.length;
|
|
291
462
|
continue;
|
|
@@ -299,6 +470,8 @@ function parseXml(xml: string): XmlElementNode {
|
|
|
299
470
|
stack[stack.length - 1]?.children.push({
|
|
300
471
|
type: "text",
|
|
301
472
|
text,
|
|
473
|
+
start: cursor,
|
|
474
|
+
end,
|
|
302
475
|
});
|
|
303
476
|
}
|
|
304
477
|
cursor = end;
|
|
@@ -316,6 +489,7 @@ function parseXml(xml: string): XmlElementNode {
|
|
|
316
489
|
if (!current || localName(current.name) !== localName(name)) {
|
|
317
490
|
throw new Error(`Malformed XML: unexpected closing tag </${name}>.`);
|
|
318
491
|
}
|
|
492
|
+
current.end = end + 1;
|
|
319
493
|
|
|
320
494
|
cursor = end + 1;
|
|
321
495
|
continue;
|
|
@@ -330,6 +504,8 @@ function parseXml(xml: string): XmlElementNode {
|
|
|
330
504
|
name,
|
|
331
505
|
attributes,
|
|
332
506
|
children: [],
|
|
507
|
+
start: cursor,
|
|
508
|
+
end: tagEnd + 1,
|
|
333
509
|
};
|
|
334
510
|
stack[stack.length - 1]?.children.push(element);
|
|
335
511
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface XmlElementNode {
|
|
1
|
+
export interface XmlElementNode {
|
|
2
2
|
type: "element";
|
|
3
3
|
name: string;
|
|
4
4
|
attributes: Record<string, string>;
|
|
@@ -7,14 +7,14 @@ interface XmlElementNode {
|
|
|
7
7
|
end: number;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
interface XmlTextNode {
|
|
10
|
+
export interface XmlTextNode {
|
|
11
11
|
type: "text";
|
|
12
12
|
text: string;
|
|
13
13
|
start: number;
|
|
14
14
|
end: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
type XmlNode = XmlElementNode | XmlTextNode;
|
|
17
|
+
export type XmlNode = XmlElementNode | XmlTextNode;
|
|
18
18
|
|
|
19
19
|
export interface ParsedBorderSpec {
|
|
20
20
|
value?: string;
|
|
@@ -163,7 +163,7 @@ function parseRow(node: XmlElementNode, sourceXml: string): ParsedTableRow {
|
|
|
163
163
|
rawXml: sourceXml.slice(node.start, node.end),
|
|
164
164
|
...(height !== undefined ? { height } : {}),
|
|
165
165
|
...(heightRule ? { heightRule } : {}),
|
|
166
|
-
...(isHeader ? { isHeader } : {}),
|
|
166
|
+
...(isHeader !== undefined ? { isHeader } : {}),
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -193,7 +193,7 @@ function parseCell(node: XmlElementNode, sourceXml: string): ParsedTableCell {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
function readGridColumns(node: XmlElementNode): number[] {
|
|
196
|
+
export function readGridColumns(node: XmlElementNode): number[] {
|
|
197
197
|
return node.children
|
|
198
198
|
.filter((child): child is XmlElementNode => child.type === "element" && localName(child.name) === "gridCol")
|
|
199
199
|
.map((child) => parsePositiveInteger(child.attributes["w:w"] ?? child.attributes.w ?? "0"));
|
|
@@ -443,7 +443,7 @@ function decodeXmlEntities(value: string): string {
|
|
|
443
443
|
|
|
444
444
|
// Cell property readers
|
|
445
445
|
|
|
446
|
-
function readCellWidth(propertiesNode: XmlElementNode): ParsedTableWidth | undefined {
|
|
446
|
+
export function readCellWidth(propertiesNode: XmlElementNode): ParsedTableWidth | undefined {
|
|
447
447
|
const widthNode = findFirstChild(propertiesNode, "tcW");
|
|
448
448
|
if (!widthNode) return undefined;
|
|
449
449
|
const value = parsePositiveInteger(widthNode.attributes["w:w"] ?? widthNode.attributes.w);
|
|
@@ -453,7 +453,7 @@ function readCellWidth(propertiesNode: XmlElementNode): ParsedTableWidth | undef
|
|
|
453
453
|
return { value, type };
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
-
function readCellBorders(propertiesNode: XmlElementNode): ParsedTableCellBorders | undefined {
|
|
456
|
+
export function readCellBorders(propertiesNode: XmlElementNode): ParsedTableCellBorders | undefined {
|
|
457
457
|
const bordersNode = findFirstChild(propertiesNode, "tcBorders");
|
|
458
458
|
if (!bordersNode) return undefined;
|
|
459
459
|
const borders: ParsedTableCellBorders = {};
|
|
@@ -467,7 +467,7 @@ function readCellBorders(propertiesNode: XmlElementNode): ParsedTableCellBorders
|
|
|
467
467
|
return Object.keys(borders).length > 0 ? borders : undefined;
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
-
function readCellShading(propertiesNode: XmlElementNode): ParsedCellShading | undefined {
|
|
470
|
+
export function readCellShading(propertiesNode: XmlElementNode): ParsedCellShading | undefined {
|
|
471
471
|
const shdNode = findFirstChild(propertiesNode, "shd");
|
|
472
472
|
if (!shdNode) return undefined;
|
|
473
473
|
const fill = shdNode.attributes["w:fill"] ?? shdNode.attributes.fill;
|
|
@@ -481,7 +481,7 @@ function readCellShading(propertiesNode: XmlElementNode): ParsedCellShading | un
|
|
|
481
481
|
return result;
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
function readCellVerticalAlign(propertiesNode: XmlElementNode): "top" | "center" | "bottom" | undefined {
|
|
484
|
+
export function readCellVerticalAlign(propertiesNode: XmlElementNode): "top" | "center" | "bottom" | undefined {
|
|
485
485
|
const vAlignNode = findFirstChild(propertiesNode, "vAlign");
|
|
486
486
|
if (!vAlignNode) return undefined;
|
|
487
487
|
const val = vAlignNode.attributes["w:val"] ?? vAlignNode.attributes.val;
|
|
@@ -491,7 +491,7 @@ function readCellVerticalAlign(propertiesNode: XmlElementNode): "top" | "center"
|
|
|
491
491
|
|
|
492
492
|
// Table property readers
|
|
493
493
|
|
|
494
|
-
function readTableWidth(propertiesNode: XmlElementNode): ParsedTableWidth | undefined {
|
|
494
|
+
export function readTableWidth(propertiesNode: XmlElementNode): ParsedTableWidth | undefined {
|
|
495
495
|
const widthNode = findFirstChild(propertiesNode, "tblW");
|
|
496
496
|
if (!widthNode) return undefined;
|
|
497
497
|
const value = parsePositiveInteger(widthNode.attributes["w:w"] ?? widthNode.attributes.w);
|
|
@@ -501,7 +501,7 @@ function readTableWidth(propertiesNode: XmlElementNode): ParsedTableWidth | unde
|
|
|
501
501
|
return { value, type };
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
-
function readTableAlignment(propertiesNode: XmlElementNode): "left" | "center" | "right" | undefined {
|
|
504
|
+
export function readTableAlignment(propertiesNode: XmlElementNode): "left" | "center" | "right" | undefined {
|
|
505
505
|
const jcNode = findFirstChild(propertiesNode, "jc");
|
|
506
506
|
if (!jcNode) return undefined;
|
|
507
507
|
const val = jcNode.attributes["w:val"] ?? jcNode.attributes.val;
|
|
@@ -509,13 +509,13 @@ function readTableAlignment(propertiesNode: XmlElementNode): "left" | "center" |
|
|
|
509
509
|
return undefined;
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
-
function readTableStyleId(propertiesNode: XmlElementNode): string | undefined {
|
|
512
|
+
export function readTableStyleId(propertiesNode: XmlElementNode): string | undefined {
|
|
513
513
|
const styleNode = findFirstChild(propertiesNode, "tblStyle");
|
|
514
514
|
if (!styleNode) return undefined;
|
|
515
515
|
return styleNode.attributes["w:val"] ?? styleNode.attributes.val;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
-
function readTableLook(propertiesNode: XmlElementNode): ParsedTableLook | undefined {
|
|
518
|
+
export function readTableLook(propertiesNode: XmlElementNode): ParsedTableLook | undefined {
|
|
519
519
|
const tblLookNode = findFirstChild(propertiesNode, "tblLook");
|
|
520
520
|
if (!tblLookNode) return undefined;
|
|
521
521
|
|
|
@@ -543,7 +543,7 @@ function readTableLook(propertiesNode: XmlElementNode): ParsedTableLook | undefi
|
|
|
543
543
|
return Object.keys(tableLook).length > 0 ? tableLook : undefined;
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
-
function readTableBorders(propertiesNode: XmlElementNode): ParsedTableBorders | undefined {
|
|
546
|
+
export function readTableBorders(propertiesNode: XmlElementNode): ParsedTableBorders | undefined {
|
|
547
547
|
const bordersNode = findFirstChild(propertiesNode, "tblBorders");
|
|
548
548
|
if (!bordersNode) return undefined;
|
|
549
549
|
const borders: ParsedTableBorders = {};
|
|
@@ -557,30 +557,39 @@ function readTableBorders(propertiesNode: XmlElementNode): ParsedTableBorders |
|
|
|
557
557
|
return Object.keys(borders).length > 0 ? borders : undefined;
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
function readTableCellMargins(propertiesNode: XmlElementNode): ParsedCellMargins | undefined {
|
|
560
|
+
export function readTableCellMargins(propertiesNode: XmlElementNode): ParsedCellMargins | undefined {
|
|
561
561
|
const marginsNode = findFirstChild(propertiesNode, "tblCellMar");
|
|
562
562
|
if (!marginsNode) return undefined;
|
|
563
|
-
const readSide = (name: string): number => {
|
|
564
|
-
const
|
|
565
|
-
|
|
563
|
+
const readSide = (name: string): number | undefined => {
|
|
564
|
+
const node = findFirstChild(marginsNode, name);
|
|
565
|
+
if (!node) return undefined;
|
|
566
|
+
return parsePositiveInteger(node.attributes["w:w"] ?? node.attributes.w);
|
|
566
567
|
};
|
|
567
568
|
const top = readSide("top");
|
|
568
569
|
const bottom = readSide("bottom");
|
|
569
|
-
const left = readSide("start")
|
|
570
|
-
const right = readSide("end")
|
|
571
|
-
if (top ===
|
|
572
|
-
|
|
570
|
+
const left = readSide("start") ?? readSide("left");
|
|
571
|
+
const right = readSide("end") ?? readSide("right");
|
|
572
|
+
if (top === undefined && bottom === undefined && left === undefined && right === undefined) {
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
...(top !== undefined ? { top } : {}),
|
|
578
|
+
...(bottom !== undefined ? { bottom } : {}),
|
|
579
|
+
...(left !== undefined ? { left } : {}),
|
|
580
|
+
...(right !== undefined ? { right } : {}),
|
|
581
|
+
};
|
|
573
582
|
}
|
|
574
583
|
|
|
575
584
|
// Row property readers
|
|
576
585
|
|
|
577
|
-
function readRowHeight(propertiesNode: XmlElementNode): number | undefined {
|
|
586
|
+
export function readRowHeight(propertiesNode: XmlElementNode): number | undefined {
|
|
578
587
|
const heightNode = findFirstChild(propertiesNode, "trHeight");
|
|
579
588
|
if (!heightNode) return undefined;
|
|
580
589
|
return parsePositiveInteger(heightNode.attributes["w:val"] ?? heightNode.attributes.val);
|
|
581
590
|
}
|
|
582
591
|
|
|
583
|
-
function readRowHeightRule(propertiesNode: XmlElementNode): "auto" | "atLeast" | "exact" | undefined {
|
|
592
|
+
export function readRowHeightRule(propertiesNode: XmlElementNode): "auto" | "atLeast" | "exact" | undefined {
|
|
584
593
|
const heightNode = findFirstChild(propertiesNode, "trHeight");
|
|
585
594
|
if (!heightNode) return undefined;
|
|
586
595
|
const raw = (heightNode.attributes["w:hRule"] ?? heightNode.attributes.hRule ?? "").toLowerCase();
|
|
@@ -590,7 +599,7 @@ function readRowHeightRule(propertiesNode: XmlElementNode): "auto" | "atLeast" |
|
|
|
590
599
|
return undefined;
|
|
591
600
|
}
|
|
592
601
|
|
|
593
|
-
function readRowIsHeader(propertiesNode: XmlElementNode): boolean | undefined {
|
|
602
|
+
export function readRowIsHeader(propertiesNode: XmlElementNode): boolean | undefined {
|
|
594
603
|
const headerNode = findFirstChild(propertiesNode, "tblHeader");
|
|
595
604
|
if (!headerNode) return undefined;
|
|
596
605
|
const val = headerNode.attributes["w:val"] ?? headerNode.attributes.val;
|
|
@@ -50,8 +50,21 @@ export function mapRevisionBoundaries(
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (localName(child.name) !== "p") {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
if (localName(child.name) === "tbl") {
|
|
54
|
+
const nested = collectNestedParagraphBoundaries(
|
|
55
|
+
child.children,
|
|
56
|
+
paragraphIndex,
|
|
57
|
+
cursor,
|
|
58
|
+
previousWasParagraph,
|
|
59
|
+
);
|
|
60
|
+
paragraphIndex = nested.paragraphIndex;
|
|
61
|
+
cursor = nested.cursor;
|
|
62
|
+
previousWasParagraph = nested.previousWasParagraph;
|
|
63
|
+
paragraphs.push(...nested.paragraphs);
|
|
64
|
+
} else {
|
|
65
|
+
cursor += 1;
|
|
66
|
+
previousWasParagraph = false;
|
|
67
|
+
}
|
|
55
68
|
continue;
|
|
56
69
|
}
|
|
57
70
|
|
|
@@ -105,6 +118,117 @@ export function mapRevisionBoundaries(
|
|
|
105
118
|
return paragraphs;
|
|
106
119
|
}
|
|
107
120
|
|
|
121
|
+
function collectNestedParagraphBoundaries(
|
|
122
|
+
nodes: XmlNode[],
|
|
123
|
+
paragraphIndex: number,
|
|
124
|
+
cursor: number,
|
|
125
|
+
previousWasParagraph: boolean,
|
|
126
|
+
): {
|
|
127
|
+
paragraphs: RevisionParagraphBoundary[];
|
|
128
|
+
paragraphIndex: number;
|
|
129
|
+
cursor: number;
|
|
130
|
+
previousWasParagraph: boolean;
|
|
131
|
+
} {
|
|
132
|
+
const paragraphs: RevisionParagraphBoundary[] = [];
|
|
133
|
+
let nextParagraphIndex = paragraphIndex;
|
|
134
|
+
let nextCursor = cursor;
|
|
135
|
+
let nextPreviousWasParagraph = previousWasParagraph;
|
|
136
|
+
|
|
137
|
+
for (const node of nodes) {
|
|
138
|
+
if (node.type !== "element") {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const type = localName(node.name);
|
|
142
|
+
|
|
143
|
+
if (type === "p") {
|
|
144
|
+
nextParagraphIndex += 1;
|
|
145
|
+
const boundaries = new Map<number, number>();
|
|
146
|
+
boundaries.set(nextCursor, node.openingTagEnd);
|
|
147
|
+
|
|
148
|
+
const paragraphProperties = findChildElement(node, "pPr");
|
|
149
|
+
const paragraphRunProperties = paragraphProperties
|
|
150
|
+
? findChildElement(paragraphProperties, "rPr")
|
|
151
|
+
: undefined;
|
|
152
|
+
|
|
153
|
+
walkStoryNodesForBoundaries(
|
|
154
|
+
node.children,
|
|
155
|
+
boundaries,
|
|
156
|
+
() => nextCursor,
|
|
157
|
+
(next) => {
|
|
158
|
+
nextCursor = next;
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
boundaries.set(nextCursor, node.closingTagStart);
|
|
163
|
+
paragraphs.push({
|
|
164
|
+
paragraphIndex: nextParagraphIndex,
|
|
165
|
+
start: Math.min(...boundaries.keys()),
|
|
166
|
+
end: Math.max(...boundaries.keys()),
|
|
167
|
+
boundaries,
|
|
168
|
+
paragraphStart: node.start,
|
|
169
|
+
paragraphStartTagEnd: node.openingTagEnd,
|
|
170
|
+
paragraphEndTagStart: node.closingTagStart,
|
|
171
|
+
paragraphEnd: node.end,
|
|
172
|
+
...(paragraphProperties
|
|
173
|
+
? {
|
|
174
|
+
paragraphPropertiesStart: paragraphProperties.start,
|
|
175
|
+
paragraphPropertiesEnd: paragraphProperties.end,
|
|
176
|
+
}
|
|
177
|
+
: {}),
|
|
178
|
+
...(paragraphRunProperties
|
|
179
|
+
? {
|
|
180
|
+
paragraphRunPropertiesStart: paragraphRunProperties.start,
|
|
181
|
+
paragraphRunPropertiesEnd: paragraphRunProperties.end,
|
|
182
|
+
}
|
|
183
|
+
: {}),
|
|
184
|
+
});
|
|
185
|
+
nextPreviousWasParagraph = false;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
type === "tbl" ||
|
|
191
|
+
type === "tr" ||
|
|
192
|
+
type === "tc" ||
|
|
193
|
+
type === "sdtContent" ||
|
|
194
|
+
type === "customXml" ||
|
|
195
|
+
type === "smartTag"
|
|
196
|
+
) {
|
|
197
|
+
const nested = collectNestedParagraphBoundaries(
|
|
198
|
+
node.children,
|
|
199
|
+
nextParagraphIndex,
|
|
200
|
+
nextCursor,
|
|
201
|
+
nextPreviousWasParagraph,
|
|
202
|
+
);
|
|
203
|
+
nextParagraphIndex = nested.paragraphIndex;
|
|
204
|
+
nextCursor = nested.cursor;
|
|
205
|
+
nextPreviousWasParagraph = nested.previousWasParagraph;
|
|
206
|
+
paragraphs.push(...nested.paragraphs);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (
|
|
211
|
+
type === "tblPr" ||
|
|
212
|
+
type === "tblGrid" ||
|
|
213
|
+
type === "gridCol" ||
|
|
214
|
+
type === "trPr" ||
|
|
215
|
+
type === "tcPr"
|
|
216
|
+
) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
nextCursor += 1;
|
|
221
|
+
nextPreviousWasParagraph = false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
paragraphs,
|
|
226
|
+
paragraphIndex: nextParagraphIndex,
|
|
227
|
+
cursor: nextCursor,
|
|
228
|
+
previousWasParagraph: nextPreviousWasParagraph,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
108
232
|
function walkStoryNodesForBoundaries(
|
|
109
233
|
nodes: XmlNode[],
|
|
110
234
|
boundaries: Map<number, number>,
|
|
@@ -163,7 +287,7 @@ function walkStoryNodeForBoundaries(
|
|
|
163
287
|
if (!boundaries.has(getCursor())) {
|
|
164
288
|
boundaries.set(getCursor(), node.start);
|
|
165
289
|
}
|
|
166
|
-
setCursor(getCursor() + text.length);
|
|
290
|
+
setCursor(getCursor() + Array.from(text).length);
|
|
167
291
|
boundaries.set(getCursor(), node.end);
|
|
168
292
|
return;
|
|
169
293
|
}
|