@beyondwork/docx-react-component 1.0.28 → 1.0.30
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.
- package/package.json +26 -37
- package/src/api/public-types.ts +531 -0
- package/src/api/session-state.ts +2 -0
- package/src/core/commands/index.ts +201 -79
- package/src/core/commands/table-structure-commands.ts +138 -5
- package/src/core/state/text-transaction.ts +370 -3
- package/src/index.ts +41 -0
- package/src/io/docx-session.ts +318 -25
- package/src/io/export/serialize-footnotes.ts +41 -46
- package/src/io/export/serialize-headers-footers.ts +36 -40
- package/src/io/export/serialize-main-document.ts +55 -89
- package/src/io/export/serialize-numbering.ts +104 -4
- package/src/io/export/serialize-runtime-revisions.ts +196 -2
- package/src/io/export/split-story-blocks-for-runtime-revisions.ts +252 -0
- package/src/io/export/table-properties-xml.ts +318 -0
- package/src/io/normalize/normalize-text.ts +34 -3
- package/src/io/ooxml/parse-comments.ts +6 -0
- package/src/io/ooxml/parse-footnotes.ts +69 -13
- package/src/io/ooxml/parse-headers-footers.ts +54 -11
- package/src/io/ooxml/parse-main-document.ts +112 -42
- package/src/io/ooxml/parse-numbering.ts +341 -26
- package/src/io/ooxml/parse-revisions.ts +118 -4
- package/src/io/ooxml/parse-styles.ts +176 -0
- package/src/io/ooxml/parse-tables.ts +34 -25
- package/src/io/ooxml/revision-boundaries.ts +127 -3
- package/src/io/ooxml/workflow-payload.ts +544 -0
- package/src/model/canonical-document.ts +91 -1
- package/src/model/snapshot.ts +112 -1
- package/src/preservation/store.ts +73 -3
- package/src/review/store/comment-store.ts +19 -1
- package/src/review/store/revision-actions.ts +29 -0
- package/src/review/store/revision-store.ts +12 -1
- package/src/review/store/revision-types.ts +11 -0
- package/src/runtime/context-analytics.ts +824 -0
- package/src/runtime/document-locations.ts +521 -0
- package/src/runtime/document-navigation.ts +14 -1
- package/src/runtime/document-outline.ts +440 -0
- package/src/runtime/document-runtime.ts +941 -45
- package/src/runtime/event-refresh-hints.ts +137 -0
- package/src/runtime/numbering-prefix.ts +67 -39
- package/src/runtime/page-layout-estimation.ts +100 -7
- package/src/runtime/resolved-numbering-geometry.ts +293 -0
- package/src/runtime/session-capabilities.ts +2 -2
- package/src/runtime/suggestions-snapshot.ts +137 -0
- package/src/runtime/surface-projection.ts +223 -27
- package/src/runtime/table-style-resolver.ts +409 -0
- package/src/runtime/view-state.ts +17 -1
- package/src/runtime/workflow-markup.ts +54 -14
- package/src/ui/WordReviewEditor.tsx +1269 -87
- package/src/ui/editor-command-bag.ts +7 -0
- package/src/ui/editor-runtime-boundary.ts +111 -10
- package/src/ui/editor-shell-view.tsx +17 -15
- package/src/ui/editor-surface-controller.tsx +5 -0
- package/src/ui/headless/selection-tool-context.ts +19 -0
- package/src/ui/headless/selection-tool-resolver.ts +752 -0
- package/src/ui/headless/selection-tool-types.ts +129 -0
- package/src/ui/headless/selection-toolbar-model.ts +10 -33
- package/src/ui/runtime-shortcut-dispatch.ts +365 -0
- package/src/ui-tailwind/chrome/chrome-preset-model.ts +107 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +15 -0
- package/src/ui-tailwind/chrome/review-queue-bar.tsx +97 -0
- package/src/ui-tailwind/chrome/tw-context-analytics-summary.tsx +122 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +1 -9
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +1 -5
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +8 -29
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +23 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +35 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +37 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +298 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +116 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-suggestion.tsx +29 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +27 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +3 -3
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +3 -3
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +86 -14
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +57 -52
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +36 -52
- package/src/ui-tailwind/editor-surface/pm-schema.ts +56 -5
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +87 -24
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +4 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +135 -32
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +74 -7
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +17 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +19 -17
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +10 -10
- package/src/ui-tailwind/status/tw-status-bar.tsx +10 -6
- package/src/ui-tailwind/theme/editor-theme.css +58 -40
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +250 -181
- package/src/ui-tailwind/tw-review-workspace.tsx +323 -280
- package/src/validation/compatibility-engine.ts +246 -2
- package/src/validation/docx-comment-proof.ts +24 -11
|
@@ -26,13 +26,13 @@ export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
|
26
26
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
27
27
|
onClick={props.onClick}
|
|
28
28
|
className={[
|
|
29
|
-
"inline-flex h-7 w-7 items-center justify-center rounded-
|
|
29
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-lg border border-transparent transition-colors outline-none",
|
|
30
30
|
"disabled:opacity-30 disabled:cursor-not-allowed",
|
|
31
31
|
props.emphasis
|
|
32
|
-
? "text-accent hover:bg-
|
|
32
|
+
? "text-accent hover:border-border/60 hover:bg-surface"
|
|
33
33
|
: props.active
|
|
34
|
-
? "bg-
|
|
35
|
-
: "text-secondary hover:bg-surface hover:text-primary",
|
|
34
|
+
? "border-border/70 bg-surface text-accent shadow-[0_4px_12px_-10px_var(--color-shadow-strong)]"
|
|
35
|
+
: "text-secondary hover:border-border/60 hover:bg-surface hover:text-primary",
|
|
36
36
|
focusRingClass,
|
|
37
37
|
].join(" ")}
|
|
38
38
|
>
|
|
@@ -6,6 +6,7 @@ import * as Toggle from "@radix-ui/react-toggle";
|
|
|
6
6
|
import * as ToggleGroup from "@radix-ui/react-toggle-group";
|
|
7
7
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
|
8
8
|
import {
|
|
9
|
+
AlertCircle,
|
|
9
10
|
AlignCenter,
|
|
10
11
|
AlignJustify,
|
|
11
12
|
AlignLeft,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
ImagePlus,
|
|
22
23
|
Indent,
|
|
23
24
|
Italic,
|
|
25
|
+
List,
|
|
24
26
|
MessageSquare,
|
|
25
27
|
Minus,
|
|
26
28
|
Monitor,
|
|
@@ -32,13 +34,12 @@ import {
|
|
|
32
34
|
Strikethrough,
|
|
33
35
|
Subscript,
|
|
34
36
|
Superscript,
|
|
35
|
-
ShieldAlert,
|
|
36
|
-
ShieldCheck,
|
|
37
37
|
Underline,
|
|
38
38
|
Undo2,
|
|
39
39
|
} from "lucide-react";
|
|
40
40
|
|
|
41
41
|
import type {
|
|
42
|
+
ActiveListContext,
|
|
42
43
|
CompatibilityPanelSnapshot,
|
|
43
44
|
EditorStoryTarget,
|
|
44
45
|
EditorWarning,
|
|
@@ -48,6 +49,7 @@ import type {
|
|
|
48
49
|
SectionBreakType,
|
|
49
50
|
StyleCatalogSnapshot,
|
|
50
51
|
WorkflowBlockedCommandReason,
|
|
52
|
+
WordReviewEditorChromePreset,
|
|
51
53
|
WorkspaceMode,
|
|
52
54
|
ZoomLevel,
|
|
53
55
|
} from "../../api/public-types";
|
|
@@ -57,15 +59,17 @@ import { TwHealthPanel } from "../review/tw-health-panel";
|
|
|
57
59
|
import { TwToolbarIconButton } from "./tw-toolbar-icon-button";
|
|
58
60
|
|
|
59
61
|
export interface TwToolbarProps {
|
|
60
|
-
sourceLabel?: string;
|
|
61
62
|
capabilities?: SessionCapabilities;
|
|
62
63
|
compatibility?: CompatibilityPanelSnapshot;
|
|
63
64
|
warnings?: EditorWarning[];
|
|
64
65
|
blockedReasons?: WorkflowBlockedCommandReason[];
|
|
66
|
+
showDiagnosticsChrome?: boolean;
|
|
65
67
|
interactionPolicy?: ToolbarInteractionPolicy;
|
|
68
|
+
preset?: WordReviewEditorChromePreset;
|
|
66
69
|
workspaceMode: WorkspaceMode;
|
|
67
70
|
zoomLevel?: ZoomLevel;
|
|
68
71
|
formattingState?: FormattingStateSnapshot;
|
|
72
|
+
activeListContext?: ActiveListContext | null;
|
|
69
73
|
styleCatalog?: StyleCatalogSnapshot;
|
|
70
74
|
/** Display toggle for tracked change decorations (not a runtime mutation toggle). */
|
|
71
75
|
showTrackedChanges: boolean;
|
|
@@ -87,6 +91,8 @@ export interface TwToolbarProps {
|
|
|
87
91
|
onSetTextColor?: (color: string) => void;
|
|
88
92
|
onSetHighlightColor?: (color: string | null) => void;
|
|
89
93
|
onSetAlignment?: (alignment: FormattingAlignment) => void;
|
|
94
|
+
onToggleBulletedList?: () => void;
|
|
95
|
+
onToggleNumberedList?: () => void;
|
|
90
96
|
onOutdent?: () => void;
|
|
91
97
|
onIndent?: () => void;
|
|
92
98
|
onAddComment: () => void;
|
|
@@ -98,6 +104,10 @@ export interface TwToolbarProps {
|
|
|
98
104
|
onWorkspaceModeChange: (value: WorkspaceMode) => void;
|
|
99
105
|
onZoomChange?: (level: ZoomLevel) => void;
|
|
100
106
|
onShowTrackedChangesChange: (show: boolean) => void;
|
|
107
|
+
onRestartNumbering?: () => void;
|
|
108
|
+
onContinueNumbering?: () => void;
|
|
109
|
+
onUpdateFields?: () => void;
|
|
110
|
+
onUpdateTableOfContents?: () => void;
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
export interface ToolbarInteractionPolicy {
|
|
@@ -116,24 +126,33 @@ const focusRingClass =
|
|
|
116
126
|
|
|
117
127
|
const FONT_FAMILIES = ["Arial", "Times New Roman", "Calibri", "Cambria", "Georgia", "Verdana"];
|
|
118
128
|
const FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36];
|
|
119
|
-
const TEXT_COLORS = ["#
|
|
129
|
+
const TEXT_COLORS = ["#1f1f1f", "#5c5852", "#1660a8", "#50684d", "#9b4f49", "#7b5f32"];
|
|
120
130
|
const HIGHLIGHT_COLORS = [
|
|
121
131
|
{ value: "#ffff00", label: "Yellow" },
|
|
122
|
-
{ value: "#
|
|
123
|
-
{ value: "#
|
|
124
|
-
{ value: "#
|
|
132
|
+
{ value: "#d9e7f7", label: "Blue" },
|
|
133
|
+
{ value: "#f1dbd6", label: "Rose" },
|
|
134
|
+
{ value: "#e6e0d4", label: "Stone" },
|
|
125
135
|
{ value: null, label: "None" },
|
|
126
136
|
] as const;
|
|
127
137
|
|
|
128
138
|
export function TwToolbar(props: TwToolbarProps) {
|
|
129
139
|
const caps = props.capabilities;
|
|
140
|
+
const preset = props.preset ?? "advanced";
|
|
130
141
|
const workspaceMode = props.workspaceMode;
|
|
131
|
-
const isPageMode = workspaceMode === "page";
|
|
132
142
|
const paragraphStyles = props.styleCatalog?.paragraphs ?? [];
|
|
133
143
|
const zoomLevel = props.zoomLevel ?? 100;
|
|
134
144
|
const canEdit = props.interactionPolicy?.canFormatText ?? (caps ? caps.canEdit : false);
|
|
135
145
|
const canInsertStructural = props.interactionPolicy?.canInsertStructural ?? canEdit;
|
|
136
146
|
const canAddComment = props.interactionPolicy?.canAddComment ?? (caps ? caps.canAddComment : false);
|
|
147
|
+
const showStyleSelectors = preset === "advanced";
|
|
148
|
+
const showAdvancedFormatting = preset === "advanced";
|
|
149
|
+
const showFormattingColors = preset !== "review";
|
|
150
|
+
const showInsertMenu = preset === "simple" || preset === "advanced";
|
|
151
|
+
const showTrackedChangesToggle = preset !== "simple";
|
|
152
|
+
const showDiagnosticsChrome = props.showDiagnosticsChrome ?? true;
|
|
153
|
+
const showHealth = showDiagnosticsChrome && Boolean(props.compatibility && props.warnings);
|
|
154
|
+
const showListActions = preset === "simple" || preset === "advanced";
|
|
155
|
+
const showUpdateActions = preset === "advanced";
|
|
137
156
|
const zoomLabel =
|
|
138
157
|
typeof zoomLevel === "number"
|
|
139
158
|
? `${zoomLevel}%`
|
|
@@ -142,9 +161,9 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
142
161
|
: "Fit page";
|
|
143
162
|
|
|
144
163
|
return (
|
|
145
|
-
<header className="flex h-
|
|
164
|
+
<header className="flex h-11 shrink-0 items-center gap-1 rounded-xl border border-border/70 bg-canvas/92 px-2.5 shadow-[0_8px_20px_-18px_var(--color-shadow-strong)] backdrop-blur-sm">
|
|
146
165
|
{/* Left cluster: undo/redo + formatting */}
|
|
147
|
-
<div className="flex items-center gap-0.5">
|
|
166
|
+
<div className="flex min-w-0 flex-1 items-center gap-0.5">
|
|
148
167
|
<TwToolbarIconButton
|
|
149
168
|
icon={Undo2}
|
|
150
169
|
label="Undo"
|
|
@@ -159,25 +178,29 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
159
178
|
/>
|
|
160
179
|
<div className="mx-1 h-4 w-px bg-border" />
|
|
161
180
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
181
|
+
{showStyleSelectors ? (
|
|
182
|
+
<>
|
|
183
|
+
<ToolbarParagraphStyleSelect
|
|
184
|
+
disabled={!canEdit || paragraphStyles.length === 0 || !props.onSetParagraphStyle}
|
|
185
|
+
styles={paragraphStyles}
|
|
186
|
+
value={props.formattingState?.paragraphStyleId}
|
|
187
|
+
onValueChange={props.onSetParagraphStyle}
|
|
188
|
+
/>
|
|
168
189
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
190
|
+
<ToolbarFontFamilySelect
|
|
191
|
+
disabled={!canEdit || !props.onSetFontFamily}
|
|
192
|
+
value={props.formattingState?.fontFamily}
|
|
193
|
+
onValueChange={props.onSetFontFamily}
|
|
194
|
+
/>
|
|
195
|
+
<ToolbarFontSizeSelect
|
|
196
|
+
disabled={!canEdit || !props.onSetFontSize}
|
|
197
|
+
value={props.formattingState?.fontSize}
|
|
198
|
+
onValueChange={props.onSetFontSize}
|
|
199
|
+
/>
|
|
179
200
|
|
|
180
|
-
|
|
201
|
+
<div className="mx-1 h-4 w-px bg-border" />
|
|
202
|
+
</>
|
|
203
|
+
) : null}
|
|
181
204
|
|
|
182
205
|
<TwToolbarIconButton
|
|
183
206
|
icon={Bold}
|
|
@@ -200,41 +223,66 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
200
223
|
disabled={!canEdit}
|
|
201
224
|
onClick={props.onToggleUnderline}
|
|
202
225
|
/>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
props.onSetTextColor
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
226
|
+
{showAdvancedFormatting ? (
|
|
227
|
+
<ToolbarFormattingOverflow
|
|
228
|
+
disabled={!canEdit}
|
|
229
|
+
formattingState={props.formattingState}
|
|
230
|
+
onToggleStrikethrough={props.onToggleStrikethrough}
|
|
231
|
+
onToggleSuperscript={props.onToggleSuperscript}
|
|
232
|
+
onToggleSubscript={props.onToggleSubscript}
|
|
233
|
+
/>
|
|
234
|
+
) : null}
|
|
235
|
+
{showFormattingColors ? (
|
|
236
|
+
<>
|
|
237
|
+
<ToolbarColorPopover
|
|
238
|
+
ariaLabel="Text color"
|
|
239
|
+
colors={TEXT_COLORS.map((value) => ({ value, label: value }))}
|
|
240
|
+
disabled={!canEdit || !props.onSetTextColor}
|
|
241
|
+
icon={<Baseline className="h-3.5 w-3.5" />}
|
|
242
|
+
onSelect={(value) => {
|
|
243
|
+
if (value) {
|
|
244
|
+
props.onSetTextColor?.(value);
|
|
245
|
+
}
|
|
246
|
+
}}
|
|
247
|
+
title="Text color"
|
|
248
|
+
/>
|
|
249
|
+
<ToolbarColorPopover
|
|
250
|
+
ariaLabel="Highlight color"
|
|
251
|
+
colors={HIGHLIGHT_COLORS.map((entry) => ({ value: entry.value, label: entry.label }))}
|
|
252
|
+
disabled={!canEdit || !props.onSetHighlightColor}
|
|
253
|
+
icon={<Highlighter className="h-3.5 w-3.5" />}
|
|
254
|
+
onSelect={(value) => props.onSetHighlightColor?.(value)}
|
|
255
|
+
title="Highlight color"
|
|
256
|
+
/>
|
|
257
|
+
<ToolbarAlignmentPopover
|
|
258
|
+
activeAlignment={props.formattingState?.alignment}
|
|
259
|
+
disabled={!canEdit || !props.onSetAlignment}
|
|
260
|
+
onSelect={(alignment) => props.onSetAlignment?.(alignment)}
|
|
261
|
+
/>
|
|
262
|
+
</>
|
|
263
|
+
) : null}
|
|
235
264
|
|
|
236
265
|
<div className="mx-1 h-4 w-px bg-border" />
|
|
237
266
|
|
|
267
|
+
{showListActions ? (
|
|
268
|
+
<>
|
|
269
|
+
<TwToolbarIconButton
|
|
270
|
+
icon={List}
|
|
271
|
+
label="Bulleted list"
|
|
272
|
+
active={Boolean(props.activeListContext && !props.activeListContext.isOrdered)}
|
|
273
|
+
disabled={!canEdit}
|
|
274
|
+
onClick={props.onToggleBulletedList}
|
|
275
|
+
/>
|
|
276
|
+
<TwToolbarIconButton
|
|
277
|
+
icon={Rows3}
|
|
278
|
+
label="Numbered list"
|
|
279
|
+
active={Boolean(props.activeListContext?.isOrdered)}
|
|
280
|
+
disabled={!canEdit}
|
|
281
|
+
onClick={props.onToggleNumberedList}
|
|
282
|
+
/>
|
|
283
|
+
</>
|
|
284
|
+
) : null}
|
|
285
|
+
|
|
238
286
|
<TwToolbarIconButton
|
|
239
287
|
icon={Outdent}
|
|
240
288
|
label="Outdent"
|
|
@@ -247,13 +295,63 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
247
295
|
disabled={!canEdit}
|
|
248
296
|
onClick={props.onIndent}
|
|
249
297
|
/>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
298
|
+
{showListActions && props.activeListContext ? (
|
|
299
|
+
<>
|
|
300
|
+
<button
|
|
301
|
+
type="button"
|
|
302
|
+
aria-label="Restart numbering"
|
|
303
|
+
disabled={!canEdit || !props.onRestartNumbering}
|
|
304
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
305
|
+
onClick={props.onRestartNumbering}
|
|
306
|
+
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}`}
|
|
307
|
+
>
|
|
308
|
+
Restart
|
|
309
|
+
</button>
|
|
310
|
+
<button
|
|
311
|
+
type="button"
|
|
312
|
+
aria-label="Continue numbering"
|
|
313
|
+
disabled={!canEdit || !props.onContinueNumbering}
|
|
314
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
315
|
+
onClick={props.onContinueNumbering}
|
|
316
|
+
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}`}
|
|
317
|
+
>
|
|
318
|
+
Continue
|
|
319
|
+
</button>
|
|
320
|
+
</>
|
|
321
|
+
) : null}
|
|
322
|
+
{showInsertMenu ? (
|
|
323
|
+
<ToolbarInsertMenu
|
|
324
|
+
disabled={!canInsertStructural}
|
|
325
|
+
onInsertImage={props.onInsertImage}
|
|
326
|
+
onInsertPageBreak={props.onInsertPageBreak}
|
|
327
|
+
onInsertSectionBreak={props.onInsertSectionBreak}
|
|
328
|
+
onInsertTable={props.onInsertTable}
|
|
329
|
+
/>
|
|
330
|
+
) : null}
|
|
331
|
+
{showUpdateActions ? (
|
|
332
|
+
<>
|
|
333
|
+
<button
|
|
334
|
+
type="button"
|
|
335
|
+
aria-label="Refresh fields"
|
|
336
|
+
disabled={!canEdit || !props.onUpdateFields}
|
|
337
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
338
|
+
onClick={props.onUpdateFields}
|
|
339
|
+
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}`}
|
|
340
|
+
>
|
|
341
|
+
Fields
|
|
342
|
+
</button>
|
|
343
|
+
<button
|
|
344
|
+
type="button"
|
|
345
|
+
aria-label="Refresh table of contents"
|
|
346
|
+
disabled={!canEdit || !props.onUpdateTableOfContents}
|
|
347
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
348
|
+
onClick={props.onUpdateTableOfContents}
|
|
349
|
+
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}`}
|
|
350
|
+
>
|
|
351
|
+
TOC
|
|
352
|
+
</button>
|
|
353
|
+
</>
|
|
354
|
+
) : null}
|
|
257
355
|
|
|
258
356
|
{/* Story focus breadcrumb — visible when editing a secondary story */}
|
|
259
357
|
{props.activeStory && props.activeStory.kind !== "main" ? (
|
|
@@ -263,7 +361,7 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
263
361
|
type="button"
|
|
264
362
|
onClick={props.onCloseStory}
|
|
265
363
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
266
|
-
className={`inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-xs font-medium text-accent hover:bg-
|
|
364
|
+
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}`}
|
|
267
365
|
aria-label={`Editing ${storyLabel(props.activeStory)} — click to return to main body`}
|
|
268
366
|
>
|
|
269
367
|
<span className="text-secondary">←</span>
|
|
@@ -273,13 +371,6 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
273
371
|
) : null}
|
|
274
372
|
</div>
|
|
275
373
|
|
|
276
|
-
{/* Center: document title */}
|
|
277
|
-
<div className="flex-1 text-center min-w-0">
|
|
278
|
-
<span className="text-sm font-medium truncate block">
|
|
279
|
-
{props.sourceLabel ?? "Untitled"}
|
|
280
|
-
</span>
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
374
|
{/* Right cluster: comment, track changes, markup, view, export */}
|
|
284
375
|
<div className="flex items-center gap-0.5">
|
|
285
376
|
<TwToolbarIconButton
|
|
@@ -290,29 +381,33 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
290
381
|
onClick={props.onAddComment}
|
|
291
382
|
/>
|
|
292
383
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
<
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
384
|
+
{showTrackedChangesToggle ? (
|
|
385
|
+
<>
|
|
386
|
+
<Tooltip.Root>
|
|
387
|
+
<Tooltip.Trigger asChild>
|
|
388
|
+
<Toggle.Root
|
|
389
|
+
pressed={props.showTrackedChanges}
|
|
390
|
+
onPressedChange={props.onShowTrackedChangesChange}
|
|
391
|
+
disabled={caps ? !caps.trackChangesSupported : false}
|
|
392
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
393
|
+
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}`}
|
|
394
|
+
>
|
|
395
|
+
{props.showTrackedChanges ? <Eye className="h-4 w-4" /> : <EyeOff className="h-4 w-4" />}
|
|
396
|
+
</Toggle.Root>
|
|
397
|
+
</Tooltip.Trigger>
|
|
398
|
+
<Tooltip.Portal>
|
|
399
|
+
<Tooltip.Content
|
|
400
|
+
className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50"
|
|
401
|
+
sideOffset={6}
|
|
402
|
+
>
|
|
403
|
+
{props.showTrackedChanges ? "Hide tracked changes" : "Show tracked changes"}
|
|
404
|
+
</Tooltip.Content>
|
|
405
|
+
</Tooltip.Portal>
|
|
406
|
+
</Tooltip.Root>
|
|
314
407
|
|
|
315
|
-
|
|
408
|
+
<div className="mx-1 h-4 w-px bg-border" />
|
|
409
|
+
</>
|
|
410
|
+
) : null}
|
|
316
411
|
|
|
317
412
|
{/* View mode toggle group: Canvas (clean, flowing) / Page (layout-sensitive) */}
|
|
318
413
|
<ToggleGroup.Root
|
|
@@ -329,7 +424,7 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
329
424
|
value="canvas"
|
|
330
425
|
aria-label="Canvas workspace"
|
|
331
426
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
332
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-
|
|
427
|
+
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}`}
|
|
333
428
|
>
|
|
334
429
|
<Monitor className="h-3.5 w-3.5" />
|
|
335
430
|
</ToggleGroup.Item>
|
|
@@ -346,7 +441,7 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
346
441
|
value="page"
|
|
347
442
|
aria-label="Page workspace"
|
|
348
443
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
349
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-
|
|
444
|
+
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}`}
|
|
350
445
|
>
|
|
351
446
|
<FileText className="h-3.5 w-3.5" />
|
|
352
447
|
</ToggleGroup.Item>
|
|
@@ -359,8 +454,8 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
359
454
|
</Tooltip.Root>
|
|
360
455
|
</ToggleGroup.Root>
|
|
361
456
|
|
|
362
|
-
{/* Zoom controls —
|
|
363
|
-
{
|
|
457
|
+
{/* Zoom controls — available in all workspace modes */}
|
|
458
|
+
{props.onZoomChange ? (
|
|
364
459
|
<>
|
|
365
460
|
<div className="mx-1 h-4 w-px bg-border" />
|
|
366
461
|
<div className="flex items-center gap-0.5">
|
|
@@ -460,86 +555,60 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
460
555
|
</>
|
|
461
556
|
) : null}
|
|
462
557
|
|
|
463
|
-
{
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
558
|
+
{showHealth ? (
|
|
559
|
+
<>
|
|
560
|
+
<Popover.Root>
|
|
561
|
+
<Tooltip.Root>
|
|
562
|
+
<Tooltip.Trigger asChild>
|
|
563
|
+
<Popover.Trigger asChild>
|
|
564
|
+
<button
|
|
565
|
+
type="button"
|
|
566
|
+
aria-label="Document health"
|
|
567
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
568
|
+
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}`}
|
|
569
|
+
>
|
|
570
|
+
<AlertCircle className="h-4 w-4" />
|
|
571
|
+
{(caps?.healthIssueCount ?? 0) > 0 ? (
|
|
572
|
+
<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">
|
|
573
|
+
{caps?.healthIssueCount}
|
|
574
|
+
</span>
|
|
575
|
+
) : null}
|
|
576
|
+
</button>
|
|
577
|
+
</Popover.Trigger>
|
|
578
|
+
</Tooltip.Trigger>
|
|
579
|
+
<Tooltip.Portal>
|
|
580
|
+
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
476
581
|
{(caps?.healthIssueCount ?? 0) > 0
|
|
477
|
-
?
|
|
478
|
-
:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
<Popover.Content
|
|
499
|
-
className="w-[360px] max-h-[480px] overflow-y-auto rounded-lg bg-canvas shadow-lg ring-1 ring-border p-3 z-50"
|
|
500
|
-
sideOffset={8}
|
|
501
|
-
align="end"
|
|
502
|
-
>
|
|
503
|
-
<TwHealthPanel
|
|
504
|
-
blockedReasons={props.blockedReasons}
|
|
505
|
-
compatibility={props.compatibility}
|
|
506
|
-
warnings={props.warnings}
|
|
507
|
-
/>
|
|
508
|
-
</Popover.Content>
|
|
509
|
-
</Popover.Portal>
|
|
510
|
-
</Popover.Root>
|
|
582
|
+
? `Document health — ${caps?.healthIssueCount} issue${(caps?.healthIssueCount ?? 0) !== 1 ? "s" : ""}`
|
|
583
|
+
: "Document health — no issues"}
|
|
584
|
+
</Tooltip.Content>
|
|
585
|
+
</Tooltip.Portal>
|
|
586
|
+
</Tooltip.Root>
|
|
587
|
+
<Popover.Portal>
|
|
588
|
+
<Popover.Content
|
|
589
|
+
className="w-[360px] max-h-[480px] overflow-y-auto rounded-lg bg-canvas shadow-lg ring-1 ring-border p-3 z-50"
|
|
590
|
+
sideOffset={8}
|
|
591
|
+
align="end"
|
|
592
|
+
>
|
|
593
|
+
<TwHealthPanel
|
|
594
|
+
blockedReasons={props.blockedReasons}
|
|
595
|
+
compatibility={props.compatibility!}
|
|
596
|
+
warnings={props.warnings!}
|
|
597
|
+
/>
|
|
598
|
+
</Popover.Content>
|
|
599
|
+
</Popover.Portal>
|
|
600
|
+
</Popover.Root>
|
|
601
|
+
<div className="mx-1 h-4 w-px bg-border" />
|
|
602
|
+
</>
|
|
511
603
|
) : null}
|
|
512
604
|
|
|
513
|
-
<
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
disabled={caps ? !caps.canExport : true}
|
|
521
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
522
|
-
className={[
|
|
523
|
-
"inline-flex h-7 items-center gap-1.5 rounded-md px-2.5 text-xs font-semibold transition-colors outline-none",
|
|
524
|
-
focusRingClass,
|
|
525
|
-
caps?.exportBlocked
|
|
526
|
-
? "cursor-not-allowed text-danger opacity-50"
|
|
527
|
-
: "text-accent hover:bg-accent-soft",
|
|
528
|
-
].join(" ")}
|
|
529
|
-
onClick={props.onExport}
|
|
530
|
-
>
|
|
531
|
-
<Download className="h-3.5 w-3.5" />
|
|
532
|
-
Export .docx
|
|
533
|
-
</button>
|
|
534
|
-
</Tooltip.Trigger>
|
|
535
|
-
<Tooltip.Portal>
|
|
536
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
537
|
-
{caps?.exportBlocked
|
|
538
|
-
? "Export blocked by unsupported content"
|
|
539
|
-
: "Export document"}
|
|
540
|
-
</Tooltip.Content>
|
|
541
|
-
</Tooltip.Portal>
|
|
542
|
-
</Tooltip.Root>
|
|
605
|
+
<TwToolbarIconButton
|
|
606
|
+
icon={Download}
|
|
607
|
+
label={caps?.exportBlocked ? "Export blocked" : "Download document"}
|
|
608
|
+
disabled={caps ? !caps.canExport : true}
|
|
609
|
+
emphasis
|
|
610
|
+
onClick={props.onExport}
|
|
611
|
+
/>
|
|
543
612
|
</div>
|
|
544
613
|
</header>
|
|
545
614
|
);
|
|
@@ -584,7 +653,7 @@ function ToolbarParagraphStyleSelect(props: {
|
|
|
584
653
|
<Select.Viewport className="p-1">
|
|
585
654
|
{props.styles.map((style) => (
|
|
586
655
|
<Select.Item
|
|
587
|
-
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-accent-
|
|
656
|
+
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}`}
|
|
588
657
|
key={style.styleId}
|
|
589
658
|
value={style.styleId}
|
|
590
659
|
>
|
|
@@ -633,7 +702,7 @@ function ToolbarFontFamilySelect(props: {
|
|
|
633
702
|
<Select.Viewport className="p-1">
|
|
634
703
|
{FONT_FAMILIES.map((font) => (
|
|
635
704
|
<Select.Item
|
|
636
|
-
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-accent-
|
|
705
|
+
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}`}
|
|
637
706
|
key={font}
|
|
638
707
|
value={font}
|
|
639
708
|
>
|
|
@@ -683,7 +752,7 @@ function ToolbarFontSizeSelect(props: {
|
|
|
683
752
|
<Select.Viewport className="p-1">
|
|
684
753
|
{FONT_SIZES.map((size) => (
|
|
685
754
|
<Select.Item
|
|
686
|
-
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-accent-
|
|
755
|
+
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}`}
|
|
687
756
|
key={size}
|
|
688
757
|
value={String(size)}
|
|
689
758
|
>
|
|
@@ -1018,7 +1087,7 @@ function ToolbarPopoverActionButton(props: {
|
|
|
1018
1087
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1019
1088
|
onClick={props.onClick}
|
|
1020
1089
|
className={`inline-flex h-8 items-center justify-center rounded-md border border-border transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1021
|
-
props.active ? "bg-accent-
|
|
1090
|
+
props.active ? "bg-canvas text-accent ring-1 ring-accent/30 shadow-sm" : "bg-canvas text-secondary hover:bg-surface"
|
|
1022
1091
|
} ${focusRingClass}`}
|
|
1023
1092
|
>
|
|
1024
1093
|
{props.icon}
|