@beyondwork/docx-react-component 1.0.106 → 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 +2 -1
- 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-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +38 -0
- package/src/api/v3/ai/attach.ts +7 -2
- package/src/api/v3/ai/replacement.ts +101 -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 +1 -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 +149 -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 +13 -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 +22 -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 +118 -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 +240 -97
- 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 +591 -20
- 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 +181 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +21 -4
- package/src/runtime/layout/paginated-layout-engine.ts +139 -15
- package/src/runtime/layout/project-block-fragments.ts +265 -7
- package/src/runtime/layout/public-facet.ts +78 -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 +3 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +168 -10
- 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 +0 -1
- package/src/ui/ui-controller-factory.ts +7 -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
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export type PerfProbeKind =
|
|
2
|
+
| "typing"
|
|
3
|
+
| "typing.predicted"
|
|
4
|
+
| "typing.reconcile"
|
|
5
|
+
| "typing.divergence"
|
|
6
|
+
| "selection"
|
|
7
|
+
| "runtime.create"
|
|
8
|
+
| "loadSession.laycacheProbe"
|
|
9
|
+
| "loadSession.compatReportCached"
|
|
10
|
+
| "snapshot.surface"
|
|
11
|
+
| "snapshot.compatibility"
|
|
12
|
+
| "snapshot.navigation"
|
|
13
|
+
| "pm.rebuild"
|
|
14
|
+
| "pm.decorations"
|
|
15
|
+
| "pm.decorations.apply"
|
|
16
|
+
| "pm.decorations.comments"
|
|
17
|
+
| "pm.decorations.revisions"
|
|
18
|
+
| "pm.decorations.workflow"
|
|
19
|
+
| "pm.mount"
|
|
20
|
+
| "shell.render"
|
|
21
|
+
| "workspace.chrome"
|
|
22
|
+
| "selection.sync"
|
|
23
|
+
| "layout.incremental"
|
|
24
|
+
| "layout.full"
|
|
25
|
+
| "render.frame_build"
|
|
26
|
+
| "render.frame_diff"
|
|
27
|
+
| "render.decoration_resolve"
|
|
28
|
+
| "chrome.overlay_reposition"
|
|
29
|
+
| "chrome.hit_test"
|
|
30
|
+
| "rail.segment_project";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Counter names the FastTextEditLane emits via `incrementInvalidationCounter`.
|
|
34
|
+
* Expose them as a const so integrators can read the shape without duplicating
|
|
35
|
+
* strings.
|
|
36
|
+
*/
|
|
37
|
+
export const PREDICTED_LANE_COUNTERS = {
|
|
38
|
+
applied: "predictions.applied",
|
|
39
|
+
equivalent: "predictions.equivalent",
|
|
40
|
+
adjusted: "predictions.adjusted",
|
|
41
|
+
rejected: "predictions.rejected",
|
|
42
|
+
rollback: "predictions.rollback",
|
|
43
|
+
structuralDivergence: "predictions.structuralDivergence",
|
|
44
|
+
bailBeforePredict: "predictions.bailBeforePredict",
|
|
45
|
+
refreshSelectionOnly: "predictions.refresh.selectionOnly",
|
|
46
|
+
refreshLocalTextEquivalent: "predictions.refresh.localTextEquivalent",
|
|
47
|
+
refreshSurfaceOnly: "predictions.refresh.surfaceOnly",
|
|
48
|
+
refreshFullProjection: "predictions.refresh.fullProjection",
|
|
49
|
+
refreshBlocked: "predictions.refresh.blocked",
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
export interface PerfProbeSample {
|
|
53
|
+
token: string;
|
|
54
|
+
kind: PerfProbeKind;
|
|
55
|
+
durationMs: number;
|
|
56
|
+
recordedAt: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface PendingProbe {
|
|
60
|
+
kind: PerfProbeKind;
|
|
61
|
+
startedAt: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface PerfProbeState {
|
|
65
|
+
enabled?: boolean;
|
|
66
|
+
nextToken?: number;
|
|
67
|
+
pending?: Record<string, PendingProbe>;
|
|
68
|
+
samples?: PerfProbeSample[];
|
|
69
|
+
maxSamples?: number;
|
|
70
|
+
invalidationCounts?: Record<string, number>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface PerfProbeSummary {
|
|
74
|
+
samples: PerfProbeSample[];
|
|
75
|
+
latest: Partial<Record<PerfProbeKind, PerfProbeSample | null>>;
|
|
76
|
+
invalidationCounts: Record<string, number>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
declare global {
|
|
80
|
+
interface Window {
|
|
81
|
+
__DOCX_REACT_PERF_PROBE__?: PerfProbeState;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function startPerfProbe(kind: PerfProbeKind): string | null {
|
|
86
|
+
const state = getEnabledState();
|
|
87
|
+
if (!state) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const token = `${kind}-${state.nextToken ?? 0}`;
|
|
92
|
+
state.nextToken = (state.nextToken ?? 0) + 1;
|
|
93
|
+
state.pending ??= {};
|
|
94
|
+
state.pending[token] = {
|
|
95
|
+
kind,
|
|
96
|
+
startedAt: performance.now(),
|
|
97
|
+
};
|
|
98
|
+
return token;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function finishPerfProbe(token: string | null | undefined): PerfProbeSample | null {
|
|
102
|
+
if (!token) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const state = getEnabledState();
|
|
106
|
+
if (!state?.pending?.[token]) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const pending = state.pending[token];
|
|
111
|
+
delete state.pending[token];
|
|
112
|
+
|
|
113
|
+
const sample: PerfProbeSample = {
|
|
114
|
+
token,
|
|
115
|
+
kind: pending.kind,
|
|
116
|
+
durationMs: performance.now() - pending.startedAt,
|
|
117
|
+
recordedAt: Date.now(),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
pushSample(state, sample);
|
|
121
|
+
|
|
122
|
+
return sample;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function recordPerfSample(
|
|
126
|
+
kind: PerfProbeKind,
|
|
127
|
+
durationMs = 0,
|
|
128
|
+
): PerfProbeSample | null {
|
|
129
|
+
const state = getEnabledState();
|
|
130
|
+
if (!state) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const token = `${kind}-${state.nextToken ?? 0}`;
|
|
135
|
+
state.nextToken = (state.nextToken ?? 0) + 1;
|
|
136
|
+
const sample: PerfProbeSample = {
|
|
137
|
+
token,
|
|
138
|
+
kind,
|
|
139
|
+
durationMs,
|
|
140
|
+
recordedAt: Date.now(),
|
|
141
|
+
};
|
|
142
|
+
pushSample(state, sample);
|
|
143
|
+
return sample;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function incrementInvalidationCounter(
|
|
147
|
+
counter: string,
|
|
148
|
+
amount = 1,
|
|
149
|
+
): number {
|
|
150
|
+
const state = getEnabledState();
|
|
151
|
+
if (!state) {
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
state.invalidationCounts ??= {};
|
|
156
|
+
state.invalidationCounts[counter] =
|
|
157
|
+
(state.invalidationCounts[counter] ?? 0) + amount;
|
|
158
|
+
return state.invalidationCounts[counter]!;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getLatestPerfSummary(): PerfProbeSummary | null {
|
|
162
|
+
const state = getEnabledState();
|
|
163
|
+
const samples = state?.samples ?? [];
|
|
164
|
+
if (!state || samples.length === 0) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
samples: [...samples],
|
|
170
|
+
latest: buildLatestSampleMap(samples),
|
|
171
|
+
invalidationCounts: { ...(state.invalidationCounts ?? {}) },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function resetPerfProbeState(): void {
|
|
176
|
+
const state = getEnabledState();
|
|
177
|
+
if (!state) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
state.nextToken = 0;
|
|
181
|
+
state.pending = {};
|
|
182
|
+
state.samples = [];
|
|
183
|
+
state.invalidationCounts = {};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getEnabledState(): PerfProbeState | null {
|
|
187
|
+
if (typeof window === "undefined") {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const state = window.__DOCX_REACT_PERF_PROBE__;
|
|
191
|
+
if (!state?.enabled) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return state;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function pushSample(state: PerfProbeState, sample: PerfProbeSample): void {
|
|
198
|
+
state.samples ??= [];
|
|
199
|
+
state.samples.push(sample);
|
|
200
|
+
const maxSamples = state.maxSamples ?? 20;
|
|
201
|
+
if (state.samples.length > maxSamples) {
|
|
202
|
+
state.samples.splice(0, state.samples.length - maxSamples);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function buildLatestSampleMap(
|
|
207
|
+
samples: PerfProbeSample[],
|
|
208
|
+
): Partial<Record<PerfProbeKind, PerfProbeSample | null>> {
|
|
209
|
+
const latest: Partial<Record<PerfProbeKind, PerfProbeSample | null>> = {};
|
|
210
|
+
for (const sample of [...samples].reverse()) {
|
|
211
|
+
if (latest[sample.kind] === undefined) {
|
|
212
|
+
latest[sample.kind] = sample;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return latest;
|
|
216
|
+
}
|
|
@@ -35,6 +35,21 @@ export interface VirtualizedViewport {
|
|
|
35
35
|
overscanHeight?: number;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export interface VirtualizedBlockGeometryProvider {
|
|
39
|
+
getBlock(blockId: string): { rects: readonly { topPx: number }[] } | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ComputeVirtualizedWindowOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Optional L05 geometry source. When present, `offsetTop` is derived from
|
|
45
|
+
* `GeometryFacet.getBlock(firstVisibleBlockId).rects[0].topPx` instead of
|
|
46
|
+
* the synthetic virtualization estimate. This keeps DOM-free consumers on
|
|
47
|
+
* geometry truth while preserving the legacy estimate for unwired tests and
|
|
48
|
+
* pre-paint sessions.
|
|
49
|
+
*/
|
|
50
|
+
geometryFacet?: VirtualizedBlockGeometryProvider;
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
export interface VirtualizedWindow {
|
|
39
54
|
startIndex: number;
|
|
40
55
|
endIndex: number;
|
|
@@ -121,6 +136,7 @@ export function createVirtualizedRenderingSession(
|
|
|
121
136
|
export function computeVirtualizedWindow(
|
|
122
137
|
session: VirtualizedRenderingSession,
|
|
123
138
|
viewport: VirtualizedViewport,
|
|
139
|
+
options: ComputeVirtualizedWindowOptions = {},
|
|
124
140
|
): VirtualizedWindow {
|
|
125
141
|
if (session.metrics.length === 0) {
|
|
126
142
|
return {
|
|
@@ -145,19 +161,38 @@ export function computeVirtualizedWindow(
|
|
|
145
161
|
const safeEndIndex = Math.max(startIndex + 1, endIndexExclusive);
|
|
146
162
|
const visibleMetrics = session.metrics.slice(startIndex, safeEndIndex);
|
|
147
163
|
const visibleBlocks = session.blocks.slice(startIndex, safeEndIndex);
|
|
164
|
+
const firstMetric = visibleMetrics[0];
|
|
165
|
+
const firstBlock = visibleBlocks[0];
|
|
148
166
|
const lastMetric = visibleMetrics[visibleMetrics.length - 1]!;
|
|
149
167
|
|
|
150
168
|
return {
|
|
151
169
|
startIndex,
|
|
152
170
|
endIndex: safeEndIndex,
|
|
153
171
|
totalHeight: session.totalHeight,
|
|
154
|
-
offsetTop:
|
|
172
|
+
offsetTop: resolveWindowOffsetTop(
|
|
173
|
+
firstBlock?.blockId ?? firstMetric?.blockId,
|
|
174
|
+
firstMetric,
|
|
175
|
+
options,
|
|
176
|
+
),
|
|
155
177
|
offsetBottom: Math.max(0, session.totalHeight - lastMetric.bottom),
|
|
156
178
|
visibleBlocks,
|
|
157
179
|
visibleMetrics,
|
|
158
180
|
};
|
|
159
181
|
}
|
|
160
182
|
|
|
183
|
+
function resolveWindowOffsetTop(
|
|
184
|
+
blockId: string | undefined,
|
|
185
|
+
fallbackMetric: VirtualizedRenderingMetrics | undefined,
|
|
186
|
+
options: ComputeVirtualizedWindowOptions,
|
|
187
|
+
): number {
|
|
188
|
+
const geometryTop = blockId
|
|
189
|
+
? options.geometryFacet?.getBlock(blockId)?.rects[0]?.topPx
|
|
190
|
+
: undefined;
|
|
191
|
+
return typeof geometryTop === "number" && Number.isFinite(geometryTop)
|
|
192
|
+
? geometryTop
|
|
193
|
+
: (fallbackMetric?.top ?? 0);
|
|
194
|
+
}
|
|
195
|
+
|
|
161
196
|
function findFirstVisibleIndex(
|
|
162
197
|
metrics: VirtualizedRenderingMetrics[],
|
|
163
198
|
top: number,
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkflowEventOrigin,
|
|
3
|
+
WorkflowMetadataEntry,
|
|
4
|
+
WorkflowMetadataSnapshot,
|
|
5
|
+
} from "../../api/public-types.ts";
|
|
6
|
+
|
|
7
|
+
export const AI_ISSUE_METADATA_ID = "ai.issue" as const;
|
|
8
|
+
|
|
9
|
+
export type AIIssueStatus = "open" | "resolved";
|
|
10
|
+
export type AIIssueLifecycleAction = "resolve" | "reopen";
|
|
11
|
+
|
|
12
|
+
export interface AIIssueLifecycleAuditEntry {
|
|
13
|
+
readonly action: AIIssueLifecycleAction;
|
|
14
|
+
readonly actorId: string;
|
|
15
|
+
readonly at: string;
|
|
16
|
+
readonly origin: string;
|
|
17
|
+
readonly fromStatus: AIIssueStatus;
|
|
18
|
+
readonly toStatus: AIIssueStatus;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AIIssueLifecycleValue {
|
|
22
|
+
readonly issueId: string;
|
|
23
|
+
readonly summary: string;
|
|
24
|
+
readonly severity?: string;
|
|
25
|
+
readonly status: AIIssueStatus;
|
|
26
|
+
readonly createdAtUtc?: string;
|
|
27
|
+
readonly statusUpdatedAtUtc?: string;
|
|
28
|
+
readonly resolvedAtUtc?: string;
|
|
29
|
+
readonly resolvedBy?: string;
|
|
30
|
+
readonly reopenedAtUtc?: string;
|
|
31
|
+
readonly reopenedBy?: string;
|
|
32
|
+
readonly lifecycle?: readonly AIIssueLifecycleAuditEntry[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AIIssueLifecycleReadback {
|
|
36
|
+
readonly issueId: string;
|
|
37
|
+
readonly entryId: string;
|
|
38
|
+
readonly scopeId?: string;
|
|
39
|
+
readonly summary: string;
|
|
40
|
+
readonly severity?: string;
|
|
41
|
+
readonly status: AIIssueStatus;
|
|
42
|
+
readonly canResolve: boolean;
|
|
43
|
+
readonly canReopen: boolean;
|
|
44
|
+
readonly createdAtUtc?: string;
|
|
45
|
+
readonly statusUpdatedAtUtc?: string;
|
|
46
|
+
readonly lastTransition?: AIIssueLifecycleAuditEntry;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface AIIssueLifecycleRuntime {
|
|
50
|
+
getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
|
|
51
|
+
setWorkflowMetadataEntries(
|
|
52
|
+
entries: readonly WorkflowMetadataEntry[],
|
|
53
|
+
origin?: WorkflowEventOrigin,
|
|
54
|
+
): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface TransitionAIIssueLifecycleInput {
|
|
58
|
+
readonly issueId: string;
|
|
59
|
+
readonly action: AIIssueLifecycleAction;
|
|
60
|
+
readonly actorId: string;
|
|
61
|
+
readonly at: string;
|
|
62
|
+
readonly origin?: WorkflowEventOrigin;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type TransitionAIIssueLifecycleResult =
|
|
66
|
+
| {
|
|
67
|
+
readonly transitioned: true;
|
|
68
|
+
readonly issueId: string;
|
|
69
|
+
readonly entryId: string;
|
|
70
|
+
readonly fromStatus: AIIssueStatus;
|
|
71
|
+
readonly toStatus: AIIssueStatus;
|
|
72
|
+
readonly audit: AIIssueLifecycleAuditEntry;
|
|
73
|
+
}
|
|
74
|
+
| {
|
|
75
|
+
readonly transitioned: false;
|
|
76
|
+
readonly issueId: string;
|
|
77
|
+
readonly reason:
|
|
78
|
+
| "issue-not-found"
|
|
79
|
+
| "issue-value-malformed"
|
|
80
|
+
| "already-resolved"
|
|
81
|
+
| "already-open";
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export function projectAIIssueLifecycleReadback(
|
|
85
|
+
entries: readonly WorkflowMetadataEntry[],
|
|
86
|
+
): AIIssueLifecycleReadback[] {
|
|
87
|
+
return entries
|
|
88
|
+
.filter((entry) => entry.metadataId === AI_ISSUE_METADATA_ID)
|
|
89
|
+
.map(projectEntry)
|
|
90
|
+
.filter((entry): entry is AIIssueLifecycleReadback => entry !== null)
|
|
91
|
+
.sort((a, b) => a.issueId.localeCompare(b.issueId));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function transitionAIIssueLifecycle(
|
|
95
|
+
runtime: AIIssueLifecycleRuntime,
|
|
96
|
+
input: TransitionAIIssueLifecycleInput,
|
|
97
|
+
): TransitionAIIssueLifecycleResult {
|
|
98
|
+
const snapshot = runtime.getWorkflowMetadataSnapshot();
|
|
99
|
+
const target = snapshot.entries.find((entry) => {
|
|
100
|
+
if (entry.metadataId !== AI_ISSUE_METADATA_ID) return false;
|
|
101
|
+
const value = coerceIssueValue(entry.value);
|
|
102
|
+
return value?.issueId === input.issueId || entry.entryId === input.issueId;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!target) {
|
|
106
|
+
return {
|
|
107
|
+
transitioned: false,
|
|
108
|
+
issueId: input.issueId,
|
|
109
|
+
reason: "issue-not-found",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const currentValue = coerceIssueValue(target.value);
|
|
114
|
+
if (!currentValue) {
|
|
115
|
+
return {
|
|
116
|
+
transitioned: false,
|
|
117
|
+
issueId: input.issueId,
|
|
118
|
+
reason: "issue-value-malformed",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const fromStatus = currentValue.status;
|
|
123
|
+
const toStatus = input.action === "resolve" ? "resolved" : "open";
|
|
124
|
+
if (fromStatus === toStatus) {
|
|
125
|
+
return {
|
|
126
|
+
transitioned: false,
|
|
127
|
+
issueId: currentValue.issueId,
|
|
128
|
+
reason: toStatus === "resolved" ? "already-resolved" : "already-open",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const audit: AIIssueLifecycleAuditEntry = {
|
|
133
|
+
action: input.action,
|
|
134
|
+
actorId: input.actorId,
|
|
135
|
+
at: input.at,
|
|
136
|
+
origin: input.origin?.source ?? "agent",
|
|
137
|
+
fromStatus,
|
|
138
|
+
toStatus,
|
|
139
|
+
};
|
|
140
|
+
const nextValue = nextIssueValue(currentValue, audit);
|
|
141
|
+
const nextEntry: WorkflowMetadataEntry = {
|
|
142
|
+
...target,
|
|
143
|
+
value: nextValue as unknown as Record<string, unknown>,
|
|
144
|
+
};
|
|
145
|
+
const nextEntries = snapshot.entries.map((entry) =>
|
|
146
|
+
entry.entryId === target.entryId ? nextEntry : entry,
|
|
147
|
+
);
|
|
148
|
+
runtime.setWorkflowMetadataEntries(nextEntries, input.origin ?? {
|
|
149
|
+
source: audit.origin,
|
|
150
|
+
at: input.at,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
transitioned: true,
|
|
155
|
+
issueId: currentValue.issueId,
|
|
156
|
+
entryId: target.entryId,
|
|
157
|
+
fromStatus,
|
|
158
|
+
toStatus,
|
|
159
|
+
audit,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function projectEntry(entry: WorkflowMetadataEntry): AIIssueLifecycleReadback | null {
|
|
164
|
+
const value = coerceIssueValue(entry.value);
|
|
165
|
+
if (!value) return null;
|
|
166
|
+
const lastTransition = value.lifecycle?.[value.lifecycle.length - 1];
|
|
167
|
+
return {
|
|
168
|
+
issueId: value.issueId,
|
|
169
|
+
entryId: entry.entryId,
|
|
170
|
+
...(entry.scopeId ? { scopeId: entry.scopeId } : {}),
|
|
171
|
+
summary: value.summary,
|
|
172
|
+
...(value.severity ? { severity: value.severity } : {}),
|
|
173
|
+
status: value.status,
|
|
174
|
+
canResolve: value.status === "open",
|
|
175
|
+
canReopen: value.status === "resolved",
|
|
176
|
+
...(value.createdAtUtc ? { createdAtUtc: value.createdAtUtc } : {}),
|
|
177
|
+
...(value.statusUpdatedAtUtc ? { statusUpdatedAtUtc: value.statusUpdatedAtUtc } : {}),
|
|
178
|
+
...(lastTransition ? { lastTransition } : {}),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function nextIssueValue(
|
|
183
|
+
value: AIIssueLifecycleValue,
|
|
184
|
+
audit: AIIssueLifecycleAuditEntry,
|
|
185
|
+
): AIIssueLifecycleValue {
|
|
186
|
+
const lifecycle = [...(value.lifecycle ?? []), audit];
|
|
187
|
+
const base =
|
|
188
|
+
audit.action === "resolve"
|
|
189
|
+
? omitReopenFields(value)
|
|
190
|
+
: omitResolveFields(value);
|
|
191
|
+
return {
|
|
192
|
+
...base,
|
|
193
|
+
status: audit.toStatus,
|
|
194
|
+
statusUpdatedAtUtc: audit.at,
|
|
195
|
+
...(audit.action === "resolve"
|
|
196
|
+
? { resolvedAtUtc: audit.at, resolvedBy: audit.actorId }
|
|
197
|
+
: { reopenedAtUtc: audit.at, reopenedBy: audit.actorId }),
|
|
198
|
+
lifecycle,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function omitReopenFields(value: AIIssueLifecycleValue): AIIssueLifecycleValue {
|
|
203
|
+
const { reopenedAtUtc: _reopenedAtUtc, reopenedBy: _reopenedBy, ...base } = value;
|
|
204
|
+
return base;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function omitResolveFields(value: AIIssueLifecycleValue): AIIssueLifecycleValue {
|
|
208
|
+
const { resolvedAtUtc: _resolvedAtUtc, resolvedBy: _resolvedBy, ...base } = value;
|
|
209
|
+
return base;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function coerceIssueValue(value: unknown): AIIssueLifecycleValue | null {
|
|
213
|
+
if (!isRecord(value)) return null;
|
|
214
|
+
if (typeof value.issueId !== "string" || value.issueId.length === 0) return null;
|
|
215
|
+
if (typeof value.summary !== "string" || value.summary.length === 0) return null;
|
|
216
|
+
const status = value.status === "resolved" ? "resolved" : value.status === "open" ? "open" : null;
|
|
217
|
+
if (!status) return null;
|
|
218
|
+
const lifecycle = Array.isArray(value.lifecycle)
|
|
219
|
+
? value.lifecycle.filter(isLifecycleAuditEntry)
|
|
220
|
+
: undefined;
|
|
221
|
+
return {
|
|
222
|
+
...value,
|
|
223
|
+
issueId: value.issueId,
|
|
224
|
+
summary: value.summary,
|
|
225
|
+
...(typeof value.severity === "string" ? { severity: value.severity } : {}),
|
|
226
|
+
status,
|
|
227
|
+
...(typeof value.createdAtUtc === "string" ? { createdAtUtc: value.createdAtUtc } : {}),
|
|
228
|
+
...(typeof value.statusUpdatedAtUtc === "string"
|
|
229
|
+
? { statusUpdatedAtUtc: value.statusUpdatedAtUtc }
|
|
230
|
+
: {}),
|
|
231
|
+
...(typeof value.resolvedAtUtc === "string" ? { resolvedAtUtc: value.resolvedAtUtc } : {}),
|
|
232
|
+
...(typeof value.resolvedBy === "string" ? { resolvedBy: value.resolvedBy } : {}),
|
|
233
|
+
...(typeof value.reopenedAtUtc === "string" ? { reopenedAtUtc: value.reopenedAtUtc } : {}),
|
|
234
|
+
...(typeof value.reopenedBy === "string" ? { reopenedBy: value.reopenedBy } : {}),
|
|
235
|
+
...(lifecycle ? { lifecycle } : {}),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function isLifecycleAuditEntry(value: unknown): value is AIIssueLifecycleAuditEntry {
|
|
240
|
+
return (
|
|
241
|
+
isRecord(value) &&
|
|
242
|
+
(value.action === "resolve" || value.action === "reopen") &&
|
|
243
|
+
typeof value.actorId === "string" &&
|
|
244
|
+
typeof value.at === "string" &&
|
|
245
|
+
typeof value.origin === "string" &&
|
|
246
|
+
(value.fromStatus === "open" || value.fromStatus === "resolved") &&
|
|
247
|
+
(value.toStatus === "open" || value.toStatus === "resolved")
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
252
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
253
|
+
}
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
*
|
|
18
18
|
* - W3 (single interaction-guard verdict) — `getInteractionGuardSnapshot`
|
|
19
19
|
* is the sole authority for effective mode + blocked reasons. The
|
|
20
|
-
* snapshot caches against (
|
|
21
|
-
* readOnly, documentMode, protectionSnapshot, overlay
|
|
22
|
-
* sharedWorkflowState).
|
|
20
|
+
* snapshot caches against structural workflow inputs (activeStory,
|
|
21
|
+
* selection, readOnly, documentMode, protectionSnapshot, overlay
|
|
22
|
+
* reference, sharedWorkflowState).
|
|
23
23
|
*
|
|
24
24
|
* - W4 (AI action policy orthogonal to guard) — this coordinator does
|
|
25
25
|
* not re-implement AI policy (see `ai-action-policy.ts`); guard
|
|
@@ -95,6 +95,11 @@ import {
|
|
|
95
95
|
import { resolveScope } from "./scope-resolver.ts";
|
|
96
96
|
import { insertScopeMarkers, removeScopeMarkers } from "../../core/commands/add-scope.ts";
|
|
97
97
|
import type { OverlayStore, MergeDetachedWarningsResult } from "./overlay-store.ts";
|
|
98
|
+
import {
|
|
99
|
+
projectWorkflowReviewOverlayLane,
|
|
100
|
+
type WorkflowReviewOverlayLaneKind,
|
|
101
|
+
type WorkflowReviewOverlayLaneSnapshot,
|
|
102
|
+
} from "./overlay-lanes.ts";
|
|
98
103
|
import {
|
|
99
104
|
type OverlayKind,
|
|
100
105
|
type OverlayVisibilityPolicy,
|
|
@@ -252,7 +257,10 @@ export interface WorkflowCoordinator {
|
|
|
252
257
|
definitions: readonly WorkflowMetadataDefinition[],
|
|
253
258
|
): void;
|
|
254
259
|
clearWorkflowMetadataDefinitions(): void;
|
|
255
|
-
setWorkflowMetadataEntries(
|
|
260
|
+
setWorkflowMetadataEntries(
|
|
261
|
+
entries: readonly WorkflowMetadataEntry[],
|
|
262
|
+
origin?: CoordinatorCommandOrigin,
|
|
263
|
+
): void;
|
|
256
264
|
clearWorkflowMetadataEntries(): void;
|
|
257
265
|
getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
|
|
258
266
|
/* --- W10 overlay-visibility policy (class-A state) --- */
|
|
@@ -290,6 +298,7 @@ export interface WorkflowCoordinator {
|
|
|
290
298
|
getInteractionGuardSnapshot(): InteractionGuardSnapshot;
|
|
291
299
|
getWorkflowScopeSnapshot(): WorkflowScopeSnapshot | null;
|
|
292
300
|
getWorkflowMarkupSnapshot(): WorkflowMarkupSnapshot;
|
|
301
|
+
getReviewOverlayLane(kind: WorkflowReviewOverlayLaneKind): WorkflowReviewOverlayLaneSnapshot;
|
|
293
302
|
/* --- scope matching / blocked reasons --- */
|
|
294
303
|
evaluateBlockedReasons(
|
|
295
304
|
selection: EditorState["selection"],
|
|
@@ -350,7 +359,6 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
350
359
|
|
|
351
360
|
let cachedInteractionGuardSnapshot:
|
|
352
361
|
| {
|
|
353
|
-
revisionToken: string;
|
|
354
362
|
activeStoryKey: string;
|
|
355
363
|
selection: EditorState["selection"];
|
|
356
364
|
readOnly: boolean;
|
|
@@ -372,7 +380,6 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
372
380
|
|
|
373
381
|
let cachedWorkflowMarkupSnapshot:
|
|
374
382
|
| {
|
|
375
|
-
revisionToken: string;
|
|
376
383
|
activeStoryKey: string;
|
|
377
384
|
protectionSnapshot: ProtectionSnapshot;
|
|
378
385
|
preservation: CanonicalDocumentEnvelope["preservation"];
|
|
@@ -631,7 +638,6 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
631
638
|
|
|
632
639
|
if (
|
|
633
640
|
cachedInteractionGuardSnapshot &&
|
|
634
|
-
cachedInteractionGuardSnapshot.revisionToken === state.revisionToken &&
|
|
635
641
|
cachedInteractionGuardSnapshot.activeStoryKey === activeStoryKey &&
|
|
636
642
|
cachedInteractionGuardSnapshot.selection === state.selection &&
|
|
637
643
|
cachedInteractionGuardSnapshot.readOnly === state.readOnly &&
|
|
@@ -706,7 +712,6 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
706
712
|
blockedReasons,
|
|
707
713
|
};
|
|
708
714
|
cachedInteractionGuardSnapshot = {
|
|
709
|
-
revisionToken: state.revisionToken,
|
|
710
715
|
activeStoryKey,
|
|
711
716
|
selection: state.selection,
|
|
712
717
|
readOnly: state.readOnly,
|
|
@@ -751,7 +756,6 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
751
756
|
|
|
752
757
|
if (
|
|
753
758
|
cachedWorkflowMarkupSnapshot &&
|
|
754
|
-
cachedWorkflowMarkupSnapshot.revisionToken === state.revisionToken &&
|
|
755
759
|
cachedWorkflowMarkupSnapshot.activeStoryKey === activeStoryKey &&
|
|
756
760
|
cachedWorkflowMarkupSnapshot.protectionSnapshot === protectionSnapshot &&
|
|
757
761
|
cachedWorkflowMarkupSnapshot.preservation === preservation &&
|
|
@@ -770,7 +774,6 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
770
774
|
workflowMetadataSnapshot: overlayStore.getMetadataSnapshot(),
|
|
771
775
|
});
|
|
772
776
|
cachedWorkflowMarkupSnapshot = {
|
|
773
|
-
revisionToken: state.revisionToken,
|
|
774
777
|
activeStoryKey,
|
|
775
778
|
protectionSnapshot,
|
|
776
779
|
preservation,
|
|
@@ -1118,11 +1121,12 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
1118
1121
|
|
|
1119
1122
|
function setWorkflowMetadataEntries(
|
|
1120
1123
|
entries: readonly WorkflowMetadataEntry[],
|
|
1124
|
+
origin: CoordinatorCommandOrigin = { source: "api", at: clock() },
|
|
1121
1125
|
): void {
|
|
1122
1126
|
deps.dispatch({
|
|
1123
1127
|
type: "workflow.set-metadata-entries",
|
|
1124
1128
|
entries,
|
|
1125
|
-
origin
|
|
1129
|
+
origin,
|
|
1126
1130
|
});
|
|
1127
1131
|
deps.editorStateChannel.recordMutation("workflowMetadata", {
|
|
1128
1132
|
namespace: "workflowMetadata",
|
|
@@ -1146,6 +1150,11 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
1146
1150
|
state: SharedWorkflowState | null,
|
|
1147
1151
|
origin: CoordinatorCommandOrigin = { source: "api", at: clock() },
|
|
1148
1152
|
): void {
|
|
1153
|
+
if (isAwarenessWorkflowOrigin(origin)) {
|
|
1154
|
+
throw new Error(
|
|
1155
|
+
"runtime.workflow.setSharedWorkflowState: awareness is transient presence state and cannot enforce durable workflow policy",
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1149
1158
|
const prior = overlayStore.getSharedWorkflowState();
|
|
1150
1159
|
if (state === prior) return;
|
|
1151
1160
|
overlayStore.replaceSharedWorkflowState(state);
|
|
@@ -1265,6 +1274,10 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
1265
1274
|
});
|
|
1266
1275
|
}
|
|
1267
1276
|
|
|
1277
|
+
function isAwarenessWorkflowOrigin(origin: CoordinatorCommandOrigin): boolean {
|
|
1278
|
+
return origin.source === "awareness";
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1268
1281
|
/* -------- queries + rail -------- */
|
|
1269
1282
|
|
|
1270
1283
|
function queryScopes(filter?: ScopeQueryFilter): ScopeQueryResult[] {
|
|
@@ -1318,6 +1331,20 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
1318
1331
|
});
|
|
1319
1332
|
}
|
|
1320
1333
|
|
|
1334
|
+
function getReviewOverlayLane(
|
|
1335
|
+
kind: WorkflowReviewOverlayLaneKind,
|
|
1336
|
+
): WorkflowReviewOverlayLaneSnapshot {
|
|
1337
|
+
const renderSnapshot = deps.getRenderSnapshot();
|
|
1338
|
+
return projectWorkflowReviewOverlayLane(kind, {
|
|
1339
|
+
comments: renderSnapshot.comments,
|
|
1340
|
+
trackedChanges: renderSnapshot.trackedChanges,
|
|
1341
|
+
suggestions: deps.getSuggestionsSnapshot() as never,
|
|
1342
|
+
workflowScope: getCachedWorkflowScopeSnapshot() ?? undefined,
|
|
1343
|
+
workflowMarkup: getCachedWorkflowMarkupSnapshot(),
|
|
1344
|
+
revision: deps.getState().revision,
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1321
1348
|
/* -------- dispatch branch handler -------- */
|
|
1322
1349
|
|
|
1323
1350
|
function applyOverlayCommand(
|
|
@@ -1483,6 +1510,7 @@ export function createWorkflowCoordinator(deps: CoordinatorDeps): WorkflowCoordi
|
|
|
1483
1510
|
getInteractionGuardSnapshot: getCachedInteractionGuardSnapshot,
|
|
1484
1511
|
getWorkflowScopeSnapshot: getCachedWorkflowScopeSnapshot,
|
|
1485
1512
|
getWorkflowMarkupSnapshot: getCachedWorkflowMarkupSnapshot,
|
|
1513
|
+
getReviewOverlayLane,
|
|
1486
1514
|
evaluateBlockedReasons,
|
|
1487
1515
|
getMatchingScope: getMatchingWorkflowScope,
|
|
1488
1516
|
getMatchingScopeStack: buildMatchingScopeStack,
|