@beyondwork/docx-react-component 1.0.103 → 1.0.105
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 +66 -1
- package/src/api/v3/_runtime-handle.ts +2 -0
- package/src/api/v3/ai/_pe2-evidence.ts +153 -0
- package/src/api/v3/ai/bundle.ts +13 -5
- package/src/api/v3/ai/inspect.ts +7 -1
- package/src/api/v3/ai/outline.ts +2 -7
- package/src/api/v3/ai/replacement.ts +113 -0
- package/src/api/v3/runtime/geometry.ts +79 -0
- package/src/api/v3/ui/_types.ts +86 -0
- package/src/api/v3/ui/index.ts +5 -0
- package/src/api/v3/ui/overlays.ts +104 -0
- package/src/io/ooxml/parse-drawing.ts +99 -1
- package/src/io/ooxml/parse-fields.ts +27 -6
- package/src/io/ooxml/parse-shapes.ts +130 -0
- package/src/model/canonical-document.ts +34 -3
- package/src/model/canonical-layout-inputs.ts +979 -0
- package/src/model/layout/index.ts +9 -0
- package/src/model/layout/page-graph-types.ts +150 -0
- package/src/model/layout/runtime-page-graph-types.ts +23 -0
- package/src/runtime/collab/runtime-collab-sync.ts +3 -3
- package/src/runtime/debug/build-debug-inspector-snapshot.ts +17 -4
- package/src/runtime/document-runtime.ts +30 -14
- package/src/runtime/event-refresh-hints.ts +35 -5
- package/src/runtime/formatting/formatting-context.ts +110 -9
- package/src/runtime/formatting/index.ts +2 -0
- package/src/runtime/formatting/layout-inputs.ts +67 -3
- package/src/runtime/geometry/caret-geometry.ts +82 -10
- package/src/runtime/geometry/geometry-facet.ts +44 -0
- package/src/runtime/geometry/geometry-index.ts +1268 -0
- package/src/runtime/geometry/geometry-types.ts +227 -1
- package/src/runtime/geometry/index.ts +26 -0
- package/src/runtime/geometry/inert-geometry-facet.ts +3 -0
- package/src/runtime/geometry/object-handles.ts +7 -4
- package/src/runtime/geometry/replacement-envelope.ts +41 -2
- package/src/runtime/layout/layout-engine-instance.ts +2 -0
- package/src/runtime/layout/layout-engine-version.ts +44 -1
- package/src/runtime/layout/page-graph.ts +877 -2
- package/src/runtime/layout/project-block-fragments.ts +101 -1
- package/src/runtime/layout/public-facet.ts +152 -0
- package/src/runtime/prerender/graph-canonicalize.ts +44 -0
- package/src/runtime/surface-projection.ts +43 -3
- package/src/runtime/workflow/coordinator.ts +57 -11
- package/src/ui/ui-controller-factory.ts +11 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +3 -0
|
@@ -59,6 +59,15 @@
|
|
|
59
59
|
export type {
|
|
60
60
|
RuntimePageRegions,
|
|
61
61
|
RuntimePageRegion,
|
|
62
|
+
RuntimeTwipsRect,
|
|
63
|
+
RuntimeResolvedRegions,
|
|
64
|
+
RuntimeExclusionZone,
|
|
65
|
+
RuntimeLayoutDivergenceKind,
|
|
66
|
+
RuntimeLayoutDivergence,
|
|
67
|
+
RuntimePageFrame,
|
|
68
|
+
RuntimePageLocalStoryInstance,
|
|
69
|
+
RuntimeResolvedStoryField,
|
|
70
|
+
RuntimeStoryAnchoredObject,
|
|
62
71
|
RuntimeBlockFragment,
|
|
63
72
|
RuntimeLineBox,
|
|
64
73
|
RuntimeNoteAllocation,
|
|
@@ -48,6 +48,17 @@ export interface RuntimePageRegions {
|
|
|
48
48
|
footnotes?: RuntimePageRegion[];
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
export interface RuntimeTwipsRect {
|
|
52
|
+
/** Twips offset from the physical page left edge. */
|
|
53
|
+
xTwips: number;
|
|
54
|
+
/** Twips offset from the physical page top edge. */
|
|
55
|
+
yTwips: number;
|
|
56
|
+
/** Width in twips. */
|
|
57
|
+
widthTwips: number;
|
|
58
|
+
/** Height in twips. */
|
|
59
|
+
heightTwips: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
export interface RuntimePageRegion {
|
|
52
63
|
kind: "body" | "header" | "footer" | "column" | "footnote-area";
|
|
53
64
|
/** Twips offset from page top (header) or similar region-specific origin. */
|
|
@@ -56,10 +67,141 @@ export interface RuntimePageRegion {
|
|
|
56
67
|
widthTwips: number;
|
|
57
68
|
/** Height in twips. */
|
|
58
69
|
heightTwips: number;
|
|
70
|
+
/**
|
|
71
|
+
* PE2 Slice 2 — explicit twips-space frame for this region.
|
|
72
|
+
* This is semantic layout geometry, not rendered pixel geometry.
|
|
73
|
+
*/
|
|
74
|
+
rectTwips?: RuntimeTwipsRect;
|
|
59
75
|
/** IDs of block fragments rendered in this region, in order. */
|
|
60
76
|
fragmentIds: string[];
|
|
61
77
|
}
|
|
62
78
|
|
|
79
|
+
export interface RuntimeResolvedRegions {
|
|
80
|
+
body: RuntimePageRegion;
|
|
81
|
+
header?: RuntimePageRegion;
|
|
82
|
+
footer?: RuntimePageRegion;
|
|
83
|
+
columns?: RuntimePageRegion[];
|
|
84
|
+
footnotes?: RuntimePageRegion[];
|
|
85
|
+
exclusionZones: RuntimeExclusionZone[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface RuntimeExclusionZone {
|
|
89
|
+
rectTwips: RuntimeTwipsRect;
|
|
90
|
+
objectId: string;
|
|
91
|
+
wrapMode: string;
|
|
92
|
+
layoutInCell?: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type RuntimeLayoutDivergenceKind =
|
|
96
|
+
| "frame-collision"
|
|
97
|
+
| "intentional-overflow"
|
|
98
|
+
| "unsupported-wrap"
|
|
99
|
+
| "stale-field-label"
|
|
100
|
+
| "preserve-only-placeholder";
|
|
101
|
+
|
|
102
|
+
export interface RuntimeLayoutDivergence {
|
|
103
|
+
divergenceId: string;
|
|
104
|
+
kind: RuntimeLayoutDivergenceKind;
|
|
105
|
+
source: "runtime";
|
|
106
|
+
severity: "info" | "warning" | "error";
|
|
107
|
+
message: string;
|
|
108
|
+
regionKinds?: RuntimePageRegion["kind"][];
|
|
109
|
+
fragmentIds?: string[];
|
|
110
|
+
objectIds?: string[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface RuntimePageFrame {
|
|
114
|
+
frameId: string;
|
|
115
|
+
pageId: string;
|
|
116
|
+
pageIndex: number;
|
|
117
|
+
sectionIndex: number;
|
|
118
|
+
displayPageNumber: number;
|
|
119
|
+
physicalBoundsTwips: RuntimeTwipsRect;
|
|
120
|
+
regions: RuntimeResolvedRegions;
|
|
121
|
+
pageLocalStories: RuntimePageLocalStoryInstance[];
|
|
122
|
+
divergenceIds: string[];
|
|
123
|
+
signature: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface RuntimePageLocalStoryInstance {
|
|
127
|
+
instanceId: string;
|
|
128
|
+
storyKey: string;
|
|
129
|
+
pageId: string;
|
|
130
|
+
kind: "header" | "footer";
|
|
131
|
+
variant: "default" | "first" | "even" | "odd";
|
|
132
|
+
relationshipId: string;
|
|
133
|
+
sectionIndex?: number;
|
|
134
|
+
resolvedFields: RuntimeResolvedStoryField[];
|
|
135
|
+
anchoredObjects: RuntimeStoryAnchoredObject[];
|
|
136
|
+
measuredFrameHeightTwips: number;
|
|
137
|
+
signature: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface RuntimeResolvedStoryField {
|
|
141
|
+
fieldId: string;
|
|
142
|
+
family: string;
|
|
143
|
+
displayText: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface RuntimeStoryAnchoredObject {
|
|
147
|
+
objectId: string;
|
|
148
|
+
sourceType:
|
|
149
|
+
| "image"
|
|
150
|
+
| "drawing-frame"
|
|
151
|
+
| "chart-preview"
|
|
152
|
+
| "smartart-preview"
|
|
153
|
+
| "shape"
|
|
154
|
+
| "wordart"
|
|
155
|
+
| "vml-shape"
|
|
156
|
+
| "ole-embed"
|
|
157
|
+
| "opaque-inline";
|
|
158
|
+
display: "inline" | "floating" | "unknown";
|
|
159
|
+
extentTwips?: {
|
|
160
|
+
widthTwips: number;
|
|
161
|
+
heightTwips: number;
|
|
162
|
+
};
|
|
163
|
+
relationshipIds?: readonly string[];
|
|
164
|
+
preserveOnly: boolean;
|
|
165
|
+
divergenceIds: string[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export type RuntimeLayoutContinuationCursor =
|
|
169
|
+
| RuntimeParagraphContinuationCursor
|
|
170
|
+
| RuntimeTableContinuationCursor;
|
|
171
|
+
|
|
172
|
+
export interface RuntimeParagraphContinuationCursor {
|
|
173
|
+
kind: "paragraph";
|
|
174
|
+
sequenceIndex: number;
|
|
175
|
+
sliceCount: number;
|
|
176
|
+
lineRange: {
|
|
177
|
+
from: number;
|
|
178
|
+
to: number;
|
|
179
|
+
totalLines: number;
|
|
180
|
+
};
|
|
181
|
+
continuesFromPreviousPage: boolean;
|
|
182
|
+
continuesToNextPage: boolean;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface RuntimeTableContinuationCursor {
|
|
186
|
+
kind: "table";
|
|
187
|
+
sequenceIndex: number;
|
|
188
|
+
sliceCount: number;
|
|
189
|
+
rowRange: {
|
|
190
|
+
from: number;
|
|
191
|
+
to: number;
|
|
192
|
+
totalRows: number;
|
|
193
|
+
};
|
|
194
|
+
continuesFromPreviousPage: boolean;
|
|
195
|
+
continuesToNextPage: boolean;
|
|
196
|
+
repeatedHeaderRowIndexes: readonly number[];
|
|
197
|
+
verticalMergeCarry: readonly RuntimeTableVerticalMergeCarry[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface RuntimeTableVerticalMergeCarry {
|
|
201
|
+
columnIndex: number;
|
|
202
|
+
restartRowIndex: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
63
205
|
// ---------------------------------------------------------------------------
|
|
64
206
|
// Fragment types
|
|
65
207
|
// ---------------------------------------------------------------------------
|
|
@@ -115,6 +257,14 @@ export interface RuntimeBlockFragment {
|
|
|
115
257
|
to: number;
|
|
116
258
|
totalRows: number;
|
|
117
259
|
};
|
|
260
|
+
/**
|
|
261
|
+
* PE2 Slice 3 — typed continuation cursor for fragments that render a
|
|
262
|
+
* logical block over multiple page/column frames. Existing slice fields
|
|
263
|
+
* remain for compatibility; this cursor makes repeated-header,
|
|
264
|
+
* line/row-range, and vertical-merge carry explicit for debug/render
|
|
265
|
+
* consumers without reading DOM or canonical tables again.
|
|
266
|
+
*/
|
|
267
|
+
continuation?: RuntimeLayoutContinuationCursor;
|
|
118
268
|
/**
|
|
119
269
|
* Slice 5 — opaque style-chain ref derived from the block's `styleId`.
|
|
120
270
|
* Used by `analyzeStylesChange` to bound invalidation to the first page
|
|
@@ -14,11 +14,17 @@
|
|
|
14
14
|
* layer (`src/runtime/layout/page-graph.ts`).
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import type {
|
|
18
|
+
FooterDocument,
|
|
19
|
+
HeaderDocument,
|
|
20
|
+
} from "../canonical-document.ts";
|
|
17
21
|
import type {
|
|
18
22
|
RuntimeBlockFragment,
|
|
23
|
+
RuntimeLayoutDivergence,
|
|
19
24
|
RuntimeLineBox,
|
|
20
25
|
RuntimeNoteAllocation,
|
|
21
26
|
RuntimePageAnchor,
|
|
27
|
+
RuntimePageFrame,
|
|
22
28
|
RuntimePageRegions,
|
|
23
29
|
} from "./page-graph-types.ts";
|
|
24
30
|
import type {
|
|
@@ -63,6 +69,14 @@ export interface RuntimePageNode {
|
|
|
63
69
|
stories: ResolvedPageStories;
|
|
64
70
|
/** Sub-regions on the page. */
|
|
65
71
|
regions: RuntimePageRegions;
|
|
72
|
+
/**
|
|
73
|
+
* PE2 Slice 2 — page-level frame record for consumers that need a
|
|
74
|
+
* single immutable twips-space layout payload instead of reconstructing
|
|
75
|
+
* it from `layout` + `regions`.
|
|
76
|
+
*/
|
|
77
|
+
frame?: RuntimePageFrame;
|
|
78
|
+
/** Typed layout limitations or overlaps detected while building this page. */
|
|
79
|
+
divergences?: RuntimeLayoutDivergence[];
|
|
66
80
|
/** Line boxes rendered in the body region. */
|
|
67
81
|
lineBoxes: RuntimeLineBox[];
|
|
68
82
|
/** Footnote allocations reserved at the bottom of the page. */
|
|
@@ -105,4 +119,13 @@ export interface BuildPageGraphInput {
|
|
|
105
119
|
* `page-${revision}-${index}` pageId in advance.
|
|
106
120
|
*/
|
|
107
121
|
noteAllocationsByPageIndex?: ReadonlyMap<number, RuntimeNoteAllocation[]>;
|
|
122
|
+
/**
|
|
123
|
+
* Optional canonical header/footer parts. When present, `buildPageGraph`
|
|
124
|
+
* resolves page-instance fields inside page-local header/footer story
|
|
125
|
+
* records without making render/UI consumers re-walk subparts.
|
|
126
|
+
*/
|
|
127
|
+
subParts?: {
|
|
128
|
+
headers?: ReadonlyArray<HeaderDocument>;
|
|
129
|
+
footers?: ReadonlyArray<FooterDocument>;
|
|
130
|
+
};
|
|
108
131
|
}
|
|
@@ -473,11 +473,11 @@ export function createRuntimeCollabSync(
|
|
|
473
473
|
// For a late joiner it may already be populated — the seed ensures the
|
|
474
474
|
// runtime reflects pre-existing shared state at attach time (i.e. if a
|
|
475
475
|
// peer already set `lockedMode`, the new peer starts out locked).
|
|
476
|
-
runtime.setSharedWorkflowState(workflowShared.get());
|
|
476
|
+
runtime.setSharedWorkflowState(workflowShared.get(), { source: "collab" });
|
|
477
477
|
|
|
478
478
|
const workflowUnsub = workflowShared.subscribe((state) => {
|
|
479
479
|
if (readOnly) return; // don't propagate while in read-only (post-mismatch)
|
|
480
|
-
runtime.setSharedWorkflowState(state);
|
|
480
|
+
runtime.setSharedWorkflowState(state, { source: "collab" });
|
|
481
481
|
});
|
|
482
482
|
|
|
483
483
|
const unsubscribeCommandApplied = commandAppliedBridge.subscribe((command, _transaction, context, meta) => {
|
|
@@ -607,7 +607,7 @@ export function createRuntimeCollabSync(
|
|
|
607
607
|
yCheckpoints.unobserve(onCheckpointsChange);
|
|
608
608
|
workflowUnsub();
|
|
609
609
|
workflowShared.destroy();
|
|
610
|
-
runtime.setSharedWorkflowState(null);
|
|
610
|
+
runtime.setSharedWorkflowState(null, { source: "runtime" });
|
|
611
611
|
listeners.clear();
|
|
612
612
|
// R3 — abort the current remote-activity signal so any lingering
|
|
613
613
|
// idle consumers detect the shutdown.
|
|
@@ -576,13 +576,23 @@ function projectLayout(
|
|
|
576
576
|
runtime: DocumentRuntime,
|
|
577
577
|
fallbacks: string[],
|
|
578
578
|
): DebugInspectorLayoutSection {
|
|
579
|
-
const
|
|
579
|
+
const graphPageCount = safeCall(
|
|
580
580
|
runtime,
|
|
581
|
-
(r) => r.
|
|
581
|
+
(r) => r.getLayoutFacet().getPageCount(),
|
|
582
582
|
null,
|
|
583
583
|
fallbacks,
|
|
584
|
-
"page-
|
|
584
|
+
"layout-page-count",
|
|
585
585
|
);
|
|
586
|
+
const pageLayout =
|
|
587
|
+
graphPageCount === null
|
|
588
|
+
? safeCall(
|
|
589
|
+
runtime,
|
|
590
|
+
(r) => r.getPageLayoutSnapshot(),
|
|
591
|
+
null,
|
|
592
|
+
fallbacks,
|
|
593
|
+
"page-layout-summary",
|
|
594
|
+
)
|
|
595
|
+
: null;
|
|
586
596
|
const sections = safeCall(runtime, (r) => r.getSections(), [], fallbacks, "sections");
|
|
587
597
|
const stories = safeCall(
|
|
588
598
|
runtime,
|
|
@@ -592,7 +602,10 @@ function projectLayout(
|
|
|
592
602
|
"document-text-stream",
|
|
593
603
|
);
|
|
594
604
|
return {
|
|
595
|
-
pageCount:
|
|
605
|
+
pageCount:
|
|
606
|
+
graphPageCount ??
|
|
607
|
+
(pageLayout as { pages?: unknown[] } | null)?.pages?.length ??
|
|
608
|
+
null,
|
|
596
609
|
sectionCount: sections.length,
|
|
597
610
|
storyCount: stories.length,
|
|
598
611
|
};
|
|
@@ -82,6 +82,7 @@ import type {
|
|
|
82
82
|
WorkflowCandidateRange,
|
|
83
83
|
WorkflowCandidateRangeOptions,
|
|
84
84
|
WorkflowBlockedCommandReason,
|
|
85
|
+
WorkflowEventOrigin,
|
|
85
86
|
WorkflowMetadataDefinition,
|
|
86
87
|
OverlayKind,
|
|
87
88
|
OverlayVisibilityPolicy,
|
|
@@ -764,7 +765,10 @@ export interface DocumentRuntime {
|
|
|
764
765
|
setWorkflowOverlay(overlay: WorkflowOverlay): void;
|
|
765
766
|
clearWorkflowOverlay(): void;
|
|
766
767
|
getWorkflowOverlay(): WorkflowOverlay | null;
|
|
767
|
-
setSharedWorkflowState(
|
|
768
|
+
setSharedWorkflowState(
|
|
769
|
+
state: SharedWorkflowState | null,
|
|
770
|
+
origin?: WorkflowEventOrigin,
|
|
771
|
+
): void;
|
|
768
772
|
getWorkflowScopeSnapshot(): WorkflowScopeSnapshot | null;
|
|
769
773
|
getInteractionGuardSnapshot(): InteractionGuardSnapshot;
|
|
770
774
|
getWorkflowMarkupSnapshot(): WorkflowMarkupSnapshot;
|
|
@@ -1880,6 +1884,19 @@ export function createDocumentRuntime(
|
|
|
1880
1884
|
nextActiveStory: EditorStoryTarget,
|
|
1881
1885
|
): DocumentNavigationSnapshot {
|
|
1882
1886
|
const activeStoryKey = storyTargetKey(nextActiveStory);
|
|
1887
|
+
const buildSnapshot = (): DocumentNavigationSnapshot =>
|
|
1888
|
+
layoutEngine.getNavigationSnapshot(
|
|
1889
|
+
{
|
|
1890
|
+
document: nextState.document,
|
|
1891
|
+
viewState: {
|
|
1892
|
+
activeStory: nextActiveStory,
|
|
1893
|
+
workspaceMode: viewState.workspaceMode,
|
|
1894
|
+
zoomLevel: viewState.zoomLevel,
|
|
1895
|
+
},
|
|
1896
|
+
},
|
|
1897
|
+
nextState.selection,
|
|
1898
|
+
nextActiveStory,
|
|
1899
|
+
);
|
|
1883
1900
|
if (
|
|
1884
1901
|
cachedNavigation &&
|
|
1885
1902
|
cachedNavigation.revisionToken === nextState.revisionToken &&
|
|
@@ -1889,11 +1906,7 @@ export function createDocumentRuntime(
|
|
|
1889
1906
|
return cachedNavigation.snapshot;
|
|
1890
1907
|
}
|
|
1891
1908
|
|
|
1892
|
-
const snapshot =
|
|
1893
|
-
nextState.document,
|
|
1894
|
-
nextState.selection.head,
|
|
1895
|
-
nextActiveStory,
|
|
1896
|
-
);
|
|
1909
|
+
const snapshot = buildSnapshot();
|
|
1897
1910
|
if (
|
|
1898
1911
|
snapshot.activePageIndex === cachedNavigation.snapshot.activePageIndex &&
|
|
1899
1912
|
snapshot.activeSectionIndex === cachedNavigation.snapshot.activeSectionIndex
|
|
@@ -1915,11 +1928,7 @@ export function createDocumentRuntime(
|
|
|
1915
1928
|
return snapshot;
|
|
1916
1929
|
}
|
|
1917
1930
|
|
|
1918
|
-
const snapshot =
|
|
1919
|
-
nextState.document,
|
|
1920
|
-
nextState.selection.head,
|
|
1921
|
-
nextActiveStory,
|
|
1922
|
-
);
|
|
1931
|
+
const snapshot = buildSnapshot();
|
|
1923
1932
|
recordPerfSample("snapshot.navigation");
|
|
1924
1933
|
incrementInvalidationCounter("runtime.snapshot.navigationMisses");
|
|
1925
1934
|
cachedNavigation = {
|
|
@@ -4560,6 +4569,7 @@ export function createDocumentRuntime(
|
|
|
4560
4569
|
// P5 — TOC entries print Word's display number (honors page-
|
|
4561
4570
|
// number restarts), not the raw 0-based pageIndex+1.
|
|
4562
4571
|
(pageIndex: number) => layoutFacet.getDisplayPageNumber(pageIndex),
|
|
4572
|
+
getCachedDocumentNavigationSnapshot(state, activeStory),
|
|
4563
4573
|
);
|
|
4564
4574
|
if (refreshed.changed) {
|
|
4565
4575
|
this.dispatch({
|
|
@@ -4709,8 +4719,8 @@ export function createDocumentRuntime(
|
|
|
4709
4719
|
getWorkflowOverlay() {
|
|
4710
4720
|
return workflowCoordinator.getWorkflowOverlay();
|
|
4711
4721
|
},
|
|
4712
|
-
setSharedWorkflowState(sharedState) {
|
|
4713
|
-
workflowCoordinator.setSharedWorkflowState(sharedState);
|
|
4722
|
+
setSharedWorkflowState(sharedState, origin) {
|
|
4723
|
+
workflowCoordinator.setSharedWorkflowState(sharedState, origin);
|
|
4714
4724
|
},
|
|
4715
4725
|
getWorkflowScopeSnapshot() {
|
|
4716
4726
|
return workflowCoordinator.getWorkflowScopeSnapshot();
|
|
@@ -5935,6 +5945,7 @@ export function createDocumentRuntime(
|
|
|
5935
5945
|
activeStory,
|
|
5936
5946
|
undefined,
|
|
5937
5947
|
(pageIndex: number) => layoutFacet.getDisplayPageNumber(pageIndex),
|
|
5948
|
+
getCachedDocumentNavigationSnapshot(state, activeStory),
|
|
5938
5949
|
);
|
|
5939
5950
|
perfCounters.increment("toc.autoRefresh.us", Math.round((performance.now() - t) * 1000));
|
|
5940
5951
|
if (!refreshed.changed) {
|
|
@@ -6144,6 +6155,9 @@ export function createDocumentRuntime(
|
|
|
6144
6155
|
case "story_changed":
|
|
6145
6156
|
case "workflow_overlay_changed":
|
|
6146
6157
|
case "workflow_active_work_item_changed":
|
|
6158
|
+
case "workflow_shared_state_changed":
|
|
6159
|
+
case "workflow_visibility_policy_changed":
|
|
6160
|
+
case "workflow_markup_mode_policy_changed":
|
|
6147
6161
|
case "change_authored":
|
|
6148
6162
|
case "change_accepted":
|
|
6149
6163
|
case "change_rejected":
|
|
@@ -7482,6 +7496,7 @@ function refreshDocumentTableOfContents(
|
|
|
7482
7496
|
activeStory: EditorStoryTarget,
|
|
7483
7497
|
options?: TocRefreshOptions,
|
|
7484
7498
|
resolveDisplayPageNumber?: (pageIndex: number) => number | null,
|
|
7499
|
+
navigationSnapshot?: DocumentNavigationSnapshot,
|
|
7485
7500
|
): {
|
|
7486
7501
|
document: CanonicalDocumentEnvelope;
|
|
7487
7502
|
result: TocRefreshResult;
|
|
@@ -7511,7 +7526,8 @@ function refreshDocumentTableOfContents(
|
|
|
7511
7526
|
};
|
|
7512
7527
|
}
|
|
7513
7528
|
|
|
7514
|
-
const navigation =
|
|
7529
|
+
const navigation =
|
|
7530
|
+
navigationSnapshot ?? createDocumentNavigationSnapshot(document, selectionHead, activeStory);
|
|
7515
7531
|
// Build a single O(N) map from paragraph offset → bookmark name so the
|
|
7516
7532
|
// per-heading lookup below is O(1) instead of O(N) per heading.
|
|
7517
7533
|
const bookmarkNameByOffset = new Map<number, string>();
|
|
@@ -25,6 +25,8 @@ export function describeEventImpact(
|
|
|
25
25
|
};
|
|
26
26
|
case "workflow_overlay_changed":
|
|
27
27
|
case "workflow_active_work_item_changed":
|
|
28
|
+
case "workflow_shared_state_changed":
|
|
29
|
+
case "workflow_markup_mode_policy_changed":
|
|
28
30
|
return {
|
|
29
31
|
invalidate: [
|
|
30
32
|
"workflowScope",
|
|
@@ -37,9 +39,27 @@ export function describeEventImpact(
|
|
|
37
39
|
staleTargets: ["anchors"],
|
|
38
40
|
changeKinds: ["workflow"],
|
|
39
41
|
};
|
|
42
|
+
case "workflow_visibility_policy_changed":
|
|
43
|
+
return {
|
|
44
|
+
invalidate: [
|
|
45
|
+
"workflowScope",
|
|
46
|
+
"workflowMarkup",
|
|
47
|
+
"reviewWork",
|
|
48
|
+
"chunks",
|
|
49
|
+
"contextAnalytics",
|
|
50
|
+
],
|
|
51
|
+
staleTargets: ["none"],
|
|
52
|
+
changeKinds: ["workflow"],
|
|
53
|
+
};
|
|
40
54
|
case "workflow_metadata_changed":
|
|
41
55
|
return {
|
|
42
|
-
invalidate: [
|
|
56
|
+
invalidate: [
|
|
57
|
+
"workflowMarkup",
|
|
58
|
+
"locations",
|
|
59
|
+
"reviewWork",
|
|
60
|
+
"chunks",
|
|
61
|
+
"contextAnalytics",
|
|
62
|
+
],
|
|
43
63
|
staleTargets: ["anchors"],
|
|
44
64
|
changeKinds: ["workflow"],
|
|
45
65
|
};
|
|
@@ -47,10 +67,6 @@ export function describeEventImpact(
|
|
|
47
67
|
case "change_accepted":
|
|
48
68
|
case "change_rejected":
|
|
49
69
|
case "suggestion_authored":
|
|
50
|
-
case "suggestion_updated":
|
|
51
|
-
case "comment_added":
|
|
52
|
-
case "comment_resolved":
|
|
53
|
-
case "comments_changed":
|
|
54
70
|
return {
|
|
55
71
|
invalidate: [
|
|
56
72
|
"render",
|
|
@@ -69,6 +85,20 @@ export function describeEventImpact(
|
|
|
69
85
|
staleTargets: ["search_results", "anchors"],
|
|
70
86
|
changeKinds: ["content", "review", "structure"],
|
|
71
87
|
};
|
|
88
|
+
case "suggestion_updated":
|
|
89
|
+
return {
|
|
90
|
+
invalidate: ["trackedChanges", "reviewWork", "chunks", "contextAnalytics"],
|
|
91
|
+
staleTargets: ["none"],
|
|
92
|
+
changeKinds: ["review"],
|
|
93
|
+
};
|
|
94
|
+
case "comment_added":
|
|
95
|
+
case "comment_resolved":
|
|
96
|
+
case "comments_changed":
|
|
97
|
+
return {
|
|
98
|
+
invalidate: ["comments", "reviewWork", "chunks", "contextAnalytics"],
|
|
99
|
+
staleTargets: ["none"],
|
|
100
|
+
changeKinds: ["review"],
|
|
101
|
+
};
|
|
72
102
|
case "warning_added":
|
|
73
103
|
case "warning_cleared":
|
|
74
104
|
case "error":
|
|
@@ -46,6 +46,7 @@ import type {
|
|
|
46
46
|
TableStyleConditionalRegion,
|
|
47
47
|
Mutable,
|
|
48
48
|
} from "../../model/canonical-document.ts";
|
|
49
|
+
import { collectCanonicalFieldRegionIdentities } from "../../model/canonical-layout-inputs.ts";
|
|
49
50
|
import type { FieldPageGraph, FieldResolver, ResolvedField } from "./field/resolver.ts";
|
|
50
51
|
import { createFieldResolver } from "./field/resolver.ts";
|
|
51
52
|
import { ThemeColorResolver, concretizeThemeColors } from "./theme-color.ts";
|
|
@@ -68,13 +69,21 @@ import type {
|
|
|
68
69
|
EffectiveNumbering,
|
|
69
70
|
EffectiveParagraphFormatting,
|
|
70
71
|
EffectiveRunFormatting,
|
|
72
|
+
RevisionDisplayFlags,
|
|
71
73
|
} from "./formatting-types.ts";
|
|
72
74
|
import {
|
|
75
|
+
buildEffectiveLayoutFormatting,
|
|
76
|
+
buildRevisionLayoutPosture,
|
|
73
77
|
collectFieldLayoutInputs,
|
|
78
|
+
mergeLayoutTabStops,
|
|
74
79
|
toFieldLayoutInput,
|
|
80
|
+
toLayoutTabStops,
|
|
75
81
|
toNumberingLayoutInput,
|
|
82
|
+
type EffectiveLayoutFormatting,
|
|
76
83
|
type FieldLayoutInput,
|
|
84
|
+
type LayoutTabStopInput,
|
|
77
85
|
type NumberingLayoutInput,
|
|
86
|
+
type RevisionLayoutPosture,
|
|
78
87
|
} from "./layout-inputs.ts";
|
|
79
88
|
|
|
80
89
|
/**
|
|
@@ -342,6 +351,23 @@ export interface FormattingContext {
|
|
|
342
351
|
/** Resolve every field/TOC registry entry into PE2 field-layout inputs. */
|
|
343
352
|
collectFieldLayoutInputs(): readonly FieldLayoutInput[];
|
|
344
353
|
|
|
354
|
+
/** Resolve paragraph tab stops into L04-ready twip/leader inputs. */
|
|
355
|
+
resolveParagraphTabStops(para: ParagraphNode): readonly LayoutTabStopInput[];
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Consolidated PE2 paragraph formatting bundle. This is the L03-owned
|
|
359
|
+
* handoff shape for L04 measurement: paragraph cascade, run formatting,
|
|
360
|
+
* numbering input, paragraph/numbering tabs, compatibility flags, revision
|
|
361
|
+
* posture, and a stable structural hash are resolved in one context call.
|
|
362
|
+
*/
|
|
363
|
+
resolveParagraphLayoutFormatting(input: {
|
|
364
|
+
readonly paragraph: ParagraphNode;
|
|
365
|
+
readonly runs?: readonly EffectiveRunFormatting[];
|
|
366
|
+
readonly revisionDisplays?: readonly RevisionDisplayFlags[];
|
|
367
|
+
readonly revisionMode?: RevisionLayoutPosture["mode"];
|
|
368
|
+
readonly compatFlags?: readonly string[];
|
|
369
|
+
}): EffectiveLayoutFormatting;
|
|
370
|
+
|
|
345
371
|
/** Resolve the effective font family for a run following
|
|
346
372
|
* ECMA-376 §17.3.2.26 precedence + theme-minor fallback. */
|
|
347
373
|
resolveFontFamily(input: RunResolveInput, themeMinorFont?: string): string | undefined;
|
|
@@ -637,9 +663,54 @@ class FormattingContextImpl implements FormattingContext {
|
|
|
637
663
|
collectFieldLayoutInputs(): readonly FieldLayoutInput[] {
|
|
638
664
|
return collectFieldLayoutInputs(this.doc.fieldRegistry, (entry) =>
|
|
639
665
|
this.resolveField(entry),
|
|
666
|
+
collectCanonicalFieldRegionIdentities(this.doc),
|
|
640
667
|
);
|
|
641
668
|
}
|
|
642
669
|
|
|
670
|
+
resolveParagraphTabStops(para: ParagraphNode): readonly LayoutTabStopInput[] {
|
|
671
|
+
return toLayoutTabStops(this.resolveParagraphCascade(para).tabStops, "paragraph");
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
resolveParagraphLayoutFormatting(input: {
|
|
675
|
+
readonly paragraph: ParagraphNode;
|
|
676
|
+
readonly runs?: readonly EffectiveRunFormatting[];
|
|
677
|
+
readonly revisionDisplays?: readonly RevisionDisplayFlags[];
|
|
678
|
+
readonly revisionMode?: RevisionLayoutPosture["mode"];
|
|
679
|
+
readonly compatFlags?: readonly string[];
|
|
680
|
+
}): EffectiveLayoutFormatting {
|
|
681
|
+
const numberingDetail = this.resolveParagraphNumbering(input.paragraph, { advance: true });
|
|
682
|
+
const numbering = toNumberingLayoutInput(numberingDetail);
|
|
683
|
+
const paragraph = buildEffectiveParagraphFormatting(
|
|
684
|
+
this.resolveParagraphCascade(input.paragraph),
|
|
685
|
+
numberingDetail,
|
|
686
|
+
this.theme,
|
|
687
|
+
);
|
|
688
|
+
const runs = input.runs ?? [];
|
|
689
|
+
const revisionDisplays =
|
|
690
|
+
input.revisionDisplays ??
|
|
691
|
+
runs
|
|
692
|
+
.map((run) => run.revisionDisplay)
|
|
693
|
+
.filter((display): display is RevisionDisplayFlags => display !== undefined);
|
|
694
|
+
const revisionMode = input.revisionMode ?? revisionDisplays[0]?.markupMode;
|
|
695
|
+
const revisionPosture = revisionMode
|
|
696
|
+
? buildRevisionLayoutPosture(revisionMode, revisionDisplays)
|
|
697
|
+
: undefined;
|
|
698
|
+
return buildEffectiveLayoutFormatting({
|
|
699
|
+
paragraph,
|
|
700
|
+
runs,
|
|
701
|
+
...(numbering ? { numbering } : {}),
|
|
702
|
+
tabs: mergeLayoutTabStops(
|
|
703
|
+
this.resolveParagraphTabStops(input.paragraph),
|
|
704
|
+
numbering?.associatedTabStops,
|
|
705
|
+
),
|
|
706
|
+
compatFlags: mergeCompatFlags(
|
|
707
|
+
collectParagraphCompatFlags(paragraph),
|
|
708
|
+
input.compatFlags ?? [],
|
|
709
|
+
),
|
|
710
|
+
...(revisionPosture ? { revisionPosture } : {}),
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
643
714
|
resolveFontFamily(
|
|
644
715
|
input: RunResolveInput,
|
|
645
716
|
themeMinorFont?: string,
|
|
@@ -655,17 +726,11 @@ class FormattingContextImpl implements FormattingContext {
|
|
|
655
726
|
|
|
656
727
|
resolveParagraph(para: ParagraphNode): EffectiveParagraphFormatting {
|
|
657
728
|
const cascade = this.resolveParagraphCascade(para);
|
|
658
|
-
|
|
729
|
+
return buildEffectiveParagraphFormatting(
|
|
730
|
+
cascade,
|
|
659
731
|
this.resolveParagraphNumbering(para, { advance: true }),
|
|
732
|
+
this.theme,
|
|
660
733
|
);
|
|
661
|
-
const paragraphMarkRun = cascade.paragraphMarkRunProperties && this.theme
|
|
662
|
-
? (concretizeThemeColors(cascade.paragraphMarkRunProperties, this.theme) as EffectiveRunFormatting)
|
|
663
|
-
: (cascade.paragraphMarkRunProperties as EffectiveRunFormatting | undefined);
|
|
664
|
-
return {
|
|
665
|
-
...cascade,
|
|
666
|
-
...(numbering ? { numbering } : {}),
|
|
667
|
-
...(paragraphMarkRun ? { paragraphMarkRun } : {}),
|
|
668
|
-
};
|
|
669
734
|
}
|
|
670
735
|
|
|
671
736
|
resolveRunWithProvenance(input: {
|
|
@@ -910,6 +975,42 @@ function buildEffectiveNumbering(
|
|
|
910
975
|
return result;
|
|
911
976
|
}
|
|
912
977
|
|
|
978
|
+
function buildEffectiveParagraphFormatting(
|
|
979
|
+
cascade: CanonicalParagraphFormatting,
|
|
980
|
+
numberingDetail: NumberingResolution | null,
|
|
981
|
+
theme: ThemeColorResolver | undefined,
|
|
982
|
+
): EffectiveParagraphFormatting {
|
|
983
|
+
const numbering = buildEffectiveNumbering(numberingDetail);
|
|
984
|
+
const paragraphMarkRun = cascade.paragraphMarkRunProperties && theme
|
|
985
|
+
? (concretizeThemeColors(cascade.paragraphMarkRunProperties, theme) as EffectiveRunFormatting)
|
|
986
|
+
: (cascade.paragraphMarkRunProperties as EffectiveRunFormatting | undefined);
|
|
987
|
+
return {
|
|
988
|
+
...cascade,
|
|
989
|
+
...(numbering ? { numbering } : {}),
|
|
990
|
+
...(paragraphMarkRun ? { paragraphMarkRun } : {}),
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function collectParagraphCompatFlags(
|
|
995
|
+
paragraph: EffectiveParagraphFormatting,
|
|
996
|
+
): readonly string[] {
|
|
997
|
+
const flags: string[] = [];
|
|
998
|
+
if (paragraph.keepNext === true) flags.push("keepNext");
|
|
999
|
+
if (paragraph.keepLines === true) flags.push("keepLines");
|
|
1000
|
+
if (paragraph.pageBreakBefore === true) flags.push("pageBreakBefore");
|
|
1001
|
+
if (paragraph.contextualSpacing === true) flags.push("contextualSpacing");
|
|
1002
|
+
if (paragraph.widowControl === false) flags.push("widowControlDisabled");
|
|
1003
|
+
if (paragraph.bidi === true) flags.push("bidi");
|
|
1004
|
+
if (paragraph.suppressLineNumbers === true) flags.push("suppressLineNumbers");
|
|
1005
|
+
return flags;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function mergeCompatFlags(
|
|
1009
|
+
...groups: readonly (readonly string[] | undefined)[]
|
|
1010
|
+
): readonly string[] {
|
|
1011
|
+
return [...new Set(groups.flatMap((group) => group ?? []))].sort();
|
|
1012
|
+
}
|
|
1013
|
+
|
|
913
1014
|
// Type-only re-export of HyperlinkNode so callers can type their walk.
|
|
914
1015
|
export type { HyperlinkNode, InlineNode };
|
|
915
1016
|
|
|
@@ -109,7 +109,9 @@ export {
|
|
|
109
109
|
buildRevisionLayoutPosture,
|
|
110
110
|
collectFieldLayoutInputs,
|
|
111
111
|
createStructuralHash,
|
|
112
|
+
mergeLayoutTabStops,
|
|
112
113
|
normalizeNumberingMarkerSuffix,
|
|
114
|
+
toMeasuredRevisionLayoutPosture,
|
|
113
115
|
toFieldLayoutInput,
|
|
114
116
|
toLayoutTabStops,
|
|
115
117
|
toNumberingLayoutInput,
|