@beyondwork/docx-react-component 1.0.42 → 1.0.43
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 +30 -41
- package/src/api/editor-state-types.ts +110 -0
- package/src/api/public-types.ts +194 -1
- package/src/core/commands/index.ts +33 -8
- package/src/core/search/search-text.ts +15 -2
- package/src/index.ts +13 -0
- package/src/io/docx-session.ts +672 -2
- package/src/io/load-scheduler.ts +230 -0
- package/src/io/normalize/normalize-text.ts +83 -0
- package/src/io/ooxml/workflow-payload-validator.ts +97 -1
- package/src/io/ooxml/workflow-payload.ts +172 -1
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/document-runtime.ts +364 -36
- package/src/runtime/editor-state-channel.ts +544 -0
- package/src/runtime/editor-state-integration.ts +217 -0
- package/src/runtime/layout/index.ts +2 -0
- package/src/runtime/layout/inert-layout-facet.ts +2 -0
- package/src/runtime/layout/layout-engine-instance.ts +17 -2
- package/src/runtime/layout/paginated-layout-engine.ts +211 -14
- package/src/runtime/layout/public-facet.ts +400 -1
- package/src/runtime/perf-counters.ts +28 -0
- package/src/runtime/render/render-frame-types.ts +17 -0
- package/src/runtime/render/render-kernel.ts +172 -29
- package/src/runtime/surface-projection.ts +10 -5
- package/src/runtime/workflow-markup.ts +71 -16
- package/src/ui/WordReviewEditor.tsx +67 -45
- package/src/ui/editor-command-bag.ts +14 -0
- package/src/ui/editor-runtime-boundary.ts +110 -11
- package/src/ui/editor-shell-view.tsx +10 -0
- package/src/ui/editor-surface-controller.tsx +5 -0
- package/src/ui/headless/selection-helpers.ts +10 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +62 -2
- package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +281 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +48 -0
- package/src/ui-tailwind/editor-surface/paste-plain-text.ts +72 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +118 -8
- package/src/ui-tailwind/editor-surface/pm-schema.ts +152 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +35 -7
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +265 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +8 -255
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +9 -0
- package/src/ui-tailwind/index.ts +5 -1
- package/src/ui-tailwind/page-stack/tw-endnote-area.tsx +57 -0
- package/src/ui-tailwind/page-stack/tw-footnote-area.tsx +71 -0
- package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +73 -0
- package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +74 -0
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +477 -0
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +374 -0
- package/src/ui-tailwind/review/comment-markdown-renderer.tsx +155 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +77 -16
- package/src/ui-tailwind/tw-review-workspace.tsx +172 -94
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
ActiveListContext,
|
|
18
18
|
CommentSidebarThreadSnapshot,
|
|
19
19
|
DocumentNavigationSnapshot,
|
|
20
|
+
EditorStoryTarget,
|
|
20
21
|
EditorViewStateSnapshot,
|
|
21
22
|
FormattingStateSnapshot,
|
|
22
23
|
FormattingAlignment,
|
|
@@ -157,6 +158,16 @@ export interface TwReviewWorkspaceProps {
|
|
|
157
158
|
interactionGuardSnapshot?: InteractionGuardSnapshot;
|
|
158
159
|
chromePreset?: WordReviewEditorChromePreset;
|
|
159
160
|
chromeOptions?: Partial<WordReviewEditorChromeOptions>;
|
|
161
|
+
/** P9g — live collab session for the `"collab"` chrome preset's top nav. */
|
|
162
|
+
collabSession?: import("../runtime/collab-session.ts").CollabSession;
|
|
163
|
+
collabTransportStatus?: import("../api/awareness-identity-types.ts").TransportStatus;
|
|
164
|
+
collabActorId?: string;
|
|
165
|
+
collabSendBaseline?: {
|
|
166
|
+
originDocumentId: string;
|
|
167
|
+
originPayloadId: string;
|
|
168
|
+
originContentHash: string;
|
|
169
|
+
payloadXml: string;
|
|
170
|
+
};
|
|
160
171
|
reviewQueue?: ReviewQueueSnapshot;
|
|
161
172
|
documentContextAnalytics?: RuntimeContextAnalyticsSnapshot | null;
|
|
162
173
|
selectionContextAnalytics?: RuntimeContextAnalyticsSnapshot | null;
|
|
@@ -278,7 +289,18 @@ export interface TwReviewWorkspaceProps {
|
|
|
278
289
|
onAcceptAllChanges?: () => void;
|
|
279
290
|
onRejectAllChanges?: () => void;
|
|
280
291
|
onCloseStory?: () => void;
|
|
292
|
+
/**
|
|
293
|
+
* @deprecated P8.11 — the workspace no longer renders a workspace-level
|
|
294
|
+
* header band with an "Edit header" button; per-page header bands route
|
|
295
|
+
* clicks via `onOpenStory` / `runtime.openStory` directly. The prop
|
|
296
|
+
* remains optional for one release so existing hosts continue to
|
|
297
|
+
* compile; supplying it emits a `console.warn` on mount.
|
|
298
|
+
*/
|
|
281
299
|
onOpenHeaderStory?: () => void;
|
|
300
|
+
/**
|
|
301
|
+
* @deprecated P8.11 — see `onOpenHeaderStory`. Footer variant of the
|
|
302
|
+
* same deprecation.
|
|
303
|
+
*/
|
|
282
304
|
onOpenFooterStory?: () => void;
|
|
283
305
|
/**
|
|
284
306
|
* Open a header/footer story for a specific page. Called when the user
|
|
@@ -288,6 +310,13 @@ export interface TwReviewWorkspaceProps {
|
|
|
288
310
|
*/
|
|
289
311
|
onOpenHeaderStoryForPage?: (pageIndex: number) => void;
|
|
290
312
|
onOpenFooterStoryForPage?: (pageIndex: number) => void;
|
|
313
|
+
/**
|
|
314
|
+
* P8.11 — fired when a per-page chrome band (header / footer) is
|
|
315
|
+
* clicked to promote it into the active editing surface. Wire to
|
|
316
|
+
* `runtime.openStory(target)`; the chrome layer's portal mechanism
|
|
317
|
+
* then reparents the PM surface into the matching band's active slot.
|
|
318
|
+
*/
|
|
319
|
+
onOpenStory?: (target: EditorStoryTarget) => void;
|
|
291
320
|
onSetParagraphIndentation?: (indentation: {
|
|
292
321
|
left?: number;
|
|
293
322
|
right?: number;
|
|
@@ -384,6 +413,17 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
384
413
|
} as TwReviewWorkspaceProps & EditorCommandBag;
|
|
385
414
|
const { snapshot, viewState } = props;
|
|
386
415
|
const selectionToolbarRootRef = useRef<HTMLDivElement>(null);
|
|
416
|
+
// P8.11 — body slot wrapping `{props.document}` (the PM surface) + scroll
|
|
417
|
+
// root ref. The chrome layer's `TwPageStackChromeLayer` needs both to
|
|
418
|
+
// measure per-page rects and to reparent PM's DOM node across band
|
|
419
|
+
// portals when `activeStory` changes. See comment near the body slot
|
|
420
|
+
// in the render tree below.
|
|
421
|
+
const bodySlotRef = useRef<HTMLDivElement | null>(null);
|
|
422
|
+
const scrollRootRef = useRef<HTMLDivElement | null>(null);
|
|
423
|
+
const [pmSurfaceElement, setPmSurfaceElement] =
|
|
424
|
+
useState<HTMLElement | null>(null);
|
|
425
|
+
const [pageStackScrollRoot, setPageStackScrollRoot] =
|
|
426
|
+
useState<HTMLElement | null>(null);
|
|
387
427
|
const caps = props.capabilities;
|
|
388
428
|
const isPageWorkspace = props.workspaceMode === "page";
|
|
389
429
|
const markupDisplay = props.markupDisplay;
|
|
@@ -640,8 +680,10 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
640
680
|
};
|
|
641
681
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
642
682
|
}, [props.layoutFacet, selectionPosition, snapshot.activeStory, renderFrameRevision]);
|
|
643
|
-
|
|
644
|
-
|
|
683
|
+
// P8.11 — `headerBandLabel` / `footerBandLabel` retired along with the
|
|
684
|
+
// workspace-level bands. Per-page bands in `TwPageStackChromeLayer`
|
|
685
|
+
// render the actual header / footer story blocks via
|
|
686
|
+
// `TwRegionBlockRenderer`, so a label row is no longer meaningful here.
|
|
645
687
|
const hidePageBorderForActiveEditing =
|
|
646
688
|
isPageWorkspace &&
|
|
647
689
|
snapshot.activeStory.kind === "main" &&
|
|
@@ -708,6 +750,88 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
708
750
|
}
|
|
709
751
|
}, [isPageWorkspace, snapshot.activeStory.kind]);
|
|
710
752
|
|
|
753
|
+
// P8.11 — capture the scroll-root DOM element on mount so the chrome
|
|
754
|
+
// overlay's `TwPageStackChromeLayer` can measure per-page rects and
|
|
755
|
+
// observe DOM mutations. `scrollRootRef` is attached to the existing
|
|
756
|
+
// `[data-wre-scroll-root]` container; rely on a mount effect rather
|
|
757
|
+
// than a ref callback so render-time state stays cheap.
|
|
758
|
+
useEffect(() => {
|
|
759
|
+
if (scrollRootRef.current !== pageStackScrollRoot) {
|
|
760
|
+
setPageStackScrollRoot(scrollRootRef.current);
|
|
761
|
+
}
|
|
762
|
+
// A `useEffect` re-runs after every render; the comparison guard
|
|
763
|
+
// keeps `setPageStackScrollRoot` from firing every commit. The
|
|
764
|
+
// scroll-root identity only changes when the component re-mounts.
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
// P8.11 — capture the PM surface DOM element. The ProseMirror surface
|
|
768
|
+
// mounts inside `bodySlotRef` on its own schedule (the PM constructor
|
|
769
|
+
// runs inside the `TwProseMirrorSurface` child component). A
|
|
770
|
+
// `MutationObserver` scoped to the body slot's `childList` picks up
|
|
771
|
+
// the PM root on first commit; once captured, the chrome layer owns
|
|
772
|
+
// reparent state (including portal-slot promotion), so we skip
|
|
773
|
+
// further updates unless PM is actually disconnected from the
|
|
774
|
+
// document (e.g. session/document swap tearing PM down).
|
|
775
|
+
useEffect(() => {
|
|
776
|
+
const slot = bodySlotRef.current;
|
|
777
|
+
if (!slot) return undefined;
|
|
778
|
+
|
|
779
|
+
// If we already hold a live reference, the chrome layer may have
|
|
780
|
+
// portaled PM into a per-page band — PM has left `bodySlotRef` but
|
|
781
|
+
// is still connected to the document. We keep the reference until
|
|
782
|
+
// the node is fully disconnected.
|
|
783
|
+
if (pmSurfaceElement && pmSurfaceElement.isConnected) {
|
|
784
|
+
return undefined;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const readPm = (): HTMLElement | null =>
|
|
788
|
+
slot.querySelector<HTMLElement>(".ProseMirror");
|
|
789
|
+
|
|
790
|
+
const current = readPm();
|
|
791
|
+
if (current !== pmSurfaceElement) {
|
|
792
|
+
setPmSurfaceElement(current);
|
|
793
|
+
}
|
|
794
|
+
const runtime = slot.ownerDocument?.defaultView as
|
|
795
|
+
| (Window & { MutationObserver?: typeof MutationObserver })
|
|
796
|
+
| null;
|
|
797
|
+
if (!runtime?.MutationObserver) return undefined;
|
|
798
|
+
const observer = new runtime.MutationObserver(() => {
|
|
799
|
+
const next = readPm();
|
|
800
|
+
if (next !== null && next !== pmSurfaceElement) {
|
|
801
|
+
setPmSurfaceElement(next);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
// `childList: true, subtree: false` — we only care when children of
|
|
805
|
+
// the body slot change (e.g. PM is added for the first time).
|
|
806
|
+
// Subtree mutations (PM's own edits) are not our concern and would
|
|
807
|
+
// fire on every keystroke.
|
|
808
|
+
observer.observe(slot, { childList: true, subtree: false });
|
|
809
|
+
return () => observer.disconnect();
|
|
810
|
+
}, [pmSurfaceElement]);
|
|
811
|
+
|
|
812
|
+
// P8.11 — deprecation shim for the legacy `onOpenHeaderStory` /
|
|
813
|
+
// `onOpenFooterStory` props. Per-page chrome bands route clicks via
|
|
814
|
+
// `onOpenStory` + `runtime.openStory` directly; the workspace-level
|
|
815
|
+
// bands that consumed these callbacks are gone. Kept optional for one
|
|
816
|
+
// release so existing hosts compile; a mount-time `console.warn` nudges
|
|
817
|
+
// them toward `onOpenStory`.
|
|
818
|
+
useEffect(() => {
|
|
819
|
+
if (props.onOpenHeaderStory) {
|
|
820
|
+
// eslint-disable-next-line no-console
|
|
821
|
+
console.warn(
|
|
822
|
+
"[docx-react-component] `onOpenHeaderStory` is deprecated. Per-page header bands route clicks via runtime.openStory directly. (P8)",
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
if (props.onOpenFooterStory) {
|
|
826
|
+
// eslint-disable-next-line no-console
|
|
827
|
+
console.warn(
|
|
828
|
+
"[docx-react-component] `onOpenFooterStory` is deprecated. Per-page footer bands route clicks via runtime.openStory directly. (P8)",
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
// Mount-once: we only want to nudge hosts at startup, not per render.
|
|
832
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
833
|
+
}, []);
|
|
834
|
+
|
|
711
835
|
useEffect(() => {
|
|
712
836
|
if (typeof window === "undefined") {
|
|
713
837
|
return;
|
|
@@ -818,6 +942,20 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
818
942
|
<div className="px-3 pt-3">
|
|
819
943
|
<ChromePresetToolbar
|
|
820
944
|
chromePreset={chromePreset}
|
|
945
|
+
{...(props.collabSession ? { collabSession: props.collabSession } : {})}
|
|
946
|
+
{...(props.collabTransportStatus
|
|
947
|
+
? { collabTransportStatus: props.collabTransportStatus }
|
|
948
|
+
: {})}
|
|
949
|
+
{...(props.activeCommentId !== undefined
|
|
950
|
+
? { activeCommentId: props.activeCommentId }
|
|
951
|
+
: {})}
|
|
952
|
+
{...(props.collabActorId !== undefined
|
|
953
|
+
? { collabActorId: props.collabActorId }
|
|
954
|
+
: {})}
|
|
955
|
+
{...(props.collabSendBaseline
|
|
956
|
+
? { collabSendBaseline: props.collabSendBaseline }
|
|
957
|
+
: {})}
|
|
958
|
+
chromeOptionsResolved={chromeOptions}
|
|
821
959
|
capabilities={caps}
|
|
822
960
|
compatibility={snapshot.compatibility}
|
|
823
961
|
warnings={snapshot.warnings}
|
|
@@ -1076,6 +1214,7 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
1076
1214
|
{/* Document column */}
|
|
1077
1215
|
<div className="flex flex-1 flex-col min-w-0">
|
|
1078
1216
|
<div
|
|
1217
|
+
ref={scrollRootRef}
|
|
1079
1218
|
className="flex-1 overflow-y-auto bg-surface"
|
|
1080
1219
|
data-wre-scroll-root="true"
|
|
1081
1220
|
>
|
|
@@ -1345,25 +1484,6 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
1345
1484
|
}
|
|
1346
1485
|
: pageChromeModel.documentGridStyle}
|
|
1347
1486
|
>
|
|
1348
|
-
{isPageWorkspace && chromeVisibility.pageChrome ? (
|
|
1349
|
-
<div
|
|
1350
|
-
data-testid="page-header-band"
|
|
1351
|
-
className="relative z-10 flex items-center justify-between border-b border-border/50 bg-surface/45 px-4 text-[11px] text-secondary backdrop-blur-[1px]"
|
|
1352
|
-
style={pageShellMetrics.headerBandStyle}
|
|
1353
|
-
>
|
|
1354
|
-
<span className="uppercase tracking-[0.12em] text-tertiary">{headerBandLabel}</span>
|
|
1355
|
-
{snapshot.pageLayout?.headerVariants[0] ? (
|
|
1356
|
-
<button
|
|
1357
|
-
type="button"
|
|
1358
|
-
aria-label="Open header story"
|
|
1359
|
-
onClick={props.onOpenHeaderStory}
|
|
1360
|
-
className="rounded-md px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface"
|
|
1361
|
-
>
|
|
1362
|
-
Edit header
|
|
1363
|
-
</button>
|
|
1364
|
-
) : null}
|
|
1365
|
-
</div>
|
|
1366
|
-
) : null}
|
|
1367
1487
|
{isPageWorkspace && chromeVisibility.pageChrome && pageChromeModel.showPageBorder && !hidePageBorderForActiveEditing ? (
|
|
1368
1488
|
<div
|
|
1369
1489
|
aria-hidden="true"
|
|
@@ -1373,15 +1493,23 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
1373
1493
|
/>
|
|
1374
1494
|
) : null}
|
|
1375
1495
|
<div className={isPageWorkspace ? "relative z-10" : "relative"}>
|
|
1376
|
-
{/*
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
`
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1496
|
+
{/* P8.11 — workspace-level header / footer bands
|
|
1497
|
+
retired. The PM surface now mounts inside
|
|
1498
|
+
`data-pm-body-slot`; per-page header, footer,
|
|
1499
|
+
footnote, and endnote chrome is owned by
|
|
1500
|
+
`TwPageStackChromeLayer` inside `TwChromeOverlay`
|
|
1501
|
+
(see below). When the user clicks a per-page
|
|
1502
|
+
band, the chrome layer reparents PM's DOM node
|
|
1503
|
+
into the active band's `data-pm-portal-slot`;
|
|
1504
|
+
when the user returns to the body, PM slides
|
|
1505
|
+
back into this wrapper. */}
|
|
1506
|
+
<div
|
|
1507
|
+
data-pm-body-slot=""
|
|
1508
|
+
ref={bodySlotRef}
|
|
1509
|
+
style={{ width: "100%" }}
|
|
1510
|
+
>
|
|
1511
|
+
{props.document}
|
|
1512
|
+
</div>
|
|
1385
1513
|
{props.layoutFacet ? (
|
|
1386
1514
|
<TwChromeOverlay
|
|
1387
1515
|
facet={props.layoutFacet}
|
|
@@ -1410,28 +1538,18 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
|
|
|
1410
1538
|
? handleScopeCardAskAgent
|
|
1411
1539
|
: undefined
|
|
1412
1540
|
}
|
|
1541
|
+
pageStackScrollRoot={
|
|
1542
|
+
isPageWorkspace && chromeVisibility.pageChrome
|
|
1543
|
+
? pageStackScrollRoot
|
|
1544
|
+
: undefined
|
|
1545
|
+
}
|
|
1546
|
+
renderFrameRevision={renderFrameRevision}
|
|
1547
|
+
activeStory={snapshot.activeStory}
|
|
1548
|
+
onOpenStory={props.onOpenStory}
|
|
1549
|
+
pmSurfaceElement={pmSurfaceElement}
|
|
1413
1550
|
/>
|
|
1414
1551
|
) : null}
|
|
1415
1552
|
</div>
|
|
1416
|
-
{isPageWorkspace && chromeVisibility.pageChrome ? (
|
|
1417
|
-
<div
|
|
1418
|
-
data-testid="page-footer-band"
|
|
1419
|
-
className="relative z-10 flex items-center justify-between border-t border-border/50 bg-surface/45 px-4 text-[11px] text-secondary backdrop-blur-[1px]"
|
|
1420
|
-
style={pageShellMetrics.footerBandStyle}
|
|
1421
|
-
>
|
|
1422
|
-
<span className="uppercase tracking-[0.12em] text-tertiary">{footerBandLabel}</span>
|
|
1423
|
-
{snapshot.pageLayout?.footerVariants[0] ? (
|
|
1424
|
-
<button
|
|
1425
|
-
type="button"
|
|
1426
|
-
aria-label="Open footer story"
|
|
1427
|
-
onClick={props.onOpenFooterStory}
|
|
1428
|
-
className="rounded-md px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface"
|
|
1429
|
-
>
|
|
1430
|
-
Edit footer
|
|
1431
|
-
</button>
|
|
1432
|
-
) : null}
|
|
1433
|
-
</div>
|
|
1434
|
-
) : null}
|
|
1435
1553
|
</div>
|
|
1436
1554
|
</div>
|
|
1437
1555
|
</div>
|
|
@@ -1717,8 +1835,6 @@ export interface PageShellMetrics {
|
|
|
1717
1835
|
frameHeightPx?: number;
|
|
1718
1836
|
contentInsetStyle: CSSProperties;
|
|
1719
1837
|
pageFrameStyle: CSSProperties;
|
|
1720
|
-
headerBandStyle: CSSProperties;
|
|
1721
|
-
footerBandStyle: CSSProperties;
|
|
1722
1838
|
}
|
|
1723
1839
|
|
|
1724
1840
|
function buildPageChromeModel(
|
|
@@ -1764,8 +1880,6 @@ export function buildPageShellMetrics(
|
|
|
1764
1880
|
return {
|
|
1765
1881
|
contentInsetStyle: {},
|
|
1766
1882
|
pageFrameStyle: {},
|
|
1767
|
-
headerBandStyle: {},
|
|
1768
|
-
footerBandStyle: {},
|
|
1769
1883
|
frameWidthPx: 0,
|
|
1770
1884
|
frameHeightPx: 0,
|
|
1771
1885
|
};
|
|
@@ -1779,17 +1893,11 @@ export function buildPageShellMetrics(
|
|
|
1779
1893
|
const frameHeightPx = Math.round(pageLayout.pageHeight * pxPerTwip);
|
|
1780
1894
|
const horizontalInsetPx = Math.round(pageLayout.marginLeft * pxPerTwip);
|
|
1781
1895
|
const horizontalInsetRightPx = Math.round(pageLayout.marginRight * pxPerTwip);
|
|
1782
|
-
|
|
1783
|
-
//
|
|
1784
|
-
//
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
Math.round(Math.max(0, pageLayout.marginTop - pageLayout.headerMargin) * pxPerTwip),
|
|
1788
|
-
);
|
|
1789
|
-
const footerBandHeightPx = Math.max(
|
|
1790
|
-
MIN_BAND_HEIGHT_PX,
|
|
1791
|
-
Math.round(Math.max(0, pageLayout.marginBottom - pageLayout.footerMargin) * pxPerTwip),
|
|
1792
|
-
);
|
|
1896
|
+
|
|
1897
|
+
// P8.11 — `headerBandStyle` / `footerBandStyle` removed. The
|
|
1898
|
+
// workspace-level band divs that consumed them are gone; per-page
|
|
1899
|
+
// bands (rendered by `TwPageStackChromeLayer`) compute their own
|
|
1900
|
+
// heights from the runtime's `PageRegionsSnapshot`.
|
|
1793
1901
|
|
|
1794
1902
|
return {
|
|
1795
1903
|
contentInsetStyle: {
|
|
@@ -1802,12 +1910,6 @@ export function buildPageShellMetrics(
|
|
|
1802
1910
|
boxShadow: "0 24px 48px -32px rgba(15, 23, 42, 0.38), 0 8px 20px -18px rgba(15, 23, 42, 0.22)",
|
|
1803
1911
|
border: "1px solid rgba(148, 163, 184, 0.2)",
|
|
1804
1912
|
},
|
|
1805
|
-
headerBandStyle: {
|
|
1806
|
-
minHeight: `${headerBandHeightPx}px`,
|
|
1807
|
-
},
|
|
1808
|
-
footerBandStyle: {
|
|
1809
|
-
minHeight: `${footerBandHeightPx}px`,
|
|
1810
|
-
},
|
|
1811
1913
|
frameWidthPx,
|
|
1812
1914
|
frameHeightPx,
|
|
1813
1915
|
};
|
|
@@ -1849,30 +1951,6 @@ export function resolveZoomMultiplier(
|
|
|
1849
1951
|
);
|
|
1850
1952
|
}
|
|
1851
1953
|
|
|
1852
|
-
function resolvePageBandLabel(
|
|
1853
|
-
region: "header" | "footer",
|
|
1854
|
-
activeStory: RuntimeRenderSnapshot["activeStory"],
|
|
1855
|
-
): string {
|
|
1856
|
-
const regionLabel = region === "header" ? "header" : "footer";
|
|
1857
|
-
let label: string;
|
|
1858
|
-
if (activeStory.kind !== region) {
|
|
1859
|
-
label = region === "header" ? "Header" : "Footer";
|
|
1860
|
-
} else {
|
|
1861
|
-
switch (activeStory.variant) {
|
|
1862
|
-
case "first":
|
|
1863
|
-
label = `First page ${regionLabel}`;
|
|
1864
|
-
break;
|
|
1865
|
-
case "even":
|
|
1866
|
-
label = `Even page ${regionLabel}`;
|
|
1867
|
-
break;
|
|
1868
|
-
default:
|
|
1869
|
-
label = `Default ${regionLabel}`;
|
|
1870
|
-
break;
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
return label;
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
1954
|
function buildLineNumberMarkers(
|
|
1877
1955
|
blocks: readonly SurfaceBlockSnapshot[],
|
|
1878
1956
|
pages: ReadonlyArray<DocumentNavigationSnapshot["pages"][number]>,
|