@beyondwork/docx-react-component 1.0.71 → 1.0.73

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 (87) hide show
  1. package/README.md +964 -75
  2. package/package.json +1 -1
  3. package/src/api/public-types.ts +280 -1
  4. package/src/api/v3/_create.ts +16 -1
  5. package/src/api/v3/_runtime-handle.ts +2 -0
  6. package/src/api/v3/ai/evaluate.ts +113 -0
  7. package/src/api/v3/ai/outline.ts +140 -0
  8. package/src/api/v3/ai/policy.ts +31 -0
  9. package/src/api/v3/ai/replacement.ts +8 -0
  10. package/src/api/v3/ai/review.ts +342 -0
  11. package/src/api/v3/ai/stats.ts +62 -0
  12. package/src/api/v3/runtime/viewport.ts +181 -0
  13. package/src/api/v3/runtime/workflow.ts +114 -1
  14. package/src/api/v3/ui/_types.ts +35 -0
  15. package/src/api/v3/ui/chrome-preset-model.ts +6 -0
  16. package/src/api/v3/ui/index.ts +1 -0
  17. package/src/api/v3/ui/viewport.ts +112 -0
  18. package/src/compare/diff-engine.ts +2 -0
  19. package/src/core/commands/formatting-commands.ts +1 -0
  20. package/src/core/commands/table-structure-commands.ts +1 -0
  21. package/src/core/state/editor-state.ts +49 -6
  22. package/src/io/export/serialize-footnotes.ts +6 -0
  23. package/src/io/export/serialize-headers-footers.ts +7 -0
  24. package/src/io/export/serialize-main-document.ts +20 -0
  25. package/src/io/export/serialize-paragraph-formatting.ts +34 -0
  26. package/src/io/export/split-review-boundaries.ts +1 -0
  27. package/src/io/normalize/normalize-text.ts +49 -2
  28. package/src/io/ooxml/parse-headers-footers.ts +31 -0
  29. package/src/io/ooxml/parse-main-document.ts +148 -7
  30. package/src/io/ooxml/parse-paragraph-formatting.ts +105 -0
  31. package/src/model/canonical-document.ts +401 -1
  32. package/src/runtime/formatting/formatting-context.ts +2 -1
  33. package/src/runtime/geometry/overlay-rects.ts +7 -10
  34. package/src/runtime/layout/layout-engine-version.ts +278 -1
  35. package/src/runtime/layout/paginated-layout-engine.ts +181 -8
  36. package/src/runtime/layout/resolved-formatting-state.ts +108 -13
  37. package/src/runtime/markdown-sanitizer.ts +21 -4
  38. package/src/runtime/render/render-kernel.ts +21 -1
  39. package/src/runtime/scopes/action-validation.ts +30 -4
  40. package/src/runtime/scopes/audit-bundle.ts +8 -0
  41. package/src/runtime/scopes/compiler-service.ts +1 -0
  42. package/src/runtime/scopes/enumerate-scopes.ts +61 -3
  43. package/src/runtime/scopes/replacement/apply.ts +50 -3
  44. package/src/runtime/scopes/scope-kinds/paragraph.ts +170 -7
  45. package/src/runtime/scopes/semantic-scope-types.ts +27 -0
  46. package/src/runtime/surface-projection.ts +77 -0
  47. package/src/runtime/workflow/coordinator.ts +3 -0
  48. package/src/runtime/workflow/scope-writer.ts +34 -0
  49. package/src/session/export/embedded-reconstitute.ts +37 -3
  50. package/src/session/import/embedded-offload.ts +26 -1
  51. package/src/session/import/loader-types.ts +18 -0
  52. package/src/session/import/loader.ts +2 -0
  53. package/src/shell/media-previews.ts +8 -6
  54. package/src/ui/WordReviewEditor.tsx +1 -0
  55. package/src/ui/editor-surface-controller.tsx +11 -0
  56. package/src/ui/headless/selection-helpers.ts +2 -2
  57. package/src/ui/runtime-shortcut-dispatch.ts +4 -4
  58. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +22 -4
  59. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +11 -11
  60. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +1 -1
  61. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +5 -0
  62. package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +18 -1
  63. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +22 -6
  64. package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +18 -1
  65. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +98 -3
  66. package/src/ui-tailwind/editor-surface/pm-schema.ts +50 -4
  67. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +6 -0
  68. package/src/ui-tailwind/editor-surface/scroll-anchor.ts +8 -1
  69. package/src/ui-tailwind/editor-surface/search-plugin.ts +2 -4
  70. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +114 -0
  71. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +12 -4
  72. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +29 -4
  73. package/src/ui-tailwind/index.ts +4 -2
  74. package/src/ui-tailwind/page-chrome-model.ts +5 -7
  75. package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +54 -34
  76. package/src/ui-tailwind/page-stack/tw-endnote-area.tsx +4 -1
  77. package/src/ui-tailwind/page-stack/tw-footnote-area.tsx +4 -1
  78. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +10 -1
  79. package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +8 -1
  80. package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +11 -1
  81. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +7 -1
  82. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +139 -10
  83. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +1 -1
  84. package/src/ui-tailwind/review-workspace/page-chrome.ts +4 -4
  85. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +1 -1
  86. package/src/ui-tailwind/theme/editor-theme.css +15 -16
  87. package/src/ui-tailwind/tw-review-workspace.tsx +22 -14
package/README.md CHANGED
@@ -11,136 +11,1025 @@ canonical: true
11
11
 
12
12
  [![CI](https://github.com/bwllaming/React-OOXML-Office/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/bwllaming/React-OOXML-Office/actions/workflows/ci.yml)
13
13
 
14
- Embeddable backoffice components for the [Beyond Work](https://beyondwork.ai) runtime. This repository ships **`@beyondwork/docx-react-component`** a fidelity-first React `.docx` editor for legal review, contract negotiation, and any workflow where documents round-trip through Microsoft Word without damage.
14
+ This repository builds **backoffice work components** for the [Beyond Work](https://beyondwork.ai) runtime — embeddable, composable modules that handle document editing, review workflows, data processing, and task automation inside enterprise workspaces.
15
15
 
16
- **Design principle human-AI parity.** Every capability in the UI has an equivalent, structured API. The agent and the user operate on the same runtime, the same commands, the same read models. There is no "agent-only" shortcut and no "human-only" feature.
16
+ The design principle across every component: **advanced tasks should be equally easy for AI agents and human users.** Every capability exposed through the UI is also available through a structured API. Every API is designed so an agent can inspect state, reason about it, and act on it without special accommodation. Humans and agents share the same runtime, the same commands, the same read models, and the same fidelity guarantees.
17
17
 
18
- ---
18
+ ## Use This README For
19
+
20
+ - confirming what is shipped today vs coming
21
+ - installing the package
22
+ - finding the right documentation lane quickly
23
+
24
+ Do not use this README as the full package contract. The canonical consumer path starts in `docs/README.md`, [`docs/reference/public-api.md`](docs/reference/public-api.md) (V1 + V2), [`docs/reference/public-api-3.md`](docs/reference/public-api-3.md) (V3 additive surface), and [`docs/reference/integration-guide.md`](docs/reference/integration-guide.md).
25
+
26
+ The target end-state architecture — 11-layer stack, Runtime/AI/UI API split, semantic scope compiler, and Phase 0–7 migration plan — is documented in [`docs/architecture/00-overview.md`](docs/architecture/00-overview.md). Read it before designing cross-cutting changes. The technical wiki under [`docs/wiki/`](docs/wiki/) has feature-level pages (API v1/v2/v3, runtime-truth, debugging, doc-debug-service-guide, chrome-composition / chrome-composition-backlog, testing-and-fixtures, etc.); navigate via [`docs/README.md`](docs/README.md).
27
+
28
+ ## Components
29
+
30
+ ### Document Editor (`@beyondwork/docx-react-component`)
31
+
32
+ A fidelity-first React `.docx` editor centered on `WordReviewEditor`. Built for legal review, contract negotiation, and any workflow where documents need to survive a round-trip through Microsoft Word without damage.
33
+
34
+ **Shipped capabilities:**
35
+ - Full tracked-change and comment support (add, resolve, accept, reject) — including Lane 7b X4.a/X4.b container + range revision markers and move-promotion
36
+ - Suggesting mode where every edit automatically becomes a tracked change
37
+ - Round-trip OOXML preservation — open, edit, export, reopen in Word without repair prompts; preserve-first unknown handling (Lanes 7b/7c closed)
38
+ - Workflow overlays with scope-based editing constraints, marker-backed scope identity, and three-way scope-source discrimination (marker-backed / overlay-only / detached)
39
+ - Runtime-backed read models for document structure, review state, selection, compatibility, and fields
40
+ - Document comparison with LCS-based diffing and redline export
41
+ - Legal document analysis (bookmarks, cross-references, defined terms, signature blocks)
42
+ - Real-time collaboration via Yjs — Lane 4 Phase C shipped (R1 remote-replay coalescing, R3 idle-priority abort signal, pairRuntimes migration)
43
+ - Layout engine proven at **97.77%** (615/629) external layout-structure truth and **96.34%** (606/629) rendered-word geometry over the CCEP contract corpus (see [`services/truth-baseline/`](services/truth-baseline/))
44
+ - Charts v1 — native SVG renderer for 7 chart families with progressive time-slicing (Lane 5 Stages 0–6 shipped)
45
+ - Debug substrate Phase 0 + Phase 1 — runtime projector + 12-channel `TelemetryBus` + `DebugInspectorSnapshot` + `UxResponse` event stream
46
+ - Additive V3 public surface — `createApiV3(runtime)` with Runtime + AI API split, 31 functions under a 4-status taxonomy (`live` / `live-with-adapter` / `partial` / `mock`); Phase P' closed 2026-04-21
47
+
48
+ **Human-AI parity in practice:**
49
+ - The human clicks "Add Comment" on selected text; the agent calls `ref.addComment({ anchor, body })` (V1) or `api.runtime.review.resolveComment(...)` (V3) on the same selection
50
+ - The human reviews tracked changes in the sidebar; the agent reads `getTrackedChanges()` (V1) or `api.runtime.review.getChanges()` (V3) and makes the same accept/reject decisions
51
+ - The human exports via toolbar; the agent calls `exportDocx()` (V1) or `api.runtime.document.export()` (V3) after the same compatibility check
52
+ - Workflow overlays constrain both humans and agents identically — `getInteractionGuardSnapshot()` / `api.runtime.workflow.getGuard()` is the single source of posture truth
53
+ - The AI action policy module gates 37 discrete agent operations with risk classification and context validation
54
+ - Every `uiVisible` V3 function emits exactly one `UxResponse` event on the `api` telemetry channel — the stream the Phase Q debug UX consumes to render mock-or-live visual traces identically
55
+
56
+ ### Workblocks
57
+
58
+ Composable task modules that automate business processes. Each workblock is a self-contained package of Python backend logic, React UI components, and a declarative task graph defined in `manifest.yaml`. They execute on the Beyond Work platform via Temporal workflows.
59
+
60
+ **Categories:**
61
+ - **Document processing** — invoice extraction, PDF template OCR, bulk processing, email-to-action
62
+ - **AI agents** — explore, flow debugging, data querying, task launching, authoring
63
+ - **Analytics** — execution history, company insights, timeline queries
64
+ - **Learning** — human correction capture, learning set curation, embedding management
65
+ - **Infrastructure** — registry, health checks, credential management (9 types)
66
+
67
+ **Human-AI parity in practice:**
68
+ - Users create workblocks through VS Code Studio with prompts and visual editing; authoring agents create them through the same manifest schema and file tools
69
+ - Users validate invoice fields in a React form; agents validate the same fields through typed task inputs/outputs
70
+ - Both humans and agents operate within the same workblock execution engine — same task graph, same data flow, same error handling
71
+
72
+ ## The Beyond Work Runtime
73
+
74
+ These components plug into a four-layer platform:
75
+
76
+ ```
77
+ User-Facing (Layer 4)
78
+ neui (Next.js 15), bport (BFF), MCP server, VS Code extension
79
+ |
80
+ Workblocks (Layer 3)
81
+ 20+ composable task modules: agents, data processing, credentials
82
+ |
83
+ Platform Services (Layer 2)
84
+ bworker (Go), modelproxy (LiteLLM), registry, pythonworker, Temporal
85
+ |
86
+ AWS Infrastructure (Layer 1)
87
+ EKS, Crossplane, Terraform, PostgreSQL, Redis, Qdrant, S3, SQS
88
+ ```
89
+
90
+ The document editor lives at Layer 4 as an embeddable surface. Workblocks live at Layer 3 and are orchestrated by the platform. Both expose their capabilities symmetrically to human UIs and agent APIs.
91
+
92
+ ## Shipped vs Coming
93
+
94
+ The shipped contract today is docx-first. The table below delineates what is ready to build on vs what is planned end-state work. For the full migration narrative, see [`docs/architecture/00-overview.md`](docs/architecture/00-overview.md) §"Migration Plan" (Phases 0–7).
95
+
96
+ | Area | Shipped today | Coming |
97
+ |---|---|---|
98
+ | Package runtime | `docx` — the only shipped runtime contract | `xlsx` planned; `pdf` future work outside current package boundary |
99
+ | Public API | V1 flat-method `WordReviewEditorRef` (`public-stable`) + V2 facet projections + dual-error `EditorDiagnostic` (`advanced-supported`) + V3 `createApiV3(runtime)` Runtime + AI split (`advanced-supported`; Phase P' 2026-04-21) | V3 UI API surface (Phase 3 remainder); semantic scope compiler (Phase 4); replacement-scope lifecycle (Phase 5); semantic-scope-first AI layer (Phase 6) |
100
+ | Middle stack | Canonical model (`src/model/canonical-document.ts`); runtime core (`src/runtime/document-runtime.ts`); layout engine (`src/runtime/layout/**`); render kernel MVP (`src/runtime/render/**`) | Formal `src/session/**` package/session boundary (Phase 1); formatting semantics layer (Phase 2); explicit geometry projection layer (Phase 2); semantic scope compiler (Phase 4) |
101
+ | Layout / geometry | External truth baseline proven at 97.77% layout / 96.34% rendered-word on 629 CCEP docs | First-class runtime-owned geometry contract for caret/hit-test/replacement envelopes |
102
+ | Debug substrate | Runtime projector + `TelemetryBus` (12 channels, 8 active/wired, 4 reserved) + `DebugInspectorSnapshot` + `UxResponse` stream + `wrapRefForTelemetry` (Phase 0 + Phase 1 shipped 2026-04-20) | `services/debug/` Railway HTTPS service (Phase 2 in progress); MCP proxy + dev-drawer retarget (Phase 3 planned); Phase Q debug UX at `src/ui-tailwind/debug/**` |
103
+ | External oracles | OpenXML SDK validator MCP (round-trip proof); LibreOffice-backed `services/truth-baseline` for layout + geometry; visual-smoke MCP for CCEP templates | Phase F full oracle integration: stage-token lineage, alignment keys, known-divergence register, deterministic environment |
104
+ | BW / CLM integration | Stateless DOCX runtime service at `vendor/beyondwork/`; Yjs transport; timeline/audit substrate | Grouped service families aligned with Runtime/AI API (Phase 7); scope-native CLM primitives replacing raw `blob_ref` + `story/start/end` glue |
105
+
106
+ The shipped primitives are correct for their respective layers; the target architecture widens the middle and tops the stack, without replacing anything below.
19
107
 
20
108
  ## Install
21
109
 
22
110
  ```bash
23
- pnpm add @beyondwork/docx-react-component
111
+ pnpm add @beyondwork/docx-react-component react react-dom tailwindcss \
112
+ prosemirror-commands prosemirror-keymap prosemirror-model \
113
+ prosemirror-state prosemirror-tables prosemirror-transform prosemirror-view
114
+ ```
115
+
116
+ Current packaging truth:
117
+
118
+ - the package is ESM-only
119
+ - exports point at shipped TypeScript source entry points
120
+ - `types` and subpath `types` entries resolve to the shipped source-backed TypeScript contracts
121
+ - consumers need a bundler or runtime that can resolve `.ts` and `.tsx` ESM imports
122
+ - consumers should import `@beyondwork/docx-react-component/ui-tailwind/theme/editor-theme.css` once and provide a Tailwind v4 CSS pipeline for it
123
+ - package and source identifiers remain docx-first until a deliberate rename lands
124
+
125
+ ## Shipped Product
126
+
127
+ The primary shipped surface is:
128
+
129
+ ```tsx
130
+ import { WordReviewEditor } from "@beyondwork/docx-react-component";
131
+
132
+ <WordReviewEditor />
133
+ ```
134
+
135
+ `WordReviewEditor` remains uncontrolled by default:
136
+
137
+ - host passes `initialDocx`, `initialSessionState`, or `initialSnapshot`, or provides `hostAdapter.load()` / `datastore.load()`
138
+ - runtime owns the live working session
139
+ - host receives events, warnings, errors, session state, compatibility snapshots, and exported artifacts
140
+
141
+ Persistence direction:
142
+
143
+ - `EditorSessionState` is the canonical host-facing live-session contract
144
+ - `EditorHostAdapter` is the preferred persistence boundary for `load`, `saveSession`, `saveExport`, and telemetry
145
+ - `EditorDatastoreAdapter` remains the legacy snapshot bridge for hosts that still persist `PersistedEditorSnapshot`
146
+
147
+ Snapshot/export note:
148
+
149
+ - when a session starts from a real `.docx`, persisted snapshots now carry embedded source-package provenance so later snapshot-origin `.docx` export can use the same package-backed exporter
150
+ - legacy snapshots without that provenance still load, but `.docx` export is intentionally blocked rather than falling back to a lossy minimal package
151
+
152
+ The current shipped ESM exports include:
153
+
154
+ - stable default entrypoints:
155
+ - `@beyondwork/docx-react-component`
156
+ - `@beyondwork/docx-react-component/public-types`
157
+ - `@beyondwork/docx-react-component/ui-tailwind`
158
+ - `@beyondwork/docx-react-component/legal`
159
+ - `@beyondwork/docx-react-component/compare`
160
+ - `@beyondwork/docx-react-component/ui-tailwind/theme/editor-theme.css`
161
+ - V3 public API (additive, Phase P' 2026-04-21):
162
+ - `@beyondwork/docx-react-component/api/v3` — `createApiV3(runtime)` + metadata primitives + `UxResponse`
163
+ - advanced-supported subpaths for wrapper teams and package-adjacent integrations:
164
+ - `@beyondwork/docx-react-component/runtime/document-runtime`
165
+ - `@beyondwork/docx-react-component/io/docx-session`
166
+ - `@beyondwork/docx-react-component/core/commands/*`
167
+ - `@beyondwork/docx-react-component/core/selection/mapping`
168
+ - `@beyondwork/docx-react-component/core/state/editor-state`
169
+ - `@beyondwork/docx-react-component/ui-tailwind/editor-surface/search-plugin`
170
+ - alias subpaths:
171
+ - `@beyondwork/docx-react-component/tailwind`
172
+ - `@beyondwork/docx-react-component/api/public-types`
173
+
174
+ Use [`docs/reference/public-api.md`](docs/reference/public-api.md) + `docs/reference/public-api.manifest.json` for the V1/V2 contract inventory, and [`docs/reference/public-api-3.md`](docs/reference/public-api-3.md) for the V3 surface. Stability guidance for each V3 namespace is in the `docs/wiki/api-v3.md` page (reached via `docs/README.md`).
175
+
176
+ ## Product Contract
177
+
178
+ For every component this repo ships, the standard is:
179
+
180
+ > Open -> edit -> save -> reopen in the host application without damage.
181
+
182
+ For the current shipped `docx` implementation, that specifically means:
183
+
184
+ - open in recent Microsoft Word without repair prompts
185
+ - preserve supported content and review structures
186
+ - preserve unsupported but preservable OOXML
187
+ - remain editable in Word after export
188
+
189
+ For the normative API and host contract, use:
190
+
191
+ - [`docs/reference/public-api.md`](docs/reference/public-api.md)
192
+ - [`docs/reference/integration-guide.md`](docs/reference/integration-guide.md)
193
+ - [`docs/reference/ooxml-compliance.md`](docs/reference/ooxml-compliance.md)
194
+
195
+ ## Documentation Map
196
+
197
+ ### Start Here
198
+
199
+ - [`docs/README.md`](docs/README.md)
200
+
201
+ ### Main Consumer Path
202
+
203
+ - [`docs/reference/quick-start.md`](docs/reference/quick-start.md)
204
+ - [`docs/reference/consumer-matrix.md`](docs/reference/consumer-matrix.md)
205
+ - [`docs/reference/public-api.md`](docs/reference/public-api.md) — V1 + V2 canonical contract
206
+ - [`docs/reference/public-api-3.md`](docs/reference/public-api-3.md) — V3 Runtime + AI API (additive)
207
+ - [`docs/reference/integration-guide.md`](docs/reference/integration-guide.md)
208
+ - [`docs/reference/glossary.md`](docs/reference/glossary.md)
209
+
210
+ ### Architecture & Mental Model
211
+
212
+ - [`docs/architecture/00-overview.md`](docs/architecture/00-overview.md) — target end-state: 11-layer stack, Runtime/AI/UI API split, semantic-scope compiler, Phase 0–7 migration plan
213
+ - `docs/wiki/runtime-truth.md` — canonical-truth narrative: package > model > runtime > PM view, inverted-PM model, 10 perf invariants (navigate via [`docs/README.md`](docs/README.md))
214
+ - `docs/wiki/future-architecture.md` — forward-looking design anchors + proof status
215
+
216
+ ### Wrapper And Agent Path
217
+
218
+ - [`docs/reference/consumer-matrix.md`](docs/reference/consumer-matrix.md)
219
+ - [`docs/reference/agent-integration-guide.md`](docs/reference/agent-integration-guide.md)
220
+ - [`docs/reference/public-api.md`](docs/reference/public-api.md) — V1 + V2
221
+ - [`docs/reference/public-api-3.md`](docs/reference/public-api-3.md) — V3
222
+ - [`docs/reference/service-wrapper-guidance.md`](docs/reference/service-wrapper-guidance.md)
223
+ - [`docs/reference/agent-capability-map.md`](docs/reference/agent-capability-map.md)
224
+ - [`docs/reference/agent-read-models-and-snapshots.md`](docs/reference/agent-read-models-and-snapshots.md)
225
+ - [`docs/reference/agent-workflow-and-suggestions.md`](docs/reference/agent-workflow-and-suggestions.md)
226
+ - [`docs/reference/scope-aware-selection-tooling.md`](docs/reference/scope-aware-selection-tooling.md)
227
+ - [`docs/reference/glossary.md`](docs/reference/glossary.md)
228
+ - [`docs/reference/editor-integration-style.md`](docs/reference/editor-integration-style.md)
229
+ - [`docs/reference/ui-theming-and-styling.md`](docs/reference/ui-theming-and-styling.md)
230
+ - [`docs/reference/beyondwork-runtime-environment.md`](docs/reference/beyondwork-runtime-environment.md)
231
+
232
+ Wrapper and agent docs stay on `main`, but they are downstream integration guidance rather than the canonical package contract.
233
+
234
+ Current integration honesty:
235
+
236
+ - the shipped React host surface is still `WordReviewEditor`
237
+ - `showReviewPanel={false}` only hides the bundled review rail
238
+ - a distinct public chromeless React surface is not yet shipped
239
+ - V3 is additive — React editor mounts keep using V1/V2; V3 is for stateless services, out-of-process agents, workblocks, and debug consumers
240
+ - `ai.*` mutations (`resolveReference`, `getScopeBundle`, `proposeReplacementScope`, `validateReplacementScope`, `applyReplacementScope`, `attachExplanation`, `createIssue`) are **mock today** pending Phase 4 (scope compiler) and Phase 5 (replacement lifecycle) — the shape is stable, the behavior will be replaced
241
+
242
+ ### Maintainer Path
243
+
244
+ - [`docs/maintainers/README.md`](docs/maintainers/README.md)
245
+ - [`docs/ccep-contract-templates/README.md`](docs/ccep-contract-templates/README.md)
246
+ - [`docs/plans/README.md`](docs/plans/README.md) — current plans/audits router + archived execution history
247
+
248
+ The CCEP corpus is kept on `main` as a maintainer-safe smoke-doc source set for agreement-heavy validation and wrapper or agent benchmarking.
249
+
250
+ ### Plans and audits
251
+
252
+ `docs/plans/` now keeps only the maintainer-facing artifacts that are still in
253
+ scope for current audit/debug work:
254
+
255
+ - `chrome-composition-audit.md`
256
+ - `chrome-component-index.json`
257
+ - `ux-inventory.md`
258
+ - `scope-audit.md` + `scope-audit-classification.json`
259
+ - `debug-tooling-v2-architecture.md`
260
+ - `doc-debug-stack.md`
261
+ - `doc-debug-test-protocol.md`
262
+
263
+ Closed lane and one-off execution plans now live under
264
+ `docs/plans/archived/`. The `gap-analysis/` and `v2/`
265
+ subdirectories remain only where current docs or generators still depend on
266
+ them. (The former `lane-prompts` subdirectory was deleted on 2026-04-21;
267
+ its content was consolidated into `docs/wiki/shipped-history.md`.)
268
+
269
+ ### Technical Wiki
270
+
271
+ [`docs/wiki/`](docs/wiki/) is the feature-by-feature technical layer (40+ topics covering OOXML, ProseMirror, runtime, debug, and platform). Enter via [`docs/README.md`](docs/README.md). Highlights:
272
+
273
+ - `api-v1` · `api-v2` · `api-v3` — the three public API generations, delineated
274
+ - `runtime-truth` — the canonical-truth narrative every agent should internalize
275
+ - `debugging` + `doc-debug-service-guide` — runtime debug substrate + HTTPS debug service
276
+ - `chrome-composition` + `chrome-composition-backlog` — UX composition audit split: shipped vs outstanding
277
+ - `testing-and-fixtures` — F01–F52 corpus + CCEP 629-doc corpus + truth-baseline numbers + MCP tooling map
278
+
279
+ ## Agent Integration
280
+
281
+ Agents consume the editor through a consistent pipeline. Two entry shapes, same pipeline:
282
+
283
+ **V1/V2 ref (React editor mounts):**
284
+ ```
285
+ inspect --> locate --> mutate --> validate --> export
286
+ ```
287
+
288
+ **V3 `createApiV3(runtime)` (stateless services, workblocks, out-of-process agents):**
289
+ ```
290
+ inspect --> listScopes --> getScopeBundle --> propose --> validate --> apply --> export
291
+ └── mock today (Phase 4/5) ──┘
292
+ ```
293
+
294
+ Beyond Work platform agents extend the V1/V2 pipeline with propose/approve gates:
295
+ ```
296
+ inspect --> locate --> propose --> approve --> mutate --> validate --> export
24
297
  ```
25
298
 
26
- Peer dependencies: `react` 19.2, `react-dom` 19.2, the ProseMirror family (`prosemirror-state`, `prosemirror-view`, `prosemirror-model`, `prosemirror-transform`, `prosemirror-keymap`, `prosemirror-commands`, `prosemirror-schema-basic`), `tailwindcss` ≥ 4.2, and `yjs` + `y-protocols` for collaboration. See `package.json > peerDependencies`.
299
+ The package exposes purpose-built read models not a monolithic state dump. V1 getters and V3 equivalents dispatch through the same runtime:
300
+
301
+ | Read model | V1 getter | V3 equivalent | Purpose |
302
+ |---|---|---|---|
303
+ | Document surface | `getRenderSnapshot()` | (composed via `runtime.content.*`) | Structure, blocks, text, selection |
304
+ | Comments | `getComments()` | `api.runtime.review.getComments()` (partial) | Thread state, resolution, anchors |
305
+ | Tracked changes | `getTrackedChanges()` | `api.runtime.review.getChanges()` (partial) | Revision inventory, actionability |
306
+ | Workflow scope | `getWorkflowScopeSnapshot()` | `api.runtime.workflow.queryScopes()` (live) | Scope boundaries, work items, mode |
307
+ | Interaction guard | `getInteractionGuardSnapshot()` | `api.runtime.workflow.getGuard()` (live) | Effective mode, blocked reasons |
308
+ | Compatibility | `getCompatibilityReport()` | `api.runtime.document.validate()` (live-with-adapter) | Export risk assessment |
309
+ | Warnings | `getWarnings()` | (bundled into `validate()`) | Active fidelity or mutation warnings |
310
+ | Layout | `ref.layout.getPage(...)` | `api.runtime.layout.getPage(...)` (live-with-adapter) | Page/block layout reads |
311
+ | Debug snapshot | `runtime.debug.getSnapshot()` | same (shared projector) | Canonical/workflow/review/scope/layout one-shot JSON |
312
+
313
+ Agents should read the narrow snapshot that answers the next question, not pull the full render snapshot for every decision. Every V3 `uiVisible` call emits exactly one `UxResponse` on the `api` telemetry channel — subscribe via `docs/wiki/debugging.md` (navigate through `docs/README.md`) for mock-or-live traces.
314
+
315
+ ## Packaging And Release
316
+
317
+ - `.github/workflows/publish.yml` publishes on `v*` tags after verifying the tag matches `package.json`
318
+ - `pnpm pack --dry-run` is the baseline package proof
319
+ - npm provenance is enabled in `publishConfig` and in the publish workflow invocation
320
+ - the published package currently ships source ESM entry points plus TypeScript source-backed `types` exports
321
+ - the Microsoft Open XML SDK remains CI/internal-service only, never part of the shipped browser runtime
322
+
323
+ ## Contribution Rules
324
+
325
+ - respect the runtime-owned state model
326
+ - do not bypass commands and transactions
327
+ - do not treat the DOM as canonical state
328
+ - do not silently drop unknown OOXML
329
+ - keep docs honest about shipped versus planned behavior
330
+ - add or extend fixtures for compatibility-critical changes
331
+ - `bash scripts/validate-fixtures.sh` now uses the Railway validator service and requires `OPENXML_VALIDATOR_AUTH_TOKEN` when hitting the public domain
332
+ - `node scripts/validate-docs-navigation.mjs` enforces the core docs catalog, required indexes, and frontmatter contract
333
+
334
+ ## Guiding Principle
335
+
336
+ This repo builds backoffice work components where humans and AI agents are first-class peers. Every capability is designed for both audiences from the start — not bolted on for one after the other. The result is tools that are powerful enough for complex enterprise workflows and structured enough that an agent can operate them safely and predictably.
337
+
338
+ ## Using the package
339
+
340
+ ### WordReviewEditor
341
+
342
+ `WordReviewEditor` is a React component for loading, editing, and exporting `.docx` files with full comment and tracked-change (redline) support. It is exported from `@beyondwork/docx-react-component`.
27
343
 
28
- ## Quick start — mount the editor
344
+ ### Installation
29
345
 
30
346
  ```tsx
31
- import { WordReviewEditor } from "@beyondwork/docx-react-component/ui-tailwind";
32
- import "@beyondwork/docx-react-component/ui-tailwind/theme/editor-theme.css";
347
+ import {
348
+ WordReviewEditor,
349
+ type WordReviewEditorRef,
350
+ type WordReviewEditorProps,
351
+ type WordReviewEditorEvent,
352
+ } from "@beyondwork/docx-react-component";
353
+ ```
354
+
355
+ ### Basic mount
356
+
357
+ ```tsx
358
+ import { useRef } from "react";
359
+ import { WordReviewEditor, type WordReviewEditorRef } from "@beyondwork/docx-react-component";
360
+
361
+ export function MyEditor({ docxBytes }: { docxBytes: Uint8Array }) {
362
+ const editorRef = useRef<WordReviewEditorRef>(null);
33
363
 
34
- export function App() {
35
364
  return (
36
365
  <WordReviewEditor
37
- initialDocument={docxBytes}
38
- onExport={(bytes) => /* persist */}
366
+ ref={editorRef}
367
+ documentId="doc-001"
368
+ currentUser={{ userId: "u1", displayName: "Alice" }}
369
+ initialDocx={docxBytes}
370
+ onEvent={(event) => console.log(event)}
39
371
  />
40
372
  );
41
373
  }
42
374
  ```
43
375
 
44
- ## Quick start — headless / stateless service
376
+ ---
377
+
378
+ ### Props reference
379
+
380
+ | Prop | Type | Description |
381
+ |---|---|---|
382
+ | `documentId` | `string` | **Required.** Stable identifier for this document. |
383
+ | `currentUser` | `EditorUser` | **Required.** The user performing edits and adding comments. |
384
+ | `initialDocx` | `Uint8Array \| ArrayBuffer` | Raw `.docx` bytes to load on first mount. |
385
+ | `initialSessionState` | `EditorSessionState` | Previously saved session state to restore. |
386
+ | `initialSnapshot` | `PersistedEditorSnapshot` | Previously saved snapshot to restore. |
387
+ | `externalDocSource` | `ExternalDocumentSource` | Alternative source with explicit `kind` (`"docx"`, `"session"`, `"snapshot"`). |
388
+ | `readOnly` | `boolean` | When `true`, all editing commands are disabled. |
389
+ | `reviewMode` | `"editing" \| "review"` | Shell layout hint — affects toolbar/panel arrangement but not editing authority. |
390
+ | `markupDisplay` | `"clean" \| "simple" \| "all"` | Controls tracked-change visibility. |
391
+ | `showReviewPanel` | `boolean` | Shows or hides the right-side comment and tracked-change panel. |
392
+ | `autosave` | `AutosaveConfig` | Enables automatic saving. |
393
+ | `hostAdapter` | `EditorHostAdapter` | Callbacks for `load`, `saveSession`, `saveExport`. |
394
+ | `datastore` | `EditorDatastoreAdapter` | Alternative persistence adapter with `load`, `saveSnapshot`. |
395
+ | `onEvent` | `(event: WordReviewEditorEvent) => void` | Unified event handler (see [Events](#events)). |
396
+ | `onWarning` | `(warning: EditorWarning) => void` | Fired for non-fatal warnings. |
397
+ | `onError` | `(error: EditorError) => void` | Fired for fatal errors. |
398
+
399
+ #### EditorUser
400
+
401
+ ```ts
402
+ interface EditorUser {
403
+ userId: string;
404
+ displayName: string;
405
+ email?: string;
406
+ avatarUrl?: string;
407
+ }
408
+ ```
409
+
410
+ #### AutosaveConfig
411
+
412
+ ```ts
413
+ interface AutosaveConfig {
414
+ enabled?: boolean;
415
+ debounceMs?: number; // default: 2000
416
+ }
417
+ ```
418
+
419
+ ---
420
+
421
+ ### Show / hide UI regions
422
+
423
+ #### Review panel
424
+
425
+ The right-side panel lists comment threads and tracked changes.
426
+
427
+ ```tsx
428
+ <WordReviewEditor showReviewPanel={false} ... /> // hide panel
429
+ <WordReviewEditor showReviewPanel={true} ... /> // show panel (default)
430
+ ```
431
+
432
+ #### Tracked-change display mode
433
+
434
+ `markupDisplay` controls how tracked changes appear in the document body.
435
+
436
+ | Value | Behaviour |
437
+ |---|---|
438
+ | `"clean"` | Show the accepted version — insertions visible, deletions hidden. |
439
+ | `"simple"` | Show a simplified view of changes without inline markup. |
440
+ | `"all"` | Show all insertion and deletion marks inline (Word's "Show Markup" mode). |
441
+
442
+ ```tsx
443
+ <WordReviewEditor markupDisplay="clean" ... />
444
+ ```
445
+
446
+ You can also change the display mode at runtime:
45
447
 
46
448
  ```ts
47
- import { DocxSession } from "@beyondwork/docx-react-component/session";
48
- import { createApiV3 } from "@beyondwork/docx-react-component/api/v3";
449
+ // no ref method for markupDisplay — pass as a prop; React re-renders propagate the change.
450
+ ```
451
+
452
+ #### Document mode
453
+
454
+ `DocumentMode` controls editing authority, not just appearance.
455
+
456
+ | Mode | Effect |
457
+ |---|---|
458
+ | `"editing"` | Edits are applied directly (no tracking). |
459
+ | `"suggesting"` | Every edit is automatically wrapped in a tracked change. |
460
+ | `"viewing"` | Document is read-only regardless of the `readOnly` prop. |
461
+
462
+ Set via ref:
463
+
464
+ ```ts
465
+ editorRef.current.setDocumentMode("suggesting");
466
+ ```
467
+
468
+ Or pass `reviewMode="review"` as a prop to start in a review-friendly shell layout (the component internally maps this to `"suggesting"` document mode).
469
+
470
+ #### Read-only mode
49
471
 
50
- const session = await DocxSession.open(docxBytes);
51
- const api = createApiV3(session.runtime);
472
+ ```tsx
473
+ <WordReviewEditor readOnly={true} ... />
474
+ ```
475
+
476
+ All editing, commenting, and tracked-change commands are blocked. The toolbar is still rendered but all buttons are disabled.
52
477
 
53
- const outline = await api.runtime.document.getOutline();
54
- const issues = await api.runtime.review.getChanges();
55
- const bytes = await api.runtime.document.export();
478
+ #### Workspace layout
479
+
480
+ ```ts
481
+ editorRef.current.setWorkspaceMode("canvas"); // continuous scroll
482
+ editorRef.current.setWorkspaceMode("page"); // paginated view
56
483
  ```
57
484
 
58
- See [`examples/headless-service/`](examples/headless-service/) for a full stateless consumer.
485
+ ---
486
+
487
+ ### Imperative ref
488
+
489
+ Obtain the ref via `useRef<WordReviewEditorRef>()`:
490
+
491
+ ```tsx
492
+ const editorRef = useRef<WordReviewEditorRef>(null);
493
+ <WordReviewEditor ref={editorRef} ... />
494
+
495
+ // then:
496
+ editorRef.current?.addComment({ body: "Needs revision" });
497
+ ```
59
498
 
60
499
  ---
61
500
 
62
- ## What's shipped
501
+ ### Comment operations
502
+
503
+ #### Add a comment
504
+
505
+ ```ts
506
+ addComment(params: AddCommentParams): string
507
+ // returns the new commentId
508
+ ```
509
+
510
+ ```ts
511
+ interface AddCommentParams {
512
+ anchor?: EditorAnchorProjection; // defaults to the current selection
513
+ body?: string;
514
+ authorId?: string; // defaults to currentUser.userId
515
+ }
516
+ ```
517
+
518
+ **Important**: if you want the comment to land on a specific text selection, capture the anchor *before* opening any draft UI (e.g. a modal or popover), because opening a modal typically collapses the editor selection.
519
+
520
+ ```ts
521
+ // 1. Capture anchor while text is still selected
522
+ const snapshot = editorRef.current.getRenderSnapshot();
523
+ const anchor = snapshot.selection.activeRange;
524
+
525
+ // 2. Open your draft UI, let user type a message...
526
+ // 3. On submit:
527
+ const commentId = editorRef.current.addComment({ anchor, body: draftText });
528
+ ```
529
+
530
+ #### Resolve a comment
531
+
532
+ ```ts
533
+ editorRef.current.resolveComment(commentId);
534
+ ```
535
+
536
+ Marks the thread as resolved. The comment remains in the document and can be exported; it is moved to the resolved list in the sidebar.
537
+
538
+ #### Reopen a resolved comment
539
+
540
+ ```ts
541
+ editorRef.current.reopenComment(commentId);
542
+ ```
543
+
544
+ #### Delete a comment permanently
545
+
546
+ ```ts
547
+ editorRef.current.deleteComment(commentId);
548
+ ```
549
+
550
+ Removes the comment entirely. Use this to clean up failed or unwanted drafts.
551
+
552
+ #### Add a reply to an existing thread
553
+
554
+ ```ts
555
+ editorRef.current.addCommentReply(commentId, "Reply text here");
556
+ ```
557
+
558
+ #### Edit a comment body
559
+
560
+ ```ts
561
+ editorRef.current.editCommentBody(commentId, "Updated text");
562
+ ```
563
+
564
+ #### Scroll to a comment
63
565
 
64
- **Editing & review**
65
- - Tracked changes (add, accept, reject, resolve) incl. container + range markers and move-promotion
66
- - Suggesting mode — every edit automatically becomes a tracked change
67
- - Comments — anchored, threaded, mention-aware, with presentation/negotiation layer
68
- - Workflow overlays and scope-based editing guards
69
- - Document comparison (LCS-backed diffing + redline export)
566
+ ```ts
567
+ editorRef.current.scrollToComment(commentId);
568
+ ```
70
569
 
71
- **Fidelity**
72
- - OOXML round-trip — open, edit, export, reopen in Word without repair prompts
73
- - Preserve-first handling for unknown fragments (opaque preservation)
74
- - Layout engine — **97.77 %** external layout-structure truth and **96.34 %** rendered-word geometry on the CCEP contract corpus
75
- - Charts v1 — native SVG renderer for 7 chart families, progressive time-sliced
570
+ #### Open (focus) a comment in the sidebar
76
571
 
77
- **Collaboration**
78
- - Yjs transport with HMAC-signed awareness, tamper-gating, external-custody round-trip
79
- - Prerender cache (`prerenderDocument()`) for cold-open parity
572
+ ```ts
573
+ editorRef.current.openComment(commentId);
574
+ ```
80
575
 
81
- **Automation surface**
82
- - **V1/V2 ref-method API** — stable React imperative surface on `WordReviewEditorRef`
83
- - **V3 stateless API** — `createApiV3(runtime)` exposes Runtime + AI families under a `live` / `live-with-adapter` / `partial` / `mock` taxonomy
84
- - AI action policy — risk-classified gating for 37 discrete agent operations
85
- - Every `uiVisible` V3 function emits exactly one `UxResponse` on the `api` telemetry channel — the stream the debug surfaces consume
576
+ #### Get all comments
86
577
 
87
- **Accessibility**
88
- - Keyboard-first, reduced-motion respected, WCAG-audited (audit harness lives outside the shipping snapshot)
578
+ ```ts
579
+ const sidebar: CommentSidebarSnapshot = editorRef.current.getComments();
580
+ ```
89
581
 
90
- Feature-level status + known gaps: [`KNOWN-ISSUES.md`](KNOWN-ISSUES.md), [`KNOWN_ISSUES.md`](KNOWN_ISSUES.md), [`KNOWN-ISSUES-VISUAL.md`](KNOWN-ISSUES-VISUAL.md).
582
+ ```ts
583
+ interface CommentSidebarSnapshot {
584
+ activeCommentId?: string;
585
+ openCommentIds: string[];
586
+ resolvedCommentIds: string[];
587
+ detachedCommentIds: string[];
588
+ totalCount: number;
589
+ threads: CommentSidebarThreadSnapshot[];
590
+ }
591
+
592
+ interface CommentSidebarThreadSnapshot {
593
+ commentId: string;
594
+ status: "open" | "resolved" | "detached";
595
+ anchor: EditorAnchorProjection;
596
+ excerpt: string; // the anchored text snippet
597
+ entries: CommentSidebarThreadEntrySnapshot[];
598
+ entryCount: number;
599
+ createdAt: string; // ISO 8601
600
+ createdBy: string; // userId
601
+ resolvedAt?: string;
602
+ resolvedBy?: string;
603
+ }
604
+ ```
605
+
606
+ #### Detached comments
607
+
608
+ A comment becomes **detached** when the text it was anchored to is deleted. Detached comments:
609
+
610
+ - Still appear in `sidebar.detachedCommentIds` and have `status: "detached"`.
611
+ - Have `anchor.kind === "detached"` with a `lastKnownRange` and a `reason` (`"deleted"`, `"invalidatedByStructureChange"`, or `"importAmbiguity"`).
612
+ - Do **not** block DOCX export.
613
+ - Can be resolved, reopened, or deleted via the same methods above.
91
614
 
92
615
  ---
93
616
 
94
- ## Architecture
617
+ ### Tracked-change operations
618
+
619
+ #### Get all tracked changes
620
+
621
+ ```ts
622
+ const changes: TrackedChangesSnapshot = editorRef.current.getTrackedChanges();
623
+ ```
624
+
625
+ ```ts
626
+ interface TrackedChangesSnapshot {
627
+ pendingChangeIds: string[];
628
+ acceptedChangeIds: string[];
629
+ rejectedChangeIds: string[];
630
+ detachedChangeIds: string[];
631
+ actionableChangeIds: string[];
632
+ preserveOnlyChangeIds: string[];
633
+ totalCount: number;
634
+ revisions: TrackedChangeEntrySnapshot[];
635
+ }
636
+
637
+ interface TrackedChangeEntrySnapshot {
638
+ revisionId: string;
639
+ kind: "insertion" | "deletion" | "formatting" | "move" | "property-change";
640
+ status: "active" | "accepted" | "rejected" | "detached";
641
+ actionability: "actionable" | "preserve-only";
642
+ canAccept: boolean;
643
+ canReject: boolean;
644
+ anchor: EditorAnchorProjection;
645
+ anchorLabel: string;
646
+ excerpt?: string;
647
+ detail?: string;
648
+ authorId: string;
649
+ createdAt: string;
650
+ }
651
+ ```
652
+
653
+ `preserve-only` revisions (`formatting`, `move`) can be displayed but cannot be individually accepted or rejected through the API.
654
+
655
+ #### Accept a single change
656
+
657
+ ```ts
658
+ editorRef.current.acceptChange(revisionId);
659
+ ```
95
660
 
96
- The editor runs ProseMirror in an **inverted model**: PM is a view surface, the runtime owns every mutation, and the canonical truth order is
661
+ #### Reject a single change
97
662
 
98
- 1. OOXML package bytes
99
- 2. `CanonicalDocument`
100
- 3. `DocumentRuntime` derived state (formatting, layout, geometry, scopes, review)
101
- 4. `EditorSurfaceSnapshot` + PM state + DOM
663
+ ```ts
664
+ editorRef.current.rejectChange(revisionId);
665
+ ```
102
666
 
103
- Higher layers are projections of lower layers. Reading the DOM or PM state as truth is a bug.
667
+ #### Accept all pending changes
104
668
 
105
- The 11-layer target architecture — session, canonical document, formatting, layout, geometry, workflow, runtime API, semantic scope compiler, AI API, UI API, presentation — is documented in [`docs/architecture/00-overview.md`](docs/architecture/00-overview.md), with per-layer canonical specs in [`docs/architecture/01-package-session.md`](docs/architecture/01-package-session.md) … [`11-presentation-surfaces.md`](docs/architecture/11-presentation-surfaces.md).
669
+ ```ts
670
+ editorRef.current.acceptAllChanges();
671
+ ```
672
+
673
+ #### Reject all pending changes
674
+
675
+ ```ts
676
+ editorRef.current.rejectAllChanges();
677
+ ```
678
+
679
+ #### Scroll to a tracked change
680
+
681
+ ```ts
682
+ editorRef.current.scrollToRevision(revisionId);
683
+ ```
106
684
 
107
685
  ---
108
686
 
109
- ## Documentation lanes
687
+ ### Export
110
688
 
111
- | Lane | Path | When to start here |
112
- |---|---|---|
113
- | Router | [`docs/README.md`](docs/README.md) | Any time triage into the right lane |
114
- | Architecture | [`docs/architecture/00-overview.md`](docs/architecture/00-overview.md) | Canonical per-layer target design for the 11-layer stack |
115
- | Wiki (feature deep-dives) | [`docs/wiki/`](docs/wiki/) | Topic-organized narrative connecting OOXML + PM + implementation + testing |
116
- | Migration | [`MIGRATION.md`](MIGRATION.md) | Upgrading across major versions |
117
- | Known issues | [`KNOWN-ISSUES.md`](KNOWN-ISSUES.md), [`KNOWN_ISSUES.md`](KNOWN_ISSUES.md), [`KNOWN-ISSUES-VISUAL.md`](KNOWN-ISSUES-VISUAL.md) | Preview caveats + visual-fidelity gap inventory |
118
- | Changelog | [`CHANGELOG.md`](CHANGELOG.md) | Release notes |
689
+ ```ts
690
+ const result = await editorRef.current.exportDocx({ fileName: "output.docx" });
691
+ // result.bytes is the Uint8Array of the .docx file
692
+ ```
693
+
694
+ Export will throw if any non-detached comment no longer maps to a serializable range in the document. To diagnose, check `getComments().threads` for entries where `status !== "detached"` but whose `anchor.kind` is unexpected.
119
695
 
120
696
  ---
121
697
 
122
- ## Development
698
+ ### Events
123
699
 
124
- | Command | Purpose |
125
- |---|---|
126
- | `pnpm run lint:tsgo` | TypeScript type check |
127
- | `pnpm test:repo` | Contract + validation test suite |
128
- | `pnpm run build` | Build the shipped package |
700
+ All events are dispatched through the single `onEvent` prop. The `type` discriminator narrows the payload.
701
+
702
+ ```tsx
703
+ <WordReviewEditor
704
+ onEvent={(event) => {
705
+ if (event.type === "comment_added") {
706
+ console.log("New comment:", event.commentId);
707
+ }
708
+ }}
709
+ />
710
+ ```
711
+
712
+ #### `ready`
713
+
714
+ Fired once after the document finishes loading and the editor is interactive.
715
+
716
+ ```ts
717
+ {
718
+ type: "ready";
719
+ documentId: string;
720
+ sessionId: string;
721
+ source: "docx" | "session" | "snapshot";
722
+ stats: DocumentStats; // storyLength, commentCount, revisionCount
723
+ compatibility: CompatibilityReport;
724
+ comments: CommentSidebarSnapshot;
725
+ trackedChanges: TrackedChangesSnapshot;
726
+ }
727
+ ```
728
+
729
+ #### `comment_added`
730
+
731
+ Fired when a new comment thread is created (via `addComment` or the toolbar).
732
+
733
+ ```ts
734
+ {
735
+ type: "comment_added";
736
+ documentId: string;
737
+ commentId: string;
738
+ anchor: EditorAnchorProjection;
739
+ }
740
+ ```
741
+
742
+ #### `comment_resolved`
743
+
744
+ Fired when a comment is resolved (via `resolveComment` or the sidebar).
745
+
746
+ ```ts
747
+ {
748
+ type: "comment_resolved";
749
+ documentId: string;
750
+ commentId: string;
751
+ }
752
+ ```
753
+
754
+ For a general-purpose "something about comments changed" signal (covering deletions, reply appends, body edits, reopen, and every other mutation — including Yjs-driven mutations that route through the editor's imperative ref), subscribe to `comments_changed`.
755
+
756
+ #### `comments_changed`
757
+
758
+ Fired once per commit whenever the runtime's comment snapshot differs from the previous commit. Use this when you render comments in a sibling component and need to know when to re-fetch via `editorRef.current.getComments()`.
759
+
760
+ ```ts
761
+ {
762
+ type: "comments_changed";
763
+ documentId: string;
764
+ changedCommentIds: string[]; // always non-empty
765
+ }
766
+ ```
767
+
768
+ Covers: `addComment`, `deleteComment`, `resolveComment`, `reopenComment`, `addCommentReply`, `editCommentBody`, and any remote-origin mutation that funnels through the same runtime APIs. Does NOT fire on selection/focus changes.
769
+
770
+ #### `change_accepted`
771
+
772
+ Fired when a tracked change is accepted.
773
+
774
+ ```ts
775
+ {
776
+ type: "change_accepted";
777
+ documentId: string;
778
+ changeId: string;
779
+ }
780
+ ```
781
+
782
+ #### `change_rejected`
783
+
784
+ Fired when a tracked change is rejected.
785
+
786
+ ```ts
787
+ {
788
+ type: "change_rejected";
789
+ documentId: string;
790
+ changeId: string;
791
+ }
792
+ ```
793
+
794
+ #### `selection_changed`
795
+
796
+ Fired whenever the editor cursor or selection changes.
797
+
798
+ ```ts
799
+ {
800
+ type: "selection_changed";
801
+ documentId: string;
802
+ selection: SelectionSnapshot;
803
+ }
804
+
805
+ interface SelectionSnapshot {
806
+ anchor: number;
807
+ head: number;
808
+ isCollapsed: boolean;
809
+ activeRange: EditorAnchorProjection;
810
+ storyTarget?: EditorStoryTarget;
811
+ }
812
+ ```
813
+
814
+ Use `selection.activeRange` as the `anchor` argument to `addComment` — but capture it *before* opening any modal UI.
815
+
816
+ #### `dirty_changed`
817
+
818
+ Fired when the document transitions between clean and dirty (unsaved) states.
819
+
820
+ ```ts
821
+ {
822
+ type: "dirty_changed";
823
+ documentId: string;
824
+ isDirty: boolean;
825
+ }
826
+ ```
827
+
828
+ #### `story_changed`
829
+
830
+ Fired when the user navigates between document stories (e.g. main body -> header/footer).
831
+
832
+ ```ts
833
+ {
834
+ type: "story_changed";
835
+ documentId: string;
836
+ activeStory: EditorStoryTarget;
837
+ }
838
+ ```
839
+
840
+ #### `export_completed`
841
+
842
+ Fired after a successful `exportDocx` call, after the host `saveExport` callback (if any) has resolved.
843
+
844
+ ```ts
845
+ {
846
+ type: "export_completed";
847
+ documentId: string;
848
+ result: ExportResult; // result.bytes, result.fileName, result.mimeType
849
+ }
850
+ ```
851
+
852
+ #### `session_saved`
853
+
854
+ Fired after the host `saveSession` callback resolves.
855
+
856
+ ```ts
857
+ {
858
+ type: "session_saved";
859
+ documentId: string;
860
+ sessionState: EditorSessionState;
861
+ savedAt: string;
862
+ isAutosave: boolean;
863
+ }
864
+ ```
865
+
866
+ #### `snapshot_saved`
867
+
868
+ Fired after the datastore `saveSnapshot` callback resolves.
869
+
870
+ ```ts
871
+ {
872
+ type: "snapshot_saved";
873
+ documentId: string;
874
+ snapshot: PersistedEditorSnapshot;
875
+ isAutosave: boolean;
876
+ }
877
+ ```
878
+
879
+ #### `autosave_state`
880
+
881
+ Fired when the autosave lifecycle transitions.
882
+
883
+ ```ts
884
+ {
885
+ type: "autosave_state";
886
+ documentId: string;
887
+ state: "idle" | "pending" | "saving" | "saved" | "error";
888
+ }
889
+ ```
890
+
891
+ #### `warning_added` / `warning_cleared`
892
+
893
+ Non-fatal import or rendering warnings.
894
+
895
+ ```ts
896
+ { type: "warning_added"; documentId: string; warning: EditorWarning; }
897
+ { type: "warning_cleared"; documentId: string; warningId: string; code: EditorWarningCode; }
898
+ ```
899
+
900
+ #### `error`
901
+
902
+ Fatal editor error. The editor may be in an unrecoverable state after this event.
903
+
904
+ ```ts
905
+ {
906
+ type: "error";
907
+ documentId: string;
908
+ error: EditorError; // error.code, error.message, error.isFatal
909
+ }
910
+ ```
911
+
912
+ #### Workflow events
913
+
914
+ These events relate to the optional workflow overlay feature.
129
915
 
130
- Agent onboarding: [`CLAUDE.md`](CLAUDE.md), [`AGENTS.md`](AGENTS.md).
131
- Product charters: [`DESIGN-EDITOR.md`](DESIGN-EDITOR.md), [`DESIGN-WORKBLOCK.md`](DESIGN-WORKBLOCK.md).
132
- Findings log: [`SHIP-LOG.md`](SHIP-LOG.md).
916
+ ```ts
917
+ { type: "workflow_overlay_changed"; documentId: string; snapshot: WorkflowScopeSnapshot; }
918
+ { type: "workflow_active_work_item_changed"; documentId: string; activeWorkItemId: string | null; }
919
+ { type: "command_blocked"; documentId: string; command: string; reasons: WorkflowBlockedCommandReason[]; }
920
+ ```
133
921
 
134
922
  ---
135
923
 
136
- ## Browser / runtime support
924
+ ### Key types
925
+
926
+ #### EditorAnchorProjection
137
927
 
138
- - Modern evergreen browsers (Chrome, Firefox, Safari, Edge)
139
- - React 19.2+, Node 22+ for server-side usage
140
- - No web workers — all time-slicing via `scheduler.yield` / `requestIdleCallback` on the main thread
928
+ Describes a position or range in the document. Returned by `selection.activeRange` and stored on comments/revisions.
929
+
930
+ ```ts
931
+ type EditorAnchorProjection =
932
+ | { kind: "range"; from: number; to: number; assoc: { start: -1|1; end: -1|1 } }
933
+ | { kind: "node"; at: number; assoc: -1|1 }
934
+ | { kind: "detached"; lastKnownRange: { from: number; to: number };
935
+ reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity" };
936
+ ```
937
+
938
+ A `"range"` anchor is required for `addComment` if the document contains tables or the selection spans multiple characters. The `from`/`to` positions are in the editor's internal runtime coordinate space — always capture them from `getRenderSnapshot().selection.activeRange`, never construct them manually.
939
+
940
+ #### DocumentMode
941
+
942
+ ```ts
943
+ type DocumentMode = "editing" | "suggesting" | "viewing";
944
+ ```
945
+
946
+ #### WorkspaceMode
947
+
948
+ ```ts
949
+ type WorkspaceMode = "canvas" | "page";
950
+ ```
141
951
 
142
952
  ---
143
953
 
144
- ## License
954
+ ### Common patterns
955
+
956
+ #### Full add-comment flow with custom UI
957
+
958
+ ```tsx
959
+ function CommentButton({ editorRef }: { editorRef: React.RefObject<WordReviewEditorRef> }) {
960
+ const [draft, setDraft] = useState<{ anchor: EditorAnchorProjection; text: string } | null>(null);
961
+
962
+ function openDraft() {
963
+ // Capture anchor BEFORE the modal steals focus from the editor
964
+ const snapshot = editorRef.current?.getRenderSnapshot();
965
+ if (!snapshot) return;
966
+ const anchor = snapshot.selection.activeRange;
967
+ if (anchor.kind !== "range" || anchor.from === anchor.to) return;
968
+ setDraft({ anchor, text: "" });
969
+ }
970
+
971
+ function submit() {
972
+ if (!draft) return;
973
+ editorRef.current?.addComment({ anchor: draft.anchor, body: draft.text });
974
+ setDraft(null);
975
+ }
976
+
977
+ return (
978
+ <>
979
+ <button onClick={openDraft}>Comment</button>
980
+ {draft && (
981
+ <dialog open>
982
+ <textarea value={draft.text} onChange={(e) => setDraft({ ...draft, text: e.target.value })} />
983
+ <button onClick={submit}>Add</button>
984
+ <button onClick={() => setDraft(null)}>Cancel</button>
985
+ </dialog>
986
+ )}
987
+ </>
988
+ );
989
+ }
990
+ ```
991
+
992
+ #### Listen for comment and review-change events
145
993
 
146
- See [`LICENSE.md`](LICENSE.md). The package is published under the Beyond Work license for use inside the Beyond Work platform and its authorized integrations.
994
+ ```tsx
995
+ <WordReviewEditor
996
+ onEvent={(event) => {
997
+ switch (event.type) {
998
+ case "comment_added":
999
+ console.log("comment added:", event.commentId, event.anchor);
1000
+ break;
1001
+ case "comment_resolved":
1002
+ console.log("comment resolved:", event.commentId);
1003
+ break;
1004
+ case "comments_changed":
1005
+ console.log("comments changed:", event.changedCommentIds);
1006
+ break;
1007
+ case "change_accepted":
1008
+ console.log("change accepted:", event.changeId);
1009
+ break;
1010
+ case "change_rejected":
1011
+ console.log("change rejected:", event.changeId);
1012
+ break;
1013
+ }
1014
+ }}
1015
+ />
1016
+ ```
1017
+
1018
+ #### Resolve all open comments programmatically
1019
+
1020
+ ```ts
1021
+ const { threads } = editorRef.current.getComments();
1022
+ for (const thread of threads) {
1023
+ if (thread.status === "open") {
1024
+ editorRef.current.resolveComment(thread.commentId);
1025
+ }
1026
+ }
1027
+ ```
1028
+
1029
+ #### Accept or reject all actionable changes
1030
+
1031
+ ```ts
1032
+ editorRef.current.acceptAllChanges();
1033
+ // or
1034
+ editorRef.current.rejectAllChanges();
1035
+ ```