@beyondwork/docx-react-component 1.0.41 → 1.0.43

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 (118) hide show
  1. package/package.json +38 -37
  2. package/src/api/awareness-identity-types.ts +35 -0
  3. package/src/api/comment-negotiation-types.ts +130 -0
  4. package/src/api/comment-presentation-types.ts +106 -0
  5. package/src/api/editor-state-types.ts +110 -0
  6. package/src/api/external-custody-types.ts +74 -0
  7. package/src/api/participants-types.ts +18 -0
  8. package/src/api/public-types.ts +541 -5
  9. package/src/api/scope-metadata-resolver-types.ts +88 -0
  10. package/src/core/commands/formatting-commands.ts +1 -1
  11. package/src/core/commands/index.ts +601 -9
  12. package/src/core/search/search-text.ts +15 -2
  13. package/src/index.ts +131 -1
  14. package/src/io/docx-session.ts +672 -2
  15. package/src/io/export/escape-xml-attribute.ts +26 -0
  16. package/src/io/export/external-send.ts +188 -0
  17. package/src/io/export/serialize-comments.ts +13 -16
  18. package/src/io/export/serialize-footnotes.ts +17 -24
  19. package/src/io/export/serialize-headers-footers.ts +17 -24
  20. package/src/io/export/serialize-main-document.ts +59 -62
  21. package/src/io/export/serialize-numbering.ts +20 -27
  22. package/src/io/export/serialize-runtime-revisions.ts +2 -9
  23. package/src/io/export/serialize-tables.ts +8 -15
  24. package/src/io/export/table-properties-xml.ts +25 -32
  25. package/src/io/import/external-reimport.ts +40 -0
  26. package/src/io/load-scheduler.ts +230 -0
  27. package/src/io/normalize/normalize-text.ts +83 -0
  28. package/src/io/ooxml/bw-xml.ts +244 -0
  29. package/src/io/ooxml/canonicalize-payload.ts +301 -0
  30. package/src/io/ooxml/comment-negotiation-payload.ts +288 -0
  31. package/src/io/ooxml/comment-presentation-payload.ts +311 -0
  32. package/src/io/ooxml/external-custody-payload.ts +102 -0
  33. package/src/io/ooxml/participants-payload.ts +97 -0
  34. package/src/io/ooxml/payload-signature.ts +112 -0
  35. package/src/io/ooxml/workflow-payload-validator.ts +367 -0
  36. package/src/io/ooxml/workflow-payload.ts +317 -7
  37. package/src/runtime/awareness-identity.ts +173 -0
  38. package/src/runtime/collab/event-types.ts +27 -0
  39. package/src/runtime/collab-session-bridge.ts +157 -0
  40. package/src/runtime/collab-session-facet.ts +193 -0
  41. package/src/runtime/collab-session.ts +273 -0
  42. package/src/runtime/comment-negotiation-sync.ts +91 -0
  43. package/src/runtime/comment-negotiation.ts +158 -0
  44. package/src/runtime/comment-presentation.ts +223 -0
  45. package/src/runtime/document-runtime.ts +639 -124
  46. package/src/runtime/editor-state-channel.ts +544 -0
  47. package/src/runtime/editor-state-integration.ts +217 -0
  48. package/src/runtime/external-send-runtime.ts +117 -0
  49. package/src/runtime/layout/docx-font-loader.ts +11 -30
  50. package/src/runtime/layout/index.ts +2 -0
  51. package/src/runtime/layout/inert-layout-facet.ts +4 -0
  52. package/src/runtime/layout/layout-engine-instance.ts +139 -14
  53. package/src/runtime/layout/page-graph.ts +79 -7
  54. package/src/runtime/layout/paginated-layout-engine.ts +441 -48
  55. package/src/runtime/layout/public-facet.ts +585 -14
  56. package/src/runtime/layout/table-row-split.ts +316 -0
  57. package/src/runtime/markdown-sanitizer.ts +132 -0
  58. package/src/runtime/participants.ts +134 -0
  59. package/src/runtime/perf-counters.ts +28 -0
  60. package/src/runtime/render/render-frame-types.ts +17 -0
  61. package/src/runtime/render/render-kernel.ts +172 -29
  62. package/src/runtime/resign-payload.ts +120 -0
  63. package/src/runtime/surface-projection.ts +10 -5
  64. package/src/runtime/tamper-gate.ts +157 -0
  65. package/src/runtime/workflow-markup.ts +80 -16
  66. package/src/runtime/workflow-rail-segments.ts +244 -5
  67. package/src/ui/WordReviewEditor.tsx +654 -45
  68. package/src/ui/editor-command-bag.ts +14 -0
  69. package/src/ui/editor-runtime-boundary.ts +111 -11
  70. package/src/ui/editor-shell-view.tsx +21 -0
  71. package/src/ui/editor-surface-controller.tsx +5 -0
  72. package/src/ui/headless/selection-helpers.ts +10 -0
  73. package/src/ui-tailwind/chrome/chrome-preset-model.ts +28 -0
  74. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +62 -2
  75. package/src/ui-tailwind/chrome/collab-audience-chip.tsx +73 -0
  76. package/src/ui-tailwind/chrome/collab-negotiation-action-bar.tsx +244 -0
  77. package/src/ui-tailwind/chrome/collab-presence-strip.tsx +150 -0
  78. package/src/ui-tailwind/chrome/collab-role-chip.tsx +62 -0
  79. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +68 -0
  80. package/src/ui-tailwind/chrome/collab-send-to-supplier-modal.tsx +149 -0
  81. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +68 -0
  82. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +281 -0
  83. package/src/ui-tailwind/chrome/forward-non-drag-click.ts +104 -0
  84. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +1 -0
  85. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +7 -1
  86. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +1 -38
  87. package/src/ui-tailwind/chrome-overlay/index.ts +6 -0
  88. package/src/ui-tailwind/chrome-overlay/scope-card-role-model.ts +78 -0
  89. package/src/ui-tailwind/chrome-overlay/scope-keyboard-cycle.ts +49 -0
  90. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +106 -0
  91. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +527 -0
  92. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +120 -22
  93. package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +310 -32
  94. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +93 -14
  95. package/src/ui-tailwind/editor-surface/paste-plain-text.ts +72 -0
  96. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +118 -8
  97. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -1
  98. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +86 -3
  99. package/src/ui-tailwind/editor-surface/pm-schema.ts +167 -17
  100. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +35 -7
  101. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +20 -3
  102. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +265 -0
  103. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +10 -256
  104. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +9 -0
  105. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +66 -0
  106. package/src/ui-tailwind/index.ts +37 -1
  107. package/src/ui-tailwind/page-stack/tw-endnote-area.tsx +57 -0
  108. package/src/ui-tailwind/page-stack/tw-footnote-area.tsx +71 -0
  109. package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +73 -0
  110. package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +74 -0
  111. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +477 -0
  112. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +374 -0
  113. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +155 -0
  114. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +77 -16
  115. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +29 -3
  116. package/src/ui-tailwind/status/tw-status-bar.tsx +52 -1
  117. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  118. package/src/ui-tailwind/tw-review-workspace.tsx +455 -118
@@ -3,6 +3,47 @@ import type { CanonicalParagraphFormatting, CanonicalRunFormatting } from "../mo
3
3
  import type { WordReviewEditorLayoutFacet } from "../runtime/layout/public-facet.ts";
4
4
  import type { RenderFrameRect } from "../runtime/render/index.ts";
5
5
  import type { ScopeRailPosture } from "../runtime/workflow-rail-segments.ts";
6
+ import type {
7
+ MetadataPersistenceMode,
8
+ ScopeMetadataPersistence,
9
+ ScopeMetadataResolver,
10
+ ScopeMetadataStorageRef,
11
+ } from "./scope-metadata-resolver-types.ts";
12
+ import type {
13
+ EditorStateNamespace,
14
+ EditorStateLocation,
15
+ EditorStateResolveErrorMode,
16
+ EditorStatePolicyEntry,
17
+ EditorStatePolicy,
18
+ EditorStateStorageRef,
19
+ EditorStateBlob,
20
+ EditorStateResolver,
21
+ EditorStatePersister,
22
+ EditorStatePolicyMigration,
23
+ EditorStatePartLoadFailure,
24
+ EditorStatePartPersistFailure,
25
+ } from "./editor-state-types.ts";
26
+
27
+ export type {
28
+ MetadataPersistenceMode,
29
+ ScopeMetadataPersistence,
30
+ ScopeMetadataResolver,
31
+ ScopeMetadataStorageRef,
32
+ };
33
+ export type {
34
+ EditorStateNamespace,
35
+ EditorStateLocation,
36
+ EditorStateResolveErrorMode,
37
+ EditorStatePolicyEntry,
38
+ EditorStatePolicy,
39
+ EditorStateStorageRef,
40
+ EditorStateBlob,
41
+ EditorStateResolver,
42
+ EditorStatePersister,
43
+ EditorStatePolicyMigration,
44
+ EditorStatePartLoadFailure,
45
+ EditorStatePartPersistFailure,
46
+ };
6
47
 
7
48
  export type { CanonicalParagraphFormatting, CanonicalRunFormatting };
8
49
 
@@ -21,6 +62,8 @@ export type {
21
62
  PublicPageRegion,
22
63
  PublicPageRegions,
23
64
  PublicPageSpan,
65
+ PublicRegionBlock,
66
+ PublicRegionKind,
24
67
  PublicResolvedPageStories,
25
68
  PublicResolvedParagraphFormatting,
26
69
  PublicResolvedRunFormatting,
@@ -360,6 +403,38 @@ export interface SuggestionEntrySnapshot {
360
403
  preserveOnlyReason?: string;
361
404
  excerpt?: string;
362
405
  detail?: string;
406
+ /**
407
+ * R3 — scope-card-overlay P2. When present, links this entry to a
408
+ * `SuggestionGroup` via `SuggestionsSnapshot.groups[].groupId`.
409
+ * A single issue may fan out to multiple suggestions via the group.
410
+ */
411
+ groupId?: string;
412
+ /**
413
+ * R3 — origin of the suggestion's rationale (used by the card's
414
+ * suggestion row for attribution hints).
415
+ */
416
+ rationaleSource?: "playbook" | "agent" | "host" | "user";
417
+ /**
418
+ * R3 — tier within a preferred/fallback_1/fallback_2/escalate_only
419
+ * ladder. Hosts render this as the chip label in the scope card.
420
+ */
421
+ tier?: "preferred" | "fallback_1" | "fallback_2" | "escalate_only";
422
+ }
423
+
424
+ /**
425
+ * R3 — scope-card-overlay P2. Groups `SuggestionEntrySnapshot`
426
+ * entries under a single issue so the scope card exposes atomic
427
+ * accept/reject actions. One issue → N suggestions → N revisions on
428
+ * accept, committed as a single runtime transaction.
429
+ */
430
+ export interface SuggestionGroup {
431
+ groupId: string;
432
+ issueId?: string;
433
+ ruleId?: string;
434
+ rationale?: string;
435
+ severity?: IssueSeverity;
436
+ tier?: "preferred" | "fallback_1" | "fallback_2" | "escalate_only";
437
+ suggestionIds: string[];
363
438
  }
364
439
 
365
440
  export interface SuggestionsSnapshot {
@@ -371,6 +446,8 @@ export interface SuggestionsSnapshot {
371
446
  rejectedSuggestionIds: string[];
372
447
  detachedSuggestionIds: string[];
373
448
  suggestions: SuggestionEntrySnapshot[];
449
+ /** R3 — optional groups keying into `suggestions` by `groupId`. */
450
+ groups?: SuggestionGroup[];
374
451
  }
375
452
 
376
453
  export type FormattingAlignment = "left" | "center" | "right" | "justify";
@@ -706,7 +783,7 @@ export type SurfaceInlineSegment =
706
783
  textColor?: string;
707
784
  };
708
785
  hyperlinkHref?: string;
709
- /** Cascaded run formatting for this text segment (docDefaults → paragraph style chain → character style → direct). Added in Task 11. TODO Task 15: wire up from resolveEffectiveRunFormatting in surface-projection. */
786
+ /** Cascaded run formatting for this text segment (docDefaults → paragraph style chain → character style → direct). Populated in `src/runtime/surface-projection.ts` via `resolveEffectiveRunFormatting`. Present only when the resolved cascade has at least one field. */
710
787
  resolvedRunFormatting?: CanonicalRunFormatting;
711
788
  }
712
789
  | {
@@ -740,6 +817,14 @@ export type SurfaceInlineSegment =
740
817
  blockedReasonCode?: "workflow_preserve_only" | "workflow_blocked_import";
741
818
  presentation?: "inline-chip" | "quiet-marker" | "text-box" | "checkbox";
742
819
  displayText?: string;
820
+ /**
821
+ * For chart/SmartArt/WordArt/VML opaque inlines, the media-catalog id
822
+ * of the preview bitmap extracted from `mc:Fallback` (when Word cached
823
+ * one). Resolved by the surface into a blob URL and rendered by the
824
+ * corresponding PM atom so reviewers see the chart picture instead of
825
+ * a text badge. Absent when the object has no cached fallback image.
826
+ */
827
+ previewMediaId?: string;
743
828
  state: "locked-preserve-only";
744
829
  }
745
830
  | {
@@ -1622,12 +1707,31 @@ export interface WorkflowScope {
1622
1707
  domain?: "legal" | "commercial" | "finance" | "other";
1623
1708
  metadataRefs?: string[];
1624
1709
  metadata?: WorkflowScopeMetadataField[];
1710
+ /**
1711
+ * Schema 1.1 — override the overlay default for this scope.
1712
+ * `"inherit"` defers to the overlay; absent is equivalent to
1713
+ * `"inherit"`.
1714
+ */
1715
+ metadataPersistence?: ScopeMetadataPersistence;
1625
1716
  }
1626
1717
 
1627
1718
  export interface WorkflowScopeMetadataField {
1628
1719
  key: string;
1629
1720
  valueType?: "string" | "number" | "boolean" | "json";
1630
1721
  value?: string | number | boolean | Record<string, unknown>;
1722
+ /**
1723
+ * Schema 1.1 — per-field override. Resolution order:
1724
+ * field > scope > overlay > "internal" default.
1725
+ */
1726
+ metadataPersistence?: ScopeMetadataPersistence;
1727
+ storageRef?: ScopeMetadataStorageRef;
1728
+ /**
1729
+ * Schema 1.1 — monotonically-increasing version hint. Internal-mode
1730
+ * writes bump this; external-mode writes read it from the
1731
+ * resolver's `publish` result. Used by readers to detect divergence
1732
+ * between embedded and resolver-returned values.
1733
+ */
1734
+ metadataVersion?: number;
1631
1735
  }
1632
1736
 
1633
1737
  export interface WorkflowWorkItem {
@@ -1645,6 +1749,12 @@ export interface WorkflowOverlay {
1645
1749
  scopes: WorkflowScope[];
1646
1750
  workItems?: WorkflowWorkItem[];
1647
1751
  activeWorkItemId?: string | null;
1752
+ /**
1753
+ * Schema 1.1 — overlay-level default for metadata persistence. When
1754
+ * absent, readers treat as `"internal"`. Per-scope / per-entry /
1755
+ * per-field overrides can opt in or out of the default.
1756
+ */
1757
+ metadataPersistence?: MetadataPersistenceMode;
1648
1758
  }
1649
1759
 
1650
1760
  export type WorkflowMetadataPersistence =
@@ -1670,6 +1780,23 @@ export interface WorkflowMetadataEntry {
1670
1780
  value?: Record<string, unknown>;
1671
1781
  scopeId?: string;
1672
1782
  workItemId?: string;
1783
+ /**
1784
+ * Schema 1.1 — per-entry override. `"inherit"` (or absent) defers
1785
+ * to the effective scope or overlay default.
1786
+ */
1787
+ metadataPersistence?: ScopeMetadataPersistence;
1788
+ /**
1789
+ * Schema 1.1 — opaque host reference when effective persistence
1790
+ * resolves to `"external"`. Absent when inline.
1791
+ */
1792
+ storageRef?: ScopeMetadataStorageRef;
1793
+ /**
1794
+ * Schema 1.1 — monotonically-increasing version hint. Internal-mode
1795
+ * writes bump this; external-mode writes read it from the
1796
+ * resolver's `publish` result. Used by readers to detect divergence
1797
+ * between embedded and resolver-returned values.
1798
+ */
1799
+ metadataVersion?: number;
1673
1800
  }
1674
1801
 
1675
1802
  export interface WorkflowMetadataSnapshot {
@@ -1748,6 +1875,41 @@ export type ScopeIssueAction =
1748
1875
  | "escalate"
1749
1876
  | "acknowledge";
1750
1877
 
1878
+ // ---------------------------------------------------------------------------
1879
+ // K1-light — review-action audit trail (scope-card-overlay P2)
1880
+ // ---------------------------------------------------------------------------
1881
+
1882
+ /**
1883
+ * Canonical metadata id for append-only review actions. Hosts push
1884
+ * one `WorkflowMetadataEntry` per review decision; the scope card's
1885
+ * timeline reads entries whose value matches the attached issue.
1886
+ *
1887
+ * Round-trip through `src/io/ooxml/workflow-payload.ts` lands with a
1888
+ * later K1 phase; for P2 the entries live in the session snapshot.
1889
+ */
1890
+ export const REVIEW_ACTION_METADATA_ID =
1891
+ "workflow.metadata.review_action" as const;
1892
+
1893
+ export type ReviewActionKind =
1894
+ | "include"
1895
+ | "edit"
1896
+ | "discard"
1897
+ | "escalate"
1898
+ | "acknowledge"
1899
+ | "resolve"
1900
+ | "waive";
1901
+
1902
+ export interface ReviewActionMetadataValue {
1903
+ reviewActionId: string;
1904
+ action: ReviewActionKind;
1905
+ actor: string;
1906
+ actorRole: IssueOwner | "agent";
1907
+ issueId?: string;
1908
+ suggestedRedlineId?: string;
1909
+ payload?: Record<string, unknown>;
1910
+ createdAt: string;
1911
+ }
1912
+
1751
1913
  /**
1752
1914
  * Scope card projection consumed by the chrome overlay's card layer.
1753
1915
  * Joins a `WorkflowScope` with its attached issue metadata (R2),
@@ -1765,11 +1927,19 @@ export interface ScopeCardModel {
1765
1927
  label: string;
1766
1928
  primaryAnchorRect: RenderFrameRect | null;
1767
1929
  issue?: IssueMetadataValue;
1768
- /** R3 suggestion groups attached to the scope. P2 populates; P1 = []. */
1930
+ /** Id-only lookup; `suggestionGroups` holds the full entries. */
1769
1931
  suggestionGroupIds: readonly string[];
1770
- /** K1 review-action count for the scope's issue. P2 populates; P1 = 0. */
1932
+ /** R3 full group entries attached to the scope's issue. */
1933
+ suggestionGroups: readonly SuggestionGroup[];
1934
+ /** K1 — count of review actions attached to the scope's issue. */
1771
1935
  reviewActionCount: number;
1772
- /** K2 agent-pending flag (overlapping WorkflowCandidateRange source:"ai"). P2 populates; P1 = false. */
1936
+ /** K1 — newest-first review-action entries for the card timeline. */
1937
+ reviewActions: readonly ReviewActionMetadataValue[];
1938
+ /**
1939
+ * K2 — true when a `WorkflowCandidateRange` with `source: "ai"`
1940
+ * overlaps the scope. Drives the rail-layer's agent-pending
1941
+ * shimmer.
1942
+ */
1773
1943
  agentPending: boolean;
1774
1944
  }
1775
1945
 
@@ -1863,6 +2033,25 @@ export interface WorkflowMetadataMarkup extends WorkflowMarkupBase {
1863
2033
  value?: Record<string, unknown>;
1864
2034
  scopeId?: string;
1865
2035
  workItemId?: string;
2036
+ /**
2037
+ * Schema 1.1 — mirrors `WorkflowMetadataEntry.metadataPersistence`.
2038
+ * Lets the card layer detect external entries without re-consulting the
2039
+ * entry snapshot. `"external"` means the inline `value` is empty and the
2040
+ * real value lives in the host rowstore; `"internal"` (or absent) means
2041
+ * `value` is the authoritative copy.
2042
+ */
2043
+ metadataPersistence?: ScopeMetadataPersistence;
2044
+ /**
2045
+ * Schema 1.1 — opaque host reference when `metadataPersistence === "external"`.
2046
+ * Passed verbatim to `ScopeMetadataResolver.resolve`; the editor never
2047
+ * parses this string.
2048
+ */
2049
+ storageRef?: ScopeMetadataStorageRef;
2050
+ /**
2051
+ * Schema 1.1 — monotonically-increasing version hint. Used by readers to
2052
+ * detect divergence between the embedded value and the resolver-returned one.
2053
+ */
2054
+ metadataVersion?: number;
1866
2055
  }
1867
2056
 
1868
2057
  export interface WorkflowCommentMarkup extends WorkflowMarkupBase {
@@ -2148,6 +2337,25 @@ export interface AutosaveConfig {
2148
2337
  debounceMs?: number;
2149
2338
  }
2150
2339
 
2340
+ /**
2341
+ * Fired after a plain-text paste or drop has been dispatched through
2342
+ * the runtime-owned input callbacks. Rich-text paste (HTML, Office
2343
+ * clipboard) does NOT produce this event — those payloads still fire
2344
+ * `onBlockedInput` with a distinguishing message.
2345
+ *
2346
+ * See `docs/plans/editor-paste-drop.md` for the full semantics.
2347
+ */
2348
+ export interface WordReviewEditorPasteEvent {
2349
+ type: "paste_applied";
2350
+ documentId: string;
2351
+ /** Count of segments dispatched across text + split + hard_break. */
2352
+ segmentCount: number;
2353
+ /** Total character count across all text segments in the payload. */
2354
+ charCount: number;
2355
+ /** Whether the event originated from a paste or a drop. */
2356
+ source: "paste" | "drop";
2357
+ }
2358
+
2151
2359
  export type WordReviewEditorEvent =
2152
2360
  | {
2153
2361
  type: "ready";
@@ -2291,6 +2499,7 @@ export type WordReviewEditorEvent =
2291
2499
  command: string;
2292
2500
  reasons: WorkflowBlockedCommandReason[];
2293
2501
  }
2502
+ | WordReviewEditorPasteEvent
2294
2503
  | {
2295
2504
  /**
2296
2505
  * Scope card mode selector fired a mode change. Host relays to the
@@ -2314,8 +2523,137 @@ export type WordReviewEditorEvent =
2314
2523
  scopeId: string;
2315
2524
  issueId: string;
2316
2525
  action: ScopeIssueAction;
2526
+ }
2527
+ | {
2528
+ /**
2529
+ * K2 — scope card "Ask review agent" was clicked. Host routes
2530
+ * the request to the CCEP contract review agent (or equivalent)
2531
+ * with the scope's anchor + story + surrounding text; when the
2532
+ * agent responds, the host pushes an issue + suggestion group
2533
+ * through the existing R2/R3 paths and clears any
2534
+ * `WorkflowCandidateRange` it used as the pending marker.
2535
+ */
2536
+ type: "agent-on-selection-requested";
2537
+ documentId: string;
2538
+ requestId: string;
2539
+ scopeId?: string;
2540
+ anchor: EditorAnchorProjection;
2541
+ storyTarget?: EditorStoryTarget;
2542
+ selectionText: string;
2543
+ surroundingText?: string;
2544
+ reviewSessionId?: string;
2545
+ }
2546
+ | {
2547
+ type: "metadata_persistence_mode_changed";
2548
+ documentId: string;
2549
+ mode: MetadataPersistenceMode;
2550
+ }
2551
+ | {
2552
+ /**
2553
+ * Emitted when a per-scope persistence override changes. `scopeId`
2554
+ * is `"*"` when `setAllScopesMetadataPersistence` fires the event.
2555
+ */
2556
+ type: "scope_metadata_persistence_changed";
2557
+ documentId: string;
2558
+ scopeId: string;
2559
+ persistence: ScopeMetadataPersistence;
2560
+ }
2561
+ | {
2562
+ /**
2563
+ * Emitted when the editor detects divergence between the
2564
+ * docx-embedded value / version and the resolver-returned
2565
+ * value / version. Host resolves via `resolveMetadataConflict`.
2566
+ */
2567
+ type: "metadata_conflict_detected";
2568
+ documentId: string;
2569
+ scopeId?: string;
2570
+ entryId?: string;
2571
+ fieldKey?: string;
2572
+ embedded: {
2573
+ value?: Record<string, unknown>;
2574
+ version?: number;
2575
+ } | null;
2576
+ external: {
2577
+ value?: Record<string, unknown>;
2578
+ version?: number;
2579
+ } | null;
2580
+ defaultPolicy: MetadataConflictPolicy;
2581
+ }
2582
+ | {
2583
+ /**
2584
+ * Schema 1.2 — emitted when a keyed namespace fails to resolve
2585
+ * during load. Subsystem fallback behavior is per `onResolveError`
2586
+ * policy.
2587
+ */
2588
+ type: "editor_state_part_load_failed";
2589
+ documentId: string;
2590
+ failure: EditorStatePartLoadFailure;
2591
+ }
2592
+ | {
2593
+ /**
2594
+ * Schema 1.2 — emitted when a debounced persist call fails.
2595
+ * The editor retains the dirty snapshot for retry.
2596
+ */
2597
+ type: "editor_state_part_persist_failed";
2598
+ documentId: string;
2599
+ failure: EditorStatePartPersistFailure;
2600
+ }
2601
+ | {
2602
+ /**
2603
+ * Schema 1.2 — emitted when the saved location in a docx differs
2604
+ * from the current host policy; the current policy wins and the
2605
+ * next serialize honors it.
2606
+ */
2607
+ type: "editor_state_policy_migrated";
2608
+ documentId: string;
2609
+ migration: EditorStatePolicyMigration;
2610
+ }
2611
+ | {
2612
+ /**
2613
+ * Schema 1.2 — emitted when an unknown namespace is encountered
2614
+ * in the payload (forward-compat warning; preserved opaquely).
2615
+ */
2616
+ type: "editor_state_unknown_namespace";
2617
+ documentId: string;
2618
+ namespace: string;
2619
+ }
2620
+ | {
2621
+ /**
2622
+ * Emitted once per load-pipeline stage when the staged loader is
2623
+ * active. Purely observational — hosts can render a progress chip
2624
+ * or ignore entirely. See `docs/plans/fastload.md` for the stage
2625
+ * ordering contract.
2626
+ */
2627
+ type: "load-stage";
2628
+ documentId: string;
2629
+ stage: LoadStage;
2630
+ durationMs: number;
2631
+ }
2632
+ | {
2633
+ /**
2634
+ * Emitted once sub-parts (headers, footers, footnotes, endnotes,
2635
+ * theme, settings) have finished their post-ready idle hydration.
2636
+ * Before this event fires, `canonicalDocument.subParts.*` may be
2637
+ * empty arrays / defaults; after it fires, they carry the parsed
2638
+ * values. Calling `ensureSubPartsHydrated` eagerly forces
2639
+ * hydration synchronously.
2640
+ */
2641
+ type: "subparts-hydrated";
2642
+ documentId: string;
2317
2643
  };
2318
2644
 
2645
+ /**
2646
+ * Ordered list of stages emitted by the staged load pipeline. The
2647
+ * skeleton-ready stage corresponds to the `ready` event firing.
2648
+ */
2649
+ export type LoadStage =
2650
+ | "opc"
2651
+ | "body"
2652
+ | "styles-numbering-comments"
2653
+ | "skeleton-ready"
2654
+ | "subparts"
2655
+ | "compatibility";
2656
+
2319
2657
  export interface LoadResult {
2320
2658
  source?: ExternalDocumentSource;
2321
2659
  }
@@ -2385,6 +2723,16 @@ export interface WordReviewEditorRef {
2385
2723
  rejectChange(changeId: string): void;
2386
2724
  acceptAllChanges(): void;
2387
2725
  rejectAllChanges(): void;
2726
+ /**
2727
+ * R3 — scope-card-overlay P2. Accept every `changeId` attached to
2728
+ * the group's `suggestionIds` as a single logical action. The
2729
+ * runtime batches the individual `acceptChange` calls inside one
2730
+ * commit boundary so the group lands atomically. No-op when the
2731
+ * group id is not present in the current `SuggestionsSnapshot`.
2732
+ */
2733
+ acceptSuggestionGroup(groupId: string): void;
2734
+ /** R3 — rejects every member of the group. */
2735
+ rejectSuggestionGroup(groupId: string): void;
2388
2736
  exportDocx(options?: ExportDocxOptions): Promise<ExportResult>;
2389
2737
  getSessionState(): EditorSessionState;
2390
2738
  getSnapshot(): PersistedEditorSnapshot;
@@ -2495,6 +2843,91 @@ export interface WordReviewEditorRef {
2495
2843
  setWorkflowMetadataEntries(entries: WorkflowMetadataEntry[]): void;
2496
2844
  clearWorkflowMetadataEntries(): void;
2497
2845
  getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
2846
+ /**
2847
+ * Schema 1.1 — set the overlay default for metadata persistence.
2848
+ * Author-only per collab-master-plan §7 role-gating matrix;
2849
+ * observers / reviewers receive `command_blocked` with
2850
+ * `collab_role_restricted`. Throws `MetadataResolverMissingError`
2851
+ * synchronously when called with `"external"` before a resolver has
2852
+ * been registered via `setScopeMetadataResolver`.
2853
+ */
2854
+ setMetadataPersistenceMode(mode: MetadataPersistenceMode): void;
2855
+ getMetadataPersistenceMode(): MetadataPersistenceMode;
2856
+ /**
2857
+ * Toggle a single scope's persistence override. Reviewers may toggle
2858
+ * scopes they own (`workItemId` matches their active work item);
2859
+ * authors may toggle any scope.
2860
+ */
2861
+ setScopeMetadataPersistence(
2862
+ scopeId: string,
2863
+ persistence: ScopeMetadataPersistence,
2864
+ ): void;
2865
+ getScopeMetadataPersistence(scopeId: string): ScopeMetadataPersistence;
2866
+ /**
2867
+ * Bulk toggle — apply `persistence` to every scope in the active
2868
+ * overlay. Author-only. `"inherit"` clears all per-scope overrides so
2869
+ * scopes fall back to the overlay default.
2870
+ */
2871
+ setAllScopesMetadataPersistence(persistence: ScopeMetadataPersistence): void;
2872
+ /**
2873
+ * Register (or clear) the host-supplied resolver. Must be set before
2874
+ * any external-mode operation; if absent, switching to `"external"`
2875
+ * throws `MetadataResolverMissingError` synchronously.
2876
+ */
2877
+ setScopeMetadataResolver(resolver: ScopeMetadataResolver | null): void;
2878
+ /**
2879
+ * Resolve a pending `metadata_conflict_detected` event. The host
2880
+ * picks a `choice` and the editor writes the chosen value back
2881
+ * through the normal overlay-mutation path + re-sign.
2882
+ */
2883
+ resolveMetadataConflict(input: ResolveMetadataConflictInput): void;
2884
+ /**
2885
+ * Migrate external-mode entries on the listed scopes back to
2886
+ * internal: for each entry / field whose effective persistence is
2887
+ * `"external"`, call `resolver.resolve(ref)`, inline the returned
2888
+ * value, clear `storageRef`, preserve `metadataVersion` as a hint.
2889
+ * Call this before exporting a docx outside the resolver's reach
2890
+ * (e.g., sending to an external supplier).
2891
+ */
2892
+ convertScopesToInternal(scopeIds: string[]): Promise<void>;
2893
+ /**
2894
+ * Migrate internal-mode entries on the listed scopes to external:
2895
+ * for each entry / field whose effective persistence is
2896
+ * `"internal"`, call `resolver.publish(...)`, set `storageRef` +
2897
+ * `metadataVersion` from the return value, clear the inline body.
2898
+ * Throws `MetadataResolverMissingError` when no resolver is
2899
+ * registered.
2900
+ */
2901
+ convertScopesToExternal(scopeIds: string[]): Promise<void>;
2902
+ /**
2903
+ * Schema 1.2 — editor-state persistence channel. Configures which
2904
+ * subsystems persist in-document / rowstore / key-only. Each call
2905
+ * merges into the current policy map. Mismatched locations with an
2906
+ * already-loaded docx fire `editor_state_policy_migrated` and
2907
+ * schedule the next serialize to honor the new location.
2908
+ */
2909
+ configureEditorStatePolicy(policy: EditorStatePolicy): void;
2910
+ /**
2911
+ * Register the host's pull-resolver. Required before the editor
2912
+ * loads a 1.2 docx whose policy includes any keyed location.
2913
+ */
2914
+ registerEditorStateResolver(resolver: EditorStateResolver): void;
2915
+ /**
2916
+ * Register the host's persister. Required before any subsystem
2917
+ * mutation under keyed policy fires a debounced persist.
2918
+ */
2919
+ registerEditorStatePersister(persister: EditorStatePersister): void;
2920
+ /**
2921
+ * Return the entry key currently associated with a namespace under
2922
+ * keyed policy, or undefined when the namespace is in-document /
2923
+ * key not yet assigned.
2924
+ */
2925
+ getEditorStateKey(namespace: EditorStateNamespace): string | undefined;
2926
+ /**
2927
+ * Re-attempt any persists that failed since the last successful
2928
+ * write. Pass a namespace to scope the retry; omit to retry all.
2929
+ */
2930
+ retryPendingPersist(namespace?: EditorStateNamespace): Promise<void>;
2498
2931
  setHostAnnotationOverlay(overlay: HostAnnotationOverlay): void;
2499
2932
  clearHostAnnotationOverlay(): void;
2500
2933
  getHostAnnotationSnapshot(): HostAnnotationSnapshot;
@@ -2547,7 +2980,8 @@ export type WordReviewEditorChromePreset =
2547
2980
  | "simple"
2548
2981
  | "advanced"
2549
2982
  | "review"
2550
- | "workflow";
2983
+ | "workflow"
2984
+ | "collab";
2551
2985
 
2552
2986
  export interface WordReviewEditorChromeOptions {
2553
2987
  /**
@@ -2566,6 +3000,26 @@ export interface WordReviewEditorChromeOptions {
2566
3000
  */
2567
3001
  showSectionTagAction: boolean;
2568
3002
  showReviewRail: boolean;
3003
+ /**
3004
+ * Collab preset (P9a). When `true` the chrome mounts the collab
3005
+ * top nav: presence strip, role + audience chips, tamper banner,
3006
+ * negotiation action bar, and send-to-supplier button. Requires a
3007
+ * `CollabSession` to be wired via the host; the components are
3008
+ * defensive and render empty / disabled when the session is
3009
+ * absent or detached.
3010
+ */
3011
+ showCollabTopNav?: boolean;
3012
+ /** Collab preset (P9b) — presence strip sub-visibility flag. */
3013
+ showCollabPresenceStrip?: boolean;
3014
+ /** Collab preset (P9c) — role + audience chips. */
3015
+ showCollabRoleChip?: boolean;
3016
+ showCollabAudienceChip?: boolean;
3017
+ /** Collab preset (P9d) — metadata-tamper banner. */
3018
+ showCollabTamperBanner?: boolean;
3019
+ /** Collab preset (P9e) — per-state negotiation action bar. */
3020
+ showCollabNegotiationActionBar?: boolean;
3021
+ /** Collab preset (P9f) — "Send to supplier" button + modal. */
3022
+ showCollabSendToSupplier?: boolean;
2569
3023
  }
2570
3024
 
2571
3025
  export interface WordReviewEditorProps {
@@ -2573,6 +3027,38 @@ export interface WordReviewEditorProps {
2573
3027
  currentUser: EditorUser;
2574
3028
  ydoc?: import('yjs').Doc;
2575
3029
  awareness?: import("y-protocols/awareness").Awareness;
3030
+ /**
3031
+ * Optional collab session built via `createCollabSession(...)`. When
3032
+ * set, the `"collab"` chrome preset mounts the top nav (presence
3033
+ * strip, role + audience chips, tamper banner, negotiation action
3034
+ * bar, send-to-supplier button). P9a–f components are pure
3035
+ * presentational; this prop is what wires them to live state. Pass
3036
+ * `undefined` to keep the chrome in non-collab mode even when
3037
+ * `chromePreset === "collab"`.
3038
+ */
3039
+ collabSession?: import("../runtime/collab-session.ts").CollabSession;
3040
+ /**
3041
+ * Transport status signal for the presence strip + role chip (P9b /
3042
+ * P9c). Optional — defaults to "offline" when omitted.
3043
+ */
3044
+ collabTransportStatus?: "connected" | "syncing" | "offline";
3045
+ /**
3046
+ * Identifier for the currently-focused comment, used by the collab
3047
+ * top nav's audience chip + negotiation action bar (P9c / P9e). The
3048
+ * host derives this from its own selection / rail-focus signal.
3049
+ */
3050
+ activeCommentId?: string;
3051
+ /**
3052
+ * Baseline metadata for the send-to-supplier flow (P9f). Required
3053
+ * to enable the button; when omitted, the button stays present but
3054
+ * the confirmation modal becomes a no-op pass-through.
3055
+ */
3056
+ collabSendBaseline?: {
3057
+ originDocumentId: string;
3058
+ originPayloadId: string;
3059
+ originContentHash: string;
3060
+ payloadXml: string;
3061
+ };
2576
3062
  initialDocx?: Uint8Array | ArrayBuffer;
2577
3063
  initialSessionState?: EditorSessionState;
2578
3064
  initialSnapshot?: PersistedEditorSnapshot;
@@ -2670,3 +3156,53 @@ export interface TextCommandAck {
2670
3156
  /** Tag touches the runtime applied so the lane can redraw decorations without a PM rebuild. */
2671
3157
  scopeTagTouches?: readonly ScopeTagTouch[];
2672
3158
  }
3159
+
3160
+ // ---------------------------------------------------------------------------
3161
+ // Schema 1.1 — metadata-persistence conflict types (P17 Task 3b)
3162
+ // ---------------------------------------------------------------------------
3163
+
3164
+ /**
3165
+ * How the host wants `metadata_conflict_detected` events resolved by
3166
+ * default. `"prompt"` means the host handles each event manually
3167
+ * (e.g., show a dialog); the other three let the editor compute a
3168
+ * default choice and suggest it in the event's `defaultPolicy` field.
3169
+ */
3170
+ export type MetadataConflictPolicy =
3171
+ | "prefer-embedded"
3172
+ | "prefer-external"
3173
+ | "prefer-latest"
3174
+ | "prompt";
3175
+
3176
+ /**
3177
+ * Input shape for `WordReviewEditorRef.resolveMetadataConflict`.
3178
+ * At least one of `scopeId` / `entryId` / `fieldKey` MUST be set — it
3179
+ * identifies which pending conflict to resolve. `choice` is the
3180
+ * host's final decision; `mergedValue` is only consulted when
3181
+ * `choice === "merge"`.
3182
+ */
3183
+ export interface ResolveMetadataConflictInput {
3184
+ scopeId?: string;
3185
+ entryId?: string;
3186
+ fieldKey?: string;
3187
+ choice: "embedded" | "external" | "merge";
3188
+ mergedValue?: Record<string, unknown>;
3189
+ }
3190
+
3191
+ // ---------------------------------------------------------------------------
3192
+ // Schema 1.1 — metadata-persistence errors (P17)
3193
+ // ---------------------------------------------------------------------------
3194
+
3195
+ /**
3196
+ * Thrown synchronously when a caller switches to `"external"` persistence
3197
+ * mode before registering a host resolver via `setScopeMetadataResolver`.
3198
+ * Follows the fail-closed policy in collab-master-plan §1.6.
3199
+ */
3200
+ export class MetadataResolverMissingError extends Error {
3201
+ constructor() {
3202
+ super(
3203
+ "setMetadataPersistenceMode('external') requires a resolver — call " +
3204
+ "setScopeMetadataResolver(...) first.",
3205
+ );
3206
+ this.name = "MetadataResolverMissingError";
3207
+ }
3208
+ }