@beyondwork/docx-react-component 1.0.38 → 1.0.40

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 (85) hide show
  1. package/package.json +41 -31
  2. package/src/api/public-types.ts +305 -6
  3. package/src/core/commands/table-structure-commands.ts +31 -2
  4. package/src/core/commands/text-commands.ts +122 -2
  5. package/src/index.ts +9 -0
  6. package/src/io/docx-session.ts +1 -0
  7. package/src/io/export/serialize-numbering.ts +42 -8
  8. package/src/io/export/serialize-paragraph-formatting.ts +152 -0
  9. package/src/io/export/serialize-run-formatting.ts +90 -0
  10. package/src/io/export/serialize-styles.ts +212 -0
  11. package/src/io/ooxml/parse-fields.ts +10 -3
  12. package/src/io/ooxml/parse-numbering.ts +41 -1
  13. package/src/io/ooxml/parse-paragraph-formatting.ts +188 -0
  14. package/src/io/ooxml/parse-run-formatting.ts +129 -0
  15. package/src/io/ooxml/parse-styles.ts +31 -0
  16. package/src/io/ooxml/xml-attr-helpers.ts +60 -0
  17. package/src/io/ooxml/xml-element.ts +19 -0
  18. package/src/model/canonical-document.ts +83 -3
  19. package/src/runtime/collab/event-types.ts +165 -0
  20. package/src/runtime/collab/index.ts +22 -0
  21. package/src/runtime/collab/remote-cursor-awareness.ts +93 -0
  22. package/src/runtime/collab/runtime-collab-sync.ts +273 -0
  23. package/src/runtime/document-runtime.ts +141 -18
  24. package/src/runtime/layout/docx-font-loader.ts +30 -11
  25. package/src/runtime/layout/index.ts +2 -0
  26. package/src/runtime/layout/inert-layout-facet.ts +3 -0
  27. package/src/runtime/layout/layout-engine-instance.ts +69 -2
  28. package/src/runtime/layout/layout-invalidation.ts +14 -5
  29. package/src/runtime/layout/page-graph.ts +36 -0
  30. package/src/runtime/layout/paginate-paragraph-lines.ts +128 -0
  31. package/src/runtime/layout/paginated-layout-engine.ts +342 -28
  32. package/src/runtime/layout/project-block-fragments.ts +154 -20
  33. package/src/runtime/layout/public-facet.ts +81 -1
  34. package/src/runtime/layout/resolve-page-fields.ts +70 -0
  35. package/src/runtime/layout/resolve-page-previews.ts +185 -0
  36. package/src/runtime/layout/resolved-formatting-state.ts +30 -26
  37. package/src/runtime/layout/table-render-plan.ts +21 -1
  38. package/src/runtime/numbering-prefix.ts +5 -0
  39. package/src/runtime/paragraph-style-resolver.ts +194 -0
  40. package/src/runtime/render/render-kernel.ts +5 -1
  41. package/src/runtime/resolved-numbering-geometry.ts +9 -1
  42. package/src/runtime/surface-projection.ts +129 -9
  43. package/src/runtime/table-schema.ts +11 -0
  44. package/src/runtime/workflow-rail-segments.ts +149 -1
  45. package/src/ui/WordReviewEditor.tsx +302 -5
  46. package/src/ui/editor-command-bag.ts +4 -0
  47. package/src/ui/editor-runtime-boundary.ts +16 -0
  48. package/src/ui/editor-shell-view.tsx +22 -0
  49. package/src/ui/editor-surface-controller.tsx +9 -1
  50. package/src/ui/headless/chrome-registry.ts +34 -5
  51. package/src/ui/headless/scoped-chrome-policy.ts +29 -0
  52. package/src/ui-tailwind/chrome/review-queue-bar.tsx +2 -14
  53. package/src/ui-tailwind/chrome/role-action-sets.ts +14 -8
  54. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +80 -0
  55. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +7 -10
  56. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +11 -0
  57. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +11 -0
  58. package/src/ui-tailwind/chrome/tw-table-border-picker.tsx +245 -0
  59. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +101 -21
  60. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +353 -0
  61. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +5 -1
  62. package/src/ui-tailwind/chrome-overlay/index.ts +2 -6
  63. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +82 -18
  64. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +133 -0
  65. package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +386 -0
  66. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +140 -69
  67. package/src/ui-tailwind/editor-surface/page-slice-util.ts +15 -0
  68. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +28 -3
  69. package/src/ui-tailwind/editor-surface/pm-decorations.ts +7 -2
  70. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +389 -0
  71. package/src/ui-tailwind/editor-surface/pm-schema.ts +40 -2
  72. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +170 -63
  73. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +179 -0
  74. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +559 -0
  75. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +224 -78
  76. package/src/ui-tailwind/editor-surface/tw-table-bands.css +61 -0
  77. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +19 -0
  78. package/src/ui-tailwind/index.ts +6 -5
  79. package/src/ui-tailwind/theme/editor-theme.css +108 -15
  80. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +122 -1
  81. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +36 -3
  82. package/src/ui-tailwind/tw-review-workspace.tsx +207 -54
  83. package/src/runtime/collab-review-sync.ts +0 -254
  84. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +0 -95
  85. package/src/ui-tailwind/editor-surface/pm-collab-plugins.ts +0 -40
@@ -408,8 +408,16 @@
408
408
  box-shadow: inset -1px 0 0 color-mix(in srgb, var(--color-danger) 35%, transparent);
409
409
  }
410
410
 
411
+ /*
412
+ * `wre-workflow-inline-active` no longer emits a visual outline. The
413
+ * per-run inset box-shadow produced a halo around every text fragment
414
+ * (one box per run, due to box-decoration-break: clone above), which
415
+ * fought with the overlay's flat tint. The class name is kept on the
416
+ * inline decoration as a data hook (no visual), and emphasis for the
417
+ * active scope now lives on the ChromeOverlay rail stripe + scope card.
418
+ */
411
419
  .prosemirror-surface .ProseMirror .wre-workflow-inline-active {
412
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 30%, transparent);
420
+ /* intentionally empty visual emphasis handled by ChromeOverlay */
413
421
  }
414
422
 
415
423
  /*
@@ -423,9 +431,37 @@
423
431
  pointer-events: none;
424
432
  }
425
433
 
434
+ /*
435
+ * ─── Gutter lane tokens ───
436
+ *
437
+ * The scope rail and scope card chrome live in a reserved lane to the
438
+ * left of the page frame so they visibly read as chrome (not document
439
+ * content). Page surfaces use 64px, canvas surfaces 48px. Host apps
440
+ * can override via CSS custom property.
441
+ */
442
+ :root {
443
+ --wre-gutter-lane-width: 64px;
444
+ --wre-gutter-lane-pad: 8px;
445
+ }
446
+
447
+ .wre-canvas-surface {
448
+ --wre-gutter-lane-width: 48px;
449
+ }
450
+
451
+ /*
452
+ * The page-with-gutter grid shell allocates the gutter as its own
453
+ * column so absolute-positioned chrome overlays (which fill inset 0 of
454
+ * this shell) land INSIDE the gutter, not over the shell background.
455
+ */
456
+ .wre-page-with-gutter {
457
+ display: grid;
458
+ grid-template-columns: var(--wre-gutter-lane-width) 1fr;
459
+ position: relative;
460
+ }
461
+
426
462
  .wre-scope-rail-tint {
427
463
  position: absolute;
428
- border-radius: 0.35rem;
464
+ border-radius: 0.2rem;
429
465
  pointer-events: none;
430
466
  z-index: 0;
431
467
  transition: background 140ms ease-out;
@@ -452,32 +488,89 @@
452
488
  outline-offset: -1px;
453
489
  }
454
490
 
491
+ /*
492
+ * ─── Scope rail stripe ───
493
+ *
494
+ * The rail stripe is the rest-state representation of a scope: a 4px
495
+ * color stripe in the gutter lane. Posture color comes from the
496
+ * accent/warning/insert/secondary/danger tokens. Hover widens the
497
+ * stripe via transform (zero layout cost) and reveals the label pill.
498
+ */
499
+ .wre-scope-rail-stripe {
500
+ position: absolute;
501
+ width: 4px;
502
+ border-radius: 2px;
503
+ background: currentColor;
504
+ pointer-events: auto;
505
+ cursor: pointer;
506
+ z-index: 1;
507
+ transform-origin: left center;
508
+ transition: transform 120ms ease-out, opacity 120ms ease-out;
509
+ opacity: 0.75;
510
+ /* Reset button defaults. */
511
+ border: none;
512
+ padding: 0;
513
+ margin: 0;
514
+ font: inherit;
515
+ color: inherit;
516
+ background-clip: padding-box;
517
+ }
518
+
519
+ .wre-scope-rail-stripe:hover,
520
+ .wre-scope-rail-stripe:focus-visible {
521
+ transform: scaleX(1.5);
522
+ opacity: 1;
523
+ outline: none;
524
+ }
525
+
526
+ .wre-scope-rail-stripe-active {
527
+ opacity: 1;
528
+ transform: scaleX(1.75);
529
+ }
530
+
531
+ .wre-scope-rail-stripe.wre-scope-rail-label-accent { color: var(--color-accent); }
532
+ .wre-scope-rail-stripe.wre-scope-rail-label-warning { color: var(--color-warning); }
533
+ .wre-scope-rail-stripe.wre-scope-rail-label-insert { color: var(--color-insert); }
534
+ .wre-scope-rail-stripe.wre-scope-rail-label-secondary { color: var(--color-secondary); }
535
+ .wre-scope-rail-stripe.wre-scope-rail-label-danger { color: var(--color-danger); }
536
+
537
+ /*
538
+ * ─── Scope rail label pill ───
539
+ *
540
+ * Shown only on stripe hover (CSS-driven). The pill overlays the
541
+ * stripe with icon + short posture label, anchored to the first line
542
+ * of the scope.
543
+ */
455
544
  .wre-scope-rail-label {
456
545
  position: absolute;
457
546
  display: flex;
458
- flex-direction: column;
459
547
  align-items: center;
460
548
  justify-content: center;
461
- gap: 0.15rem;
462
- padding: 0.25rem 0.5rem;
463
- border-radius: 0.375rem;
549
+ gap: 0.2rem;
550
+ padding: 0.15rem 0.3rem;
551
+ border-radius: var(--radius-sm);
464
552
  border: 1px solid transparent;
465
553
  background: var(--color-canvas, #fff);
466
- box-shadow: 0 2px 6px -3px rgba(0, 0, 0, 0.18);
467
- font-size: 10px;
554
+ box-shadow: var(--shadow-sm);
555
+ font-size: 9.5px;
468
556
  line-height: 1;
469
557
  text-transform: uppercase;
470
- letter-spacing: 0.08em;
558
+ letter-spacing: 0.06em;
471
559
  font-weight: 600;
472
560
  cursor: pointer;
473
- pointer-events: auto;
474
- z-index: 1;
475
- transition: transform 120ms ease-out, box-shadow 120ms ease-out;
561
+ z-index: 2;
562
+ opacity: 0;
563
+ pointer-events: none;
564
+ transition: opacity 140ms ease-out, transform 140ms ease-out;
565
+ transform: translateX(-4px);
476
566
  }
477
567
 
478
- .wre-scope-rail-label:hover {
479
- transform: translateY(-1px);
480
- box-shadow: 0 4px 12px -4px rgba(0, 0, 0, 0.22);
568
+ .wre-scope-rail-stripe:hover + .wre-scope-rail-label,
569
+ .wre-scope-rail-label:hover,
570
+ .wre-scope-rail-stripe:focus-visible + .wre-scope-rail-label {
571
+ opacity: 1;
572
+ pointer-events: auto;
573
+ transform: translateX(0);
481
574
  }
482
575
 
483
576
  .wre-scope-rail-label-accent {
@@ -15,6 +15,8 @@
15
15
 
16
16
  import React, { useState } from "react";
17
17
  import * as Popover from "@radix-ui/react-popover";
18
+ import * as Toggle from "@radix-ui/react-toggle";
19
+ import * as Tooltip from "@radix-ui/react-tooltip";
18
20
  import {
19
21
  BookmarkCheck,
20
22
  Check,
@@ -23,8 +25,13 @@ import {
23
25
  ChevronLeft,
24
26
  ChevronRight,
25
27
  CircleOff,
28
+ Eye,
29
+ EyeOff,
30
+ FileDiff,
26
31
  Flag,
27
32
  Hand,
33
+ MessageSquare,
34
+ MessageSquareDot,
28
35
  MessageSquareText,
29
36
  Rows3,
30
37
  SkipForward,
@@ -38,6 +45,7 @@ import type {
38
45
  ReviewQueueSnapshot,
39
46
  ScopeRailPosture,
40
47
  } from "../../api/public-types";
48
+ import type { SessionCapabilities } from "../../runtime/session-capabilities";
41
49
  import type { ScopedChromePolicy } from "../../ui/headless/scoped-chrome-policy";
42
50
  import type { ToolbarChromeItemId } from "../../ui/headless/chrome-registry";
43
51
  import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
@@ -68,7 +76,23 @@ export interface TwRoleActionRegionProps {
68
76
  workflowItem?: WorkflowWorkItemSnapshot | null;
69
77
  markupDisplay?: MarkupDisplayMode;
70
78
 
71
- // Editor role
79
+ // Shared: editor + review role
80
+ canAddComment?: boolean;
81
+ showTrackedChanges?: boolean;
82
+ onAddComment?: () => void;
83
+ onShowTrackedChangesChange?: (show: boolean) => void;
84
+ /**
85
+ * Session capabilities used to gate the role-region tracked-changes
86
+ * toggle (mirrors the right-cluster gate at tw-toolbar.tsx). When
87
+ * `capabilities.trackChangesSupported` is false, the toggle is disabled.
88
+ */
89
+ capabilities?: SessionCapabilities;
90
+
91
+ // Review sidebar panel (optional — hidden when not provided)
92
+ onReviewSidebarTrackedChanges?: () => void;
93
+ onReviewSidebarComments?: () => void;
94
+
95
+ // Workflow + review role: scope posture menu
72
96
  onMarkScopePosture?: (posture: ScopeRailPosture) => void;
73
97
 
74
98
  // Review role
@@ -142,7 +166,104 @@ interface RoleActionButtonProps {
142
166
 
143
167
  function RoleActionButton(arg: RoleActionButtonProps): React.JSX.Element | null {
144
168
  const { id, props } = arg;
169
+ const focusRingClass =
170
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
145
171
  switch (id) {
172
+ case "comment":
173
+ return (
174
+ <Tooltip.Root>
175
+ <Tooltip.Trigger asChild>
176
+ <button
177
+ type="button"
178
+ aria-label="Add comment"
179
+ disabled={!props.canAddComment || !props.onAddComment}
180
+ onMouseDown={preserveEditorSelectionMouseDown}
181
+ onClick={props.onAddComment}
182
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 outline-none ${focusRingClass}`}
183
+ data-testid="role-add-comment"
184
+ >
185
+ <MessageSquare className="h-3.5 w-3.5" />
186
+ </button>
187
+ </Tooltip.Trigger>
188
+ <Tooltip.Portal>
189
+ <Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
190
+ Add comment
191
+ </Tooltip.Content>
192
+ </Tooltip.Portal>
193
+ </Tooltip.Root>
194
+ );
195
+ case "tracked-changes-toggle":
196
+ return (
197
+ <Tooltip.Root>
198
+ <Tooltip.Trigger asChild>
199
+ <Toggle.Root
200
+ pressed={props.showTrackedChanges ?? false}
201
+ onPressedChange={(v) => props.onShowTrackedChangesChange?.(v)}
202
+ disabled={props.capabilities ? !props.capabilities.trackChangesSupported : false}
203
+ onMouseDown={preserveEditorSelectionMouseDown}
204
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-canvas data-[state=on]:text-accent data-[state=on]:ring-1 data-[state=on]:ring-accent/30 data-[state=on]:shadow-sm outline-none disabled:opacity-40 ${focusRingClass}`}
205
+ data-testid="role-tracked-changes-toggle"
206
+ >
207
+ {(props.showTrackedChanges ?? false) ? (
208
+ <Eye className="h-3.5 w-3.5" />
209
+ ) : (
210
+ <EyeOff className="h-3.5 w-3.5" />
211
+ )}
212
+ </Toggle.Root>
213
+ </Tooltip.Trigger>
214
+ <Tooltip.Portal>
215
+ <Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
216
+ {(props.showTrackedChanges ?? false) ? "Hide tracked changes" : "Show tracked changes"}
217
+ </Tooltip.Content>
218
+ </Tooltip.Portal>
219
+ </Tooltip.Root>
220
+ );
221
+ case "review-sidebar-tracked-changes":
222
+ return (
223
+ <Tooltip.Root>
224
+ <Tooltip.Trigger asChild>
225
+ <button
226
+ type="button"
227
+ aria-label="Show tracked changes in sidebar"
228
+ disabled={!props.onReviewSidebarTrackedChanges}
229
+ onMouseDown={preserveEditorSelectionMouseDown}
230
+ onClick={props.onReviewSidebarTrackedChanges}
231
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 outline-none ${focusRingClass}`}
232
+ data-testid="role-sidebar-tracked-changes"
233
+ >
234
+ <FileDiff className="h-3.5 w-3.5" />
235
+ </button>
236
+ </Tooltip.Trigger>
237
+ <Tooltip.Portal>
238
+ <Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
239
+ Tracked changes panel
240
+ </Tooltip.Content>
241
+ </Tooltip.Portal>
242
+ </Tooltip.Root>
243
+ );
244
+ case "review-sidebar-comments":
245
+ return (
246
+ <Tooltip.Root>
247
+ <Tooltip.Trigger asChild>
248
+ <button
249
+ type="button"
250
+ aria-label="Show comments in sidebar"
251
+ disabled={!props.onReviewSidebarComments}
252
+ onMouseDown={preserveEditorSelectionMouseDown}
253
+ onClick={props.onReviewSidebarComments}
254
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 outline-none ${focusRingClass}`}
255
+ data-testid="role-sidebar-comments"
256
+ >
257
+ <MessageSquareDot className="h-3.5 w-3.5" />
258
+ </button>
259
+ </Tooltip.Trigger>
260
+ <Tooltip.Portal>
261
+ <Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
262
+ Comments panel
263
+ </Tooltip.Content>
264
+ </Tooltip.Portal>
265
+ </Tooltip.Root>
266
+ );
146
267
  case "editor-scope-posture-menu":
147
268
  return (
148
269
  <TwScopePostureMenu
@@ -42,6 +42,8 @@ import {
42
42
 
43
43
  import type {
44
44
  ActiveListContext,
45
+ ChromePinSurface,
46
+ ChromePinsState,
45
47
  CompatibilityPanelSnapshot,
46
48
  EditorRole,
47
49
  EditorStoryTarget,
@@ -49,6 +51,7 @@ import type {
49
51
  FormattingStateSnapshot,
50
52
  FormattingAlignment,
51
53
  InsertImageOptions,
54
+ PinState,
52
55
  ReviewQueueSnapshot,
53
56
  ScopeRailPosture,
54
57
  SectionBreakType,
@@ -61,6 +64,7 @@ import type {
61
64
  import type { SessionCapabilities } from "../../runtime/session-capabilities";
62
65
  import {
63
66
  getToolbarChromePlacement,
67
+ isChromeItemOwnedByRoleRegion,
64
68
  isToolbarChromeItemVisible,
65
69
  resolveScopedChromePolicy,
66
70
  type ScopedChromePolicy,
@@ -72,6 +76,7 @@ import {
72
76
  type MarkupDisplayMode,
73
77
  type WorkflowWorkItemSnapshot,
74
78
  } from "./tw-role-action-region";
79
+ import { TwDetachHandle } from "../chrome/tw-detach-handle";
75
80
  import { TwToolbarIconButton } from "./tw-toolbar-icon-button";
76
81
 
77
82
  export interface TwToolbarProps {
@@ -144,7 +149,10 @@ export interface TwToolbarProps {
144
149
  /** Markup display mode for the review role. */
145
150
  markupDisplay?: MarkupDisplayMode;
146
151
 
147
- // Editor role
152
+ // Shared: editor + review role (comment + TC in role region)
153
+ onReviewSidebarTrackedChanges?: () => void;
154
+ onReviewSidebarComments?: () => void;
155
+ // Workflow + review role: scope posture
148
156
  onMarkScopePosture?: (posture: ScopeRailPosture) => void;
149
157
  // Review role
150
158
  onReviewPrev?: () => void;
@@ -162,6 +170,10 @@ export interface TwToolbarProps {
162
170
  onWorkflowSkip?: () => void;
163
171
  onWorkflowMarkBlocked?: () => void;
164
172
  onWorkflowJumpToScope?: () => void;
173
+ /** Current chrome pin state; when supplied enables the topnav detach handle. */
174
+ chromePins?: ChromePinsState;
175
+ /** Called when the user detaches or re-attaches the topnav. */
176
+ onChromePinChange?: (surface: ChromePinSurface, pin: PinState | null) => void;
165
177
  }
166
178
 
167
179
  export interface ToolbarInteractionPolicy {
@@ -219,7 +231,12 @@ export function TwToolbar(props: TwToolbarProps) {
219
231
  const showTextColors = isToolbarChromeItemVisible(scopedChromePolicy, "text-colors");
220
232
  const showParagraphAlignment = isToolbarChromeItemVisible(scopedChromePolicy, "paragraph-alignment");
221
233
  const showInsertMenu = isToolbarChromeItemVisible(scopedChromePolicy, "insert-actions");
222
- const showTrackedChangesToggle = isToolbarChromeItemVisible(scopedChromePolicy, "tracked-changes-toggle");
234
+ const showTrackedChangesToggle =
235
+ isToolbarChromeItemVisible(scopedChromePolicy, "tracked-changes-toggle") &&
236
+ !isChromeItemOwnedByRoleRegion("tracked-changes-toggle", props.role);
237
+ const showRightClusterComment =
238
+ isToolbarChromeItemVisible(scopedChromePolicy, "comment") &&
239
+ !isChromeItemOwnedByRoleRegion("comment", props.role);
223
240
  const showHealth =
224
241
  showDiagnosticsChrome &&
225
242
  isToolbarChromeItemVisible(scopedChromePolicy, "health") &&
@@ -527,6 +544,13 @@ export function TwToolbar(props: TwToolbarProps) {
527
544
  reviewQueue={props.reviewQueue}
528
545
  workflowItem={props.workflowItem}
529
546
  markupDisplay={props.markupDisplay}
547
+ canAddComment={canAddComment}
548
+ showTrackedChanges={props.showTrackedChanges}
549
+ capabilities={caps}
550
+ onAddComment={props.onAddComment}
551
+ onShowTrackedChangesChange={props.onShowTrackedChangesChange}
552
+ onReviewSidebarTrackedChanges={props.onReviewSidebarTrackedChanges}
553
+ onReviewSidebarComments={props.onReviewSidebarComments}
530
554
  onMarkScopePosture={props.onMarkScopePosture}
531
555
  onReviewPrev={props.onReviewPrev}
532
556
  onReviewNext={props.onReviewNext}
@@ -567,7 +591,7 @@ export function TwToolbar(props: TwToolbarProps) {
567
591
  </>
568
592
  ) : null}
569
593
 
570
- {isToolbarChromeItemVisible(scopedChromePolicy, "comment") ? (
594
+ {showRightClusterComment ? (
571
595
  <TwToolbarIconButton
572
596
  icon={MessageSquare}
573
597
  label="Add comment"
@@ -809,6 +833,15 @@ export function TwToolbar(props: TwToolbarProps) {
809
833
  onClick={props.onExport}
810
834
  />
811
835
  ) : null}
836
+
837
+ {props.onChromePinChange ? (
838
+ <TwDetachHandle
839
+ surface="topnav"
840
+ pin={props.chromePins?.topnav}
841
+ onChange={props.onChromePinChange}
842
+ label="Detach toolbar"
843
+ />
844
+ ) : null}
812
845
  </div>
813
846
  </header>
814
847
  );