@beyondwork/docx-react-component 1.0.36 → 1.0.38

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 (107) hide show
  1. package/README.md +103 -13
  2. package/package.json +1 -1
  3. package/src/api/package-version.ts +13 -0
  4. package/src/api/public-types.ts +402 -1
  5. package/src/core/commands/index.ts +18 -1
  6. package/src/core/commands/section-layout-commands.ts +58 -0
  7. package/src/core/commands/table-grid.ts +431 -0
  8. package/src/core/commands/table-structure-commands.ts +815 -55
  9. package/src/core/selection/mapping.ts +6 -0
  10. package/src/io/docx-session.ts +24 -9
  11. package/src/io/export/build-app-properties-xml.ts +88 -0
  12. package/src/io/export/serialize-comments.ts +6 -1
  13. package/src/io/export/serialize-footnotes.ts +10 -9
  14. package/src/io/export/serialize-headers-footers.ts +11 -10
  15. package/src/io/export/serialize-main-document.ts +328 -50
  16. package/src/io/export/serialize-numbering.ts +114 -24
  17. package/src/io/export/serialize-tables.ts +87 -11
  18. package/src/io/export/table-properties-xml.ts +174 -20
  19. package/src/io/export/twip.ts +66 -0
  20. package/src/io/normalize/normalize-text.ts +20 -0
  21. package/src/io/ooxml/parse-footnotes.ts +62 -1
  22. package/src/io/ooxml/parse-headers-footers.ts +62 -1
  23. package/src/io/ooxml/parse-main-document.ts +158 -1
  24. package/src/io/ooxml/parse-tables.ts +249 -0
  25. package/src/legal/bookmarks.ts +78 -0
  26. package/src/model/canonical-document.ts +45 -0
  27. package/src/review/store/scope-tag-diff.ts +130 -0
  28. package/src/runtime/document-layout.ts +4 -2
  29. package/src/runtime/document-navigation.ts +2 -306
  30. package/src/runtime/document-runtime.ts +287 -11
  31. package/src/runtime/layout/default-page-format.ts +96 -0
  32. package/src/runtime/layout/docx-font-loader.ts +143 -0
  33. package/src/runtime/layout/index.ts +233 -0
  34. package/src/runtime/layout/inert-layout-facet.ts +59 -0
  35. package/src/runtime/layout/layout-engine-instance.ts +628 -0
  36. package/src/runtime/layout/layout-invalidation.ts +257 -0
  37. package/src/runtime/layout/layout-measurement-provider.ts +175 -0
  38. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  39. package/src/runtime/layout/measurement-backend-canvas.ts +307 -0
  40. package/src/runtime/layout/measurement-backend-empirical.ts +208 -0
  41. package/src/runtime/layout/page-format-catalog.ts +233 -0
  42. package/src/runtime/layout/page-fragment-mapper.ts +179 -0
  43. package/src/runtime/layout/page-graph.ts +452 -0
  44. package/src/runtime/layout/page-layout-snapshot-adapter.ts +70 -0
  45. package/src/runtime/layout/page-story-resolver.ts +195 -0
  46. package/src/runtime/layout/paginated-layout-engine.ts +921 -0
  47. package/src/runtime/layout/project-block-fragments.ts +91 -0
  48. package/src/runtime/layout/public-facet.ts +1398 -0
  49. package/src/runtime/layout/resolved-formatting-document.ts +317 -0
  50. package/src/runtime/layout/resolved-formatting-state.ts +430 -0
  51. package/src/runtime/layout/table-render-plan.ts +229 -0
  52. package/src/runtime/render/block-fragment-projection.ts +35 -0
  53. package/src/runtime/render/decoration-resolver.ts +189 -0
  54. package/src/runtime/render/index.ts +57 -0
  55. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  56. package/src/runtime/render/render-frame-types.ts +317 -0
  57. package/src/runtime/render/render-kernel.ts +755 -0
  58. package/src/runtime/scope-tag-registry.ts +95 -0
  59. package/src/runtime/surface-projection.ts +1 -0
  60. package/src/runtime/text-ack-range.ts +49 -0
  61. package/src/runtime/view-state.ts +67 -0
  62. package/src/runtime/workflow-markup.ts +1 -5
  63. package/src/runtime/workflow-rail-segments.ts +280 -0
  64. package/src/ui/WordReviewEditor.tsx +99 -15
  65. package/src/ui/editor-runtime-boundary.ts +10 -1
  66. package/src/ui/editor-shell-view.tsx +6 -0
  67. package/src/ui/editor-surface-controller.tsx +3 -0
  68. package/src/ui/headless/chrome-registry.ts +501 -0
  69. package/src/ui/headless/scoped-chrome-policy.ts +183 -0
  70. package/src/ui/headless/selection-tool-context.ts +2 -0
  71. package/src/ui/headless/selection-tool-resolver.ts +36 -17
  72. package/src/ui/headless/selection-tool-types.ts +10 -0
  73. package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
  74. package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
  75. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  76. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
  77. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
  78. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  79. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  80. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
  81. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
  82. package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
  83. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
  84. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  85. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
  86. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +337 -0
  87. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +100 -0
  88. package/src/ui-tailwind/editor-surface/perf-probe.ts +27 -1
  89. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +20 -2
  90. package/src/ui-tailwind/editor-surface/pm-decorations.ts +93 -23
  91. package/src/ui-tailwind/editor-surface/predicted-position-map.ts +78 -0
  92. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +63 -0
  93. package/src/ui-tailwind/editor-surface/predicted-tx-gate.ts +39 -0
  94. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +176 -6
  95. package/src/ui-tailwind/index.ts +33 -0
  96. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  97. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  98. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  99. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  100. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  101. package/src/ui-tailwind/theme/editor-theme.css +505 -144
  102. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
  103. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  104. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  105. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +2 -2
  106. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +304 -166
  107. package/src/ui-tailwind/tw-review-workspace.tsx +163 -2
package/README.md CHANGED
@@ -1,17 +1,19 @@
1
1
  ---
2
- title: React OOXML Office
3
- summary: Shipped docx package landing page and primary router into consumer, wrapper, agent, and maintainer documentation.
2
+ title: Beyond Work Components
3
+ summary: Backoffice work components landing page and primary router into consumer, wrapper, agent, and maintainer documentation.
4
4
  audience: consumer, wrapper, agent, maintainer
5
5
  stability: main-path
6
6
  docRole: main-path
7
7
  canonical: true
8
8
  ---
9
9
 
10
- # React OOXML Office
10
+ # Beyond Work Components
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
- `@beyondwork/docx-react-component` is the shipped product in this repository: a fidelity-first React docx editor centered on `WordReviewEditor`.
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
+
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.
15
17
 
16
18
  ## Use This README For
17
19
 
@@ -19,11 +21,70 @@ canonical: true
19
21
  - installing the package
20
22
  - finding the right documentation lane quickly
21
23
 
22
- Do not use this README as the full package contract. The canonical consumer path starts in `docs/README.md`, `docs/reference/public-api.md`, and `docs/reference/integration-guide.md`.
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), and [`docs/reference/integration-guide.md`](docs/reference/integration-guide.md).
25
+
26
+ ## Components
27
+
28
+ ### Document Editor (`@beyondwork/docx-react-component`)
29
+
30
+ 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.
31
+
32
+ **Capabilities:**
33
+ - Full tracked-change and comment support (add, resolve, accept, reject)
34
+ - Suggesting mode where every edit automatically becomes a tracked change
35
+ - Round-trip OOXML preservation — open, edit, export, reopen in Word without repair prompts
36
+ - Workflow overlays with scope-based editing constraints
37
+ - Runtime-backed read models for document structure, review state, selection, compatibility, and fields
38
+ - Document comparison with LCS-based diffing and redline export
39
+ - Legal document analysis (bookmarks, cross-references, defined terms, signature blocks)
40
+ - Real-time collaboration via Yjs
41
+
42
+ **Human-AI parity in practice:**
43
+ - The human clicks "Add Comment" on selected text; the agent calls `addComment({ anchor, body })` on the same selection
44
+ - The human reviews tracked changes in the sidebar; the agent reads `getTrackedChanges()` and makes the same accept/reject decisions
45
+ - The human exports via toolbar; the agent calls `exportDocx()` after the same `getCompatibilityReport()` check
46
+ - Workflow overlays constrain both humans and agents identically — `getInteractionGuardSnapshot()` is the single source of posture truth
47
+ - The AI action policy module gates 37 discrete agent operations with risk classification and context validation
48
+
49
+ ### Workblocks
50
+
51
+ 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.
52
+
53
+ **Categories:**
54
+ - **Document processing** — invoice extraction, PDF template OCR, bulk processing, email-to-action
55
+ - **AI agents** — explore, flow debugging, data querying, task launching, authoring
56
+ - **Analytics** — execution history, company insights, timeline queries
57
+ - **Learning** — human correction capture, learning set curation, embedding management
58
+ - **Infrastructure** — registry, health checks, credential management (9 types)
59
+
60
+ **Human-AI parity in practice:**
61
+ - Users create workblocks through VS Code Studio with prompts and visual editing; authoring agents create them through the same manifest schema and file tools
62
+ - Users validate invoice fields in a React form; agents validate the same fields through typed task inputs/outputs
63
+ - Both humans and agents operate within the same workblock execution engine — same task graph, same data flow, same error handling
64
+
65
+ ## The Beyond Work Runtime
66
+
67
+ These components plug into a four-layer platform:
68
+
69
+ ```
70
+ User-Facing (Layer 4)
71
+ neui (Next.js 15), bport (BFF), MCP server, VS Code extension
72
+ |
73
+ Workblocks (Layer 3)
74
+ 20+ composable task modules: agents, data processing, credentials
75
+ |
76
+ Platform Services (Layer 2)
77
+ bworker (Go), modelproxy (LiteLLM), registry, pythonworker, Temporal
78
+ |
79
+ AWS Infrastructure (Layer 1)
80
+ EKS, Crossplane, Terraform, PostgreSQL, Redis, Qdrant, S3, SQS
81
+ ```
82
+
83
+ 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.
23
84
 
24
85
  ## Current Package Reality
25
86
 
26
- The repository may also carry broader branch-local work, but the shipped contract today is still docx-first:
87
+ The shipped contract today is docx-first:
27
88
 
28
89
  - `docx` is the only implemented and shipped runtime contract
29
90
  - `xlsx` is planned work, not part of the current package contract
@@ -93,11 +154,11 @@ The current shipped ESM exports include:
93
154
  - `@beyondwork/docx-react-component/tailwind`
94
155
  - `@beyondwork/docx-react-component/api/public-types`
95
156
 
96
- Use `docs/reference/public-api.md` and `docs/reference/public-api.manifest.json` for the current contract inventory and stability guidance.
157
+ Use [`docs/reference/public-api.md`](docs/reference/public-api.md) and `docs/reference/public-api.manifest.json` for the current contract inventory and stability guidance.
97
158
 
98
159
  ## Product Contract
99
160
 
100
- For every format the repo eventually ships, the standard is:
161
+ For every component this repo ships, the standard is:
101
162
 
102
163
  > Open -> edit -> save -> reopen in the host application without damage.
103
164
 
@@ -158,7 +219,38 @@ Current integration honesty:
158
219
 
159
220
  The CCEP corpus is kept on `main` as a maintainer-safe smoke-doc source set for agreement-heavy validation and wrapper or agent benchmarking.
160
221
 
161
- Maintainer prompts, operator runbooks, and proof/closure material remain important, but they are not the first reading path for package consumers.
222
+ ### Technical Wiki
223
+
224
+ - [`docs/wiki/`](docs/wiki/) — Feature-by-feature technical documentation (25+ topics covering OOXML, ProseMirror, runtime, and platform)
225
+
226
+ ## Agent Integration
227
+
228
+ Agents consume the editor through a consistent pipeline:
229
+
230
+ ```
231
+ inspect --> locate --> mutate --> validate --> export
232
+ ```
233
+
234
+ Beyond Work platform agents extend this with propose/approve gates:
235
+
236
+ ```
237
+ inspect --> locate --> propose --> approve --> mutate --> validate --> export
238
+ ```
239
+
240
+ The package exposes purpose-built read models — not a monolithic state dump:
241
+
242
+ | Read model | Getter | Purpose |
243
+ |---|---|---|
244
+ | Document surface | `getRenderSnapshot()` | Structure, blocks, text, selection |
245
+ | Comments | `getComments()` | Thread state, resolution, anchors |
246
+ | Tracked changes | `getTrackedChanges()` | Revision inventory, actionability |
247
+ | Workflow scope | `getWorkflowScopeSnapshot()` | Scope boundaries, work items, mode |
248
+ | Interaction guard | `getInteractionGuardSnapshot()` | Effective mode, blocked reasons |
249
+ | Compatibility | `getCompatibilityReport()` | Export risk assessment |
250
+ | Warnings | `getWarnings()` | Active fidelity or mutation warnings |
251
+ | Fields | `getFieldSnapshot()` | Field posture and refresh state |
252
+
253
+ Agents should read the narrow snapshot that answers the next question, not pull the full render snapshot for every decision.
162
254
 
163
255
  ## Packaging And Release
164
256
 
@@ -181,9 +273,7 @@ Maintainer prompts, operator runbooks, and proof/closure material remain importa
181
273
 
182
274
  ## Guiding Principle
183
275
 
184
- This repo is not trying to become a generic office clone.
185
-
186
- It is building fidelity-first office-document runtimes with explicit preservation and calm, reviewable UI.
276
+ 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.
187
277
 
188
278
  ## Using the package
189
279
 
@@ -663,7 +753,7 @@ Fired when the document transitions between clean and dirty (unsaved) states.
663
753
 
664
754
  #### `story_changed`
665
755
 
666
- Fired when the user navigates between document stories (e.g. main body header/footer).
756
+ Fired when the user navigates between document stories (e.g. main body -> header/footer).
667
757
 
668
758
  ```ts
669
759
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beyondwork/docx-react-component",
3
3
  "publisher": "beyondwork",
4
- "version": "1.0.36",
4
+ "version": "1.0.38",
5
5
  "description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
6
6
  "type": "module",
7
7
  "sideEffects": [
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Package version string — pinned to package.json `version`.
3
+ *
4
+ * Consumed by export paths that embed a producer identity (e.g.
5
+ * `/docProps/app.xml` `<Application>` tag) so exported packages can be
6
+ * traced back to the exact runtime release.
7
+ *
8
+ * Keep this in lockstep with `package.json#version`. A future enhancement
9
+ * would be to generate this file from package.json at build time; for now
10
+ * the string is committed directly so the runtime works without a build
11
+ * step for tests.
12
+ */
13
+ export const PACKAGE_VERSION = "1.0.36";
@@ -1,4 +1,60 @@
1
1
  import type { PersistedEditorSnapshot as RuntimePersistedEditorSnapshot } from "../core/state/editor-state.ts";
2
+ import type { WordReviewEditorLayoutFacet } from "../runtime/layout/public-facet.ts";
3
+
4
+ export type {
5
+ WordReviewEditorLayoutFacet,
6
+ LayoutFacetEvent,
7
+ LayoutFacetInvalidationReason,
8
+ PublicBlockFragment,
9
+ PublicBlockMeasurement,
10
+ PublicFieldDirtinessReport,
11
+ PublicLineBox,
12
+ PublicMeasurementFidelity,
13
+ PublicNoteAllocation,
14
+ PublicPageAnchor,
15
+ PublicPageNode,
16
+ PublicPageRegion,
17
+ PublicPageRegions,
18
+ PublicPageSpan,
19
+ PublicResolvedPageStories,
20
+ PublicResolvedParagraphFormatting,
21
+ PublicResolvedRunFormatting,
22
+ PublicSectionNode,
23
+ // R0.5: named page-format + margin-preset catalogs
24
+ PageFormatDefinition,
25
+ ActivePageFormat,
26
+ MarginPresetDefinition,
27
+ ActiveMarginPreset,
28
+ RenderZoomSummary,
29
+ } from "../runtime/layout/public-facet.ts";
30
+
31
+ // R1: render-kernel shapes (frame, decoration index, anchor index) — these
32
+ // are exposed additively so consumers can read the future `getRenderFrame`
33
+ // output without depending on the internal render kernel module.
34
+ export type {
35
+ RenderFrame,
36
+ RenderPage,
37
+ RenderPageRegions,
38
+ RenderStoryRegion,
39
+ RenderBlock,
40
+ RenderLine,
41
+ RenderLineAnchor,
42
+ RenderBlockDecoration,
43
+ RenderFrameRect,
44
+ RenderZoom,
45
+ RenderAnchorIndex,
46
+ RenderAnchorQuery,
47
+ RenderFrameQueryOptions,
48
+ RenderHitResult,
49
+ RenderKernelEvent,
50
+ RenderPoint,
51
+ DecorationIndex,
52
+ PageChromeReservations,
53
+ } from "../runtime/render/index.ts";
54
+
55
+ // R0: scope-rail posture vocabulary and chrome pin state — exposed so
56
+ // host apps can drive the editor role and render custom posture menus.
57
+ export type { ScopeRailPosture } from "../runtime/workflow-rail-segments.ts";
2
58
 
3
59
  export type FieldFamily = import("../model/canonical-document.ts").FieldFamily;
4
60
  export type FieldRefreshStatus = import("../model/canonical-document.ts").FieldRefreshStatus;
@@ -767,6 +823,7 @@ export type SurfaceBlockSnapshot =
767
823
  keepNext?: boolean;
768
824
  keepLines?: boolean;
769
825
  pageBreakBefore?: boolean;
826
+ widowControl?: boolean;
770
827
  outlineLevel?: number;
771
828
  bidi?: boolean;
772
829
  suppressLineNumbers?: boolean;
@@ -1013,9 +1070,231 @@ export interface TableStructureContextSnapshot {
1013
1070
  mergeCells: TableOperationCapabilitySnapshot;
1014
1071
  splitCell: TableOperationCapabilitySnapshot;
1015
1072
  deleteTable: TableOperationCapabilitySnapshot;
1073
+ // P2g — table-level authoring ops
1074
+ setTableWidth: TableOperationCapabilitySnapshot;
1075
+ setTableAlignment: TableOperationCapabilitySnapshot;
1076
+ setTableIndent: TableOperationCapabilitySnapshot;
1077
+ setTableLayoutMode: TableOperationCapabilitySnapshot;
1078
+ setTableCellMargins: TableOperationCapabilitySnapshot;
1079
+ setTableBorders: TableOperationCapabilitySnapshot;
1080
+ setTableCaption: TableOperationCapabilitySnapshot;
1081
+ setTableDescription: TableOperationCapabilitySnapshot;
1082
+ // P2h — column / row sizing + row props
1083
+ setColumnWidth: TableOperationCapabilitySnapshot;
1084
+ distributeColumnsEvenly: TableOperationCapabilitySnapshot;
1085
+ setRowHeight: TableOperationCapabilitySnapshot;
1086
+ setRowCantSplit: TableOperationCapabilitySnapshot;
1087
+ setRowIsHeader: TableOperationCapabilitySnapshot;
1088
+ setRowAlignment: TableOperationCapabilitySnapshot;
1089
+ insertRows: TableOperationCapabilitySnapshot;
1090
+ insertColumns: TableOperationCapabilitySnapshot;
1091
+ // P2i — cell-level ops (over a locator)
1092
+ setCellBorders: TableOperationCapabilitySnapshot;
1093
+ setCellShading: TableOperationCapabilitySnapshot;
1094
+ clearCellShading: TableOperationCapabilitySnapshot;
1095
+ setCellMargins: TableOperationCapabilitySnapshot;
1096
+ setCellVerticalAlign: TableOperationCapabilitySnapshot;
1097
+ setCellTextDirection: TableOperationCapabilitySnapshot;
1098
+ setCellNoWrap: TableOperationCapabilitySnapshot;
1099
+ setCellFitText: TableOperationCapabilitySnapshot;
1016
1100
  };
1017
1101
  }
1018
1102
 
1103
+ // ─── Table typed-op surface (P2.5) ───────────────────────────────────────────
1104
+
1105
+ /**
1106
+ * Width/height unit used by OOXML table properties.
1107
+ */
1108
+ export interface PublicTableWidth {
1109
+ value: number;
1110
+ type: "dxa" | "auto" | "pct" | "nil";
1111
+ }
1112
+
1113
+ /**
1114
+ * Reusable border-spec shape for table-level and cell-level borders.
1115
+ * Matches OOXML w:*Borders child attributes.
1116
+ */
1117
+ export interface PublicBorderSpec {
1118
+ value?: string;
1119
+ size?: number;
1120
+ space?: number;
1121
+ color?: string;
1122
+ }
1123
+
1124
+ export interface PublicTableBorders {
1125
+ top?: PublicBorderSpec;
1126
+ left?: PublicBorderSpec;
1127
+ bottom?: PublicBorderSpec;
1128
+ right?: PublicBorderSpec;
1129
+ insideH?: PublicBorderSpec;
1130
+ insideV?: PublicBorderSpec;
1131
+ }
1132
+
1133
+ export interface PublicTableCellBorders {
1134
+ top?: PublicBorderSpec;
1135
+ left?: PublicBorderSpec;
1136
+ bottom?: PublicBorderSpec;
1137
+ right?: PublicBorderSpec;
1138
+ insideH?: PublicBorderSpec;
1139
+ insideV?: PublicBorderSpec;
1140
+ }
1141
+
1142
+ export interface PublicTableCellMargins {
1143
+ top?: number;
1144
+ left?: number;
1145
+ bottom?: number;
1146
+ right?: number;
1147
+ }
1148
+
1149
+ export interface PublicCellShading {
1150
+ fill?: string;
1151
+ color?: string;
1152
+ val?: string;
1153
+ }
1154
+
1155
+ /**
1156
+ * Addresses a cell (or set of cells) for locator-driven cell-level ops.
1157
+ *
1158
+ * - `anchor`: the currently anchored cell from the active selection
1159
+ * - `index`: explicit (rowIndex, columnIndex)
1160
+ * - `rect`: rectangular range in logical grid coordinates
1161
+ * - `row` / `column`: all cells in a single row or column
1162
+ * - `selection`: whatever the active selection covers
1163
+ */
1164
+ export type PublicCellLocator =
1165
+ | { kind: "anchor" }
1166
+ | { kind: "index"; rowIndex: number; columnIndex: number }
1167
+ | {
1168
+ kind: "rect";
1169
+ rect: { top: number; left: number; bottom: number; right: number };
1170
+ }
1171
+ | { kind: "row"; rowIndex: number }
1172
+ | { kind: "column"; columnIndex: number }
1173
+ | { kind: "selection" };
1174
+
1175
+ /**
1176
+ * Discriminated union of every table operation the public API exposes.
1177
+ * Mirrors the internal `TableStructureOperation` variants on a stable
1178
+ * kebab-case `kind` discriminator.
1179
+ *
1180
+ * Stability: additive. New ops land as new `kind` values; existing
1181
+ * variants keep the same shape across minor versions.
1182
+ */
1183
+ export type TableOp =
1184
+ | { kind: "insert"; rows: number; columns: number }
1185
+ | { kind: "delete-table" }
1186
+ | { kind: "add-row-before" }
1187
+ | { kind: "add-row-after" }
1188
+ | { kind: "add-column-before" }
1189
+ | { kind: "add-column-after" }
1190
+ | { kind: "delete-row" }
1191
+ | { kind: "delete-column" }
1192
+ | { kind: "merge-cells" }
1193
+ | { kind: "split-cell" }
1194
+ | { kind: "set-cell-background"; color: string }
1195
+ // Table-level (P2g)
1196
+ | { kind: "set-table-width"; width: PublicTableWidth }
1197
+ | { kind: "set-table-alignment"; alignment: "left" | "center" | "right" }
1198
+ | { kind: "set-table-indent"; indent: PublicTableWidth }
1199
+ | { kind: "set-table-layout-mode"; mode: "fixed" | "autofit" }
1200
+ | { kind: "set-table-cell-margins"; margins: Partial<PublicTableCellMargins> }
1201
+ | { kind: "set-table-borders"; borders: Partial<PublicTableBorders> }
1202
+ | { kind: "set-table-style"; styleId: string | null }
1203
+ | { kind: "set-table-caption"; caption: string | null }
1204
+ | { kind: "set-table-description"; description: string | null }
1205
+ // Column / row sizing + row props (P2h)
1206
+ | { kind: "set-column-width"; columnIndex: number; twips: number }
1207
+ | {
1208
+ kind: "distribute-columns-evenly";
1209
+ columnRange?: { from: number; to: number };
1210
+ }
1211
+ | {
1212
+ kind: "set-row-height";
1213
+ rowIndex: number;
1214
+ twips: number;
1215
+ rule: "auto" | "atLeast" | "exact";
1216
+ }
1217
+ | { kind: "set-row-cant-split"; rowIndex: number; value: boolean }
1218
+ | { kind: "set-row-is-header"; rowIndex: number; value: boolean }
1219
+ | {
1220
+ kind: "set-row-alignment";
1221
+ rowIndex: number;
1222
+ alignment: "left" | "center" | "right";
1223
+ }
1224
+ | {
1225
+ kind: "insert-rows";
1226
+ rowIndex: number;
1227
+ at: "before" | "after";
1228
+ count: number;
1229
+ }
1230
+ | {
1231
+ kind: "insert-columns";
1232
+ columnIndex: number;
1233
+ at: "before" | "after";
1234
+ count: number;
1235
+ widths?: readonly number[];
1236
+ }
1237
+ // Cell-level (P2i)
1238
+ | {
1239
+ kind: "set-cell-borders";
1240
+ locator: PublicCellLocator;
1241
+ borders: Partial<PublicTableCellBorders>;
1242
+ }
1243
+ | {
1244
+ kind: "set-cell-shading";
1245
+ locator: PublicCellLocator;
1246
+ shading: Partial<PublicCellShading> | null;
1247
+ }
1248
+ | { kind: "clear-cell-shading"; locator: PublicCellLocator }
1249
+ | {
1250
+ kind: "set-cell-margins";
1251
+ locator: PublicCellLocator;
1252
+ margins: Partial<PublicTableCellMargins>;
1253
+ }
1254
+ | {
1255
+ kind: "set-cell-vertical-align";
1256
+ locator: PublicCellLocator;
1257
+ align: "top" | "center" | "bottom";
1258
+ }
1259
+ | {
1260
+ kind: "set-cell-text-direction";
1261
+ locator: PublicCellLocator;
1262
+ direction: "lrTb" | "tbRl" | "btLr";
1263
+ }
1264
+ | { kind: "set-cell-no-wrap"; locator: PublicCellLocator; value: boolean }
1265
+ | { kind: "set-cell-fit-text"; locator: PublicCellLocator; value: boolean };
1266
+
1267
+ /**
1268
+ * Outcome of `ref.tables.apply(op)`.
1269
+ */
1270
+ export interface TableOpResult {
1271
+ /** Whether the op mutated canonical document state. */
1272
+ changed: boolean;
1273
+ /**
1274
+ * When the op was refused (e.g. no table selection, merge not clean),
1275
+ * a short human-readable reason. `null` when the op committed.
1276
+ */
1277
+ coercedReason: string | null;
1278
+ /**
1279
+ * Fresh capability snapshot post-op. `null` when there is no table in
1280
+ * the current selection.
1281
+ */
1282
+ capabilities: TableStructureContextSnapshot | null;
1283
+ }
1284
+
1285
+ /**
1286
+ * The `tables` property on `WordReviewEditorRef`. Minimal surface in this
1287
+ * phase: a typed dispatch boundary for every table op plus a capability
1288
+ * read. Read-only geometry queries (columns, rows, render plan, ...)
1289
+ * land with the layout engine + render kernel integration.
1290
+ */
1291
+ export interface WordReviewEditorTablesFacet {
1292
+ /** Dispatch a typed table op through the runtime. */
1293
+ apply(op: TableOp): TableOpResult;
1294
+ /** Current capability snapshot for the active table selection. */
1295
+ getCapabilities(): TableStructureContextSnapshot | null;
1296
+ }
1297
+
1019
1298
  export interface PageRegionHitTest {
1020
1299
  region: "body" | "header" | "footer" | "margin" | "gutter";
1021
1300
  sectionIndex: number;
@@ -1044,8 +1323,50 @@ export interface EditorViewStateSnapshot {
1044
1323
  activeObjectFrame: LayoutMeasurement["objectFrame"] | null;
1045
1324
  measurement: LayoutMeasurement;
1046
1325
  isFocused: boolean;
1326
+ /**
1327
+ * Role-scoped chrome dimension (spec §6.4). Drives which action set the
1328
+ * top toolbar renders: `"editor"` exposes authoring (insert, format,
1329
+ * Mark-section menu, tracked-changes toggle), `"review"` exposes review
1330
+ * queue nav + accept/reject, `"workflow"` exposes work-item traversal.
1331
+ * Independent of `viewMode` / `documentMode`.
1332
+ */
1333
+ editorRole: EditorRole;
1334
+ /**
1335
+ * Pin state for detachable chrome surfaces — topnav and the selection
1336
+ * tier tool. Absent key means "docked default"; `detached: true` means
1337
+ * the surface renders as a floating, user-positioned panel. State is
1338
+ * runtime-owned, per-session, and survives snapshot rebuilds.
1339
+ */
1340
+ chromePins: ChromePinsState;
1341
+ }
1342
+
1343
+ /**
1344
+ * Role-scoped chrome dimension (spec §6.4 of runtime-rendering-and-chrome-phase.md).
1345
+ *
1346
+ * - `"editor"` — authoring posture. Toolbar surfaces format, insert menu,
1347
+ * Mark-section posture menu, tracked-changes display toggle, comment.
1348
+ * - `"review"` — reviewing posture. Toolbar surfaces prev/next, accept,
1349
+ * reject, accept-all / reject-all in scope, markup mode, comment.
1350
+ * - `"workflow"` — workflow-actor posture. Toolbar surfaces prev/next
1351
+ * work item, claim, skip, mark complete, mark blocked, jump to scope.
1352
+ */
1353
+ export type EditorRole = "editor" | "review" | "workflow";
1354
+
1355
+ /**
1356
+ * Chrome surfaces that can be detached into a floating, draggable panel.
1357
+ * Extend this union as new surfaces adopt the `DraggableFloat` primitive.
1358
+ */
1359
+ export type ChromePinSurface = "topnav" | "selectionTier";
1360
+
1361
+ export interface PinState {
1362
+ /** True when the surface is detached from its default dock. */
1363
+ detached: boolean;
1364
+ /** User-chosen offset in CSS px from the surface's dock origin. */
1365
+ offset: { x: number; y: number };
1047
1366
  }
1048
1367
 
1368
+ export type ChromePinsState = Partial<Record<ChromePinSurface, PinState>>;
1369
+
1049
1370
  export interface CommandStateSnapshot {
1050
1371
  canUndo: boolean;
1051
1372
  canRedo: boolean;
@@ -1916,13 +2237,46 @@ export interface WordReviewEditorRef {
1916
2237
  getRuntimeContextAnalytics(
1917
2238
  query?: RuntimeContextAnalyticsQuery,
1918
2239
  ): RuntimeContextAnalyticsSnapshot | null;
2240
+
2241
+ /**
2242
+ * Runtime-owned layout facet.
2243
+ *
2244
+ * The ergonomic surface for walking the page graph, resolving offsets to
2245
+ * fragments, inspecting formatting, and observing layout events. Prefer
2246
+ * this over `getPageLayoutSnapshot()` / `getDocumentNavigationSnapshot()`
2247
+ * for new code — those remain as thin adapters for compatibility.
2248
+ *
2249
+ * Returns `null` only when the runtime is still loading; once the `ready`
2250
+ * event has fired, the facet is always available.
2251
+ */
2252
+ readonly layout: WordReviewEditorLayoutFacet;
2253
+ /**
2254
+ * Typed dispatch boundary for every table operation the runtime
2255
+ * supports. Existing flat verbs (`addRowBefore`, `setCellBackground`,
2256
+ * …) remain as compatibility wrappers; new ops land through
2257
+ * `ref.tables.apply(op)` with a typed discriminated union.
2258
+ */
2259
+ readonly tables: WordReviewEditorTablesFacet;
1919
2260
  }
1920
2261
 
2262
+ /**
2263
+ * Density + role-scoped chrome preset.
2264
+ *
2265
+ * Historically a pure density knob (`selection` → `simple` → `advanced` →
2266
+ * `review` progressively exposed more chrome). `"workflow"` was added in
2267
+ * chrome-phase R1 to surface workflow-actor primary actions (prev/next
2268
+ * work item, claim, skip, mark complete, jump to scope) inline in the
2269
+ * top toolbar. Host apps that already use `chromePreset` keep working
2270
+ * unchanged; consumers that want the new workflow view set the preset
2271
+ * to `"workflow"`. For finer-grained role control independent of
2272
+ * density, set `editorRole` on the view state directly.
2273
+ */
1921
2274
  export type WordReviewEditorChromePreset =
1922
2275
  | "selection"
1923
2276
  | "simple"
1924
2277
  | "advanced"
1925
- | "review";
2278
+ | "review"
2279
+ | "workflow";
1926
2280
 
1927
2281
  export interface WordReviewEditorChromeOptions {
1928
2282
  showReviewQueueBar: boolean;
@@ -1970,3 +2324,50 @@ export interface WordReviewEditorChromeVisibility {
1970
2324
  statusBar: boolean;
1971
2325
  reviewRail: boolean;
1972
2326
  }
2327
+
2328
+ // ---------------------------------------------------------------------------
2329
+ // Bounded local-first predicted-text lane contract
2330
+ // ---------------------------------------------------------------------------
2331
+
2332
+ export type TextCommandAckKind =
2333
+ | "equivalent"
2334
+ | "adjusted"
2335
+ | "rejected"
2336
+ | "structural-divergence";
2337
+
2338
+ export interface ScopeTagTouch {
2339
+ /** Tag family: "comment" | "revision" | "field" | "bookmark" | "sdt" | "opaque" | custom string. */
2340
+ tagType: string;
2341
+ tagId: string;
2342
+ behavior: "extended" | "trimmed" | "split" | "detached" | "unchanged";
2343
+ range: { from: number; to: number };
2344
+ }
2345
+
2346
+ /**
2347
+ * Sync reconciliation result returned by `DocumentRuntime.applyActiveStoryTextCommand`.
2348
+ *
2349
+ * The predicted-text lane in the mounted editor dispatches a local PM transaction,
2350
+ * then calls the runtime synchronously. The ack tells the lane how to reconcile:
2351
+ *
2352
+ * - `equivalent`: runtime accepted the edit and produced the same text shape;
2353
+ * the lane can skip the Lane A PM rebuild entirely.
2354
+ * - `adjusted`: runtime accepted but normalized or added review marks; the lane
2355
+ * patches the affected range from the canonical snapshot.
2356
+ * - `rejected`: runtime blocked the edit (workflow, protection, read-only);
2357
+ * the lane rolls back the predicted PM range from its pre-image.
2358
+ * - `structural-divergence`: the command affected structure beyond the
2359
+ * predicted range; the lane falls back to a full PM rebuild.
2360
+ */
2361
+ export interface TextCommandAck {
2362
+ kind: TextCommandAckKind;
2363
+ /** Opaque id echoed back from the predicted op. Undefined for canonical callers. */
2364
+ opId?: string;
2365
+ /** Revision token of the document AFTER commit. Empty string when `kind === "rejected"`. */
2366
+ newRevisionToken: string;
2367
+ /** For `adjusted`: the canonical range that differs from the prediction. */
2368
+ adjustedRange?: { fromRuntime: number; toRuntime: number };
2369
+ /** For `rejected`: the blocked reasons. */
2370
+ blockedReasons?: readonly { code: string; message: string }[];
2371
+ /** Tag touches the runtime applied so the lane can redraw decorations without a PM rebuild. */
2372
+ scopeTagTouches?: readonly ScopeTagTouch[];
2373
+ }
@@ -35,6 +35,7 @@ import {
35
35
  } from "./text-commands.ts";
36
36
  import type { RevisionRecord as CanonicalRevisionRecord } from "../../model/canonical-document.ts";
37
37
  import { remapCommentThreads } from "../../review/store/comment-remapping.ts";
38
+ import { collectScopeTagTouches } from "../../review/store/scope-tag-diff.ts";
38
39
  import { applyRevisionRuntimeCommand } from "../../runtime/revision-runtime.ts";
39
40
  import type { RevisionStore } from "../../review/store/revision-store.ts";
40
41
 
@@ -878,6 +879,22 @@ function applyTextCommand(
878
879
  result.mapping,
879
880
  );
880
881
 
882
+ const scopeTagTouches = collectScopeTagTouches(
883
+ state.document.review.comments,
884
+ reviewState.document.review.comments,
885
+ state.document.review.revisions,
886
+ reviewState.document.review.revisions,
887
+ );
888
+ const mappingWithTouches: TransactionMapping = scopeTagTouches.length > 0
889
+ ? {
890
+ ...result.mapping,
891
+ metadata: {
892
+ ...(result.mapping.metadata ?? {}),
893
+ scopeTagTouches,
894
+ },
895
+ }
896
+ : result.mapping;
897
+
881
898
  return createTransaction(
882
899
  {
883
900
  ...state,
@@ -892,7 +909,7 @@ function applyTextCommand(
892
909
  {
893
910
  historyBoundary: "push",
894
911
  markDirty: true,
895
- mapping: result.mapping,
912
+ mapping: mappingWithTouches,
896
913
  effects: reviewState.effects,
897
914
  },
898
915
  );