@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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.105",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": [
|
package/src/api/public-types.ts
CHANGED
|
@@ -177,9 +177,11 @@ export type {
|
|
|
177
177
|
PublicBlockMeasurement,
|
|
178
178
|
PublicFieldDirtinessReport,
|
|
179
179
|
PublicLineBox,
|
|
180
|
+
PublicLayoutDivergence,
|
|
180
181
|
PublicMeasurementFidelity,
|
|
181
182
|
PublicNoteAllocation,
|
|
182
183
|
PublicPageAnchor,
|
|
184
|
+
PublicPageFrame,
|
|
183
185
|
PublicPageNode,
|
|
184
186
|
PublicPageRegion,
|
|
185
187
|
PublicPageRegions,
|
|
@@ -190,6 +192,7 @@ export type {
|
|
|
190
192
|
PublicResolvedParagraphFormatting,
|
|
191
193
|
PublicResolvedRunFormatting,
|
|
192
194
|
PublicSectionNode,
|
|
195
|
+
PublicTwipsRect,
|
|
193
196
|
// R0.5: named page-format + margin-preset catalogs
|
|
194
197
|
PageFormatDefinition,
|
|
195
198
|
ActivePageFormat,
|
|
@@ -1223,6 +1226,23 @@ export interface SurfacePictureEffects {
|
|
|
1223
1226
|
glow?: { radius: number; color: string; colorType: "srgbClr" | "schemeClr" };
|
|
1224
1227
|
}
|
|
1225
1228
|
|
|
1229
|
+
export interface SurfacePreserveOnlyObjectSizing {
|
|
1230
|
+
sourceId?: string;
|
|
1231
|
+
display: "inline" | "floating" | "unknown";
|
|
1232
|
+
extentEmu?: { widthEmu: number; heightEmu: number };
|
|
1233
|
+
fallbackHint:
|
|
1234
|
+
| "drawing-inline"
|
|
1235
|
+
| "drawing-floating"
|
|
1236
|
+
| "chart"
|
|
1237
|
+
| "smartart"
|
|
1238
|
+
| "shape"
|
|
1239
|
+
| "wordart"
|
|
1240
|
+
| "vml-shape"
|
|
1241
|
+
| "ole-object"
|
|
1242
|
+
| "opaque-object";
|
|
1243
|
+
relationshipIds?: string[];
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1226
1246
|
export type SurfaceInlineSegment =
|
|
1227
1247
|
| {
|
|
1228
1248
|
segmentId: string;
|
|
@@ -1338,6 +1358,20 @@ export type SurfaceInlineSegment =
|
|
|
1338
1358
|
* can render `ChartSurface` instead of the fallback bitmap or badge.
|
|
1339
1359
|
*/
|
|
1340
1360
|
parsedChartId?: string;
|
|
1361
|
+
/**
|
|
1362
|
+
* PE2 preserve-only object handoff. Present for unsupported drawing,
|
|
1363
|
+
* chart, SmartArt, WordArt, VML, and opaque-object previews when import
|
|
1364
|
+
* could recover source ids, relationship ids, fallback hints, or
|
|
1365
|
+
* extents. Layout consumers may size placeholders from this metadata;
|
|
1366
|
+
* it is not a layout computation.
|
|
1367
|
+
*/
|
|
1368
|
+
preserveOnlyObject?: SurfacePreserveOnlyObjectSizing;
|
|
1369
|
+
/**
|
|
1370
|
+
* Drawing anchor geometry for preserve-only objects that originated in a
|
|
1371
|
+
* `drawing_frame`. Mirrors image/shape anchor semantics so L04 can join
|
|
1372
|
+
* the object to source/canonical/layout rows without re-parsing OOXML.
|
|
1373
|
+
*/
|
|
1374
|
+
anchor?: SurfaceDrawingAnchor;
|
|
1341
1375
|
state: "locked-preserve-only";
|
|
1342
1376
|
}
|
|
1343
1377
|
| {
|
|
@@ -1403,6 +1437,7 @@ export type SurfaceInlineSegment =
|
|
|
1403
1437
|
isTextBox?: boolean;
|
|
1404
1438
|
/** Text box body layout from `wps:bodyPr` / `a:bodyPr`. */
|
|
1405
1439
|
textBoxBody?: SurfaceTextBoxBodyProperties;
|
|
1440
|
+
preserveOnlyObject?: SurfacePreserveOnlyObjectSizing;
|
|
1406
1441
|
/** First-paragraph plain-text preview when `isTextBox` is true. */
|
|
1407
1442
|
txbxText?: string;
|
|
1408
1443
|
/** Run-level marks from the first text-box text run, when available. */
|
|
@@ -3609,6 +3644,11 @@ export interface WordReviewEditorPasteEvent {
|
|
|
3609
3644
|
source: "paste" | "drop";
|
|
3610
3645
|
}
|
|
3611
3646
|
|
|
3647
|
+
export interface WorkflowEventOrigin {
|
|
3648
|
+
readonly source: "api" | "runtime" | "collab" | string;
|
|
3649
|
+
readonly at?: string;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3612
3652
|
export type WordReviewEditorEvent =
|
|
3613
3653
|
| {
|
|
3614
3654
|
type: "ready";
|
|
@@ -3724,16 +3764,38 @@ export type WordReviewEditorEvent =
|
|
|
3724
3764
|
type: "workflow_overlay_changed";
|
|
3725
3765
|
documentId: string;
|
|
3726
3766
|
snapshot: WorkflowScopeSnapshot;
|
|
3767
|
+
origin?: WorkflowEventOrigin;
|
|
3727
3768
|
}
|
|
3728
3769
|
| {
|
|
3729
3770
|
type: "workflow_active_work_item_changed";
|
|
3730
3771
|
documentId: string;
|
|
3731
3772
|
activeWorkItemId: string | null;
|
|
3773
|
+
origin?: WorkflowEventOrigin;
|
|
3732
3774
|
}
|
|
3733
3775
|
| {
|
|
3734
3776
|
type: "workflow_metadata_changed";
|
|
3735
3777
|
documentId: string;
|
|
3736
3778
|
snapshot: WorkflowMetadataSnapshot;
|
|
3779
|
+
origin?: WorkflowEventOrigin;
|
|
3780
|
+
}
|
|
3781
|
+
| {
|
|
3782
|
+
type: "workflow_shared_state_changed";
|
|
3783
|
+
documentId: string;
|
|
3784
|
+
state: SharedWorkflowState | null;
|
|
3785
|
+
origin: WorkflowEventOrigin;
|
|
3786
|
+
}
|
|
3787
|
+
| {
|
|
3788
|
+
type: "workflow_visibility_policy_changed";
|
|
3789
|
+
documentId: string;
|
|
3790
|
+
kind?: OverlayKind;
|
|
3791
|
+
policy?: OverlayVisibilityPolicy | null;
|
|
3792
|
+
origin: WorkflowEventOrigin;
|
|
3793
|
+
}
|
|
3794
|
+
| {
|
|
3795
|
+
type: "workflow_markup_mode_policy_changed";
|
|
3796
|
+
documentId: string;
|
|
3797
|
+
policy: WorkflowMarkupModePolicy | null;
|
|
3798
|
+
origin: WorkflowEventOrigin;
|
|
3737
3799
|
}
|
|
3738
3800
|
| {
|
|
3739
3801
|
type: "host_annotation_overlay_changed";
|
|
@@ -5010,7 +5072,10 @@ export interface WordReviewEditorRef {
|
|
|
5010
5072
|
* `replaceText` calls to block with `workflow_comment_only`.
|
|
5011
5073
|
*/
|
|
5012
5074
|
getWorkflowOverlay(): WorkflowOverlay | null;
|
|
5013
|
-
setSharedWorkflowState(
|
|
5075
|
+
setSharedWorkflowState(
|
|
5076
|
+
state: SharedWorkflowState | null,
|
|
5077
|
+
origin?: WorkflowEventOrigin,
|
|
5078
|
+
): void;
|
|
5014
5079
|
getWorkflowScopeSnapshot(): WorkflowScopeSnapshot | null;
|
|
5015
5080
|
getInteractionGuardSnapshot(): InteractionGuardSnapshot;
|
|
5016
5081
|
getWorkflowMarkupSnapshot(): WorkflowMarkupSnapshot;
|
|
@@ -48,6 +48,7 @@ export type RuntimeApiHandle = Pick<
|
|
|
48
48
|
| "getCompatibilityReport"
|
|
49
49
|
| "getWarnings"
|
|
50
50
|
| "getRenderSnapshot"
|
|
51
|
+
| "getDocumentNavigationSnapshot"
|
|
51
52
|
// Canonical document read (ai.inspect + ai.bundle + ai.resolve families,
|
|
52
53
|
// added in refactor/08 Slice 2/3 graduations)
|
|
53
54
|
| "getCanonicalDocument"
|
|
@@ -155,6 +156,7 @@ export const RUNTIME_API_HANDLE_SHAPE_CHECK: Record<keyof RuntimeApiHandle, true
|
|
|
155
156
|
getCompatibilityReport: true,
|
|
156
157
|
getWarnings: true,
|
|
157
158
|
getRenderSnapshot: true,
|
|
159
|
+
getDocumentNavigationSnapshot: true,
|
|
158
160
|
getCanonicalDocument: true,
|
|
159
161
|
findAllText: true,
|
|
160
162
|
replaceText: true,
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 09 PE2 evidence projection.
|
|
3
|
+
*
|
|
4
|
+
* The AI API should not leak the full runtime geometry index. Agents need a
|
|
5
|
+
* small, JSON-stable read model that says whether PE2 geometry evidence is
|
|
6
|
+
* available and, for scope reads, whether a replacement envelope exists for
|
|
7
|
+
* the requested scope.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { RuntimeApiHandle } from "../_runtime-handle.ts";
|
|
11
|
+
import type {
|
|
12
|
+
GeometryIndexCoverage,
|
|
13
|
+
GeometryPrecision,
|
|
14
|
+
GeometryRehydrationStatus,
|
|
15
|
+
GeometryReplacementEnvelopeEntry,
|
|
16
|
+
GeometrySourceIdentity,
|
|
17
|
+
} from "../../../runtime/geometry/index.ts";
|
|
18
|
+
|
|
19
|
+
export interface AiPe2GeometryCoverageEvidence {
|
|
20
|
+
readonly status: GeometryRehydrationStatus;
|
|
21
|
+
readonly pageCount: number;
|
|
22
|
+
readonly regionCount: number;
|
|
23
|
+
readonly sliceCount: number;
|
|
24
|
+
readonly lineCount: number;
|
|
25
|
+
readonly anchorCount: number;
|
|
26
|
+
readonly hitTargetCount: number;
|
|
27
|
+
readonly semanticEntryCount: number;
|
|
28
|
+
readonly replacementEnvelopeCount: number;
|
|
29
|
+
readonly objectHandleCount: number;
|
|
30
|
+
readonly precision: {
|
|
31
|
+
readonly exact: number;
|
|
32
|
+
readonly "within-tolerance": number;
|
|
33
|
+
readonly heuristic: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AiPe2DocumentEvidence {
|
|
38
|
+
readonly geometry: AiPe2GeometryCoverageEvidence;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AiPe2ScopeReplacementEnvelopeEvidence {
|
|
42
|
+
readonly status: GeometryRehydrationStatus;
|
|
43
|
+
readonly precision: GeometryPrecision;
|
|
44
|
+
readonly rectCount: number;
|
|
45
|
+
readonly pageIds?: readonly string[];
|
|
46
|
+
readonly sourceIdentity?: GeometrySourceIdentity;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface AiPe2ScopeEvidence {
|
|
50
|
+
readonly geometry: {
|
|
51
|
+
readonly coverage: AiPe2GeometryCoverageEvidence;
|
|
52
|
+
readonly replacementEnvelope?: AiPe2ScopeReplacementEnvelopeEvidence;
|
|
53
|
+
readonly reason?: "geometry-index-unavailable" | "scope-envelope-not-found";
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function copyCoverage(coverage: GeometryIndexCoverage): AiPe2GeometryCoverageEvidence {
|
|
58
|
+
return {
|
|
59
|
+
status: coverage.status,
|
|
60
|
+
pageCount: coverage.pageCount,
|
|
61
|
+
regionCount: coverage.regionCount,
|
|
62
|
+
sliceCount: coverage.sliceCount,
|
|
63
|
+
lineCount: coverage.lineCount,
|
|
64
|
+
anchorCount: coverage.anchorCount,
|
|
65
|
+
hitTargetCount: coverage.hitTargetCount,
|
|
66
|
+
semanticEntryCount: coverage.semanticEntryCount,
|
|
67
|
+
replacementEnvelopeCount: coverage.replacementEnvelopeCount,
|
|
68
|
+
objectHandleCount: coverage.objectHandleCount,
|
|
69
|
+
precision: {
|
|
70
|
+
exact: coverage.precision.exact,
|
|
71
|
+
"within-tolerance": coverage.precision["within-tolerance"],
|
|
72
|
+
heuristic: coverage.precision.heuristic,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function copyEnvelope(
|
|
78
|
+
envelope: GeometryReplacementEnvelopeEntry,
|
|
79
|
+
): AiPe2ScopeReplacementEnvelopeEvidence {
|
|
80
|
+
return {
|
|
81
|
+
status: envelope.status,
|
|
82
|
+
precision: envelope.precision,
|
|
83
|
+
rectCount: envelope.rects.length,
|
|
84
|
+
...(envelope.pageIds ? { pageIds: [...envelope.pageIds] } : {}),
|
|
85
|
+
...(envelope.sourceIdentity ? { sourceIdentity: { ...envelope.sourceIdentity } } : {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const UNAVAILABLE_COVERAGE: AiPe2GeometryCoverageEvidence = {
|
|
90
|
+
status: "unavailable",
|
|
91
|
+
pageCount: 0,
|
|
92
|
+
regionCount: 0,
|
|
93
|
+
sliceCount: 0,
|
|
94
|
+
lineCount: 0,
|
|
95
|
+
anchorCount: 0,
|
|
96
|
+
hitTargetCount: 0,
|
|
97
|
+
semanticEntryCount: 0,
|
|
98
|
+
replacementEnvelopeCount: 0,
|
|
99
|
+
objectHandleCount: 0,
|
|
100
|
+
precision: { exact: 0, "within-tolerance": 0, heuristic: 0 },
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function projectDocumentPe2Evidence(
|
|
104
|
+
runtime: RuntimeApiHandle,
|
|
105
|
+
): AiPe2DocumentEvidence {
|
|
106
|
+
if (!runtime.geometry) {
|
|
107
|
+
return { geometry: UNAVAILABLE_COVERAGE };
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
geometry: copyCoverage(runtime.geometry.getGeometryCoverage()),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function projectScopePe2Evidence(
|
|
115
|
+
runtime: RuntimeApiHandle,
|
|
116
|
+
scopeId: string,
|
|
117
|
+
): AiPe2ScopeEvidence {
|
|
118
|
+
if (!runtime.geometry) {
|
|
119
|
+
return {
|
|
120
|
+
geometry: {
|
|
121
|
+
coverage: UNAVAILABLE_COVERAGE,
|
|
122
|
+
reason: "geometry-index-unavailable",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const coverage = copyCoverage(runtime.geometry.getGeometryCoverage());
|
|
127
|
+
const index = runtime.geometry.getGeometryIndex();
|
|
128
|
+
if (!index) {
|
|
129
|
+
return {
|
|
130
|
+
geometry: {
|
|
131
|
+
coverage,
|
|
132
|
+
reason: "geometry-index-unavailable",
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const envelope = index.replacementEnvelopes.find((entry) => entry.scopeId === scopeId);
|
|
138
|
+
if (!envelope) {
|
|
139
|
+
return {
|
|
140
|
+
geometry: {
|
|
141
|
+
coverage,
|
|
142
|
+
reason: "scope-envelope-not-found",
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
geometry: {
|
|
149
|
+
coverage,
|
|
150
|
+
replacementEnvelope: copyEnvelope(envelope),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
package/src/api/v3/ai/bundle.ts
CHANGED
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
type SemanticScope,
|
|
25
25
|
} from "../../../runtime/scopes/index.ts";
|
|
26
26
|
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
27
|
+
import {
|
|
28
|
+
projectScopePe2Evidence,
|
|
29
|
+
type AiPe2ScopeEvidence,
|
|
30
|
+
} from "./_pe2-evidence.ts";
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
33
|
* Handle-shaped input (architecture A3 — scope-handle-first targeting).
|
|
@@ -42,8 +46,9 @@ export interface GetScopeBundleInput {
|
|
|
42
46
|
readonly nowUtc: string;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
export type ScopeBundle = RuntimeScopeBundle & {
|
|
50
|
+
readonly pe2Evidence: AiPe2ScopeEvidence;
|
|
51
|
+
};
|
|
47
52
|
|
|
48
53
|
export interface ScopeBundleNotFound {
|
|
49
54
|
readonly notFound: true;
|
|
@@ -91,7 +96,7 @@ export const getScopeBundleMetadata: ApiV3FnMetadata = {
|
|
|
91
96
|
boundedScope: "scope",
|
|
92
97
|
auditCategory: "scope-bundle",
|
|
93
98
|
contextPromptShape:
|
|
94
|
-
"Bundle content + formatting + layout + geometry + workflow + replaceability for prompt context.",
|
|
99
|
+
"Bundle content + formatting + layout + geometry + workflow + replaceability + PE2 geometry coverage/envelope evidence for prompt context.",
|
|
95
100
|
},
|
|
96
101
|
stateClass: "A-canonical",
|
|
97
102
|
persistsTo: "canonical",
|
|
@@ -110,7 +115,7 @@ export function createBundleFamily(runtime: RuntimeApiHandle) {
|
|
|
110
115
|
return compiled?.scope ?? null;
|
|
111
116
|
},
|
|
112
117
|
|
|
113
|
-
getScopeBundle(input: GetScopeBundleInput):
|
|
118
|
+
getScopeBundle(input: GetScopeBundleInput): ScopeBundle | ScopeBundleNotFound {
|
|
114
119
|
// @endStateApi — live-with-adapter. Routes through the compiler-
|
|
115
120
|
// service facade.
|
|
116
121
|
//
|
|
@@ -126,7 +131,10 @@ export function createBundleFamily(runtime: RuntimeApiHandle) {
|
|
|
126
131
|
reason: "no scope with this id in canonical document or review store",
|
|
127
132
|
};
|
|
128
133
|
}
|
|
129
|
-
return
|
|
134
|
+
return {
|
|
135
|
+
...bundle,
|
|
136
|
+
pe2Evidence: projectScopePe2Evidence(runtime, scopeId),
|
|
137
|
+
};
|
|
130
138
|
},
|
|
131
139
|
};
|
|
132
140
|
}
|
package/src/api/v3/ai/inspect.ts
CHANGED
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
type SemanticScopeKind,
|
|
17
17
|
} from "../../../runtime/scopes/index.ts";
|
|
18
18
|
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
19
|
+
import {
|
|
20
|
+
projectDocumentPe2Evidence,
|
|
21
|
+
type AiPe2DocumentEvidence,
|
|
22
|
+
} from "./_pe2-evidence.ts";
|
|
19
23
|
|
|
20
24
|
export interface InspectDocumentResult {
|
|
21
25
|
readonly documentId: string;
|
|
@@ -23,6 +27,7 @@ export interface InspectDocumentResult {
|
|
|
23
27
|
readonly pageCount?: number;
|
|
24
28
|
readonly semanticSummary: string;
|
|
25
29
|
readonly kindDistribution: Readonly<Partial<Record<SemanticScopeKind, number>>>;
|
|
30
|
+
readonly pe2Evidence: AiPe2DocumentEvidence;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
export const inspectDocumentMetadata: ApiV3FnMetadata = {
|
|
@@ -40,7 +45,7 @@ export const inspectDocumentMetadata: ApiV3FnMetadata = {
|
|
|
40
45
|
boundedScope: "document",
|
|
41
46
|
auditCategory: "document-inspect",
|
|
42
47
|
contextPromptShape:
|
|
43
|
-
"Summarize document by scope count + page count + semantic kind distribution.",
|
|
48
|
+
"Summarize document by scope count + page count + semantic kind distribution + PE2 geometry coverage evidence.",
|
|
44
49
|
},
|
|
45
50
|
stateClass: "A-canonical",
|
|
46
51
|
persistsTo: "canonical",
|
|
@@ -97,6 +102,7 @@ export function createInspectFamily(runtime: RuntimeApiHandle) {
|
|
|
97
102
|
scopeCount: scopes.length,
|
|
98
103
|
semanticSummary: summary,
|
|
99
104
|
kindDistribution,
|
|
105
|
+
pe2Evidence: projectDocumentPe2Evidence(runtime),
|
|
100
106
|
};
|
|
101
107
|
},
|
|
102
108
|
|
package/src/api/v3/ai/outline.ts
CHANGED
|
@@ -19,7 +19,6 @@ import type {
|
|
|
19
19
|
DocumentOutlineHeadingSnapshot,
|
|
20
20
|
DocumentOutlineSnapshot,
|
|
21
21
|
} from "../../public-types.ts";
|
|
22
|
-
import { createDocumentNavigationSnapshot } from "../../../runtime/document-navigation.ts";
|
|
23
22
|
import { createDocumentOutlineSnapshot } from "../../../runtime/document-outline.ts";
|
|
24
23
|
import {
|
|
25
24
|
computeBlockPositions,
|
|
@@ -68,7 +67,7 @@ export const getDocumentOutlineMetadata: ApiV3FnMetadata = {
|
|
|
68
67
|
stateClass: "A-canonical",
|
|
69
68
|
persistsTo: "canonical",
|
|
70
69
|
rwdReference:
|
|
71
|
-
"§AI API § ai.getDocumentOutline. Composes
|
|
70
|
+
"§AI API § ai.getDocumentOutline. Composes runtime.getDocumentNavigationSnapshot() (L04 graph-derived navigation) with createDocumentOutlineSnapshot (L07) to surface the heading tree. Read-only; no audit emission.",
|
|
72
71
|
};
|
|
73
72
|
|
|
74
73
|
export function createOutlineFamily(runtime: RuntimeApiHandle) {
|
|
@@ -82,11 +81,7 @@ export function createOutlineFamily(runtime: RuntimeApiHandle) {
|
|
|
82
81
|
const snapshot = runtime.getRenderSnapshot();
|
|
83
82
|
const document = runtime.getCanonicalDocument();
|
|
84
83
|
const selectionHead = snapshot.selection.head;
|
|
85
|
-
const navigation =
|
|
86
|
-
document,
|
|
87
|
-
selectionHead,
|
|
88
|
-
snapshot.activeStory,
|
|
89
|
-
);
|
|
84
|
+
const navigation = runtime.getDocumentNavigationSnapshot();
|
|
90
85
|
const outline = createDocumentOutlineSnapshot({
|
|
91
86
|
navigation,
|
|
92
87
|
activeStory: snapshot.activeStory,
|
|
@@ -123,6 +123,7 @@ export interface ApplyResult {
|
|
|
123
123
|
readonly applied: boolean;
|
|
124
124
|
readonly reason?: string;
|
|
125
125
|
readonly blockers?: readonly string[];
|
|
126
|
+
readonly blockerDetails?: readonly ActionBlockerDetail[];
|
|
126
127
|
readonly auditHint?: string;
|
|
127
128
|
/**
|
|
128
129
|
* Gap A (post-Slice-7 integration) — revision IDs authored during
|
|
@@ -133,6 +134,19 @@ export interface ApplyResult {
|
|
|
133
134
|
readonly authoredRevisionIds: readonly string[];
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
export interface ActionBlockerDetail {
|
|
138
|
+
readonly code: string;
|
|
139
|
+
readonly category:
|
|
140
|
+
| "unsupported-scope-kind"
|
|
141
|
+
| "unsupported-operation"
|
|
142
|
+
| "unresolved-scope"
|
|
143
|
+
| "policy-or-guard";
|
|
144
|
+
readonly message: string;
|
|
145
|
+
readonly nextStep: string;
|
|
146
|
+
readonly scopeKind?: string;
|
|
147
|
+
readonly operation?: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
136
150
|
export interface ApplyReplacementScopeInput {
|
|
137
151
|
readonly targetScopeId: string;
|
|
138
152
|
readonly operation?: ReplacementOperationKind;
|
|
@@ -263,6 +277,97 @@ function projectValidationResult(
|
|
|
263
277
|
};
|
|
264
278
|
}
|
|
265
279
|
|
|
280
|
+
function blockerDetailFor(code: string): ActionBlockerDetail | null {
|
|
281
|
+
if (code.startsWith("scope-not-resolvable:")) {
|
|
282
|
+
return {
|
|
283
|
+
code,
|
|
284
|
+
category: "unresolved-scope",
|
|
285
|
+
message: "The target scope no longer resolves in the current document.",
|
|
286
|
+
nextStep:
|
|
287
|
+
"Call ai.resolveReference or ai.queryScopeAtPosition again, then retry with the returned handle's scopeId.",
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!code.startsWith("compile-refused:")) return null;
|
|
292
|
+
|
|
293
|
+
const [, scopeKind = "unknown", ...rest] = code.split(":");
|
|
294
|
+
const suffix = rest.join(":");
|
|
295
|
+
if (suffix.startsWith("operation-not-implemented:")) {
|
|
296
|
+
const operation = suffix.slice("operation-not-implemented:".length);
|
|
297
|
+
return {
|
|
298
|
+
code,
|
|
299
|
+
category: "unsupported-operation",
|
|
300
|
+
scopeKind,
|
|
301
|
+
operation,
|
|
302
|
+
message: `The ${operation} operation is not implemented for ${scopeKind} scopes.`,
|
|
303
|
+
nextStep:
|
|
304
|
+
"Retry with operation:\"replace\" when that is acceptable, or attach an explanation/issue instead of mutating.",
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (scopeKind === "scope" && suffix === "multi-paragraph-replace-not-implemented") {
|
|
309
|
+
return {
|
|
310
|
+
code,
|
|
311
|
+
category: "unsupported-scope-kind",
|
|
312
|
+
scopeKind,
|
|
313
|
+
message: "Multi-paragraph scope replacement is not implemented.",
|
|
314
|
+
nextStep:
|
|
315
|
+
"Split the request into paragraph-scoped replacements, or attach an explanation/issue until the multi-paragraph planner ships.",
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (scopeKind === "table" || scopeKind === "table-row" || scopeKind === "table-cell") {
|
|
320
|
+
return {
|
|
321
|
+
code,
|
|
322
|
+
category: "unsupported-scope-kind",
|
|
323
|
+
scopeKind,
|
|
324
|
+
message: `Flat text replacement is not implemented for ${scopeKind} scopes because it can break table structure.`,
|
|
325
|
+
nextStep:
|
|
326
|
+
"Use ai.attachExplanation or ai.createIssue for now, or wait for the Layer 08 table-family replacement planner.",
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (scopeKind === "field") {
|
|
331
|
+
return {
|
|
332
|
+
code,
|
|
333
|
+
category: "unsupported-scope-kind",
|
|
334
|
+
scopeKind,
|
|
335
|
+
message: "Field result replacement is preserve-only because field text is computed from instructions.",
|
|
336
|
+
nextStep:
|
|
337
|
+
"Attach an explanation/issue, or use a future field-aware edit path that updates field instructions safely.",
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (scopeKind === "image" || scopeKind === "note") {
|
|
342
|
+
return {
|
|
343
|
+
code,
|
|
344
|
+
category: "unsupported-scope-kind",
|
|
345
|
+
scopeKind,
|
|
346
|
+
message: `Replacement is not implemented for ${scopeKind} scopes.`,
|
|
347
|
+
nextStep:
|
|
348
|
+
"Attach an explanation/issue rather than mutating until a scope-specific planner exists.",
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
code,
|
|
354
|
+
category: "unsupported-scope-kind",
|
|
355
|
+
scopeKind,
|
|
356
|
+
message: `Replacement is not implemented for ${scopeKind} scopes.`,
|
|
357
|
+
nextStep:
|
|
358
|
+
"Use a supported paragraph-like scope, or attach an explanation/issue and route the unsupported planner to the owning layer.",
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function projectBlockerDetails(
|
|
363
|
+
blockers: readonly string[],
|
|
364
|
+
): readonly ActionBlockerDetail[] | undefined {
|
|
365
|
+
const details = blockers
|
|
366
|
+
.map((code) => blockerDetailFor(code))
|
|
367
|
+
.filter((detail): detail is ActionBlockerDetail => detail !== null);
|
|
368
|
+
return details.length > 0 ? Object.freeze(details) : undefined;
|
|
369
|
+
}
|
|
370
|
+
|
|
266
371
|
export function createReplacementFamily(runtime: RuntimeApiHandle) {
|
|
267
372
|
const compiler = createScopeCompilerService(runtime);
|
|
268
373
|
return {
|
|
@@ -371,6 +476,9 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
|
|
|
371
476
|
expectedDelta: applyReplacementScopeMetadata.uxIntent.expectedDelta,
|
|
372
477
|
});
|
|
373
478
|
|
|
479
|
+
const blockerDetails = projectBlockerDetails(
|
|
480
|
+
result.validation.blockedReasons,
|
|
481
|
+
);
|
|
374
482
|
return {
|
|
375
483
|
proposalId,
|
|
376
484
|
applied: result.applied,
|
|
@@ -378,6 +486,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
|
|
|
378
486
|
...(result.validation.blockedReasons.length > 0
|
|
379
487
|
? { blockers: Object.freeze([...result.validation.blockedReasons]) }
|
|
380
488
|
: {}),
|
|
489
|
+
...(blockerDetails ? { blockerDetails } : {}),
|
|
381
490
|
...(result.audit ? { auditHint: result.audit.actionId } : {}),
|
|
382
491
|
authoredRevisionIds: result.authoredRevisionIds,
|
|
383
492
|
};
|
|
@@ -412,6 +521,9 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
|
|
|
412
521
|
expectedDelta: applyScopeActionMetadata.uxIntent.expectedDelta,
|
|
413
522
|
});
|
|
414
523
|
|
|
524
|
+
const blockerDetails = projectBlockerDetails(
|
|
525
|
+
result.validation.blockedReasons,
|
|
526
|
+
);
|
|
415
527
|
return {
|
|
416
528
|
proposalId,
|
|
417
529
|
applied: result.applied,
|
|
@@ -419,6 +531,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
|
|
|
419
531
|
...(result.validation.blockedReasons.length > 0
|
|
420
532
|
? { blockers: Object.freeze([...result.validation.blockedReasons]) }
|
|
421
533
|
: {}),
|
|
534
|
+
...(blockerDetails ? { blockerDetails } : {}),
|
|
422
535
|
...(result.audit ? { auditHint: result.audit.actionId } : {}),
|
|
423
536
|
authoredRevisionIds: result.authoredRevisionIds,
|
|
424
537
|
};
|
|
@@ -15,6 +15,26 @@ import type { RuntimeApiHandle } from "../_runtime-handle.ts";
|
|
|
15
15
|
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
16
16
|
import { mockPayload } from "../_mocks.ts";
|
|
17
17
|
import type { MockPayload } from "../_layer-metadata.ts";
|
|
18
|
+
import type {
|
|
19
|
+
GeometryIndex,
|
|
20
|
+
GeometryIndexCoverage,
|
|
21
|
+
} from "../../../runtime/geometry/index.ts";
|
|
22
|
+
|
|
23
|
+
function createUnavailableGeometryCoverage(): GeometryIndexCoverage {
|
|
24
|
+
return {
|
|
25
|
+
status: "unavailable",
|
|
26
|
+
pageCount: 0,
|
|
27
|
+
regionCount: 0,
|
|
28
|
+
sliceCount: 0,
|
|
29
|
+
lineCount: 0,
|
|
30
|
+
anchorCount: 0,
|
|
31
|
+
hitTargetCount: 0,
|
|
32
|
+
semanticEntryCount: 0,
|
|
33
|
+
replacementEnvelopeCount: 0,
|
|
34
|
+
objectHandleCount: 0,
|
|
35
|
+
precision: { exact: 0, "within-tolerance": 0, heuristic: 0 },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
18
38
|
|
|
19
39
|
export interface BlockRectEntry {
|
|
20
40
|
readonly blockId: string;
|
|
@@ -57,6 +77,50 @@ export interface HitTestResult {
|
|
|
57
77
|
readonly __mock?: true;
|
|
58
78
|
}
|
|
59
79
|
|
|
80
|
+
/* ================================================================== */
|
|
81
|
+
/* PE2 geometry index + coverage */
|
|
82
|
+
/* ================================================================== */
|
|
83
|
+
|
|
84
|
+
export const getGeometryIndexMetadata: ApiV3FnMetadata = {
|
|
85
|
+
name: "runtime.geometry.getGeometryIndex",
|
|
86
|
+
status: "live-with-adapter",
|
|
87
|
+
sourceLayer: "geometry-projection",
|
|
88
|
+
liveEvidence: {
|
|
89
|
+
runnerTest: "test/api/v3/geometry-uses-geometry-handle.test.ts",
|
|
90
|
+
commit: "refactor-07-pe2-geometry-index-read-surface",
|
|
91
|
+
},
|
|
92
|
+
uxIntent: { uiVisible: false, expectsUxResponse: "none" },
|
|
93
|
+
agentMetadata: {
|
|
94
|
+
readOrMutate: "read",
|
|
95
|
+
boundedScope: "document",
|
|
96
|
+
auditCategory: "geometry-read",
|
|
97
|
+
},
|
|
98
|
+
stateClass: "A-canonical",
|
|
99
|
+
persistsTo: "canonical",
|
|
100
|
+
rwdReference:
|
|
101
|
+
"§Runtime API § runtime.geometry.getGeometryIndex. PE2 read surface over GeometryFacet.getGeometryIndex(): renderer-neutral pages, regions, slices, lines, anchors, hit targets, semantic entries, replacement envelopes, object handles, and coverage. Returns null when no geometry facet or render frame is warm. Promotes to live when L05 projects directly from L04 page slices instead of the render-frame adapter.",
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const getGeometryCoverageMetadata: ApiV3FnMetadata = {
|
|
105
|
+
name: "runtime.geometry.getGeometryCoverage",
|
|
106
|
+
status: "live-with-adapter",
|
|
107
|
+
sourceLayer: "geometry-projection",
|
|
108
|
+
liveEvidence: {
|
|
109
|
+
runnerTest: "test/api/v3/geometry-uses-geometry-handle.test.ts",
|
|
110
|
+
commit: "refactor-07-pe2-geometry-index-read-surface",
|
|
111
|
+
},
|
|
112
|
+
uxIntent: { uiVisible: false, expectsUxResponse: "none" },
|
|
113
|
+
agentMetadata: {
|
|
114
|
+
readOrMutate: "read",
|
|
115
|
+
boundedScope: "document",
|
|
116
|
+
auditCategory: "geometry-read",
|
|
117
|
+
},
|
|
118
|
+
stateClass: "A-canonical",
|
|
119
|
+
persistsTo: "canonical",
|
|
120
|
+
rwdReference:
|
|
121
|
+
"§Runtime API § runtime.geometry.getGeometryCoverage. PE2 lightweight status read over GeometryFacet.getGeometryCoverage(). Always returns a coverage object; unwired/pre-paint states report status:'unavailable' with zero counts so agents and evidence runners can distinguish no geometry from empty geometry.",
|
|
122
|
+
};
|
|
123
|
+
|
|
60
124
|
/* ================================================================== */
|
|
61
125
|
/* getCaret — Slice-4 caret geometry (Slice X3 of §4 of coord-05) */
|
|
62
126
|
/* ================================================================== */
|
|
@@ -224,6 +288,21 @@ export function createGeometryFamily(runtime: RuntimeApiHandle) {
|
|
|
224
288
|
const geometry = runtime.geometry;
|
|
225
289
|
|
|
226
290
|
return {
|
|
291
|
+
getGeometryIndex(): GeometryIndex | null {
|
|
292
|
+
// @endStateApi — live-with-adapter. Exposes the Layer-05 PE2
|
|
293
|
+
// geometry index through the v3 runtime seam. The current L05
|
|
294
|
+
// index is projected from the render frame; callers still get
|
|
295
|
+
// renderer-neutral value objects and no DOM/PM/runtime instances.
|
|
296
|
+
return geometry?.getGeometryIndex() ?? null;
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
getGeometryCoverage(): GeometryIndexCoverage {
|
|
300
|
+
// @endStateApi — live-with-adapter. Coverage is intentionally
|
|
301
|
+
// total: an unwired/pre-paint handle reports an unavailable zero
|
|
302
|
+
// summary instead of null so evidence code can branch on status.
|
|
303
|
+
return geometry?.getGeometryCoverage() ?? createUnavailableGeometryCoverage();
|
|
304
|
+
},
|
|
305
|
+
|
|
227
306
|
getBlockRects(blockIds: readonly string[]): ReadonlyArray<BlockRectEntry & Partial<MockPayload>> {
|
|
228
307
|
// @endStateApi — live-with-adapter. Routes directly through the
|
|
229
308
|
// Layer-05 GeometryFacet's `getBlockRects(blockId)`, which walks
|