@beyondwork/docx-react-component 1.0.81 → 1.0.83
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 +2 -1
- 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/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 +243 -2
- package/src/ui-tailwind/chrome/editor-action-registry.ts +220 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +59 -35
- 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/use-chrome-policy.ts +32 -6
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +273 -24
- package/src/ui-tailwind/theme/editor-theme.css +3 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +21 -0
- package/src/ui-tailwind/tw-review-workspace.tsx +4 -0
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import type { RuntimeRenderSnapshot } from "../../api/public-types.ts";
|
|
3
|
+
import type { GeometryFacet, RuntimeRenderSnapshot } from "../../api/public-types.ts";
|
|
4
4
|
import {
|
|
5
|
+
resolveVisibleBlockRangesFromPageOffsets,
|
|
6
|
+
resolveVisibleBlockRangesFromPageRange,
|
|
7
|
+
resolveVisiblePageIndexRangeFromViewport,
|
|
5
8
|
useVisibleBlockRange,
|
|
6
9
|
useVisibleBlockRanges,
|
|
7
10
|
useVisiblePageIndexRange,
|
|
11
|
+
type VisiblePageIndexRange,
|
|
8
12
|
} from "../page-stack/use-visible-block-range.ts";
|
|
9
13
|
|
|
10
14
|
export interface UsePageMarkersOptions {
|
|
11
15
|
pageStackScrollRoot: HTMLElement | null;
|
|
12
16
|
snapshot: RuntimeRenderSnapshot;
|
|
13
17
|
layoutFacet?: import("../../runtime/layout/index.ts").WordReviewEditorLayoutFacet;
|
|
18
|
+
geometryFacet?: GeometryFacet;
|
|
19
|
+
renderFrameRevision?: number;
|
|
20
|
+
/** CSS zoom applied to the document surface; scroll pixels are normalized by this. */
|
|
21
|
+
viewportScale?: number;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
export interface PageMarkersResult {
|
|
@@ -27,6 +35,151 @@ export interface PageMarkersResult {
|
|
|
27
35
|
visiblePageIndexRange: ReturnType<typeof useVisiblePageIndexRange>;
|
|
28
36
|
}
|
|
29
37
|
|
|
38
|
+
function pageRangeEqual(
|
|
39
|
+
a: VisiblePageIndexRange | null,
|
|
40
|
+
b: VisiblePageIndexRange | null,
|
|
41
|
+
): boolean {
|
|
42
|
+
if (a === b) return true;
|
|
43
|
+
if (!a || !b) return false;
|
|
44
|
+
return a.start === b.start && a.end === b.end;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readMarkerSignature(el: HTMLElement): string {
|
|
48
|
+
return [
|
|
49
|
+
el.getAttribute("data-page-frame") ?? "",
|
|
50
|
+
el.getAttribute("data-page-first-block-index") ?? "",
|
|
51
|
+
el.getAttribute("data-page-last-block-index") ?? "",
|
|
52
|
+
].join(":");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isSyntheticPageZeroMarker(el: HTMLElement): boolean {
|
|
56
|
+
return !el.isConnected && el.getAttribute("data-page-frame") === "0";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function pageMarkersEqual(
|
|
60
|
+
a: readonly HTMLElement[],
|
|
61
|
+
b: readonly HTMLElement[],
|
|
62
|
+
): boolean {
|
|
63
|
+
if (a === b) return true;
|
|
64
|
+
if (a.length !== b.length) return false;
|
|
65
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
66
|
+
const left = a[i]!;
|
|
67
|
+
const right = b[i]!;
|
|
68
|
+
if (left === right) continue;
|
|
69
|
+
if (
|
|
70
|
+
isSyntheticPageZeroMarker(left) &&
|
|
71
|
+
isSyntheticPageZeroMarker(right) &&
|
|
72
|
+
readMarkerSignature(left) === readMarkerSignature(right)
|
|
73
|
+
) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function useGeometryVisiblePageIndexRange(input: {
|
|
82
|
+
scrollRoot: HTMLElement | null;
|
|
83
|
+
geometryFacet?: GeometryFacet;
|
|
84
|
+
layoutFacet?: import("../../runtime/layout/index.ts").WordReviewEditorLayoutFacet;
|
|
85
|
+
pageMarkerCount: number;
|
|
86
|
+
overscanPages: number;
|
|
87
|
+
renderFrameRevision?: number;
|
|
88
|
+
viewportScale?: number;
|
|
89
|
+
}): VisiblePageIndexRange | null {
|
|
90
|
+
const {
|
|
91
|
+
scrollRoot,
|
|
92
|
+
geometryFacet,
|
|
93
|
+
layoutFacet,
|
|
94
|
+
pageMarkerCount,
|
|
95
|
+
overscanPages,
|
|
96
|
+
renderFrameRevision,
|
|
97
|
+
viewportScale,
|
|
98
|
+
} = input;
|
|
99
|
+
const [range, setRange] = useState<VisiblePageIndexRange | null>(null);
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!scrollRoot || !geometryFacet) {
|
|
103
|
+
setRange((prev) => (prev === null ? prev : null));
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const runtime = scrollRoot.ownerDocument?.defaultView as
|
|
108
|
+
| (Window & {
|
|
109
|
+
requestAnimationFrame?: (cb: () => void) => number;
|
|
110
|
+
cancelAnimationFrame?: (handle: number) => void;
|
|
111
|
+
ResizeObserver?: typeof ResizeObserver;
|
|
112
|
+
})
|
|
113
|
+
| null;
|
|
114
|
+
let rafHandle: number | null = null;
|
|
115
|
+
let disposed = false;
|
|
116
|
+
|
|
117
|
+
const publish = () => {
|
|
118
|
+
if (disposed) return;
|
|
119
|
+
const pageCount = layoutFacet?.getPageCount() ?? pageMarkerCount;
|
|
120
|
+
const scale =
|
|
121
|
+
typeof viewportScale === "number" &&
|
|
122
|
+
Number.isFinite(viewportScale) &&
|
|
123
|
+
viewportScale > 0
|
|
124
|
+
? viewportScale
|
|
125
|
+
: 1;
|
|
126
|
+
const next = resolveVisiblePageIndexRangeFromViewport({
|
|
127
|
+
pageCount,
|
|
128
|
+
viewportTopPx: scrollRoot.scrollTop / scale,
|
|
129
|
+
viewportHeightPx: scrollRoot.clientHeight / scale,
|
|
130
|
+
overscanPages,
|
|
131
|
+
getPageFrame: (pageIndex) => {
|
|
132
|
+
const page = geometryFacet.getPage(pageIndex);
|
|
133
|
+
return page ? page.frame : null;
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
setRange((prev) => (pageRangeEqual(prev, next) ? prev : next));
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const schedule = () => {
|
|
140
|
+
if (disposed || rafHandle !== null) return;
|
|
141
|
+
const raf = runtime?.requestAnimationFrame;
|
|
142
|
+
if (!raf) {
|
|
143
|
+
publish();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
rafHandle = raf.call(runtime, () => {
|
|
147
|
+
rafHandle = null;
|
|
148
|
+
publish();
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
publish();
|
|
153
|
+
scrollRoot.addEventListener("scroll", schedule, { passive: true });
|
|
154
|
+
|
|
155
|
+
const ResizeObserverCtor = runtime?.ResizeObserver;
|
|
156
|
+
const resizeObserver = ResizeObserverCtor
|
|
157
|
+
? new ResizeObserverCtor(schedule)
|
|
158
|
+
: null;
|
|
159
|
+
resizeObserver?.observe(scrollRoot);
|
|
160
|
+
|
|
161
|
+
return () => {
|
|
162
|
+
disposed = true;
|
|
163
|
+
scrollRoot.removeEventListener("scroll", schedule);
|
|
164
|
+
resizeObserver?.disconnect();
|
|
165
|
+
if (rafHandle !== null && runtime?.cancelAnimationFrame) {
|
|
166
|
+
runtime.cancelAnimationFrame(rafHandle);
|
|
167
|
+
rafHandle = null;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}, [
|
|
171
|
+
geometryFacet,
|
|
172
|
+
layoutFacet,
|
|
173
|
+
overscanPages,
|
|
174
|
+
pageMarkerCount,
|
|
175
|
+
renderFrameRevision,
|
|
176
|
+
scrollRoot,
|
|
177
|
+
viewportScale,
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
return range;
|
|
181
|
+
}
|
|
182
|
+
|
|
30
183
|
/**
|
|
31
184
|
* L7 Phase 2 Task 2.2.4a — viewport-scroll wiring.
|
|
32
185
|
*
|
|
@@ -46,17 +199,29 @@ export interface PageMarkersResult {
|
|
|
46
199
|
* `data-page-frame="N+1"`, so page 0 has no widget before it.
|
|
47
200
|
*/
|
|
48
201
|
export function usePageMarkers(options: UsePageMarkersOptions): PageMarkersResult {
|
|
49
|
-
const {
|
|
202
|
+
const {
|
|
203
|
+
pageStackScrollRoot,
|
|
204
|
+
snapshot,
|
|
205
|
+
layoutFacet,
|
|
206
|
+
geometryFacet,
|
|
207
|
+
renderFrameRevision,
|
|
208
|
+
viewportScale,
|
|
209
|
+
} = options;
|
|
210
|
+
const overscanPages = 2;
|
|
50
211
|
|
|
51
212
|
const [pageMarkers, setPageMarkers] = useState<readonly HTMLElement[]>([]);
|
|
52
213
|
|
|
53
214
|
useEffect(() => {
|
|
54
215
|
const root = pageStackScrollRoot;
|
|
55
216
|
if (!root) {
|
|
56
|
-
setPageMarkers([]);
|
|
217
|
+
setPageMarkers((prev) => (prev.length === 0 ? prev : []));
|
|
57
218
|
return undefined;
|
|
58
219
|
}
|
|
59
|
-
const
|
|
220
|
+
const view = root.ownerDocument?.defaultView;
|
|
221
|
+
let rafHandle: number | null = null;
|
|
222
|
+
let disposed = false;
|
|
223
|
+
|
|
224
|
+
const collectMarkers = (): readonly HTMLElement[] => {
|
|
60
225
|
const found = Array.from(root.querySelectorAll<HTMLElement>("[data-page-frame]"));
|
|
61
226
|
const hasPage0 = found.some(
|
|
62
227
|
(el) => el.getAttribute("data-page-frame") === "0",
|
|
@@ -76,17 +241,42 @@ export function usePageMarkers(options: UsePageMarkersOptions): PageMarkersResul
|
|
|
76
241
|
synth.setAttribute("data-page-frame", "0");
|
|
77
242
|
synth.setAttribute("data-page-first-block-index", "0");
|
|
78
243
|
synth.setAttribute("data-page-last-block-index", String(Math.max(0, page0Last)));
|
|
79
|
-
|
|
80
|
-
} else {
|
|
81
|
-
setPageMarkers(found);
|
|
244
|
+
return [synth, ...found];
|
|
82
245
|
}
|
|
246
|
+
return found;
|
|
83
247
|
};
|
|
84
|
-
|
|
85
|
-
const
|
|
248
|
+
|
|
249
|
+
const publish = () => {
|
|
250
|
+
if (disposed) return;
|
|
251
|
+
const next = collectMarkers();
|
|
252
|
+
setPageMarkers((prev) => (pageMarkersEqual(prev, next) ? prev : next));
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const schedulePublish = () => {
|
|
256
|
+
if (disposed || rafHandle !== null) return;
|
|
257
|
+
const raf = view?.requestAnimationFrame;
|
|
258
|
+
if (!raf) {
|
|
259
|
+
publish();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
rafHandle = raf.call(view, () => {
|
|
263
|
+
rafHandle = null;
|
|
264
|
+
publish();
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
publish();
|
|
86
269
|
if (!view?.MutationObserver) return undefined;
|
|
87
|
-
const mo = new view.MutationObserver(
|
|
270
|
+
const mo = new view.MutationObserver(schedulePublish);
|
|
88
271
|
mo.observe(root, { childList: true, subtree: true });
|
|
89
|
-
return () =>
|
|
272
|
+
return () => {
|
|
273
|
+
disposed = true;
|
|
274
|
+
mo.disconnect();
|
|
275
|
+
if (rafHandle !== null && view.cancelAnimationFrame) {
|
|
276
|
+
view.cancelAnimationFrame(rafHandle);
|
|
277
|
+
rafHandle = null;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
90
280
|
// Re-run when the scroll root changes OR when a new snapshot lands
|
|
91
281
|
// (which may have a different page count and new widgets in the PM DOM).
|
|
92
282
|
// NOTE: snapshot.surface is intentionally excluded — its reference changes on
|
|
@@ -108,13 +298,83 @@ export function usePageMarkers(options: UsePageMarkersOptions): PageMarkersResul
|
|
|
108
298
|
return null;
|
|
109
299
|
}, [snapshot.selection, snapshot.surface]);
|
|
110
300
|
|
|
111
|
-
const
|
|
301
|
+
const markerVisibleBlockRanges = useVisibleBlockRanges({
|
|
112
302
|
pageMarkers,
|
|
113
|
-
overscanPages
|
|
303
|
+
overscanPages,
|
|
114
304
|
selectionBlockIndex,
|
|
115
305
|
totalBlockCount: snapshot.surface?.blocks.length ?? 0,
|
|
116
306
|
});
|
|
117
307
|
|
|
308
|
+
// Marker IO is retained as the cold-open/degraded path, but the warm editor
|
|
309
|
+
// path derives the page window from render-kernel geometry plus viewport
|
|
310
|
+
// scroll. Boundary widgets only exist in the inter-page gaps; using them as
|
|
311
|
+
// primary visibility sentinels makes culling disappear while the user is in
|
|
312
|
+
// the middle of a page.
|
|
313
|
+
const markerVisiblePageIndexRange = useVisiblePageIndexRange({
|
|
314
|
+
pageMarkers,
|
|
315
|
+
overscanPages,
|
|
316
|
+
});
|
|
317
|
+
const geometryVisiblePageIndexRange = useGeometryVisiblePageIndexRange({
|
|
318
|
+
scrollRoot: pageStackScrollRoot,
|
|
319
|
+
geometryFacet,
|
|
320
|
+
layoutFacet,
|
|
321
|
+
pageMarkerCount: pageMarkers.length,
|
|
322
|
+
overscanPages,
|
|
323
|
+
renderFrameRevision,
|
|
324
|
+
viewportScale,
|
|
325
|
+
});
|
|
326
|
+
const visiblePageIndexRange =
|
|
327
|
+
geometryVisiblePageIndexRange ?? markerVisiblePageIndexRange;
|
|
328
|
+
|
|
329
|
+
const visibleBlockRangesFromPageOffsets = useMemo(
|
|
330
|
+
() =>
|
|
331
|
+
visiblePageIndexRange && layoutFacet
|
|
332
|
+
? resolveVisibleBlockRangesFromPageOffsets({
|
|
333
|
+
blocks: snapshot.surface?.blocks ?? [],
|
|
334
|
+
pageCount: layoutFacet.getPageCount(),
|
|
335
|
+
visiblePageIndexRange,
|
|
336
|
+
selectionBlockIndex,
|
|
337
|
+
totalBlockCount: snapshot.surface?.blocks.length ?? 0,
|
|
338
|
+
getPageOffsets: (pageIndex) => {
|
|
339
|
+
const page = layoutFacet.getPage(pageIndex);
|
|
340
|
+
return page
|
|
341
|
+
? { startOffset: page.startOffset, endOffset: page.endOffset }
|
|
342
|
+
: null;
|
|
343
|
+
},
|
|
344
|
+
})
|
|
345
|
+
: null,
|
|
346
|
+
[
|
|
347
|
+
layoutFacet,
|
|
348
|
+
selectionBlockIndex,
|
|
349
|
+
snapshot.surface?.blocks,
|
|
350
|
+
snapshot.surface?.blocks.length,
|
|
351
|
+
visiblePageIndexRange,
|
|
352
|
+
],
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
const visibleBlockRangesFromPageRange = useMemo(
|
|
356
|
+
() =>
|
|
357
|
+
visiblePageIndexRange
|
|
358
|
+
? resolveVisibleBlockRangesFromPageRange({
|
|
359
|
+
pageMarkers,
|
|
360
|
+
visiblePageIndexRange,
|
|
361
|
+
selectionBlockIndex,
|
|
362
|
+
totalBlockCount: snapshot.surface?.blocks.length ?? 0,
|
|
363
|
+
})
|
|
364
|
+
: null,
|
|
365
|
+
[
|
|
366
|
+
pageMarkers,
|
|
367
|
+
selectionBlockIndex,
|
|
368
|
+
snapshot.surface?.blocks.length,
|
|
369
|
+
visiblePageIndexRange,
|
|
370
|
+
],
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
const visibleBlockRanges =
|
|
374
|
+
visibleBlockRangesFromPageOffsets ??
|
|
375
|
+
visibleBlockRangesFromPageRange ??
|
|
376
|
+
markerVisibleBlockRanges;
|
|
377
|
+
|
|
118
378
|
// Bounding hull of the disjoint ranges for the back-compat `visibleBlockRange`
|
|
119
379
|
// result field + for the effect's dep key (below).
|
|
120
380
|
const visibleBlockRange = useMemo(() => {
|
|
@@ -128,17 +388,6 @@ export function usePageMarkers(options: UsePageMarkersOptions): PageMarkersResul
|
|
|
128
388
|
return { start, end };
|
|
129
389
|
}, [visibleBlockRanges]);
|
|
130
390
|
|
|
131
|
-
// L7 Phase 2.8 — viewport cull for `TwPageStackChromeLayer`. Returns
|
|
132
|
-
// `null` while the IntersectionObserver hasn't reported yet; the chrome
|
|
133
|
-
// layer treats null as "render every page" so first paint is
|
|
134
|
-
// unaffected. Once the observer fires, the chrome layer only mounts
|
|
135
|
-
// bands for pages inside `[start, end)` plus overscan, eliminating the
|
|
136
|
-
// measured 412 ms Layout + 229 ms Pre-paint cost on 138-pp extra-large.
|
|
137
|
-
const visiblePageIndexRange = useVisiblePageIndexRange({
|
|
138
|
-
pageMarkers,
|
|
139
|
-
overscanPages: 2,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
391
|
// Stable fingerprint of the current disjoint-range set; used as the effect
|
|
143
392
|
// dep so identity-preserving recomputes (same intervals, new memo array
|
|
144
393
|
// reference) don't re-fire the viewport refresh.
|
|
@@ -83,12 +83,10 @@
|
|
|
83
83
|
* The outer `.wre-page-chrome` wrapper paints this; the inner paper
|
|
84
84
|
* frame paints the white `--color-page-bg` on top.
|
|
85
85
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* earlier tone was close enough that the paper edges blurred against
|
|
89
|
-
* the canvas on high-brightness screens).
|
|
86
|
+
* Editor-experience pass (2026-04-23): keep the canvas quiet so paper
|
|
87
|
+
* edges come from the card border + shadow, not a high-contrast gray field.
|
|
90
88
|
*/
|
|
91
|
-
--color-workspace-canvas: #
|
|
89
|
+
--color-workspace-canvas: #f7f8fa;
|
|
92
90
|
|
|
93
91
|
/*
|
|
94
92
|
* ─── Radius tokens (balanced 10 / 8 / 4 / 2) ───
|
|
@@ -139,6 +139,7 @@ export function TwRoleActionRegion(
|
|
|
139
139
|
for (const id of order) {
|
|
140
140
|
const entry = props.policy.toolbar[id];
|
|
141
141
|
if (!entry?.visible) continue;
|
|
142
|
+
if (!isRoleActionRenderable(id, props)) continue;
|
|
142
143
|
if (entry.placement === "overflow") {
|
|
143
144
|
overflowIds.push(id);
|
|
144
145
|
} else if (entry.placement === "inline") {
|
|
@@ -171,6 +172,26 @@ export function TwRoleActionRegion(
|
|
|
171
172
|
);
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
function isRoleActionRenderable(
|
|
176
|
+
id: ToolbarChromeItemId,
|
|
177
|
+
props: TwRoleActionRegionProps,
|
|
178
|
+
): boolean {
|
|
179
|
+
const reviewQueueTotal = props.reviewQueue?.totalCount ?? 0;
|
|
180
|
+
switch (id) {
|
|
181
|
+
case "review-queue-prev":
|
|
182
|
+
case "review-queue-next":
|
|
183
|
+
case "review-queue-counts":
|
|
184
|
+
case "review-queue-active-label":
|
|
185
|
+
case "review-accept":
|
|
186
|
+
case "review-reject":
|
|
187
|
+
case "review-accept-all":
|
|
188
|
+
case "review-reject-all":
|
|
189
|
+
return reviewQueueTotal > 0;
|
|
190
|
+
default:
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
174
195
|
interface RoleActionButtonProps {
|
|
175
196
|
id: ToolbarChromeItemId;
|
|
176
197
|
compact: boolean;
|
|
@@ -357,6 +357,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
357
357
|
role: viewState.editorRole,
|
|
358
358
|
hasSidebarPanelAccess,
|
|
359
359
|
effectiveSelectionMode,
|
|
360
|
+
activeStoryKind: snapshot.activeStory.kind,
|
|
360
361
|
});
|
|
361
362
|
|
|
362
363
|
// L7 Phase 2 Task 2.2.4a — viewport-scroll wiring. Page marker
|
|
@@ -366,6 +367,9 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
366
367
|
pageStackScrollRoot,
|
|
367
368
|
snapshot,
|
|
368
369
|
layoutFacet: props.layoutFacet,
|
|
370
|
+
geometryFacet: props.geometryFacet,
|
|
371
|
+
renderFrameRevision,
|
|
372
|
+
viewportScale: zoomScale,
|
|
369
373
|
});
|
|
370
374
|
|
|
371
375
|
const { dismissSelectionToolbar, runWithSelectionToolbarDismiss } =
|