@beyondwork/docx-react-component 1.0.37 → 1.0.39

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 (116) hide show
  1. package/package.json +41 -31
  2. package/src/api/public-types.ts +496 -1
  3. package/src/core/commands/section-layout-commands.ts +58 -0
  4. package/src/core/commands/table-grid.ts +431 -0
  5. package/src/core/commands/table-structure-commands.ts +845 -56
  6. package/src/core/commands/text-commands.ts +122 -2
  7. package/src/io/docx-session.ts +1 -0
  8. package/src/io/export/serialize-main-document.ts +2 -11
  9. package/src/io/export/serialize-numbering.ts +43 -10
  10. package/src/io/export/serialize-paragraph-formatting.ts +152 -0
  11. package/src/io/export/serialize-run-formatting.ts +90 -0
  12. package/src/io/export/serialize-styles.ts +212 -0
  13. package/src/io/export/serialize-tables.ts +74 -0
  14. package/src/io/export/table-properties-xml.ts +139 -4
  15. package/src/io/normalize/normalize-text.ts +15 -0
  16. package/src/io/ooxml/parse-fields.ts +10 -3
  17. package/src/io/ooxml/parse-footnotes.ts +60 -0
  18. package/src/io/ooxml/parse-headers-footers.ts +60 -0
  19. package/src/io/ooxml/parse-main-document.ts +137 -0
  20. package/src/io/ooxml/parse-numbering.ts +41 -1
  21. package/src/io/ooxml/parse-paragraph-formatting.ts +188 -0
  22. package/src/io/ooxml/parse-run-formatting.ts +129 -0
  23. package/src/io/ooxml/parse-styles.ts +31 -0
  24. package/src/io/ooxml/parse-tables.ts +249 -0
  25. package/src/io/ooxml/xml-attr-helpers.ts +60 -0
  26. package/src/io/ooxml/xml-element.ts +19 -0
  27. package/src/model/canonical-document.ts +117 -3
  28. package/src/runtime/collab/event-types.ts +165 -0
  29. package/src/runtime/collab/index.ts +22 -0
  30. package/src/runtime/collab/remote-cursor-awareness.ts +93 -0
  31. package/src/runtime/collab/runtime-collab-sync.ts +273 -0
  32. package/src/runtime/document-layout.ts +4 -2
  33. package/src/runtime/document-navigation.ts +1 -1
  34. package/src/runtime/document-runtime.ts +248 -18
  35. package/src/runtime/layout/default-page-format.ts +96 -0
  36. package/src/runtime/layout/index.ts +47 -0
  37. package/src/runtime/layout/inert-layout-facet.ts +16 -0
  38. package/src/runtime/layout/layout-engine-instance.ts +100 -23
  39. package/src/runtime/layout/layout-invalidation.ts +14 -5
  40. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  41. package/src/runtime/layout/page-format-catalog.ts +233 -0
  42. package/src/runtime/layout/page-graph.ts +55 -0
  43. package/src/runtime/layout/paginate-paragraph-lines.ts +128 -0
  44. package/src/runtime/layout/paginated-layout-engine.ts +484 -37
  45. package/src/runtime/layout/project-block-fragments.ts +225 -0
  46. package/src/runtime/layout/public-facet.ts +748 -16
  47. package/src/runtime/layout/resolve-page-fields.ts +70 -0
  48. package/src/runtime/layout/resolve-page-previews.ts +185 -0
  49. package/src/runtime/layout/resolved-formatting-state.ts +30 -26
  50. package/src/runtime/layout/table-render-plan.ts +249 -0
  51. package/src/runtime/numbering-prefix.ts +5 -0
  52. package/src/runtime/paragraph-style-resolver.ts +194 -0
  53. package/src/runtime/render/block-fragment-projection.ts +35 -0
  54. package/src/runtime/render/decoration-resolver.ts +189 -0
  55. package/src/runtime/render/index.ts +57 -0
  56. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  57. package/src/runtime/render/render-frame-types.ts +317 -0
  58. package/src/runtime/render/render-kernel.ts +759 -0
  59. package/src/runtime/resolved-numbering-geometry.ts +9 -1
  60. package/src/runtime/surface-projection.ts +129 -9
  61. package/src/runtime/table-schema.ts +11 -0
  62. package/src/runtime/view-state.ts +67 -0
  63. package/src/runtime/workflow-markup.ts +1 -5
  64. package/src/runtime/workflow-rail-segments.ts +280 -0
  65. package/src/ui/WordReviewEditor.tsx +368 -19
  66. package/src/ui/editor-command-bag.ts +4 -0
  67. package/src/ui/editor-runtime-boundary.ts +16 -0
  68. package/src/ui/editor-shell-view.tsx +10 -0
  69. package/src/ui/editor-surface-controller.tsx +9 -1
  70. package/src/ui/headless/chrome-registry.ts +310 -15
  71. package/src/ui/headless/scoped-chrome-policy.ts +49 -1
  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/review-queue-bar.tsx +2 -14
  75. package/src/ui-tailwind/chrome/role-action-sets.ts +80 -0
  76. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  77. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +160 -0
  78. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +68 -92
  79. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  80. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +11 -0
  81. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  82. package/src/ui-tailwind/chrome/tw-table-border-picker.tsx +245 -0
  83. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +356 -140
  84. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +284 -0
  85. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +94 -0
  86. package/src/ui-tailwind/chrome-overlay/index.ts +16 -0
  87. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +96 -0
  88. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  89. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +4 -0
  90. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +11 -0
  91. package/src/ui-tailwind/editor-surface/page-slice-util.ts +15 -0
  92. package/src/ui-tailwind/editor-surface/perf-probe.ts +7 -1
  93. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +40 -4
  94. package/src/ui-tailwind/editor-surface/pm-decorations.ts +22 -12
  95. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +389 -0
  96. package/src/ui-tailwind/editor-surface/pm-schema.ts +40 -2
  97. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +144 -62
  98. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +179 -0
  99. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +559 -0
  100. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +224 -75
  101. package/src/ui-tailwind/editor-surface/tw-table-bands.css +61 -0
  102. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +19 -0
  103. package/src/ui-tailwind/index.ts +29 -0
  104. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  105. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  106. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  107. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  108. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  109. package/src/ui-tailwind/theme/editor-theme.css +498 -163
  110. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +680 -0
  111. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  112. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  113. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +104 -2
  114. package/src/ui-tailwind/tw-review-workspace.tsx +234 -21
  115. package/src/runtime/collab-review-sync.ts +0 -254
  116. package/src/ui-tailwind/editor-surface/pm-collab-plugins.ts +0 -40
@@ -1,6 +1,9 @@
1
1
  import type { PersistedEditorSnapshot as RuntimePersistedEditorSnapshot } from "../core/state/editor-state.ts";
2
+ import type { CanonicalParagraphFormatting, CanonicalRunFormatting } from "../model/canonical-document.ts";
2
3
  import type { WordReviewEditorLayoutFacet } from "../runtime/layout/public-facet.ts";
3
4
 
5
+ export type { CanonicalParagraphFormatting, CanonicalRunFormatting };
6
+
4
7
  export type {
5
8
  WordReviewEditorLayoutFacet,
6
9
  LayoutFacetEvent,
@@ -20,8 +23,42 @@ export type {
20
23
  PublicResolvedParagraphFormatting,
21
24
  PublicResolvedRunFormatting,
22
25
  PublicSectionNode,
26
+ // R0.5: named page-format + margin-preset catalogs
27
+ PageFormatDefinition,
28
+ ActivePageFormat,
29
+ MarginPresetDefinition,
30
+ ActiveMarginPreset,
31
+ RenderZoomSummary,
23
32
  } from "../runtime/layout/public-facet.ts";
24
33
 
34
+ // R1: render-kernel shapes (frame, decoration index, anchor index) — these
35
+ // are exposed additively so consumers can read the future `getRenderFrame`
36
+ // output without depending on the internal render kernel module.
37
+ export type {
38
+ RenderFrame,
39
+ RenderPage,
40
+ RenderPageRegions,
41
+ RenderStoryRegion,
42
+ RenderBlock,
43
+ RenderLine,
44
+ RenderLineAnchor,
45
+ RenderBlockDecoration,
46
+ RenderFrameRect,
47
+ RenderZoom,
48
+ RenderAnchorIndex,
49
+ RenderAnchorQuery,
50
+ RenderFrameQueryOptions,
51
+ RenderHitResult,
52
+ RenderKernelEvent,
53
+ RenderPoint,
54
+ DecorationIndex,
55
+ PageChromeReservations,
56
+ } from "../runtime/render/index.ts";
57
+
58
+ // R0: scope-rail posture vocabulary and chrome pin state — exposed so
59
+ // host apps can drive the editor role and render custom posture menus.
60
+ export type { ScopeRailPosture } from "../runtime/workflow-rail-segments.ts";
61
+
25
62
  export type FieldFamily = import("../model/canonical-document.ts").FieldFamily;
26
63
  export type FieldRefreshStatus = import("../model/canonical-document.ts").FieldRefreshStatus;
27
64
  export type SupportedFieldFamily = import("../model/canonical-document.ts").SupportedFieldFamily;
@@ -667,6 +704,8 @@ export type SurfaceInlineSegment =
667
704
  textColor?: string;
668
705
  };
669
706
  hyperlinkHref?: string;
707
+ /** Cascaded run formatting for this text segment (docDefaults → paragraph style chain → character style → direct). Added in Task 11. TODO Task 15: wire up from resolveEffectiveRunFormatting in surface-projection. */
708
+ resolvedRunFormatting?: CanonicalRunFormatting;
670
709
  }
671
710
  | {
672
711
  segmentId: string;
@@ -733,6 +772,13 @@ export interface SurfaceTableCellSnapshot {
733
772
  borderRight?: string | null;
734
773
  borderBottom?: string | null;
735
774
  borderLeft?: string | null;
775
+ /**
776
+ * R2a: space-joined CSS class names from the resolved table-style conditional
777
+ * regions (e.g. "band-firstRow band-band1Horz"). Consumers apply these to the
778
+ * cell so theme toggles repaint via CSS vars instead of re-projecting the
779
+ * surface. Direct shading overrides still win over band defaults at render.
780
+ */
781
+ bandClasses?: string | null;
736
782
  content: SurfaceBlockSnapshot[];
737
783
  }
738
784
 
@@ -743,6 +789,8 @@ export interface SurfaceTableRowSnapshot {
743
789
  height?: number;
744
790
  heightRule?: "auto" | "atLeast" | "exact";
745
791
  isHeader?: boolean;
792
+ /** R1b: row carries `w:cantSplit` — pagination keeps the whole table on one page. */
793
+ cantSplit?: boolean;
746
794
  }
747
795
 
748
796
  export interface ResolvedNumberingGeometrySnapshot {
@@ -763,6 +811,8 @@ export interface ResolvedNumberingSnapshot {
763
811
  isLegalNumbering?: boolean;
764
812
  suffix?: "tab" | "space" | "nothing";
765
813
  geometry: ResolvedNumberingGeometrySnapshot;
814
+ /** CASCADED marker run formatting: docDefaults → paragraph style chain rPr → paragraph-mark rPr → level rPr. Added in Task 11. The raw level rPr is still available at geometry.markerRunProperties. */
815
+ markerRunProperties?: CanonicalRunFormatting;
766
816
  }
767
817
 
768
818
  export type SurfaceBlockSnapshot =
@@ -779,6 +829,8 @@ export type SurfaceBlockSnapshot =
779
829
  numberingPrefix?: string;
780
830
  numberingSuffix?: "tab" | "space" | "nothing";
781
831
  resolvedNumbering?: ResolvedNumberingSnapshot;
832
+ /** Cascaded paragraph formatting: docDefaults → style chain → direct paragraph properties. Added in Task 11. */
833
+ resolvedParagraphFormatting?: CanonicalParagraphFormatting;
782
834
  alignment?: "left" | "center" | "right" | "both" | "distribute";
783
835
  spacing?: { before?: number; after?: number; line?: number; lineRule?: string };
784
836
  contextualSpacing?: boolean;
@@ -804,6 +856,8 @@ export type SurfaceBlockSnapshot =
804
856
  gridColumns: number[];
805
857
  alignment?: "left" | "center" | "right";
806
858
  tblLook?: {
859
+ /** R2d: raw `w:tblLook/@w:val` hex so vendor-extended bits survive round-trip. */
860
+ val?: string;
807
861
  firstRow?: boolean;
808
862
  lastRow?: boolean;
809
863
  firstColumn?: boolean;
@@ -1036,9 +1090,349 @@ export interface TableStructureContextSnapshot {
1036
1090
  mergeCells: TableOperationCapabilitySnapshot;
1037
1091
  splitCell: TableOperationCapabilitySnapshot;
1038
1092
  deleteTable: TableOperationCapabilitySnapshot;
1093
+ // P2g — table-level authoring ops
1094
+ setTableWidth: TableOperationCapabilitySnapshot;
1095
+ setTableAlignment: TableOperationCapabilitySnapshot;
1096
+ setTableIndent: TableOperationCapabilitySnapshot;
1097
+ setTableLayoutMode: TableOperationCapabilitySnapshot;
1098
+ setTableCellMargins: TableOperationCapabilitySnapshot;
1099
+ setTableBorders: TableOperationCapabilitySnapshot;
1100
+ setTableCaption: TableOperationCapabilitySnapshot;
1101
+ setTableDescription: TableOperationCapabilitySnapshot;
1102
+ // P2h — column / row sizing + row props
1103
+ setColumnWidth: TableOperationCapabilitySnapshot;
1104
+ distributeColumnsEvenly: TableOperationCapabilitySnapshot;
1105
+ setRowHeight: TableOperationCapabilitySnapshot;
1106
+ setRowCantSplit: TableOperationCapabilitySnapshot;
1107
+ setRowIsHeader: TableOperationCapabilitySnapshot;
1108
+ setRowAlignment: TableOperationCapabilitySnapshot;
1109
+ insertRows: TableOperationCapabilitySnapshot;
1110
+ insertColumns: TableOperationCapabilitySnapshot;
1111
+ // P2i — cell-level ops (over a locator)
1112
+ setCellBorders: TableOperationCapabilitySnapshot;
1113
+ setCellShading: TableOperationCapabilitySnapshot;
1114
+ clearCellShading: TableOperationCapabilitySnapshot;
1115
+ setCellMargins: TableOperationCapabilitySnapshot;
1116
+ setCellVerticalAlign: TableOperationCapabilitySnapshot;
1117
+ setCellTextDirection: TableOperationCapabilitySnapshot;
1118
+ setCellNoWrap: TableOperationCapabilitySnapshot;
1119
+ setCellFitText: TableOperationCapabilitySnapshot;
1039
1120
  };
1040
1121
  }
1041
1122
 
1123
+ // ─── Table typed-op surface (P2.5) ───────────────────────────────────────────
1124
+
1125
+ /**
1126
+ * Width/height unit used by OOXML table properties.
1127
+ */
1128
+ export interface PublicTableWidth {
1129
+ value: number;
1130
+ type: "dxa" | "auto" | "pct" | "nil";
1131
+ }
1132
+
1133
+ /**
1134
+ * Reusable border-spec shape for table-level and cell-level borders.
1135
+ * Matches OOXML w:*Borders child attributes.
1136
+ */
1137
+ export interface PublicBorderSpec {
1138
+ value?: string;
1139
+ size?: number;
1140
+ space?: number;
1141
+ color?: string;
1142
+ }
1143
+
1144
+ export interface PublicTableBorders {
1145
+ top?: PublicBorderSpec;
1146
+ left?: PublicBorderSpec;
1147
+ bottom?: PublicBorderSpec;
1148
+ right?: PublicBorderSpec;
1149
+ insideH?: PublicBorderSpec;
1150
+ insideV?: PublicBorderSpec;
1151
+ }
1152
+
1153
+ export interface PublicTableCellBorders {
1154
+ top?: PublicBorderSpec;
1155
+ left?: PublicBorderSpec;
1156
+ bottom?: PublicBorderSpec;
1157
+ right?: PublicBorderSpec;
1158
+ insideH?: PublicBorderSpec;
1159
+ insideV?: PublicBorderSpec;
1160
+ }
1161
+
1162
+ export interface PublicTableCellMargins {
1163
+ top?: number;
1164
+ left?: number;
1165
+ bottom?: number;
1166
+ right?: number;
1167
+ }
1168
+
1169
+ export interface PublicCellShading {
1170
+ fill?: string;
1171
+ color?: string;
1172
+ val?: string;
1173
+ }
1174
+
1175
+ /**
1176
+ * Addresses a cell (or set of cells) for locator-driven cell-level ops.
1177
+ *
1178
+ * - `anchor`: the currently anchored cell from the active selection
1179
+ * - `index`: explicit (rowIndex, columnIndex)
1180
+ * - `rect`: rectangular range in logical grid coordinates
1181
+ * - `row` / `column`: all cells in a single row or column
1182
+ * - `selection`: whatever the active selection covers
1183
+ */
1184
+ export type PublicCellLocator =
1185
+ | { kind: "anchor" }
1186
+ | { kind: "index"; rowIndex: number; columnIndex: number }
1187
+ | {
1188
+ kind: "rect";
1189
+ rect: { top: number; left: number; bottom: number; right: number };
1190
+ }
1191
+ | { kind: "row"; rowIndex: number }
1192
+ | { kind: "column"; columnIndex: number }
1193
+ | { kind: "selection" };
1194
+
1195
+ /**
1196
+ * Discriminated union of every table operation the public API exposes.
1197
+ * Mirrors the internal `TableStructureOperation` variants on a stable
1198
+ * kebab-case `kind` discriminator.
1199
+ *
1200
+ * Stability: additive. New ops land as new `kind` values; existing
1201
+ * variants keep the same shape across minor versions.
1202
+ */
1203
+ export type TableOp =
1204
+ | { kind: "insert"; rows: number; columns: number }
1205
+ | { kind: "delete-table" }
1206
+ | { kind: "add-row-before" }
1207
+ | { kind: "add-row-after" }
1208
+ | { kind: "add-column-before" }
1209
+ | { kind: "add-column-after" }
1210
+ | { kind: "delete-row" }
1211
+ | { kind: "delete-column" }
1212
+ | { kind: "merge-cells" }
1213
+ | { kind: "split-cell" }
1214
+ | { kind: "set-cell-background"; color: string }
1215
+ // Table-level (P2g)
1216
+ | { kind: "set-table-width"; width: PublicTableWidth }
1217
+ | { kind: "set-table-alignment"; alignment: "left" | "center" | "right" }
1218
+ | { kind: "set-table-indent"; indent: PublicTableWidth }
1219
+ | { kind: "set-table-layout-mode"; mode: "fixed" | "autofit" }
1220
+ | { kind: "set-table-cell-margins"; margins: Partial<PublicTableCellMargins> }
1221
+ | { kind: "set-table-borders"; borders: Partial<PublicTableBorders> }
1222
+ | { kind: "set-table-style"; styleId: string | null }
1223
+ | { kind: "set-table-caption"; caption: string | null }
1224
+ | { kind: "set-table-description"; description: string | null }
1225
+ // Column / row sizing + row props (P2h)
1226
+ | { kind: "set-column-width"; columnIndex: number; twips: number }
1227
+ | {
1228
+ kind: "distribute-columns-evenly";
1229
+ columnRange?: { from: number; to: number };
1230
+ }
1231
+ | {
1232
+ kind: "set-row-height";
1233
+ rowIndex: number;
1234
+ twips: number;
1235
+ rule: "auto" | "atLeast" | "exact";
1236
+ }
1237
+ | { kind: "set-row-cant-split"; rowIndex: number; value: boolean }
1238
+ | { kind: "set-row-is-header"; rowIndex: number; value: boolean }
1239
+ | {
1240
+ kind: "set-row-alignment";
1241
+ rowIndex: number;
1242
+ alignment: "left" | "center" | "right";
1243
+ }
1244
+ | {
1245
+ kind: "insert-rows";
1246
+ rowIndex: number;
1247
+ at: "before" | "after";
1248
+ count: number;
1249
+ }
1250
+ | {
1251
+ kind: "insert-columns";
1252
+ columnIndex: number;
1253
+ at: "before" | "after";
1254
+ count: number;
1255
+ widths?: readonly number[];
1256
+ }
1257
+ // Cell-level (P2i)
1258
+ | {
1259
+ kind: "set-cell-borders";
1260
+ locator: PublicCellLocator;
1261
+ borders: Partial<PublicTableCellBorders>;
1262
+ }
1263
+ | {
1264
+ kind: "set-cell-shading";
1265
+ locator: PublicCellLocator;
1266
+ shading: Partial<PublicCellShading> | null;
1267
+ }
1268
+ | { kind: "clear-cell-shading"; locator: PublicCellLocator }
1269
+ | {
1270
+ kind: "set-cell-margins";
1271
+ locator: PublicCellLocator;
1272
+ margins: Partial<PublicTableCellMargins>;
1273
+ }
1274
+ | {
1275
+ kind: "set-cell-vertical-align";
1276
+ locator: PublicCellLocator;
1277
+ align: "top" | "center" | "bottom";
1278
+ }
1279
+ | {
1280
+ kind: "set-cell-text-direction";
1281
+ locator: PublicCellLocator;
1282
+ direction: "lrTb" | "tbRl" | "btLr";
1283
+ }
1284
+ | { kind: "set-cell-no-wrap"; locator: PublicCellLocator; value: boolean }
1285
+ | { kind: "set-cell-fit-text"; locator: PublicCellLocator; value: boolean };
1286
+
1287
+ /**
1288
+ * Outcome of `ref.tables.apply(op)`.
1289
+ */
1290
+ export interface TableOpResult {
1291
+ /** Whether the op mutated canonical document state. */
1292
+ changed: boolean;
1293
+ /**
1294
+ * When the op was refused (e.g. no table selection, merge not clean),
1295
+ * a short human-readable reason. `null` when the op committed.
1296
+ */
1297
+ coercedReason: string | null;
1298
+ /**
1299
+ * Fresh capability snapshot post-op. `null` when there is no table in
1300
+ * the current selection.
1301
+ */
1302
+ capabilities: TableStructureContextSnapshot | null;
1303
+ }
1304
+
1305
+ /**
1306
+ * The `tables` property on `WordReviewEditorRef`. Minimal surface in this
1307
+ * phase: a typed dispatch boundary for every table op plus a capability
1308
+ * read. Read-only geometry queries (columns, rows, render plan, ...)
1309
+ * land with the layout engine + render kernel integration.
1310
+ */
1311
+ /**
1312
+ * Compact summary for one canonical table in the document.
1313
+ */
1314
+ export interface PublicTableSummary {
1315
+ /** 0-based index across top-level canonical table blocks (walk order). */
1316
+ tableBlockIndex: number;
1317
+ /** The projected surface block id ("table-{index}"). */
1318
+ blockId: string;
1319
+ /** Style chain head, if declared. */
1320
+ styleId: string | null;
1321
+ /** Rows and logical columns (including gridBefore/after padding). */
1322
+ rowCount: number;
1323
+ columnCount: number;
1324
+ /** Raw canonical gridColumns in twips. */
1325
+ gridColumnsTwips: readonly number[];
1326
+ /** Table-level alignment if declared. */
1327
+ alignment: "left" | "center" | "right" | null;
1328
+ /** Whether any row has a vMerge chain crossing it. */
1329
+ hasVerticalMerges: boolean;
1330
+ /** Whether any cell in the table has colspan > 1. */
1331
+ hasHorizontalSpans: boolean;
1332
+ /** 0-based page index where the table's start offset lands, if known. */
1333
+ pageIndex: number | null;
1334
+ }
1335
+
1336
+ /**
1337
+ * Public shape of the per-page render plan. Sanitized copy of the
1338
+ * internal `TableRenderPlan`: chrome consumers read here, not directly
1339
+ * from the runtime layout module.
1340
+ */
1341
+ export interface PublicTableRenderPlan {
1342
+ blockId: string;
1343
+ pageIndex: number;
1344
+ columnsTwips: readonly number[];
1345
+ bandClasses: {
1346
+ rows: readonly { rowIndex: number; regions: readonly string[] }[];
1347
+ cells: readonly {
1348
+ rowIndex: number;
1349
+ columnIndex: number;
1350
+ regions: readonly string[];
1351
+ }[];
1352
+ };
1353
+ verticalMerges: readonly {
1354
+ columnIndex: number;
1355
+ startRowIndex: number;
1356
+ endRowIndex: number;
1357
+ columnSpan: number;
1358
+ }[];
1359
+ repeatedHeaderRows: readonly {
1360
+ sourceRowIndex: number;
1361
+ virtualFragmentId: string;
1362
+ }[];
1363
+ columnResizeHandles: readonly {
1364
+ columnIndex: number;
1365
+ originTwips: number;
1366
+ heightTwips: number;
1367
+ }[];
1368
+ }
1369
+
1370
+ /**
1371
+ * Per-row height read derived from pagination measurement + explicit
1372
+ * row height declarations.
1373
+ */
1374
+ export interface PublicTableRowHeight {
1375
+ /** Measured height in twips from the pagination engine (fallback estimate
1376
+ * when no fragments exist for the row). */
1377
+ measured: number;
1378
+ /** Explicit w:trHeight value when declared. */
1379
+ explicit?: number;
1380
+ /** w:trHeight rule when declared. */
1381
+ rule?: "auto" | "atLeast" | "exact";
1382
+ /** Whether the row is marked as an OOXML header row (tblHeader). */
1383
+ isHeader: boolean;
1384
+ }
1385
+
1386
+ /**
1387
+ * Minimal public representation of a table style definition.
1388
+ */
1389
+ export interface PublicTableStyle {
1390
+ styleId: string;
1391
+ displayName: string;
1392
+ basedOn?: string;
1393
+ isDefault: boolean;
1394
+ }
1395
+
1396
+ /**
1397
+ * Events emitted by `ref.tables.subscribe(listener)`. Sourced from the
1398
+ * runtime change stream + layout facet events so chrome surfaces can
1399
+ * refresh their derived reads without polling.
1400
+ */
1401
+ export type PublicTableEvent =
1402
+ | { kind: "table_structure_changed"; revisionToken: string }
1403
+ | { kind: "table_style_changed"; revisionToken: string }
1404
+ | { kind: "table_render_plan_ready"; revision: number }
1405
+ | { kind: "table_capabilities_changed" };
1406
+
1407
+ export interface WordReviewEditorTablesFacet {
1408
+ /** Dispatch a typed table op through the runtime. */
1409
+ apply(op: TableOp): TableOpResult;
1410
+ /** Current capability snapshot for the active table selection. */
1411
+ getCapabilities(): TableStructureContextSnapshot | null;
1412
+ /** List every top-level table in the main document. */
1413
+ getTables(options?: { sectionIndex?: number }): PublicTableSummary[];
1414
+ /** Summary for a single table by tableBlockIndex, or `null` when out of range. */
1415
+ getTable(tableBlockIndex: number): PublicTableSummary | null;
1416
+ /** The table the active selection currently sits in, or `null`. */
1417
+ getTableForSelection(): PublicTableSummary | null;
1418
+ /** Per-page render plan for a table, or `null` when no table / no kernel. */
1419
+ getRenderPlan(
1420
+ tableBlockIndex: number,
1421
+ pageIndex?: number,
1422
+ ): PublicTableRenderPlan | null;
1423
+ /** Current gridColumns in twips for the table. */
1424
+ getColumnWidths(tableBlockIndex: number): readonly number[];
1425
+ /** Per-row height reads, combining pagination measurement + explicit declarations. */
1426
+ getRowHeights(tableBlockIndex: number): readonly PublicTableRowHeight[];
1427
+ /** Table-style subset of the document style catalog. */
1428
+ getStyleCatalog(): readonly PublicTableStyle[];
1429
+ /**
1430
+ * Subscribe to table-facet events. Returns an unsubscribe function.
1431
+ * Chrome consumers use this instead of polling `getTables()`.
1432
+ */
1433
+ subscribe(listener: (event: PublicTableEvent) => void): () => void;
1434
+ }
1435
+
1042
1436
  export interface PageRegionHitTest {
1043
1437
  region: "body" | "header" | "footer" | "margin" | "gutter";
1044
1438
  sectionIndex: number;
@@ -1067,8 +1461,57 @@ export interface EditorViewStateSnapshot {
1067
1461
  activeObjectFrame: LayoutMeasurement["objectFrame"] | null;
1068
1462
  measurement: LayoutMeasurement;
1069
1463
  isFocused: boolean;
1464
+ /**
1465
+ * Role-scoped chrome dimension (spec §6.4). Drives which action set the
1466
+ * top toolbar renders: `"editor"` exposes authoring (insert, format,
1467
+ * Mark-section menu, tracked-changes toggle), `"review"` exposes review
1468
+ * queue nav + accept/reject, `"workflow"` exposes work-item traversal.
1469
+ * Independent of `viewMode` / `documentMode`.
1470
+ */
1471
+ editorRole: EditorRole;
1472
+ /**
1473
+ * Pin state for detachable chrome surfaces — topnav and the selection
1474
+ * tier tool. Absent key means "docked default"; `detached: true` means
1475
+ * the surface renders as a floating, user-positioned panel. State is
1476
+ * runtime-owned, per-session, and survives snapshot rebuilds.
1477
+ */
1478
+ chromePins: ChromePinsState;
1479
+ }
1480
+
1481
+ /**
1482
+ * Role-scoped chrome dimension (spec §6.4 of runtime-rendering-and-chrome-phase.md).
1483
+ *
1484
+ * - `"editor"` — authoring posture. Role action region surfaces the two
1485
+ * review-layer icons: add comment + inline tracked-changes toggle. Left
1486
+ * cluster carries formatting + insert/update actions as usual. Scope
1487
+ * posture is owned by the `"workflow"` role, not editor.
1488
+ * - `"review"` — reviewing posture. Role action region surfaces optional
1489
+ * sidebar-panel shortcuts (`onReviewSidebarTrackedChanges` /
1490
+ * `onReviewSidebarComments`, hidden unless the host provides callbacks),
1491
+ * add comment + inline tracked-changes toggle, review-queue prev/next +
1492
+ * counts + active label, per-item accept/reject, batch accept-all /
1493
+ * reject-all, and markup-mode selector.
1494
+ * - `"workflow"` — workflow-actor posture. Role action region surfaces
1495
+ * the scope posture menu + work-item prev/next, claim, skip, mark
1496
+ * complete, mark blocked, jump to scope.
1497
+ */
1498
+ export type EditorRole = "editor" | "review" | "workflow";
1499
+
1500
+ /**
1501
+ * Chrome surfaces that can be detached into a floating, draggable panel.
1502
+ * Extend this union as new surfaces adopt the `DraggableFloat` primitive.
1503
+ */
1504
+ export type ChromePinSurface = "topnav" | "selectionTier";
1505
+
1506
+ export interface PinState {
1507
+ /** True when the surface is detached from its default dock. */
1508
+ detached: boolean;
1509
+ /** User-chosen offset in CSS px from the surface's dock origin. */
1510
+ offset: { x: number; y: number };
1070
1511
  }
1071
1512
 
1513
+ export type ChromePinsState = Partial<Record<ChromePinSurface, PinState>>;
1514
+
1072
1515
  export interface CommandStateSnapshot {
1073
1516
  canUndo: boolean;
1074
1517
  canRedo: boolean;
@@ -1906,6 +2349,10 @@ export interface WordReviewEditorRef {
1906
2349
  getProtectionSnapshot(): ProtectionSnapshot;
1907
2350
  setWorkspaceMode(mode: WorkspaceMode): void;
1908
2351
  setZoom(level: ZoomLevel): void;
2352
+ /** Switch the per-role top-chrome action set. Drives toolbar + review rail. */
2353
+ setEditorRole(role: EditorRole): void;
2354
+ /** Persist a detach/attach pin for a chrome surface across snapshot rebuilds. */
2355
+ setChromePin(surface: ChromePinSurface, pin: PinState | null): void;
1909
2356
  insertSectionBreak(type: SectionBreakType, options?: { afterSectionIndex?: number }): void;
1910
2357
  deleteSectionBreak(sectionIndex: number): void;
1911
2358
  updateSectionLayout(sectionIndex: number, patch: SectionLayoutPatch): void;
@@ -1952,16 +2399,49 @@ export interface WordReviewEditorRef {
1952
2399
  * event has fired, the facet is always available.
1953
2400
  */
1954
2401
  readonly layout: WordReviewEditorLayoutFacet;
2402
+ /**
2403
+ * Typed dispatch boundary for every table operation the runtime
2404
+ * supports. Existing flat verbs (`addRowBefore`, `setCellBackground`,
2405
+ * …) remain as compatibility wrappers; new ops land through
2406
+ * `ref.tables.apply(op)` with a typed discriminated union.
2407
+ */
2408
+ readonly tables: WordReviewEditorTablesFacet;
1955
2409
  }
1956
2410
 
2411
+ /**
2412
+ * Density + role-scoped chrome preset.
2413
+ *
2414
+ * Historically a pure density knob (`selection` → `simple` → `advanced` →
2415
+ * `review` progressively exposed more chrome). `"workflow"` was added in
2416
+ * chrome-phase R1 to surface workflow-actor primary actions (prev/next
2417
+ * work item, claim, skip, mark complete, jump to scope) inline in the
2418
+ * top toolbar. Host apps that already use `chromePreset` keep working
2419
+ * unchanged; consumers that want the new workflow view set the preset
2420
+ * to `"workflow"`. For finer-grained role control independent of
2421
+ * density, set `editorRole` on the view state directly.
2422
+ */
1957
2423
  export type WordReviewEditorChromePreset =
1958
2424
  | "selection"
1959
2425
  | "simple"
1960
2426
  | "advanced"
1961
- | "review";
2427
+ | "review"
2428
+ | "workflow";
1962
2429
 
1963
2430
  export interface WordReviewEditorChromeOptions {
2431
+ /**
2432
+ * @deprecated The legacy second-strip review queue bar has been collapsed
2433
+ * into the role action region (spec §6.4). Set `role="review"` on the
2434
+ * editor to surface queue Prev/Next + accept/reject inline in the top
2435
+ * toolbar. This flag is retained for type back-compat and has no runtime
2436
+ * effect; it will be removed in a future release.
2437
+ */
1964
2438
  showReviewQueueBar: boolean;
2439
+ /**
2440
+ * @deprecated The "Mark section" button lived on the legacy
2441
+ * `TwReviewQueueBar`. Scope tagging is now the workflow role's posture
2442
+ * menu (`editor-scope-posture-menu`, surfaced via `role="workflow"`).
2443
+ * Retained for type back-compat; will be removed in a future release.
2444
+ */
1965
2445
  showSectionTagAction: boolean;
1966
2446
  showReviewRail: boolean;
1967
2447
  }
@@ -1994,6 +2474,21 @@ export interface WordReviewEditorProps {
1994
2474
  onEvent?: (event: WordReviewEditorEvent) => void;
1995
2475
  onWarning?: (warning: EditorWarning) => void;
1996
2476
  onError?: (error: EditorError) => void;
2477
+ /**
2478
+ * Optional: opens the host's sidebar to the tracked-changes panel. When
2479
+ * supplied, the review role surfaces an inline "Tracked changes panel"
2480
+ * icon in the top toolbar's role action region (spec §6.4). The button
2481
+ * is hidden when this callback is not provided, so the base runtime
2482
+ * chrome is sidebar-agnostic by default and only the harness / host that
2483
+ * owns a sidebar opts in.
2484
+ */
2485
+ onReviewSidebarTrackedChanges?: () => void;
2486
+ /**
2487
+ * Optional: opens the host's sidebar to the comments panel. Same
2488
+ * hidden-by-default gating as `onReviewSidebarTrackedChanges` — the
2489
+ * inline "Comments panel" icon appears only when a callback is wired.
2490
+ */
2491
+ onReviewSidebarComments?: () => void;
1997
2492
  }
1998
2493
 
1999
2494
  export interface WordReviewEditorChromeVisibility {
@@ -21,6 +21,10 @@ import type {
21
21
  SectionBreakNode,
22
22
  SectionProperties,
23
23
  } from "../../model/canonical-document.ts";
24
+ import type {
25
+ MarginPresetDefinition,
26
+ PageFormatDefinition,
27
+ } from "../../runtime/layout/index.ts";
24
28
 
25
29
  // ---- Public types ----
26
30
 
@@ -57,6 +61,60 @@ export interface SectionLinkPatch {
57
61
  relationshipId?: string | null;
58
62
  }
59
63
 
64
+ // ---- Page-format / margin-preset patch builders (R0.5) ----
65
+
66
+ /**
67
+ * Build a `SectionLayoutPatch` that swaps the page size to a named format
68
+ * at the requested orientation. Width/height are swapped when orientation
69
+ * is `"landscape"` so the caller does not have to think about which twip
70
+ * goes on which axis.
71
+ *
72
+ * `custom` formats carry `portraitWidthTwips === 0`; callers should pass
73
+ * their desired twips through `SectionLayoutPatch.pageSize` directly in
74
+ * that case rather than going through this helper.
75
+ */
76
+ export function buildPageFormatPatch(
77
+ format: PageFormatDefinition,
78
+ orientation: "portrait" | "landscape",
79
+ ): SectionLayoutPatch {
80
+ if (format.portraitWidthTwips <= 0 || format.portraitHeightTwips <= 0) {
81
+ return {};
82
+ }
83
+ const [width, height] =
84
+ orientation === "landscape"
85
+ ? [format.portraitHeightTwips, format.portraitWidthTwips]
86
+ : [format.portraitWidthTwips, format.portraitHeightTwips];
87
+ return {
88
+ pageSize: {
89
+ width,
90
+ height,
91
+ orientation,
92
+ },
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Build a `SectionLayoutPatch` that applies a named margin preset to the
98
+ * section. `custom` presets carry zero values and are a no-op here; the
99
+ * caller should pass explicit twips through `SectionLayoutPatch.pageMargins`.
100
+ */
101
+ export function buildMarginPresetPatch(
102
+ preset: MarginPresetDefinition,
103
+ ): SectionLayoutPatch {
104
+ if (preset.id === "custom") {
105
+ return {};
106
+ }
107
+ return {
108
+ pageMargins: {
109
+ top: preset.topTwips,
110
+ bottom: preset.bottomTwips,
111
+ left: preset.leftTwips,
112
+ right: preset.rightTwips,
113
+ gutter: preset.gutterTwips,
114
+ },
115
+ };
116
+ }
117
+
60
118
  // ---- Insert section break ----
61
119
 
62
120
  /**