@beyondwork/docx-react-component 1.0.105 → 1.0.108

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 (193) hide show
  1. package/package.json +19 -5
  2. package/src/api/geometry-overlay-rects.ts +5 -0
  3. package/src/api/package-version.ts +1 -1
  4. package/src/api/page-anchor-id.ts +5 -0
  5. package/src/api/public-types.ts +16 -9
  6. package/src/api/table-node-specs.ts +6 -0
  7. package/src/api/v3/_create.ts +10 -2
  8. package/src/api/v3/_page-anchor-id.ts +52 -0
  9. package/src/api/v3/_runtime-handle.ts +92 -1
  10. package/src/api/v3/ai/_audit-reference.ts +28 -0
  11. package/src/api/v3/ai/_audit-time.ts +5 -0
  12. package/src/api/v3/ai/_pe2-evidence.ts +310 -6
  13. package/src/api/v3/ai/attach.ts +29 -4
  14. package/src/api/v3/ai/bundle.ts +6 -2
  15. package/src/api/v3/ai/inspect.ts +6 -2
  16. package/src/api/v3/ai/replacement.ts +112 -18
  17. package/src/api/v3/ai/resolve.ts +2 -2
  18. package/src/api/v3/ai/review.ts +177 -3
  19. package/src/api/v3/index.ts +8 -0
  20. package/src/api/v3/runtime/collab.ts +462 -0
  21. package/src/api/v3/runtime/document.ts +503 -20
  22. package/src/api/v3/runtime/geometry.ts +97 -0
  23. package/src/api/v3/runtime/layout.ts +744 -0
  24. package/src/api/v3/runtime/perf-probe.ts +14 -0
  25. package/src/api/v3/runtime/viewport.ts +9 -8
  26. package/src/api/v3/ui/_types.ts +202 -55
  27. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  28. package/src/api/v3/ui/debug.ts +115 -2
  29. package/src/api/v3/ui/index.ts +17 -0
  30. package/src/api/v3/ui/overlays.ts +0 -8
  31. package/src/api/v3/ui/surface.ts +56 -0
  32. package/src/api/v3/ui/viewport.ts +119 -9
  33. package/src/core/commands/image-commands.ts +1 -0
  34. package/src/core/commands/index.ts +6 -0
  35. package/src/core/schema/text-schema.ts +43 -5
  36. package/src/core/selection/mapping.ts +8 -1
  37. package/src/core/selection/review-anchors.ts +5 -1
  38. package/src/core/state/text-transaction.ts +8 -2
  39. package/src/io/export/serialize-revisions.ts +149 -1
  40. package/src/io/normalize/normalize-text.ts +6 -0
  41. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  42. package/src/io/ooxml/parse-fields.ts +24 -2
  43. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  44. package/src/io/ooxml/parse-main-document.ts +153 -9
  45. package/src/io/ooxml/parse-numbering.ts +20 -0
  46. package/src/io/ooxml/parse-revisions.ts +19 -8
  47. package/src/io/opc/package-reader.ts +98 -8
  48. package/src/model/anchor.ts +4 -3
  49. package/src/model/canonical-document.ts +220 -2
  50. package/src/model/canonical-hash.ts +221 -0
  51. package/src/model/canonical-layout-inputs.ts +245 -6
  52. package/src/model/layout/index.ts +1 -0
  53. package/src/model/layout/page-graph-types.ts +147 -1
  54. package/src/model/review/revision-types.ts +14 -3
  55. package/src/preservation/store.ts +20 -4
  56. package/src/review/README.md +1 -1
  57. package/src/review/store/revision-actions.ts +14 -2
  58. package/src/runtime/collab/event-types.ts +67 -1
  59. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  60. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  61. package/src/runtime/document-heading-outline.ts +147 -0
  62. package/src/runtime/document-navigation.ts +8 -243
  63. package/src/runtime/document-runtime.ts +279 -115
  64. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  65. package/src/runtime/formatting/layout-inputs.ts +38 -5
  66. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  67. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  68. package/src/runtime/geometry/caret-geometry.ts +5 -6
  69. package/src/runtime/geometry/geometry-facet.ts +60 -10
  70. package/src/runtime/geometry/geometry-index.ts +661 -16
  71. package/src/runtime/geometry/geometry-types.ts +59 -0
  72. package/src/runtime/geometry/hit-test.ts +11 -1
  73. package/src/runtime/geometry/overlay-rects.ts +5 -3
  74. package/src/runtime/geometry/project-anchors.ts +1 -1
  75. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  76. package/src/runtime/layout/index.ts +6 -0
  77. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  78. package/src/runtime/layout/layout-engine-version.ts +188 -16
  79. package/src/runtime/layout/layout-facet-types.ts +6 -0
  80. package/src/runtime/layout/page-graph.ts +23 -4
  81. package/src/runtime/layout/paginated-layout-engine.ts +149 -15
  82. package/src/runtime/layout/project-block-fragments.ts +351 -14
  83. package/src/runtime/layout/public-facet.ts +162 -24
  84. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  85. package/src/runtime/layout/table-row-split.ts +92 -35
  86. package/src/runtime/prerender/cache-envelope.ts +2 -2
  87. package/src/runtime/prerender/cache-key.ts +5 -4
  88. package/src/runtime/prerender/customxml-cache.ts +0 -1
  89. package/src/runtime/render/render-kernel.ts +1 -1
  90. package/src/runtime/revision-runtime.ts +112 -10
  91. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  92. package/src/runtime/scopes/action-validation.ts +22 -2
  93. package/src/runtime/scopes/capabilities.ts +316 -0
  94. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  95. package/src/runtime/scopes/compiler-service.ts +108 -4
  96. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  97. package/src/runtime/scopes/create-issue.ts +5 -5
  98. package/src/runtime/scopes/evidence.ts +91 -0
  99. package/src/runtime/scopes/formatting/apply.ts +2 -0
  100. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  101. package/src/runtime/scopes/index.ts +54 -0
  102. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  103. package/src/runtime/scopes/layout-evidence.ts +374 -0
  104. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  105. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  106. package/src/runtime/scopes/replacement/apply.ts +97 -34
  107. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  108. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  109. package/src/runtime/scopes/visualization.ts +28 -0
  110. package/src/runtime/surface-projection.ts +44 -5
  111. package/src/runtime/telemetry/perf-probe.ts +216 -0
  112. package/src/runtime/virtualized-rendering.ts +36 -1
  113. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  114. package/src/runtime/workflow/coordinator.ts +39 -11
  115. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  116. package/src/runtime/workflow/index.ts +4 -0
  117. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  118. package/src/runtime/workflow/overlay-lanes.ts +386 -0
  119. package/src/runtime/workflow/overlay-store.ts +2 -2
  120. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  121. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  122. package/src/session/_sync-legacy.ts +17 -27
  123. package/src/session/import/loader.ts +6 -4
  124. package/src/session/import/source-package-evidence.ts +186 -2
  125. package/src/session/index.ts +5 -6
  126. package/src/session/session.ts +30 -56
  127. package/src/session/types.ts +8 -13
  128. package/src/shell/session-bootstrap.ts +155 -81
  129. package/src/ui/WordReviewEditor.tsx +520 -12
  130. package/src/ui/editor-shell-view.tsx +14 -4
  131. package/src/ui/editor-surface-controller.tsx +5 -3
  132. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  133. package/src/ui/presence-overlay-lane.ts +130 -0
  134. package/src/ui/ui-controller-factory.ts +17 -0
  135. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  136. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  137. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  138. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  139. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  140. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  141. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  142. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  143. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  144. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  145. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  146. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  147. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  148. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  149. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  150. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  151. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  152. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  153. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  154. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  155. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  156. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  157. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  158. package/src/ui-tailwind/debug/README.md +4 -1
  159. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  160. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  161. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  162. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  163. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  164. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  165. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  166. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  167. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  168. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  169. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  170. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  171. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  172. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  173. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  174. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  175. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  176. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  177. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  178. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  179. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  180. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  181. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  182. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  183. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  184. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  185. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  186. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  187. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  188. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  189. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  190. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  191. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  192. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  193. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -8,7 +8,6 @@ import React, {
8
8
  } from "react";
9
9
 
10
10
  import * as Tooltip from "@radix-ui/react-tooltip";
11
- import { ChevronRight } from "lucide-react";
12
11
 
13
12
  import { sliceBlocksForPage } from "./editor-surface/page-slice-util.ts";
14
13
  import {
@@ -19,7 +18,6 @@ import { TwPageBlockView } from "./editor-surface/tw-page-block-view.tsx";
19
18
  import { computeLineMarkersIfEnabled } from "./page-chrome-model.ts";
20
19
  import { shouldRenderSelectionToolKind } from "../ui/headless/scoped-chrome-policy";
21
20
  import type { EditorCommandBag } from "../ui/editor-command-bag.ts";
22
- import { preserveEditorSelectionMouseDown } from "../ui/headless/preserve-editor-selection";
23
21
  import { TwAlertBanner } from "./chrome/tw-alert-banner";
24
22
  import { TwPageRuler } from "./chrome/tw-page-ruler";
25
23
  import { ChromePresetToolbar } from "./chrome/chrome-preset-toolbar";
@@ -35,13 +33,6 @@ import {
35
33
  type TwReviewWorkspaceRailMode,
36
34
  } from "./review-workspace/tw-review-workspace-rail.tsx";
37
35
  import { TwStatusBar } from "./status/tw-status-bar";
38
- import {
39
- TwShellHeader,
40
- type ShellHeaderMode,
41
- type ShellHeaderModeOption,
42
- } from "./toolbar/tw-shell-header";
43
- import { TwContextBand } from "./chrome/tw-context-band";
44
- import { TwRoleActionRegion } from "./toolbar/tw-role-action-region";
45
36
  import { LocalSurfaceArbiterContext } from "./chrome/local-surface-arbiter";
46
37
  import {
47
38
  TwWorkspaceChromeHost,
@@ -62,14 +53,20 @@ import { useViewportDimensions } from "./review-workspace/use-viewport-dimension
62
53
  import { useScopeCardState } from "./review-workspace/use-scope-card-state.ts";
63
54
  import { usePageMarkers } from "./review-workspace/use-page-markers.ts";
64
55
  import { useDiagnosticsSignal } from "./review-workspace/use-diagnostics-signal.ts";
56
+ import {
57
+ filterProductCompatibility,
58
+ filterProductWarnings,
59
+ } from "./review-workspace/diagnostics-visibility.ts";
65
60
  import { useWorkspaceComposition } from "./review-workspace/use-workspace-composition.ts";
66
61
  import { useWorkspaceArbiter } from "./review-workspace/use-workspace-arbiter.ts";
67
62
  import { useLayoutFacetRenderSignal } from "./review-workspace/use-layout-facet-render-signal.ts";
68
63
  import { useScrollRootCapture } from "./review-workspace/use-scroll-root-capture.ts";
69
64
  import { usePmSurfaceCapture } from "./review-workspace/use-pm-surface-capture.ts";
70
65
  import { TwReviewWorkspaceNavigator } from "./review-workspace/tw-review-workspace-navigator.tsx";
66
+ import { DEFAULT_CANVAS_BODY_WIDTH_PX } from "./review-workspace/page-shell-metrics.ts";
71
67
 
72
68
  export {
69
+ DEFAULT_CANVAS_BODY_WIDTH_PX,
73
70
  FRAME_PX_PER_TWIP_AT_96DPI,
74
71
  MIN_BAND_HEIGHT_PX,
75
72
  buildPageShellMetrics,
@@ -85,82 +82,23 @@ export type {
85
82
  TwReviewWorkspaceProps,
86
83
  } from "./review-workspace/types.ts";
87
84
 
88
- import type { EditorRole, EditorStoryTarget } from "../api/public-types.ts";
89
-
90
- // Default shell-header modes for the workspace's default composition.
91
- // Designsystem §6.1 prescribes a 4-mode switcher; all four are reachable in
92
- // the product path. "More" is a shell-owned diagnostics/search posture, so it
93
- // uses `ChromeCompositionInput.modeOverride` instead of changing runtime role.
94
- const DEFAULT_WORKSPACE_SHELL_MODES: readonly ShellHeaderModeOption[] = [
95
- { id: "edit", label: "Edit" },
96
- { id: "review", label: "Review" },
97
- { id: "workflow", label: "Workflow" },
98
- { id: "more", label: "More" },
99
- ];
85
+ import type {
86
+ EditorRole,
87
+ EditorStoryTarget,
88
+ WorkflowScopeSnapshot,
89
+ } from "../api/public-types.ts";
100
90
 
101
- function editorRoleToShellMode(role: EditorRole): ShellHeaderMode {
102
- switch (role) {
103
- case "editor":
104
- return "edit";
105
- case "review":
106
- return "review";
107
- case "workflow":
108
- return "workflow";
91
+ function resolveRoleChromePreset(
92
+ basePreset: ReturnType<typeof resolveChromePreset>,
93
+ role: EditorRole,
94
+ ): ReturnType<typeof resolveChromePreset> {
95
+ if (role === "workflow") {
96
+ return "workflow";
109
97
  }
110
- }
111
-
112
- function shellModeToEditorRole(mode: ShellHeaderMode): EditorRole | null {
113
- switch (mode) {
114
- case "edit":
115
- return "editor";
116
- case "review":
117
- return "review";
118
- case "workflow":
119
- return "workflow";
120
- case "more":
121
- return null;
98
+ if (role === "review") {
99
+ return "review";
122
100
  }
123
- }
124
-
125
- function TwMoreContextBandContent(props: {
126
- onOpenDiagnostics: () => void;
127
- onOpenCompatibility: () => void;
128
- onOpenOutline: () => void;
129
- onOpenSearch?: () => void;
130
- }): React.JSX.Element {
131
- return (
132
- <div
133
- className="flex min-w-0 items-center gap-1"
134
- data-testid="more-context-band-content"
135
- >
136
- <ContextBandButton label="Diagnostics" onClick={props.onOpenDiagnostics} />
137
- <ContextBandButton label="Compatibility" onClick={props.onOpenCompatibility} />
138
- <ContextBandButton label="Outline" onClick={props.onOpenOutline} />
139
- <ContextBandButton
140
- label="Search"
141
- onClick={props.onOpenSearch}
142
- disabled={!props.onOpenSearch}
143
- />
144
- </div>
145
- );
146
- }
147
-
148
- function ContextBandButton(props: {
149
- label: string;
150
- onClick?: () => void;
151
- disabled?: boolean;
152
- }): React.JSX.Element {
153
- return (
154
- <button
155
- type="button"
156
- disabled={props.disabled}
157
- onMouseDown={preserveEditorSelectionMouseDown}
158
- onClick={props.onClick}
159
- className="inline-flex h-7 items-center rounded-[var(--radius-md)] px-2.5 text-xs font-medium text-secondary transition-colors hover:bg-hover focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)] disabled:cursor-not-allowed disabled:opacity-40"
160
- >
161
- {props.label}
162
- </button>
163
- );
101
+ return basePreset;
164
102
  }
165
103
 
166
104
  export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
@@ -172,8 +110,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
172
110
  const selectionToolbarRootRef = useRef<HTMLDivElement>(null);
173
111
  const workspaceChromeControllerRef =
174
112
  useRef<TwWorkspaceChromeHostController | null>(null);
175
- const [shellModeOverride, setShellModeOverride] =
176
- useState<ShellHeaderMode | null>(null);
177
113
  // P8.11 — body slot wrapping `{props.document}` (the PM surface) + scroll
178
114
  // root ref. The chrome layer's `TwPageStackChromeLayer` needs both to
179
115
  // measure per-page rects and to reparent PM's DOM node across band
@@ -215,7 +151,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
215
151
  handleScopeCardRejectSuggestionGroup,
216
152
  handleScopeCardAskAgent,
217
153
  } = useScopeCardState({
218
- layoutFacet: props.layoutFacet,
219
154
  workflowFacet: props.workflowFacet,
220
155
  onScopeModeChangeRequested: props.onScopeModeChangeRequested,
221
156
  onScopeIssueActionRequested: props.onScopeIssueActionRequested,
@@ -229,17 +164,19 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
229
164
  // `pageShellMetrics` has been computed (P2.c).
230
165
  const numericZoomScale = typeof zoomLevel === "number" ? zoomLevel / 100 : 1;
231
166
  const chromePreset = resolveChromePreset(props.chromePreset, props.reviewMode);
232
- const chromeOptions = resolveChromePresetOptions(chromePreset, props.chromeOptions, viewState.editorRole);
167
+ const productChromePreset = resolveRoleChromePreset(chromePreset, viewState.editorRole);
168
+ const chromeOptions = resolveChromePresetOptions(productChromePreset, props.chromeOptions, viewState.editorRole);
233
169
  const preserveOnlyCount = caps?.preserveOnlyCount ??
234
170
  snapshot.compatibility.featureEntries.filter(
235
171
  (entry) => entry.featureClass === "preserve-only",
236
172
  ).length;
173
+ const showDebugDiagnostics = props.showDebugDiagnostics === true;
237
174
  const blockedReasons =
238
175
  props.interactionGuardSnapshot?.blockedReasons ??
239
176
  props.workflowScopeSnapshot?.blockedReasons ??
240
177
  [];
241
178
  const chromeVisibility = resolveChromeVisibilityForPreset({
242
- chromePreset,
179
+ chromePreset: productChromePreset,
243
180
  chromeOptions,
244
181
  chromeVisibility: props.chromeVisibility,
245
182
  });
@@ -265,7 +202,11 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
265
202
  const element = event.target as HTMLElement | null;
266
203
  const revisionId =
267
204
  element?.closest?.("[data-revision-id]")?.getAttribute("data-revision-id") ?? null;
268
- if (!revisionId || (revisionId === lastHoveredRevisionIdRef.current && reviewRailOpen)) {
205
+ if (
206
+ viewState.editorRole === "editor" ||
207
+ !revisionId ||
208
+ (revisionId === lastHoveredRevisionIdRef.current && reviewRailOpen)
209
+ ) {
269
210
  return;
270
211
  }
271
212
  lastHoveredRevisionIdRef.current = revisionId;
@@ -274,7 +215,13 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
274
215
  props.onActiveRailTabChange?.("changes");
275
216
  }
276
217
  },
277
- [props.onActiveRailTabChange, reviewRailAvailable, reviewRailOpen, setReviewRailOpen],
218
+ [
219
+ props.onActiveRailTabChange,
220
+ reviewRailAvailable,
221
+ reviewRailOpen,
222
+ setReviewRailOpen,
223
+ viewState.editorRole,
224
+ ],
278
225
  );
279
226
  // Incremented on zoom_changed / render_frame_ready so the placement
280
227
  // useMemo below re-executes when the render kernel emits new rects.
@@ -303,11 +250,18 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
303
250
  }, [renderFrameRevision, uiApi, shellChannels]);
304
251
  const scopeRailSegments = useMemo(
305
252
  () =>
253
+ // Mounted path: current scope rail truth flows through Layer 10
254
+ // `ui.scope.rail()`. The workflow facet remains only as the
255
+ // compatibility fallback when no UiApiProvider is mounted.
306
256
  uiApi?.scope.rail().segments ??
307
257
  props.workflowFacet?.getAllRailSegments() ??
308
258
  [],
309
259
  [uiApi, props.workflowFacet, renderFrameRevision],
310
260
  );
261
+ const mountedSemanticScopeCount = useMemo(
262
+ () => uiApi?.scope.list().length,
263
+ [uiApi, renderFrameRevision],
264
+ );
311
265
  const headings = props.documentNavigation?.headings ?? [];
312
266
  const headerVariant = snapshot.pageLayout?.headerVariants[0]?.variant ?? "default";
313
267
  const footerVariant = snapshot.pageLayout?.footerVariants[0]?.variant ?? "default";
@@ -345,6 +299,30 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
345
299
  viewportHeight,
346
300
  isPageWorkspace,
347
301
  });
302
+ const canvasDocumentColumnStyle = useMemo<CSSProperties | undefined>(() => {
303
+ if (isPageWorkspace) {
304
+ return undefined;
305
+ }
306
+ const contentWidthPx =
307
+ pageShellMetrics.contentWidthPx || DEFAULT_CANVAS_BODY_WIDTH_PX;
308
+ return { maxWidth: `${contentWidthPx}px` };
309
+ }, [isPageWorkspace, pageShellMetrics.contentWidthPx]);
310
+ const documentColumnStyle = useMemo<CSSProperties | undefined>(() => {
311
+ if (isPageWorkspace) {
312
+ return pageChromeModel.documentGridStyle;
313
+ }
314
+ if (!canvasDocumentColumnStyle && !pageChromeModel.documentGridStyle) {
315
+ return undefined;
316
+ }
317
+ return {
318
+ ...(canvasDocumentColumnStyle ?? {}),
319
+ ...(pageChromeModel.documentGridStyle ?? {}),
320
+ };
321
+ }, [
322
+ canvasDocumentColumnStyle,
323
+ isPageWorkspace,
324
+ pageChromeModel.documentGridStyle,
325
+ ]);
348
326
  // DS-C1 — publish the current selection anchor so
349
327
  // `ui.overlays.getAnchor({ kind: "selection" })` resolves to a real
350
328
  // rect on the mounted path (designsystem.md §8.8.1 "Selection toolbar"
@@ -391,7 +369,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
391
369
  snapshot.activeStory.kind !== "main" ||
392
370
  effectiveSelectionMode !== "edit";
393
371
  const hasEditableSelectionTarget =
394
- viewState.isFocused &&
395
372
  viewState.selection.activeRange.kind === "range" &&
396
373
  viewState.activeObjectFrame === null;
397
374
  const hasSidebarPanelAccess = Boolean(
@@ -402,7 +379,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
402
379
  viewportWidth,
403
380
  reviewRailAvailable,
404
381
  reviewRailOpen,
405
- chromePreset,
382
+ chromePreset: productChromePreset,
406
383
  caps,
407
384
  interactionGuardSnapshot: props.interactionGuardSnapshot,
408
385
  workflowScopeSnapshot: props.workflowScopeSnapshot,
@@ -412,11 +389,19 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
412
389
  effectiveSelectionMode,
413
390
  activeStoryKind: snapshot.activeStory.kind,
414
391
  });
392
+ const suppressFloatingToolDuringEditorInput =
393
+ viewState.editorRole === "editor" &&
394
+ viewState.selection.isCollapsed;
395
+ const shouldRenderFloatingSelectionTool =
396
+ chromeVisibility.selectionOverlay &&
397
+ gatedSelectionTool &&
398
+ !suppressFloatingToolDuringEditorInput &&
399
+ shouldRenderSelectionToolKind(scopedChromePolicy, gatedSelectionTool.kind);
415
400
 
416
401
  // L7 Phase 2 Task 2.2.4a — viewport-scroll wiring. Page marker
417
402
  // collection, selection-backed visible block/page ranges, and the
418
403
  // facet push all live in the hook.
419
- const { visibleBlockRange, visiblePageIndexRange } = usePageMarkers({
404
+ const { visiblePageIndexRange } = usePageMarkers({
420
405
  pageStackScrollRoot,
421
406
  snapshot,
422
407
  layoutFacet: props.layoutFacet,
@@ -510,56 +495,14 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
510
495
  [],
511
496
  );
512
497
 
513
- // Audit §2.4 the shell header is ALWAYS present in default composition.
514
- // When the host does not supply a pre-assembled shell node, fall back to
515
- // a default TwShellHeader wired to the workspace's editor-role state so
516
- // the mode tabs actually change the active role instead of being
517
- // decorative. The "more" tab is shell-owned diagnostics/search posture:
518
- // it drives composition mode via `modeOverride` and does not mutate the
519
- // runtime editor role.
520
- // Host-supplied shells continue to win (back-compat).
521
- const defaultShellModes: readonly ShellHeaderModeOption[] =
522
- DEFAULT_WORKSPACE_SHELL_MODES;
523
- const defaultShellActiveMode: ShellHeaderMode = editorRoleToShellMode(
524
- viewState.editorRole,
525
- );
526
- const activeShellMode: ShellHeaderMode =
527
- shellModeOverride === "more" ? "more" : defaultShellActiveMode;
528
- // coord-11 §21 — host-supplied shellHeader wins; otherwise the default
529
- // TwShellHeader mounts only when `chromeVisibility.shellHeader === true`
530
- // (default for every preset except `selection`). The `selection` preset
531
- // is intended for minimal embeds (incl. visual-fidelity `chrome=none`)
532
- // and must not paint a workspace mode-tab header.
498
+ // Host-supplied shells continue to win, but the product no longer mounts
499
+ // a default top mode-strip. Edit / Review / Workflow switching lives in
500
+ // the main toolbar so the document opens with one chrome row.
533
501
  const renderedShell =
534
- props.shellHeader !== undefined
502
+ chromeVisibility.shellHeader && props.shellHeader !== undefined
535
503
  ? props.shellHeader
536
- : chromeVisibility.shellHeader
537
- ? (
538
- <TwShellHeader
539
- modes={defaultShellModes}
540
- activeMode={activeShellMode}
541
- onModeChange={(mode) => {
542
- if (mode === "more") {
543
- setShellModeOverride("more");
544
- setReviewRailOpen(true);
545
- props.onActiveRailTabChange?.("health");
546
- return;
547
- }
548
- setShellModeOverride(null);
549
- const nextRole = shellModeToEditorRole(mode);
550
- if (nextRole !== null && nextRole !== viewState.editorRole) {
551
- props.onEditorRoleChange?.(nextRole);
552
- }
553
- }}
554
- />
555
- )
556
- : null;
504
+ : null;
557
505
 
558
- // Audit §2.5 — context band mounts as a composition-level sibling below
559
- // the shell header only when the active mode has meaningful mode-owned
560
- // content. Plain editor mode keeps its authoring controls in the toolbar
561
- // and intentionally does not render an empty "Edit" ribbon.
562
- //
563
506
  // `resolveChromeComposition` is pure but each call allocates new Sets +
564
507
  // arrays. `TwReviewWorkspace` re-renders on every PM transaction (the
565
508
  // inverted-truth architecture calls `view.updateState()` wholesale) so
@@ -573,12 +516,36 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
573
516
  snapshot,
574
517
  caps,
575
518
  preserveOnlyCount,
576
- blockedReasonsCount: blockedReasons.length,
519
+ blockedReasonsCount: showDebugDiagnostics ? blockedReasons.length : 0,
520
+ includePreservationDiagnostics: showDebugDiagnostics,
577
521
  });
578
522
  const healthIssueCount = diagnosticsSignal.count;
523
+ const diagnosticsCompatibility = useMemo(
524
+ () =>
525
+ showDebugDiagnostics
526
+ ? snapshot.compatibility
527
+ : filterProductCompatibility(snapshot.compatibility),
528
+ [showDebugDiagnostics, snapshot.compatibility],
529
+ );
530
+ const diagnosticsWarnings = useMemo(
531
+ () =>
532
+ showDebugDiagnostics
533
+ ? snapshot.warnings
534
+ : filterProductWarnings(snapshot.warnings),
535
+ [showDebugDiagnostics, snapshot.warnings],
536
+ );
537
+ const diagnosticsBlockedReasons = showDebugDiagnostics ? blockedReasons : [];
538
+ const chromeCapabilities = useMemo(() => {
539
+ if (!caps) return caps;
540
+ return {
541
+ ...caps,
542
+ healthIssueCount,
543
+ preserveOnlyCount: showDebugDiagnostics ? caps.preserveOnlyCount : 0,
544
+ };
545
+ }, [caps, healthIssueCount, showDebugDiagnostics]);
579
546
 
580
547
  const composition = useWorkspaceComposition({
581
- chromePreset,
548
+ chromePreset: productChromePreset,
582
549
  chromeOptions: props.chromeOptions,
583
550
  chromeVisibility: props.chromeVisibility,
584
551
  reviewMode: props.reviewMode,
@@ -590,137 +557,18 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
590
557
  density: props.density,
591
558
  containerWidth: viewportWidth,
592
559
  diagnosticsSignal,
593
- modeOverride: shellModeOverride === "more" ? "more" : undefined,
594
560
  });
595
561
  const showHealthRailTab = composition.rail.visibleTabs.has("health");
596
- const showContextBand =
597
- chromeVisibility.toolbar &&
598
- (composition.mode === "more" || viewState.editorRole !== "editor");
599
-
600
562
  const arbiter = useWorkspaceArbiter();
601
563
 
602
564
  return (
603
565
  <LocalSurfaceArbiterContext.Provider value={arbiter}>
604
566
  <Tooltip.Provider delayDuration={400}>
605
- <div className="flex h-full flex-col bg-canvas text-primary">
567
+ <div
568
+ className="flex h-full min-h-0 flex-col overflow-hidden bg-canvas text-primary"
569
+ data-testid="tw-review-workspace"
570
+ >
606
571
  {renderedShell}
607
- {/*
608
- * The context band is a workspace-layer surface (DESIGN-EDITOR.md
609
- * §4.2) and therefore gated on the toolbar-layer visibility flag
610
- * — the `selection` chrome preset suppresses both toolbar and
611
- * band so minimal embeds stay chrome-free. If a future preset
612
- * wants the band without the formatting toolbar, introduce a
613
- * dedicated `chromeVisibility.contextBand` flag rather than
614
- * decoupling this branch.
615
- */}
616
- {/*
617
- * Chrome Closure Pass · Task 1 (designsystem.md §6.3) — review,
618
- * workflow, and More mode actions live in `TwContextBand`; editor
619
- * mode stays toolbar-only unless a future pass adds real contextual
620
- * authoring content.
621
- */}
622
- {showContextBand ? (
623
- <TwContextBand mode={composition.mode}>
624
- {composition.mode === "more" ? (
625
- <TwMoreContextBandContent
626
- onOpenDiagnostics={() => {
627
- setReviewRailOpen(true);
628
- props.onActiveRailTabChange?.("health");
629
- }}
630
- onOpenCompatibility={() => {
631
- setReviewRailOpen(true);
632
- props.onActiveRailTabChange?.("health");
633
- }}
634
- onOpenOutline={() => setNavOpen(true)}
635
- onOpenSearch={props.onOpenInlineFind ?? props.reviewRailFooter?.onSearch}
636
- />
637
- ) : viewState.editorRole ? (
638
- <TwRoleActionRegion
639
- role={viewState.editorRole}
640
- policy={scopedChromePolicy}
641
- compactMode={responsiveChrome.isNarrow}
642
- reviewQueue={props.reviewQueue}
643
- markupDisplay={markupDisplay}
644
- canAddComment={
645
- toolbarInteractionPolicy?.canAddComment ??
646
- (caps ? caps.canAddComment : false)
647
- }
648
- showTrackedChanges={trackedChangesAuthoringEnabled}
649
- capabilities={caps}
650
- onAddComment={
651
- props.onAddComment
652
- ? runWithSelectionToolbarDismiss(props.onAddComment)
653
- : undefined
654
- }
655
- onShowTrackedChangesChange={(show) => {
656
- dismissSelectionToolbar();
657
- props.onShowTrackedChangesChange(show);
658
- }}
659
- onReviewMarkupMode={(mode) => {
660
- dismissSelectionToolbar();
661
- props.onReviewMarkupModeChange?.(mode);
662
- }}
663
- onReviewSidebarTrackedChanges={
664
- props.onReviewSidebarTrackedChanges
665
- ? runWithSelectionToolbarDismiss(props.onReviewSidebarTrackedChanges)
666
- : undefined
667
- }
668
- onReviewSidebarComments={
669
- props.onReviewSidebarComments
670
- ? runWithSelectionToolbarDismiss(props.onReviewSidebarComments)
671
- : undefined
672
- }
673
- onMarkScopePosture={
674
- props.onMarkSectionForReview
675
- ? runWithSelectionToolbarDismiss(props.onMarkSectionForReview)
676
- : undefined
677
- }
678
- onReviewPrev={
679
- props.onGoToPreviousReviewItem
680
- ? runWithSelectionToolbarDismiss(props.onGoToPreviousReviewItem)
681
- : undefined
682
- }
683
- onReviewNext={
684
- props.onGoToNextReviewItem
685
- ? runWithSelectionToolbarDismiss(props.onGoToNextReviewItem)
686
- : undefined
687
- }
688
- onReviewAccept={(() => {
689
- const active = props.reviewQueue?.items[props.reviewQueue.activeIndex];
690
- if (active?.kind !== "change" || !props.onAcceptRevision) {
691
- return undefined;
692
- }
693
- const revisionId = active.itemId;
694
- return () => {
695
- dismissSelectionToolbar();
696
- props.onAcceptRevision?.(revisionId);
697
- };
698
- })()}
699
- onReviewReject={(() => {
700
- const active = props.reviewQueue?.items[props.reviewQueue.activeIndex];
701
- if (active?.kind !== "change" || !props.onRejectRevision) {
702
- return undefined;
703
- }
704
- const revisionId = active.itemId;
705
- return () => {
706
- dismissSelectionToolbar();
707
- props.onRejectRevision?.(revisionId);
708
- };
709
- })()}
710
- onReviewAcceptAll={
711
- props.onAcceptAllChanges
712
- ? runWithSelectionToolbarDismiss(props.onAcceptAllChanges)
713
- : undefined
714
- }
715
- onReviewRejectAll={
716
- props.onRejectAllChanges
717
- ? runWithSelectionToolbarDismiss(props.onRejectAllChanges)
718
- : undefined
719
- }
720
- />
721
- ) : null}
722
- </TwContextBand>
723
- ) : null}
724
572
  {/*
725
573
  * Phase C.γ.3 — mount the workspace chrome host when the
726
574
  * integrator has wired an editor-action bag. Omitting the
@@ -746,9 +594,12 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
746
594
  />
747
595
  ) : null}
748
596
  {chromeVisibility.toolbar ? (
749
- <div className="px-3 pt-3">
597
+ <div
598
+ className="sticky top-0 z-30 shrink-0 bg-[var(--color-bg-canvas)]/95 px-3 pt-3 backdrop-blur-sm"
599
+ data-testid="tw-review-workspace__sticky-toolbar"
600
+ >
750
601
  <ChromePresetToolbar
751
- chromePreset={chromePreset}
602
+ chromePreset={productChromePreset}
752
603
  {...(props.collabSession ? { collabSession: props.collabSession } : {})}
753
604
  {...(props.collabTransportStatus
754
605
  ? { collabTransportStatus: props.collabTransportStatus }
@@ -763,10 +614,10 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
763
614
  ? { collabSendBaseline: props.collabSendBaseline }
764
615
  : {})}
765
616
  chromeOptionsResolved={chromeOptions}
766
- capabilities={caps}
767
- compatibility={snapshot.compatibility}
768
- warnings={snapshot.warnings}
769
- blockedReasons={blockedReasons}
617
+ capabilities={chromeCapabilities}
618
+ compatibility={diagnosticsCompatibility}
619
+ warnings={diagnosticsWarnings}
620
+ blockedReasons={diagnosticsBlockedReasons}
770
621
  showDiagnosticsChrome={chromeVisibility.alerts}
771
622
  interactionPolicy={toolbarInteractionPolicy}
772
623
  scopedChromePolicy={scopedChromePolicy}
@@ -894,6 +745,12 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
894
745
  props.onReviewMarkupModeChange?.(mode);
895
746
  }}
896
747
  role={viewState.editorRole}
748
+ onEditorRoleChange={(role) => {
749
+ dismissSelectionToolbar();
750
+ if (role !== viewState.editorRole) {
751
+ props.onEditorRoleChange?.(role);
752
+ }
753
+ }}
897
754
  reviewQueue={props.reviewQueue}
898
755
  markupDisplay={markupDisplay}
899
756
  onReviewSidebarTrackedChanges={props.onReviewSidebarTrackedChanges
@@ -943,8 +800,9 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
943
800
 
944
801
  {chromeVisibility.alerts ? <TwAlertBanner
945
802
  snapshot={snapshot}
946
- preserveOnlyCount={preserveOnlyCount}
947
- workflowBlockedReasons={blockedReasons}
803
+ preserveOnlyCount={showDebugDiagnostics ? preserveOnlyCount : 0}
804
+ workflowBlockedReasons={diagnosticsBlockedReasons}
805
+ showPreservationDiagnostics={showDebugDiagnostics}
948
806
  onShowDetail={() => {
949
807
  setReviewRailOpen(true);
950
808
  props.onActiveRailTabChange?.("health");
@@ -964,7 +822,10 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
964
822
  />
965
823
 
966
824
  {/* Document column */}
967
- <div className="flex flex-1 flex-col min-w-0">
825
+ <div
826
+ className="flex min-h-0 flex-1 flex-col min-w-0"
827
+ data-testid="tw-review-workspace__document-scroll-column"
828
+ >
968
829
  <div
969
830
  ref={scrollRootRef}
970
831
  onMouseOver={handleDocumentMouseOver}
@@ -989,11 +850,10 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
989
850
  * section-properties controls move to the on-demand
990
851
  * band-activation ribbon (Slice B).
991
852
  */}
992
- {chromeVisibility.selectionOverlay &&
993
- gatedSelectionTool &&
994
- shouldRenderSelectionToolKind(scopedChromePolicy, gatedSelectionTool.kind) ? (
853
+ {shouldRenderFloatingSelectionTool ? (
995
854
  <TwSelectionToolHost
996
855
  tool={gatedSelectionTool}
856
+ selectionKey={`${viewState.selection.anchor}:${viewState.selection.head}:${viewState.selection.storyTarget?.kind ?? "main"}`}
997
857
  contextAnalytics={
998
858
  chromeVisibility.contextAnalytics
999
859
  ? props.selectionContextAnalytics
@@ -1037,6 +897,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1037
897
  onSetTableAlignment={props.onSetTableAlignment}
1038
898
  onSetCellVerticalAlign={props.onSetCellVerticalAlign}
1039
899
  onOpenTableMore={openTableMoreMenu}
900
+ showPreservationDiagnostics={showDebugDiagnostics}
1040
901
  chromePins={viewState.chromePins}
1041
902
  onChromePinChange={props.onChromePinChange}
1042
903
  />
@@ -1135,10 +996,11 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1135
996
  style={isPageWorkspace ? pageShellMetrics.contentInsetStyle : undefined}
1136
997
  >
1137
998
  <div
1138
- className={isPageWorkspace ? "relative" : undefined}
999
+ className={isPageWorkspace ? "relative" : "relative mx-auto w-full max-w-[80rem]"}
1000
+ data-testid="tw-review-workspace__document-column"
1139
1001
  data-document-grid={pageChromeModel.documentGridType}
1140
1002
  data-page-border-display={pageChromeModel.pageBorderDisplay}
1141
- style={pageChromeModel.documentGridStyle}
1003
+ style={documentColumnStyle}
1142
1004
  >
1143
1005
  {isPageWorkspace && chromeVisibility.pageChrome && pageChromeModel.showPageBorder && !hidePageBorderForActiveEditing ? (
1144
1006
  <div
@@ -1188,6 +1050,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1188
1050
  grabbedObjectFromOffset={grabbedSegmentOffsets?.from ?? null}
1189
1051
  grabbedObjectToOffset={grabbedSegmentOffsets?.to ?? null}
1190
1052
  onDeselectObject={props.onDeselectObject}
1053
+ onBeginObjectDrag={props.onBeginObjectDrag}
1191
1054
  tableContext={props.tableContext}
1192
1055
  onSetColumnWidth={props.onSetColumnWidth}
1193
1056
  onSetRowHeight={props.onSetRowHeight}
@@ -1243,7 +1106,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1243
1106
  <TwStatusBar
1244
1107
  isDirty={snapshot.isDirty}
1245
1108
  isExportBlocked={snapshot.compatibility.blockExport}
1246
- preserveOnlyCount={preserveOnlyCount}
1109
+ preserveOnlyCount={showDebugDiagnostics ? preserveOnlyCount : 0}
1247
1110
  commentCount={snapshot.comments.totalCount}
1248
1111
  changeCount={snapshot.trackedChanges.totalCount}
1249
1112
  sessionId={snapshot.sessionId}
@@ -1255,15 +1118,16 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1255
1118
  displayPageNumber={statusBarPageFacts.displayPageNumber}
1256
1119
  pageCount={statusBarPageFacts.pageCount}
1257
1120
  measurementFidelity={statusBarPageFacts.measurementFidelity}
1121
+ debugMode={showDebugDiagnostics}
1258
1122
  />
1259
1123
  ) : null}
1260
1124
  </div>
1261
1125
 
1262
- {/* Review rail — docked on desktop, drawer-backed on narrow layouts */}
1126
+ {/* Review rail — overlay on desktop, drawer-backed on narrow layouts. */}
1263
1127
  <TwReviewWorkspaceRail
1264
1128
  mode={
1265
1129
  (responsiveChrome.showDockedReviewRail
1266
- ? "docked"
1130
+ ? "overlay"
1267
1131
  : responsiveChrome.showDrawerReviewRail
1268
1132
  ? "drawer"
1269
1133
  : "hidden") satisfies TwReviewWorkspaceRailMode
@@ -1274,8 +1138,8 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1274
1138
  currentUserId: props.currentUserId,
1275
1139
  comments: snapshot.comments,
1276
1140
  trackedChanges: snapshot.trackedChanges,
1277
- compatibility: snapshot.compatibility,
1278
- warnings: snapshot.warnings,
1141
+ compatibility: diagnosticsCompatibility,
1142
+ warnings: diagnosticsWarnings,
1279
1143
  markupDisplay,
1280
1144
  contextAnalytics: chromeVisibility.contextAnalytics
1281
1145
  ? props.currentScopeContextAnalytics
@@ -1295,7 +1159,8 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1295
1159
  onAcceptAllChanges: props.onAcceptAllChanges,
1296
1160
  onRejectAllChanges: props.onRejectAllChanges,
1297
1161
  // Layer 11 closeout: mounted workspace chrome reads scope
1298
- // rail data through `api.ui.scope.rail()`, with the workflow
1162
+ // rail data through `api.ui.scope.rail()` and scope identity
1163
+ // counts through `api.ui.scope.list()`, with the workflow
1299
1164
  // facet retained as the no-provider fallback.
1300
1165
  scopeRailSegments,
1301
1166
  activeScopeId,
@@ -1303,7 +1168,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1303
1168
  handleScopeStripeClick({ scopeId: segment.scopeId });
1304
1169
  },
1305
1170
  workflowTab: props.reviewRailWorkflowTab,
1306
- workflowCount: props.reviewRailWorkflowCount,
1171
+ workflowCount: props.reviewRailWorkflowCount ?? mountedSemanticScopeCount,
1307
1172
  workflowScopesTitle: props.reviewRailWorkflowScopesTitle,
1308
1173
  intelligenceEyebrow: props.reviewRailIntelligenceEyebrow,
1309
1174
  intelligenceHeader: props.reviewRailIntelligenceHeader,
@@ -1313,7 +1178,8 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
1313
1178
  ...(diagnosticsSignal.severity !== "none"
1314
1179
  ? { healthSeverity: diagnosticsSignal.severity }
1315
1180
  : {}),
1316
- workflowBlockedReasons: blockedReasons,
1181
+ workflowBlockedReasons: diagnosticsBlockedReasons,
1182
+ showPreservationDiagnostics: showDebugDiagnostics,
1317
1183
  }}
1318
1184
  />
1319
1185
  </div>