@beyondwork/docx-react-component 1.0.80 → 1.0.82
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 +1 -1
- package/src/api/public-types.ts +12 -13
- package/src/api/v3/_runtime-handle.ts +4 -0
- package/src/api/v3/runtime/document.ts +61 -3
- package/src/api/v3/runtime/review.ts +55 -2
- package/src/api/v3/ui/chrome-composition.ts +10 -2
- package/src/io/normalize/normalize-text.ts +4 -1
- package/src/io/ooxml/parse-drawing.ts +4 -0
- package/src/model/canonical-document.ts +2 -0
- package/src/ui/WordReviewEditor.tsx +132 -3
- package/src/ui/editor-shell-view.tsx +1 -0
- package/src/ui-tailwind/chrome/editor-action-registry.ts +373 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +59 -35
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +2 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/use-context-menu-controller.ts +15 -10
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +1 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +256 -37
- package/src/ui-tailwind/editor-surface/pm-schema.ts +54 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +31 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +15 -0
- package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +24 -5
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +35 -6
- package/src/ui-tailwind/page-stack/use-visible-block-range.ts +333 -43
- package/src/ui-tailwind/review-workspace/types.ts +1 -0
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +273 -24
- package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +46 -6
- package/src/ui-tailwind/theme/editor-theme.css +3 -5
- package/src/ui-tailwind/tw-review-workspace.tsx +117 -14
|
@@ -43,7 +43,10 @@ import {
|
|
|
43
43
|
import { TwContextBand } from "./chrome/tw-context-band";
|
|
44
44
|
import { TwRoleActionRegion } from "./toolbar/tw-role-action-region";
|
|
45
45
|
import { LocalSurfaceArbiterContext } from "./chrome/local-surface-arbiter";
|
|
46
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
TwWorkspaceChromeHost,
|
|
48
|
+
type TwWorkspaceChromeHostController,
|
|
49
|
+
} from "./chrome/tw-workspace-chrome-host";
|
|
47
50
|
import { TwChromeOverlay, TwPageStackOverlayLayer } from "./chrome-overlay";
|
|
48
51
|
import { TwFloatingImageLayer } from "./page-stack/tw-floating-image-layer.tsx";
|
|
49
52
|
import { shouldHidePageBorderForSelection } from "./review-workspace/paragraph-layout.ts";
|
|
@@ -85,16 +88,14 @@ export type {
|
|
|
85
88
|
import type { EditorRole } from "../api/public-types.ts";
|
|
86
89
|
|
|
87
90
|
// Default shell-header modes for the workspace's default composition.
|
|
88
|
-
// Designsystem §6.1 prescribes a 4-mode switcher
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// Slice 5 (Phase Q debug UX). Hosts that want a fully-wired 4-mode set
|
|
92
|
-
// supply their own `shellHeader` prop.
|
|
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.
|
|
93
94
|
const DEFAULT_WORKSPACE_SHELL_MODES: readonly ShellHeaderModeOption[] = [
|
|
94
95
|
{ id: "edit", label: "Edit" },
|
|
95
96
|
{ id: "review", label: "Review" },
|
|
96
97
|
{ id: "workflow", label: "Workflow" },
|
|
97
|
-
{ id: "more", label: "More"
|
|
98
|
+
{ id: "more", label: "More" },
|
|
98
99
|
];
|
|
99
100
|
|
|
100
101
|
function editorRoleToShellMode(role: EditorRole): ShellHeaderMode {
|
|
@@ -121,6 +122,47 @@ function shellModeToEditorRole(mode: ShellHeaderMode): EditorRole | null {
|
|
|
121
122
|
}
|
|
122
123
|
}
|
|
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
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
124
166
|
export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
125
167
|
const props = {
|
|
126
168
|
...inputProps,
|
|
@@ -128,6 +170,10 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
128
170
|
} as TwReviewWorkspaceProps & EditorCommandBag;
|
|
129
171
|
const { snapshot, viewState } = props;
|
|
130
172
|
const selectionToolbarRootRef = useRef<HTMLDivElement>(null);
|
|
173
|
+
const workspaceChromeControllerRef =
|
|
174
|
+
useRef<TwWorkspaceChromeHostController | null>(null);
|
|
175
|
+
const [shellModeOverride, setShellModeOverride] =
|
|
176
|
+
useState<ShellHeaderMode | null>(null);
|
|
131
177
|
// P8.11 — body slot wrapping `{props.document}` (the PM surface) + scroll
|
|
132
178
|
// root ref. The chrome layer's `TwPageStackChromeLayer` needs both to
|
|
133
179
|
// measure per-page rects and to reparent PM's DOM node across band
|
|
@@ -320,6 +366,9 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
320
366
|
pageStackScrollRoot,
|
|
321
367
|
snapshot,
|
|
322
368
|
layoutFacet: props.layoutFacet,
|
|
369
|
+
geometryFacet: props.geometryFacet,
|
|
370
|
+
renderFrameRevision,
|
|
371
|
+
viewportScale: zoomScale,
|
|
323
372
|
});
|
|
324
373
|
|
|
325
374
|
const { dismissSelectionToolbar, runWithSelectionToolbarDismiss } =
|
|
@@ -382,18 +431,46 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
382
431
|
props.onSetParagraphTabStops,
|
|
383
432
|
]);
|
|
384
433
|
|
|
434
|
+
const setWorkspaceChromeController = useCallback(
|
|
435
|
+
(instance: TwWorkspaceChromeHostController | null) => {
|
|
436
|
+
workspaceChromeControllerRef.current = instance;
|
|
437
|
+
const ref = props.chromeControllerRef;
|
|
438
|
+
if (!ref) return;
|
|
439
|
+
if (typeof ref === "function") {
|
|
440
|
+
ref(instance);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
(ref as React.MutableRefObject<TwWorkspaceChromeHostController | null>).current =
|
|
444
|
+
instance;
|
|
445
|
+
},
|
|
446
|
+
[props.chromeControllerRef],
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
const openTableMoreMenu = useCallback(
|
|
450
|
+
(coords: { clientX: number; clientY: number }) => {
|
|
451
|
+
workspaceChromeControllerRef.current?.openWithKinds({
|
|
452
|
+
...coords,
|
|
453
|
+
kinds: ["table-cell"],
|
|
454
|
+
});
|
|
455
|
+
},
|
|
456
|
+
[],
|
|
457
|
+
);
|
|
458
|
+
|
|
385
459
|
// Audit §2.4 — the shell header is ALWAYS present in default composition.
|
|
386
460
|
// When the host does not supply a pre-assembled shell node, fall back to
|
|
387
461
|
// a default TwShellHeader wired to the workspace's editor-role state so
|
|
388
462
|
// the mode tabs actually change the active role instead of being
|
|
389
|
-
// decorative.
|
|
390
|
-
//
|
|
463
|
+
// decorative. The "more" tab is shell-owned diagnostics/search posture:
|
|
464
|
+
// it drives composition mode via `modeOverride` and does not mutate the
|
|
465
|
+
// runtime editor role.
|
|
391
466
|
// Host-supplied shells continue to win (back-compat).
|
|
392
467
|
const defaultShellModes: readonly ShellHeaderModeOption[] =
|
|
393
468
|
DEFAULT_WORKSPACE_SHELL_MODES;
|
|
394
469
|
const defaultShellActiveMode: ShellHeaderMode = editorRoleToShellMode(
|
|
395
470
|
viewState.editorRole,
|
|
396
471
|
);
|
|
472
|
+
const activeShellMode: ShellHeaderMode =
|
|
473
|
+
shellModeOverride === "more" ? "more" : defaultShellActiveMode;
|
|
397
474
|
// coord-11 §21 — host-supplied shellHeader wins; otherwise the default
|
|
398
475
|
// TwShellHeader mounts only when `chromeVisibility.shellHeader === true`
|
|
399
476
|
// (default for every preset except `selection`). The `selection` preset
|
|
@@ -406,8 +483,15 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
406
483
|
? (
|
|
407
484
|
<TwShellHeader
|
|
408
485
|
modes={defaultShellModes}
|
|
409
|
-
activeMode={
|
|
486
|
+
activeMode={activeShellMode}
|
|
410
487
|
onModeChange={(mode) => {
|
|
488
|
+
if (mode === "more") {
|
|
489
|
+
setShellModeOverride("more");
|
|
490
|
+
setReviewRailOpen(true);
|
|
491
|
+
props.onActiveRailTabChange?.("health");
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
setShellModeOverride(null);
|
|
411
495
|
const nextRole = shellModeToEditorRole(mode);
|
|
412
496
|
if (nextRole !== null && nextRole !== viewState.editorRole) {
|
|
413
497
|
props.onEditorRoleChange?.(nextRole);
|
|
@@ -444,10 +528,17 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
444
528
|
const composition = useWorkspaceComposition({
|
|
445
529
|
chromePreset,
|
|
446
530
|
chromeOptions: props.chromeOptions,
|
|
531
|
+
chromeVisibility: props.chromeVisibility,
|
|
447
532
|
reviewMode: props.reviewMode,
|
|
448
533
|
role: viewState.editorRole,
|
|
534
|
+
readOnly: snapshot.readOnly,
|
|
449
535
|
markupDisplay: props.markupDisplay,
|
|
536
|
+
activeRailTab: props.activeRailTab,
|
|
537
|
+
railOpen: reviewRailOpen,
|
|
538
|
+
density: props.density,
|
|
539
|
+
containerWidth: viewportWidth,
|
|
450
540
|
diagnosticsSignal,
|
|
541
|
+
modeOverride: shellModeOverride === "more" ? "more" : undefined,
|
|
451
542
|
});
|
|
452
543
|
const showHealthRailTab = composition.rail.visibleTabs.has("health");
|
|
453
544
|
|
|
@@ -481,7 +572,20 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
481
572
|
*/}
|
|
482
573
|
{chromeVisibility.toolbar ? (
|
|
483
574
|
<TwContextBand mode={composition.mode}>
|
|
484
|
-
{
|
|
575
|
+
{composition.mode === "more" ? (
|
|
576
|
+
<TwMoreContextBandContent
|
|
577
|
+
onOpenDiagnostics={() => {
|
|
578
|
+
setReviewRailOpen(true);
|
|
579
|
+
props.onActiveRailTabChange?.("health");
|
|
580
|
+
}}
|
|
581
|
+
onOpenCompatibility={() => {
|
|
582
|
+
setReviewRailOpen(true);
|
|
583
|
+
props.onActiveRailTabChange?.("health");
|
|
584
|
+
}}
|
|
585
|
+
onOpenOutline={() => setNavOpen(true)}
|
|
586
|
+
onOpenSearch={props.reviewRailFooter?.onSearch}
|
|
587
|
+
/>
|
|
588
|
+
) : viewState.editorRole ? (
|
|
485
589
|
<TwRoleActionRegion
|
|
486
590
|
role={viewState.editorRole}
|
|
487
591
|
policy={scopedChromePolicy}
|
|
@@ -574,9 +678,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
574
678
|
<TwWorkspaceChromeHost
|
|
575
679
|
mode={composition.mode}
|
|
576
680
|
editorActionHost={props.editorActionHost}
|
|
577
|
-
{
|
|
578
|
-
? { controllerRef: props.chromeControllerRef }
|
|
579
|
-
: {})}
|
|
681
|
+
controllerRef={setWorkspaceChromeController}
|
|
580
682
|
{...(props.commandPaletteDisabled !== undefined
|
|
581
683
|
? { paletteDisabled: props.commandPaletteDisabled }
|
|
582
684
|
: {})}
|
|
@@ -875,6 +977,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
875
977
|
onDistributeColumnsEvenly={props.onDistributeColumnsEvenly}
|
|
876
978
|
onSetTableAlignment={props.onSetTableAlignment}
|
|
877
979
|
onSetCellVerticalAlign={props.onSetCellVerticalAlign}
|
|
980
|
+
onOpenTableMore={openTableMoreMenu}
|
|
878
981
|
chromePins={viewState.chromePins}
|
|
879
982
|
onChromePinChange={props.onChromePinChange}
|
|
880
983
|
/>
|