@beyondwork/docx-react-component 1.0.37 → 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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +319 -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 +815 -55
  6. package/src/io/export/serialize-main-document.ts +2 -11
  7. package/src/io/export/serialize-numbering.ts +1 -2
  8. package/src/io/export/serialize-tables.ts +74 -0
  9. package/src/io/export/table-properties-xml.ts +139 -4
  10. package/src/io/normalize/normalize-text.ts +15 -0
  11. package/src/io/ooxml/parse-footnotes.ts +60 -0
  12. package/src/io/ooxml/parse-headers-footers.ts +60 -0
  13. package/src/io/ooxml/parse-main-document.ts +137 -0
  14. package/src/io/ooxml/parse-tables.ts +249 -0
  15. package/src/model/canonical-document.ts +34 -0
  16. package/src/runtime/document-layout.ts +4 -2
  17. package/src/runtime/document-navigation.ts +1 -1
  18. package/src/runtime/document-runtime.ts +114 -0
  19. package/src/runtime/layout/default-page-format.ts +96 -0
  20. package/src/runtime/layout/index.ts +45 -0
  21. package/src/runtime/layout/inert-layout-facet.ts +14 -0
  22. package/src/runtime/layout/layout-engine-instance.ts +33 -23
  23. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  24. package/src/runtime/layout/page-format-catalog.ts +233 -0
  25. package/src/runtime/layout/page-graph.ts +19 -0
  26. package/src/runtime/layout/paginated-layout-engine.ts +142 -9
  27. package/src/runtime/layout/project-block-fragments.ts +91 -0
  28. package/src/runtime/layout/public-facet.ts +709 -16
  29. package/src/runtime/layout/table-render-plan.ts +229 -0
  30. package/src/runtime/render/block-fragment-projection.ts +35 -0
  31. package/src/runtime/render/decoration-resolver.ts +189 -0
  32. package/src/runtime/render/index.ts +57 -0
  33. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  34. package/src/runtime/render/render-frame-types.ts +317 -0
  35. package/src/runtime/render/render-kernel.ts +755 -0
  36. package/src/runtime/view-state.ts +67 -0
  37. package/src/runtime/workflow-markup.ts +1 -5
  38. package/src/runtime/workflow-rail-segments.ts +280 -0
  39. package/src/ui/WordReviewEditor.tsx +84 -15
  40. package/src/ui/editor-shell-view.tsx +6 -0
  41. package/src/ui/headless/chrome-registry.ts +280 -14
  42. package/src/ui/headless/scoped-chrome-policy.ts +20 -1
  43. package/src/ui/headless/selection-tool-types.ts +10 -0
  44. package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
  45. package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
  46. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  47. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
  48. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
  49. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  50. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  51. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
  52. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
  53. package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
  54. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
  55. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  56. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
  57. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +4 -0
  58. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +11 -0
  59. package/src/ui-tailwind/editor-surface/perf-probe.ts +7 -1
  60. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +12 -1
  61. package/src/ui-tailwind/editor-surface/pm-decorations.ts +22 -12
  62. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +3 -0
  63. package/src/ui-tailwind/index.ts +33 -0
  64. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  65. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  66. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  67. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  68. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  69. package/src/ui-tailwind/theme/editor-theme.css +498 -163
  70. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
  71. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  72. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  73. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +69 -0
  74. package/src/ui-tailwind/tw-review-workspace.tsx +136 -1
@@ -1,4 +1,7 @@
1
- import type { WordReviewEditorChromePreset } from "../../api/public-types";
1
+ import type {
2
+ EditorRole,
3
+ WordReviewEditorChromePreset,
4
+ } from "../../api/public-types";
2
5
  import type { SelectionToolKind } from "./selection-tool-types";
3
6
 
4
7
  export type ChromeSurface = "top-toolbar" | "selection-tool";
@@ -6,6 +9,7 @@ export type ChromeDensity = "compact";
6
9
  export type ToolbarChromePlacement = "inline" | "overflow" | "hidden";
7
10
 
8
11
  export type ToolbarChromeItemId =
12
+ // Universal formatting + history
9
13
  | "history"
10
14
  | "text-style-selectors"
11
15
  | "inline-formatting"
@@ -24,7 +28,25 @@ export type ToolbarChromeItemId =
24
28
  | "workspace-mode"
25
29
  | "zoom"
26
30
  | "health"
27
- | "export";
31
+ | "export"
32
+ // R1: role-scoped action region entries
33
+ | "editor-scope-posture-menu"
34
+ | "review-queue-prev"
35
+ | "review-queue-next"
36
+ | "review-queue-counts"
37
+ | "review-queue-active-label"
38
+ | "review-accept"
39
+ | "review-reject"
40
+ | "review-accept-all"
41
+ | "review-reject-all"
42
+ | "review-markup-mode"
43
+ | "workflow-prev"
44
+ | "workflow-next"
45
+ | "workflow-mark-complete"
46
+ | "workflow-claim"
47
+ | "workflow-skip"
48
+ | "workflow-mark-blocked"
49
+ | "workflow-jump-to-scope";
28
50
 
29
51
  export interface ChromeRegistryEntryBase {
30
52
  id: string;
@@ -42,12 +64,33 @@ export interface ToolbarChromeRegistryEntry extends ChromeRegistryEntryBase {
42
64
  id: ToolbarChromeItemId;
43
65
  surfaces: ["top-toolbar"];
44
66
  presets: ReadonlyArray<WordReviewEditorChromePreset>;
67
+ /**
68
+ * Roles in which this entry appears. Empty = appears in every role.
69
+ * R1 uses this to filter the inline action set down to the per-role
70
+ * matrix described in the chrome-phase plan.
71
+ */
72
+ roles: ReadonlyArray<EditorRole>;
45
73
  fullPlacement: Exclude<ToolbarChromePlacement, "hidden">;
46
74
  compactPlacement: ToolbarChromePlacement;
47
75
  runtimeBehavior: "always" | "formatting" | "structure" | "comment";
48
76
  scopeBehavior?: "default" | "scoped-only" | "hidden-when-scoped";
77
+ /**
78
+ * Optional per-role placement override. When a role overrides the
79
+ * placement (for instance, "text-style-selectors" is `inline` in editor
80
+ * but `overflow` in review/workflow), list the role-specific placement
81
+ * here. Missing roles inherit `fullPlacement` / `compactPlacement`.
82
+ */
83
+ rolePlacement?: Partial<
84
+ Record<EditorRole, Exclude<ToolbarChromePlacement, "hidden">>
85
+ >;
49
86
  }
50
87
 
88
+ const ALL_ROLES: ReadonlyArray<EditorRole> = ["editor", "review", "workflow"];
89
+ const EDITOR_ONLY: ReadonlyArray<EditorRole> = ["editor"];
90
+ const REVIEW_ONLY: ReadonlyArray<EditorRole> = ["review"];
91
+ const WORKFLOW_ONLY: ReadonlyArray<EditorRole> = ["workflow"];
92
+ const EDITOR_AND_REVIEW: ReadonlyArray<EditorRole> = ["editor", "review"];
93
+
51
94
  export const SELECTION_TOOL_REGISTRY: ReadonlyArray<SelectionToolRegistryEntry> = [
52
95
  { id: "suggestion-review", surfaces: ["selection-tool"], group: "review", precedence: 10 },
53
96
  { id: "comment-thread", surfaces: ["selection-tool"], group: "review", precedence: 20 },
@@ -62,7 +105,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
62
105
  id: "history",
63
106
  surfaces: ["top-toolbar"],
64
107
  group: "history",
65
- presets: ["simple", "advanced", "review"],
108
+ presets: ["simple", "advanced", "review", "workflow"],
109
+ roles: ALL_ROLES,
66
110
  fullPlacement: "inline",
67
111
  compactPlacement: "inline",
68
112
  runtimeBehavior: "always",
@@ -71,61 +115,94 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
71
115
  id: "text-style-selectors",
72
116
  surfaces: ["top-toolbar"],
73
117
  group: "text",
74
- presets: ["advanced"],
118
+ presets: ["advanced", "workflow"],
119
+ roles: ALL_ROLES,
75
120
  fullPlacement: "inline",
76
121
  compactPlacement: "overflow",
77
122
  runtimeBehavior: "formatting",
123
+ rolePlacement: {
124
+ editor: "inline",
125
+ review: "overflow",
126
+ workflow: "overflow",
127
+ },
78
128
  },
79
129
  {
80
130
  id: "inline-formatting",
81
131
  surfaces: ["top-toolbar"],
82
132
  group: "text",
83
- presets: ["simple", "advanced", "review"],
133
+ presets: ["simple", "advanced", "review", "workflow"],
134
+ roles: ALL_ROLES,
84
135
  fullPlacement: "inline",
85
136
  compactPlacement: "inline",
86
137
  runtimeBehavior: "formatting",
138
+ rolePlacement: {
139
+ editor: "inline",
140
+ review: "overflow",
141
+ workflow: "overflow",
142
+ },
87
143
  },
88
144
  {
89
145
  id: "text-colors",
90
146
  surfaces: ["top-toolbar"],
91
147
  group: "text",
92
148
  presets: ["simple", "advanced"],
149
+ roles: EDITOR_AND_REVIEW,
93
150
  fullPlacement: "inline",
94
151
  compactPlacement: "inline",
95
152
  runtimeBehavior: "formatting",
153
+ rolePlacement: {
154
+ editor: "inline",
155
+ review: "overflow",
156
+ },
96
157
  },
97
158
  {
98
159
  id: "paragraph-alignment",
99
160
  surfaces: ["top-toolbar"],
100
161
  group: "paragraph",
101
162
  presets: ["simple", "advanced"],
163
+ roles: EDITOR_AND_REVIEW,
102
164
  fullPlacement: "inline",
103
165
  compactPlacement: "inline",
104
166
  runtimeBehavior: "formatting",
167
+ rolePlacement: {
168
+ editor: "inline",
169
+ review: "overflow",
170
+ },
105
171
  },
106
172
  {
107
173
  id: "list-actions",
108
174
  surfaces: ["top-toolbar"],
109
175
  group: "paragraph",
110
176
  presets: ["simple", "advanced"],
177
+ roles: EDITOR_AND_REVIEW,
111
178
  fullPlacement: "inline",
112
179
  compactPlacement: "overflow",
113
180
  runtimeBehavior: "formatting",
181
+ rolePlacement: {
182
+ editor: "inline",
183
+ review: "overflow",
184
+ },
114
185
  },
115
186
  {
116
187
  id: "indentation",
117
188
  surfaces: ["top-toolbar"],
118
189
  group: "paragraph",
119
190
  presets: ["simple", "advanced"],
191
+ roles: EDITOR_AND_REVIEW,
120
192
  fullPlacement: "inline",
121
193
  compactPlacement: "overflow",
122
194
  runtimeBehavior: "formatting",
195
+ rolePlacement: {
196
+ editor: "inline",
197
+ review: "overflow",
198
+ },
123
199
  },
124
200
  {
125
201
  id: "list-continuation",
126
202
  surfaces: ["top-toolbar"],
127
203
  group: "paragraph",
128
204
  presets: ["simple", "advanced"],
205
+ roles: EDITOR_ONLY,
129
206
  fullPlacement: "inline",
130
207
  compactPlacement: "overflow",
131
208
  runtimeBehavior: "formatting",
@@ -135,6 +212,7 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
135
212
  surfaces: ["top-toolbar"],
136
213
  group: "document",
137
214
  presets: ["simple", "advanced"],
215
+ roles: EDITOR_ONLY,
138
216
  fullPlacement: "inline",
139
217
  compactPlacement: "overflow",
140
218
  runtimeBehavior: "structure",
@@ -145,6 +223,7 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
145
223
  surfaces: ["top-toolbar"],
146
224
  group: "document",
147
225
  presets: ["advanced"],
226
+ roles: EDITOR_ONLY,
148
227
  fullPlacement: "inline",
149
228
  compactPlacement: "overflow",
150
229
  runtimeBehavior: "structure",
@@ -154,7 +233,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
154
233
  id: "scope-status",
155
234
  surfaces: ["top-toolbar"],
156
235
  group: "scope",
157
- presets: ["simple", "advanced", "review"],
236
+ presets: ["simple", "advanced", "review", "workflow"],
237
+ roles: ALL_ROLES,
158
238
  fullPlacement: "inline",
159
239
  compactPlacement: "inline",
160
240
  runtimeBehavior: "always",
@@ -164,7 +244,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
164
244
  id: "story-breadcrumb",
165
245
  surfaces: ["top-toolbar"],
166
246
  group: "scope",
167
- presets: ["simple", "advanced", "review"],
247
+ presets: ["simple", "advanced", "review", "workflow"],
248
+ roles: ALL_ROLES,
168
249
  fullPlacement: "inline",
169
250
  compactPlacement: "inline",
170
251
  runtimeBehavior: "always",
@@ -173,7 +254,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
173
254
  id: "sidebar-toggle",
174
255
  surfaces: ["top-toolbar"],
175
256
  group: "review",
176
- presets: ["simple", "advanced", "review"],
257
+ presets: ["simple", "advanced", "review", "workflow"],
258
+ roles: ALL_ROLES,
177
259
  fullPlacement: "inline",
178
260
  compactPlacement: "inline",
179
261
  runtimeBehavior: "always",
@@ -182,7 +264,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
182
264
  id: "comment",
183
265
  surfaces: ["top-toolbar"],
184
266
  group: "review",
185
- presets: ["simple", "advanced", "review"],
267
+ presets: ["simple", "advanced", "review", "workflow"],
268
+ roles: ALL_ROLES,
186
269
  fullPlacement: "inline",
187
270
  compactPlacement: "inline",
188
271
  runtimeBehavior: "comment",
@@ -191,7 +274,10 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
191
274
  id: "tracked-changes-toggle",
192
275
  surfaces: ["top-toolbar"],
193
276
  group: "review",
194
- presets: ["advanced", "review"],
277
+ // R1 promotes this toggle to every preset that shows a toolbar so the
278
+ // editor role can see inline tracked changes without switching preset.
279
+ presets: ["simple", "advanced", "review", "workflow"],
280
+ roles: ALL_ROLES,
195
281
  fullPlacement: "inline",
196
282
  compactPlacement: "inline",
197
283
  runtimeBehavior: "always",
@@ -200,7 +286,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
200
286
  id: "workspace-mode",
201
287
  surfaces: ["top-toolbar"],
202
288
  group: "view",
203
- presets: ["simple", "advanced", "review"],
289
+ presets: ["simple", "advanced", "review", "workflow"],
290
+ roles: ALL_ROLES,
204
291
  fullPlacement: "inline",
205
292
  compactPlacement: "inline",
206
293
  runtimeBehavior: "always",
@@ -209,7 +296,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
209
296
  id: "zoom",
210
297
  surfaces: ["top-toolbar"],
211
298
  group: "view",
212
- presets: ["simple", "advanced", "review"],
299
+ presets: ["simple", "advanced", "review", "workflow"],
300
+ roles: ALL_ROLES,
213
301
  fullPlacement: "inline",
214
302
  compactPlacement: "inline",
215
303
  runtimeBehavior: "always",
@@ -218,7 +306,8 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
218
306
  id: "health",
219
307
  surfaces: ["top-toolbar"],
220
308
  group: "status",
221
- presets: ["simple", "advanced", "review"],
309
+ presets: ["simple", "advanced", "review", "workflow"],
310
+ roles: ALL_ROLES,
222
311
  fullPlacement: "inline",
223
312
  compactPlacement: "inline",
224
313
  runtimeBehavior: "always",
@@ -227,9 +316,186 @@ export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry>
227
316
  id: "export",
228
317
  surfaces: ["top-toolbar"],
229
318
  group: "status",
230
- presets: ["simple", "advanced", "review"],
319
+ presets: ["simple", "advanced", "review", "workflow"],
320
+ roles: ALL_ROLES,
321
+ fullPlacement: "inline",
322
+ compactPlacement: "inline",
323
+ runtimeBehavior: "always",
324
+ },
325
+
326
+ // ───── R1: Editor-role primaries ──────────────────────────────────────
327
+ {
328
+ id: "editor-scope-posture-menu",
329
+ surfaces: ["top-toolbar"],
330
+ group: "scope",
331
+ presets: ["advanced", "review", "workflow"],
332
+ roles: EDITOR_ONLY,
333
+ fullPlacement: "inline",
334
+ compactPlacement: "overflow",
335
+ runtimeBehavior: "always",
336
+ },
337
+
338
+ // ───── R1: Review-role primaries ──────────────────────────────────────
339
+ {
340
+ id: "review-queue-prev",
341
+ surfaces: ["top-toolbar"],
342
+ group: "review-queue",
343
+ presets: ["review", "workflow"],
344
+ roles: REVIEW_ONLY,
345
+ fullPlacement: "inline",
346
+ compactPlacement: "inline",
347
+ runtimeBehavior: "always",
348
+ },
349
+ {
350
+ id: "review-queue-next",
351
+ surfaces: ["top-toolbar"],
352
+ group: "review-queue",
353
+ presets: ["review", "workflow"],
354
+ roles: REVIEW_ONLY,
355
+ fullPlacement: "inline",
356
+ compactPlacement: "inline",
357
+ runtimeBehavior: "always",
358
+ },
359
+ {
360
+ id: "review-queue-counts",
361
+ surfaces: ["top-toolbar"],
362
+ group: "review-queue",
363
+ presets: ["review"],
364
+ roles: REVIEW_ONLY,
365
+ fullPlacement: "inline",
366
+ compactPlacement: "overflow",
367
+ runtimeBehavior: "always",
368
+ },
369
+ {
370
+ id: "review-queue-active-label",
371
+ surfaces: ["top-toolbar"],
372
+ group: "review-queue",
373
+ presets: ["review"],
374
+ roles: REVIEW_ONLY,
375
+ fullPlacement: "inline",
376
+ compactPlacement: "overflow",
377
+ runtimeBehavior: "always",
378
+ },
379
+ {
380
+ id: "review-accept",
381
+ surfaces: ["top-toolbar"],
382
+ group: "review-action",
383
+ presets: ["review"],
384
+ roles: REVIEW_ONLY,
385
+ fullPlacement: "inline",
386
+ compactPlacement: "inline",
387
+ runtimeBehavior: "always",
388
+ },
389
+ {
390
+ id: "review-reject",
391
+ surfaces: ["top-toolbar"],
392
+ group: "review-action",
393
+ presets: ["review"],
394
+ roles: REVIEW_ONLY,
395
+ fullPlacement: "inline",
396
+ compactPlacement: "inline",
397
+ runtimeBehavior: "always",
398
+ },
399
+ {
400
+ id: "review-accept-all",
401
+ surfaces: ["top-toolbar"],
402
+ group: "review-action",
403
+ presets: ["review"],
404
+ roles: REVIEW_ONLY,
405
+ fullPlacement: "inline",
406
+ compactPlacement: "overflow",
407
+ runtimeBehavior: "always",
408
+ },
409
+ {
410
+ id: "review-reject-all",
411
+ surfaces: ["top-toolbar"],
412
+ group: "review-action",
413
+ presets: ["review"],
414
+ roles: REVIEW_ONLY,
415
+ fullPlacement: "inline",
416
+ compactPlacement: "overflow",
417
+ runtimeBehavior: "always",
418
+ },
419
+ {
420
+ id: "review-markup-mode",
421
+ surfaces: ["top-toolbar"],
422
+ group: "review-action",
423
+ presets: ["review"],
424
+ roles: REVIEW_ONLY,
425
+ fullPlacement: "inline",
426
+ compactPlacement: "inline",
427
+ runtimeBehavior: "always",
428
+ },
429
+
430
+ // ───── R1: Workflow-role primaries ────────────────────────────────────
431
+ {
432
+ id: "workflow-prev",
433
+ surfaces: ["top-toolbar"],
434
+ group: "workflow-queue",
435
+ presets: ["workflow"],
436
+ roles: WORKFLOW_ONLY,
437
+ fullPlacement: "inline",
438
+ compactPlacement: "inline",
439
+ runtimeBehavior: "always",
440
+ },
441
+ {
442
+ id: "workflow-next",
443
+ surfaces: ["top-toolbar"],
444
+ group: "workflow-queue",
445
+ presets: ["workflow"],
446
+ roles: WORKFLOW_ONLY,
447
+ fullPlacement: "inline",
448
+ compactPlacement: "inline",
449
+ runtimeBehavior: "always",
450
+ },
451
+ {
452
+ id: "workflow-mark-complete",
453
+ surfaces: ["top-toolbar"],
454
+ group: "workflow-action",
455
+ presets: ["workflow"],
456
+ roles: WORKFLOW_ONLY,
457
+ fullPlacement: "inline",
458
+ compactPlacement: "inline",
459
+ runtimeBehavior: "always",
460
+ },
461
+ {
462
+ id: "workflow-claim",
463
+ surfaces: ["top-toolbar"],
464
+ group: "workflow-action",
465
+ presets: ["workflow"],
466
+ roles: WORKFLOW_ONLY,
231
467
  fullPlacement: "inline",
232
468
  compactPlacement: "inline",
233
469
  runtimeBehavior: "always",
234
470
  },
471
+ {
472
+ id: "workflow-skip",
473
+ surfaces: ["top-toolbar"],
474
+ group: "workflow-action",
475
+ presets: ["workflow"],
476
+ roles: WORKFLOW_ONLY,
477
+ fullPlacement: "inline",
478
+ compactPlacement: "overflow",
479
+ runtimeBehavior: "always",
480
+ },
481
+ {
482
+ id: "workflow-mark-blocked",
483
+ surfaces: ["top-toolbar"],
484
+ group: "workflow-action",
485
+ presets: ["workflow"],
486
+ roles: WORKFLOW_ONLY,
487
+ fullPlacement: "inline",
488
+ compactPlacement: "overflow",
489
+ runtimeBehavior: "always",
490
+ },
491
+ {
492
+ id: "workflow-jump-to-scope",
493
+ surfaces: ["top-toolbar"],
494
+ group: "workflow-action",
495
+ presets: ["workflow"],
496
+ roles: WORKFLOW_ONLY,
497
+ fullPlacement: "inline",
498
+ compactPlacement: "overflow",
499
+ runtimeBehavior: "always",
500
+ },
235
501
  ];
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  ActiveListContext,
3
+ EditorRole,
3
4
  InteractionGuardSnapshot,
4
5
  WorkflowScopeSnapshot,
5
6
  WordReviewEditorChromePreset,
@@ -36,6 +37,13 @@ export interface ResolveScopedChromePolicyInput {
36
37
  interactionGuardSnapshot?: InteractionGuardSnapshot;
37
38
  workflowScopeSnapshot?: WorkflowScopeSnapshot | null;
38
39
  activeListContext?: ActiveListContext | null;
40
+ /**
41
+ * Active chrome role (spec §6.4). When supplied, the policy filters
42
+ * registry entries whose `roles` array excludes the active role. When
43
+ * omitted, the preset filter alone drives visibility (back-compat for
44
+ * callers that haven't adopted the role model yet).
45
+ */
46
+ role?: EditorRole;
39
47
  }
40
48
 
41
49
  export function resolveScopedChromePolicy(
@@ -62,6 +70,13 @@ export function resolveScopedChromePolicy(
62
70
  TOOLBAR_CHROME_REGISTRY.map((entry) => {
63
71
  let visible = entry.presets.includes(input.preset);
64
72
 
73
+ // Role filter — apply only when the caller supplied a role so existing
74
+ // host apps that haven't adopted the role model keep the pre-R1
75
+ // behavior (preset + capability only).
76
+ if (visible && input.role !== undefined) {
77
+ visible = entry.roles.includes(input.role);
78
+ }
79
+
65
80
  if (visible) {
66
81
  switch (entry.runtimeBehavior) {
67
82
  case "formatting":
@@ -93,8 +108,12 @@ export function resolveScopedChromePolicy(
93
108
  visible = !hasScopedContext;
94
109
  }
95
110
 
111
+ // Resolve placement: when the entry overrides placement for the
112
+ // active role, that wins over the preset-density default.
113
+ const fullPlacement =
114
+ (input.role && entry.rolePlacement?.[input.role]) ?? entry.fullPlacement;
96
115
  const placement = visible
97
- ? (input.compactMode ? entry.compactPlacement : entry.fullPlacement)
116
+ ? (input.compactMode ? entry.compactPlacement : fullPlacement)
98
117
  : "hidden";
99
118
  const enabled =
100
119
  visible &&
@@ -59,6 +59,16 @@ export interface FormattingInlineSelectionToolModel extends BaseSelectionToolMod
59
59
  italicActive: boolean;
60
60
  underlineActive: boolean;
61
61
  canAddComment: boolean;
62
+ /**
63
+ * One-click "apply" color for the text-color affordance. R2.5 plumbs
64
+ * the current `formattingState.textColor` (or the user's most recent
65
+ * pick) here so the mini-toolbar reflects the active color instead of
66
+ * a hardcoded blue. Consumers fall back to a fixed default when
67
+ * absent.
68
+ */
69
+ textColorDefault?: string;
70
+ /** Matching one-click highlight color. See `textColorDefault`. */
71
+ highlightColorDefault?: string;
62
72
  }
63
73
 
64
74
  export interface SuggestionReviewSelectionToolModel extends BaseSelectionToolModel {
@@ -7,12 +7,15 @@ import type {
7
7
 
8
8
  export function resolveChromePreset(
9
9
  chromePreset: WordReviewEditorProps["chromePreset"],
10
- _reviewMode: WordReviewEditorProps["reviewMode"] = "review",
10
+ reviewMode: WordReviewEditorProps["reviewMode"] = "review",
11
11
  ): WordReviewEditorChromePreset {
12
12
  if (chromePreset) {
13
13
  return chromePreset;
14
14
  }
15
- return "advanced";
15
+ // When the host does not set an explicit preset, pick one from the
16
+ // review-mode signal so review sessions get the review strip inline
17
+ // with the toolbar (rather than the density default "advanced").
18
+ return reviewMode === "review" ? "review" : "advanced";
16
19
  }
17
20
 
18
21
  export function resolveChromePresetOptions(
@@ -40,6 +43,14 @@ export function resolveChromePresetOptions(
40
43
  showSectionTagAction: true,
41
44
  showReviewRail: true,
42
45
  },
46
+ workflow: {
47
+ // Workflow role consolidates prev/next + mark-section into the top
48
+ // toolbar via the role-action region, so the separate queue bar
49
+ // strip is suppressed.
50
+ showReviewQueueBar: false,
51
+ showSectionTagAction: false,
52
+ showReviewRail: true,
53
+ },
43
54
  };
44
55
 
45
56
  return {
@@ -95,6 +106,16 @@ export function resolveChromeVisibilityForPreset(input: {
95
106
  statusBar: true,
96
107
  reviewRail: options.showReviewRail,
97
108
  },
109
+ workflow: {
110
+ toolbar: true,
111
+ alerts: true,
112
+ selectionOverlay: true,
113
+ contextToolbars: true,
114
+ contextAnalytics: true,
115
+ pageChrome: true,
116
+ statusBar: true,
117
+ reviewRail: options.showReviewRail,
118
+ },
98
119
  };
99
120
 
100
121
  return {
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Role → ordered primary-action id mapping for the top-toolbar's inline
3
+ * role-action region (spec §6.4).
4
+ *
5
+ * The registry in `chrome-registry.ts` knows which ids exist per role;
6
+ * this module decides the *rendering order* so the role region presents
7
+ * a tight, task-focused set instead of the preset-density order.
8
+ *
9
+ * Consumers of `TwRoleActionRegion` look up the array for the active
10
+ * role, iterate it in order, and let `scoped-chrome-policy` filter out
11
+ * anything currently invisible (for instance, review-role accept/reject
12
+ * buttons disappear when there are no pending revisions).
13
+ */
14
+
15
+ import type { EditorRole } from "../../api/public-types";
16
+ import type { ToolbarChromeItemId } from "../../ui/headless/chrome-registry";
17
+
18
+ /**
19
+ * Ordered role-action ids. Each array covers the *role-primary* actions
20
+ * only — the general left cluster (history, formatting, style selectors)
21
+ * and right cluster (tracked-changes, workspace mode, zoom, health,
22
+ * export) stay in the base `TwToolbar` layout regardless of role.
23
+ */
24
+ export const ROLE_ACTION_SETS: Record<
25
+ EditorRole,
26
+ ReadonlyArray<ToolbarChromeItemId>
27
+ > = {
28
+ editor: [
29
+ // Posture menu replaces the flat "Mark section" button per spec §6.4.
30
+ "editor-scope-posture-menu",
31
+ // Insert menu (tables / images / page breaks / section breaks) — only
32
+ // meaningful for authoring, so it lives in the editor role's action
33
+ // region rather than the base toolbar.
34
+ "insert-actions",
35
+ "update-actions",
36
+ "list-continuation",
37
+ ],
38
+ review: [
39
+ // Queue navigation + counts, collapsed from the old TwReviewQueueBar.
40
+ "review-queue-prev",
41
+ "review-queue-next",
42
+ "review-queue-counts",
43
+ "review-queue-active-label",
44
+ // Per-item accept/reject — the canonical review actions.
45
+ "review-accept",
46
+ "review-reject",
47
+ // Batch + markup-mode live in the overflow popover.
48
+ "review-accept-all",
49
+ "review-reject-all",
50
+ "review-markup-mode",
51
+ ],
52
+ workflow: [
53
+ // Work-item navigation (distinct from review-queue nav).
54
+ "workflow-prev",
55
+ "workflow-next",
56
+ // Primary workflow actions.
57
+ "workflow-mark-complete",
58
+ "workflow-claim",
59
+ "workflow-skip",
60
+ "workflow-mark-blocked",
61
+ "workflow-jump-to-scope",
62
+ ],
63
+ };
64
+
65
+ /**
66
+ * Reverse lookup: role → entry order with registry details merged in.
67
+ * Consumers who need to iterate role actions with presets/runtimeBehavior
68
+ * import from here to avoid re-running `Array.find` per render.
69
+ */
70
+ export function resolveRoleActionOrder(
71
+ role: EditorRole,
72
+ ): ReadonlyArray<ToolbarChromeItemId> {
73
+ return ROLE_ACTION_SETS[role];
74
+ }