@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
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
NumberingCatalog,
|
|
3
|
+
NumberingLevelDefinition,
|
|
4
|
+
NumberingLevelParagraphGeometry,
|
|
5
|
+
NumberingLevelOverride,
|
|
6
|
+
NumberingLevelOverrideDefinition,
|
|
7
|
+
ParagraphSpacing,
|
|
8
|
+
ParagraphIndentation,
|
|
9
|
+
TabStop,
|
|
10
|
+
} from "../../model/canonical-document.ts";
|
|
2
11
|
|
|
3
12
|
export interface ParsedParagraphNumberingReference {
|
|
4
13
|
paragraphIndex: number;
|
|
@@ -136,32 +145,12 @@ function readLevels(abstractNode: XmlElementNode): NumberingLevelDefinition[] {
|
|
|
136
145
|
if (level === undefined) {
|
|
137
146
|
continue;
|
|
138
147
|
}
|
|
148
|
+
const definition = readLevelDefinition(child, level);
|
|
149
|
+
if (!definition) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
139
152
|
|
|
140
|
-
|
|
141
|
-
const formatNode = findChildElementOptional(child, "numFmt");
|
|
142
|
-
const textNode = findChildElementOptional(child, "lvlText");
|
|
143
|
-
const paragraphStyleNode = findChildElementOptional(child, "pStyle");
|
|
144
|
-
const rawStart = startNode?.attributes["w:val"] ?? startNode?.attributes.val;
|
|
145
|
-
const startAt = rawStart === undefined ? undefined : parseInteger(rawStart);
|
|
146
|
-
const format = formatNode?.attributes["w:val"] ?? formatNode?.attributes.val ?? "decimal";
|
|
147
|
-
const text = textNode?.attributes["w:val"] ?? textNode?.attributes.val ?? `%${level + 1}.`;
|
|
148
|
-
const paragraphStyleId =
|
|
149
|
-
paragraphStyleNode?.attributes["w:val"] ?? paragraphStyleNode?.attributes.val;
|
|
150
|
-
const isLegalNode = findChildElementOptional(child, "isLgl");
|
|
151
|
-
const isLegalNumbering = isLegalNode !== undefined;
|
|
152
|
-
const suffixNode = findChildElementOptional(child, "suff");
|
|
153
|
-
const suffixVal = suffixNode?.attributes["w:val"] ?? suffixNode?.attributes.val;
|
|
154
|
-
const suffix = suffixVal === "space" || suffixVal === "nothing" ? suffixVal : suffixVal === "tab" ? "tab" : undefined;
|
|
155
|
-
|
|
156
|
-
levels.push({
|
|
157
|
-
level,
|
|
158
|
-
format,
|
|
159
|
-
text,
|
|
160
|
-
...(startAt !== undefined ? { startAt } : {}),
|
|
161
|
-
...(paragraphStyleId ? { paragraphStyleId } : {}),
|
|
162
|
-
...(isLegalNumbering ? { isLegalNumbering } : {}),
|
|
163
|
-
...(suffix ? { suffix } : {}),
|
|
164
|
-
});
|
|
153
|
+
levels.push(definition);
|
|
165
154
|
}
|
|
166
155
|
|
|
167
156
|
return levels.sort((left, right) => left.level - right.level);
|
|
@@ -184,16 +173,342 @@ function readOverrides(numNode: XmlElementNode): NumberingLevelOverride[] {
|
|
|
184
173
|
const startOverrideNode = findChildElementOptional(child, "startOverride");
|
|
185
174
|
const rawStart = startOverrideNode?.attributes["w:val"] ?? startOverrideNode?.attributes.val;
|
|
186
175
|
const startAt = rawStart === undefined ? undefined : parseInteger(rawStart);
|
|
176
|
+
const levelDefinitionNode = findChildElementOptional(child, "lvl");
|
|
177
|
+
const levelDefinition = levelDefinitionNode
|
|
178
|
+
? readLevelOverrideDefinition(levelDefinitionNode, level)
|
|
179
|
+
: undefined;
|
|
187
180
|
|
|
188
181
|
overrides.push({
|
|
189
182
|
level,
|
|
190
183
|
...(startAt !== undefined ? { startAt } : {}),
|
|
184
|
+
...(levelDefinition ? { levelDefinition } : {}),
|
|
191
185
|
});
|
|
192
186
|
}
|
|
193
187
|
|
|
194
188
|
return overrides.sort((left, right) => left.level - right.level);
|
|
195
189
|
}
|
|
196
190
|
|
|
191
|
+
function readLevelDefinition(
|
|
192
|
+
levelNode: XmlElementNode,
|
|
193
|
+
fallbackLevel?: number,
|
|
194
|
+
): NumberingLevelDefinition | undefined {
|
|
195
|
+
const rawLevel = levelNode.attributes["w:ilvl"] ?? levelNode.attributes.ilvl;
|
|
196
|
+
const level = rawLevel === undefined ? fallbackLevel : parseInteger(rawLevel);
|
|
197
|
+
if (level === undefined) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const startNode = findChildElementOptional(levelNode, "start");
|
|
202
|
+
const formatNode = findChildElementOptional(levelNode, "numFmt");
|
|
203
|
+
const textNode = findChildElementOptional(levelNode, "lvlText");
|
|
204
|
+
const paragraphStyleNode = findChildElementOptional(levelNode, "pStyle");
|
|
205
|
+
const rawStart = startNode?.attributes["w:val"] ?? startNode?.attributes.val;
|
|
206
|
+
const startAt = rawStart === undefined ? undefined : parseInteger(rawStart);
|
|
207
|
+
const format = formatNode?.attributes["w:val"] ?? formatNode?.attributes.val ?? "decimal";
|
|
208
|
+
const text = textNode?.attributes["w:val"] ?? textNode?.attributes.val ?? `%${level + 1}.`;
|
|
209
|
+
const paragraphStyleId =
|
|
210
|
+
paragraphStyleNode?.attributes["w:val"] ?? paragraphStyleNode?.attributes.val;
|
|
211
|
+
const isLegalNode = findChildElementOptional(levelNode, "isLgl");
|
|
212
|
+
const isLegalNumbering = readIsLegalNumberingValue(isLegalNode);
|
|
213
|
+
const suffixNode = findChildElementOptional(levelNode, "suff");
|
|
214
|
+
const suffixVal = suffixNode?.attributes["w:val"] ?? suffixNode?.attributes.val;
|
|
215
|
+
const suffix =
|
|
216
|
+
suffixVal === "space" || suffixVal === "nothing"
|
|
217
|
+
? suffixVal
|
|
218
|
+
: suffixVal === "tab"
|
|
219
|
+
? "tab"
|
|
220
|
+
: undefined;
|
|
221
|
+
const paragraphGeometry = readLevelParagraphGeometry(levelNode);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
level,
|
|
225
|
+
format,
|
|
226
|
+
text,
|
|
227
|
+
...(startAt !== undefined ? { startAt } : {}),
|
|
228
|
+
...(paragraphStyleId ? { paragraphStyleId } : {}),
|
|
229
|
+
...(isLegalNumbering ? { isLegalNumbering: true } : {}),
|
|
230
|
+
...(suffix ? { suffix } : {}),
|
|
231
|
+
...(paragraphGeometry ? { paragraphGeometry } : {}),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function readLevelOverrideDefinition(
|
|
236
|
+
levelNode: XmlElementNode,
|
|
237
|
+
fallbackLevel?: number,
|
|
238
|
+
): NumberingLevelOverrideDefinition | undefined {
|
|
239
|
+
const rawLevel = levelNode.attributes["w:ilvl"] ?? levelNode.attributes.ilvl;
|
|
240
|
+
const level = rawLevel === undefined ? fallbackLevel : parseInteger(rawLevel);
|
|
241
|
+
if (level === undefined) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const startNode = findChildElementOptional(levelNode, "start");
|
|
246
|
+
const formatNode = findChildElementOptional(levelNode, "numFmt");
|
|
247
|
+
const textNode = findChildElementOptional(levelNode, "lvlText");
|
|
248
|
+
const paragraphStyleNode = findChildElementOptional(levelNode, "pStyle");
|
|
249
|
+
const rawStart = startNode?.attributes["w:val"] ?? startNode?.attributes.val;
|
|
250
|
+
const startAt = rawStart === undefined ? undefined : parseInteger(rawStart);
|
|
251
|
+
const format = formatNode?.attributes["w:val"] ?? formatNode?.attributes.val;
|
|
252
|
+
const text = textNode?.attributes["w:val"] ?? textNode?.attributes.val;
|
|
253
|
+
const paragraphStyleId =
|
|
254
|
+
paragraphStyleNode?.attributes["w:val"] ?? paragraphStyleNode?.attributes.val;
|
|
255
|
+
const isLegalNode = findChildElementOptional(levelNode, "isLgl");
|
|
256
|
+
const isLegalNumbering = readIsLegalNumberingValue(isLegalNode);
|
|
257
|
+
const suffixNode = findChildElementOptional(levelNode, "suff");
|
|
258
|
+
const suffixVal = suffixNode?.attributes["w:val"] ?? suffixNode?.attributes.val;
|
|
259
|
+
const suffix =
|
|
260
|
+
suffixVal === "space" || suffixVal === "nothing"
|
|
261
|
+
? suffixVal
|
|
262
|
+
: suffixVal === "tab"
|
|
263
|
+
? "tab"
|
|
264
|
+
: undefined;
|
|
265
|
+
const paragraphGeometry = readLevelParagraphGeometry(levelNode);
|
|
266
|
+
|
|
267
|
+
const hasExplicitFields =
|
|
268
|
+
startAt !== undefined ||
|
|
269
|
+
format !== undefined ||
|
|
270
|
+
text !== undefined ||
|
|
271
|
+
paragraphStyleId !== undefined ||
|
|
272
|
+
isLegalNumbering !== undefined ||
|
|
273
|
+
suffix !== undefined ||
|
|
274
|
+
paragraphGeometry !== undefined;
|
|
275
|
+
|
|
276
|
+
if (!hasExplicitFields) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
level,
|
|
282
|
+
...(startAt !== undefined ? { startAt } : {}),
|
|
283
|
+
...(format !== undefined ? { format } : {}),
|
|
284
|
+
...(text !== undefined ? { text } : {}),
|
|
285
|
+
...(paragraphStyleId ? { paragraphStyleId } : {}),
|
|
286
|
+
...(isLegalNumbering !== undefined ? { isLegalNumbering } : {}),
|
|
287
|
+
...(suffix ? { suffix } : {}),
|
|
288
|
+
...(paragraphGeometry ? { paragraphGeometry } : {}),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function readLevelParagraphGeometry(
|
|
293
|
+
levelNode: XmlElementNode,
|
|
294
|
+
): NumberingLevelDefinition["paragraphGeometry"] | undefined {
|
|
295
|
+
const paragraphPropertiesNode = findChildElementOptional(levelNode, "pPr");
|
|
296
|
+
const justification = readLevelJustification(levelNode);
|
|
297
|
+
const spacing = paragraphPropertiesNode ? readParagraphSpacing(paragraphPropertiesNode) : undefined;
|
|
298
|
+
const indentation = paragraphPropertiesNode
|
|
299
|
+
? readParagraphIndentation(paragraphPropertiesNode)
|
|
300
|
+
: undefined;
|
|
301
|
+
const tabStops = paragraphPropertiesNode ? readParagraphTabStops(paragraphPropertiesNode) : undefined;
|
|
302
|
+
|
|
303
|
+
if (!justification && !spacing && !indentation && (!tabStops || tabStops.length === 0)) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
...(justification ? { justification } : {}),
|
|
309
|
+
...(spacing ? { spacing } : {}),
|
|
310
|
+
...(indentation ? { indentation } : {}),
|
|
311
|
+
...(tabStops && tabStops.length > 0 ? { tabStops } : {}),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function readLevelJustification(
|
|
316
|
+
levelNode: XmlElementNode,
|
|
317
|
+
): NumberingLevelParagraphGeometry["justification"] {
|
|
318
|
+
const justificationNode = findChildElementOptional(levelNode, "lvlJc");
|
|
319
|
+
if (!justificationNode) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const rawValue = (
|
|
324
|
+
justificationNode.attributes["w:val"] ??
|
|
325
|
+
justificationNode.attributes.val ??
|
|
326
|
+
""
|
|
327
|
+
).toLowerCase();
|
|
328
|
+
|
|
329
|
+
if (rawValue === "start") {
|
|
330
|
+
return "left";
|
|
331
|
+
}
|
|
332
|
+
if (rawValue === "end") {
|
|
333
|
+
return "right";
|
|
334
|
+
}
|
|
335
|
+
if (
|
|
336
|
+
rawValue === "left" ||
|
|
337
|
+
rawValue === "center" ||
|
|
338
|
+
rawValue === "right" ||
|
|
339
|
+
rawValue === "both" ||
|
|
340
|
+
rawValue === "distribute"
|
|
341
|
+
) {
|
|
342
|
+
return rawValue;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function readParagraphIndentation(node: XmlElementNode): ParagraphIndentation | undefined {
|
|
349
|
+
const indNode = findChildElementOptional(node, "ind");
|
|
350
|
+
if (!indNode) {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const indentation: ParagraphIndentation = {};
|
|
355
|
+
const left =
|
|
356
|
+
indNode.attributes["w:start"] ??
|
|
357
|
+
indNode.attributes.start ??
|
|
358
|
+
indNode.attributes["w:left"] ??
|
|
359
|
+
indNode.attributes.left;
|
|
360
|
+
const right =
|
|
361
|
+
indNode.attributes["w:end"] ??
|
|
362
|
+
indNode.attributes.end ??
|
|
363
|
+
indNode.attributes["w:right"] ??
|
|
364
|
+
indNode.attributes.right;
|
|
365
|
+
const firstLine = indNode.attributes["w:firstLine"] ?? indNode.attributes.firstLine;
|
|
366
|
+
const hanging = indNode.attributes["w:hanging"] ?? indNode.attributes.hanging;
|
|
367
|
+
|
|
368
|
+
if (left !== undefined) {
|
|
369
|
+
const value = Number.parseInt(left, 10);
|
|
370
|
+
if (Number.isFinite(value)) indentation.left = value;
|
|
371
|
+
}
|
|
372
|
+
if (right !== undefined) {
|
|
373
|
+
const value = Number.parseInt(right, 10);
|
|
374
|
+
if (Number.isFinite(value)) indentation.right = value;
|
|
375
|
+
}
|
|
376
|
+
if (firstLine !== undefined) {
|
|
377
|
+
const value = Number.parseInt(firstLine, 10);
|
|
378
|
+
if (Number.isFinite(value)) indentation.firstLine = value;
|
|
379
|
+
}
|
|
380
|
+
if (hanging !== undefined) {
|
|
381
|
+
const value = Number.parseInt(hanging, 10);
|
|
382
|
+
if (Number.isFinite(value)) indentation.hanging = value;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (
|
|
386
|
+
indentation.left === undefined &&
|
|
387
|
+
indentation.right === undefined &&
|
|
388
|
+
indentation.firstLine === undefined &&
|
|
389
|
+
indentation.hanging === undefined
|
|
390
|
+
) {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return indentation;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function readParagraphSpacing(node: XmlElementNode): ParagraphSpacing | undefined {
|
|
398
|
+
const spacingNode = findChildElementOptional(node, "spacing");
|
|
399
|
+
if (!spacingNode) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const spacing: ParagraphSpacing = {};
|
|
404
|
+
const before = spacingNode.attributes["w:before"] ?? spacingNode.attributes.before;
|
|
405
|
+
const after = spacingNode.attributes["w:after"] ?? spacingNode.attributes.after;
|
|
406
|
+
const line = spacingNode.attributes["w:line"] ?? spacingNode.attributes.line;
|
|
407
|
+
const lineRule = spacingNode.attributes["w:lineRule"] ?? spacingNode.attributes.lineRule;
|
|
408
|
+
|
|
409
|
+
if (before !== undefined) {
|
|
410
|
+
const value = Number.parseInt(before, 10);
|
|
411
|
+
if (Number.isFinite(value)) spacing.before = value;
|
|
412
|
+
}
|
|
413
|
+
if (after !== undefined) {
|
|
414
|
+
const value = Number.parseInt(after, 10);
|
|
415
|
+
if (Number.isFinite(value)) spacing.after = value;
|
|
416
|
+
}
|
|
417
|
+
if (line !== undefined) {
|
|
418
|
+
const value = Number.parseInt(line, 10);
|
|
419
|
+
if (Number.isFinite(value)) spacing.line = value;
|
|
420
|
+
}
|
|
421
|
+
if (lineRule !== undefined) {
|
|
422
|
+
const normalized = lineRule.toLowerCase();
|
|
423
|
+
if (normalized === "auto" || normalized === "exact") {
|
|
424
|
+
spacing.lineRule = normalized;
|
|
425
|
+
} else if (normalized === "atleast") {
|
|
426
|
+
spacing.lineRule = "atLeast";
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (
|
|
431
|
+
spacing.before === undefined &&
|
|
432
|
+
spacing.after === undefined &&
|
|
433
|
+
spacing.line === undefined &&
|
|
434
|
+
spacing.lineRule === undefined
|
|
435
|
+
) {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return spacing;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function readParagraphTabStops(node: XmlElementNode): TabStop[] | undefined {
|
|
443
|
+
const tabsNode = findChildElementOptional(node, "tabs");
|
|
444
|
+
if (!tabsNode) {
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const tabStops: TabStop[] = [];
|
|
449
|
+
for (const child of tabsNode.children) {
|
|
450
|
+
if (child.type !== "element" || localName(child.name) !== "tab") {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const pos = child.attributes["w:pos"] ?? child.attributes.pos;
|
|
455
|
+
const val = (child.attributes["w:val"] ?? child.attributes.val ?? "left").toLowerCase();
|
|
456
|
+
const leader = (child.attributes["w:leader"] ?? child.attributes.leader ?? "none").toLowerCase();
|
|
457
|
+
if (pos === undefined) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const position = Number.parseInt(pos, 10);
|
|
462
|
+
if (!Number.isFinite(position)) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const align = (["left", "center", "right", "decimal", "num", "bar", "clear"] as const).includes(
|
|
467
|
+
val as TabStop["align"],
|
|
468
|
+
)
|
|
469
|
+
? (val as TabStop["align"])
|
|
470
|
+
: "left";
|
|
471
|
+
const leaderValue =
|
|
472
|
+
leader === "none" ||
|
|
473
|
+
leader === "dot" ||
|
|
474
|
+
leader === "hyphen" ||
|
|
475
|
+
leader === "underscore" ||
|
|
476
|
+
leader === "heavy"
|
|
477
|
+
? (leader as Exclude<TabStop["leader"], "middleDot">)
|
|
478
|
+
: leader === "middledot"
|
|
479
|
+
? "middleDot"
|
|
480
|
+
: undefined;
|
|
481
|
+
|
|
482
|
+
tabStops.push({
|
|
483
|
+
position,
|
|
484
|
+
align,
|
|
485
|
+
...(leaderValue && leaderValue !== "none" ? { leader: leaderValue } : {}),
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return tabStops.length > 0 ? tabStops : undefined;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function readIsLegalNumberingValue(
|
|
493
|
+
isLegalNode: XmlElementNode | undefined,
|
|
494
|
+
): boolean | undefined {
|
|
495
|
+
if (!isLegalNode) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const rawValue = (
|
|
500
|
+
isLegalNode.attributes["w:val"] ??
|
|
501
|
+
isLegalNode.attributes.val ??
|
|
502
|
+
"1"
|
|
503
|
+
).toLowerCase();
|
|
504
|
+
|
|
505
|
+
if (rawValue === "0" || rawValue === "false" || rawValue === "off") {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
|
|
197
512
|
function findChildElement(node: XmlElementNode, childLocalName: string): XmlElementNode {
|
|
198
513
|
const child = findChildElementOptional(node, childLocalName);
|
|
199
514
|
if (!child) {
|
|
@@ -106,11 +106,24 @@ export function parseRevisionsFromDocumentXml(
|
|
|
106
106
|
if (childType !== "p") {
|
|
107
107
|
if (childType === "tbl") {
|
|
108
108
|
parseTblPropertyRevisions(child, cursor, state);
|
|
109
|
+
const nested = parseNestedParagraphContainers(
|
|
110
|
+
child.children,
|
|
111
|
+
paragraphIndex,
|
|
112
|
+
cursor,
|
|
113
|
+
previousWasParagraph,
|
|
114
|
+
state,
|
|
115
|
+
);
|
|
116
|
+
paragraphIndex = nested.paragraphIndex;
|
|
117
|
+
cursor = nested.cursor;
|
|
118
|
+
previousWasParagraph = nested.previousWasParagraph;
|
|
109
119
|
} else if (childType === "sectPr") {
|
|
110
120
|
parseSectPrRevisions(child, cursor, state);
|
|
121
|
+
cursor += 1;
|
|
122
|
+
previousWasParagraph = false;
|
|
123
|
+
} else {
|
|
124
|
+
cursor += 1;
|
|
125
|
+
previousWasParagraph = false;
|
|
111
126
|
}
|
|
112
|
-
cursor += 1;
|
|
113
|
-
previousWasParagraph = false;
|
|
114
127
|
continue;
|
|
115
128
|
}
|
|
116
129
|
|
|
@@ -140,6 +153,106 @@ export function parseRevisionsFromDocumentXml(
|
|
|
140
153
|
};
|
|
141
154
|
}
|
|
142
155
|
|
|
156
|
+
function parseNestedParagraphContainers(
|
|
157
|
+
nodes: XmlNode[],
|
|
158
|
+
paragraphIndex: number,
|
|
159
|
+
cursor: number,
|
|
160
|
+
previousWasParagraph: boolean,
|
|
161
|
+
state: ParseState,
|
|
162
|
+
): {
|
|
163
|
+
paragraphIndex: number;
|
|
164
|
+
cursor: number;
|
|
165
|
+
previousWasParagraph: boolean;
|
|
166
|
+
} {
|
|
167
|
+
let nextParagraphIndex = paragraphIndex;
|
|
168
|
+
let nextCursor = cursor;
|
|
169
|
+
let nextPreviousWasParagraph = previousWasParagraph;
|
|
170
|
+
|
|
171
|
+
for (const node of nodes) {
|
|
172
|
+
if (node.type !== "element") {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const type = localName(node.name);
|
|
177
|
+
if (type === "p") {
|
|
178
|
+
nextParagraphIndex += 1;
|
|
179
|
+
const paragraphBoundary = state.boundaries[nextParagraphIndex];
|
|
180
|
+
if (!paragraphBoundary) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
parseParagraphMarkRevisions(node, paragraphBoundary, state);
|
|
184
|
+
walkParagraphContent(node.children, nextParagraphIndex, state, () => nextCursor, (next) => {
|
|
185
|
+
nextCursor = next;
|
|
186
|
+
});
|
|
187
|
+
nextPreviousWasParagraph = false;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (
|
|
192
|
+
type === "tbl" ||
|
|
193
|
+
type === "tr" ||
|
|
194
|
+
type === "tc" ||
|
|
195
|
+
type === "sdtContent" ||
|
|
196
|
+
type === "customXml" ||
|
|
197
|
+
type === "smartTag"
|
|
198
|
+
) {
|
|
199
|
+
const nested = parseNestedParagraphContainers(
|
|
200
|
+
node.children,
|
|
201
|
+
nextParagraphIndex,
|
|
202
|
+
nextCursor,
|
|
203
|
+
nextPreviousWasParagraph,
|
|
204
|
+
state,
|
|
205
|
+
);
|
|
206
|
+
nextParagraphIndex = nested.paragraphIndex;
|
|
207
|
+
nextCursor = nested.cursor;
|
|
208
|
+
nextPreviousWasParagraph = nested.previousWasParagraph;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (
|
|
213
|
+
type === "tblPr" ||
|
|
214
|
+
type === "tblGrid" ||
|
|
215
|
+
type === "gridCol" ||
|
|
216
|
+
type === "trPr" ||
|
|
217
|
+
type === "tcPr"
|
|
218
|
+
) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
nextCursor += 1;
|
|
223
|
+
nextPreviousWasParagraph = false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
paragraphIndex: nextParagraphIndex,
|
|
228
|
+
cursor: nextCursor,
|
|
229
|
+
previousWasParagraph: nextPreviousWasParagraph,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function parseRevisionsFromStoryXml(
|
|
234
|
+
storyXml: string,
|
|
235
|
+
): ParsedRevisionsResult {
|
|
236
|
+
const xmlDeclMatch = storyXml.match(/^<\?xml[\s\S]*?\?>\s*/u);
|
|
237
|
+
const withoutDecl = xmlDeclMatch ? storyXml.slice(xmlDeclMatch[0].length) : storyXml;
|
|
238
|
+
const rootMatch = withoutDecl.match(/^<([A-Za-z0-9:._-]+)([^>]*)>([\s\S]*)<\/\1>\s*$/u);
|
|
239
|
+
if (!rootMatch) {
|
|
240
|
+
return {
|
|
241
|
+
revisions: [],
|
|
242
|
+
preservedMarkup: [],
|
|
243
|
+
diagnostics: [],
|
|
244
|
+
boundaries: [],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const innerXml = rootMatch[3] ?? "";
|
|
249
|
+
const wrapped =
|
|
250
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
|
|
251
|
+
`<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">` +
|
|
252
|
+
`<w:body>${innerXml}</w:body></w:document>`;
|
|
253
|
+
return parseRevisionsFromDocumentXml(wrapped);
|
|
254
|
+
}
|
|
255
|
+
|
|
143
256
|
function parseParagraphMarkRevisions(
|
|
144
257
|
paragraph: XmlElementNode,
|
|
145
258
|
boundary: RevisionParagraphBoundary,
|
|
@@ -225,6 +338,7 @@ function parseParagraphMarkRevisions(
|
|
|
225
338
|
createdAt: metadata.createdAt,
|
|
226
339
|
metadata: {
|
|
227
340
|
source: "import",
|
|
341
|
+
semanticKind: "structural-change",
|
|
228
342
|
importedRevisionForm:
|
|
229
343
|
type === "ins" ? "paragraph-insertion" : "paragraph-deletion",
|
|
230
344
|
originalRevisionType: `paragraph-${type}`,
|
|
@@ -444,7 +558,7 @@ function walkContentNode(
|
|
|
444
558
|
.filter((child): child is XmlElementNode["children"][number] & { type: "text" } => child.type === "text")
|
|
445
559
|
.map((child) => child.text)
|
|
446
560
|
.join("");
|
|
447
|
-
setCursor(getCursor() + text.length);
|
|
561
|
+
setCursor(getCursor() + Array.from(text).length);
|
|
448
562
|
return;
|
|
449
563
|
}
|
|
450
564
|
case "tab":
|
|
@@ -914,7 +1028,7 @@ function measureStoryLength(node: XmlNode): number {
|
|
|
914
1028
|
case "delInstrText":
|
|
915
1029
|
return node.children
|
|
916
1030
|
.filter((child): child is XmlElementNode["children"][number] & { type: "text" } => child.type === "text")
|
|
917
|
-
.map((child) => child.text.length)
|
|
1031
|
+
.map((child) => Array.from(child.text).length)
|
|
918
1032
|
.reduce((total, length) => total + length, 0);
|
|
919
1033
|
case "tab":
|
|
920
1034
|
case "br":
|