@beyondwork/docx-react-component 1.0.36 → 1.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +103 -13
  2. package/package.json +1 -1
  3. package/src/api/package-version.ts +13 -0
  4. package/src/api/public-types.ts +402 -1
  5. package/src/core/commands/index.ts +18 -1
  6. package/src/core/commands/section-layout-commands.ts +58 -0
  7. package/src/core/commands/table-grid.ts +431 -0
  8. package/src/core/commands/table-structure-commands.ts +815 -55
  9. package/src/core/selection/mapping.ts +6 -0
  10. package/src/io/docx-session.ts +24 -9
  11. package/src/io/export/build-app-properties-xml.ts +88 -0
  12. package/src/io/export/serialize-comments.ts +6 -1
  13. package/src/io/export/serialize-footnotes.ts +10 -9
  14. package/src/io/export/serialize-headers-footers.ts +11 -10
  15. package/src/io/export/serialize-main-document.ts +328 -50
  16. package/src/io/export/serialize-numbering.ts +114 -24
  17. package/src/io/export/serialize-tables.ts +87 -11
  18. package/src/io/export/table-properties-xml.ts +174 -20
  19. package/src/io/export/twip.ts +66 -0
  20. package/src/io/normalize/normalize-text.ts +20 -0
  21. package/src/io/ooxml/parse-footnotes.ts +62 -1
  22. package/src/io/ooxml/parse-headers-footers.ts +62 -1
  23. package/src/io/ooxml/parse-main-document.ts +158 -1
  24. package/src/io/ooxml/parse-tables.ts +249 -0
  25. package/src/legal/bookmarks.ts +78 -0
  26. package/src/model/canonical-document.ts +45 -0
  27. package/src/review/store/scope-tag-diff.ts +130 -0
  28. package/src/runtime/document-layout.ts +4 -2
  29. package/src/runtime/document-navigation.ts +2 -306
  30. package/src/runtime/document-runtime.ts +287 -11
  31. package/src/runtime/layout/default-page-format.ts +96 -0
  32. package/src/runtime/layout/docx-font-loader.ts +143 -0
  33. package/src/runtime/layout/index.ts +233 -0
  34. package/src/runtime/layout/inert-layout-facet.ts +59 -0
  35. package/src/runtime/layout/layout-engine-instance.ts +628 -0
  36. package/src/runtime/layout/layout-invalidation.ts +257 -0
  37. package/src/runtime/layout/layout-measurement-provider.ts +175 -0
  38. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  39. package/src/runtime/layout/measurement-backend-canvas.ts +307 -0
  40. package/src/runtime/layout/measurement-backend-empirical.ts +208 -0
  41. package/src/runtime/layout/page-format-catalog.ts +233 -0
  42. package/src/runtime/layout/page-fragment-mapper.ts +179 -0
  43. package/src/runtime/layout/page-graph.ts +452 -0
  44. package/src/runtime/layout/page-layout-snapshot-adapter.ts +70 -0
  45. package/src/runtime/layout/page-story-resolver.ts +195 -0
  46. package/src/runtime/layout/paginated-layout-engine.ts +921 -0
  47. package/src/runtime/layout/project-block-fragments.ts +91 -0
  48. package/src/runtime/layout/public-facet.ts +1398 -0
  49. package/src/runtime/layout/resolved-formatting-document.ts +317 -0
  50. package/src/runtime/layout/resolved-formatting-state.ts +430 -0
  51. package/src/runtime/layout/table-render-plan.ts +229 -0
  52. package/src/runtime/render/block-fragment-projection.ts +35 -0
  53. package/src/runtime/render/decoration-resolver.ts +189 -0
  54. package/src/runtime/render/index.ts +57 -0
  55. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  56. package/src/runtime/render/render-frame-types.ts +317 -0
  57. package/src/runtime/render/render-kernel.ts +755 -0
  58. package/src/runtime/scope-tag-registry.ts +95 -0
  59. package/src/runtime/surface-projection.ts +1 -0
  60. package/src/runtime/text-ack-range.ts +49 -0
  61. package/src/runtime/view-state.ts +67 -0
  62. package/src/runtime/workflow-markup.ts +1 -5
  63. package/src/runtime/workflow-rail-segments.ts +280 -0
  64. package/src/ui/WordReviewEditor.tsx +99 -15
  65. package/src/ui/editor-runtime-boundary.ts +10 -1
  66. package/src/ui/editor-shell-view.tsx +6 -0
  67. package/src/ui/editor-surface-controller.tsx +3 -0
  68. package/src/ui/headless/chrome-registry.ts +501 -0
  69. package/src/ui/headless/scoped-chrome-policy.ts +183 -0
  70. package/src/ui/headless/selection-tool-context.ts +2 -0
  71. package/src/ui/headless/selection-tool-resolver.ts +36 -17
  72. package/src/ui/headless/selection-tool-types.ts +10 -0
  73. package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
  74. package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
  75. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  76. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
  77. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
  78. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  79. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  80. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
  81. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
  82. package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
  83. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
  84. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  85. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
  86. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +337 -0
  87. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +100 -0
  88. package/src/ui-tailwind/editor-surface/perf-probe.ts +27 -1
  89. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +20 -2
  90. package/src/ui-tailwind/editor-surface/pm-decorations.ts +93 -23
  91. package/src/ui-tailwind/editor-surface/predicted-position-map.ts +78 -0
  92. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +63 -0
  93. package/src/ui-tailwind/editor-surface/predicted-tx-gate.ts +39 -0
  94. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +176 -6
  95. package/src/ui-tailwind/index.ts +33 -0
  96. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  97. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  98. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  99. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  100. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  101. package/src/ui-tailwind/theme/editor-theme.css +505 -144
  102. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
  103. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  104. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  105. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +2 -2
  106. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +304 -166
  107. package/src/ui-tailwind/tw-review-workspace.tsx +163 -2
@@ -43,11 +43,14 @@ import {
43
43
  import type {
44
44
  ActiveListContext,
45
45
  CompatibilityPanelSnapshot,
46
+ EditorRole,
46
47
  EditorStoryTarget,
47
48
  EditorWarning,
48
49
  FormattingStateSnapshot,
49
50
  FormattingAlignment,
50
51
  InsertImageOptions,
52
+ ReviewQueueSnapshot,
53
+ ScopeRailPosture,
51
54
  SectionBreakType,
52
55
  StyleCatalogSnapshot,
53
56
  WorkflowBlockedCommandReason,
@@ -56,9 +59,19 @@ import type {
56
59
  ZoomLevel,
57
60
  } from "../../api/public-types";
58
61
  import type { SessionCapabilities } from "../../runtime/session-capabilities";
62
+ import {
63
+ getToolbarChromePlacement,
64
+ isToolbarChromeItemVisible,
65
+ resolveScopedChromePolicy,
66
+ type ScopedChromePolicy,
67
+ } from "../../ui/headless/scoped-chrome-policy";
59
68
  import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
60
69
  import { TwHealthPanel } from "../review/tw-health-panel";
61
- import { resolveToolbarLayoutModel } from "./toolbar-layout";
70
+ import {
71
+ TwRoleActionRegion,
72
+ type MarkupDisplayMode,
73
+ type WorkflowWorkItemSnapshot,
74
+ } from "./tw-role-action-region";
62
75
  import { TwToolbarIconButton } from "./tw-toolbar-icon-button";
63
76
 
64
77
  export interface TwToolbarProps {
@@ -68,6 +81,7 @@ export interface TwToolbarProps {
68
81
  blockedReasons?: WorkflowBlockedCommandReason[];
69
82
  showDiagnosticsChrome?: boolean;
70
83
  interactionPolicy?: ToolbarInteractionPolicy;
84
+ scopedChromePolicy?: ScopedChromePolicy;
71
85
  preset?: WordReviewEditorChromePreset;
72
86
  compactMode?: boolean;
73
87
  workspaceMode: WorkspaceMode;
@@ -115,6 +129,39 @@ export interface TwToolbarProps {
115
129
  onContinueNumbering?: () => void;
116
130
  onUpdateFields?: () => void;
117
131
  onUpdateTableOfContents?: () => void;
132
+
133
+ // ───── R1: role-scoped inline action region (spec §6.4) ──────────────
134
+ /**
135
+ * Active editor role. When supplied, the toolbar renders the inline
136
+ * `TwRoleActionRegion` between the left formatting cluster and the
137
+ * right view cluster. Omit to keep the pre-R1 layout.
138
+ */
139
+ role?: EditorRole;
140
+ /** Review-queue snapshot for the review role's inline prev/next/counts. */
141
+ reviewQueue?: ReviewQueueSnapshot;
142
+ /** Active work item for the workflow role's inline queue. */
143
+ workflowItem?: WorkflowWorkItemSnapshot | null;
144
+ /** Markup display mode for the review role. */
145
+ markupDisplay?: MarkupDisplayMode;
146
+
147
+ // Editor role
148
+ onMarkScopePosture?: (posture: ScopeRailPosture) => void;
149
+ // Review role
150
+ onReviewPrev?: () => void;
151
+ onReviewNext?: () => void;
152
+ onReviewAccept?: () => void;
153
+ onReviewReject?: () => void;
154
+ onReviewAcceptAll?: () => void;
155
+ onReviewRejectAll?: () => void;
156
+ onReviewMarkupMode?: (mode: MarkupDisplayMode) => void;
157
+ // Workflow role
158
+ onWorkflowPrev?: () => void;
159
+ onWorkflowNext?: () => void;
160
+ onWorkflowMarkComplete?: () => void;
161
+ onWorkflowClaim?: () => void;
162
+ onWorkflowSkip?: () => void;
163
+ onWorkflowMarkBlocked?: () => void;
164
+ onWorkflowJumpToScope?: () => void;
118
165
  }
119
166
 
120
167
  export interface ToolbarInteractionPolicy {
@@ -152,27 +199,56 @@ export function TwToolbar(props: TwToolbarProps) {
152
199
  const canEdit = props.interactionPolicy?.canFormatText ?? (caps ? caps.canEdit : false);
153
200
  const canInsertStructural = props.interactionPolicy?.canInsertStructural ?? canEdit;
154
201
  const canAddComment = props.interactionPolicy?.canAddComment ?? (caps ? caps.canAddComment : false);
155
- const showStyleSelectors = preset === "advanced";
156
- const showAdvancedFormatting = preset === "advanced";
157
- const showFormattingColors = preset !== "review";
158
- const showInsertMenu = preset === "simple" || preset === "advanced";
159
- const showTrackedChangesToggle = preset !== "simple";
160
202
  const showDiagnosticsChrome = props.showDiagnosticsChrome ?? true;
161
- const showHealth = showDiagnosticsChrome && Boolean(props.compatibility && props.warnings);
162
- const showListActions = preset === "simple" || preset === "advanced";
163
- const showUpdateActions = preset === "advanced";
164
- const showSidebarToggle = props.showSidebarToggle ?? false;
165
- const layoutModel = resolveToolbarLayoutModel({
166
- compactMode: isCompact,
203
+ const scopedChromePolicy = props.scopedChromePolicy ?? resolveScopedChromePolicy({
167
204
  preset,
168
- hasActiveListContext: Boolean(props.activeListContext),
205
+ compactMode: isCompact,
206
+ capabilities: caps,
207
+ interactionGuardSnapshot: props.interactionPolicy
208
+ ? ({
209
+ effectiveMode: props.interactionPolicy.mode,
210
+ blockedReasons: props.blockedReasons ?? [],
211
+ } as any)
212
+ : undefined,
213
+ activeListContext: props.activeListContext,
214
+ ...(props.role ? { role: props.role } : {}),
169
215
  });
216
+ const showStyleSelectors = isToolbarChromeItemVisible(scopedChromePolicy, "text-style-selectors");
217
+ const showInlineFormatting = isToolbarChromeItemVisible(scopedChromePolicy, "inline-formatting");
218
+ const showAdvancedFormatting = preset === "advanced" && showInlineFormatting;
219
+ const showTextColors = isToolbarChromeItemVisible(scopedChromePolicy, "text-colors");
220
+ const showParagraphAlignment = isToolbarChromeItemVisible(scopedChromePolicy, "paragraph-alignment");
221
+ const showInsertMenu = isToolbarChromeItemVisible(scopedChromePolicy, "insert-actions");
222
+ const showTrackedChangesToggle = isToolbarChromeItemVisible(scopedChromePolicy, "tracked-changes-toggle");
223
+ const showHealth =
224
+ showDiagnosticsChrome &&
225
+ isToolbarChromeItemVisible(scopedChromePolicy, "health") &&
226
+ Boolean(props.compatibility && props.warnings);
227
+ const showListActions = isToolbarChromeItemVisible(scopedChromePolicy, "list-actions");
228
+ const showUpdateActions = isToolbarChromeItemVisible(scopedChromePolicy, "update-actions");
229
+ const showSidebarToggle =
230
+ (props.showSidebarToggle ?? false) &&
231
+ isToolbarChromeItemVisible(scopedChromePolicy, "sidebar-toggle");
232
+ const showStyleSelectorsInRow = getToolbarChromePlacement(scopedChromePolicy, "text-style-selectors") === "inline";
233
+ const showListActionsInRow = getToolbarChromePlacement(scopedChromePolicy, "list-actions") === "inline";
234
+ const showSpacingActionsInRow = getToolbarChromePlacement(scopedChromePolicy, "indentation") === "inline";
235
+ const showListContinuationInRow =
236
+ getToolbarChromePlacement(scopedChromePolicy, "list-continuation") === "inline";
237
+ const showInsertActionsInRow = getToolbarChromePlacement(scopedChromePolicy, "insert-actions") === "inline";
238
+ const showUpdateActionsInRow = getToolbarChromePlacement(scopedChromePolicy, "update-actions") === "inline";
239
+ const showCompactOverflow =
240
+ getToolbarChromePlacement(scopedChromePolicy, "text-style-selectors") === "overflow" ||
241
+ getToolbarChromePlacement(scopedChromePolicy, "list-actions") === "overflow" ||
242
+ getToolbarChromePlacement(scopedChromePolicy, "indentation") === "overflow" ||
243
+ getToolbarChromePlacement(scopedChromePolicy, "list-continuation") === "overflow" ||
244
+ getToolbarChromePlacement(scopedChromePolicy, "insert-actions") === "overflow" ||
245
+ getToolbarChromePlacement(scopedChromePolicy, "update-actions") === "overflow";
170
246
  const showPostFormattingDivider =
171
- layoutModel.showListActionsInRow ||
172
- layoutModel.showSpacingActionsInRow ||
173
- (showInsertMenu && layoutModel.showInsertActionsInRow) ||
174
- (showUpdateActions && layoutModel.showUpdateActionsInRow) ||
175
- layoutModel.showCompactOverflow;
247
+ showListActionsInRow ||
248
+ showSpacingActionsInRow ||
249
+ showInsertActionsInRow ||
250
+ showUpdateActionsInRow ||
251
+ showCompactOverflow;
176
252
  const zoomLabel =
177
253
  typeof zoomLevel === "number"
178
254
  ? `${zoomLevel}%`
@@ -185,8 +261,8 @@ export function TwToolbar(props: TwToolbarProps) {
185
261
  className={[
186
262
  "shrink-0 rounded-xl border border-border/70 bg-canvas/92 px-2.5 shadow-[0_8px_20px_-18px_var(--color-shadow-strong)] backdrop-blur-sm",
187
263
  isCompact
188
- ? "flex min-h-11 flex-wrap items-center gap-1.5 py-2"
189
- : "flex h-11 items-center gap-1",
264
+ ? "flex min-h-10 flex-wrap items-center gap-1.5 py-1.5"
265
+ : "flex h-10 items-center gap-1",
190
266
  ].join(" ")}
191
267
  >
192
268
  {/* Left cluster: undo/redo + formatting */}
@@ -205,7 +281,7 @@ export function TwToolbar(props: TwToolbarProps) {
205
281
  />
206
282
  <div className="mx-1 h-4 w-px bg-border" />
207
283
 
208
- {showStyleSelectors && layoutModel.showStyleSelectorsInRow ? (
284
+ {showStyleSelectors && showStyleSelectorsInRow ? (
209
285
  <>
210
286
  <ToolbarParagraphStyleSelect
211
287
  disabled={!canEdit || paragraphStyles.length === 0 || !props.onSetParagraphStyle}
@@ -229,69 +305,79 @@ export function TwToolbar(props: TwToolbarProps) {
229
305
  </>
230
306
  ) : null}
231
307
 
232
- <TwToolbarIconButton
233
- icon={Bold}
234
- label="Bold"
235
- active={props.formattingState?.bold ?? false}
236
- disabled={!canEdit}
237
- onClick={props.onToggleBold}
238
- />
239
- <TwToolbarIconButton
240
- icon={Italic}
241
- label="Italic"
242
- active={props.formattingState?.italic ?? false}
243
- disabled={!canEdit}
244
- onClick={props.onToggleItalic}
245
- />
246
- <TwToolbarIconButton
247
- icon={Underline}
248
- label="Underline"
249
- active={props.formattingState?.underline ?? false}
250
- disabled={!canEdit}
251
- onClick={props.onToggleUnderline}
252
- />
253
- {showAdvancedFormatting ? (
254
- <ToolbarFormattingOverflow
255
- disabled={!canEdit}
256
- formattingState={props.formattingState}
257
- onToggleStrikethrough={props.onToggleStrikethrough}
258
- onToggleSuperscript={props.onToggleSuperscript}
259
- onToggleSubscript={props.onToggleSubscript}
260
- />
261
- ) : null}
262
- {showFormattingColors ? (
308
+ {showInlineFormatting ? (
263
309
  <>
264
- <ToolbarColorPopover
265
- ariaLabel="Text color"
266
- colors={TEXT_COLORS.map((value) => ({ value, label: value }))}
267
- disabled={!canEdit || !props.onSetTextColor}
268
- icon={<Baseline className="h-3.5 w-3.5" />}
269
- onSelect={(value) => {
270
- if (value) {
271
- props.onSetTextColor?.(value);
272
- }
273
- }}
274
- title="Text color"
310
+ <TwToolbarIconButton
311
+ icon={Bold}
312
+ label="Bold"
313
+ active={props.formattingState?.bold ?? false}
314
+ disabled={!canEdit}
315
+ onClick={props.onToggleBold}
275
316
  />
276
- <ToolbarColorPopover
277
- ariaLabel="Highlight color"
278
- colors={HIGHLIGHT_COLORS.map((entry) => ({ value: entry.value, label: entry.label }))}
279
- disabled={!canEdit || !props.onSetHighlightColor}
280
- icon={<Highlighter className="h-3.5 w-3.5" />}
281
- onSelect={(value) => props.onSetHighlightColor?.(value)}
282
- title="Highlight color"
317
+ <TwToolbarIconButton
318
+ icon={Italic}
319
+ label="Italic"
320
+ active={props.formattingState?.italic ?? false}
321
+ disabled={!canEdit}
322
+ onClick={props.onToggleItalic}
283
323
  />
284
- <ToolbarAlignmentPopover
285
- activeAlignment={props.formattingState?.alignment}
286
- disabled={!canEdit || !props.onSetAlignment}
287
- onSelect={(alignment) => props.onSetAlignment?.(alignment)}
324
+ <TwToolbarIconButton
325
+ icon={Underline}
326
+ label="Underline"
327
+ active={props.formattingState?.underline ?? false}
328
+ disabled={!canEdit}
329
+ onClick={props.onToggleUnderline}
288
330
  />
331
+ {showAdvancedFormatting ? (
332
+ <ToolbarFormattingOverflow
333
+ disabled={!canEdit}
334
+ formattingState={props.formattingState}
335
+ onToggleStrikethrough={props.onToggleStrikethrough}
336
+ onToggleSuperscript={props.onToggleSuperscript}
337
+ onToggleSubscript={props.onToggleSubscript}
338
+ />
339
+ ) : null}
340
+ </>
341
+ ) : null}
342
+ {showTextColors || showParagraphAlignment ? (
343
+ <>
344
+ {showTextColors ? (
345
+ <>
346
+ <ToolbarColorPopover
347
+ ariaLabel="Text color"
348
+ colors={TEXT_COLORS.map((value) => ({ value, label: value }))}
349
+ disabled={!canEdit || !props.onSetTextColor}
350
+ icon={<Baseline className="h-3.5 w-3.5" />}
351
+ onSelect={(value) => {
352
+ if (value) {
353
+ props.onSetTextColor?.(value);
354
+ }
355
+ }}
356
+ title="Text color"
357
+ />
358
+ <ToolbarColorPopover
359
+ ariaLabel="Highlight color"
360
+ colors={HIGHLIGHT_COLORS.map((entry) => ({ value: entry.value, label: entry.label }))}
361
+ disabled={!canEdit || !props.onSetHighlightColor}
362
+ icon={<Highlighter className="h-3.5 w-3.5" />}
363
+ onSelect={(value) => props.onSetHighlightColor?.(value)}
364
+ title="Highlight color"
365
+ />
366
+ </>
367
+ ) : null}
368
+ {showParagraphAlignment ? (
369
+ <ToolbarAlignmentPopover
370
+ activeAlignment={props.formattingState?.alignment}
371
+ disabled={!canEdit || !props.onSetAlignment}
372
+ onSelect={(alignment) => props.onSetAlignment?.(alignment)}
373
+ />
374
+ ) : null}
289
375
  </>
290
376
  ) : null}
291
377
 
292
378
  {showPostFormattingDivider ? <div className="mx-1 h-4 w-px bg-border" /> : null}
293
379
 
294
- {showListActions && layoutModel.showListActionsInRow ? (
380
+ {showListActions && showListActionsInRow ? (
295
381
  <>
296
382
  <TwToolbarIconButton
297
383
  icon={List}
@@ -309,7 +395,7 @@ export function TwToolbar(props: TwToolbarProps) {
309
395
  />
310
396
  </>
311
397
  ) : null}
312
- {layoutModel.showSpacingActionsInRow ? (
398
+ {showSpacingActionsInRow ? (
313
399
  <>
314
400
  <TwToolbarIconButton
315
401
  icon={Outdent}
@@ -325,7 +411,7 @@ export function TwToolbar(props: TwToolbarProps) {
325
411
  />
326
412
  </>
327
413
  ) : null}
328
- {layoutModel.showListContinuationInRow ? (
414
+ {showListContinuationInRow ? (
329
415
  <>
330
416
  <button
331
417
  type="button"
@@ -333,7 +419,7 @@ export function TwToolbar(props: TwToolbarProps) {
333
419
  disabled={!canEdit || !props.onRestartNumbering}
334
420
  onMouseDown={preserveEditorSelectionMouseDown}
335
421
  onClick={props.onRestartNumbering}
336
- className={`inline-flex h-7 items-center rounded-md px-2 text-xs font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
422
+ className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
337
423
  >
338
424
  Restart
339
425
  </button>
@@ -343,13 +429,13 @@ export function TwToolbar(props: TwToolbarProps) {
343
429
  disabled={!canEdit || !props.onContinueNumbering}
344
430
  onMouseDown={preserveEditorSelectionMouseDown}
345
431
  onClick={props.onContinueNumbering}
346
- className={`inline-flex h-7 items-center rounded-md px-2 text-xs font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
432
+ className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
347
433
  >
348
434
  Continue
349
435
  </button>
350
436
  </>
351
437
  ) : null}
352
- {showInsertMenu && layoutModel.showInsertActionsInRow ? (
438
+ {showInsertMenu && showInsertActionsInRow ? (
353
439
  <ToolbarInsertMenu
354
440
  disabled={!canInsertStructural}
355
441
  onInsertImage={props.onInsertImage}
@@ -358,7 +444,7 @@ export function TwToolbar(props: TwToolbarProps) {
358
444
  onInsertTable={props.onInsertTable}
359
445
  />
360
446
  ) : null}
361
- {showUpdateActions && layoutModel.showUpdateActionsInRow ? (
447
+ {showUpdateActions && showUpdateActionsInRow ? (
362
448
  <>
363
449
  <button
364
450
  type="button"
@@ -366,7 +452,7 @@ export function TwToolbar(props: TwToolbarProps) {
366
452
  disabled={!canEdit || !props.onUpdateFields}
367
453
  onMouseDown={preserveEditorSelectionMouseDown}
368
454
  onClick={props.onUpdateFields}
369
- className={`inline-flex h-7 items-center rounded-md px-2 text-xs font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
455
+ className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
370
456
  >
371
457
  Fields
372
458
  </button>
@@ -376,23 +462,25 @@ export function TwToolbar(props: TwToolbarProps) {
376
462
  disabled={!canEdit || !props.onUpdateTableOfContents}
377
463
  onMouseDown={preserveEditorSelectionMouseDown}
378
464
  onClick={props.onUpdateTableOfContents}
379
- className={`inline-flex h-7 items-center rounded-md px-2 text-xs font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
465
+ className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
380
466
  >
381
467
  TOC
382
468
  </button>
383
469
  </>
384
470
  ) : null}
385
- {layoutModel.showCompactOverflow ? (
471
+ {showCompactOverflow ? (
386
472
  <ToolbarCompactOverflow
387
473
  activeListContext={props.activeListContext}
388
474
  canEdit={canEdit}
389
475
  canInsertStructural={canInsertStructural}
390
476
  formattingState={props.formattingState}
391
477
  paragraphStyles={paragraphStyles}
392
- showInsertMenu={showInsertMenu}
393
- showListActions={showListActions}
394
- showStyleSelectors={showStyleSelectors}
395
- showUpdateActions={showUpdateActions}
478
+ showInsertMenu={getToolbarChromePlacement(scopedChromePolicy, "insert-actions") === "overflow"}
479
+ showListActions={getToolbarChromePlacement(scopedChromePolicy, "list-actions") === "overflow"}
480
+ showParagraphActions={getToolbarChromePlacement(scopedChromePolicy, "indentation") === "overflow"}
481
+ showListContinuation={getToolbarChromePlacement(scopedChromePolicy, "list-continuation") === "overflow"}
482
+ showStyleSelectors={getToolbarChromePlacement(scopedChromePolicy, "text-style-selectors") === "overflow"}
483
+ showUpdateActions={getToolbarChromePlacement(scopedChromePolicy, "update-actions") === "overflow"}
396
484
  onSetParagraphStyle={props.onSetParagraphStyle}
397
485
  onSetFontFamily={props.onSetFontFamily}
398
486
  onSetFontSize={props.onSetFontSize}
@@ -412,14 +500,15 @@ export function TwToolbar(props: TwToolbarProps) {
412
500
  ) : null}
413
501
 
414
502
  {/* Story focus breadcrumb — visible when editing a secondary story */}
415
- {props.activeStory && props.activeStory.kind !== "main" ? (
503
+ {props.activeStory && props.activeStory.kind !== "main" &&
504
+ isToolbarChromeItemVisible(scopedChromePolicy, "story-breadcrumb") ? (
416
505
  <>
417
506
  <div className="mx-1 h-4 w-px bg-border" />
418
507
  <button
419
508
  type="button"
420
509
  onClick={props.onCloseStory}
421
510
  onMouseDown={preserveEditorSelectionMouseDown}
422
- className={`inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-xs font-medium text-accent hover:bg-surface transition-colors outline-none ${focusRingClass}`}
511
+ className={`inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] font-medium text-accent hover:bg-surface transition-colors outline-none ${focusRingClass}`}
423
512
  aria-label={`Editing ${storyLabel(props.activeStory)} — click to return to main body`}
424
513
  >
425
514
  <span className="text-secondary">&larr;</span>
@@ -429,8 +518,43 @@ export function TwToolbar(props: TwToolbarProps) {
429
518
  ) : null}
430
519
  </div>
431
520
 
521
+ {/* R1: role-scoped inline action region (spec §6.4) */}
522
+ {props.role ? (
523
+ <TwRoleActionRegion
524
+ role={props.role}
525
+ policy={scopedChromePolicy}
526
+ compactMode={isCompact}
527
+ reviewQueue={props.reviewQueue}
528
+ workflowItem={props.workflowItem}
529
+ markupDisplay={props.markupDisplay}
530
+ onMarkScopePosture={props.onMarkScopePosture}
531
+ onReviewPrev={props.onReviewPrev}
532
+ onReviewNext={props.onReviewNext}
533
+ onReviewAccept={props.onReviewAccept}
534
+ onReviewReject={props.onReviewReject}
535
+ onReviewAcceptAll={props.onReviewAcceptAll}
536
+ onReviewRejectAll={props.onReviewRejectAll}
537
+ onReviewMarkupMode={props.onReviewMarkupMode}
538
+ onWorkflowPrev={props.onWorkflowPrev}
539
+ onWorkflowNext={props.onWorkflowNext}
540
+ onWorkflowMarkComplete={props.onWorkflowMarkComplete}
541
+ onWorkflowClaim={props.onWorkflowClaim}
542
+ onWorkflowSkip={props.onWorkflowSkip}
543
+ onWorkflowMarkBlocked={props.onWorkflowMarkBlocked}
544
+ onWorkflowJumpToScope={props.onWorkflowJumpToScope}
545
+ />
546
+ ) : null}
547
+
432
548
  {/* Right cluster: comment, track changes, markup, view, export */}
433
549
  <div className={`flex items-center gap-0.5 ${isCompact ? "ml-auto flex-wrap justify-end" : ""}`}>
550
+ {scopedChromePolicy.scopeStatusLabel ? (
551
+ <>
552
+ <span className="inline-flex h-6 items-center rounded-full bg-surface px-2 text-[10px] font-medium text-secondary ring-1 ring-border/50">
553
+ {scopedChromePolicy.scopeStatusLabel}
554
+ </span>
555
+ <div className="mx-1 h-4 w-px bg-border" />
556
+ </>
557
+ ) : null}
434
558
  {showSidebarToggle ? (
435
559
  <>
436
560
  <TwToolbarIconButton
@@ -443,13 +567,15 @@ export function TwToolbar(props: TwToolbarProps) {
443
567
  </>
444
568
  ) : null}
445
569
 
446
- <TwToolbarIconButton
447
- icon={MessageSquare}
448
- label="Add comment"
449
- disabled={!canAddComment}
450
- emphasis
451
- onClick={props.onAddComment}
452
- />
570
+ {isToolbarChromeItemVisible(scopedChromePolicy, "comment") ? (
571
+ <TwToolbarIconButton
572
+ icon={MessageSquare}
573
+ label="Add comment"
574
+ disabled={!canAddComment}
575
+ emphasis
576
+ onClick={props.onAddComment}
577
+ />
578
+ ) : null}
453
579
 
454
580
  {showTrackedChangesToggle ? (
455
581
  <>
@@ -460,9 +586,9 @@ export function TwToolbar(props: TwToolbarProps) {
460
586
  onPressedChange={props.onShowTrackedChangesChange}
461
587
  disabled={caps ? !caps.trackChangesSupported : false}
462
588
  onMouseDown={preserveEditorSelectionMouseDown}
463
- className={`inline-flex h-7 w-7 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}`}
589
+ 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}`}
464
590
  >
465
- {props.showTrackedChanges ? <Eye className="h-4 w-4" /> : <EyeOff className="h-4 w-4" />}
591
+ {props.showTrackedChanges ? <Eye className="h-3.5 w-3.5" /> : <EyeOff className="h-3.5 w-3.5" />}
466
592
  </Toggle.Root>
467
593
  </Tooltip.Trigger>
468
594
  <Tooltip.Portal>
@@ -480,21 +606,22 @@ export function TwToolbar(props: TwToolbarProps) {
480
606
  ) : null}
481
607
 
482
608
  {/* View mode toggle group: Canvas (clean, flowing) / Page (layout-sensitive) */}
483
- <ToggleGroup.Root
484
- type="single"
485
- value={workspaceMode}
486
- onValueChange={(v: string) => {
487
- if (v) props.onWorkspaceModeChange(v as WorkspaceMode);
488
- }}
489
- className="flex items-center gap-0.5"
490
- >
609
+ {isToolbarChromeItemVisible(scopedChromePolicy, "workspace-mode") ? (
610
+ <ToggleGroup.Root
611
+ type="single"
612
+ value={workspaceMode}
613
+ onValueChange={(v: string) => {
614
+ if (v) props.onWorkspaceModeChange(v as WorkspaceMode);
615
+ }}
616
+ className="flex items-center gap-0.5"
617
+ >
491
618
  <Tooltip.Root>
492
619
  <Tooltip.Trigger asChild>
493
620
  <ToggleGroup.Item
494
621
  value="canvas"
495
622
  aria-label="Canvas workspace"
496
623
  onMouseDown={preserveEditorSelectionMouseDown}
497
- className={`inline-flex h-7 w-7 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 ${focusRingClass}`}
624
+ 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 ${focusRingClass}`}
498
625
  >
499
626
  <Monitor className="h-3.5 w-3.5" />
500
627
  </ToggleGroup.Item>
@@ -511,7 +638,7 @@ export function TwToolbar(props: TwToolbarProps) {
511
638
  value="page"
512
639
  aria-label="Page workspace"
513
640
  onMouseDown={preserveEditorSelectionMouseDown}
514
- className={`inline-flex h-7 w-7 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 ${focusRingClass}`}
641
+ 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 ${focusRingClass}`}
515
642
  >
516
643
  <FileText className="h-3.5 w-3.5" />
517
644
  </ToggleGroup.Item>
@@ -522,10 +649,11 @@ export function TwToolbar(props: TwToolbarProps) {
522
649
  </Tooltip.Content>
523
650
  </Tooltip.Portal>
524
651
  </Tooltip.Root>
525
- </ToggleGroup.Root>
652
+ </ToggleGroup.Root>
653
+ ) : null}
526
654
 
527
655
  {/* Zoom controls — available in all workspace modes */}
528
- {props.onZoomChange ? (
656
+ {isToolbarChromeItemVisible(scopedChromePolicy, "zoom") && props.onZoomChange ? (
529
657
  <>
530
658
  <div className="mx-1 h-4 w-px bg-border" />
531
659
  <div className="flex items-center gap-0.5">
@@ -534,7 +662,7 @@ export function TwToolbar(props: TwToolbarProps) {
534
662
  <button
535
663
  type="button"
536
664
  aria-label="Zoom out"
537
- className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
665
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
538
666
  disabled={typeof zoomLevel === "number" && zoomLevel <= 50}
539
667
  onMouseDown={preserveEditorSelectionMouseDown}
540
668
  onClick={() => {
@@ -560,7 +688,7 @@ export function TwToolbar(props: TwToolbarProps) {
560
688
  type="button"
561
689
  aria-label={`Zoom: ${zoomLabel}`}
562
690
  onMouseDown={preserveEditorSelectionMouseDown}
563
- className={`inline-flex h-7 items-center justify-center rounded-md px-1.5 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
691
+ className={`inline-flex h-6 items-center justify-center rounded-md px-1.5 text-[10px] font-medium text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
564
692
  >
565
693
  {zoomLabel}
566
694
  </button>
@@ -604,7 +732,7 @@ export function TwToolbar(props: TwToolbarProps) {
604
732
  <button
605
733
  type="button"
606
734
  aria-label="Zoom in"
607
- className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
735
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
608
736
  disabled={typeof zoomLevel === "number" && zoomLevel >= 200}
609
737
  onMouseDown={preserveEditorSelectionMouseDown}
610
738
  onClick={() => {
@@ -635,9 +763,9 @@ export function TwToolbar(props: TwToolbarProps) {
635
763
  type="button"
636
764
  aria-label="Document health"
637
765
  onMouseDown={preserveEditorSelectionMouseDown}
638
- className={`relative inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface hover:text-primary outline-none ${focusRingClass}`}
766
+ className={`relative inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface hover:text-primary outline-none ${focusRingClass}`}
639
767
  >
640
- <AlertCircle className="h-4 w-4" />
768
+ <AlertCircle className="h-3.5 w-3.5" />
641
769
  {(caps?.healthIssueCount ?? 0) > 0 ? (
642
770
  <span className="absolute -top-0.5 -right-0.5 flex h-3 min-w-[12px] items-center justify-center rounded-full bg-tertiary text-[8px] font-medium text-white">
643
771
  {caps?.healthIssueCount}
@@ -672,13 +800,15 @@ export function TwToolbar(props: TwToolbarProps) {
672
800
  </>
673
801
  ) : null}
674
802
 
675
- <TwToolbarIconButton
676
- icon={Download}
677
- label={caps?.exportBlocked ? "Export blocked" : "Download document"}
678
- disabled={caps ? !caps.canExport : true}
679
- emphasis
680
- onClick={props.onExport}
681
- />
803
+ {isToolbarChromeItemVisible(scopedChromePolicy, "export") ? (
804
+ <TwToolbarIconButton
805
+ icon={Download}
806
+ label={caps?.exportBlocked ? "Export blocked" : "Download document"}
807
+ disabled={caps ? !caps.canExport : true}
808
+ emphasis
809
+ onClick={props.onExport}
810
+ />
811
+ ) : null}
682
812
  </div>
683
813
  </header>
684
814
  );
@@ -706,7 +836,7 @@ function ToolbarParagraphStyleSelect(props: {
706
836
  aria-disabled={props.disabled || undefined}
707
837
  data-disabled={props.disabled ? "" : undefined}
708
838
  onMouseDown={preserveEditorSelectionMouseDown}
709
- className={`inline-flex h-7 min-w-[8.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2.5 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
839
+ className={`inline-flex h-6 min-w-[7.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
710
840
  >
711
841
  <Select.Value placeholder="Style" />
712
842
  <Select.Icon>
@@ -723,7 +853,7 @@ function ToolbarParagraphStyleSelect(props: {
723
853
  <Select.Viewport className="p-1">
724
854
  {props.styles.map((style) => (
725
855
  <Select.Item
726
- className={`flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-canvas data-[state=checked]:text-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent/20 ${focusRingClass}`}
856
+ className={`flex cursor-pointer items-center rounded-md px-2 py-1 text-[11px] text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-canvas data-[state=checked]:text-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent/20 ${focusRingClass}`}
727
857
  key={style.styleId}
728
858
  value={style.styleId}
729
859
  >
@@ -755,7 +885,7 @@ function ToolbarFontFamilySelect(props: {
755
885
  aria-disabled={props.disabled || undefined}
756
886
  data-disabled={props.disabled ? "" : undefined}
757
887
  onMouseDown={preserveEditorSelectionMouseDown}
758
- className={`inline-flex h-7 min-w-[7rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
888
+ className={`inline-flex h-6 min-w-[6.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
759
889
  >
760
890
  <Select.Value placeholder="Font" />
761
891
  <Select.Icon>
@@ -772,7 +902,7 @@ function ToolbarFontFamilySelect(props: {
772
902
  <Select.Viewport className="p-1">
773
903
  {FONT_FAMILIES.map((font) => (
774
904
  <Select.Item
775
- className={`flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-canvas data-[state=checked]:text-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent/20 ${focusRingClass}`}
905
+ className={`flex cursor-pointer items-center rounded-md px-2 py-1 text-[11px] text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-canvas data-[state=checked]:text-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent/20 ${focusRingClass}`}
776
906
  key={font}
777
907
  value={font}
778
908
  >
@@ -805,7 +935,7 @@ function ToolbarFontSizeSelect(props: {
805
935
  aria-disabled={props.disabled || undefined}
806
936
  data-disabled={props.disabled ? "" : undefined}
807
937
  onMouseDown={preserveEditorSelectionMouseDown}
808
- className={`inline-flex h-7 min-w-[4rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
938
+ className={`inline-flex h-6 min-w-[3.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
809
939
  >
810
940
  <Select.Value placeholder="Size" />
811
941
  <Select.Icon>
@@ -822,7 +952,7 @@ function ToolbarFontSizeSelect(props: {
822
952
  <Select.Viewport className="p-1">
823
953
  {FONT_SIZES.map((size) => (
824
954
  <Select.Item
825
- className={`flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-canvas data-[state=checked]:text-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent/20 ${focusRingClass}`}
955
+ className={`flex cursor-pointer items-center rounded-md px-2 py-1 text-[11px] text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-canvas data-[state=checked]:text-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent/20 ${focusRingClass}`}
826
956
  key={size}
827
957
  value={String(size)}
828
958
  >
@@ -844,6 +974,8 @@ function ToolbarCompactOverflow(props: {
844
974
  paragraphStyles: StyleCatalogSnapshot["paragraphs"];
845
975
  showInsertMenu: boolean;
846
976
  showListActions: boolean;
977
+ showParagraphActions: boolean;
978
+ showListContinuation: boolean;
847
979
  showStyleSelectors: boolean;
848
980
  showUpdateActions: boolean;
849
981
  onSetParagraphStyle?: (styleId: string) => void;
@@ -891,7 +1023,7 @@ function ToolbarCompactOverflow(props: {
891
1023
  aria-expanded={open}
892
1024
  onMouseDown={preserveEditorSelectionMouseDown}
893
1025
  onClick={() => setOpen((value) => !value)}
894
- className={`inline-flex h-7 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
1026
+ className={`inline-flex h-6 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
895
1027
  >
896
1028
  More
897
1029
  <ChevronDown className="h-3.5 w-3.5 text-tertiary" />
@@ -971,31 +1103,36 @@ function ToolbarCompactOverflow(props: {
971
1103
  </div>
972
1104
  ) : null}
973
1105
 
974
- <div className="space-y-1">
975
- <div className="px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
976
- Paragraph
977
- </div>
978
- <ToolbarMenuButton
979
- ariaLabel="Outdent"
980
- disabled={!props.canEdit || !props.onOutdent}
981
- icon={<Outdent className="h-3.5 w-3.5" />}
982
- label="Outdent"
983
- onClick={() => {
984
- props.onOutdent?.();
985
- setOpen(false);
986
- }}
987
- />
988
- <ToolbarMenuButton
989
- ariaLabel="Indent"
990
- disabled={!props.canEdit || !props.onIndent}
991
- icon={<Indent className="h-3.5 w-3.5" />}
992
- label="Indent"
993
- onClick={() => {
994
- props.onIndent?.();
995
- setOpen(false);
996
- }}
997
- />
998
- {props.activeListContext ? (
1106
+ {props.showParagraphActions || (props.showListContinuation && props.activeListContext) ? (
1107
+ <div className="space-y-1">
1108
+ <div className="px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
1109
+ Paragraph
1110
+ </div>
1111
+ {props.showParagraphActions ? (
1112
+ <>
1113
+ <ToolbarMenuButton
1114
+ ariaLabel="Outdent"
1115
+ disabled={!props.canEdit || !props.onOutdent}
1116
+ icon={<Outdent className="h-3.5 w-3.5" />}
1117
+ label="Outdent"
1118
+ onClick={() => {
1119
+ props.onOutdent?.();
1120
+ setOpen(false);
1121
+ }}
1122
+ />
1123
+ <ToolbarMenuButton
1124
+ ariaLabel="Indent"
1125
+ disabled={!props.canEdit || !props.onIndent}
1126
+ icon={<Indent className="h-3.5 w-3.5" />}
1127
+ label="Indent"
1128
+ onClick={() => {
1129
+ props.onIndent?.();
1130
+ setOpen(false);
1131
+ }}
1132
+ />
1133
+ </>
1134
+ ) : null}
1135
+ {props.showListContinuation && props.activeListContext ? (
999
1136
  <>
1000
1137
  <ToolbarMenuButton
1001
1138
  ariaLabel="Restart numbering"
@@ -1018,8 +1155,9 @@ function ToolbarCompactOverflow(props: {
1018
1155
  }}
1019
1156
  />
1020
1157
  </>
1021
- ) : null}
1022
- </div>
1158
+ ) : null}
1159
+ </div>
1160
+ ) : null}
1023
1161
 
1024
1162
  {props.showInsertMenu ? (
1025
1163
  <div className="space-y-1">
@@ -1047,7 +1185,7 @@ function ToolbarCompactOverflow(props: {
1047
1185
  }}
1048
1186
  />
1049
1187
  <label
1050
- className={`flex h-8 cursor-pointer items-center gap-2 rounded-md px-2 text-left text-xs font-medium text-primary transition-colors hover:bg-surface ${
1188
+ className={`flex h-7 cursor-pointer items-center gap-2 rounded-md px-2 text-left text-[11px] font-medium text-primary transition-colors hover:bg-surface ${
1051
1189
  !props.canInsertStructural || !props.onInsertImage ? "pointer-events-none opacity-40" : ""
1052
1190
  }`}
1053
1191
  >
@@ -1131,7 +1269,7 @@ function ToolbarFormattingOverflow(props: {
1131
1269
  disabled={props.disabled}
1132
1270
  onMouseDown={preserveEditorSelectionMouseDown}
1133
1271
  onClick={() => setOpen((value) => !value)}
1134
- className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1272
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1135
1273
  >
1136
1274
  <MoreHorizontal className="h-3.5 w-3.5" />
1137
1275
  </button>
@@ -1206,7 +1344,7 @@ function ToolbarColorPopover(props: {
1206
1344
  disabled={props.disabled}
1207
1345
  onMouseDown={preserveEditorSelectionMouseDown}
1208
1346
  onClick={() => setOpen((value) => !value)}
1209
- className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1347
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1210
1348
  >
1211
1349
  {props.icon}
1212
1350
  </button>
@@ -1234,7 +1372,7 @@ function ToolbarColorPopover(props: {
1234
1372
  props.onSelect(color.value);
1235
1373
  setOpen(false);
1236
1374
  }}
1237
- className={`inline-flex h-8 items-center justify-center rounded-md border border-border text-[10px] font-medium text-primary transition-transform hover:scale-[1.04] disabled:cursor-not-allowed disabled:opacity-40 ${
1375
+ className={`inline-flex h-7 items-center justify-center rounded-md border border-border text-[10px] font-medium text-primary transition-transform hover:scale-[1.04] disabled:cursor-not-allowed disabled:opacity-40 ${
1238
1376
  color.value ? "" : "bg-surface"
1239
1377
  } ${focusRingClass}`}
1240
1378
  style={color.value ? { backgroundColor: color.value } : undefined}
@@ -1273,7 +1411,7 @@ function ToolbarAlignmentPopover(props: {
1273
1411
  disabled={props.disabled}
1274
1412
  onMouseDown={preserveEditorSelectionMouseDown}
1275
1413
  onClick={() => setOpen((value) => !value)}
1276
- className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1414
+ className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1277
1415
  >
1278
1416
  {(alignments.find((entry) => entry.value === props.activeAlignment) ?? alignments[0])?.icon}
1279
1417
  </button>
@@ -1346,7 +1484,7 @@ function ToolbarInsertMenu(props: {
1346
1484
  disabled={props.disabled}
1347
1485
  onMouseDown={preserveEditorSelectionMouseDown}
1348
1486
  onClick={() => setOpen((value) => !value)}
1349
- className={`inline-flex h-7 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1487
+ className={`inline-flex h-6 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1350
1488
  >
1351
1489
  Insert
1352
1490
  <ChevronDown className="h-3.5 w-3.5 text-tertiary" />
@@ -1382,7 +1520,7 @@ function ToolbarInsertMenu(props: {
1382
1520
  }}
1383
1521
  />
1384
1522
  <label
1385
- className={`flex h-8 cursor-pointer items-center gap-2 rounded-md px-2 text-xs font-medium text-primary transition-colors hover:bg-surface ${
1523
+ className={`flex h-7 cursor-pointer items-center gap-2 rounded-md px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface ${
1386
1524
  props.disabled || !props.onInsertImage ? "pointer-events-none opacity-40" : ""
1387
1525
  }`}
1388
1526
  >
@@ -1431,7 +1569,7 @@ function ToolbarPopoverActionButton(props: {
1431
1569
  disabled={props.disabled}
1432
1570
  onMouseDown={preserveEditorSelectionMouseDown}
1433
1571
  onClick={props.onClick}
1434
- className={`inline-flex h-8 items-center justify-center rounded-md border border-border transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
1572
+ className={`inline-flex h-7 items-center justify-center rounded-md border border-border transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
1435
1573
  props.active ? "bg-canvas text-accent ring-1 ring-accent/30 shadow-sm" : "bg-canvas text-secondary hover:bg-surface"
1436
1574
  } ${focusRingClass}`}
1437
1575
  >
@@ -1454,7 +1592,7 @@ function ToolbarMenuButton(props: {
1454
1592
  disabled={props.disabled}
1455
1593
  onMouseDown={preserveEditorSelectionMouseDown}
1456
1594
  onClick={props.onClick}
1457
- className={`flex h-8 w-full items-center gap-2 rounded-md px-2 text-left text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1595
+ className={`flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-[11px] font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
1458
1596
  >
1459
1597
  <span className="text-secondary">{props.icon}</span>
1460
1598
  <span>{props.label}</span>