@beyondwork/docx-react-component 1.0.17 → 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.
Files changed (74) hide show
  1. package/README.md +8 -2
  2. package/package.json +32 -34
  3. package/src/api/README.md +5 -1
  4. package/src/api/public-types.ts +374 -4
  5. package/src/api/session-state.ts +58 -0
  6. package/src/core/commands/formatting-commands.ts +1 -0
  7. package/src/core/commands/image-commands.ts +147 -0
  8. package/src/core/commands/index.ts +5 -1
  9. package/src/core/commands/list-commands.ts +231 -36
  10. package/src/core/commands/paragraph-layout-commands.ts +339 -0
  11. package/src/core/commands/section-layout-commands.ts +680 -0
  12. package/src/core/commands/style-commands.ts +262 -0
  13. package/src/core/search/search-text.ts +329 -0
  14. package/src/core/selection/mapping.ts +41 -0
  15. package/src/core/state/editor-state.ts +1 -1
  16. package/src/index.ts +30 -0
  17. package/src/io/docx-session.ts +260 -39
  18. package/src/io/export/serialize-main-document.ts +202 -5
  19. package/src/io/export/serialize-numbering.ts +28 -7
  20. package/src/io/normalize/normalize-text.ts +63 -25
  21. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  22. package/src/io/ooxml/parse-footnotes.ts +212 -20
  23. package/src/io/ooxml/parse-headers-footers.ts +229 -25
  24. package/src/io/ooxml/parse-inline-media.ts +16 -0
  25. package/src/io/ooxml/parse-main-document.ts +411 -6
  26. package/src/io/ooxml/parse-numbering.ts +7 -0
  27. package/src/io/ooxml/parse-settings.ts +184 -0
  28. package/src/io/ooxml/parse-shapes.ts +25 -0
  29. package/src/io/ooxml/parse-styles.ts +463 -0
  30. package/src/io/ooxml/parse-theme.ts +32 -0
  31. package/src/model/canonical-document.ts +133 -3
  32. package/src/model/cds-1.0.0.ts +13 -0
  33. package/src/model/snapshot.ts +2 -1
  34. package/src/runtime/document-layout.ts +332 -0
  35. package/src/runtime/document-navigation.ts +564 -0
  36. package/src/runtime/document-runtime.ts +265 -35
  37. package/src/runtime/document-search.ts +145 -0
  38. package/src/runtime/numbering-prefix.ts +47 -26
  39. package/src/runtime/page-layout-estimation.ts +212 -0
  40. package/src/runtime/read-only-diagnostics-runtime.ts +1 -0
  41. package/src/runtime/session-capabilities.ts +2 -0
  42. package/src/runtime/story-context.ts +164 -0
  43. package/src/runtime/story-targeting.ts +162 -0
  44. package/src/runtime/surface-projection.ts +239 -12
  45. package/src/runtime/table-schema.ts +87 -5
  46. package/src/runtime/view-state.ts +459 -0
  47. package/src/ui/WordReviewEditor.tsx +1902 -312
  48. package/src/ui/browser-export.ts +52 -0
  49. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  50. package/src/ui/headless/selection-helpers.ts +20 -0
  51. package/src/ui/headless/selection-toolbar-model.ts +22 -0
  52. package/src/ui/headless/use-editor-keyboard.ts +6 -1
  53. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
  54. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +125 -14
  55. package/src/ui-tailwind/editor-surface/perf-probe.ts +107 -0
  56. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +45 -6
  57. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  58. package/src/ui-tailwind/editor-surface/pm-position-map.ts +2 -2
  59. package/src/ui-tailwind/editor-surface/pm-schema.ts +47 -5
  60. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +95 -22
  61. package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
  62. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
  63. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +394 -77
  64. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
  65. package/src/ui-tailwind/index.ts +2 -1
  66. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
  67. package/src/ui-tailwind/review/tw-review-rail.tsx +6 -6
  68. package/src/ui-tailwind/theme/editor-theme.css +123 -0
  69. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
  70. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +291 -12
  71. package/src/ui-tailwind/tw-review-workspace.tsx +926 -27
  72. package/src/validation/compatibility-engine.ts +92 -20
  73. package/src/validation/diagnostics.ts +1 -0
  74. package/src/validation/docx-comment-proof.ts +487 -0
@@ -6,12 +6,25 @@ export type ExternalDocumentSource =
6
6
  bytes: Uint8Array | ArrayBuffer;
7
7
  sourceLabel?: string;
8
8
  }
9
+ | {
10
+ kind: "session";
11
+ sessionState: EditorSessionState;
12
+ sourceLabel?: string;
13
+ }
9
14
  | {
10
15
  kind: "snapshot";
11
16
  snapshot: PersistedEditorSnapshot;
12
17
  sourceLabel?: string;
13
18
  };
14
19
 
20
+ export type LoadSourcePolicy = "prefer-saved-state" | "prefer-source-package";
21
+
22
+ export interface LoadRequest {
23
+ documentId: string;
24
+ loadRevision?: string;
25
+ loadSourcePolicy?: LoadSourcePolicy;
26
+ }
27
+
15
28
  export interface EditorUser {
16
29
  userId: string;
17
30
  displayName: string;
@@ -43,11 +56,129 @@ export type EditorAnchorProjection =
43
56
  reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity";
44
57
  };
45
58
 
59
+ export type EditorStoryTarget =
60
+ | { kind: "main" }
61
+ | {
62
+ kind: "header";
63
+ relationshipId: string;
64
+ variant: "default" | "first" | "even";
65
+ sectionIndex?: number;
66
+ }
67
+ | {
68
+ kind: "footer";
69
+ relationshipId: string;
70
+ variant: "default" | "first" | "even";
71
+ sectionIndex?: number;
72
+ }
73
+ | { kind: "footnote"; noteId: string }
74
+ | { kind: "endnote"; noteId: string };
75
+
46
76
  export interface SelectionSnapshot {
47
77
  anchor: number;
48
78
  head: number;
49
79
  isCollapsed: boolean;
50
80
  activeRange: EditorAnchorProjection;
81
+ storyTarget?: EditorStoryTarget;
82
+ }
83
+
84
+ export interface PageLayoutSnapshot {
85
+ sectionIndex: number;
86
+ sectionType?: "continuous" | "nextPage" | "evenPage" | "oddPage" | "nextColumn";
87
+ pageWidth: number;
88
+ pageHeight: number;
89
+ marginTop: number;
90
+ marginBottom: number;
91
+ marginLeft: number;
92
+ marginRight: number;
93
+ headerMargin: number;
94
+ footerMargin: number;
95
+ gutter: number;
96
+ orientation: "portrait" | "landscape";
97
+ columns: number;
98
+ differentFirstPage: boolean;
99
+ differentOddEvenPages: boolean;
100
+ pageNumbering?: {
101
+ format?: string;
102
+ start?: number;
103
+ chapterStyle?: string;
104
+ chapterSeparator?: string;
105
+ };
106
+ lineNumbering?: {
107
+ countBy?: number;
108
+ start?: number;
109
+ distance?: number;
110
+ restart?: "newPage" | "newSection" | "continuous";
111
+ };
112
+ pageBorders?: {
113
+ top?: { value?: string; size?: number; space?: number; color?: string };
114
+ left?: { value?: string; size?: number; space?: number; color?: string };
115
+ bottom?: { value?: string; size?: number; space?: number; color?: string };
116
+ right?: { value?: string; size?: number; space?: number; color?: string };
117
+ offsetFrom?: "page" | "text";
118
+ display?: "allPages" | "firstPage" | "notFirstPage";
119
+ zOrder?: "front" | "back";
120
+ };
121
+ documentGrid?: {
122
+ type?: "default" | "lines" | "linesAndChars" | "snapToChars";
123
+ linePitch?: number;
124
+ charSpace?: number;
125
+ };
126
+ columnDefinitions: Array<{ width: number; space?: number }>;
127
+ equalWidthColumns: boolean;
128
+ columnSeparator: boolean;
129
+ headerVariants: Array<{ variant: "default" | "first" | "even"; relationshipId: string }>;
130
+ footerVariants: Array<{ variant: "default" | "first" | "even"; relationshipId: string }>;
131
+ }
132
+
133
+ export type WorkspaceMode = "canvas" | "page";
134
+
135
+ export type ZoomLevel = "pageWidth" | "onePage" | number;
136
+
137
+ export type SectionBreakType =
138
+ | "nextPage"
139
+ | "continuous"
140
+ | "evenPage"
141
+ | "oddPage"
142
+ | "nextColumn";
143
+
144
+ export interface SectionLayoutPatch {
145
+ pageSize?: Partial<{
146
+ width: number;
147
+ height: number;
148
+ orientation: "portrait" | "landscape";
149
+ }>;
150
+ pageMargins?: Partial<{
151
+ top: number;
152
+ right: number;
153
+ bottom: number;
154
+ left: number;
155
+ header: number;
156
+ footer: number;
157
+ gutter: number;
158
+ }>;
159
+ columns?: Partial<{
160
+ count: number;
161
+ space: number;
162
+ equalWidth: boolean;
163
+ columns: Array<{ width: number; space?: number }>;
164
+ separator: boolean;
165
+ }>;
166
+ titlePage?: boolean;
167
+ sectionType?: SectionBreakType;
168
+ }
169
+
170
+ export interface SectionPageNumberingPatch {
171
+ format?: string | null;
172
+ start?: number | null;
173
+ chapterStyle?: string | null;
174
+ chapterSeparator?: string | null;
175
+ }
176
+
177
+ export interface HeaderFooterLinkPatch {
178
+ kind: "header" | "footer";
179
+ variant: "default" | "first" | "even";
180
+ linkToPrevious: boolean;
181
+ relationshipId?: string | null;
51
182
  }
52
183
 
53
184
  export interface DocumentStats {
@@ -144,6 +275,7 @@ export interface FormattingStateSnapshot {
144
275
  textColor?: string;
145
276
  highlightColor?: string | null;
146
277
  alignment?: FormattingAlignment;
278
+ paragraphStyleId?: string;
147
279
  breadcrumb: FormattingBreadcrumbItem[];
148
280
  }
149
281
 
@@ -158,6 +290,79 @@ export interface SearchResultSnapshot {
158
290
  anchor: EditorAnchorProjection;
159
291
  excerpt: string;
160
292
  isActive: boolean;
293
+ storyTarget?: EditorStoryTarget;
294
+ sectionIndex?: number;
295
+ pageIndex?: number;
296
+ }
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // Document Navigation Model (Wave 37)
300
+ // ---------------------------------------------------------------------------
301
+
302
+ /**
303
+ * Runtime-owned snapshot of the document's page stack, heading outline, and
304
+ * navigation context. Derived from canonical document state and section
305
+ * properties — never from DOM scroll position or CSS layout.
306
+ */
307
+ export interface DocumentNavigationSnapshot {
308
+ /** Total estimated page count across all sections. */
309
+ pageCount: number;
310
+ /** Ordered page descriptors, one per logical page. */
311
+ pages: DocumentPageSnapshot[];
312
+ /** Ordered heading outline extracted from paragraph styles. */
313
+ headings: DocumentHeadingSnapshot[];
314
+ /** Index of the page containing the current selection head. */
315
+ activePageIndex: number;
316
+ /** Section index containing the current selection head. */
317
+ activeSectionIndex: number;
318
+ }
319
+
320
+ /** Runtime-derived descriptor for a single logical page. */
321
+ export interface DocumentPageSnapshot {
322
+ /** Zero-based page index within the whole document. */
323
+ pageIndex: number;
324
+ /** Section that owns this page (zero-based). */
325
+ sectionIndex: number;
326
+ /** Zero-based page index within its owning section. */
327
+ pageInSection: number;
328
+ /** Estimated character offset where the page begins in the main story. */
329
+ startOffset: number;
330
+ /** Estimated character offset where the page ends in the main story. */
331
+ endOffset: number;
332
+ /** Page layout properties inherited from the section. */
333
+ layout: PageLayoutSnapshot;
334
+ }
335
+
336
+ /** A heading entry in the document outline. */
337
+ export interface DocumentHeadingSnapshot {
338
+ /** Stable identifier for the heading (derived from block position). */
339
+ headingId: string;
340
+ /** Outline level (1 = Heading 1, 2 = Heading 2, etc.). */
341
+ level: number;
342
+ /** Plain-text content of the heading paragraph. */
343
+ text: string;
344
+ /** Character offset of the heading in the main story. */
345
+ offset: number;
346
+ /** Zero-based page index where the heading lives. */
347
+ pageIndex: number;
348
+ /** Section index where the heading lives. */
349
+ sectionIndex: number;
350
+ }
351
+
352
+ export interface StyleCatalogEntrySnapshot {
353
+ styleId: string;
354
+ displayName: string;
355
+ kind: "paragraph" | "character" | "table";
356
+ isDefault: boolean;
357
+ basedOn?: string;
358
+ nextStyle?: string;
359
+ }
360
+
361
+ export interface StyleCatalogSnapshot {
362
+ paragraphs: StyleCatalogEntrySnapshot[];
363
+ characters: StyleCatalogEntrySnapshot[];
364
+ tables: StyleCatalogEntrySnapshot[];
365
+ fromPackage: boolean;
161
366
  }
162
367
 
163
368
  export interface InsertTableOptions {
@@ -234,7 +439,17 @@ export type SurfaceInlineSegment =
234
439
  warningId: string;
235
440
  label: string;
236
441
  detail: string;
442
+ presentation?: "inline-chip" | "quiet-marker";
237
443
  state: "locked-preserve-only";
444
+ }
445
+ | {
446
+ segmentId: string;
447
+ kind: "note_ref";
448
+ from: number;
449
+ to: number;
450
+ noteKind: "footnote" | "endnote";
451
+ noteId: string;
452
+ label: string;
238
453
  };
239
454
 
240
455
  export interface SurfaceTableCellSnapshot {
@@ -243,11 +458,19 @@ export interface SurfaceTableCellSnapshot {
243
458
  colspan: number;
244
459
  rowspan: number;
245
460
  backgroundColor?: string | null;
461
+ verticalAlign?: "top" | "center" | "bottom" | null;
462
+ borderTop?: string | null;
463
+ borderRight?: string | null;
464
+ borderBottom?: string | null;
465
+ borderLeft?: string | null;
246
466
  content: SurfaceBlockSnapshot[];
247
467
  }
248
468
 
249
469
  export interface SurfaceTableRowSnapshot {
250
470
  cells: SurfaceTableCellSnapshot[];
471
+ height?: number;
472
+ heightRule?: "auto" | "atLeast" | "exact";
473
+ isHeader?: boolean;
251
474
  }
252
475
 
253
476
  export type SurfaceBlockSnapshot =
@@ -261,6 +484,8 @@ export type SurfaceBlockSnapshot =
261
484
  numberingInstanceId: string;
262
485
  level: number;
263
486
  };
487
+ numberingPrefix?: string;
488
+ numberingSuffix?: "tab" | "space" | "nothing";
264
489
  alignment?: "left" | "center" | "right" | "both" | "distribute";
265
490
  spacing?: { before?: number; after?: number; line?: number; lineRule?: string };
266
491
  indentation?: { left?: number; right?: number; firstLine?: number; hanging?: number };
@@ -272,6 +497,7 @@ export type SurfaceBlockSnapshot =
272
497
  pageBreakBefore?: boolean;
273
498
  outlineLevel?: number;
274
499
  bidi?: boolean;
500
+ suppressLineNumbers?: boolean;
275
501
  segments: SurfaceInlineSegment[];
276
502
  }
277
503
  | {
@@ -281,6 +507,15 @@ export type SurfaceBlockSnapshot =
281
507
  to: number;
282
508
  styleId?: string;
283
509
  gridColumns: number[];
510
+ alignment?: "left" | "center" | "right";
511
+ tblLook?: {
512
+ firstRow?: boolean;
513
+ lastRow?: boolean;
514
+ firstColumn?: boolean;
515
+ lastColumn?: boolean;
516
+ noHBand?: boolean;
517
+ noVBand?: boolean;
518
+ };
284
519
  rows: SurfaceTableRowSnapshot[];
285
520
  }
286
521
  | {
@@ -292,6 +527,11 @@ export type SurfaceBlockSnapshot =
292
527
  alias?: string;
293
528
  tag?: string;
294
529
  lock?: string;
530
+ checkboxChecked?: boolean;
531
+ dateValue?: string;
532
+ dropdownItems?: Array<{ displayText?: string; value: string }>;
533
+ comboBoxItems?: Array<{ displayText?: string; value: string }>;
534
+ showingPlcHdr?: boolean;
295
535
  children: SurfaceBlockSnapshot[];
296
536
  }
297
537
  | {
@@ -306,11 +546,19 @@ export type SurfaceBlockSnapshot =
306
546
  state: "locked-preserve-only";
307
547
  };
308
548
 
549
+ export interface SecondaryStorySurface {
550
+ target: EditorStoryTarget;
551
+ label: string;
552
+ storySize: number;
553
+ blocks: SurfaceBlockSnapshot[];
554
+ }
555
+
309
556
  export interface EditorSurfaceSnapshot {
310
557
  storySize: number;
311
558
  plainText: string;
312
559
  blocks: SurfaceBlockSnapshot[];
313
560
  lockedFragmentIds: string[];
561
+ secondaryStories: SecondaryStorySurface[];
314
562
  }
315
563
 
316
564
  export type EditorWarningCode =
@@ -354,7 +602,7 @@ export interface EditorError {
354
602
  code: EditorErrorCode;
355
603
  message: string;
356
604
  isFatal: boolean;
357
- source: "import" | "runtime" | "validation" | "datastore" | "export";
605
+ source: "import" | "runtime" | "validation" | "datastore" | "host" | "export";
358
606
  details?: Record<string, unknown>;
359
607
  }
360
608
 
@@ -389,6 +637,52 @@ export interface CompatibilityPanelSnapshot {
389
637
  featureEntries: CompatibilityFeatureEntry[];
390
638
  }
391
639
 
640
+ export type ViewMode = "editing" | "review" | "view";
641
+
642
+ export type CaretAffinity = "forward" | "backward" | "none";
643
+
644
+ export interface ActiveListContext {
645
+ numberingInstanceId: string;
646
+ level: number;
647
+ isOrdered: boolean;
648
+ markerText?: string;
649
+ }
650
+
651
+ export interface ActiveNoteContext {
652
+ noteKind: "footnote" | "endnote";
653
+ noteId: string;
654
+ referencePosition: number;
655
+ }
656
+
657
+ export interface PageRegionHitTest {
658
+ region: "body" | "header" | "footer" | "margin" | "gutter";
659
+ sectionIndex: number;
660
+ columnIndex: number;
661
+ }
662
+
663
+ export interface LayoutMeasurement {
664
+ pageRegions: PageRegionHitTest[];
665
+ caretAffinity: CaretAffinity;
666
+ tabStops: Array<{ pos: number; val?: string; leader?: string }>;
667
+ listMarkerLane?: { indent: number; markerWidth: number };
668
+ objectFrame?: { kind: "image" | "textbox" | "shape"; anchorPos: number; display: "inline" | "floating" };
669
+ }
670
+
671
+ export interface EditorViewStateSnapshot {
672
+ viewMode: ViewMode;
673
+ workspaceMode: WorkspaceMode;
674
+ zoomLevel: ZoomLevel;
675
+ activeStory: EditorStoryTarget;
676
+ selection: SelectionSnapshot;
677
+ caretAffinity: CaretAffinity;
678
+ activeListContext: ActiveListContext | null;
679
+ activeNoteContext: ActiveNoteContext | null;
680
+ activePageRegion: PageRegionHitTest | null;
681
+ activeObjectFrame: LayoutMeasurement["objectFrame"] | null;
682
+ measurement: LayoutMeasurement;
683
+ isFocused: boolean;
684
+ }
685
+
392
686
  export interface CommandStateSnapshot {
393
687
  canUndo: boolean;
394
688
  canRedo: boolean;
@@ -404,6 +698,8 @@ export interface RuntimeRenderSnapshot {
404
698
  isDirty: boolean;
405
699
  readOnly: boolean;
406
700
  selection: SelectionSnapshot;
701
+ activeStory: EditorStoryTarget;
702
+ pageLayout?: PageLayoutSnapshot;
407
703
  documentStats: DocumentStats;
408
704
  comments: CommentSidebarSnapshot;
409
705
  trackedChanges: TrackedChangesSnapshot;
@@ -414,6 +710,20 @@ export interface RuntimeRenderSnapshot {
414
710
  surface?: EditorSurfaceSnapshot;
415
711
  }
416
712
 
713
+ export interface EditorSessionState {
714
+ sessionVersion: "editor-session-state/1";
715
+ schemaVersion: RuntimePersistedEditorSnapshot["schemaVersion"];
716
+ documentId: string;
717
+ docId: string;
718
+ createdAt: string;
719
+ updatedAt: string;
720
+ editorBuild: string;
721
+ canonicalDocument: RuntimePersistedEditorSnapshot["canonicalDocument"];
722
+ compatibility: CompatibilityReport;
723
+ warningLog: EditorWarning[];
724
+ sourcePackage?: RuntimePersistedEditorSnapshot["sourcePackage"];
725
+ }
726
+
417
727
  export interface PersistedEditorSnapshot {
418
728
  snapshotVersion: RuntimePersistedEditorSnapshot["snapshotVersion"];
419
729
  schemaVersion: RuntimePersistedEditorSnapshot["schemaVersion"];
@@ -440,10 +750,16 @@ export interface ExportDocxOptions {
440
750
  reason?: string;
441
751
  }
442
752
 
753
+ export interface ExportDelivery {
754
+ mode: "downloaded" | "persisted-by-host" | "exported-bytes-only";
755
+ savedAt?: string;
756
+ }
757
+
443
758
  export interface ExportResult {
444
759
  bytes: Uint8Array;
445
760
  mimeType: string;
446
761
  fileName: string;
762
+ delivery: ExportDelivery;
447
763
  }
448
764
 
449
765
  export type AutosaveState =
@@ -462,7 +778,7 @@ export type WordReviewEditorEvent =
462
778
  type: "ready";
463
779
  documentId: string;
464
780
  sessionId: string;
465
- source: "docx" | "snapshot" | "datastore" | "canonical";
781
+ source: "docx" | "session" | "snapshot";
466
782
  stats: DocumentStats;
467
783
  compatibility: CompatibilityReport;
468
784
  comments: CommentSidebarSnapshot;
@@ -499,6 +815,11 @@ export type WordReviewEditorEvent =
499
815
  documentId: string;
500
816
  changeId: string;
501
817
  }
818
+ | {
819
+ type: "story_changed";
820
+ documentId: string;
821
+ activeStory: EditorStoryTarget;
822
+ }
502
823
  | {
503
824
  type: "warning_added";
504
825
  documentId: string;
@@ -526,6 +847,13 @@ export type WordReviewEditorEvent =
526
847
  snapshot: PersistedEditorSnapshot;
527
848
  isAutosave: boolean;
528
849
  }
850
+ | {
851
+ type: "session_saved";
852
+ documentId: string;
853
+ sessionState: EditorSessionState;
854
+ savedAt: string;
855
+ isAutosave: boolean;
856
+ }
529
857
  | {
530
858
  type: "export_completed";
531
859
  documentId: string;
@@ -546,6 +874,16 @@ export interface SaveSnapshotResult {
546
874
  savedAt: string;
547
875
  }
548
876
 
877
+ export interface SaveSessionParams {
878
+ documentId: string;
879
+ sessionState: EditorSessionState;
880
+ isAutosave: boolean;
881
+ }
882
+
883
+ export interface SaveSessionResult {
884
+ savedAt: string;
885
+ }
886
+
549
887
  export interface SaveExportParams {
550
888
  documentId: string;
551
889
  result: ExportResult;
@@ -561,10 +899,17 @@ export interface EditorTelemetryEvent {
561
899
  detail?: Record<string, unknown>;
562
900
  }
563
901
 
902
+ export interface EditorHostAdapter {
903
+ load?(params: LoadRequest): Promise<LoadResult>;
904
+ saveSession?(params: SaveSessionParams): Promise<SaveSessionResult>;
905
+ saveExport?(params: SaveExportParams): Promise<SaveExportResult>;
906
+ logEvent?(event: EditorTelemetryEvent): void;
907
+ }
908
+
564
909
  export interface EditorDatastoreAdapter {
565
- load(params: { documentId: string }): Promise<LoadResult>;
910
+ load(params: LoadRequest): Promise<LoadResult>;
566
911
  saveSnapshot(params: SaveSnapshotParams): Promise<SaveSnapshotResult>;
567
- saveExport(params: SaveExportParams): Promise<SaveExportResult>;
912
+ saveExport?(params: SaveExportParams): Promise<SaveExportResult>;
568
913
  logEvent?(event: EditorTelemetryEvent): void;
569
914
  }
570
915
 
@@ -585,6 +930,7 @@ export interface WordReviewEditorRef {
585
930
  acceptAllChanges(): void;
586
931
  rejectAllChanges(): void;
587
932
  exportDocx(options?: ExportDocxOptions): Promise<ExportResult>;
933
+ getSessionState(): EditorSessionState;
588
934
  getSnapshot(): PersistedEditorSnapshot;
589
935
  getRenderSnapshot(): RuntimeRenderSnapshot;
590
936
  getCompatibilityReport(): CompatibilityReport;
@@ -595,6 +941,7 @@ export interface WordReviewEditorRef {
595
941
  getTrackedChanges(): TrackedChangesSnapshot;
596
942
  isDirty(): boolean;
597
943
  getFormattingState(): FormattingStateSnapshot;
944
+ getStyleCatalog(): StyleCatalogSnapshot;
598
945
  replaceText(text: string, target?: EditorAnchorProjection): void;
599
946
  toggleBold(): void;
600
947
  toggleItalic(): void;
@@ -607,6 +954,8 @@ export interface WordReviewEditorRef {
607
954
  setTextColor(color: string | null): void;
608
955
  setHighlightColor(color: string | null): void;
609
956
  setAlignment(alignment: FormattingAlignment): void;
957
+ setParagraphStyle(styleId: string | null): void;
958
+ setTableStyle(styleId: string | null): void;
610
959
  indent(): void;
611
960
  outdent(): void;
612
961
  insertPageBreak(): void;
@@ -627,20 +976,41 @@ export interface WordReviewEditorRef {
627
976
  setSelection(selection: SelectionSnapshot | null): void;
628
977
  scrollToRevision(revisionId: string): void;
629
978
  scrollToComment(commentId: string): void;
979
+ openStory(target: EditorStoryTarget): void;
980
+ closeStory(): void;
981
+ getPageLayoutSnapshot(): PageLayoutSnapshot | null;
982
+ getDocumentNavigationSnapshot(): DocumentNavigationSnapshot;
983
+ getViewState(): EditorViewStateSnapshot;
984
+ setWorkspaceMode(mode: WorkspaceMode): void;
985
+ setZoom(level: ZoomLevel): void;
986
+ insertSectionBreak(type: SectionBreakType, options?: { afterSectionIndex?: number }): void;
987
+ deleteSectionBreak(sectionIndex: number): void;
988
+ updateSectionLayout(sectionIndex: number, patch: SectionLayoutPatch): void;
989
+ setSectionPageNumbering(
990
+ sectionIndex: number,
991
+ patch: SectionPageNumberingPatch | null,
992
+ ): void;
993
+ setHeaderFooterLink(sectionIndex: number, params: HeaderFooterLinkPatch): void;
994
+ setImageLayout(mediaId: string, dimensions: { widthEmu: number; heightEmu: number }): void;
995
+ setImageFrame(mediaId: string, offsets: { horizontalOffsetEmu?: number; verticalOffsetEmu?: number }): void;
630
996
  }
631
997
 
632
998
  export interface WordReviewEditorProps {
633
999
  documentId: string;
634
1000
  currentUser: EditorUser;
635
1001
  initialDocx?: Uint8Array | ArrayBuffer;
1002
+ initialSessionState?: EditorSessionState;
636
1003
  initialSnapshot?: PersistedEditorSnapshot;
637
1004
  initialSourceLabel?: string;
638
1005
  externalDocumentRevision?: string;
639
1006
  externalDocSource?: ExternalDocumentSource;
1007
+ loadRevision?: string;
1008
+ loadSourcePolicy?: LoadSourcePolicy;
640
1009
  readOnly?: boolean;
641
1010
  reviewMode?: "editing" | "review";
642
1011
  markupDisplay?: "clean" | "simple" | "all";
643
1012
  showReviewPanel?: boolean;
1013
+ hostAdapter?: EditorHostAdapter;
644
1014
  datastore?: EditorDatastoreAdapter;
645
1015
  autosave?: AutosaveConfig;
646
1016
  onEvent?: (event: WordReviewEditorEvent) => void;
@@ -0,0 +1,58 @@
1
+ import type {
2
+ EditorSessionState,
3
+ PersistedEditorSnapshot,
4
+ } from "./public-types.ts";
5
+
6
+ export const EDITOR_SESSION_STATE_VERSION = "editor-session-state/1" as const;
7
+
8
+ function cloneStructuredValue<T>(value: T): T {
9
+ return structuredClone(value);
10
+ }
11
+
12
+ export function createEditorSessionState(
13
+ input: Omit<EditorSessionState, "sessionVersion">,
14
+ ): EditorSessionState {
15
+ return cloneStructuredValue({
16
+ sessionVersion: EDITOR_SESSION_STATE_VERSION,
17
+ ...input,
18
+ });
19
+ }
20
+
21
+ export function editorSessionStateFromPersistedSnapshot(
22
+ snapshot: PersistedEditorSnapshot,
23
+ ): EditorSessionState {
24
+ return createEditorSessionState({
25
+ schemaVersion: snapshot.schemaVersion,
26
+ documentId: snapshot.documentId,
27
+ docId: snapshot.docId,
28
+ createdAt: snapshot.createdAt,
29
+ updatedAt: snapshot.updatedAt,
30
+ editorBuild: snapshot.editorBuild,
31
+ canonicalDocument: snapshot.canonicalDocument,
32
+ compatibility: snapshot.compatibility,
33
+ warningLog: snapshot.warningLog,
34
+ sourcePackage: snapshot.sourcePackage,
35
+ });
36
+ }
37
+
38
+ export function persistedSnapshotFromEditorSessionState(
39
+ sessionState: EditorSessionState,
40
+ options: {
41
+ savedAt: string;
42
+ },
43
+ ): PersistedEditorSnapshot {
44
+ return cloneStructuredValue({
45
+ snapshotVersion: "persisted-editor-snapshot/2",
46
+ schemaVersion: sessionState.schemaVersion,
47
+ documentId: sessionState.documentId,
48
+ docId: sessionState.docId,
49
+ createdAt: sessionState.createdAt,
50
+ updatedAt: sessionState.updatedAt,
51
+ savedAt: options.savedAt,
52
+ editorBuild: sessionState.editorBuild,
53
+ canonicalDocument: sessionState.canonicalDocument,
54
+ compatibility: sessionState.compatibility,
55
+ warningLog: sessionState.warningLog,
56
+ sourcePackage: sessionState.sourcePackage,
57
+ });
58
+ }
@@ -271,6 +271,7 @@ export function getFormattingStateFromRenderSnapshot(
271
271
  alignment: getConsistentValue(paragraphs, (paragraph) =>
272
272
  toPublicAlignment(paragraph.alignment),
273
273
  ),
274
+ paragraphStyleId: getConsistentValue(paragraphs, (paragraph) => paragraph.styleId),
274
275
  breadcrumb: getSelectionBreadcrumb(surface.blocks, snapshot.selection),
275
276
  };
276
277
  }