@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.
Files changed (74) 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 +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
package/README.md CHANGED
@@ -39,9 +39,15 @@ import { WordReviewEditor } from "@beyondwork/docx-react-component";
39
39
 
40
40
  `WordReviewEditor` remains uncontrolled by default:
41
41
 
42
- - host passes `initialDocx` or `initialSnapshot`
42
+ - host passes `initialDocx`, `initialSessionState`, or `initialSnapshot`, or provides `hostAdapter.load()` / `datastore.load()`
43
43
  - runtime owns the live working session
44
- - host receives events, warnings, errors, snapshots, and exported artifacts
44
+ - host receives events, warnings, errors, session state, compatibility snapshots, and exported artifacts
45
+
46
+ Persistence direction:
47
+
48
+ - `EditorSessionState` is the canonical host-facing live-session contract
49
+ - `EditorHostAdapter` is the preferred persistence boundary for `load`, `saveSession`, `saveExport`, and telemetry
50
+ - `EditorDatastoreAdapter` remains the legacy snapshot bridge for hosts that still persist `PersistedEditorSnapshot`
45
51
 
46
52
  Snapshot/export note:
47
53
 
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@beyondwork/docx-react-component",
3
3
  "publisher": "beyondwork",
4
- "version": "1.0.18",
4
+ "version": "1.0.19",
5
5
  "description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
6
- "packageManager": "pnpm@10.30.3",
7
6
  "type": "module",
8
7
  "sideEffects": [
9
8
  "**/*.css"
@@ -81,29 +80,6 @@
81
80
  "./ui-tailwind/theme/editor-theme.css": "./src/ui-tailwind/theme/editor-theme.css"
82
81
  },
83
82
  "types": "./src/index.ts",
84
- "scripts": {
85
- "build": "tsup",
86
- "test": "bash scripts/run-workspace-tests.sh",
87
- "test:repo": "node scripts/run-repo-tests.mjs core",
88
- "test:repo:all": "node scripts/run-repo-tests.mjs all",
89
- "test:repo:optional": "node scripts/run-repo-tests.mjs optional",
90
- "test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
91
- "test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
92
- "lint": "pnpm run lint:no-authored-js && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
93
- "lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
94
- "lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
95
- "lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
96
- "context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
97
- "wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
98
- "wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
99
- "wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
100
- "wave:launch:managed": "bash scripts/wave-launch.sh",
101
- "wave:status": "bash scripts/wave-status.sh",
102
- "wave:watch": "bash scripts/wave-watch.sh --follow",
103
- "wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
104
- "wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
105
- "harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
106
- },
107
83
  "keywords": [
108
84
  "docx",
109
85
  "word",
@@ -166,14 +142,28 @@
166
142
  "tsup": "^8.3.0",
167
143
  "tsx": "^4.21.0"
168
144
  },
169
- "pnpm": {
170
- "onlyBuiltDependencies": [
171
- "esbuild",
172
- "sharp"
173
- ],
174
- "overrides": {
175
- "react": "19.2.4",
176
- "react-dom": "19.2.4"
177
- }
145
+ "scripts": {
146
+ "build": "tsup",
147
+ "test": "bash scripts/run-workspace-tests.sh",
148
+ "test:repo": "node scripts/run-repo-tests.mjs core",
149
+ "test:repo:all": "node scripts/run-repo-tests.mjs all",
150
+ "test:repo:optional": "node scripts/run-repo-tests.mjs optional",
151
+ "test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
152
+ "test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
153
+ "lint": "pnpm run lint:no-authored-js && pnpm run lint:docs-contracts && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
154
+ "lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
155
+ "lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
156
+ "lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
157
+ "lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
158
+ "context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
159
+ "wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
160
+ "wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
161
+ "wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
162
+ "wave:launch:managed": "bash scripts/wave-launch.sh",
163
+ "wave:status": "bash scripts/wave-status.sh",
164
+ "wave:watch": "bash scripts/wave-watch.sh --follow",
165
+ "wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
166
+ "wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
167
+ "harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
178
168
  }
179
169
  }
package/src/api/README.md CHANGED
@@ -5,7 +5,8 @@ Public TypeScript contracts for `WordReviewEditor` belong here.
5
5
  This layer should expose:
6
6
 
7
7
  - component props and ref types
8
- - datastore adapter interfaces
8
+ - session-state and snapshot compatibility types
9
+ - host adapter and datastore adapter interfaces
9
10
  - runtime-derived position and selection projection types used by the public API
10
11
  - discriminated event unions
11
12
  - warning and error payloads
@@ -18,5 +19,8 @@ Frozen naming and boundary rules:
18
19
 
19
20
  - the shipped component name is `WordReviewEditor`
20
21
  - `DocumentRuntime` is an internal runtime contract, not a public React component name
22
+ - `EditorSessionState` is the canonical host-facing live-session contract
23
+ - `PersistedEditorSnapshot` is the legacy/store compatibility envelope
24
+ - `EditorHostAdapter` is the preferred persistence boundary; `EditorDatastoreAdapter` remains the snapshot-compatible bridge
21
25
  - public range references are canonical-position projections, not DOM-path handles
22
26
  - render snapshots stay in `src/runtime`; `src/api` only exposes persisted host-facing snapshot types
@@ -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;