@beyondwork/docx-react-component 1.0.109 → 1.0.111
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 +3 -0
- package/src/model/layout/page-graph-types.ts +33 -0
- package/src/model/layout/runtime-page-graph-types.ts +25 -0
- package/src/runtime/document-runtime.ts +46 -0
- package/src/runtime/geometry/adjacent-geometry-intake.ts +820 -15
- package/src/runtime/geometry/caret-geometry.ts +219 -7
- package/src/runtime/geometry/geometry-index.ts +52 -12
- package/src/runtime/geometry/object-handles.ts +42 -1
- package/src/runtime/layout/index.ts +3 -0
- package/src/runtime/layout/inert-layout-facet.ts +13 -0
- package/src/runtime/layout/layout-engine-instance.ts +233 -4
- package/src/runtime/layout/layout-engine-version.ts +47 -2
- package/src/runtime/layout/layout-facet-types.ts +3 -0
- package/src/runtime/layout/page-graph.ts +88 -7
- package/src/runtime/layout/paginated-layout-engine.ts +34 -0
- package/src/runtime/layout/project-block-fragments.ts +144 -1
- package/src/runtime/layout/public-facet.ts +228 -9
- package/src/runtime/layout/resolve-page-previews.ts +46 -8
- package/src/runtime/scopes/adjacent-geometry-evidence.ts +456 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +8 -0
- package/src/runtime/scopes/evidence.ts +16 -0
- package/src/runtime/scopes/index.ts +13 -0
- package/src/runtime/scopes/semantic-scope-types.ts +67 -0
- package/src/ui-tailwind/chrome-overlay/tw-table-split-row-carry-overlay.tsx +62 -0
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +104 -0
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +50 -5
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +27 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +62 -0
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +1 -0
- package/src/README.md +0 -85
- package/src/api/README.md +0 -26
- package/src/api/v3/README.md +0 -91
- package/src/component-inventory.md +0 -99
- package/src/core/README.md +0 -10
- package/src/core/commands/README.md +0 -3
- package/src/core/schema/README.md +0 -3
- package/src/core/selection/README.md +0 -3
- package/src/core/state/README.md +0 -3
- package/src/io/README.md +0 -10
- package/src/io/export/README.md +0 -3
- package/src/io/normalize/README.md +0 -3
- package/src/io/ooxml/README.md +0 -3
- package/src/io/opc/README.md +0 -3
- package/src/model/README.md +0 -3
- package/src/preservation/README.md +0 -3
- package/src/review/README.md +0 -16
- package/src/review/store/README.md +0 -3
- package/src/runtime/README.md +0 -3
- package/src/ui/README.md +0 -30
- package/src/ui/comments/README.md +0 -3
- package/src/ui/compatibility/README.md +0 -3
- package/src/ui/editor-surface/README.md +0 -3
- package/src/ui/review/README.md +0 -3
- package/src/ui/status/README.md +0 -3
- package/src/ui/theme/README.md +0 -3
- package/src/ui/toolbar/README.md +0 -3
- package/src/ui-tailwind/debug/README.md +0 -22
- package/src/validation/README.md +0 -3
|
@@ -31,6 +31,16 @@ export interface PagePreviewMaps {
|
|
|
31
31
|
footerPreviewByPageId: Map<string, string>;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
type PageLocalFieldLedger = readonly {
|
|
35
|
+
readonly family: string;
|
|
36
|
+
readonly displayText: string;
|
|
37
|
+
}[];
|
|
38
|
+
|
|
39
|
+
interface PagePreviewFieldState {
|
|
40
|
+
readonly ledger: PageLocalFieldLedger | undefined;
|
|
41
|
+
ordinal: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
export function buildPagePreviewMaps(
|
|
35
45
|
graph: RuntimePageGraph,
|
|
36
46
|
subParts: {
|
|
@@ -49,7 +59,10 @@ export function buildPagePreviewMaps(
|
|
|
49
59
|
if (source) {
|
|
50
60
|
headerPreviewByPageId.set(
|
|
51
61
|
page.pageId,
|
|
52
|
-
flattenBlocksToPreview(source.blocks, page, graph
|
|
62
|
+
flattenBlocksToPreview(source.blocks, page, graph, {
|
|
63
|
+
ledger: findPageLocalStoryFieldLedger(page, headerStory),
|
|
64
|
+
ordinal: 0,
|
|
65
|
+
}),
|
|
53
66
|
);
|
|
54
67
|
}
|
|
55
68
|
}
|
|
@@ -59,7 +72,10 @@ export function buildPagePreviewMaps(
|
|
|
59
72
|
if (source) {
|
|
60
73
|
footerPreviewByPageId.set(
|
|
61
74
|
page.pageId,
|
|
62
|
-
flattenBlocksToPreview(source.blocks, page, graph
|
|
75
|
+
flattenBlocksToPreview(source.blocks, page, graph, {
|
|
76
|
+
ledger: findPageLocalStoryFieldLedger(page, footerStory),
|
|
77
|
+
ordinal: 0,
|
|
78
|
+
}),
|
|
63
79
|
);
|
|
64
80
|
}
|
|
65
81
|
}
|
|
@@ -76,14 +92,30 @@ function findSubPart<T extends HeaderDocument | FooterDocument>(
|
|
|
76
92
|
return parts.find((p) => p.relationshipId === relationshipId);
|
|
77
93
|
}
|
|
78
94
|
|
|
95
|
+
function findPageLocalStoryFieldLedger(
|
|
96
|
+
page: RuntimePageNode,
|
|
97
|
+
story: NonNullable<RuntimePageNode["stories"]["header" | "footer"]>,
|
|
98
|
+
): PageLocalFieldLedger | undefined {
|
|
99
|
+
const pageLocalStory = page.frame?.pageLocalStories.find(
|
|
100
|
+
(candidate) =>
|
|
101
|
+
candidate.kind === story.kind &&
|
|
102
|
+
candidate.relationshipId === story.relationshipId &&
|
|
103
|
+
candidate.variant === story.variant &&
|
|
104
|
+
(story.sectionIndex === undefined ||
|
|
105
|
+
candidate.sectionIndex === story.sectionIndex),
|
|
106
|
+
);
|
|
107
|
+
return pageLocalStory?.resolvedFields;
|
|
108
|
+
}
|
|
109
|
+
|
|
79
110
|
function flattenBlocksToPreview(
|
|
80
111
|
blocks: ReadonlyArray<BlockNode>,
|
|
81
112
|
page: RuntimePageNode,
|
|
82
113
|
graph: RuntimePageGraph,
|
|
114
|
+
fieldState: PagePreviewFieldState,
|
|
83
115
|
): string {
|
|
84
116
|
const parts: string[] = [];
|
|
85
117
|
for (const block of blocks) {
|
|
86
|
-
collectBlockText(block, page, graph, parts);
|
|
118
|
+
collectBlockText(block, page, graph, fieldState, parts);
|
|
87
119
|
if (joinPreview(parts).length >= MAX_PREVIEW_CHARS) break;
|
|
88
120
|
}
|
|
89
121
|
return truncate(joinPreview(parts));
|
|
@@ -93,26 +125,27 @@ function collectBlockText(
|
|
|
93
125
|
block: BlockNode,
|
|
94
126
|
page: RuntimePageNode,
|
|
95
127
|
graph: RuntimePageGraph,
|
|
128
|
+
fieldState: PagePreviewFieldState,
|
|
96
129
|
out: string[],
|
|
97
130
|
): void {
|
|
98
131
|
switch (block.type) {
|
|
99
132
|
case "paragraph":
|
|
100
133
|
for (const child of block.children) {
|
|
101
|
-
collectInlineText(child, page, graph, out);
|
|
134
|
+
collectInlineText(child, page, graph, fieldState, out);
|
|
102
135
|
}
|
|
103
136
|
break;
|
|
104
137
|
case "table":
|
|
105
138
|
for (const row of block.rows) {
|
|
106
139
|
for (const cell of row.cells) {
|
|
107
140
|
for (const childBlock of cell.children) {
|
|
108
|
-
collectBlockText(childBlock, page, graph, out);
|
|
141
|
+
collectBlockText(childBlock, page, graph, fieldState, out);
|
|
109
142
|
}
|
|
110
143
|
}
|
|
111
144
|
}
|
|
112
145
|
break;
|
|
113
146
|
case "sdt":
|
|
114
147
|
for (const child of block.children) {
|
|
115
|
-
collectBlockText(child, page, graph, out);
|
|
148
|
+
collectBlockText(child, page, graph, fieldState, out);
|
|
116
149
|
}
|
|
117
150
|
break;
|
|
118
151
|
// opaque_block / section_break / custom_xml / alt_chunk — no preview text.
|
|
@@ -125,6 +158,7 @@ function collectInlineText(
|
|
|
125
158
|
inline: InlineNode,
|
|
126
159
|
page: RuntimePageNode,
|
|
127
160
|
graph: RuntimePageGraph,
|
|
161
|
+
fieldState: PagePreviewFieldState,
|
|
128
162
|
out: string[],
|
|
129
163
|
): void {
|
|
130
164
|
switch (inline.type) {
|
|
@@ -141,8 +175,12 @@ function collectInlineText(
|
|
|
141
175
|
const family = inline.fieldFamily ?? classifyFieldInstructionLocal(inline.instruction);
|
|
142
176
|
const cached = flattenInline(inline.children);
|
|
143
177
|
if (family === "PAGE" || family === "NUMPAGES" || family === "SECTIONPAGES") {
|
|
178
|
+
const ledgerField = fieldState.ledger?.[fieldState.ordinal];
|
|
179
|
+
fieldState.ordinal += 1;
|
|
144
180
|
out.push(
|
|
145
|
-
|
|
181
|
+
ledgerField?.family === family
|
|
182
|
+
? ledgerField.displayText
|
|
183
|
+
: resolvePageFieldDisplayText(family, cached, { page, graph }),
|
|
146
184
|
);
|
|
147
185
|
} else {
|
|
148
186
|
out.push(cached);
|
|
@@ -151,7 +189,7 @@ function collectInlineText(
|
|
|
151
189
|
}
|
|
152
190
|
case "hyperlink":
|
|
153
191
|
for (const child of inline.children) {
|
|
154
|
-
collectInlineText(child, page, graph, out);
|
|
192
|
+
collectInlineText(child, page, graph, fieldState, out);
|
|
155
193
|
}
|
|
156
194
|
break;
|
|
157
195
|
// bookmark_start/end, opaque_inline, note_ref etc — skip.
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded adjacent geometry evidence from Layer 05.
|
|
3
|
+
*
|
|
4
|
+
* L08 consumes only compositor-ready `frame-px` rows from the L05 adjacent
|
|
5
|
+
* geometry intake. This evidence is read-side scope-bundle context; it is not
|
|
6
|
+
* replacement-envelope geometry and must not widen mutation capability.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { FieldEnumeratedScope, EnumeratedScope } from "./enumerate-scopes.ts";
|
|
10
|
+
import type {
|
|
11
|
+
ScopeAdjacentGeometryEvidence,
|
|
12
|
+
ScopeAdjacentGeometryFramePixelPoint,
|
|
13
|
+
ScopeAdjacentGeometryFramePixelRect,
|
|
14
|
+
ScopeAdjacentGeometryRowEvidence,
|
|
15
|
+
SemanticScope,
|
|
16
|
+
} from "./semantic-scope-types.ts";
|
|
17
|
+
|
|
18
|
+
interface FramePixelScaleLike {
|
|
19
|
+
readonly source?: unknown;
|
|
20
|
+
readonly renderFrameRevision?: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface FramePixelProjectionLike {
|
|
24
|
+
readonly status?: unknown;
|
|
25
|
+
readonly coordinateSpace?: unknown;
|
|
26
|
+
readonly precision?: unknown;
|
|
27
|
+
readonly pageIndex?: unknown;
|
|
28
|
+
readonly pageId?: unknown;
|
|
29
|
+
readonly frameId?: unknown;
|
|
30
|
+
readonly scale?: FramePixelScaleLike;
|
|
31
|
+
readonly markerLane?: unknown;
|
|
32
|
+
readonly textColumn?: unknown;
|
|
33
|
+
readonly fieldStartAnchorPx?: unknown;
|
|
34
|
+
readonly fieldEndAnchorPx?: unknown;
|
|
35
|
+
readonly fieldResultRangePx?: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface AdjacentPageLocalGeometryLike {
|
|
39
|
+
readonly compositorReady?: unknown;
|
|
40
|
+
readonly framePixelProjection?: FramePixelProjectionLike;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface AdjacentNumberingRowLike {
|
|
44
|
+
readonly docId?: unknown;
|
|
45
|
+
readonly canonicalBlockId?: unknown;
|
|
46
|
+
readonly runtimeBlockId?: unknown;
|
|
47
|
+
readonly runtimeFragmentId?: unknown;
|
|
48
|
+
readonly runtimeNumberingId?: unknown;
|
|
49
|
+
readonly canonicalNumberingInstanceId?: unknown;
|
|
50
|
+
readonly canonicalLevel?: unknown;
|
|
51
|
+
readonly markerKind?: unknown;
|
|
52
|
+
readonly markerSuffix?: unknown;
|
|
53
|
+
readonly pageLocalGeometry?: AdjacentPageLocalGeometryLike;
|
|
54
|
+
readonly compositorReady?: unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface AdjacentFieldRegionRowLike {
|
|
58
|
+
readonly docId?: unknown;
|
|
59
|
+
readonly canonicalBlockId?: unknown;
|
|
60
|
+
readonly canonicalFieldId?: unknown;
|
|
61
|
+
readonly runtimeFieldRegionId?: unknown;
|
|
62
|
+
readonly runtimeFragmentId?: unknown;
|
|
63
|
+
readonly instructionFamily?: unknown;
|
|
64
|
+
readonly pageLocalGeometry?: AdjacentPageLocalGeometryLike;
|
|
65
|
+
readonly compositorReady?: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AdjacentGeometryIntakeLike {
|
|
69
|
+
readonly schemaVersion?: unknown;
|
|
70
|
+
readonly pageLocalNormalization?: {
|
|
71
|
+
readonly framePixelCoordinateSpace?: unknown;
|
|
72
|
+
readonly compositorReady?: unknown;
|
|
73
|
+
};
|
|
74
|
+
readonly numberingRows?: readonly AdjacentNumberingRowLike[];
|
|
75
|
+
readonly fieldRegionRows?: readonly AdjacentFieldRegionRowLike[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ScopeAdjacentGeometryEvidenceProvider {
|
|
79
|
+
getScopeAdjacentGeometryEvidence(
|
|
80
|
+
scope: SemanticScope,
|
|
81
|
+
entry: EnumeratedScope | null,
|
|
82
|
+
): ScopeAdjacentGeometryEvidence | null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ScopeAdjacentGeometryIntakeSummary {
|
|
86
|
+
readonly schemaVersion: string | null;
|
|
87
|
+
readonly intakeReady: boolean;
|
|
88
|
+
readonly numberingCompositorReadyRows: number;
|
|
89
|
+
readonly fieldRegionCompositorReadyRows: number;
|
|
90
|
+
readonly eligibleRowCount: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function stringValue(value: unknown): string | undefined {
|
|
94
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function numberValue(value: unknown): number | undefined {
|
|
98
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseParagraphBlockIndex(value: unknown): number | undefined {
|
|
102
|
+
const raw = stringValue(value);
|
|
103
|
+
if (!raw) return undefined;
|
|
104
|
+
const match = /^paragraph-(\d+)$/.exec(raw);
|
|
105
|
+
return match ? Number(match[1]) : undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function docMatches(scope: SemanticScope, docId: string | undefined): boolean {
|
|
109
|
+
return !docId || !scope.handle.documentId || scope.handle.documentId === docId;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isCompositorReady(geometry: AdjacentPageLocalGeometryLike | undefined): boolean {
|
|
113
|
+
const projection = geometry?.framePixelProjection;
|
|
114
|
+
return (
|
|
115
|
+
geometry?.compositorReady === true &&
|
|
116
|
+
projection?.status === "projected-frame-pixels" &&
|
|
117
|
+
projection.coordinateSpace === "frame-px" &&
|
|
118
|
+
projection.precision === "word-page-local-calibration"
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeRect(value: unknown): ScopeAdjacentGeometryFramePixelRect | undefined {
|
|
123
|
+
const rect = value as
|
|
124
|
+
| {
|
|
125
|
+
readonly leftPx?: unknown;
|
|
126
|
+
readonly topPx?: unknown;
|
|
127
|
+
readonly widthPx?: unknown;
|
|
128
|
+
readonly heightPx?: unknown;
|
|
129
|
+
readonly coordinateSpace?: unknown;
|
|
130
|
+
}
|
|
131
|
+
| undefined;
|
|
132
|
+
const leftPx = numberValue(rect?.leftPx);
|
|
133
|
+
const topPx = numberValue(rect?.topPx);
|
|
134
|
+
const widthPx = numberValue(rect?.widthPx);
|
|
135
|
+
const heightPx = numberValue(rect?.heightPx);
|
|
136
|
+
if (
|
|
137
|
+
leftPx === undefined ||
|
|
138
|
+
topPx === undefined ||
|
|
139
|
+
widthPx === undefined ||
|
|
140
|
+
heightPx === undefined ||
|
|
141
|
+
rect?.coordinateSpace !== "frame-px"
|
|
142
|
+
) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
return { leftPx, topPx, widthPx, heightPx, coordinateSpace: "frame-px" };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function normalizePoint(value: unknown): ScopeAdjacentGeometryFramePixelPoint | undefined {
|
|
149
|
+
const point = value as
|
|
150
|
+
| {
|
|
151
|
+
readonly xPx?: unknown;
|
|
152
|
+
readonly yPx?: unknown;
|
|
153
|
+
readonly coordinateSpace?: unknown;
|
|
154
|
+
}
|
|
155
|
+
| undefined;
|
|
156
|
+
const xPx = numberValue(point?.xPx);
|
|
157
|
+
const yPx = numberValue(point?.yPx);
|
|
158
|
+
if (xPx === undefined || yPx === undefined || point?.coordinateSpace !== "frame-px") {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
return { xPx, yPx, coordinateSpace: "frame-px" };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function baseRow(
|
|
165
|
+
axis: ScopeAdjacentGeometryRowEvidence["axis"],
|
|
166
|
+
input: {
|
|
167
|
+
readonly docId?: string;
|
|
168
|
+
readonly canonicalBlockId?: string;
|
|
169
|
+
readonly runtimeFragmentId?: string;
|
|
170
|
+
readonly projection: FramePixelProjectionLike;
|
|
171
|
+
},
|
|
172
|
+
): Omit<ScopeAdjacentGeometryRowEvidence, "numbering" | "fieldRegion"> {
|
|
173
|
+
return {
|
|
174
|
+
axis,
|
|
175
|
+
source: "l05-adjacent-geometry-intake",
|
|
176
|
+
...(input.docId ? { docId: input.docId } : {}),
|
|
177
|
+
...(input.canonicalBlockId ? { canonicalBlockId: input.canonicalBlockId } : {}),
|
|
178
|
+
...(input.runtimeFragmentId ? { runtimeFragmentId: input.runtimeFragmentId } : {}),
|
|
179
|
+
...(typeof input.projection.pageId === "string"
|
|
180
|
+
? { pageId: input.projection.pageId }
|
|
181
|
+
: {}),
|
|
182
|
+
...(typeof input.projection.frameId === "string"
|
|
183
|
+
? { frameId: input.projection.frameId }
|
|
184
|
+
: {}),
|
|
185
|
+
...(numberValue(input.projection.pageIndex) !== undefined
|
|
186
|
+
? { pageIndex: numberValue(input.projection.pageIndex) }
|
|
187
|
+
: {}),
|
|
188
|
+
precision: "word-page-local-calibration",
|
|
189
|
+
coordinateSpace: "frame-px",
|
|
190
|
+
compositorReady: true,
|
|
191
|
+
...(input.projection.scale?.source === "runtime-render-frame-page-rect"
|
|
192
|
+
? { scaleSource: "runtime-render-frame-page-rect" as const }
|
|
193
|
+
: {}),
|
|
194
|
+
...(numberValue(input.projection.scale?.renderFrameRevision) !== undefined
|
|
195
|
+
? { renderFrameRevision: numberValue(input.projection.scale?.renderFrameRevision) }
|
|
196
|
+
: {}),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function projectNumberingRow(row: AdjacentNumberingRowLike): ScopeAdjacentGeometryRowEvidence | null {
|
|
201
|
+
if (row.compositorReady !== true || !isCompositorReady(row.pageLocalGeometry)) return null;
|
|
202
|
+
const projection = row.pageLocalGeometry!.framePixelProjection!;
|
|
203
|
+
const docId = stringValue(row.docId);
|
|
204
|
+
const canonicalBlockId = stringValue(row.canonicalBlockId) ?? stringValue(row.runtimeBlockId);
|
|
205
|
+
return {
|
|
206
|
+
...baseRow("numbering-marker", {
|
|
207
|
+
docId,
|
|
208
|
+
canonicalBlockId,
|
|
209
|
+
runtimeFragmentId: stringValue(row.runtimeFragmentId),
|
|
210
|
+
projection,
|
|
211
|
+
}),
|
|
212
|
+
numbering: {
|
|
213
|
+
...(stringValue(row.runtimeNumberingId)
|
|
214
|
+
? { runtimeNumberingId: stringValue(row.runtimeNumberingId) }
|
|
215
|
+
: {}),
|
|
216
|
+
...(stringValue(row.canonicalNumberingInstanceId)
|
|
217
|
+
? { canonicalNumberingInstanceId: stringValue(row.canonicalNumberingInstanceId) }
|
|
218
|
+
: {}),
|
|
219
|
+
...(numberValue(row.canonicalLevel) !== undefined
|
|
220
|
+
? { canonicalLevel: numberValue(row.canonicalLevel) }
|
|
221
|
+
: {}),
|
|
222
|
+
...(stringValue(row.markerKind) ? { markerKind: stringValue(row.markerKind) } : {}),
|
|
223
|
+
...(stringValue(row.markerSuffix) ? { markerSuffix: stringValue(row.markerSuffix) } : {}),
|
|
224
|
+
...(normalizeRect(projection.markerLane)
|
|
225
|
+
? { markerLane: normalizeRect(projection.markerLane) }
|
|
226
|
+
: {}),
|
|
227
|
+
...(normalizeRect(projection.textColumn)
|
|
228
|
+
? { textColumn: normalizeRect(projection.textColumn) }
|
|
229
|
+
: {}),
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function projectFieldRegionRow(
|
|
235
|
+
row: AdjacentFieldRegionRowLike,
|
|
236
|
+
): ScopeAdjacentGeometryRowEvidence | null {
|
|
237
|
+
if (row.compositorReady !== true || !isCompositorReady(row.pageLocalGeometry)) return null;
|
|
238
|
+
const projection = row.pageLocalGeometry!.framePixelProjection!;
|
|
239
|
+
const docId = stringValue(row.docId);
|
|
240
|
+
const canonicalBlockId = stringValue(row.canonicalBlockId);
|
|
241
|
+
return {
|
|
242
|
+
...baseRow("field-region", {
|
|
243
|
+
docId,
|
|
244
|
+
canonicalBlockId,
|
|
245
|
+
runtimeFragmentId: stringValue(row.runtimeFragmentId),
|
|
246
|
+
projection,
|
|
247
|
+
}),
|
|
248
|
+
fieldRegion: {
|
|
249
|
+
...(stringValue(row.canonicalFieldId)
|
|
250
|
+
? { canonicalFieldId: stringValue(row.canonicalFieldId) }
|
|
251
|
+
: {}),
|
|
252
|
+
...(stringValue(row.runtimeFieldRegionId)
|
|
253
|
+
? { runtimeFieldRegionId: stringValue(row.runtimeFieldRegionId) }
|
|
254
|
+
: {}),
|
|
255
|
+
...(stringValue(row.instructionFamily)
|
|
256
|
+
? { instructionFamily: stringValue(row.instructionFamily) }
|
|
257
|
+
: {}),
|
|
258
|
+
...(normalizePoint(projection.fieldStartAnchorPx)
|
|
259
|
+
? { fieldStartAnchorPx: normalizePoint(projection.fieldStartAnchorPx) }
|
|
260
|
+
: {}),
|
|
261
|
+
...(normalizePoint(projection.fieldEndAnchorPx)
|
|
262
|
+
? { fieldEndAnchorPx: normalizePoint(projection.fieldEndAnchorPx) }
|
|
263
|
+
: {}),
|
|
264
|
+
...(normalizeRect(projection.fieldResultRangePx)
|
|
265
|
+
? { fieldResultRangePx: normalizeRect(projection.fieldResultRangePx) }
|
|
266
|
+
: {}),
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function numberingRowMatchesScope(
|
|
272
|
+
row: ScopeAdjacentGeometryRowEvidence,
|
|
273
|
+
scope: SemanticScope,
|
|
274
|
+
entry: EnumeratedScope | null,
|
|
275
|
+
): boolean {
|
|
276
|
+
if (scope.kind !== "list-item" || entry?.kind !== "list-item") return false;
|
|
277
|
+
if (!docMatches(scope, row.docId)) return false;
|
|
278
|
+
const rowBlockIndex = parseParagraphBlockIndex(row.canonicalBlockId);
|
|
279
|
+
if (rowBlockIndex !== undefined && rowBlockIndex !== entry.blockIndex) return false;
|
|
280
|
+
const numbering = row.numbering;
|
|
281
|
+
if (
|
|
282
|
+
numbering?.canonicalNumberingInstanceId &&
|
|
283
|
+
scope.formatting.numbering?.numberingInstanceId &&
|
|
284
|
+
numbering.canonicalNumberingInstanceId !== scope.formatting.numbering.numberingInstanceId
|
|
285
|
+
) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
if (
|
|
289
|
+
numbering?.canonicalLevel !== undefined &&
|
|
290
|
+
scope.formatting.numbering?.level !== undefined &&
|
|
291
|
+
numbering.canonicalLevel !== scope.formatting.numbering.level
|
|
292
|
+
) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
return rowBlockIndex !== undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function fieldRowMatchesScope(
|
|
299
|
+
row: ScopeAdjacentGeometryRowEvidence,
|
|
300
|
+
scope: SemanticScope,
|
|
301
|
+
entry: EnumeratedScope | null,
|
|
302
|
+
): boolean {
|
|
303
|
+
if (scope.kind !== "field" || entry?.kind !== "field") return false;
|
|
304
|
+
if (!docMatches(scope, row.docId)) return false;
|
|
305
|
+
const fieldEntry = entry as FieldEnumeratedScope;
|
|
306
|
+
const rowCanonicalFieldId = row.fieldRegion?.canonicalFieldId;
|
|
307
|
+
if (rowCanonicalFieldId && fieldEntry.field.canonicalFieldId) {
|
|
308
|
+
return rowCanonicalFieldId === fieldEntry.field.canonicalFieldId;
|
|
309
|
+
}
|
|
310
|
+
const rowBlockIndex = parseParagraphBlockIndex(row.canonicalBlockId);
|
|
311
|
+
return rowBlockIndex !== undefined && rowBlockIndex === fieldEntry.blockIndex;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function createAdjacentGeometryScopeEvidenceProvider(
|
|
315
|
+
intake: AdjacentGeometryIntakeLike,
|
|
316
|
+
): ScopeAdjacentGeometryEvidenceProvider {
|
|
317
|
+
const intakeReady = isIntakeReady(intake);
|
|
318
|
+
const numberingRows = intakeReady
|
|
319
|
+
? (intake.numberingRows ?? []).map(projectNumberingRow).filter(isEvidenceRow)
|
|
320
|
+
: [];
|
|
321
|
+
const fieldRegionRows = intakeReady
|
|
322
|
+
? (intake.fieldRegionRows ?? []).map(projectFieldRegionRow).filter(isEvidenceRow)
|
|
323
|
+
: [];
|
|
324
|
+
const allRows = Object.freeze([...numberingRows, ...fieldRegionRows]);
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
getScopeAdjacentGeometryEvidence(scope, entry) {
|
|
328
|
+
if (!intakeReady) {
|
|
329
|
+
return {
|
|
330
|
+
status: "unavailable",
|
|
331
|
+
source: "l05-adjacent-geometry-intake",
|
|
332
|
+
rowCount: 0,
|
|
333
|
+
reason: "l05-adjacent-intake-not-compositor-ready",
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const rows = allRows.filter(
|
|
337
|
+
(row) =>
|
|
338
|
+
numberingRowMatchesScope(row, scope, entry) ||
|
|
339
|
+
fieldRowMatchesScope(row, scope, entry),
|
|
340
|
+
);
|
|
341
|
+
if (rows.length === 0) return null;
|
|
342
|
+
return {
|
|
343
|
+
status: "available",
|
|
344
|
+
source: "l05-adjacent-geometry-intake",
|
|
345
|
+
schemaVersion: "layer-05-adjacent-geometry-intake/v2",
|
|
346
|
+
rowCount: rows.length,
|
|
347
|
+
rows: Object.freeze(rows.map(cloneAdjacentGeometryRow)),
|
|
348
|
+
};
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export function summarizeAdjacentGeometryEvidenceIntake(
|
|
354
|
+
intake: AdjacentGeometryIntakeLike,
|
|
355
|
+
): ScopeAdjacentGeometryIntakeSummary {
|
|
356
|
+
const intakeReady = isIntakeReady(intake);
|
|
357
|
+
const numberingCompositorReadyRows = intakeReady
|
|
358
|
+
? (intake.numberingRows ?? []).map(projectNumberingRow).filter(isEvidenceRow).length
|
|
359
|
+
: 0;
|
|
360
|
+
const fieldRegionCompositorReadyRows = intakeReady
|
|
361
|
+
? (intake.fieldRegionRows ?? []).map(projectFieldRegionRow).filter(isEvidenceRow).length
|
|
362
|
+
: 0;
|
|
363
|
+
return {
|
|
364
|
+
schemaVersion: typeof intake.schemaVersion === "string" ? intake.schemaVersion : null,
|
|
365
|
+
intakeReady,
|
|
366
|
+
numberingCompositorReadyRows,
|
|
367
|
+
fieldRegionCompositorReadyRows,
|
|
368
|
+
eligibleRowCount: numberingCompositorReadyRows + fieldRegionCompositorReadyRows,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function isIntakeReady(intake: AdjacentGeometryIntakeLike): boolean {
|
|
373
|
+
return (
|
|
374
|
+
intake.schemaVersion === "layer-05-adjacent-geometry-intake/v2" &&
|
|
375
|
+
intake.pageLocalNormalization?.framePixelCoordinateSpace === "frame-px" &&
|
|
376
|
+
intake.pageLocalNormalization.compositorReady === true
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function isEvidenceRow(
|
|
381
|
+
row: ScopeAdjacentGeometryRowEvidence | null,
|
|
382
|
+
): row is ScopeAdjacentGeometryRowEvidence {
|
|
383
|
+
return row !== null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function cloneRect(
|
|
387
|
+
rect: ScopeAdjacentGeometryFramePixelRect | undefined,
|
|
388
|
+
): ScopeAdjacentGeometryFramePixelRect | undefined {
|
|
389
|
+
return rect ? { ...rect } : undefined;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function clonePoint(
|
|
393
|
+
point: ScopeAdjacentGeometryFramePixelPoint | undefined,
|
|
394
|
+
): ScopeAdjacentGeometryFramePixelPoint | undefined {
|
|
395
|
+
return point ? { ...point } : undefined;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function cloneAdjacentGeometryRow(
|
|
399
|
+
row: ScopeAdjacentGeometryRowEvidence,
|
|
400
|
+
): ScopeAdjacentGeometryRowEvidence {
|
|
401
|
+
return {
|
|
402
|
+
...row,
|
|
403
|
+
...(row.numbering
|
|
404
|
+
? {
|
|
405
|
+
numbering: {
|
|
406
|
+
...row.numbering,
|
|
407
|
+
...(row.numbering.markerLane
|
|
408
|
+
? { markerLane: cloneRect(row.numbering.markerLane) }
|
|
409
|
+
: {}),
|
|
410
|
+
...(row.numbering.textColumn
|
|
411
|
+
? { textColumn: cloneRect(row.numbering.textColumn) }
|
|
412
|
+
: {}),
|
|
413
|
+
},
|
|
414
|
+
}
|
|
415
|
+
: {}),
|
|
416
|
+
...(row.fieldRegion
|
|
417
|
+
? {
|
|
418
|
+
fieldRegion: {
|
|
419
|
+
...row.fieldRegion,
|
|
420
|
+
...(row.fieldRegion.fieldStartAnchorPx
|
|
421
|
+
? { fieldStartAnchorPx: clonePoint(row.fieldRegion.fieldStartAnchorPx) }
|
|
422
|
+
: {}),
|
|
423
|
+
...(row.fieldRegion.fieldEndAnchorPx
|
|
424
|
+
? { fieldEndAnchorPx: clonePoint(row.fieldRegion.fieldEndAnchorPx) }
|
|
425
|
+
: {}),
|
|
426
|
+
...(row.fieldRegion.fieldResultRangePx
|
|
427
|
+
? { fieldResultRangePx: cloneRect(row.fieldRegion.fieldResultRangePx) }
|
|
428
|
+
: {}),
|
|
429
|
+
},
|
|
430
|
+
}
|
|
431
|
+
: {}),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function deriveScopeAdjacentGeometryEvidence(
|
|
436
|
+
scope: SemanticScope,
|
|
437
|
+
entry: EnumeratedScope | null,
|
|
438
|
+
provider?: ScopeAdjacentGeometryEvidenceProvider,
|
|
439
|
+
): ScopeAdjacentGeometryEvidence | undefined {
|
|
440
|
+
if (!provider) return undefined;
|
|
441
|
+
const evidence = provider.getScopeAdjacentGeometryEvidence(scope, entry);
|
|
442
|
+
if (!evidence) {
|
|
443
|
+
return {
|
|
444
|
+
status: "unavailable",
|
|
445
|
+
source: "l05-adjacent-geometry-intake",
|
|
446
|
+
rowCount: 0,
|
|
447
|
+
reason: "scope-adjacent-frame-pixel-row-unavailable",
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
...evidence,
|
|
452
|
+
...(evidence.rows
|
|
453
|
+
? { rows: Object.freeze(evidence.rows.map(cloneAdjacentGeometryRow)) }
|
|
454
|
+
: {}),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
WorkflowOverlay,
|
|
19
19
|
} from "./_scope-dependencies.ts";
|
|
20
20
|
import type { ScopeGeometryEvidenceProvider } from "./geometry-evidence.ts";
|
|
21
|
+
import type { ScopeAdjacentGeometryEvidenceProvider } from "./adjacent-geometry-evidence.ts";
|
|
21
22
|
|
|
22
23
|
import {
|
|
23
24
|
buildParagraphIndexMap,
|
|
@@ -59,6 +60,12 @@ export interface ScopeBundleInputs {
|
|
|
59
60
|
* records layout as unavailable instead of deriving page slices in L08.
|
|
60
61
|
*/
|
|
61
62
|
readonly layout?: ScopeLayoutEvidenceProvider;
|
|
63
|
+
/**
|
|
64
|
+
* Optional Layer-05 adjacent geometry intake seam for compositor-ready
|
|
65
|
+
* `frame-px` rows. Kept separate from replacement-envelope geometry so it
|
|
66
|
+
* cannot widen edit capability.
|
|
67
|
+
*/
|
|
68
|
+
readonly adjacentGeometry?: ScopeAdjacentGeometryEvidenceProvider;
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
/**
|
|
@@ -140,6 +147,7 @@ export function compileScopeBundle(
|
|
|
140
147
|
: {}),
|
|
141
148
|
...(inputs.geometry ? { geometry: inputs.geometry } : {}),
|
|
142
149
|
...(inputs.layout ? { layout: inputs.layout } : {}),
|
|
150
|
+
...(inputs.adjacentGeometry ? { adjacentGeometry: inputs.adjacentGeometry } : {}),
|
|
143
151
|
});
|
|
144
152
|
return {
|
|
145
153
|
scope,
|
|
@@ -24,6 +24,10 @@ import type {
|
|
|
24
24
|
} from "./_scope-dependencies.ts";
|
|
25
25
|
|
|
26
26
|
import { AI_EXPLANATION_METADATA_ID } from "./attach-explanation.ts";
|
|
27
|
+
import {
|
|
28
|
+
deriveScopeAdjacentGeometryEvidence,
|
|
29
|
+
type ScopeAdjacentGeometryEvidenceProvider,
|
|
30
|
+
} from "./adjacent-geometry-evidence.ts";
|
|
27
31
|
import { deriveScopeCapabilities } from "./capabilities.ts";
|
|
28
32
|
import { deriveScopeContentControlEvidence } from "./content-control-evidence.ts";
|
|
29
33
|
import { AI_ISSUE_METADATA_ID } from "./create-issue.ts";
|
|
@@ -107,6 +111,12 @@ export interface EvidenceInputs {
|
|
|
107
111
|
* rows are surfaced explicitly in `ScopeBundleEvidence.layout`.
|
|
108
112
|
*/
|
|
109
113
|
readonly layout?: ScopeLayoutEvidenceProvider;
|
|
114
|
+
/**
|
|
115
|
+
* Optional Layer-05 adjacent geometry intake seam. This is bounded
|
|
116
|
+
* compositor/read evidence and does not participate in replacement
|
|
117
|
+
* capability derivation.
|
|
118
|
+
*/
|
|
119
|
+
readonly adjacentGeometry?: ScopeAdjacentGeometryEvidenceProvider;
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
function normalizeSeverity(raw: unknown): AIIssueSummary["severity"] {
|
|
@@ -301,6 +311,11 @@ export function composeEvidence(inputs: EvidenceInputs): ScopeBundleEvidence {
|
|
|
301
311
|
|
|
302
312
|
const layout = deriveScopeLayoutEvidence(scope.handle.scopeId, inputs.layout);
|
|
303
313
|
const geometry = deriveScopeGeometryEvidence(scope.handle.scopeId, inputs.geometry);
|
|
314
|
+
const adjacentGeometry = deriveScopeAdjacentGeometryEvidence(
|
|
315
|
+
scope,
|
|
316
|
+
entry,
|
|
317
|
+
inputs.adjacentGeometry,
|
|
318
|
+
);
|
|
304
319
|
const contentControls = deriveScopeContentControlEvidence(document, selfRange);
|
|
305
320
|
|
|
306
321
|
return {
|
|
@@ -310,6 +325,7 @@ export function composeEvidence(inputs: EvidenceInputs): ScopeBundleEvidence {
|
|
|
310
325
|
compatibilityFlags,
|
|
311
326
|
layout,
|
|
312
327
|
geometry,
|
|
328
|
+
...(adjacentGeometry ? { adjacentGeometry } : {}),
|
|
313
329
|
visualization: deriveScopeVisualization(scope),
|
|
314
330
|
contentControls,
|
|
315
331
|
capabilities: deriveScopeCapabilities(scope, {
|
|
@@ -52,6 +52,11 @@ export type {
|
|
|
52
52
|
ScopeFormattingClearTarget,
|
|
53
53
|
ScopeFormattingScope,
|
|
54
54
|
ScopeActionPosture,
|
|
55
|
+
ScopeAdjacentGeometryAxis,
|
|
56
|
+
ScopeAdjacentGeometryEvidence,
|
|
57
|
+
ScopeAdjacentGeometryFramePixelPoint,
|
|
58
|
+
ScopeAdjacentGeometryFramePixelRect,
|
|
59
|
+
ScopeAdjacentGeometryRowEvidence,
|
|
55
60
|
ScopeBundle,
|
|
56
61
|
ScopeBundleEvidence,
|
|
57
62
|
ScopeBundleNeighborhood,
|
|
@@ -73,6 +78,14 @@ export type {
|
|
|
73
78
|
ValidationIssue,
|
|
74
79
|
ValidationResult,
|
|
75
80
|
} from "./semantic-scope-types.ts";
|
|
81
|
+
export {
|
|
82
|
+
createAdjacentGeometryScopeEvidenceProvider,
|
|
83
|
+
deriveScopeAdjacentGeometryEvidence,
|
|
84
|
+
summarizeAdjacentGeometryEvidenceIntake,
|
|
85
|
+
type AdjacentGeometryIntakeLike,
|
|
86
|
+
type ScopeAdjacentGeometryIntakeSummary,
|
|
87
|
+
type ScopeAdjacentGeometryEvidenceProvider,
|
|
88
|
+
} from "./adjacent-geometry-evidence.ts";
|
|
76
89
|
export { deriveScopeCapabilities } from "./capabilities.ts";
|
|
77
90
|
export type { ScopeCapabilityContext } from "./capabilities.ts";
|
|
78
91
|
export {
|