@beyondwork/docx-react-component 1.0.18 → 1.0.19
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/README.md +8 -2
- package/package.json +24 -34
- package/src/api/README.md +5 -1
- package/src/api/public-types.ts +374 -4
- package/src/api/session-state.ts +58 -0
- package/src/core/commands/formatting-commands.ts +1 -0
- package/src/core/commands/image-commands.ts +147 -0
- package/src/core/commands/index.ts +5 -1
- package/src/core/commands/list-commands.ts +231 -36
- package/src/core/commands/paragraph-layout-commands.ts +339 -0
- package/src/core/commands/section-layout-commands.ts +680 -0
- package/src/core/commands/style-commands.ts +262 -0
- package/src/core/search/search-text.ts +329 -0
- package/src/core/selection/mapping.ts +41 -0
- package/src/core/state/editor-state.ts +1 -1
- package/src/index.ts +30 -0
- package/src/io/docx-session.ts +260 -39
- package/src/io/export/serialize-main-document.ts +202 -5
- package/src/io/export/serialize-numbering.ts +28 -7
- package/src/io/normalize/normalize-text.ts +63 -25
- package/src/io/ooxml/numbering-sentinels.ts +44 -0
- package/src/io/ooxml/parse-footnotes.ts +212 -20
- package/src/io/ooxml/parse-headers-footers.ts +229 -25
- package/src/io/ooxml/parse-inline-media.ts +16 -0
- package/src/io/ooxml/parse-main-document.ts +411 -6
- package/src/io/ooxml/parse-numbering.ts +7 -0
- package/src/io/ooxml/parse-settings.ts +184 -0
- package/src/io/ooxml/parse-shapes.ts +25 -0
- package/src/io/ooxml/parse-styles.ts +463 -0
- package/src/io/ooxml/parse-theme.ts +32 -0
- package/src/model/canonical-document.ts +133 -3
- package/src/model/cds-1.0.0.ts +13 -0
- package/src/model/snapshot.ts +2 -1
- package/src/runtime/document-layout.ts +332 -0
- package/src/runtime/document-navigation.ts +564 -0
- package/src/runtime/document-runtime.ts +265 -35
- package/src/runtime/document-search.ts +145 -0
- package/src/runtime/numbering-prefix.ts +47 -26
- package/src/runtime/page-layout-estimation.ts +212 -0
- package/src/runtime/read-only-diagnostics-runtime.ts +1 -0
- package/src/runtime/session-capabilities.ts +2 -0
- package/src/runtime/story-context.ts +164 -0
- package/src/runtime/story-targeting.ts +162 -0
- package/src/runtime/surface-projection.ts +239 -12
- package/src/runtime/table-schema.ts +87 -5
- package/src/runtime/view-state.ts +459 -0
- package/src/ui/WordReviewEditor.tsx +1902 -312
- package/src/ui/browser-export.ts +52 -0
- package/src/ui/headless/preserve-editor-selection.ts +5 -0
- package/src/ui/headless/selection-helpers.ts +20 -0
- package/src/ui/headless/selection-toolbar-model.ts +22 -0
- package/src/ui/headless/use-editor-keyboard.ts +6 -1
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +125 -14
- package/src/ui-tailwind/editor-surface/perf-probe.ts +107 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +45 -6
- package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +2 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +47 -5
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +95 -22
- package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +394 -77
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
- package/src/ui-tailwind/index.ts +2 -1
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
- package/src/ui-tailwind/review/tw-review-rail.tsx +6 -6
- package/src/ui-tailwind/theme/editor-theme.css +123 -0
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +291 -12
- package/src/ui-tailwind/tw-review-workspace.tsx +926 -27
- package/src/validation/compatibility-engine.ts +92 -20
- package/src/validation/diagnostics.ts +1 -0
- package/src/validation/docx-comment-proof.ts +487 -0
package/src/io/docx-session.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CompatibilityReport as PublicCompatibilityReport,
|
|
3
3
|
EditorError,
|
|
4
|
+
EditorSessionState,
|
|
4
5
|
EditorWarning as PublicEditorWarning,
|
|
5
6
|
EditorAnchorProjection as PublicEditorAnchorProjection,
|
|
6
7
|
ExportDocxOptions,
|
|
7
8
|
ExportResult,
|
|
8
9
|
PersistedEditorSnapshot,
|
|
9
10
|
} from "../api/public-types.ts";
|
|
11
|
+
import { editorSessionStateFromPersistedSnapshot } from "../api/session-state.ts";
|
|
10
12
|
import type {
|
|
11
13
|
CanonicalDocumentEnvelope,
|
|
12
14
|
CompatibilityFeatureEntry as InternalCompatibilityFeatureEntry,
|
|
@@ -80,6 +82,7 @@ import type {
|
|
|
80
82
|
import { createReadOnlyDiagnosticsRuntime } from "../runtime/read-only-diagnostics-runtime.ts";
|
|
81
83
|
import {
|
|
82
84
|
WORD_NUMBERING_CONTENT_TYPE,
|
|
85
|
+
hasSerializableNumberingEntries,
|
|
83
86
|
serializeNumberingXml,
|
|
84
87
|
} from "./export/serialize-numbering.ts";
|
|
85
88
|
import {
|
|
@@ -89,6 +92,9 @@ import {
|
|
|
89
92
|
} from "./ooxml/parse-headers-footers.ts";
|
|
90
93
|
import { parseFootnotesXml, parseEndnotesXml } from "./ooxml/parse-footnotes.ts";
|
|
91
94
|
import { parseThemeXml } from "./ooxml/parse-theme.ts";
|
|
95
|
+
import { resolveTheme } from "./ooxml/parse-theme.ts";
|
|
96
|
+
import { parseSettingsXml } from "./ooxml/parse-settings.ts";
|
|
97
|
+
import { parseStylesXml, type ParseStylesResult } from "./ooxml/parse-styles.ts";
|
|
92
98
|
import {
|
|
93
99
|
serializeHeaderXml,
|
|
94
100
|
serializeFooterXml,
|
|
@@ -102,6 +108,11 @@ import {
|
|
|
102
108
|
WORD_ENDNOTES_CONTENT_TYPE,
|
|
103
109
|
} from "./export/serialize-footnotes.ts";
|
|
104
110
|
import { createPersistedSourcePackage } from "./source-package-provenance.ts";
|
|
111
|
+
import { validatePersistedEditorSnapshot } from "../model/snapshot.ts";
|
|
112
|
+
import {
|
|
113
|
+
createSyntheticDocxNullNumberingCatalog,
|
|
114
|
+
DOCX_NULL_NUMBERING_INSTANCE_ID,
|
|
115
|
+
} from "./ooxml/numbering-sentinels.ts";
|
|
105
116
|
|
|
106
117
|
const MAIN_DOCUMENT_PATH = "/word/document.xml";
|
|
107
118
|
const NUMBERING_PART_PATH = "/word/numbering.xml";
|
|
@@ -149,22 +160,29 @@ const FOOTNOTES_RELATIONSHIP_TYPE =
|
|
|
149
160
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
|
|
150
161
|
const ENDNOTES_RELATIONSHIP_TYPE =
|
|
151
162
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes";
|
|
163
|
+
const SETTINGS_RELATIONSHIP_TYPE =
|
|
164
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings";
|
|
165
|
+
const STYLES_RELATIONSHIP_TYPE =
|
|
166
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
|
|
167
|
+
const STYLES_PART_PATH = "/word/styles.xml";
|
|
152
168
|
const FOOTNOTES_PART_PATH = "/word/footnotes.xml";
|
|
153
169
|
const ENDNOTES_PART_PATH = "/word/endnotes.xml";
|
|
170
|
+
const SETTINGS_PART_PATH = "/word/settings.xml";
|
|
154
171
|
|
|
155
172
|
interface LoadDocxEditorSessionOptions {
|
|
156
173
|
documentId: string;
|
|
157
174
|
sourceLabel?: string;
|
|
158
175
|
bytes: Uint8Array | ArrayBuffer;
|
|
159
|
-
editorBuild
|
|
176
|
+
editorBuild?: string;
|
|
160
177
|
}
|
|
161
178
|
|
|
162
179
|
export interface LoadedDocxEditorSession {
|
|
180
|
+
initialSessionState: EditorSessionState;
|
|
163
181
|
initialSnapshot: PersistedEditorSnapshot;
|
|
164
182
|
fatalError?: EditorError;
|
|
165
183
|
readOnly: boolean;
|
|
166
184
|
exportDocx: (
|
|
167
|
-
|
|
185
|
+
sessionState: EditorSessionState | PersistedEditorSnapshot,
|
|
168
186
|
options?: ExportDocxOptions,
|
|
169
187
|
) => Promise<ExportResult>;
|
|
170
188
|
}
|
|
@@ -220,6 +238,10 @@ const BLOCKING_COMMENT_DIAGNOSTIC_CODES = new Set<CommentImportDiagnostic["code"
|
|
|
220
238
|
export function loadDocxEditorSession(
|
|
221
239
|
options: LoadDocxEditorSessionOptions,
|
|
222
240
|
): LoadedDocxEditorSession {
|
|
241
|
+
const editorBuild =
|
|
242
|
+
typeof options.editorBuild === "string" && options.editorBuild.length > 0
|
|
243
|
+
? options.editorBuild
|
|
244
|
+
: "dev";
|
|
223
245
|
const sourceBytes = toUint8Array(options.bytes);
|
|
224
246
|
let sourcePackage: OpcPackage;
|
|
225
247
|
|
|
@@ -404,6 +426,7 @@ export function loadDocxEditorSession(
|
|
|
404
426
|
variant: ref.variant,
|
|
405
427
|
partPath,
|
|
406
428
|
relationshipId: ref.relationshipId,
|
|
429
|
+
...(ref.sectionIndex !== undefined ? { sectionIndex: ref.sectionIndex } : {}),
|
|
407
430
|
blocks: parsed.blocks,
|
|
408
431
|
});
|
|
409
432
|
sourceHeaderPaths.push({ partPath, relationshipId: ref.relationshipId });
|
|
@@ -413,6 +436,7 @@ export function loadDocxEditorSession(
|
|
|
413
436
|
variant: ref.variant,
|
|
414
437
|
partPath,
|
|
415
438
|
relationshipId: ref.relationshipId,
|
|
439
|
+
...(ref.sectionIndex !== undefined ? { sectionIndex: ref.sectionIndex } : {}),
|
|
416
440
|
blocks: parsed.blocks,
|
|
417
441
|
});
|
|
418
442
|
sourceFooterPaths.push({ partPath, relationshipId: ref.relationshipId });
|
|
@@ -466,17 +490,54 @@ export function loadDocxEditorSession(
|
|
|
466
490
|
decodeUtf8(sourcePackage.parts.get(themePartPath)?.bytes ?? new Uint8Array()),
|
|
467
491
|
)
|
|
468
492
|
: undefined;
|
|
493
|
+
const resolvedTheme = parsedTheme ? resolveTheme(parsedTheme) : undefined;
|
|
494
|
+
const settingsPartPath = resolveDocumentRelatedPartPath(
|
|
495
|
+
sourcePackage,
|
|
496
|
+
mainDocumentPath,
|
|
497
|
+
documentPart.relationships,
|
|
498
|
+
SETTINGS_RELATIONSHIP_TYPE,
|
|
499
|
+
SETTINGS_PART_PATH,
|
|
500
|
+
);
|
|
501
|
+
const parsedSettings =
|
|
502
|
+
settingsPartPath && sourcePackage.parts.has(settingsPartPath)
|
|
503
|
+
? parseSettingsXml(
|
|
504
|
+
decodeUtf8(sourcePackage.parts.get(settingsPartPath)?.bytes ?? new Uint8Array()),
|
|
505
|
+
)
|
|
506
|
+
: undefined;
|
|
507
|
+
|
|
508
|
+
// ---- Parse styles.xml for canonical style catalog ----
|
|
509
|
+
const stylesPartPath = resolveDocumentRelatedPartPath(
|
|
510
|
+
sourcePackage,
|
|
511
|
+
mainDocumentPath,
|
|
512
|
+
documentPart.relationships,
|
|
513
|
+
STYLES_RELATIONSHIP_TYPE,
|
|
514
|
+
STYLES_PART_PATH,
|
|
515
|
+
);
|
|
516
|
+
const parsedStyles =
|
|
517
|
+
stylesPartPath && sourcePackage.parts.has(stylesPartPath)
|
|
518
|
+
? parseStylesXml(
|
|
519
|
+
decodeUtf8(sourcePackage.parts.get(stylesPartPath)?.bytes ?? new Uint8Array()),
|
|
520
|
+
)
|
|
521
|
+
: parseStylesXml("");
|
|
469
522
|
|
|
470
523
|
const subParts: SubPartsCatalog | undefined =
|
|
471
524
|
parsedHeaders.length > 0 ||
|
|
472
525
|
parsedFooters.length > 0 ||
|
|
473
526
|
footnoteCollection !== undefined ||
|
|
474
|
-
parsedTheme !== undefined
|
|
527
|
+
parsedTheme !== undefined ||
|
|
528
|
+
normalizedDocument.finalSectionProperties !== undefined ||
|
|
529
|
+
resolvedTheme !== undefined ||
|
|
530
|
+
parsedSettings !== undefined
|
|
475
531
|
? {
|
|
476
532
|
headers: parsedHeaders,
|
|
477
533
|
footers: parsedFooters,
|
|
478
534
|
...(footnoteCollection !== undefined ? { footnoteCollection } : {}),
|
|
479
535
|
...(parsedTheme !== undefined ? { theme: parsedTheme } : {}),
|
|
536
|
+
...(normalizedDocument.finalSectionProperties !== undefined
|
|
537
|
+
? { finalSectionProperties: normalizedDocument.finalSectionProperties }
|
|
538
|
+
: {}),
|
|
539
|
+
...(resolvedTheme !== undefined ? { resolvedTheme } : {}),
|
|
540
|
+
...(parsedSettings !== undefined ? { settings: parsedSettings } : {}),
|
|
480
541
|
}
|
|
481
542
|
: undefined;
|
|
482
543
|
|
|
@@ -488,6 +549,7 @@ export function loadDocxEditorSession(
|
|
|
488
549
|
media: normalizedDocument.media,
|
|
489
550
|
content: normalizedDocument.content,
|
|
490
551
|
subParts,
|
|
552
|
+
parsedStyles,
|
|
491
553
|
preservation: {
|
|
492
554
|
...normalizedDocument.preservation,
|
|
493
555
|
packageParts: {
|
|
@@ -532,12 +594,28 @@ export function loadDocxEditorSession(
|
|
|
532
594
|
});
|
|
533
595
|
const snapshot = createImportedSnapshot({
|
|
534
596
|
documentId: options.documentId,
|
|
535
|
-
editorBuild
|
|
597
|
+
editorBuild,
|
|
536
598
|
timestamp,
|
|
537
599
|
document,
|
|
538
600
|
compatibility: toPublicCompatibilityReport(compatibility),
|
|
539
601
|
sourcePackage: createPersistedSourcePackage(sourceBytes, options.sourceLabel),
|
|
540
602
|
});
|
|
603
|
+
const snapshotIssues = validatePersistedEditorSnapshot(snapshot);
|
|
604
|
+
if (snapshotIssues.length > 0) {
|
|
605
|
+
const firstIssue = snapshotIssues[0];
|
|
606
|
+
return createDiagnosticsSession(
|
|
607
|
+
options,
|
|
608
|
+
createValidationImportDiagnostics({
|
|
609
|
+
message: `DOCX import produced an invalid editor state during validation${firstIssue ? ` (${firstIssue.path}: ${firstIssue.message})` : "."}`,
|
|
610
|
+
source: "import",
|
|
611
|
+
details: {
|
|
612
|
+
issueCount: snapshotIssues.length,
|
|
613
|
+
firstIssuePath: firstIssue?.path,
|
|
614
|
+
},
|
|
615
|
+
}),
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const initialSessionState = editorSessionStateFromPersistedSnapshot(snapshot);
|
|
541
619
|
const importedState: ImportedDocxState = {
|
|
542
620
|
sourceBytes: new Uint8Array(sourceBytes),
|
|
543
621
|
sourcePackage,
|
|
@@ -597,10 +675,11 @@ export function loadDocxEditorSession(
|
|
|
597
675
|
};
|
|
598
676
|
|
|
599
677
|
return {
|
|
678
|
+
initialSessionState,
|
|
600
679
|
initialSnapshot: snapshot,
|
|
601
680
|
readOnly: false,
|
|
602
|
-
exportDocx: async (
|
|
603
|
-
exportDocxEditorSession(importedState,
|
|
681
|
+
exportDocx: async (nextSessionState, exportOptions) =>
|
|
682
|
+
exportDocxEditorSession(importedState, nextSessionState, exportOptions),
|
|
604
683
|
};
|
|
605
684
|
} catch (error) {
|
|
606
685
|
return createDiagnosticsSession(
|
|
@@ -612,14 +691,16 @@ export function loadDocxEditorSession(
|
|
|
612
691
|
|
|
613
692
|
function exportDocxEditorSession(
|
|
614
693
|
state: ImportedDocxState,
|
|
615
|
-
|
|
694
|
+
sessionStateOrSnapshot: EditorSessionState | PersistedEditorSnapshot,
|
|
616
695
|
options?: ExportDocxOptions,
|
|
617
696
|
): ExportResult {
|
|
618
|
-
|
|
697
|
+
const sessionState = toEditorSessionState(sessionStateOrSnapshot);
|
|
698
|
+
|
|
699
|
+
if (sessionState.compatibility.blockExport) {
|
|
619
700
|
throw new Error("DOCX export is blocked by the current compatibility report.");
|
|
620
701
|
}
|
|
621
702
|
|
|
622
|
-
const currentDocument =
|
|
703
|
+
const currentDocument = sessionState.canonicalDocument as CanonicalDocumentEnvelope;
|
|
623
704
|
if (
|
|
624
705
|
serializeCanonicalDocumentForExport(currentDocument) ===
|
|
625
706
|
state.initialCanonicalSignature &&
|
|
@@ -628,7 +709,10 @@ function exportDocxEditorSession(
|
|
|
628
709
|
return {
|
|
629
710
|
bytes: new Uint8Array(state.sourceBytes),
|
|
630
711
|
mimeType: DOCX_MIME_TYPE,
|
|
631
|
-
fileName: options?.fileName ?? `${
|
|
712
|
+
fileName: options?.fileName ?? `${sessionState.documentId}.docx`,
|
|
713
|
+
delivery: {
|
|
714
|
+
mode: "exported-bytes-only",
|
|
715
|
+
},
|
|
632
716
|
};
|
|
633
717
|
}
|
|
634
718
|
if (state.blockingCommentDiagnostics.length > 0) {
|
|
@@ -660,6 +744,7 @@ function exportDocxEditorSession(
|
|
|
660
744
|
{
|
|
661
745
|
documentAttributes: state.sourceDocumentAttributes,
|
|
662
746
|
media: currentDocument.media as MediaCatalog,
|
|
747
|
+
finalSectionProperties: currentDocument.subParts?.finalSectionProperties,
|
|
663
748
|
},
|
|
664
749
|
);
|
|
665
750
|
const revisionDocument = serializeRuntimeRevisionsIntoDocumentXml(
|
|
@@ -717,7 +802,9 @@ function exportDocxEditorSession(
|
|
|
717
802
|
state.sourcePeoplePartPath ?? PEOPLE_PART_PATH;
|
|
718
803
|
const numberingPartPath =
|
|
719
804
|
state.sourceNumberingPartPath ?? NUMBERING_PART_PATH;
|
|
720
|
-
const serializedNumberingXml =
|
|
805
|
+
const serializedNumberingXml = hasSerializableNumberingEntries(
|
|
806
|
+
currentDocument.numbering as NumberingCatalog,
|
|
807
|
+
)
|
|
721
808
|
? serializeNumberingXml(currentDocument.numbering as NumberingCatalog)
|
|
722
809
|
: undefined;
|
|
723
810
|
const nextRelationships = withDocumentRelatedParts(
|
|
@@ -923,10 +1010,21 @@ function exportDocxEditorSession(
|
|
|
923
1010
|
return {
|
|
924
1011
|
bytes: exportSession.serialize(),
|
|
925
1012
|
mimeType: DOCX_MIME_TYPE,
|
|
926
|
-
fileName: options?.fileName ?? `${
|
|
1013
|
+
fileName: options?.fileName ?? `${sessionState.documentId}.docx`,
|
|
1014
|
+
delivery: {
|
|
1015
|
+
mode: "exported-bytes-only",
|
|
1016
|
+
},
|
|
927
1017
|
};
|
|
928
1018
|
}
|
|
929
1019
|
|
|
1020
|
+
function toEditorSessionState(
|
|
1021
|
+
sessionStateOrSnapshot: EditorSessionState | PersistedEditorSnapshot,
|
|
1022
|
+
): EditorSessionState {
|
|
1023
|
+
return "sessionVersion" in sessionStateOrSnapshot
|
|
1024
|
+
? sessionStateOrSnapshot
|
|
1025
|
+
: editorSessionStateFromPersistedSnapshot(sessionStateOrSnapshot);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
930
1028
|
function createImportedCanonicalDocument(input: {
|
|
931
1029
|
documentId: string;
|
|
932
1030
|
timestamp: string;
|
|
@@ -934,23 +1032,21 @@ function createImportedCanonicalDocument(input: {
|
|
|
934
1032
|
media: CanonicalDocumentEnvelope["media"];
|
|
935
1033
|
content: CanonicalDocumentEnvelope["content"];
|
|
936
1034
|
subParts?: SubPartsCatalog;
|
|
1035
|
+
parsedStyles?: ParseStylesResult;
|
|
937
1036
|
preservation: CanonicalDocumentEnvelope["preservation"];
|
|
938
1037
|
diagnostics: CanonicalDocumentEnvelope["diagnostics"];
|
|
939
1038
|
review: CanonicalDocumentEnvelope["review"];
|
|
940
1039
|
}): CanonicalDocumentEnvelope {
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
.map((styleId) => [
|
|
945
|
-
styleId,
|
|
946
|
-
{
|
|
947
|
-
styleId,
|
|
948
|
-
displayName: styleId,
|
|
949
|
-
kind: "paragraph" as const,
|
|
950
|
-
isDefault: styleId === "Normal",
|
|
951
|
-
},
|
|
952
|
-
]),
|
|
1040
|
+
const numbering = ensureImportedNumberingCatalogSupportsContent(
|
|
1041
|
+
input.numbering,
|
|
1042
|
+
input.content,
|
|
953
1043
|
);
|
|
1044
|
+
|
|
1045
|
+
// Use package-backed style catalog when available; fall back to synthetic
|
|
1046
|
+
// styles derived from referenced styleId values when styles.xml is missing
|
|
1047
|
+
// or could not be parsed.
|
|
1048
|
+
const styles = buildStylesCatalog(input.parsedStyles, input.content, input.subParts);
|
|
1049
|
+
|
|
954
1050
|
return {
|
|
955
1051
|
schemaVersion: "cds/1.0.0",
|
|
956
1052
|
docId: createCanonicalDocumentId(input.documentId),
|
|
@@ -959,12 +1055,8 @@ function createImportedCanonicalDocument(input: {
|
|
|
959
1055
|
metadata: {
|
|
960
1056
|
customProperties: {},
|
|
961
1057
|
},
|
|
962
|
-
styles
|
|
963
|
-
|
|
964
|
-
characters: {},
|
|
965
|
-
tables: {},
|
|
966
|
-
},
|
|
967
|
-
numbering: input.numbering,
|
|
1058
|
+
styles,
|
|
1059
|
+
numbering,
|
|
968
1060
|
media: input.media,
|
|
969
1061
|
content: input.content,
|
|
970
1062
|
review: input.review,
|
|
@@ -974,6 +1066,136 @@ function createImportedCanonicalDocument(input: {
|
|
|
974
1066
|
};
|
|
975
1067
|
}
|
|
976
1068
|
|
|
1069
|
+
// Canonical model styleId validation pattern — styleIds that don't match
|
|
1070
|
+
// are excluded from the catalog to avoid snapshot validation failures.
|
|
1071
|
+
const VALID_STYLE_ID = /^[A-Za-z_][A-Za-z0-9._-]{0,127}$/;
|
|
1072
|
+
|
|
1073
|
+
function buildStylesCatalog(
|
|
1074
|
+
parsedStyles: ParseStylesResult | undefined,
|
|
1075
|
+
content: CanonicalDocumentEnvelope["content"],
|
|
1076
|
+
subParts?: SubPartsCatalog,
|
|
1077
|
+
): CanonicalDocumentEnvelope["styles"] {
|
|
1078
|
+
if (parsedStyles?.fromPackage) {
|
|
1079
|
+
// Package-backed catalog: filter entries whose styleId does not satisfy
|
|
1080
|
+
// the canonical model pattern (e.g. numeric-only ids from Word).
|
|
1081
|
+
const catalog = filterValidStyleIds(parsedStyles.catalog);
|
|
1082
|
+
|
|
1083
|
+
// Merge in any referenced styleIds that the package styles.xml did not
|
|
1084
|
+
// define (rare but defensive).
|
|
1085
|
+
const referencedIds = collectReferencedParagraphStyleIds(content, subParts);
|
|
1086
|
+
for (const styleId of referencedIds) {
|
|
1087
|
+
if (!catalog.paragraphs[styleId] && VALID_STYLE_ID.test(styleId)) {
|
|
1088
|
+
catalog.paragraphs[styleId] = {
|
|
1089
|
+
styleId,
|
|
1090
|
+
displayName: styleId,
|
|
1091
|
+
kind: "paragraph",
|
|
1092
|
+
isDefault: styleId === "Normal",
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return {
|
|
1097
|
+
...catalog,
|
|
1098
|
+
fromPackage: true,
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Synthetic fallback: no styles.xml available
|
|
1103
|
+
const paragraphStyles = Object.fromEntries(
|
|
1104
|
+
[...collectReferencedParagraphStyleIds(content, subParts)]
|
|
1105
|
+
.sort((left, right) => left.localeCompare(right))
|
|
1106
|
+
.filter((styleId) => VALID_STYLE_ID.test(styleId))
|
|
1107
|
+
.map((styleId) => [
|
|
1108
|
+
styleId,
|
|
1109
|
+
{
|
|
1110
|
+
styleId,
|
|
1111
|
+
displayName: styleId,
|
|
1112
|
+
kind: "paragraph" as const,
|
|
1113
|
+
isDefault: styleId === "Normal",
|
|
1114
|
+
},
|
|
1115
|
+
]),
|
|
1116
|
+
);
|
|
1117
|
+
return {
|
|
1118
|
+
paragraphs: paragraphStyles,
|
|
1119
|
+
characters: {},
|
|
1120
|
+
tables: {},
|
|
1121
|
+
fromPackage: false,
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function filterValidStyleIds(
|
|
1126
|
+
catalog: CanonicalDocumentEnvelope["styles"],
|
|
1127
|
+
): CanonicalDocumentEnvelope["styles"] {
|
|
1128
|
+
const filterRecord = <T extends { styleId: string }>(
|
|
1129
|
+
record: Record<string, T>,
|
|
1130
|
+
): Record<string, T> => {
|
|
1131
|
+
const result: Record<string, T> = {};
|
|
1132
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1133
|
+
if (VALID_STYLE_ID.test(key)) {
|
|
1134
|
+
result[key] = value;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return result;
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
return {
|
|
1141
|
+
paragraphs: filterRecord(catalog.paragraphs),
|
|
1142
|
+
characters: filterRecord(catalog.characters),
|
|
1143
|
+
tables: filterRecord(catalog.tables),
|
|
1144
|
+
...(catalog.latentStyles ? { latentStyles: catalog.latentStyles } : {}),
|
|
1145
|
+
...(catalog.fromPackage !== undefined ? { fromPackage: catalog.fromPackage } : {}),
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function ensureImportedNumberingCatalogSupportsContent(
|
|
1150
|
+
catalog: NumberingCatalog,
|
|
1151
|
+
content: CanonicalDocumentEnvelope["content"],
|
|
1152
|
+
): NumberingCatalog {
|
|
1153
|
+
if (
|
|
1154
|
+
catalog.instances[DOCX_NULL_NUMBERING_INSTANCE_ID] ||
|
|
1155
|
+
!collectReferencedNumberingInstanceIds(content).has(DOCX_NULL_NUMBERING_INSTANCE_ID)
|
|
1156
|
+
) {
|
|
1157
|
+
return catalog;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const syntheticNullCatalog = createSyntheticDocxNullNumberingCatalog();
|
|
1161
|
+
return {
|
|
1162
|
+
abstractDefinitions: {
|
|
1163
|
+
...catalog.abstractDefinitions,
|
|
1164
|
+
...syntheticNullCatalog.abstractDefinitions,
|
|
1165
|
+
},
|
|
1166
|
+
instances: {
|
|
1167
|
+
...catalog.instances,
|
|
1168
|
+
...syntheticNullCatalog.instances,
|
|
1169
|
+
},
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function collectReferencedNumberingInstanceIds(
|
|
1174
|
+
content: CanonicalDocumentEnvelope["content"],
|
|
1175
|
+
): Set<string> {
|
|
1176
|
+
const numberingInstanceIds = new Set<string>();
|
|
1177
|
+
|
|
1178
|
+
const visitBlocks = (blocks: ReadonlyArray<BlockNode>) => {
|
|
1179
|
+
for (const block of blocks) {
|
|
1180
|
+
if (block.type === "paragraph" && block.numbering?.numberingInstanceId) {
|
|
1181
|
+
numberingInstanceIds.add(block.numbering.numberingInstanceId);
|
|
1182
|
+
}
|
|
1183
|
+
if (block.type === "table") {
|
|
1184
|
+
for (const row of block.rows) {
|
|
1185
|
+
for (const cell of row.cells) {
|
|
1186
|
+
visitBlocks(cell.children);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
} else if (block.type === "sdt" || block.type === "custom_xml") {
|
|
1190
|
+
visitBlocks(block.children);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
|
+
visitBlocks(content.children);
|
|
1196
|
+
return numberingInstanceIds;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
977
1199
|
function collectReferencedParagraphStyleIds(
|
|
978
1200
|
content: CanonicalDocumentEnvelope["content"],
|
|
979
1201
|
subParts?: SubPartsCatalog,
|
|
@@ -1110,20 +1332,26 @@ function createDiagnosticsSession(
|
|
|
1110
1332
|
diagnostics: ImportDiagnosticsResult,
|
|
1111
1333
|
): LoadedDocxEditorSession {
|
|
1112
1334
|
const timestamp = new Date().toISOString();
|
|
1335
|
+
const editorBuild =
|
|
1336
|
+
typeof options.editorBuild === "string" && options.editorBuild.length > 0
|
|
1337
|
+
? options.editorBuild
|
|
1338
|
+
: "dev";
|
|
1113
1339
|
const runtime = createReadOnlyDiagnosticsRuntime({
|
|
1114
1340
|
documentId: options.documentId,
|
|
1115
1341
|
sourceLabel: options.sourceLabel,
|
|
1116
|
-
editorBuild
|
|
1342
|
+
editorBuild,
|
|
1117
1343
|
generatedAt: timestamp,
|
|
1118
1344
|
diagnostics,
|
|
1119
1345
|
});
|
|
1120
1346
|
const initialSnapshot = runtime.getPersistedSnapshot();
|
|
1347
|
+
const initialSessionState = editorSessionStateFromPersistedSnapshot(initialSnapshot);
|
|
1121
1348
|
|
|
1122
1349
|
return {
|
|
1350
|
+
initialSessionState,
|
|
1123
1351
|
initialSnapshot,
|
|
1124
1352
|
fatalError: diagnostics.fatalError,
|
|
1125
1353
|
readOnly: true,
|
|
1126
|
-
exportDocx: async (
|
|
1354
|
+
exportDocx: async (_sessionState, exportOptions) => runtime.exportDocx(exportOptions),
|
|
1127
1355
|
};
|
|
1128
1356
|
}
|
|
1129
1357
|
|
|
@@ -1670,13 +1898,6 @@ function createEmptyNumberingCatalog(): NumberingCatalog {
|
|
|
1670
1898
|
};
|
|
1671
1899
|
}
|
|
1672
1900
|
|
|
1673
|
-
function hasNumberingEntries(catalog: NumberingCatalog): boolean {
|
|
1674
|
-
return (
|
|
1675
|
-
Object.keys(catalog.abstractDefinitions ?? {}).length > 0 ||
|
|
1676
|
-
Object.keys(catalog.instances ?? {}).length > 0
|
|
1677
|
-
);
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
1901
|
function collectBrokenInternalRelationshipIssues(
|
|
1681
1902
|
sourcePackage: OpcPackage,
|
|
1682
1903
|
mainDocumentPath?: string,
|