@beyondwork/docx-react-component 1.0.105 → 1.0.108
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 +19 -5
- package/src/api/geometry-overlay-rects.ts +5 -0
- package/src/api/package-version.ts +1 -1
- package/src/api/page-anchor-id.ts +5 -0
- package/src/api/public-types.ts +16 -9
- package/src/api/table-node-specs.ts +6 -0
- package/src/api/v3/_create.ts +10 -2
- package/src/api/v3/_page-anchor-id.ts +52 -0
- package/src/api/v3/_runtime-handle.ts +92 -1
- package/src/api/v3/ai/_audit-reference.ts +28 -0
- package/src/api/v3/ai/_audit-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +310 -6
- package/src/api/v3/ai/attach.ts +29 -4
- package/src/api/v3/ai/bundle.ts +6 -2
- package/src/api/v3/ai/inspect.ts +6 -2
- package/src/api/v3/ai/replacement.ts +112 -18
- package/src/api/v3/ai/resolve.ts +2 -2
- package/src/api/v3/ai/review.ts +177 -3
- package/src/api/v3/index.ts +8 -0
- package/src/api/v3/runtime/collab.ts +462 -0
- package/src/api/v3/runtime/document.ts +503 -20
- package/src/api/v3/runtime/geometry.ts +97 -0
- package/src/api/v3/runtime/layout.ts +744 -0
- package/src/api/v3/runtime/perf-probe.ts +14 -0
- package/src/api/v3/runtime/viewport.ts +9 -8
- package/src/api/v3/ui/_types.ts +202 -55
- package/src/api/v3/ui/chrome-preset-model.ts +5 -5
- package/src/api/v3/ui/debug.ts +115 -2
- package/src/api/v3/ui/index.ts +17 -0
- package/src/api/v3/ui/overlays.ts +0 -8
- package/src/api/v3/ui/surface.ts +56 -0
- package/src/api/v3/ui/viewport.ts +119 -9
- package/src/core/commands/image-commands.ts +1 -0
- package/src/core/commands/index.ts +6 -0
- package/src/core/schema/text-schema.ts +43 -5
- package/src/core/selection/mapping.ts +8 -1
- package/src/core/selection/review-anchors.ts +5 -1
- package/src/core/state/text-transaction.ts +8 -2
- package/src/io/export/serialize-revisions.ts +149 -1
- package/src/io/normalize/normalize-text.ts +6 -0
- package/src/io/ooxml/parse-bookmark-references.ts +55 -0
- package/src/io/ooxml/parse-fields.ts +24 -2
- package/src/io/ooxml/parse-headers-footers.ts +38 -5
- package/src/io/ooxml/parse-main-document.ts +153 -9
- package/src/io/ooxml/parse-numbering.ts +20 -0
- package/src/io/ooxml/parse-revisions.ts +19 -8
- package/src/io/opc/package-reader.ts +98 -8
- package/src/model/anchor.ts +4 -3
- package/src/model/canonical-document.ts +220 -2
- package/src/model/canonical-hash.ts +221 -0
- package/src/model/canonical-layout-inputs.ts +245 -6
- package/src/model/layout/index.ts +1 -0
- package/src/model/layout/page-graph-types.ts +147 -1
- package/src/model/review/revision-types.ts +14 -3
- package/src/preservation/store.ts +20 -4
- package/src/review/README.md +1 -1
- package/src/review/store/revision-actions.ts +14 -2
- package/src/runtime/collab/event-types.ts +67 -1
- package/src/runtime/collab/runtime-collab-sync.ts +177 -5
- package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
- package/src/runtime/document-heading-outline.ts +147 -0
- package/src/runtime/document-navigation.ts +8 -243
- package/src/runtime/document-runtime.ts +279 -115
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
- package/src/runtime/formatting/layout-inputs.ts +38 -5
- package/src/runtime/formatting/numbering/geometry.ts +28 -2
- package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
- package/src/runtime/geometry/caret-geometry.ts +5 -6
- package/src/runtime/geometry/geometry-facet.ts +60 -10
- package/src/runtime/geometry/geometry-index.ts +661 -16
- package/src/runtime/geometry/geometry-types.ts +59 -0
- package/src/runtime/geometry/hit-test.ts +11 -1
- package/src/runtime/geometry/overlay-rects.ts +5 -3
- package/src/runtime/geometry/project-anchors.ts +1 -1
- package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
- package/src/runtime/layout/index.ts +6 -0
- package/src/runtime/layout/layout-engine-instance.ts +6 -1
- package/src/runtime/layout/layout-engine-version.ts +188 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +23 -4
- package/src/runtime/layout/paginated-layout-engine.ts +149 -15
- package/src/runtime/layout/project-block-fragments.ts +351 -14
- package/src/runtime/layout/public-facet.ts +162 -24
- package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
- package/src/runtime/layout/table-row-split.ts +92 -35
- package/src/runtime/prerender/cache-envelope.ts +2 -2
- package/src/runtime/prerender/cache-key.ts +5 -4
- package/src/runtime/prerender/customxml-cache.ts +0 -1
- package/src/runtime/render/render-kernel.ts +1 -1
- package/src/runtime/revision-runtime.ts +112 -10
- package/src/runtime/scopes/_scope-dependencies.ts +1 -0
- package/src/runtime/scopes/action-validation.ts +22 -2
- package/src/runtime/scopes/capabilities.ts +316 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
- package/src/runtime/scopes/compiler-service.ts +108 -4
- package/src/runtime/scopes/content-control-evidence.ts +79 -0
- package/src/runtime/scopes/create-issue.ts +5 -5
- package/src/runtime/scopes/evidence.ts +91 -0
- package/src/runtime/scopes/formatting/apply.ts +2 -0
- package/src/runtime/scopes/geometry-evidence.ts +130 -0
- package/src/runtime/scopes/index.ts +54 -0
- package/src/runtime/scopes/issue-lifecycle.ts +224 -0
- package/src/runtime/scopes/layout-evidence.ts +374 -0
- package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
- package/src/runtime/scopes/preservation-boundary.ts +7 -1
- package/src/runtime/scopes/replacement/apply.ts +97 -34
- package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
- package/src/runtime/scopes/semantic-scope-types.ts +242 -3
- package/src/runtime/scopes/visualization.ts +28 -0
- package/src/runtime/surface-projection.ts +44 -5
- package/src/runtime/telemetry/perf-probe.ts +216 -0
- package/src/runtime/virtualized-rendering.ts +36 -1
- package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
- package/src/runtime/workflow/coordinator.ts +39 -11
- package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
- package/src/runtime/workflow/index.ts +4 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +386 -0
- package/src/runtime/workflow/overlay-store.ts +2 -2
- package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
- package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
- package/src/session/_sync-legacy.ts +17 -27
- package/src/session/import/loader.ts +6 -4
- package/src/session/import/source-package-evidence.ts +186 -2
- package/src/session/index.ts +5 -6
- package/src/session/session.ts +30 -56
- package/src/session/types.ts +8 -13
- package/src/shell/session-bootstrap.ts +155 -81
- package/src/ui/WordReviewEditor.tsx +520 -12
- package/src/ui/editor-shell-view.tsx +14 -4
- package/src/ui/editor-surface-controller.tsx +5 -3
- package/src/ui/headless/selection-tool-resolver.ts +1 -2
- package/src/ui/presence-overlay-lane.ts +130 -0
- package/src/ui/ui-controller-factory.ts +17 -0
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
- package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
- package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
- package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
- package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
- package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
- package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
- package/src/ui-tailwind/debug/README.md +4 -1
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
- package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
- package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
- package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
- package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
- package/src/ui-tailwind/review-workspace/types.ts +26 -12
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
- package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
import type { EditorStoryTarget } from "../api/public-types.ts";
|
|
1
2
|
import type { CanonicalDocumentEnvelope } from "../core/state/editor-state.ts";
|
|
2
3
|
import {
|
|
3
4
|
getReviewCommandIntent,
|
|
4
5
|
isSingleRevisionReviewCommand,
|
|
5
6
|
type ReviewCommand,
|
|
6
7
|
} from "../core/commands/review-commands.ts";
|
|
7
|
-
import
|
|
8
|
+
import {
|
|
9
|
+
MAIN_STORY_TARGET,
|
|
10
|
+
storyTargetsEqual,
|
|
11
|
+
type TransactionMapping,
|
|
12
|
+
} from "../core/selection/mapping.ts";
|
|
8
13
|
import {
|
|
9
14
|
applyRevisionAction,
|
|
15
|
+
type ApplyRevisionActionResult,
|
|
10
16
|
type RevisionActionOutcome,
|
|
11
17
|
} from "../review/store/revision-actions.ts";
|
|
12
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
createRevisionStore,
|
|
20
|
+
type RevisionStore,
|
|
21
|
+
} from "../review/store/revision-store.ts";
|
|
22
|
+
import { getStoryBlocks, replaceStoryBlocks } from "./story-targeting.ts";
|
|
13
23
|
|
|
14
24
|
export interface RevisionRuntimeState {
|
|
15
25
|
document: CanonicalDocumentEnvelope;
|
|
@@ -30,7 +40,12 @@ export interface RevisionRuntimeCommandEffects {
|
|
|
30
40
|
|
|
31
41
|
export interface RevisionRuntimeCommandResult extends RevisionRuntimeState {
|
|
32
42
|
outcomes: RevisionActionOutcome[];
|
|
33
|
-
mappings: Array<{
|
|
43
|
+
mappings: Array<{
|
|
44
|
+
revisionId: string;
|
|
45
|
+
mapping: TransactionMapping;
|
|
46
|
+
steps: number;
|
|
47
|
+
storyTarget: EditorStoryTarget;
|
|
48
|
+
}>;
|
|
34
49
|
effects: RevisionRuntimeCommandEffects;
|
|
35
50
|
}
|
|
36
51
|
|
|
@@ -44,30 +59,31 @@ export function applyRevisionRuntimeCommand(
|
|
|
44
59
|
const revisionIds = expandLinkedMovePartners(rawRevisionIds, options.state.store);
|
|
45
60
|
|
|
46
61
|
const outcomes: RevisionActionOutcome[] = [];
|
|
47
|
-
const mappings:
|
|
62
|
+
const mappings: RevisionRuntimeCommandResult["mappings"] = [];
|
|
48
63
|
const appliedRevisionIds: string[] = [];
|
|
49
64
|
const detachedRevisionIds = new Set<string>();
|
|
50
65
|
|
|
51
66
|
let state = options.state;
|
|
52
67
|
|
|
53
68
|
for (const revisionId of revisionIds) {
|
|
69
|
+
const revision = state.store.revisions[revisionId];
|
|
70
|
+
const storyTarget = getRevisionStoryTarget(revision);
|
|
71
|
+
const scopedState = createStoryScopedRevisionState(state, storyTarget);
|
|
54
72
|
const result = applyRevisionAction({
|
|
55
|
-
document:
|
|
56
|
-
store:
|
|
73
|
+
document: scopedState.document,
|
|
74
|
+
store: scopedState.store,
|
|
57
75
|
revisionId,
|
|
58
76
|
intent: getReviewCommandIntent(options.command),
|
|
59
77
|
timestamp: options.timestamp,
|
|
60
78
|
});
|
|
61
79
|
|
|
62
|
-
state =
|
|
63
|
-
document: result.document,
|
|
64
|
-
store: result.store,
|
|
65
|
-
};
|
|
80
|
+
state = mergeStoryScopedRevisionResult(state, storyTarget, scopedState, result);
|
|
66
81
|
outcomes.push(result.outcome);
|
|
67
82
|
mappings.push({
|
|
68
83
|
revisionId,
|
|
69
84
|
mapping: result.mapping,
|
|
70
85
|
steps: result.mapping.steps.length,
|
|
86
|
+
storyTarget,
|
|
71
87
|
});
|
|
72
88
|
|
|
73
89
|
if (result.outcome.kind === "applied") {
|
|
@@ -94,6 +110,92 @@ export function applyRevisionRuntimeCommand(
|
|
|
94
110
|
};
|
|
95
111
|
}
|
|
96
112
|
|
|
113
|
+
interface StoryScopedRevisionState extends RevisionRuntimeState {
|
|
114
|
+
storyRevisionIds: readonly string[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createStoryScopedRevisionState(
|
|
118
|
+
state: RevisionRuntimeState,
|
|
119
|
+
storyTarget: EditorStoryTarget,
|
|
120
|
+
): StoryScopedRevisionState {
|
|
121
|
+
if (storyTargetsEqual(storyTarget, MAIN_STORY_TARGET)) {
|
|
122
|
+
return {
|
|
123
|
+
...state,
|
|
124
|
+
storyRevisionIds: Object.keys(state.store.revisions),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const storyRevisionIds = Object.entries(state.store.revisions)
|
|
129
|
+
.filter(([, revision]) =>
|
|
130
|
+
storyTargetsEqual(getRevisionStoryTarget(revision), storyTarget)
|
|
131
|
+
)
|
|
132
|
+
.map(([revisionId]) => revisionId);
|
|
133
|
+
const storyRevisions = Object.fromEntries(
|
|
134
|
+
storyRevisionIds.map((revisionId) => [
|
|
135
|
+
revisionId,
|
|
136
|
+
state.store.revisions[revisionId]!,
|
|
137
|
+
]),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
document: {
|
|
142
|
+
...state.document,
|
|
143
|
+
content: {
|
|
144
|
+
...state.document.content,
|
|
145
|
+
children: [...getStoryBlocks(state.document, storyTarget)],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
store: createRevisionStore(storyRevisions),
|
|
149
|
+
storyRevisionIds,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function mergeStoryScopedRevisionResult(
|
|
154
|
+
previousState: RevisionRuntimeState,
|
|
155
|
+
storyTarget: EditorStoryTarget,
|
|
156
|
+
scopedState: StoryScopedRevisionState,
|
|
157
|
+
result: ApplyRevisionActionResult,
|
|
158
|
+
): RevisionRuntimeState {
|
|
159
|
+
if (storyTargetsEqual(storyTarget, MAIN_STORY_TARGET)) {
|
|
160
|
+
return {
|
|
161
|
+
document: result.document,
|
|
162
|
+
store: result.store,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const nextStore = createRevisionStore({
|
|
167
|
+
...previousState.store.revisions,
|
|
168
|
+
...Object.fromEntries(
|
|
169
|
+
scopedState.storyRevisionIds
|
|
170
|
+
.filter((revisionId) => result.store.revisions[revisionId])
|
|
171
|
+
.map((revisionId) => [
|
|
172
|
+
revisionId,
|
|
173
|
+
result.store.revisions[revisionId]!,
|
|
174
|
+
]),
|
|
175
|
+
),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
document: replaceStoryBlocks(
|
|
180
|
+
{
|
|
181
|
+
...previousState.document,
|
|
182
|
+
updatedAt: result.document.updatedAt,
|
|
183
|
+
},
|
|
184
|
+
storyTarget,
|
|
185
|
+
result.document.content.children,
|
|
186
|
+
),
|
|
187
|
+
store: nextStore,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getRevisionStoryTarget(
|
|
192
|
+
revision: RevisionStore["revisions"][string] | undefined,
|
|
193
|
+
): EditorStoryTarget {
|
|
194
|
+
return revision?.metadata.storyTarget
|
|
195
|
+
? { ...revision.metadata.storyTarget }
|
|
196
|
+
: MAIN_STORY_TARGET;
|
|
197
|
+
}
|
|
198
|
+
|
|
97
199
|
function expandLinkedMovePartners(
|
|
98
200
|
revisionIds: readonly string[],
|
|
99
201
|
store: RevisionStore,
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
import { resolveScopeRange } from "./scope-range.ts";
|
|
64
64
|
import type {
|
|
65
65
|
ReplacementScope,
|
|
66
|
+
ScopeActionPosture,
|
|
66
67
|
ScopeActionOperationKind,
|
|
67
68
|
SemanticScope,
|
|
68
69
|
ValidationApproval,
|
|
@@ -144,8 +145,6 @@ function inferActionId(
|
|
|
144
145
|
case "insert-before":
|
|
145
146
|
case "insert-after":
|
|
146
147
|
return "generate_text";
|
|
147
|
-
case "split":
|
|
148
|
-
return "rewrite_paragraph";
|
|
149
148
|
case "annotate":
|
|
150
149
|
return "suggest_comment_response";
|
|
151
150
|
}
|
|
@@ -296,6 +295,17 @@ function collectPreservationVerdict(
|
|
|
296
295
|
): void {
|
|
297
296
|
const { document, scope, positionMap } = inputs;
|
|
298
297
|
if (inputs.skipPreservation === true) return;
|
|
298
|
+
// Insert-before / insert-after lower to collapsed insertions at the
|
|
299
|
+
// effective scope edge. They do not replace or delete the scope's
|
|
300
|
+
// current contents, so preserve-only payloads and nested markers
|
|
301
|
+
// inside the scope are not at risk in the same way they are for
|
|
302
|
+
// `replace`.
|
|
303
|
+
if (
|
|
304
|
+
inputs.operation === "insert-before" ||
|
|
305
|
+
inputs.operation === "insert-after"
|
|
306
|
+
) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
299
309
|
if (!document) return;
|
|
300
310
|
const pm = positionMap ?? buildScopePositionMap(document);
|
|
301
311
|
const range = inputs.enumeratedScope
|
|
@@ -397,6 +407,15 @@ function collectPolicyVerdict(
|
|
|
397
407
|
return undefined;
|
|
398
408
|
}
|
|
399
409
|
|
|
410
|
+
function validationPosture(
|
|
411
|
+
blockedReasons: readonly string[],
|
|
412
|
+
warnings: readonly ValidationIssue[],
|
|
413
|
+
): ScopeActionPosture {
|
|
414
|
+
if (blockedReasons.length > 0) return "hard-refusal";
|
|
415
|
+
if (warnings.length > 0) return "warn-and-proceed";
|
|
416
|
+
return "supported";
|
|
417
|
+
}
|
|
418
|
+
|
|
400
419
|
/**
|
|
401
420
|
* Compose a single validation verdict for a proposed scope replacement.
|
|
402
421
|
* Synchronous, deterministic, free of side effects. Safe to call in hot
|
|
@@ -420,6 +439,7 @@ export function composeScopeValidation(
|
|
|
420
439
|
const safe = blockedReasons.length === 0;
|
|
421
440
|
return {
|
|
422
441
|
safe,
|
|
442
|
+
posture: validationPosture(blockedReasons, warnings),
|
|
423
443
|
blockedReasons: Object.freeze([...blockedReasons]),
|
|
424
444
|
warnings: Object.freeze([...warnings]),
|
|
425
445
|
...(approval ? { approval } : {}),
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope action capability projection.
|
|
3
|
+
*
|
|
4
|
+
* This module reports what the shipped L08 compiler/runtime can lower today.
|
|
5
|
+
* It does not validate a specific payload and does not authorize a mutation;
|
|
6
|
+
* every apply path still re-runs validation and compile.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
ScopeContentControlEvidence,
|
|
11
|
+
ScopeGeometryEvidence,
|
|
12
|
+
ScopeLayoutEvidence,
|
|
13
|
+
ScopeCapabilities,
|
|
14
|
+
ScopeCapabilityVerdict,
|
|
15
|
+
SemanticScope,
|
|
16
|
+
SemanticScopeKind,
|
|
17
|
+
} from "./semantic-scope-types.ts";
|
|
18
|
+
import {
|
|
19
|
+
MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
|
|
20
|
+
multiParagraphReplacementBlockers,
|
|
21
|
+
} from "./multi-paragraph-refusal.ts";
|
|
22
|
+
|
|
23
|
+
const PARAGRAPH_LIKE = new Set<SemanticScopeKind>([
|
|
24
|
+
"paragraph",
|
|
25
|
+
"heading",
|
|
26
|
+
"list-item",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const FORMATTING_SUPPORTED = new Set<SemanticScopeKind>([
|
|
30
|
+
"paragraph",
|
|
31
|
+
"heading",
|
|
32
|
+
"list-item",
|
|
33
|
+
"scope",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const METADATA_SUPPORTED = new Set<SemanticScopeKind>([
|
|
37
|
+
"paragraph",
|
|
38
|
+
"heading",
|
|
39
|
+
"list-item",
|
|
40
|
+
"table",
|
|
41
|
+
"table-row",
|
|
42
|
+
"table-cell",
|
|
43
|
+
"scope",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
export interface ScopeCapabilityContext {
|
|
47
|
+
readonly layout?: ScopeLayoutEvidence;
|
|
48
|
+
readonly geometry?: ScopeGeometryEvidence;
|
|
49
|
+
readonly contentControls?: ScopeContentControlEvidence;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function freezeList(values: readonly string[]): readonly string[] {
|
|
53
|
+
return Object.freeze([...values]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function supported(
|
|
57
|
+
reason: string,
|
|
58
|
+
warnings: readonly string[] = [],
|
|
59
|
+
): ScopeCapabilityVerdict {
|
|
60
|
+
const hasWarnings = warnings.length > 0;
|
|
61
|
+
return {
|
|
62
|
+
supported: true,
|
|
63
|
+
status: hasWarnings ? "degraded" : "supported",
|
|
64
|
+
posture: hasWarnings ? "warn-and-proceed" : "supported",
|
|
65
|
+
reason,
|
|
66
|
+
...(hasWarnings ? { warnings: freezeList(warnings) } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function unsupported(
|
|
71
|
+
reason: string,
|
|
72
|
+
blockers: readonly string[] = [reason],
|
|
73
|
+
warnings: readonly string[] = [],
|
|
74
|
+
): ScopeCapabilityVerdict {
|
|
75
|
+
return {
|
|
76
|
+
supported: false,
|
|
77
|
+
status: "unsupported",
|
|
78
|
+
posture: "hard-refusal",
|
|
79
|
+
reason,
|
|
80
|
+
blockers: freezeList(blockers),
|
|
81
|
+
...(warnings.length > 0 ? { warnings: freezeList(warnings) } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function blocked(
|
|
86
|
+
reason: string,
|
|
87
|
+
blockers: readonly string[] = [reason],
|
|
88
|
+
): ScopeCapabilityVerdict {
|
|
89
|
+
return {
|
|
90
|
+
supported: false,
|
|
91
|
+
status: "blocked",
|
|
92
|
+
posture: "hard-refusal",
|
|
93
|
+
reason,
|
|
94
|
+
blockers: freezeList(blockers),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function guardBlocker(scope: SemanticScope): string | null {
|
|
99
|
+
switch (scope.workflow.effectiveMode) {
|
|
100
|
+
case "view":
|
|
101
|
+
return "guard:view-mode-active";
|
|
102
|
+
case "comment":
|
|
103
|
+
return "guard:comment-only";
|
|
104
|
+
case "blocked": {
|
|
105
|
+
const [first] = scope.workflow.blockedReasons ?? [];
|
|
106
|
+
return first ? `guard:block-${first}` : "guard:block-scope-overlay";
|
|
107
|
+
}
|
|
108
|
+
default:
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function operationRefusal(scope: SemanticScope): string {
|
|
114
|
+
return `compile-refused:${scope.kind}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function contentControlBlockers(
|
|
118
|
+
context: ScopeCapabilityContext | undefined,
|
|
119
|
+
): readonly string[] {
|
|
120
|
+
const entries = context?.contentControls?.entries ?? [];
|
|
121
|
+
if (entries.length === 0) return [];
|
|
122
|
+
return freezeList(entries.map((entry) => `preserve:content-control:${entry.evidenceId}`));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function evidenceWarnings(
|
|
126
|
+
context: ScopeCapabilityContext | undefined,
|
|
127
|
+
): readonly string[] {
|
|
128
|
+
const warnings: string[] = [];
|
|
129
|
+
if (context?.layout) {
|
|
130
|
+
if (
|
|
131
|
+
context.layout.status === "requires-rehydration" ||
|
|
132
|
+
context.layout.status === "unavailable" ||
|
|
133
|
+
context.layout.completeness !== "complete"
|
|
134
|
+
) {
|
|
135
|
+
warnings.push(`layout:${context.layout.completeness}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (context?.geometry) {
|
|
139
|
+
if (context.geometry.requiresRehydration) {
|
|
140
|
+
warnings.push("geometry:requires-rehydration");
|
|
141
|
+
} else if (context.geometry.status === "unavailable") {
|
|
142
|
+
warnings.push("geometry:unavailable");
|
|
143
|
+
}
|
|
144
|
+
if (context.geometry.continuationState?.crossesPageBoundary) {
|
|
145
|
+
warnings.push("geometry:crosses-page-boundary");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return freezeList(warnings);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function insertCapability(
|
|
152
|
+
scope: SemanticScope,
|
|
153
|
+
edge: "before" | "after",
|
|
154
|
+
context?: ScopeCapabilityContext,
|
|
155
|
+
): ScopeCapabilityVerdict {
|
|
156
|
+
const guard = guardBlocker(scope);
|
|
157
|
+
if (guard) return blocked(guard);
|
|
158
|
+
|
|
159
|
+
if (!PARAGRAPH_LIKE.has(scope.kind)) {
|
|
160
|
+
return unsupported(operationRefusal(scope));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
scope.replaceability.level === "blocked" ||
|
|
165
|
+
scope.replaceability.level === "preserve-only"
|
|
166
|
+
) {
|
|
167
|
+
const reason = scope.replaceability.reason
|
|
168
|
+
? `replaceability:${scope.replaceability.reason}`
|
|
169
|
+
: `replaceability:${scope.replaceability.level}`;
|
|
170
|
+
return unsupported(reason);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return supported(
|
|
174
|
+
`compile-supported:paragraph-like:insert-${edge}`,
|
|
175
|
+
[
|
|
176
|
+
...(scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : []),
|
|
177
|
+
...evidenceWarnings(context),
|
|
178
|
+
],
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function replaceTextCapability(
|
|
183
|
+
scope: SemanticScope,
|
|
184
|
+
context?: ScopeCapabilityContext,
|
|
185
|
+
): ScopeCapabilityVerdict {
|
|
186
|
+
const guard = guardBlocker(scope);
|
|
187
|
+
if (guard) return blocked(guard);
|
|
188
|
+
|
|
189
|
+
const contentControls = contentControlBlockers(context);
|
|
190
|
+
if (contentControls.length > 0) {
|
|
191
|
+
return blocked("preserve:content-control", contentControls);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!PARAGRAPH_LIKE.has(scope.kind)) {
|
|
195
|
+
const reason =
|
|
196
|
+
scope.kind === "scope" && scope.replaceability.reason
|
|
197
|
+
? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL
|
|
198
|
+
: `compile-refused:${scope.kind}`;
|
|
199
|
+
return unsupported(
|
|
200
|
+
reason,
|
|
201
|
+
scope.kind === "scope"
|
|
202
|
+
? multiParagraphReplacementBlockers("text")
|
|
203
|
+
: [reason],
|
|
204
|
+
evidenceWarnings(context),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (
|
|
209
|
+
scope.replaceability.level === "blocked" ||
|
|
210
|
+
scope.replaceability.level === "preserve-only" ||
|
|
211
|
+
scope.replaceability.level === "formatting-only"
|
|
212
|
+
) {
|
|
213
|
+
const reason = scope.replaceability.reason
|
|
214
|
+
? `replaceability:${scope.replaceability.reason}`
|
|
215
|
+
: `replaceability:${scope.replaceability.level}`;
|
|
216
|
+
return unsupported(reason);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return supported(
|
|
220
|
+
"compile-supported:paragraph-like:text-replace",
|
|
221
|
+
[
|
|
222
|
+
...(scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : []),
|
|
223
|
+
...evidenceWarnings(context),
|
|
224
|
+
],
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function replaceFragmentCapability(
|
|
229
|
+
scope: SemanticScope,
|
|
230
|
+
context?: ScopeCapabilityContext,
|
|
231
|
+
): ScopeCapabilityVerdict {
|
|
232
|
+
const guard = guardBlocker(scope);
|
|
233
|
+
if (guard) return blocked(guard);
|
|
234
|
+
|
|
235
|
+
const contentControls = contentControlBlockers(context);
|
|
236
|
+
if (contentControls.length > 0) {
|
|
237
|
+
return blocked("preserve:content-control", contentControls);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!PARAGRAPH_LIKE.has(scope.kind)) {
|
|
241
|
+
const reason =
|
|
242
|
+
scope.kind === "scope" && scope.replaceability.reason
|
|
243
|
+
? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL
|
|
244
|
+
: `compile-refused:${scope.kind}`;
|
|
245
|
+
return unsupported(
|
|
246
|
+
reason,
|
|
247
|
+
scope.kind === "scope"
|
|
248
|
+
? multiParagraphReplacementBlockers("fragment")
|
|
249
|
+
: [reason],
|
|
250
|
+
evidenceWarnings(context),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (scope.workflow.effectiveMode === "suggest") {
|
|
255
|
+
return unsupported(
|
|
256
|
+
`compile-refused:${scope.kind}:structured-suggesting-not-implemented`,
|
|
257
|
+
[`compile-refused:${scope.kind}:structured-suggesting-not-implemented`],
|
|
258
|
+
["guard:suggest-mode"],
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return supported(
|
|
263
|
+
"compile-supported:paragraph-like:fragment-replace",
|
|
264
|
+
evidenceWarnings(context),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function formattingCapability(
|
|
269
|
+
scope: SemanticScope,
|
|
270
|
+
context?: ScopeCapabilityContext,
|
|
271
|
+
): ScopeCapabilityVerdict {
|
|
272
|
+
const guard = guardBlocker(scope);
|
|
273
|
+
if (guard) return blocked(guard);
|
|
274
|
+
|
|
275
|
+
if (!FORMATTING_SUPPORTED.has(scope.kind)) {
|
|
276
|
+
return unsupported(`compile-refused:${scope.kind}:formatting-not-implemented`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (scope.workflow.effectiveMode === "suggest") {
|
|
280
|
+
return unsupported(
|
|
281
|
+
`compile-refused:${scope.kind}:formatting-suggesting-not-implemented`,
|
|
282
|
+
[`compile-refused:${scope.kind}:formatting-suggesting-not-implemented`],
|
|
283
|
+
["guard:suggest-mode"],
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return supported("compile-supported:scope-formatting", evidenceWarnings(context));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function metadataCapability(scope: SemanticScope): ScopeCapabilityVerdict {
|
|
291
|
+
if (METADATA_SUPPORTED.has(scope.kind)) {
|
|
292
|
+
return supported(
|
|
293
|
+
scope.kind === "table" ||
|
|
294
|
+
scope.kind === "table-row" ||
|
|
295
|
+
scope.kind === "table-cell"
|
|
296
|
+
? "metadata-supported:derived-table-family"
|
|
297
|
+
: "metadata-supported:scope-id",
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
return unsupported(`scope-not-resolvable:${scope.handle.scopeId}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function deriveScopeCapabilities(
|
|
304
|
+
scope: SemanticScope,
|
|
305
|
+
context: ScopeCapabilityContext = {},
|
|
306
|
+
): ScopeCapabilities {
|
|
307
|
+
return {
|
|
308
|
+
canReplaceText: replaceTextCapability(scope, context),
|
|
309
|
+
canReplaceFragment: replaceFragmentCapability(scope, context),
|
|
310
|
+
canInsertBefore: insertCapability(scope, "before", context),
|
|
311
|
+
canInsertAfter: insertCapability(scope, "after", context),
|
|
312
|
+
canApplyFormatting: formattingCapability(scope, context),
|
|
313
|
+
canClearFormattingLayer: formattingCapability(scope, context),
|
|
314
|
+
canAttachMetadata: metadataCapability(scope),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
WorkflowMetadataEntry,
|
|
18
18
|
WorkflowOverlay,
|
|
19
19
|
} from "./_scope-dependencies.ts";
|
|
20
|
+
import type { ScopeGeometryEvidenceProvider } from "./geometry-evidence.ts";
|
|
20
21
|
|
|
21
22
|
import {
|
|
22
23
|
buildParagraphIndexMap,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
import type { EnumeratedScope } from "./enumerate-scopes.ts";
|
|
27
28
|
import { enumerateScopes } from "./enumerate-scopes.ts";
|
|
28
29
|
import { composeEvidence } from "./evidence.ts";
|
|
30
|
+
import type { ScopeLayoutEvidenceProvider } from "./layout-evidence.ts";
|
|
29
31
|
import type {
|
|
30
32
|
ScopeBundle,
|
|
31
33
|
ScopeBundleNeighborhood,
|
|
@@ -47,6 +49,16 @@ export interface ScopeBundleInputs {
|
|
|
47
49
|
* to walk the metadata snapshot.
|
|
48
50
|
*/
|
|
49
51
|
readonly workflowMetadataEntries?: readonly WorkflowMetadataEntry[];
|
|
52
|
+
/**
|
|
53
|
+
* Optional Layer-05 replacement-envelope read seam. When omitted, bundle
|
|
54
|
+
* evidence records geometry as unavailable rather than synthesizing rects.
|
|
55
|
+
*/
|
|
56
|
+
readonly geometry?: ScopeGeometryEvidenceProvider;
|
|
57
|
+
/**
|
|
58
|
+
* Optional lower-layer layout evidence seam. When absent, bundle evidence
|
|
59
|
+
* records layout as unavailable instead of deriving page slices in L08.
|
|
60
|
+
*/
|
|
61
|
+
readonly layout?: ScopeLayoutEvidenceProvider;
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
/**
|
|
@@ -126,6 +138,8 @@ export function compileScopeBundle(
|
|
|
126
138
|
...(inputs.workflowMetadataEntries
|
|
127
139
|
? { workflowMetadataEntries: inputs.workflowMetadataEntries }
|
|
128
140
|
: {}),
|
|
141
|
+
...(inputs.geometry ? { geometry: inputs.geometry } : {}),
|
|
142
|
+
...(inputs.layout ? { layout: inputs.layout } : {}),
|
|
129
143
|
});
|
|
130
144
|
return {
|
|
131
145
|
scope,
|