@beyondwork/docx-react-component 1.0.41 → 1.0.42

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 (88) hide show
  1. package/package.json +13 -1
  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/external-custody-types.ts +74 -0
  6. package/src/api/participants-types.ts +18 -0
  7. package/src/api/public-types.ts +347 -4
  8. package/src/api/scope-metadata-resolver-types.ts +88 -0
  9. package/src/core/commands/formatting-commands.ts +1 -1
  10. package/src/core/commands/index.ts +568 -1
  11. package/src/index.ts +118 -1
  12. package/src/io/export/escape-xml-attribute.ts +26 -0
  13. package/src/io/export/external-send.ts +188 -0
  14. package/src/io/export/serialize-comments.ts +13 -16
  15. package/src/io/export/serialize-footnotes.ts +17 -24
  16. package/src/io/export/serialize-headers-footers.ts +17 -24
  17. package/src/io/export/serialize-main-document.ts +59 -62
  18. package/src/io/export/serialize-numbering.ts +20 -27
  19. package/src/io/export/serialize-runtime-revisions.ts +2 -9
  20. package/src/io/export/serialize-tables.ts +8 -15
  21. package/src/io/export/table-properties-xml.ts +25 -32
  22. package/src/io/import/external-reimport.ts +40 -0
  23. package/src/io/ooxml/bw-xml.ts +244 -0
  24. package/src/io/ooxml/canonicalize-payload.ts +301 -0
  25. package/src/io/ooxml/comment-negotiation-payload.ts +288 -0
  26. package/src/io/ooxml/comment-presentation-payload.ts +311 -0
  27. package/src/io/ooxml/external-custody-payload.ts +102 -0
  28. package/src/io/ooxml/participants-payload.ts +97 -0
  29. package/src/io/ooxml/payload-signature.ts +112 -0
  30. package/src/io/ooxml/workflow-payload-validator.ts +271 -0
  31. package/src/io/ooxml/workflow-payload.ts +146 -7
  32. package/src/runtime/awareness-identity.ts +173 -0
  33. package/src/runtime/collab/event-types.ts +27 -0
  34. package/src/runtime/collab-session-bridge.ts +157 -0
  35. package/src/runtime/collab-session-facet.ts +193 -0
  36. package/src/runtime/collab-session.ts +273 -0
  37. package/src/runtime/comment-negotiation-sync.ts +91 -0
  38. package/src/runtime/comment-negotiation.ts +158 -0
  39. package/src/runtime/comment-presentation.ts +223 -0
  40. package/src/runtime/document-runtime.ts +280 -93
  41. package/src/runtime/external-send-runtime.ts +117 -0
  42. package/src/runtime/layout/docx-font-loader.ts +11 -30
  43. package/src/runtime/layout/inert-layout-facet.ts +2 -0
  44. package/src/runtime/layout/layout-engine-instance.ts +122 -12
  45. package/src/runtime/layout/page-graph.ts +79 -7
  46. package/src/runtime/layout/paginated-layout-engine.ts +230 -34
  47. package/src/runtime/layout/public-facet.ts +185 -13
  48. package/src/runtime/layout/table-row-split.ts +316 -0
  49. package/src/runtime/markdown-sanitizer.ts +132 -0
  50. package/src/runtime/participants.ts +134 -0
  51. package/src/runtime/resign-payload.ts +120 -0
  52. package/src/runtime/tamper-gate.ts +157 -0
  53. package/src/runtime/workflow-markup.ts +9 -0
  54. package/src/runtime/workflow-rail-segments.ts +244 -5
  55. package/src/ui/WordReviewEditor.tsx +587 -0
  56. package/src/ui/editor-runtime-boundary.ts +1 -0
  57. package/src/ui/editor-shell-view.tsx +11 -0
  58. package/src/ui-tailwind/chrome/chrome-preset-model.ts +28 -0
  59. package/src/ui-tailwind/chrome/collab-audience-chip.tsx +73 -0
  60. package/src/ui-tailwind/chrome/collab-negotiation-action-bar.tsx +244 -0
  61. package/src/ui-tailwind/chrome/collab-presence-strip.tsx +150 -0
  62. package/src/ui-tailwind/chrome/collab-role-chip.tsx +62 -0
  63. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +68 -0
  64. package/src/ui-tailwind/chrome/collab-send-to-supplier-modal.tsx +149 -0
  65. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +68 -0
  66. package/src/ui-tailwind/chrome/forward-non-drag-click.ts +104 -0
  67. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +1 -0
  68. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +7 -1
  69. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +1 -38
  70. package/src/ui-tailwind/chrome-overlay/index.ts +6 -0
  71. package/src/ui-tailwind/chrome-overlay/scope-card-role-model.ts +78 -0
  72. package/src/ui-tailwind/chrome-overlay/scope-keyboard-cycle.ts +49 -0
  73. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +58 -0
  74. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +527 -0
  75. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +120 -22
  76. package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +310 -32
  77. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +93 -14
  78. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -1
  79. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +86 -3
  80. package/src/ui-tailwind/editor-surface/pm-schema.ts +15 -13
  81. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +20 -3
  82. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +15 -14
  83. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +66 -0
  84. package/src/ui-tailwind/index.ts +32 -0
  85. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +29 -3
  86. package/src/ui-tailwind/status/tw-status-bar.tsx +52 -1
  87. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  88. package/src/ui-tailwind/tw-review-workspace.tsx +293 -34
@@ -3,6 +3,19 @@ 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
+
13
+ export type {
14
+ MetadataPersistenceMode,
15
+ ScopeMetadataPersistence,
16
+ ScopeMetadataResolver,
17
+ ScopeMetadataStorageRef,
18
+ };
6
19
 
7
20
  export type { CanonicalParagraphFormatting, CanonicalRunFormatting };
8
21
 
@@ -360,6 +373,38 @@ export interface SuggestionEntrySnapshot {
360
373
  preserveOnlyReason?: string;
361
374
  excerpt?: string;
362
375
  detail?: string;
376
+ /**
377
+ * R3 — scope-card-overlay P2. When present, links this entry to a
378
+ * `SuggestionGroup` via `SuggestionsSnapshot.groups[].groupId`.
379
+ * A single issue may fan out to multiple suggestions via the group.
380
+ */
381
+ groupId?: string;
382
+ /**
383
+ * R3 — origin of the suggestion's rationale (used by the card's
384
+ * suggestion row for attribution hints).
385
+ */
386
+ rationaleSource?: "playbook" | "agent" | "host" | "user";
387
+ /**
388
+ * R3 — tier within a preferred/fallback_1/fallback_2/escalate_only
389
+ * ladder. Hosts render this as the chip label in the scope card.
390
+ */
391
+ tier?: "preferred" | "fallback_1" | "fallback_2" | "escalate_only";
392
+ }
393
+
394
+ /**
395
+ * R3 — scope-card-overlay P2. Groups `SuggestionEntrySnapshot`
396
+ * entries under a single issue so the scope card exposes atomic
397
+ * accept/reject actions. One issue → N suggestions → N revisions on
398
+ * accept, committed as a single runtime transaction.
399
+ */
400
+ export interface SuggestionGroup {
401
+ groupId: string;
402
+ issueId?: string;
403
+ ruleId?: string;
404
+ rationale?: string;
405
+ severity?: IssueSeverity;
406
+ tier?: "preferred" | "fallback_1" | "fallback_2" | "escalate_only";
407
+ suggestionIds: string[];
363
408
  }
364
409
 
365
410
  export interface SuggestionsSnapshot {
@@ -371,6 +416,8 @@ export interface SuggestionsSnapshot {
371
416
  rejectedSuggestionIds: string[];
372
417
  detachedSuggestionIds: string[];
373
418
  suggestions: SuggestionEntrySnapshot[];
419
+ /** R3 — optional groups keying into `suggestions` by `groupId`. */
420
+ groups?: SuggestionGroup[];
374
421
  }
375
422
 
376
423
  export type FormattingAlignment = "left" | "center" | "right" | "justify";
@@ -1622,12 +1669,31 @@ export interface WorkflowScope {
1622
1669
  domain?: "legal" | "commercial" | "finance" | "other";
1623
1670
  metadataRefs?: string[];
1624
1671
  metadata?: WorkflowScopeMetadataField[];
1672
+ /**
1673
+ * Schema 1.1 — override the overlay default for this scope.
1674
+ * `"inherit"` defers to the overlay; absent is equivalent to
1675
+ * `"inherit"`.
1676
+ */
1677
+ metadataPersistence?: ScopeMetadataPersistence;
1625
1678
  }
1626
1679
 
1627
1680
  export interface WorkflowScopeMetadataField {
1628
1681
  key: string;
1629
1682
  valueType?: "string" | "number" | "boolean" | "json";
1630
1683
  value?: string | number | boolean | Record<string, unknown>;
1684
+ /**
1685
+ * Schema 1.1 — per-field override. Resolution order:
1686
+ * field > scope > overlay > "internal" default.
1687
+ */
1688
+ metadataPersistence?: ScopeMetadataPersistence;
1689
+ storageRef?: ScopeMetadataStorageRef;
1690
+ /**
1691
+ * Schema 1.1 — monotonically-increasing version hint. Internal-mode
1692
+ * writes bump this; external-mode writes read it from the
1693
+ * resolver's `publish` result. Used by readers to detect divergence
1694
+ * between embedded and resolver-returned values.
1695
+ */
1696
+ metadataVersion?: number;
1631
1697
  }
1632
1698
 
1633
1699
  export interface WorkflowWorkItem {
@@ -1645,6 +1711,12 @@ export interface WorkflowOverlay {
1645
1711
  scopes: WorkflowScope[];
1646
1712
  workItems?: WorkflowWorkItem[];
1647
1713
  activeWorkItemId?: string | null;
1714
+ /**
1715
+ * Schema 1.1 — overlay-level default for metadata persistence. When
1716
+ * absent, readers treat as `"internal"`. Per-scope / per-entry /
1717
+ * per-field overrides can opt in or out of the default.
1718
+ */
1719
+ metadataPersistence?: MetadataPersistenceMode;
1648
1720
  }
1649
1721
 
1650
1722
  export type WorkflowMetadataPersistence =
@@ -1670,6 +1742,23 @@ export interface WorkflowMetadataEntry {
1670
1742
  value?: Record<string, unknown>;
1671
1743
  scopeId?: string;
1672
1744
  workItemId?: string;
1745
+ /**
1746
+ * Schema 1.1 — per-entry override. `"inherit"` (or absent) defers
1747
+ * to the effective scope or overlay default.
1748
+ */
1749
+ metadataPersistence?: ScopeMetadataPersistence;
1750
+ /**
1751
+ * Schema 1.1 — opaque host reference when effective persistence
1752
+ * resolves to `"external"`. Absent when inline.
1753
+ */
1754
+ storageRef?: ScopeMetadataStorageRef;
1755
+ /**
1756
+ * Schema 1.1 — monotonically-increasing version hint. Internal-mode
1757
+ * writes bump this; external-mode writes read it from the
1758
+ * resolver's `publish` result. Used by readers to detect divergence
1759
+ * between embedded and resolver-returned values.
1760
+ */
1761
+ metadataVersion?: number;
1673
1762
  }
1674
1763
 
1675
1764
  export interface WorkflowMetadataSnapshot {
@@ -1748,6 +1837,41 @@ export type ScopeIssueAction =
1748
1837
  | "escalate"
1749
1838
  | "acknowledge";
1750
1839
 
1840
+ // ---------------------------------------------------------------------------
1841
+ // K1-light — review-action audit trail (scope-card-overlay P2)
1842
+ // ---------------------------------------------------------------------------
1843
+
1844
+ /**
1845
+ * Canonical metadata id for append-only review actions. Hosts push
1846
+ * one `WorkflowMetadataEntry` per review decision; the scope card's
1847
+ * timeline reads entries whose value matches the attached issue.
1848
+ *
1849
+ * Round-trip through `src/io/ooxml/workflow-payload.ts` lands with a
1850
+ * later K1 phase; for P2 the entries live in the session snapshot.
1851
+ */
1852
+ export const REVIEW_ACTION_METADATA_ID =
1853
+ "workflow.metadata.review_action" as const;
1854
+
1855
+ export type ReviewActionKind =
1856
+ | "include"
1857
+ | "edit"
1858
+ | "discard"
1859
+ | "escalate"
1860
+ | "acknowledge"
1861
+ | "resolve"
1862
+ | "waive";
1863
+
1864
+ export interface ReviewActionMetadataValue {
1865
+ reviewActionId: string;
1866
+ action: ReviewActionKind;
1867
+ actor: string;
1868
+ actorRole: IssueOwner | "agent";
1869
+ issueId?: string;
1870
+ suggestedRedlineId?: string;
1871
+ payload?: Record<string, unknown>;
1872
+ createdAt: string;
1873
+ }
1874
+
1751
1875
  /**
1752
1876
  * Scope card projection consumed by the chrome overlay's card layer.
1753
1877
  * Joins a `WorkflowScope` with its attached issue metadata (R2),
@@ -1765,11 +1889,19 @@ export interface ScopeCardModel {
1765
1889
  label: string;
1766
1890
  primaryAnchorRect: RenderFrameRect | null;
1767
1891
  issue?: IssueMetadataValue;
1768
- /** R3 suggestion groups attached to the scope. P2 populates; P1 = []. */
1892
+ /** Id-only lookup; `suggestionGroups` holds the full entries. */
1769
1893
  suggestionGroupIds: readonly string[];
1770
- /** K1 review-action count for the scope's issue. P2 populates; P1 = 0. */
1894
+ /** R3 full group entries attached to the scope's issue. */
1895
+ suggestionGroups: readonly SuggestionGroup[];
1896
+ /** K1 — count of review actions attached to the scope's issue. */
1771
1897
  reviewActionCount: number;
1772
- /** K2 agent-pending flag (overlapping WorkflowCandidateRange source:"ai"). P2 populates; P1 = false. */
1898
+ /** K1 — newest-first review-action entries for the card timeline. */
1899
+ reviewActions: readonly ReviewActionMetadataValue[];
1900
+ /**
1901
+ * K2 — true when a `WorkflowCandidateRange` with `source: "ai"`
1902
+ * overlaps the scope. Drives the rail-layer's agent-pending
1903
+ * shimmer.
1904
+ */
1773
1905
  agentPending: boolean;
1774
1906
  }
1775
1907
 
@@ -1863,6 +1995,25 @@ export interface WorkflowMetadataMarkup extends WorkflowMarkupBase {
1863
1995
  value?: Record<string, unknown>;
1864
1996
  scopeId?: string;
1865
1997
  workItemId?: string;
1998
+ /**
1999
+ * Schema 1.1 — mirrors `WorkflowMetadataEntry.metadataPersistence`.
2000
+ * Lets the card layer detect external entries without re-consulting the
2001
+ * entry snapshot. `"external"` means the inline `value` is empty and the
2002
+ * real value lives in the host rowstore; `"internal"` (or absent) means
2003
+ * `value` is the authoritative copy.
2004
+ */
2005
+ metadataPersistence?: ScopeMetadataPersistence;
2006
+ /**
2007
+ * Schema 1.1 — opaque host reference when `metadataPersistence === "external"`.
2008
+ * Passed verbatim to `ScopeMetadataResolver.resolve`; the editor never
2009
+ * parses this string.
2010
+ */
2011
+ storageRef?: ScopeMetadataStorageRef;
2012
+ /**
2013
+ * Schema 1.1 — monotonically-increasing version hint. Used by readers to
2014
+ * detect divergence between the embedded value and the resolver-returned one.
2015
+ */
2016
+ metadataVersion?: number;
1866
2017
  }
1867
2018
 
1868
2019
  export interface WorkflowCommentMarkup extends WorkflowMarkupBase {
@@ -2314,6 +2465,61 @@ export type WordReviewEditorEvent =
2314
2465
  scopeId: string;
2315
2466
  issueId: string;
2316
2467
  action: ScopeIssueAction;
2468
+ }
2469
+ | {
2470
+ /**
2471
+ * K2 — scope card "Ask review agent" was clicked. Host routes
2472
+ * the request to the CCEP contract review agent (or equivalent)
2473
+ * with the scope's anchor + story + surrounding text; when the
2474
+ * agent responds, the host pushes an issue + suggestion group
2475
+ * through the existing R2/R3 paths and clears any
2476
+ * `WorkflowCandidateRange` it used as the pending marker.
2477
+ */
2478
+ type: "agent-on-selection-requested";
2479
+ documentId: string;
2480
+ requestId: string;
2481
+ scopeId?: string;
2482
+ anchor: EditorAnchorProjection;
2483
+ storyTarget?: EditorStoryTarget;
2484
+ selectionText: string;
2485
+ surroundingText?: string;
2486
+ reviewSessionId?: string;
2487
+ }
2488
+ | {
2489
+ type: "metadata_persistence_mode_changed";
2490
+ documentId: string;
2491
+ mode: MetadataPersistenceMode;
2492
+ }
2493
+ | {
2494
+ /**
2495
+ * Emitted when a per-scope persistence override changes. `scopeId`
2496
+ * is `"*"` when `setAllScopesMetadataPersistence` fires the event.
2497
+ */
2498
+ type: "scope_metadata_persistence_changed";
2499
+ documentId: string;
2500
+ scopeId: string;
2501
+ persistence: ScopeMetadataPersistence;
2502
+ }
2503
+ | {
2504
+ /**
2505
+ * Emitted when the editor detects divergence between the
2506
+ * docx-embedded value / version and the resolver-returned
2507
+ * value / version. Host resolves via `resolveMetadataConflict`.
2508
+ */
2509
+ type: "metadata_conflict_detected";
2510
+ documentId: string;
2511
+ scopeId?: string;
2512
+ entryId?: string;
2513
+ fieldKey?: string;
2514
+ embedded: {
2515
+ value?: Record<string, unknown>;
2516
+ version?: number;
2517
+ } | null;
2518
+ external: {
2519
+ value?: Record<string, unknown>;
2520
+ version?: number;
2521
+ } | null;
2522
+ defaultPolicy: MetadataConflictPolicy;
2317
2523
  };
2318
2524
 
2319
2525
  export interface LoadResult {
@@ -2385,6 +2591,16 @@ export interface WordReviewEditorRef {
2385
2591
  rejectChange(changeId: string): void;
2386
2592
  acceptAllChanges(): void;
2387
2593
  rejectAllChanges(): void;
2594
+ /**
2595
+ * R3 — scope-card-overlay P2. Accept every `changeId` attached to
2596
+ * the group's `suggestionIds` as a single logical action. The
2597
+ * runtime batches the individual `acceptChange` calls inside one
2598
+ * commit boundary so the group lands atomically. No-op when the
2599
+ * group id is not present in the current `SuggestionsSnapshot`.
2600
+ */
2601
+ acceptSuggestionGroup(groupId: string): void;
2602
+ /** R3 — rejects every member of the group. */
2603
+ rejectSuggestionGroup(groupId: string): void;
2388
2604
  exportDocx(options?: ExportDocxOptions): Promise<ExportResult>;
2389
2605
  getSessionState(): EditorSessionState;
2390
2606
  getSnapshot(): PersistedEditorSnapshot;
@@ -2495,6 +2711,62 @@ export interface WordReviewEditorRef {
2495
2711
  setWorkflowMetadataEntries(entries: WorkflowMetadataEntry[]): void;
2496
2712
  clearWorkflowMetadataEntries(): void;
2497
2713
  getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
2714
+ /**
2715
+ * Schema 1.1 — set the overlay default for metadata persistence.
2716
+ * Author-only per collab-master-plan §7 role-gating matrix;
2717
+ * observers / reviewers receive `command_blocked` with
2718
+ * `collab_role_restricted`. Throws `MetadataResolverMissingError`
2719
+ * synchronously when called with `"external"` before a resolver has
2720
+ * been registered via `setScopeMetadataResolver`.
2721
+ */
2722
+ setMetadataPersistenceMode(mode: MetadataPersistenceMode): void;
2723
+ getMetadataPersistenceMode(): MetadataPersistenceMode;
2724
+ /**
2725
+ * Toggle a single scope's persistence override. Reviewers may toggle
2726
+ * scopes they own (`workItemId` matches their active work item);
2727
+ * authors may toggle any scope.
2728
+ */
2729
+ setScopeMetadataPersistence(
2730
+ scopeId: string,
2731
+ persistence: ScopeMetadataPersistence,
2732
+ ): void;
2733
+ getScopeMetadataPersistence(scopeId: string): ScopeMetadataPersistence;
2734
+ /**
2735
+ * Bulk toggle — apply `persistence` to every scope in the active
2736
+ * overlay. Author-only. `"inherit"` clears all per-scope overrides so
2737
+ * scopes fall back to the overlay default.
2738
+ */
2739
+ setAllScopesMetadataPersistence(persistence: ScopeMetadataPersistence): void;
2740
+ /**
2741
+ * Register (or clear) the host-supplied resolver. Must be set before
2742
+ * any external-mode operation; if absent, switching to `"external"`
2743
+ * throws `MetadataResolverMissingError` synchronously.
2744
+ */
2745
+ setScopeMetadataResolver(resolver: ScopeMetadataResolver | null): void;
2746
+ /**
2747
+ * Resolve a pending `metadata_conflict_detected` event. The host
2748
+ * picks a `choice` and the editor writes the chosen value back
2749
+ * through the normal overlay-mutation path + re-sign.
2750
+ */
2751
+ resolveMetadataConflict(input: ResolveMetadataConflictInput): void;
2752
+ /**
2753
+ * Migrate external-mode entries on the listed scopes back to
2754
+ * internal: for each entry / field whose effective persistence is
2755
+ * `"external"`, call `resolver.resolve(ref)`, inline the returned
2756
+ * value, clear `storageRef`, preserve `metadataVersion` as a hint.
2757
+ * Call this before exporting a docx outside the resolver's reach
2758
+ * (e.g., sending to an external supplier).
2759
+ */
2760
+ convertScopesToInternal(scopeIds: string[]): Promise<void>;
2761
+ /**
2762
+ * Migrate internal-mode entries on the listed scopes to external:
2763
+ * for each entry / field whose effective persistence is
2764
+ * `"internal"`, call `resolver.publish(...)`, set `storageRef` +
2765
+ * `metadataVersion` from the return value, clear the inline body.
2766
+ * Throws `MetadataResolverMissingError` when no resolver is
2767
+ * registered.
2768
+ */
2769
+ convertScopesToExternal(scopeIds: string[]): Promise<void>;
2498
2770
  setHostAnnotationOverlay(overlay: HostAnnotationOverlay): void;
2499
2771
  clearHostAnnotationOverlay(): void;
2500
2772
  getHostAnnotationSnapshot(): HostAnnotationSnapshot;
@@ -2547,7 +2819,8 @@ export type WordReviewEditorChromePreset =
2547
2819
  | "simple"
2548
2820
  | "advanced"
2549
2821
  | "review"
2550
- | "workflow";
2822
+ | "workflow"
2823
+ | "collab";
2551
2824
 
2552
2825
  export interface WordReviewEditorChromeOptions {
2553
2826
  /**
@@ -2566,6 +2839,26 @@ export interface WordReviewEditorChromeOptions {
2566
2839
  */
2567
2840
  showSectionTagAction: boolean;
2568
2841
  showReviewRail: boolean;
2842
+ /**
2843
+ * Collab preset (P9a). When `true` the chrome mounts the collab
2844
+ * top nav: presence strip, role + audience chips, tamper banner,
2845
+ * negotiation action bar, and send-to-supplier button. Requires a
2846
+ * `CollabSession` to be wired via the host; the components are
2847
+ * defensive and render empty / disabled when the session is
2848
+ * absent or detached.
2849
+ */
2850
+ showCollabTopNav?: boolean;
2851
+ /** Collab preset (P9b) — presence strip sub-visibility flag. */
2852
+ showCollabPresenceStrip?: boolean;
2853
+ /** Collab preset (P9c) — role + audience chips. */
2854
+ showCollabRoleChip?: boolean;
2855
+ showCollabAudienceChip?: boolean;
2856
+ /** Collab preset (P9d) — metadata-tamper banner. */
2857
+ showCollabTamperBanner?: boolean;
2858
+ /** Collab preset (P9e) — per-state negotiation action bar. */
2859
+ showCollabNegotiationActionBar?: boolean;
2860
+ /** Collab preset (P9f) — "Send to supplier" button + modal. */
2861
+ showCollabSendToSupplier?: boolean;
2569
2862
  }
2570
2863
 
2571
2864
  export interface WordReviewEditorProps {
@@ -2670,3 +2963,53 @@ export interface TextCommandAck {
2670
2963
  /** Tag touches the runtime applied so the lane can redraw decorations without a PM rebuild. */
2671
2964
  scopeTagTouches?: readonly ScopeTagTouch[];
2672
2965
  }
2966
+
2967
+ // ---------------------------------------------------------------------------
2968
+ // Schema 1.1 — metadata-persistence conflict types (P17 Task 3b)
2969
+ // ---------------------------------------------------------------------------
2970
+
2971
+ /**
2972
+ * How the host wants `metadata_conflict_detected` events resolved by
2973
+ * default. `"prompt"` means the host handles each event manually
2974
+ * (e.g., show a dialog); the other three let the editor compute a
2975
+ * default choice and suggest it in the event's `defaultPolicy` field.
2976
+ */
2977
+ export type MetadataConflictPolicy =
2978
+ | "prefer-embedded"
2979
+ | "prefer-external"
2980
+ | "prefer-latest"
2981
+ | "prompt";
2982
+
2983
+ /**
2984
+ * Input shape for `WordReviewEditorRef.resolveMetadataConflict`.
2985
+ * At least one of `scopeId` / `entryId` / `fieldKey` MUST be set — it
2986
+ * identifies which pending conflict to resolve. `choice` is the
2987
+ * host's final decision; `mergedValue` is only consulted when
2988
+ * `choice === "merge"`.
2989
+ */
2990
+ export interface ResolveMetadataConflictInput {
2991
+ scopeId?: string;
2992
+ entryId?: string;
2993
+ fieldKey?: string;
2994
+ choice: "embedded" | "external" | "merge";
2995
+ mergedValue?: Record<string, unknown>;
2996
+ }
2997
+
2998
+ // ---------------------------------------------------------------------------
2999
+ // Schema 1.1 — metadata-persistence errors (P17)
3000
+ // ---------------------------------------------------------------------------
3001
+
3002
+ /**
3003
+ * Thrown synchronously when a caller switches to `"external"` persistence
3004
+ * mode before registering a host resolver via `setScopeMetadataResolver`.
3005
+ * Follows the fail-closed policy in collab-master-plan §1.6.
3006
+ */
3007
+ export class MetadataResolverMissingError extends Error {
3008
+ constructor() {
3009
+ super(
3010
+ "setMetadataPersistenceMode('external') requires a resolver — call " +
3011
+ "setScopeMetadataResolver(...) first.",
3012
+ );
3013
+ this.name = "MetadataResolverMissingError";
3014
+ }
3015
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Metadata persistence mode — overlay-level default.
3
+ *
4
+ * - `"internal"`: every entry / field is inlined in the bw:workflowPayload
5
+ * XML body. Self-contained docx; no host callback required.
6
+ * - `"external"`: entries / fields are stored by reference via a host-owned
7
+ * ScopeMetadataResolver; the docx carries only a `storageRef` string.
8
+ *
9
+ * Unknown values → preserve-only (spec §8.2). Readers must never silently
10
+ * coerce to a known value.
11
+ */
12
+ export type MetadataPersistenceMode = "internal" | "external";
13
+
14
+ /**
15
+ * Per-scope / per-entry / per-field override. `"inherit"` defers to the
16
+ * parent (entry inherits scope; scope inherits overlay; overlay inherits
17
+ * the default `"internal"`).
18
+ */
19
+ export type ScopeMetadataPersistence = "internal" | "external" | "inherit";
20
+
21
+ /**
22
+ * Opaque host-owned reference for externally-stored metadata values. The
23
+ * editor never parses this string — it is passed verbatim to the
24
+ * resolver. Host examples:
25
+ *
26
+ * "clm-workblock-row:wb-123:row-42:v-3"
27
+ * "https://rowstore.internal/metadata/abc"
28
+ * "s3://bucket/tenant-1/scope-metadata/e91a.json"
29
+ */
30
+ export type ScopeMetadataStorageRef = string;
31
+
32
+ /**
33
+ * Host-supplied resolver. The editor calls `resolve` whenever it needs
34
+ * the actual value of an external entry / field; it calls `publish` when
35
+ * toggling a scope / entry from `"internal"` to `"external"` and needs
36
+ * to hand the current inline value off to the host's store.
37
+ *
38
+ * All methods are async so the host can make network calls. An
39
+ * `undefined` return from `resolve` signals a missing or revoked ref;
40
+ * the editor surfaces this as a non-fatal warning and renders the entry
41
+ * as detached in the scope card. `undefined` (rather than `null`)
42
+ * matches the shipped `ExternalCustodyResolver.restore` convention (P7).
43
+ *
44
+ * `delete` is optional — hosts that don't need cleanup on toggle may
45
+ * omit it. The editor only calls it when transitioning a scope from
46
+ * `"external"` back to `"internal"` (hand the value back inline, then
47
+ * release the ref).
48
+ */
49
+ export interface ScopeMetadataResolver {
50
+ /**
51
+ * Fetch the value for an opaque storageRef. Returns `undefined`
52
+ * when the ref is unknown or revoked — the editor surfaces this as
53
+ * a non-fatal warning and renders the entry as detached in the
54
+ * scope card. `version` (when present) is the rowstore's current
55
+ * version; the editor compares it against the docx's embedded
56
+ * `metadataVersion` hint and emits `metadata_conflict_detected` on
57
+ * mismatch. Matches the `ExternalCustodyResolver.restore`
58
+ * convention (P7) — prefer `undefined` over `null` for consistency.
59
+ */
60
+ resolve(ref: ScopeMetadataStorageRef): Promise<
61
+ | { value: Record<string, unknown>; version?: number }
62
+ | undefined
63
+ >;
64
+
65
+ /**
66
+ * Publish an inline value to the host store. `expectedVersion`
67
+ * (when set) enables optimistic concurrency: the host rejects the
68
+ * write if the stored row has advanced past the expected version,
69
+ * throwing a host-defined error that the editor surfaces via
70
+ * `metadata_conflict_detected`.
71
+ *
72
+ * Returns the opaque ref plus the version the store recorded for
73
+ * this write. The editor embeds this version on the XML element.
74
+ */
75
+ publish(input: {
76
+ scopeId: string;
77
+ metadataId: string;
78
+ entryId?: string;
79
+ fieldKey?: string;
80
+ value: Record<string, unknown>;
81
+ expectedVersion?: number;
82
+ }): Promise<{
83
+ ref: ScopeMetadataStorageRef;
84
+ version: number;
85
+ }>;
86
+
87
+ delete?(ref: ScopeMetadataStorageRef): Promise<void>;
88
+ }
@@ -188,7 +188,7 @@ type ToggleFormattingMark =
188
188
  | "strikethrough"
189
189
  | "superscript"
190
190
  | "subscript";
191
- type FormattingOperation =
191
+ export type FormattingOperation =
192
192
  | { type: "toggle"; mark: ToggleFormattingMark }
193
193
  | { type: "set-font-family"; fontFamily: string | null }
194
194
  | { type: "set-font-size"; size: number | null }