@beyondwork/docx-react-component 1.0.90 → 1.0.92
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/ui/WordReviewEditor.tsx +2 -1
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +1 -17
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +1 -6
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +7 -17
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +1 -1
- package/src/ui-tailwind/review/tw-review-rail.tsx +0 -5
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +38 -32
- package/src/ui-tailwind/theme/editor-theme.css +12 -25
- package/src/ui-tailwind/tw-review-workspace.tsx +0 -15
- package/src/ui-tailwind/workflow-scope-layers.ts +0 -70
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.92",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": [
|
|
@@ -138,6 +138,7 @@ import {
|
|
|
138
138
|
type TwProseMirrorSurfaceRef,
|
|
139
139
|
} from "../ui-tailwind/editor-surface/tw-prosemirror-surface";
|
|
140
140
|
import { TwInlineFindBar } from "../ui-tailwind/chrome/tw-inline-find-bar";
|
|
141
|
+
import { PAGE_CHROME_DEFAULTS } from "../ui-tailwind/editor-surface/pm-page-break-decorations.ts";
|
|
141
142
|
import type { MediaPreviewDescriptor } from "../ui-tailwind/editor-surface/pm-state-from-snapshot";
|
|
142
143
|
import {
|
|
143
144
|
incrementInvalidationCounter,
|
|
@@ -3753,7 +3754,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
3753
3754
|
geometryFacet={activeRuntime.geometry}
|
|
3754
3755
|
pageChromeHeaderBandPx={isPageWorkspace ? 32 : 0}
|
|
3755
3756
|
pageChromeFooterBandPx={isPageWorkspace ? 32 : 0}
|
|
3756
|
-
pageChromeInterGapPx={
|
|
3757
|
+
pageChromeInterGapPx={PAGE_CHROME_DEFAULTS.interGapPx}
|
|
3757
3758
|
onOpenHeaderStoryForPage={(pageIndex: number) =>
|
|
3758
3759
|
openStoryForPage(activeRuntime, pageIndex, "header")
|
|
3759
3760
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import * as React from "react";
|
|
16
16
|
import type { OverlayCoordinateSpace } from "./chrome-overlay-projector";
|
|
17
|
-
import type {
|
|
17
|
+
import type { ScopeRailSegment } from "../../api/public-types.ts";
|
|
18
18
|
import type {
|
|
19
19
|
EditorRole,
|
|
20
20
|
EditorStoryTarget,
|
|
@@ -49,8 +49,6 @@ export interface TwChromeOverlayProps {
|
|
|
49
49
|
space?: OverlayCoordinateSpace;
|
|
50
50
|
/** Active scope id (for emphasis + rail tab sync). */
|
|
51
51
|
activeScopeId?: string | null;
|
|
52
|
-
/** Posture filters shared with the Workflow rail. Omitted means all scopes render. */
|
|
53
|
-
visibleScopePostures?: ReadonlySet<ScopeRailPosture>;
|
|
54
52
|
/**
|
|
55
53
|
* Click handler fired when the user clicks a scope rail stripe.
|
|
56
54
|
* P0 wires this to open the scope card (P1 ships the card layer).
|
|
@@ -215,7 +213,6 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
|
|
|
215
213
|
geometryFacet,
|
|
216
214
|
space,
|
|
217
215
|
activeScopeId,
|
|
218
|
-
visibleScopePostures,
|
|
219
216
|
onScopeStripeClick,
|
|
220
217
|
onScopeSegmentClick,
|
|
221
218
|
onScopeCardClose,
|
|
@@ -245,17 +242,6 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
|
|
|
245
242
|
mediaPreviews,
|
|
246
243
|
activeBandRibbonProps,
|
|
247
244
|
}) => {
|
|
248
|
-
const visibleScopeIds = React.useMemo(() => {
|
|
249
|
-
if (!visibleScopePostures) return undefined;
|
|
250
|
-
const ids = new Set<string>();
|
|
251
|
-
for (const segment of workflowFacet?.getAllRailSegments() ?? []) {
|
|
252
|
-
if (visibleScopePostures.has(segment.posture)) {
|
|
253
|
-
ids.add(segment.scopeId);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return ids;
|
|
257
|
-
}, [visibleScopePostures, workflowFacet]);
|
|
258
|
-
|
|
259
245
|
return (
|
|
260
246
|
<div
|
|
261
247
|
className="wre-chrome-overlay pointer-events-none absolute inset-0 z-30"
|
|
@@ -282,7 +268,6 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
|
|
|
282
268
|
workflowFacet={workflowFacet}
|
|
283
269
|
space={space}
|
|
284
270
|
activeScopeId={activeScopeId}
|
|
285
|
-
visibleScopePostures={visibleScopePostures}
|
|
286
271
|
onStripeClick={onScopeStripeClick}
|
|
287
272
|
onSegmentClick={onScopeSegmentClick}
|
|
288
273
|
/>
|
|
@@ -290,7 +275,6 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
|
|
|
290
275
|
facet={facet}
|
|
291
276
|
workflowFacet={workflowFacet}
|
|
292
277
|
activeScopeId={activeScopeId ?? null}
|
|
293
|
-
visibleScopeIds={visibleScopeIds}
|
|
294
278
|
onClose={onScopeCardClose ?? noop}
|
|
295
279
|
onModeChange={onScopeCardModeChange ?? noopModeChange}
|
|
296
280
|
onIssueAction={onScopeCardIssueAction ?? noopIssueAction}
|
|
@@ -52,8 +52,6 @@ export interface TwScopeCardLayerProps {
|
|
|
52
52
|
*/
|
|
53
53
|
workflowFacet: WorkflowFacet | null;
|
|
54
54
|
activeScopeId: string | null;
|
|
55
|
-
/** Scope ids currently visible under the Workflow rail layer filters. */
|
|
56
|
-
visibleScopeIds?: ReadonlySet<string>;
|
|
57
55
|
onClose: () => void;
|
|
58
56
|
onModeChange: (scopeId: string, mode: WorkflowScopeMode) => void;
|
|
59
57
|
onIssueAction: (
|
|
@@ -93,7 +91,6 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
|
|
|
93
91
|
facet,
|
|
94
92
|
workflowFacet,
|
|
95
93
|
activeScopeId,
|
|
96
|
-
visibleScopeIds,
|
|
97
94
|
onClose,
|
|
98
95
|
onModeChange,
|
|
99
96
|
onIssueAction,
|
|
@@ -122,9 +119,7 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
|
|
|
122
119
|
// The effective scope is the pinned one if it still resolves to a
|
|
123
120
|
// model, else the active one. When a pinned scope disappears
|
|
124
121
|
// (e.g. the host cleared the overlay), drop the pin.
|
|
125
|
-
const models =
|
|
126
|
-
visibleScopeIds ? visibleScopeIds.has(model.scopeId) : true,
|
|
127
|
-
);
|
|
122
|
+
const models = workflowFacet?.getAllScopeCardModels() ?? [];
|
|
128
123
|
|
|
129
124
|
const pinnedModel = pinnedScopeId
|
|
130
125
|
? models.find((m) => m.scopeId === pinnedScopeId) ?? null
|
|
@@ -46,8 +46,6 @@ export interface TwScopeRailLayerProps {
|
|
|
46
46
|
railLaneWidthPx?: number;
|
|
47
47
|
/** Scope id that should render with the `active` emphasis. */
|
|
48
48
|
activeScopeId?: string | null;
|
|
49
|
-
/** Posture filters shared with the Workflow rail. Omitted means all scopes render. */
|
|
50
|
-
visibleScopePostures?: ReadonlySet<ScopeRailPosture>;
|
|
51
49
|
/**
|
|
52
50
|
* Fires when the user clicks the rail stripe — opens the scope card.
|
|
53
51
|
* P0 wires this directly; P1 replaces with card-layer-aware routing.
|
|
@@ -89,8 +87,8 @@ const POSTURE_STYLES: Record<ScopeRailPosture, PostureStyle> = {
|
|
|
89
87
|
// ---------------------------------------------------------------------------
|
|
90
88
|
|
|
91
89
|
const DEFAULT_RAIL_LANE_PX = 44;
|
|
92
|
-
const STRIPE_WIDTH_PX =
|
|
93
|
-
const LABEL_WIDTH_PX =
|
|
90
|
+
const STRIPE_WIDTH_PX = 4;
|
|
91
|
+
const LABEL_WIDTH_PX = 40;
|
|
94
92
|
const STACK_OFFSET_PX = 6;
|
|
95
93
|
|
|
96
94
|
export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
@@ -99,15 +97,12 @@ export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
|
99
97
|
space,
|
|
100
98
|
railLaneWidthPx = DEFAULT_RAIL_LANE_PX,
|
|
101
99
|
activeScopeId,
|
|
102
|
-
visibleScopePostures,
|
|
103
100
|
onStripeClick,
|
|
104
101
|
onSegmentClick,
|
|
105
102
|
"data-testid": testId,
|
|
106
103
|
}) => {
|
|
107
104
|
const frame = geometryFacet.getRenderFrame() ?? null;
|
|
108
|
-
const segments =
|
|
109
|
-
visibleScopePostures ? visibleScopePostures.has(segment.posture) : true,
|
|
110
|
-
);
|
|
105
|
+
const segments = workflowFacet?.getAllRailSegments() ?? [];
|
|
111
106
|
|
|
112
107
|
if (!frame || segments.length === 0) {
|
|
113
108
|
return null;
|
|
@@ -228,21 +223,16 @@ export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
|
228
223
|
style={projectRectToOverlay(stripeRect, projectorSpace)}
|
|
229
224
|
/>
|
|
230
225
|
{/* Label pill — revealed on stripe hover via CSS. */}
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
tabIndex={-1}
|
|
234
|
-
className={`wre-scope-rail-label wre-scope-rail-label-${style.railToken} ${
|
|
235
|
-
isActive ? "wre-scope-rail-label-active" : ""
|
|
236
|
-
}`}
|
|
226
|
+
<div
|
|
227
|
+
className={`wre-scope-rail-label wre-scope-rail-label-${style.railToken}`}
|
|
237
228
|
data-scope-id={segment.scopeId}
|
|
238
229
|
data-posture={segment.posture}
|
|
239
|
-
aria-
|
|
240
|
-
onClick={handleActivate}
|
|
230
|
+
aria-hidden="true"
|
|
241
231
|
style={projectRectToOverlay(labelRect, projectorSpace)}
|
|
242
232
|
>
|
|
243
233
|
<span aria-hidden="true" className={`wre-scope-rail-icon wre-scope-rail-icon-${style.icon}`} />
|
|
244
234
|
<span className="wre-scope-rail-label-text">{style.labelText}</span>
|
|
245
|
-
</
|
|
235
|
+
</div>
|
|
246
236
|
</React.Fragment>
|
|
247
237
|
);
|
|
248
238
|
})}
|
|
@@ -340,7 +340,7 @@ export interface TwProseMirrorSurfaceProps {
|
|
|
340
340
|
pageChromeHeaderBandPx?: number;
|
|
341
341
|
/** Height in px of each page's footer band. Default 32. */
|
|
342
342
|
pageChromeFooterBandPx?: number;
|
|
343
|
-
/** Visible vertical gap between adjacent pages. Default
|
|
343
|
+
/** Visible vertical gap between adjacent pages. Default 48 to match the render kernel. */
|
|
344
344
|
pageChromeInterGapPx?: number;
|
|
345
345
|
/**
|
|
346
346
|
* Revision counter; incremented by the host to trigger a decoration
|
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
TwReviewRailFooter,
|
|
24
24
|
type TwReviewRailFooterProps,
|
|
25
25
|
} from "./tw-review-rail-footer";
|
|
26
|
-
import type { WorkflowScopeLayerKey } from "../workflow-scope-layers";
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
28
|
* Review rail with up to four tabs (Workflow / Comments / Changes / Health).
|
|
@@ -67,8 +66,6 @@ export interface TwReviewRailProps {
|
|
|
67
66
|
*/
|
|
68
67
|
scopeRailSegments?: readonly ScopeRailSegment[];
|
|
69
68
|
activeScopeId?: string | null;
|
|
70
|
-
workflowLayerFilters?: ReadonlySet<WorkflowScopeLayerKey>;
|
|
71
|
-
onWorkflowLayerFiltersChange?: (filters: ReadonlySet<WorkflowScopeLayerKey>) => void;
|
|
72
69
|
/**
|
|
73
70
|
* Optional host-provided Workflow-tab override. When supplied this
|
|
74
71
|
* ReactNode replaces the default TwWorkflowTab content while still using
|
|
@@ -265,8 +262,6 @@ export function TwReviewRail(props: TwReviewRailProps) {
|
|
|
265
262
|
<TwWorkflowTab
|
|
266
263
|
segments={workflowSegments}
|
|
267
264
|
activeScopeId={props.activeScopeId ?? null}
|
|
268
|
-
enabledLayerFilters={props.workflowLayerFilters}
|
|
269
|
-
onEnabledLayerFiltersChange={props.onWorkflowLayerFiltersChange}
|
|
270
265
|
onOpenScope={props.onOpenScope}
|
|
271
266
|
/>
|
|
272
267
|
)}
|
|
@@ -10,13 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
import React from "react";
|
|
12
12
|
import type { ScopeRailSegment, ScopeRailPosture } from "../../api/public-types";
|
|
13
|
-
import {
|
|
14
|
-
WORKFLOW_SCOPE_LAYER_FILTERS,
|
|
15
|
-
createDefaultWorkflowScopeLayerKeys,
|
|
16
|
-
isWorkflowScopePostureVisible,
|
|
17
|
-
toggleWorkflowScopeLayerKey,
|
|
18
|
-
type WorkflowScopeLayerKey,
|
|
19
|
-
} from "../workflow-scope-layers";
|
|
20
13
|
|
|
21
14
|
export interface TwWorkflowTabProps {
|
|
22
15
|
segments: readonly ScopeRailSegment[];
|
|
@@ -28,8 +21,6 @@ export interface TwWorkflowTabProps {
|
|
|
28
21
|
* matching overlay card. If omitted, focus sync is not wired.
|
|
29
22
|
*/
|
|
30
23
|
onActiveScopeChange?: (scopeId: string) => void;
|
|
31
|
-
enabledLayerFilters?: ReadonlySet<WorkflowScopeLayerKey>;
|
|
32
|
-
onEnabledLayerFiltersChange?: (filters: ReadonlySet<WorkflowScopeLayerKey>) => void;
|
|
33
24
|
}
|
|
34
25
|
|
|
35
26
|
const POSTURE_META: Record<
|
|
@@ -48,13 +39,26 @@ const POSTURE_META: Record<
|
|
|
48
39
|
const focusRingClass =
|
|
49
40
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
|
|
50
41
|
|
|
42
|
+
type ScopeFilterKey = "edit" | "suggest" | "comment" | "view" | "candidate" | "blocked";
|
|
43
|
+
|
|
44
|
+
const SCOPE_FILTERS: ReadonlyArray<{
|
|
45
|
+
key: ScopeFilterKey;
|
|
46
|
+
label: string;
|
|
47
|
+
postures: readonly ScopeRailPosture[];
|
|
48
|
+
}> = [
|
|
49
|
+
{ key: "edit", label: "Edit", postures: ["edit"] },
|
|
50
|
+
{ key: "suggest", label: "Suggest", postures: ["suggest"] },
|
|
51
|
+
{ key: "comment", label: "Comment", postures: ["comment"] },
|
|
52
|
+
{ key: "view", label: "Review", postures: ["view"] },
|
|
53
|
+
{ key: "candidate", label: "Scheduled", postures: ["candidate"] },
|
|
54
|
+
{ key: "blocked", label: "Blocked", postures: ["preserve-only", "blocked-import"] },
|
|
55
|
+
];
|
|
56
|
+
|
|
51
57
|
export const TwWorkflowTab: React.FC<TwWorkflowTabProps> = ({
|
|
52
58
|
segments,
|
|
53
59
|
activeScopeId,
|
|
54
60
|
onOpenScope,
|
|
55
61
|
onActiveScopeChange,
|
|
56
|
-
enabledLayerFilters,
|
|
57
|
-
onEnabledLayerFiltersChange,
|
|
58
62
|
}) => {
|
|
59
63
|
// Dedupe by scopeId so a scope spanning multiple pages shows once.
|
|
60
64
|
const uniqueSegments = React.useMemo(() => {
|
|
@@ -67,31 +71,20 @@ export const TwWorkflowTab: React.FC<TwWorkflowTabProps> = ({
|
|
|
67
71
|
return Array.from(byScopeId.values()).sort(compareWorkflowSegments(activeScopeId ?? null));
|
|
68
72
|
}, [activeScopeId, segments]);
|
|
69
73
|
const [query, setQuery] = React.useState("");
|
|
70
|
-
const [
|
|
71
|
-
|
|
72
|
-
>(
|
|
73
|
-
createDefaultWorkflowScopeLayerKeys,
|
|
74
|
-
);
|
|
75
|
-
const activeEnabledFilters = enabledLayerFilters ?? uncontrolledEnabledFilters;
|
|
76
|
-
const setEnabledFilters = React.useCallback(
|
|
77
|
-
(next: ReadonlySet<WorkflowScopeLayerKey>) => {
|
|
78
|
-
if (!enabledLayerFilters) {
|
|
79
|
-
setUncontrolledEnabledFilters(next);
|
|
80
|
-
}
|
|
81
|
-
onEnabledLayerFiltersChange?.(next);
|
|
82
|
-
},
|
|
83
|
-
[enabledLayerFilters, onEnabledLayerFiltersChange],
|
|
74
|
+
const [enabledFilters, setEnabledFilters] = React.useState<ReadonlySet<ScopeFilterKey>>(
|
|
75
|
+
() => new Set(SCOPE_FILTERS.map((filter) => filter.key)),
|
|
84
76
|
);
|
|
85
77
|
const availableFilters = React.useMemo(() => {
|
|
86
78
|
const presentPostures = new Set(uniqueSegments.map((segment) => segment.posture));
|
|
87
|
-
return
|
|
79
|
+
return SCOPE_FILTERS.filter((filter) =>
|
|
88
80
|
filter.postures.some((posture) => presentPostures.has(posture)),
|
|
89
81
|
);
|
|
90
82
|
}, [uniqueSegments]);
|
|
91
83
|
const visibleSegments = React.useMemo(() => {
|
|
92
84
|
const normalizedQuery = normalizeScopeQuery(query);
|
|
93
85
|
return uniqueSegments.filter((segment) => {
|
|
94
|
-
|
|
86
|
+
const filterKey = filterKeyForPosture(segment.posture);
|
|
87
|
+
if (!enabledFilters.has(filterKey)) {
|
|
95
88
|
return false;
|
|
96
89
|
}
|
|
97
90
|
if (!normalizedQuery) {
|
|
@@ -99,7 +92,7 @@ export const TwWorkflowTab: React.FC<TwWorkflowTabProps> = ({
|
|
|
99
92
|
}
|
|
100
93
|
return scopeSearchText(segment).includes(normalizedQuery);
|
|
101
94
|
});
|
|
102
|
-
}, [
|
|
95
|
+
}, [enabledFilters, query, uniqueSegments]);
|
|
103
96
|
|
|
104
97
|
if (uniqueSegments.length === 0) {
|
|
105
98
|
return (
|
|
@@ -153,7 +146,7 @@ export const TwWorkflowTab: React.FC<TwWorkflowTabProps> = ({
|
|
|
153
146
|
role="group"
|
|
154
147
|
>
|
|
155
148
|
{availableFilters.map((filter) => {
|
|
156
|
-
const isEnabled =
|
|
149
|
+
const isEnabled = enabledFilters.has(filter.key);
|
|
157
150
|
return (
|
|
158
151
|
<button
|
|
159
152
|
key={filter.key}
|
|
@@ -167,9 +160,15 @@ export const TwWorkflowTab: React.FC<TwWorkflowTabProps> = ({
|
|
|
167
160
|
].join(" ")}
|
|
168
161
|
data-testid={`workflow-scope-filter-${filter.key}`}
|
|
169
162
|
onClick={() => {
|
|
170
|
-
setEnabledFilters(
|
|
171
|
-
|
|
172
|
-
|
|
163
|
+
setEnabledFilters((current) => {
|
|
164
|
+
const next = new Set(current);
|
|
165
|
+
if (next.has(filter.key)) {
|
|
166
|
+
next.delete(filter.key);
|
|
167
|
+
} else {
|
|
168
|
+
next.add(filter.key);
|
|
169
|
+
}
|
|
170
|
+
return next;
|
|
171
|
+
});
|
|
173
172
|
}}
|
|
174
173
|
>
|
|
175
174
|
{filter.label}
|
|
@@ -246,6 +245,13 @@ function compareWorkflowSegments(activeScopeId: string | null) {
|
|
|
246
245
|
};
|
|
247
246
|
}
|
|
248
247
|
|
|
248
|
+
function filterKeyForPosture(posture: ScopeRailPosture): ScopeFilterKey {
|
|
249
|
+
if (posture === "preserve-only" || posture === "blocked-import") {
|
|
250
|
+
return "blocked";
|
|
251
|
+
}
|
|
252
|
+
return posture;
|
|
253
|
+
}
|
|
254
|
+
|
|
249
255
|
function normalizeScopeQuery(value: string): string {
|
|
250
256
|
return value.trim().toLocaleLowerCase();
|
|
251
257
|
}
|
|
@@ -475,19 +475,19 @@
|
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
.wre-scope-rail-tint-accent {
|
|
478
|
-
background: color-mix(in srgb, var(--color-accent)
|
|
478
|
+
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
|
|
479
479
|
}
|
|
480
480
|
.wre-scope-rail-tint-warning {
|
|
481
|
-
background: color-mix(in srgb, var(--color-warning)
|
|
481
|
+
background: color-mix(in srgb, var(--color-warning) 14%, transparent);
|
|
482
482
|
}
|
|
483
483
|
.wre-scope-rail-tint-insert {
|
|
484
|
-
background: color-mix(in srgb, var(--color-insert)
|
|
484
|
+
background: color-mix(in srgb, var(--color-insert) 12%, transparent);
|
|
485
485
|
}
|
|
486
486
|
.wre-scope-rail-tint-secondary {
|
|
487
|
-
background: color-mix(in srgb, var(--color-secondary)
|
|
487
|
+
background: color-mix(in srgb, var(--color-secondary) 9%, transparent);
|
|
488
488
|
}
|
|
489
489
|
.wre-scope-rail-tint-danger {
|
|
490
|
-
background: color-mix(in srgb, var(--color-danger)
|
|
490
|
+
background: color-mix(in srgb, var(--color-danger) 14%, transparent);
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
/* §3.7 canonical scope families */
|
|
@@ -543,22 +543,22 @@
|
|
|
543
543
|
/*
|
|
544
544
|
* ─── Scope rail stripe ───
|
|
545
545
|
*
|
|
546
|
-
* The rail stripe is the rest-state representation of a scope: a
|
|
546
|
+
* The rail stripe is the rest-state representation of a scope: a 4px
|
|
547
547
|
* color stripe in the gutter lane. Posture color comes from the
|
|
548
548
|
* accent/warning/insert/secondary/danger tokens. Hover widens the
|
|
549
549
|
* stripe via transform (zero layout cost) and reveals the label pill.
|
|
550
550
|
*/
|
|
551
551
|
.wre-scope-rail-stripe {
|
|
552
552
|
position: absolute;
|
|
553
|
-
width:
|
|
554
|
-
border-radius:
|
|
553
|
+
width: 4px;
|
|
554
|
+
border-radius: 2px;
|
|
555
555
|
background: currentColor;
|
|
556
556
|
pointer-events: auto;
|
|
557
557
|
cursor: pointer;
|
|
558
558
|
z-index: 1;
|
|
559
559
|
transform-origin: left center;
|
|
560
560
|
transition: transform 120ms ease-out, opacity 120ms ease-out;
|
|
561
|
-
opacity: 0.
|
|
561
|
+
opacity: 0.75;
|
|
562
562
|
/* Reset button defaults. */
|
|
563
563
|
border: none;
|
|
564
564
|
padding: 0;
|
|
@@ -568,22 +568,16 @@
|
|
|
568
568
|
background-clip: padding-box;
|
|
569
569
|
}
|
|
570
570
|
|
|
571
|
-
.wre-scope-rail-stripe::before {
|
|
572
|
-
content: "";
|
|
573
|
-
position: absolute;
|
|
574
|
-
inset: -5px -10px;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
571
|
.wre-scope-rail-stripe:hover,
|
|
578
572
|
.wre-scope-rail-stripe:focus-visible {
|
|
579
|
-
transform: scaleX(1.
|
|
573
|
+
transform: scaleX(1.5);
|
|
580
574
|
opacity: 1;
|
|
581
575
|
outline: none;
|
|
582
576
|
}
|
|
583
577
|
|
|
584
578
|
.wre-scope-rail-stripe-active {
|
|
585
579
|
opacity: 1;
|
|
586
|
-
transform: scaleX(1.
|
|
580
|
+
transform: scaleX(1.75);
|
|
587
581
|
}
|
|
588
582
|
|
|
589
583
|
.wre-scope-rail-stripe.wre-scope-rail-label-accent { color: var(--color-accent); }
|
|
@@ -629,8 +623,6 @@
|
|
|
629
623
|
pointer-events: none;
|
|
630
624
|
transition: opacity 140ms ease-out, transform 140ms ease-out;
|
|
631
625
|
transform: translateX(-4px);
|
|
632
|
-
margin: 0;
|
|
633
|
-
font-family: inherit;
|
|
634
626
|
}
|
|
635
627
|
|
|
636
628
|
.wre-scope-rail-stripe:hover + .wre-scope-rail-label,
|
|
@@ -700,12 +692,7 @@
|
|
|
700
692
|
}
|
|
701
693
|
|
|
702
694
|
.wre-scope-rail-label-active {
|
|
703
|
-
|
|
704
|
-
pointer-events: auto;
|
|
705
|
-
transform: translateX(0);
|
|
706
|
-
box-shadow:
|
|
707
|
-
0 0 0 1px color-mix(in srgb, currentColor 42%, transparent),
|
|
708
|
-
0 8px 22px color-mix(in srgb, currentColor 14%, transparent);
|
|
695
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent);
|
|
709
696
|
}
|
|
710
697
|
|
|
711
698
|
.wre-scope-rail-icon {
|
|
@@ -68,11 +68,6 @@ import { useLayoutFacetRenderSignal } from "./review-workspace/use-layout-facet-
|
|
|
68
68
|
import { useScrollRootCapture } from "./review-workspace/use-scroll-root-capture.ts";
|
|
69
69
|
import { usePmSurfaceCapture } from "./review-workspace/use-pm-surface-capture.ts";
|
|
70
70
|
import { TwReviewWorkspaceNavigator } from "./review-workspace/tw-review-workspace-navigator.tsx";
|
|
71
|
-
import {
|
|
72
|
-
createDefaultWorkflowScopeLayerKeys,
|
|
73
|
-
workflowScopePosturesForLayerKeys,
|
|
74
|
-
type WorkflowScopeLayerKey,
|
|
75
|
-
} from "./workflow-scope-layers";
|
|
76
71
|
|
|
77
72
|
export {
|
|
78
73
|
FRAME_PX_PER_TWIP_AT_96DPI,
|
|
@@ -179,13 +174,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
179
174
|
useRef<TwWorkspaceChromeHostController | null>(null);
|
|
180
175
|
const [shellModeOverride, setShellModeOverride] =
|
|
181
176
|
useState<ShellHeaderMode | null>(null);
|
|
182
|
-
const [workflowLayerFilters, setWorkflowLayerFilters] = useState<
|
|
183
|
-
ReadonlySet<WorkflowScopeLayerKey>
|
|
184
|
-
>(createDefaultWorkflowScopeLayerKeys);
|
|
185
|
-
const visibleScopePostures = useMemo(
|
|
186
|
-
() => workflowScopePosturesForLayerKeys(workflowLayerFilters),
|
|
187
|
-
[workflowLayerFilters],
|
|
188
|
-
);
|
|
189
177
|
// P8.11 — body slot wrapping `{props.document}` (the PM surface) + scroll
|
|
190
178
|
// root ref. The chrome layer's `TwPageStackChromeLayer` needs both to
|
|
191
179
|
// measure per-page rects and to reparent PM's DOM node across band
|
|
@@ -1178,7 +1166,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
1178
1166
|
onSetColumnWidth={props.onSetColumnWidth}
|
|
1179
1167
|
onSetRowHeight={props.onSetRowHeight}
|
|
1180
1168
|
activeScopeId={activeScopeId}
|
|
1181
|
-
visibleScopePostures={visibleScopePostures}
|
|
1182
1169
|
editorRole={viewState.editorRole}
|
|
1183
1170
|
scopeCardScopeTagEditor={props.scopeCardScopeTagEditor}
|
|
1184
1171
|
onScopeStripeClick={handleScopeStripeClick}
|
|
@@ -1285,8 +1272,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
1285
1272
|
// `getAllScopeRailSegments` (methods removed in v40 / Slice 4C).
|
|
1286
1273
|
scopeRailSegments: props.workflowFacet?.getAllRailSegments() ?? [],
|
|
1287
1274
|
activeScopeId,
|
|
1288
|
-
workflowLayerFilters,
|
|
1289
|
-
onWorkflowLayerFiltersChange: setWorkflowLayerFilters,
|
|
1290
1275
|
onOpenScope: (segment) => {
|
|
1291
1276
|
handleScopeStripeClick({ scopeId: segment.scopeId });
|
|
1292
1277
|
},
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { ScopeRailPosture } from "../api/public-types";
|
|
2
|
-
|
|
3
|
-
export type WorkflowScopeLayerKey =
|
|
4
|
-
| "edit"
|
|
5
|
-
| "suggest"
|
|
6
|
-
| "comment"
|
|
7
|
-
| "view"
|
|
8
|
-
| "candidate"
|
|
9
|
-
| "blocked";
|
|
10
|
-
|
|
11
|
-
export interface WorkflowScopeLayerFilter {
|
|
12
|
-
key: WorkflowScopeLayerKey;
|
|
13
|
-
label: string;
|
|
14
|
-
postures: readonly ScopeRailPosture[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const WORKFLOW_SCOPE_LAYER_FILTERS: readonly WorkflowScopeLayerFilter[] = [
|
|
18
|
-
{ key: "edit", label: "Edit", postures: ["edit"] },
|
|
19
|
-
{ key: "suggest", label: "Suggest", postures: ["suggest"] },
|
|
20
|
-
{ key: "comment", label: "Comment", postures: ["comment"] },
|
|
21
|
-
{ key: "view", label: "Review", postures: ["view"] },
|
|
22
|
-
{ key: "candidate", label: "Scheduled", postures: ["candidate"] },
|
|
23
|
-
{ key: "blocked", label: "Blocked", postures: ["preserve-only", "blocked-import"] },
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
export function createDefaultWorkflowScopeLayerKeys(): ReadonlySet<WorkflowScopeLayerKey> {
|
|
27
|
-
return new Set(WORKFLOW_SCOPE_LAYER_FILTERS.map((filter) => filter.key));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function workflowScopeLayerKeyForPosture(
|
|
31
|
-
posture: ScopeRailPosture,
|
|
32
|
-
): WorkflowScopeLayerKey {
|
|
33
|
-
if (posture === "preserve-only" || posture === "blocked-import") {
|
|
34
|
-
return "blocked";
|
|
35
|
-
}
|
|
36
|
-
return posture;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function isWorkflowScopePostureVisible(
|
|
40
|
-
posture: ScopeRailPosture,
|
|
41
|
-
enabledLayers: ReadonlySet<WorkflowScopeLayerKey>,
|
|
42
|
-
): boolean {
|
|
43
|
-
return enabledLayers.has(workflowScopeLayerKeyForPosture(posture));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function workflowScopePosturesForLayerKeys(
|
|
47
|
-
enabledLayers: ReadonlySet<WorkflowScopeLayerKey>,
|
|
48
|
-
): ReadonlySet<ScopeRailPosture> {
|
|
49
|
-
const postures = new Set<ScopeRailPosture>();
|
|
50
|
-
for (const filter of WORKFLOW_SCOPE_LAYER_FILTERS) {
|
|
51
|
-
if (!enabledLayers.has(filter.key)) continue;
|
|
52
|
-
for (const posture of filter.postures) {
|
|
53
|
-
postures.add(posture);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return postures;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function toggleWorkflowScopeLayerKey(
|
|
60
|
-
current: ReadonlySet<WorkflowScopeLayerKey>,
|
|
61
|
-
key: WorkflowScopeLayerKey,
|
|
62
|
-
): ReadonlySet<WorkflowScopeLayerKey> {
|
|
63
|
-
const next = new Set(current);
|
|
64
|
-
if (next.has(key)) {
|
|
65
|
-
next.delete(key);
|
|
66
|
-
} else {
|
|
67
|
-
next.add(key);
|
|
68
|
-
}
|
|
69
|
-
return next;
|
|
70
|
-
}
|