@beyondwork/docx-react-component 1.0.18 → 1.0.20

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 (105) hide show
  1. package/README.md +8 -2
  2. package/package.json +24 -34
  3. package/src/api/README.md +5 -1
  4. package/src/api/public-types.ts +710 -4
  5. package/src/api/session-state.ts +60 -0
  6. package/src/core/commands/formatting-commands.ts +2 -1
  7. package/src/core/commands/image-commands.ts +147 -0
  8. package/src/core/commands/index.ts +19 -3
  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 +357 -0
  14. package/src/core/selection/mapping.ts +41 -0
  15. package/src/core/state/editor-state.ts +4 -1
  16. package/src/index.ts +51 -0
  17. package/src/io/docx-session.ts +623 -56
  18. package/src/io/export/serialize-comments.ts +104 -34
  19. package/src/io/export/serialize-footnotes.ts +198 -1
  20. package/src/io/export/serialize-headers-footers.ts +203 -10
  21. package/src/io/export/serialize-main-document.ts +285 -8
  22. package/src/io/export/serialize-numbering.ts +28 -7
  23. package/src/io/export/split-review-boundaries.ts +181 -19
  24. package/src/io/normalize/normalize-text.ts +144 -32
  25. package/src/io/ooxml/highlight-colors.ts +39 -0
  26. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  27. package/src/io/ooxml/parse-comments.ts +85 -19
  28. package/src/io/ooxml/parse-fields.ts +396 -0
  29. package/src/io/ooxml/parse-footnotes.ts +452 -22
  30. package/src/io/ooxml/parse-headers-footers.ts +657 -29
  31. package/src/io/ooxml/parse-inline-media.ts +30 -0
  32. package/src/io/ooxml/parse-main-document.ts +807 -20
  33. package/src/io/ooxml/parse-numbering.ts +7 -0
  34. package/src/io/ooxml/parse-revisions.ts +317 -38
  35. package/src/io/ooxml/parse-settings.ts +184 -0
  36. package/src/io/ooxml/parse-shapes.ts +25 -0
  37. package/src/io/ooxml/parse-styles.ts +463 -0
  38. package/src/io/ooxml/parse-theme.ts +32 -0
  39. package/src/legal/bookmarks.ts +44 -0
  40. package/src/legal/cross-references.ts +59 -1
  41. package/src/model/canonical-document.ts +250 -4
  42. package/src/model/cds-1.0.0.ts +13 -0
  43. package/src/model/snapshot.ts +87 -2
  44. package/src/review/store/revision-store.ts +6 -0
  45. package/src/review/store/revision-types.ts +1 -0
  46. package/src/runtime/document-layout.ts +332 -0
  47. package/src/runtime/document-navigation.ts +603 -0
  48. package/src/runtime/document-runtime.ts +1754 -78
  49. package/src/runtime/document-search.ts +145 -0
  50. package/src/runtime/numbering-prefix.ts +47 -26
  51. package/src/runtime/page-layout-estimation.ts +212 -0
  52. package/src/runtime/read-only-diagnostics-runtime.ts +9 -0
  53. package/src/runtime/session-capabilities.ts +35 -3
  54. package/src/runtime/story-context.ts +164 -0
  55. package/src/runtime/story-targeting.ts +162 -0
  56. package/src/runtime/surface-projection.ts +324 -36
  57. package/src/runtime/table-schema.ts +89 -7
  58. package/src/runtime/view-state.ts +477 -0
  59. package/src/runtime/workflow-markup.ts +349 -0
  60. package/src/ui/WordReviewEditor.tsx +2469 -1344
  61. package/src/ui/browser-export.ts +52 -0
  62. package/src/ui/editor-command-bag.ts +120 -0
  63. package/src/ui/editor-runtime-boundary.ts +1422 -0
  64. package/src/ui/editor-shell-view.tsx +134 -0
  65. package/src/ui/editor-surface-controller.tsx +51 -0
  66. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  67. package/src/ui/headless/revision-decoration-model.ts +4 -4
  68. package/src/ui/headless/selection-helpers.ts +20 -0
  69. package/src/ui/headless/selection-toolbar-model.ts +22 -0
  70. package/src/ui/headless/use-editor-keyboard.ts +6 -1
  71. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  72. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
  73. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
  74. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  75. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
  76. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
  77. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +150 -14
  78. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
  79. package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
  80. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +46 -7
  81. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  82. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -0
  83. package/src/ui-tailwind/editor-surface/pm-position-map.ts +3 -3
  84. package/src/ui-tailwind/editor-surface/pm-schema.ts +186 -13
  85. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +191 -68
  86. package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
  87. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +51 -0
  88. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
  89. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
  90. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +528 -85
  91. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
  92. package/src/ui-tailwind/index.ts +2 -1
  93. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  94. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
  95. package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
  96. package/src/ui-tailwind/review/tw-review-rail.tsx +8 -8
  97. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
  98. package/src/ui-tailwind/theme/editor-theme.css +127 -0
  99. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
  100. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +829 -12
  101. package/src/ui-tailwind/tw-review-workspace.tsx +1238 -42
  102. package/src/validation/compatibility-engine.ts +119 -24
  103. package/src/validation/compatibility-report.ts +1 -0
  104. package/src/validation/diagnostics.ts +1 -0
  105. package/src/validation/docx-comment-proof.ts +707 -0
@@ -1,4 +1,13 @@
1
1
  import type { PersistedEditorSnapshot as RuntimePersistedEditorSnapshot } from "../core/state/editor-state.ts";
2
+ import type {
3
+ FieldFamily as FieldFamilyType,
4
+ FieldRefreshStatus as FieldRefreshStatusType,
5
+ SupportedFieldFamily as SupportedFieldFamilyType,
6
+ } from "../model/canonical-document.ts";
7
+
8
+ export type FieldFamily = FieldFamilyType;
9
+ export type FieldRefreshStatus = FieldRefreshStatusType;
10
+ export type SupportedFieldFamily = SupportedFieldFamilyType;
2
11
 
3
12
  export type ExternalDocumentSource =
4
13
  | {
@@ -6,12 +15,25 @@ export type ExternalDocumentSource =
6
15
  bytes: Uint8Array | ArrayBuffer;
7
16
  sourceLabel?: string;
8
17
  }
18
+ | {
19
+ kind: "session";
20
+ sessionState: EditorSessionState;
21
+ sourceLabel?: string;
22
+ }
9
23
  | {
10
24
  kind: "snapshot";
11
25
  snapshot: PersistedEditorSnapshot;
12
26
  sourceLabel?: string;
13
27
  };
14
28
 
29
+ export type LoadSourcePolicy = "prefer-saved-state" | "prefer-source-package";
30
+
31
+ export interface LoadRequest {
32
+ documentId: string;
33
+ loadRevision?: string;
34
+ loadSourcePolicy?: LoadSourcePolicy;
35
+ }
36
+
15
37
  export interface EditorUser {
16
38
  userId: string;
17
39
  displayName: string;
@@ -43,11 +65,129 @@ export type EditorAnchorProjection =
43
65
  reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity";
44
66
  };
45
67
 
68
+ export type EditorStoryTarget =
69
+ | { kind: "main" }
70
+ | {
71
+ kind: "header";
72
+ relationshipId: string;
73
+ variant: "default" | "first" | "even";
74
+ sectionIndex?: number;
75
+ }
76
+ | {
77
+ kind: "footer";
78
+ relationshipId: string;
79
+ variant: "default" | "first" | "even";
80
+ sectionIndex?: number;
81
+ }
82
+ | { kind: "footnote"; noteId: string }
83
+ | { kind: "endnote"; noteId: string };
84
+
46
85
  export interface SelectionSnapshot {
47
86
  anchor: number;
48
87
  head: number;
49
88
  isCollapsed: boolean;
50
89
  activeRange: EditorAnchorProjection;
90
+ storyTarget?: EditorStoryTarget;
91
+ }
92
+
93
+ export interface PageLayoutSnapshot {
94
+ sectionIndex: number;
95
+ sectionType?: "continuous" | "nextPage" | "evenPage" | "oddPage" | "nextColumn";
96
+ pageWidth: number;
97
+ pageHeight: number;
98
+ marginTop: number;
99
+ marginBottom: number;
100
+ marginLeft: number;
101
+ marginRight: number;
102
+ headerMargin: number;
103
+ footerMargin: number;
104
+ gutter: number;
105
+ orientation: "portrait" | "landscape";
106
+ columns: number;
107
+ differentFirstPage: boolean;
108
+ differentOddEvenPages: boolean;
109
+ pageNumbering?: {
110
+ format?: string;
111
+ start?: number;
112
+ chapterStyle?: string;
113
+ chapterSeparator?: string;
114
+ };
115
+ lineNumbering?: {
116
+ countBy?: number;
117
+ start?: number;
118
+ distance?: number;
119
+ restart?: "newPage" | "newSection" | "continuous";
120
+ };
121
+ pageBorders?: {
122
+ top?: { value?: string; size?: number; space?: number; color?: string };
123
+ left?: { value?: string; size?: number; space?: number; color?: string };
124
+ bottom?: { value?: string; size?: number; space?: number; color?: string };
125
+ right?: { value?: string; size?: number; space?: number; color?: string };
126
+ offsetFrom?: "page" | "text";
127
+ display?: "allPages" | "firstPage" | "notFirstPage";
128
+ zOrder?: "front" | "back";
129
+ };
130
+ documentGrid?: {
131
+ type?: "default" | "lines" | "linesAndChars" | "snapToChars";
132
+ linePitch?: number;
133
+ charSpace?: number;
134
+ };
135
+ columnDefinitions: Array<{ width: number; space?: number }>;
136
+ equalWidthColumns: boolean;
137
+ columnSeparator: boolean;
138
+ headerVariants: Array<{ variant: "default" | "first" | "even"; relationshipId: string }>;
139
+ footerVariants: Array<{ variant: "default" | "first" | "even"; relationshipId: string }>;
140
+ }
141
+
142
+ export type WorkspaceMode = "canvas" | "page";
143
+
144
+ export type ZoomLevel = "pageWidth" | "onePage" | number;
145
+
146
+ export type SectionBreakType =
147
+ | "nextPage"
148
+ | "continuous"
149
+ | "evenPage"
150
+ | "oddPage"
151
+ | "nextColumn";
152
+
153
+ export interface SectionLayoutPatch {
154
+ pageSize?: Partial<{
155
+ width: number;
156
+ height: number;
157
+ orientation: "portrait" | "landscape";
158
+ }>;
159
+ pageMargins?: Partial<{
160
+ top: number;
161
+ right: number;
162
+ bottom: number;
163
+ left: number;
164
+ header: number;
165
+ footer: number;
166
+ gutter: number;
167
+ }>;
168
+ columns?: Partial<{
169
+ count: number;
170
+ space: number;
171
+ equalWidth: boolean;
172
+ columns: Array<{ width: number; space?: number }>;
173
+ separator: boolean;
174
+ }>;
175
+ titlePage?: boolean;
176
+ sectionType?: SectionBreakType;
177
+ }
178
+
179
+ export interface SectionPageNumberingPatch {
180
+ format?: string | null;
181
+ start?: number | null;
182
+ chapterStyle?: string | null;
183
+ chapterSeparator?: string | null;
184
+ }
185
+
186
+ export interface HeaderFooterLinkPatch {
187
+ kind: "header" | "footer";
188
+ variant: "default" | "first" | "even";
189
+ linkToPrevious: boolean;
190
+ relationshipId?: string | null;
51
191
  }
52
192
 
53
193
  export interface DocumentStats {
@@ -144,6 +284,7 @@ export interface FormattingStateSnapshot {
144
284
  textColor?: string;
145
285
  highlightColor?: string | null;
146
286
  alignment?: FormattingAlignment;
287
+ paragraphStyleId?: string;
147
288
  breadcrumb: FormattingBreadcrumbItem[];
148
289
  }
149
290
 
@@ -158,6 +299,143 @@ export interface SearchResultSnapshot {
158
299
  anchor: EditorAnchorProjection;
159
300
  excerpt: string;
160
301
  isActive: boolean;
302
+ storyTarget?: EditorStoryTarget;
303
+ sectionIndex?: number;
304
+ pageIndex?: number;
305
+ }
306
+
307
+ // ---------------------------------------------------------------------------
308
+ // Document Navigation Model (Wave 37)
309
+ // ---------------------------------------------------------------------------
310
+
311
+ /**
312
+ * Runtime-owned snapshot of the document's page stack, heading outline, and
313
+ * navigation context. Derived from canonical document state and section
314
+ * properties — never from DOM scroll position or CSS layout.
315
+ */
316
+ export interface DocumentNavigationSnapshot {
317
+ /** Total estimated page count across all sections. */
318
+ pageCount: number;
319
+ /** Ordered page descriptors, one per logical page. */
320
+ pages: DocumentPageSnapshot[];
321
+ /** Ordered heading outline extracted from paragraph styles. */
322
+ headings: DocumentHeadingSnapshot[];
323
+ /** Index of the page containing the current selection head. */
324
+ activePageIndex: number;
325
+ /** Section index containing the current selection head. */
326
+ activeSectionIndex: number;
327
+ }
328
+
329
+ /** Runtime-derived descriptor for a single logical page. */
330
+ export interface DocumentPageSnapshot {
331
+ /** Zero-based page index within the whole document. */
332
+ pageIndex: number;
333
+ /** Section that owns this page (zero-based). */
334
+ sectionIndex: number;
335
+ /** Zero-based page index within its owning section. */
336
+ pageInSection: number;
337
+ /** Estimated character offset where the page begins in the main story. */
338
+ startOffset: number;
339
+ /** Estimated character offset where the page ends in the main story. */
340
+ endOffset: number;
341
+ /** Page layout properties inherited from the section. */
342
+ layout: PageLayoutSnapshot;
343
+ }
344
+
345
+ /** A heading entry in the document outline. */
346
+ export interface DocumentHeadingSnapshot {
347
+ /** Stable identifier for the heading (derived from block position). */
348
+ headingId: string;
349
+ /** Outline level (1 = Heading 1, 2 = Heading 2, etc.). */
350
+ level: number;
351
+ /** Plain-text content of the heading paragraph. */
352
+ text: string;
353
+ /** Character offset of the heading in the main story. */
354
+ offset: number;
355
+ /** Zero-based page index where the heading lives. */
356
+ pageIndex: number;
357
+ /** Section index where the heading lives. */
358
+ sectionIndex: number;
359
+ }
360
+
361
+ /** A single field entry in the runtime field snapshot. */
362
+ export interface FieldEntrySnapshot {
363
+ /** Stable index of the field in document order. */
364
+ index: number;
365
+ /** Classified field family. */
366
+ fieldFamily: FieldFamily;
367
+ /** Whether this family is in the supported refresh slice. */
368
+ supported: boolean;
369
+ /** Raw field instruction string. */
370
+ instruction: string;
371
+ /** Target bookmark for REF/PAGEREF/NOTEREF fields. */
372
+ fieldTarget?: string;
373
+ /** Current refresh status. */
374
+ refreshStatus: FieldRefreshStatus;
375
+ /** Resolved display text (from field children). */
376
+ displayText: string;
377
+ }
378
+
379
+ /** Runtime field snapshot — read-only view of all fields in the document. */
380
+ export interface FieldSnapshot {
381
+ /** Total number of fields in the document. */
382
+ totalCount: number;
383
+ /** Number of fields in the supported refresh slice. */
384
+ supportedCount: number;
385
+ /** Number of fields that are preserve-only. */
386
+ preserveOnlyCount: number;
387
+ /** Ordered field entries. */
388
+ fields: FieldEntrySnapshot[];
389
+ }
390
+
391
+ /** Options for runtime-backed field refresh. */
392
+ export interface UpdateFieldsOptions {
393
+ /** When true, only refresh fields in the supported slice. Default true. */
394
+ supportedOnly?: boolean;
395
+ }
396
+
397
+ /** Result of a runtime-backed field refresh operation. */
398
+ export interface UpdateFieldsResult {
399
+ /** Total number of fields in the document. */
400
+ totalCount: number;
401
+ /** Number of fields that were refreshed. */
402
+ updatedCount: number;
403
+ /** Number of preserve-only fields left unchanged. */
404
+ preserveOnlyCount: number;
405
+ }
406
+
407
+ /** Options for runtime-backed TOC refresh. */
408
+ export interface TocRefreshOptions {
409
+ /** Maximum heading outline level to include (1–9, default 3). */
410
+ maxLevel?: number;
411
+ }
412
+
413
+ /** Result of a TOC refresh operation. */
414
+ export interface TocRefreshResult {
415
+ /** Number of TOC entries generated from heading structure. */
416
+ entryCount: number;
417
+ /** Heading entries that populated the TOC. */
418
+ entries: Array<{
419
+ level: number;
420
+ text: string;
421
+ pageIndex: number;
422
+ }>;
423
+ }
424
+
425
+ export interface StyleCatalogEntrySnapshot {
426
+ styleId: string;
427
+ displayName: string;
428
+ kind: "paragraph" | "character" | "table";
429
+ isDefault: boolean;
430
+ basedOn?: string;
431
+ nextStyle?: string;
432
+ }
433
+
434
+ export interface StyleCatalogSnapshot {
435
+ paragraphs: StyleCatalogEntrySnapshot[];
436
+ characters: StyleCatalogEntrySnapshot[];
437
+ tables: StyleCatalogEntrySnapshot[];
438
+ fromPackage: boolean;
161
439
  }
162
440
 
163
441
  export interface InsertTableOptions {
@@ -234,7 +512,28 @@ export type SurfaceInlineSegment =
234
512
  warningId: string;
235
513
  label: string;
236
514
  detail: string;
515
+ presentation?: "inline-chip" | "quiet-marker";
237
516
  state: "locked-preserve-only";
517
+ }
518
+ | {
519
+ segmentId: string;
520
+ kind: "note_ref";
521
+ from: number;
522
+ to: number;
523
+ noteKind: "footnote" | "endnote";
524
+ noteId: string;
525
+ label: string;
526
+ }
527
+ | {
528
+ segmentId: string;
529
+ kind: "field_ref";
530
+ from: number;
531
+ to: number;
532
+ fieldFamily: SupportedFieldFamily;
533
+ fieldTarget?: string;
534
+ instruction: string;
535
+ refreshStatus: FieldRefreshStatus;
536
+ label: string;
238
537
  };
239
538
 
240
539
  export interface SurfaceTableCellSnapshot {
@@ -243,11 +542,19 @@ export interface SurfaceTableCellSnapshot {
243
542
  colspan: number;
244
543
  rowspan: number;
245
544
  backgroundColor?: string | null;
545
+ verticalAlign?: "top" | "center" | "bottom" | null;
546
+ borderTop?: string | null;
547
+ borderRight?: string | null;
548
+ borderBottom?: string | null;
549
+ borderLeft?: string | null;
246
550
  content: SurfaceBlockSnapshot[];
247
551
  }
248
552
 
249
553
  export interface SurfaceTableRowSnapshot {
250
554
  cells: SurfaceTableCellSnapshot[];
555
+ height?: number;
556
+ heightRule?: "auto" | "atLeast" | "exact";
557
+ isHeader?: boolean;
251
558
  }
252
559
 
253
560
  export type SurfaceBlockSnapshot =
@@ -261,8 +568,11 @@ export type SurfaceBlockSnapshot =
261
568
  numberingInstanceId: string;
262
569
  level: number;
263
570
  };
571
+ numberingPrefix?: string;
572
+ numberingSuffix?: "tab" | "space" | "nothing";
264
573
  alignment?: "left" | "center" | "right" | "both" | "distribute";
265
574
  spacing?: { before?: number; after?: number; line?: number; lineRule?: string };
575
+ contextualSpacing?: boolean;
266
576
  indentation?: { left?: number; right?: number; firstLine?: number; hanging?: number };
267
577
  borders?: { top?: unknown; left?: unknown; bottom?: unknown; right?: unknown; bar?: unknown; between?: unknown };
268
578
  shading?: { fill?: string; color?: string; val?: string };
@@ -272,6 +582,7 @@ export type SurfaceBlockSnapshot =
272
582
  pageBreakBefore?: boolean;
273
583
  outlineLevel?: number;
274
584
  bidi?: boolean;
585
+ suppressLineNumbers?: boolean;
275
586
  segments: SurfaceInlineSegment[];
276
587
  }
277
588
  | {
@@ -281,6 +592,15 @@ export type SurfaceBlockSnapshot =
281
592
  to: number;
282
593
  styleId?: string;
283
594
  gridColumns: number[];
595
+ alignment?: "left" | "center" | "right";
596
+ tblLook?: {
597
+ firstRow?: boolean;
598
+ lastRow?: boolean;
599
+ firstColumn?: boolean;
600
+ lastColumn?: boolean;
601
+ noHBand?: boolean;
602
+ noVBand?: boolean;
603
+ };
284
604
  rows: SurfaceTableRowSnapshot[];
285
605
  }
286
606
  | {
@@ -292,6 +612,11 @@ export type SurfaceBlockSnapshot =
292
612
  alias?: string;
293
613
  tag?: string;
294
614
  lock?: string;
615
+ checkboxChecked?: boolean;
616
+ dateValue?: string;
617
+ dropdownItems?: Array<{ displayText?: string; value: string }>;
618
+ comboBoxItems?: Array<{ displayText?: string; value: string }>;
619
+ showingPlcHdr?: boolean;
295
620
  children: SurfaceBlockSnapshot[];
296
621
  }
297
622
  | {
@@ -306,11 +631,19 @@ export type SurfaceBlockSnapshot =
306
631
  state: "locked-preserve-only";
307
632
  };
308
633
 
634
+ export interface SecondaryStorySurface {
635
+ target: EditorStoryTarget;
636
+ label: string;
637
+ storySize: number;
638
+ blocks: SurfaceBlockSnapshot[];
639
+ }
640
+
309
641
  export interface EditorSurfaceSnapshot {
310
642
  storySize: number;
311
643
  plainText: string;
312
644
  blocks: SurfaceBlockSnapshot[];
313
645
  lockedFragmentIds: string[];
646
+ secondaryStories: SecondaryStorySurface[];
314
647
  }
315
648
 
316
649
  export type EditorWarningCode =
@@ -354,7 +687,7 @@ export interface EditorError {
354
687
  code: EditorErrorCode;
355
688
  message: string;
356
689
  isFatal: boolean;
357
- source: "import" | "runtime" | "validation" | "datastore" | "export";
690
+ source: "import" | "runtime" | "validation" | "datastore" | "host" | "export";
358
691
  details?: Record<string, unknown>;
359
692
  }
360
693
 
@@ -389,6 +722,108 @@ export interface CompatibilityPanelSnapshot {
389
722
  featureEntries: CompatibilityFeatureEntry[];
390
723
  }
391
724
 
725
+ export type ViewMode = "editing" | "review" | "view";
726
+
727
+ /**
728
+ * Runtime document-mode — controls whether editing is unrestricted, restricted
729
+ * to suggestions, or fully read-only at the document level.
730
+ *
731
+ * Distinct from `ViewMode` (editor posture) and `WorkspaceMode` (shell layout).
732
+ * `DocumentMode` reflects document-level editing authority, not presentation.
733
+ */
734
+ export type DocumentMode = "editing" | "suggesting" | "viewing";
735
+
736
+ /**
737
+ * A single permission range parsed from `w:permStart` / `w:permEnd` in the
738
+ * source package. Ranges carry stable ids, edit-rights metadata, and an
739
+ * enforcement reason that the UI can display.
740
+ */
741
+ export interface ProtectionRange {
742
+ /** Stable range id from `w:id` on the originating `w:permStart`. */
743
+ rangeId: string;
744
+ /** Start offset of the editable range when the runtime can map it safely. */
745
+ start?: number;
746
+ /** End offset of the editable range when the runtime can map it safely. */
747
+ end?: number;
748
+ /** Editor group allowed to edit this range (`w:edGrp`), if specified. */
749
+ editorGroup?: string;
750
+ /** Specific editor identity (`w:ed`), if specified. */
751
+ editor?: string;
752
+ /** Whether this range is currently enforced by the runtime. */
753
+ enforced: boolean;
754
+ /** Human-readable reason for the enforcement state. */
755
+ enforcementReason: string;
756
+ }
757
+
758
+ /**
759
+ * Snapshot of the document's protection posture. Descriptive and bounded —
760
+ * no public bypass around protected ranges, export blockers, or preserve-only
761
+ * regions.
762
+ */
763
+ export interface ProtectionSnapshot {
764
+ /** Whether document-level protection is declared in the source package. */
765
+ hasDocumentProtection: boolean;
766
+ /**
767
+ * The declared protection edit type from `w:documentProtection` in
768
+ * `settings.xml` (e.g. `"readOnly"`, `"comments"`, `"trackedChanges"`,
769
+ * `"forms"`, `"none"`). `undefined` when no protection element exists.
770
+ */
771
+ editType?: string;
772
+ /** Whether the declared protection is enforced (`w:enforcement="1"`). */
773
+ enforcementActive: boolean;
774
+ /** All parsed permission ranges from the source package. */
775
+ ranges: ProtectionRange[];
776
+ /** Count of permission ranges that are currently enforced. */
777
+ enforcedRangeCount: number;
778
+ /** Count of permission ranges preserved but not live-enforced. */
779
+ preservedRangeCount: number;
780
+ }
781
+
782
+ export type CaretAffinity = "forward" | "backward" | "none";
783
+
784
+ export interface ActiveListContext {
785
+ numberingInstanceId: string;
786
+ level: number;
787
+ isOrdered: boolean;
788
+ markerText?: string;
789
+ }
790
+
791
+ export interface ActiveNoteContext {
792
+ noteKind: "footnote" | "endnote";
793
+ noteId: string;
794
+ referencePosition: number;
795
+ }
796
+
797
+ export interface PageRegionHitTest {
798
+ region: "body" | "header" | "footer" | "margin" | "gutter";
799
+ sectionIndex: number;
800
+ columnIndex: number;
801
+ }
802
+
803
+ export interface LayoutMeasurement {
804
+ pageRegions: PageRegionHitTest[];
805
+ caretAffinity: CaretAffinity;
806
+ tabStops: Array<{ pos: number; val?: string; leader?: string }>;
807
+ listMarkerLane?: { indent: number; markerWidth: number };
808
+ objectFrame?: { kind: "image" | "textbox" | "shape"; anchorPos: number; display: "inline" | "floating" };
809
+ }
810
+
811
+ export interface EditorViewStateSnapshot {
812
+ viewMode: ViewMode;
813
+ documentMode: DocumentMode;
814
+ workspaceMode: WorkspaceMode;
815
+ zoomLevel: ZoomLevel;
816
+ activeStory: EditorStoryTarget;
817
+ selection: SelectionSnapshot;
818
+ caretAffinity: CaretAffinity;
819
+ activeListContext: ActiveListContext | null;
820
+ activeNoteContext: ActiveNoteContext | null;
821
+ activePageRegion: PageRegionHitTest | null;
822
+ activeObjectFrame: LayoutMeasurement["objectFrame"] | null;
823
+ measurement: LayoutMeasurement;
824
+ isFocused: boolean;
825
+ }
826
+
392
827
  export interface CommandStateSnapshot {
393
828
  canUndo: boolean;
394
829
  canRedo: boolean;
@@ -403,7 +838,10 @@ export interface RuntimeRenderSnapshot {
403
838
  isReady: boolean;
404
839
  isDirty: boolean;
405
840
  readOnly: boolean;
841
+ documentMode: DocumentMode;
406
842
  selection: SelectionSnapshot;
843
+ activeStory: EditorStoryTarget;
844
+ pageLayout?: PageLayoutSnapshot;
407
845
  documentStats: DocumentStats;
408
846
  comments: CommentSidebarSnapshot;
409
847
  trackedChanges: TrackedChangesSnapshot;
@@ -412,6 +850,22 @@ export interface RuntimeRenderSnapshot {
412
850
  fatalError?: EditorError;
413
851
  commandState: CommandStateSnapshot;
414
852
  surface?: EditorSurfaceSnapshot;
853
+ protectionSnapshot: ProtectionSnapshot;
854
+ }
855
+
856
+ export interface EditorSessionState {
857
+ sessionVersion: "editor-session-state/1";
858
+ schemaVersion: RuntimePersistedEditorSnapshot["schemaVersion"];
859
+ documentId: string;
860
+ docId: string;
861
+ createdAt: string;
862
+ updatedAt: string;
863
+ editorBuild: string;
864
+ canonicalDocument: RuntimePersistedEditorSnapshot["canonicalDocument"];
865
+ compatibility: CompatibilityReport;
866
+ warningLog: EditorWarning[];
867
+ protectionSnapshot?: ProtectionSnapshot;
868
+ sourcePackage?: RuntimePersistedEditorSnapshot["sourcePackage"];
415
869
  }
416
870
 
417
871
  export interface PersistedEditorSnapshot {
@@ -426,6 +880,7 @@ export interface PersistedEditorSnapshot {
426
880
  canonicalDocument: RuntimePersistedEditorSnapshot["canonicalDocument"];
427
881
  compatibility: CompatibilityReport;
428
882
  warningLog: EditorWarning[];
883
+ protectionSnapshot?: ProtectionSnapshot;
429
884
  sourcePackage?: RuntimePersistedEditorSnapshot["sourcePackage"];
430
885
  }
431
886
 
@@ -440,10 +895,179 @@ export interface ExportDocxOptions {
440
895
  reason?: string;
441
896
  }
442
897
 
898
+ export interface ExportDelivery {
899
+ mode: "downloaded" | "persisted-by-host" | "exported-bytes-only";
900
+ savedAt?: string;
901
+ }
902
+
443
903
  export interface ExportResult {
444
904
  bytes: Uint8Array;
445
905
  mimeType: string;
446
906
  fileName: string;
907
+ delivery: ExportDelivery;
908
+ }
909
+
910
+ export type WorkflowScopeMode = "edit" | "suggest" | "comment" | "view";
911
+
912
+ export interface WorkflowCandidateRange {
913
+ candidateId: string;
914
+ storyTarget?: EditorStoryTarget;
915
+ anchor: EditorAnchorProjection;
916
+ label?: string;
917
+ source?: "search" | "playbook" | "host" | "ai";
918
+ }
919
+
920
+ export interface WorkflowScope {
921
+ scopeId: string;
922
+ mode: WorkflowScopeMode;
923
+ anchor: EditorAnchorProjection;
924
+ storyTarget?: EditorStoryTarget;
925
+ workItemId?: string;
926
+ label?: string;
927
+ domain?: "legal" | "commercial" | "finance" | "other";
928
+ }
929
+
930
+ export interface WorkflowWorkItem {
931
+ workItemId: string;
932
+ title: string;
933
+ description?: string;
934
+ domain?: "legal" | "commercial" | "finance" | "other";
935
+ status?: "pending" | "active" | "done" | "blocked";
936
+ scopeIds: string[];
937
+ }
938
+
939
+ export interface WorkflowOverlay {
940
+ overlayVersion: "workflow-overlay/1";
941
+ candidates?: WorkflowCandidateRange[];
942
+ scopes: WorkflowScope[];
943
+ workItems?: WorkflowWorkItem[];
944
+ activeWorkItemId?: string | null;
945
+ }
946
+
947
+ export interface WorkflowBlockedCommandReason {
948
+ code:
949
+ | "outside_workflow_scope"
950
+ | "workflow_comment_only"
951
+ | "workflow_view_only"
952
+ | "workflow_preserve_only"
953
+ | "workflow_blocked_import"
954
+ | "document_read_only"
955
+ | "document_viewing_mode"
956
+ | "protected_range"
957
+ | "unsupported_surface";
958
+ message: string;
959
+ scopeId?: string;
960
+ workItemId?: string;
961
+ anchor?: EditorAnchorProjection;
962
+ storyTarget?: EditorStoryTarget;
963
+ }
964
+
965
+ export interface WorkflowScopeSnapshot {
966
+ overlayPresent: boolean;
967
+ activeWorkItemId?: string | null;
968
+ activeWorkItem?: WorkflowWorkItem;
969
+ scopes: WorkflowScope[];
970
+ candidates: WorkflowCandidateRange[];
971
+ blockedReasons: WorkflowBlockedCommandReason[];
972
+ }
973
+
974
+ export interface InteractionGuardSnapshot {
975
+ blockedReasons: WorkflowBlockedCommandReason[];
976
+ }
977
+
978
+ export type WorkflowMarkupKind =
979
+ | "highlight"
980
+ | "comment"
981
+ | "revision"
982
+ | "field"
983
+ | "protected_range"
984
+ | "opaque_fragment";
985
+
986
+ export interface WorkflowMarkupBase {
987
+ markupId: string;
988
+ kind: WorkflowMarkupKind;
989
+ anchor: EditorAnchorProjection;
990
+ storyTarget?: EditorStoryTarget;
991
+ label: string;
992
+ excerpt?: string;
993
+ }
994
+
995
+ export interface WorkflowHighlightMarkup extends WorkflowMarkupBase {
996
+ kind: "highlight";
997
+ color: string;
998
+ text: string;
999
+ }
1000
+
1001
+ export interface WorkflowCommentMarkup extends WorkflowMarkupBase {
1002
+ kind: "comment";
1003
+ commentId: string;
1004
+ status: CommentSidebarThreadSnapshot["status"];
1005
+ warningCount: number;
1006
+ entryCount: number;
1007
+ createdAt: string;
1008
+ createdBy: string;
1009
+ }
1010
+
1011
+ export interface WorkflowRevisionMarkup extends WorkflowMarkupBase {
1012
+ kind: "revision";
1013
+ revisionId: string;
1014
+ revisionKind: TrackedChangeEntrySnapshot["kind"];
1015
+ status: TrackedChangeEntrySnapshot["status"];
1016
+ actionability: TrackedChangeEntrySnapshot["actionability"];
1017
+ authorId: string;
1018
+ createdAt: string;
1019
+ preserveOnlyReason?: string;
1020
+ }
1021
+
1022
+ export interface WorkflowFieldMarkup extends WorkflowMarkupBase {
1023
+ kind: "field";
1024
+ fieldIndex: number;
1025
+ fieldFamily: FieldFamily;
1026
+ fieldTarget?: string;
1027
+ refreshStatus: FieldRefreshStatus;
1028
+ displayText: string;
1029
+ }
1030
+
1031
+ export interface WorkflowProtectedRangeMarkup extends WorkflowMarkupBase {
1032
+ kind: "protected_range";
1033
+ rangeId: string;
1034
+ enforced: boolean;
1035
+ enforcementReason: string;
1036
+ editorGroup?: string;
1037
+ editor?: string;
1038
+ }
1039
+
1040
+ export interface WorkflowOpaqueFragmentMarkup extends WorkflowMarkupBase {
1041
+ kind: "opaque_fragment";
1042
+ fragmentId: string;
1043
+ warningId: string;
1044
+ detail: string;
1045
+ blockedReasonCode: "workflow_preserve_only" | "workflow_blocked_import";
1046
+ }
1047
+
1048
+ export type WorkflowMarkupItem =
1049
+ | WorkflowHighlightMarkup
1050
+ | WorkflowCommentMarkup
1051
+ | WorkflowRevisionMarkup
1052
+ | WorkflowFieldMarkup
1053
+ | WorkflowProtectedRangeMarkup
1054
+ | WorkflowOpaqueFragmentMarkup;
1055
+
1056
+ export interface WorkflowMarkupSnapshot {
1057
+ totalCount: number;
1058
+ items: WorkflowMarkupItem[];
1059
+ highlights: WorkflowHighlightMarkup[];
1060
+ comments: WorkflowCommentMarkup[];
1061
+ revisions: WorkflowRevisionMarkup[];
1062
+ fields: WorkflowFieldMarkup[];
1063
+ protectedRanges: WorkflowProtectedRangeMarkup[];
1064
+ opaqueFragments: WorkflowOpaqueFragmentMarkup[];
1065
+ }
1066
+
1067
+ export interface WorkflowCandidateRangeOptions {
1068
+ kinds?: WorkflowMarkupKind[];
1069
+ includeDetached?: boolean;
1070
+ source?: WorkflowCandidateRange["source"];
447
1071
  }
448
1072
 
449
1073
  export type AutosaveState =
@@ -462,7 +1086,7 @@ export type WordReviewEditorEvent =
462
1086
  type: "ready";
463
1087
  documentId: string;
464
1088
  sessionId: string;
465
- source: "docx" | "snapshot" | "datastore" | "canonical";
1089
+ source: "docx" | "session" | "snapshot";
466
1090
  stats: DocumentStats;
467
1091
  compatibility: CompatibilityReport;
468
1092
  comments: CommentSidebarSnapshot;
@@ -499,6 +1123,11 @@ export type WordReviewEditorEvent =
499
1123
  documentId: string;
500
1124
  changeId: string;
501
1125
  }
1126
+ | {
1127
+ type: "story_changed";
1128
+ documentId: string;
1129
+ activeStory: EditorStoryTarget;
1130
+ }
502
1131
  | {
503
1132
  type: "warning_added";
504
1133
  documentId: string;
@@ -526,10 +1155,33 @@ export type WordReviewEditorEvent =
526
1155
  snapshot: PersistedEditorSnapshot;
527
1156
  isAutosave: boolean;
528
1157
  }
1158
+ | {
1159
+ type: "session_saved";
1160
+ documentId: string;
1161
+ sessionState: EditorSessionState;
1162
+ savedAt: string;
1163
+ isAutosave: boolean;
1164
+ }
529
1165
  | {
530
1166
  type: "export_completed";
531
1167
  documentId: string;
532
1168
  result: ExportResult;
1169
+ }
1170
+ | {
1171
+ type: "workflow_overlay_changed";
1172
+ documentId: string;
1173
+ snapshot: WorkflowScopeSnapshot;
1174
+ }
1175
+ | {
1176
+ type: "workflow_active_work_item_changed";
1177
+ documentId: string;
1178
+ activeWorkItemId: string | null;
1179
+ }
1180
+ | {
1181
+ type: "command_blocked";
1182
+ documentId: string;
1183
+ command: string;
1184
+ reasons: WorkflowBlockedCommandReason[];
533
1185
  };
534
1186
 
535
1187
  export interface LoadResult {
@@ -546,6 +1198,16 @@ export interface SaveSnapshotResult {
546
1198
  savedAt: string;
547
1199
  }
548
1200
 
1201
+ export interface SaveSessionParams {
1202
+ documentId: string;
1203
+ sessionState: EditorSessionState;
1204
+ isAutosave: boolean;
1205
+ }
1206
+
1207
+ export interface SaveSessionResult {
1208
+ savedAt: string;
1209
+ }
1210
+
549
1211
  export interface SaveExportParams {
550
1212
  documentId: string;
551
1213
  result: ExportResult;
@@ -561,10 +1223,17 @@ export interface EditorTelemetryEvent {
561
1223
  detail?: Record<string, unknown>;
562
1224
  }
563
1225
 
1226
+ export interface EditorHostAdapter {
1227
+ load?(params: LoadRequest): Promise<LoadResult>;
1228
+ saveSession?(params: SaveSessionParams): Promise<SaveSessionResult>;
1229
+ saveExport?(params: SaveExportParams): Promise<SaveExportResult>;
1230
+ logEvent?(event: EditorTelemetryEvent): void;
1231
+ }
1232
+
564
1233
  export interface EditorDatastoreAdapter {
565
- load(params: { documentId: string }): Promise<LoadResult>;
1234
+ load(params: LoadRequest): Promise<LoadResult>;
566
1235
  saveSnapshot(params: SaveSnapshotParams): Promise<SaveSnapshotResult>;
567
- saveExport(params: SaveExportParams): Promise<SaveExportResult>;
1236
+ saveExport?(params: SaveExportParams): Promise<SaveExportResult>;
568
1237
  logEvent?(event: EditorTelemetryEvent): void;
569
1238
  }
570
1239
 
@@ -585,6 +1254,7 @@ export interface WordReviewEditorRef {
585
1254
  acceptAllChanges(): void;
586
1255
  rejectAllChanges(): void;
587
1256
  exportDocx(options?: ExportDocxOptions): Promise<ExportResult>;
1257
+ getSessionState(): EditorSessionState;
588
1258
  getSnapshot(): PersistedEditorSnapshot;
589
1259
  getRenderSnapshot(): RuntimeRenderSnapshot;
590
1260
  getCompatibilityReport(): CompatibilityReport;
@@ -595,6 +1265,7 @@ export interface WordReviewEditorRef {
595
1265
  getTrackedChanges(): TrackedChangesSnapshot;
596
1266
  isDirty(): boolean;
597
1267
  getFormattingState(): FormattingStateSnapshot;
1268
+ getStyleCatalog(): StyleCatalogSnapshot;
598
1269
  replaceText(text: string, target?: EditorAnchorProjection): void;
599
1270
  toggleBold(): void;
600
1271
  toggleItalic(): void;
@@ -607,6 +1278,8 @@ export interface WordReviewEditorRef {
607
1278
  setTextColor(color: string | null): void;
608
1279
  setHighlightColor(color: string | null): void;
609
1280
  setAlignment(alignment: FormattingAlignment): void;
1281
+ setParagraphStyle(styleId: string | null): void;
1282
+ setTableStyle(styleId: string | null): void;
610
1283
  indent(): void;
611
1284
  outdent(): void;
612
1285
  insertPageBreak(): void;
@@ -627,20 +1300,53 @@ export interface WordReviewEditorRef {
627
1300
  setSelection(selection: SelectionSnapshot | null): void;
628
1301
  scrollToRevision(revisionId: string): void;
629
1302
  scrollToComment(commentId: string): void;
1303
+ openStory(target: EditorStoryTarget): void;
1304
+ closeStory(): void;
1305
+ getPageLayoutSnapshot(): PageLayoutSnapshot | null;
1306
+ getDocumentNavigationSnapshot(): DocumentNavigationSnapshot;
1307
+ getFieldSnapshot(): FieldSnapshot;
1308
+ updateFields(options?: UpdateFieldsOptions): UpdateFieldsResult;
1309
+ updateTableOfContents(options?: TocRefreshOptions): TocRefreshResult;
1310
+ getViewState(): EditorViewStateSnapshot;
1311
+ setDocumentMode(mode: DocumentMode): void;
1312
+ getProtectionSnapshot(): ProtectionSnapshot;
1313
+ setWorkspaceMode(mode: WorkspaceMode): void;
1314
+ setZoom(level: ZoomLevel): void;
1315
+ insertSectionBreak(type: SectionBreakType, options?: { afterSectionIndex?: number }): void;
1316
+ deleteSectionBreak(sectionIndex: number): void;
1317
+ updateSectionLayout(sectionIndex: number, patch: SectionLayoutPatch): void;
1318
+ setSectionPageNumbering(
1319
+ sectionIndex: number,
1320
+ patch: SectionPageNumberingPatch | null,
1321
+ ): void;
1322
+ setHeaderFooterLink(sectionIndex: number, params: HeaderFooterLinkPatch): void;
1323
+ setImageLayout(mediaId: string, dimensions: { widthEmu: number; heightEmu: number }): void;
1324
+ setImageFrame(mediaId: string, offsets: { horizontalOffsetEmu?: number; verticalOffsetEmu?: number }): void;
1325
+ setWorkflowOverlay(overlay: WorkflowOverlay): void;
1326
+ clearWorkflowOverlay(): void;
1327
+ getWorkflowScopeSnapshot(): WorkflowScopeSnapshot | null;
1328
+ getInteractionGuardSnapshot(): InteractionGuardSnapshot;
1329
+ getWorkflowMarkupSnapshot(): WorkflowMarkupSnapshot;
1330
+ getWorkflowCandidateRanges(options?: WorkflowCandidateRangeOptions): WorkflowCandidateRange[];
1331
+ replaceWorkflowMarkupText(markupId: string, text: string): void;
630
1332
  }
631
1333
 
632
1334
  export interface WordReviewEditorProps {
633
1335
  documentId: string;
634
1336
  currentUser: EditorUser;
635
1337
  initialDocx?: Uint8Array | ArrayBuffer;
1338
+ initialSessionState?: EditorSessionState;
636
1339
  initialSnapshot?: PersistedEditorSnapshot;
637
1340
  initialSourceLabel?: string;
638
1341
  externalDocumentRevision?: string;
639
1342
  externalDocSource?: ExternalDocumentSource;
1343
+ loadRevision?: string;
1344
+ loadSourcePolicy?: LoadSourcePolicy;
640
1345
  readOnly?: boolean;
641
1346
  reviewMode?: "editing" | "review";
642
1347
  markupDisplay?: "clean" | "simple" | "all";
643
1348
  showReviewPanel?: boolean;
1349
+ hostAdapter?: EditorHostAdapter;
644
1350
  datastore?: EditorDatastoreAdapter;
645
1351
  autosave?: AutosaveConfig;
646
1352
  onEvent?: (event: WordReviewEditorEvent) => void;