@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,259 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ApiV3Ui,
|
|
5
|
+
GeometryRect,
|
|
6
|
+
UiOverlayLaneEntry,
|
|
7
|
+
UiOverlayLaneKind,
|
|
8
|
+
UiOverlayLaneSnapshot,
|
|
9
|
+
} from "../../api/v3/ui/index.ts";
|
|
10
|
+
import { AUTHOR_PALETTE } from "../../ui/headless/revision-decoration-model";
|
|
11
|
+
import { useUiApi } from "../ui-api-context.tsx";
|
|
12
|
+
import type { OverlayCoordinateSpace } from "./chrome-overlay-projector.ts";
|
|
13
|
+
|
|
14
|
+
type ReviewLaneKind = Extract<UiOverlayLaneKind, "comments" | "issues" | "redlines">;
|
|
15
|
+
|
|
16
|
+
const REVIEW_LANE_KINDS: readonly ReviewLaneKind[] = ["comments", "issues", "redlines"];
|
|
17
|
+
const ZERO_SPACE: OverlayCoordinateSpace = { originLeftPx: 0, originTopPx: 0 };
|
|
18
|
+
const REDLINE_BAR_WIDTH_PX = 3;
|
|
19
|
+
const REDLINE_BAR_LEFT_OFFSET_PX = 8;
|
|
20
|
+
|
|
21
|
+
export interface TwReviewOverlayLaneMarkersProps {
|
|
22
|
+
space?: OverlayCoordinateSpace;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* L11 painter for review overlay lanes surfaced by L10.
|
|
27
|
+
*
|
|
28
|
+
* L06 owns comment/issue/redline semantics and L05/L10 own anchor geometry.
|
|
29
|
+
* This layer only joins resolved lane entries to `ui.overlays.getAnchor(...)`
|
|
30
|
+
* rects and paints non-interactive markers; unavailable or rehydration-only
|
|
31
|
+
* entries stay invisible.
|
|
32
|
+
*/
|
|
33
|
+
export function TwReviewOverlayLaneMarkers({
|
|
34
|
+
space = ZERO_SPACE,
|
|
35
|
+
}: TwReviewOverlayLaneMarkersProps): React.ReactElement | null {
|
|
36
|
+
const ui = useUiApi();
|
|
37
|
+
const [lanes, setLanes] = React.useState<Readonly<Record<ReviewLaneKind, UiOverlayLaneSnapshot | null>>>(() =>
|
|
38
|
+
readReviewLanes(ui),
|
|
39
|
+
);
|
|
40
|
+
const [overlayRevision, setOverlayRevision] = React.useState(0);
|
|
41
|
+
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (!ui) {
|
|
44
|
+
setLanes(readReviewLanes(null));
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setLanes(readReviewLanes(ui));
|
|
49
|
+
const unsubscribers: Array<() => void> = [];
|
|
50
|
+
|
|
51
|
+
for (const kind of REVIEW_LANE_KINDS) {
|
|
52
|
+
try {
|
|
53
|
+
unsubscribers.push(
|
|
54
|
+
ui.overlays.subscribeLane(kind, (lane) => {
|
|
55
|
+
setLanes((current) => ({ ...current, [kind]: lane }));
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
} catch {
|
|
59
|
+
// A cold controller already reports `unavailable` through getLane.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
unsubscribers.push(
|
|
65
|
+
ui.overlays.subscribe(() => {
|
|
66
|
+
setOverlayRevision((revision) => revision + 1);
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
} catch {
|
|
70
|
+
// Overlay invalidation is best-effort; lane updates still repaint.
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return () => {
|
|
74
|
+
for (const unsubscribe of unsubscribers) unsubscribe();
|
|
75
|
+
};
|
|
76
|
+
}, [ui]);
|
|
77
|
+
|
|
78
|
+
const markers = React.useMemo(
|
|
79
|
+
() => resolveReviewLaneMarkers(ui, lanes, space),
|
|
80
|
+
[ui, lanes, overlayRevision, space],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (markers.length === 0) return null;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div
|
|
87
|
+
aria-hidden="true"
|
|
88
|
+
className="wre-review-overlay-lane-markers pointer-events-none absolute inset-0"
|
|
89
|
+
data-review-overlay-lane-markers=""
|
|
90
|
+
data-overlay-revision={overlayRevision}
|
|
91
|
+
>
|
|
92
|
+
{markers.map((marker) => (
|
|
93
|
+
<div
|
|
94
|
+
className="wre-review-overlay-lane-marker pointer-events-none absolute flex items-center justify-center rounded-sm border bg-white text-[10px] font-semibold leading-none shadow-sm"
|
|
95
|
+
data-review-lane-kind={marker.kind}
|
|
96
|
+
data-review-lane-entry-id={marker.entryId}
|
|
97
|
+
data-review-lane-marker-shape={marker.shape}
|
|
98
|
+
key={`${marker.kind}:${marker.entryId}`}
|
|
99
|
+
style={marker.style}
|
|
100
|
+
title={marker.title}
|
|
101
|
+
>
|
|
102
|
+
{marker.label}
|
|
103
|
+
</div>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface ReviewLaneMarker {
|
|
110
|
+
kind: ReviewLaneKind;
|
|
111
|
+
entryId: string;
|
|
112
|
+
label: string;
|
|
113
|
+
shape: "badge" | "bar";
|
|
114
|
+
title: string;
|
|
115
|
+
style: React.CSSProperties;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function resolveReviewLaneMarkers(
|
|
119
|
+
ui: ApiV3Ui | null,
|
|
120
|
+
lanes: Readonly<Record<ReviewLaneKind, UiOverlayLaneSnapshot | null>>,
|
|
121
|
+
space: OverlayCoordinateSpace = ZERO_SPACE,
|
|
122
|
+
): ReviewLaneMarker[] {
|
|
123
|
+
if (!ui) return [];
|
|
124
|
+
|
|
125
|
+
const markers: ReviewLaneMarker[] = [];
|
|
126
|
+
for (const kind of REVIEW_LANE_KINDS) {
|
|
127
|
+
const lane = lanes[kind];
|
|
128
|
+
if (!lane || lane.kind !== kind || lane.status === "unavailable") continue;
|
|
129
|
+
|
|
130
|
+
for (const entry of lane.entries) {
|
|
131
|
+
if (entry.status !== "resolved" || !entry.anchor) continue;
|
|
132
|
+
const rect = ui.overlays.getAnchor(entry.anchor);
|
|
133
|
+
if (!rect) continue;
|
|
134
|
+
markers.push(markerFromEntry(kind, entry, rect, space));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return markers;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function readReviewLanes(
|
|
141
|
+
ui: ApiV3Ui | null,
|
|
142
|
+
): Readonly<Record<ReviewLaneKind, UiOverlayLaneSnapshot | null>> {
|
|
143
|
+
if (!ui) {
|
|
144
|
+
return { comments: null, issues: null, redlines: null };
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
comments: ui.overlays.getLane("comments"),
|
|
148
|
+
issues: ui.overlays.getLane("issues"),
|
|
149
|
+
redlines: ui.overlays.getLane("redlines"),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function markerFromEntry(
|
|
154
|
+
kind: ReviewLaneKind,
|
|
155
|
+
entry: UiOverlayLaneEntry,
|
|
156
|
+
rect: GeometryRect,
|
|
157
|
+
space: OverlayCoordinateSpace,
|
|
158
|
+
): ReviewLaneMarker {
|
|
159
|
+
if (kind === "redlines") {
|
|
160
|
+
return redlineMarkerFromEntry(entry, rect, space);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const issueSeverity = stringValue(entry.data?.severity);
|
|
164
|
+
const color =
|
|
165
|
+
kind === "issues"
|
|
166
|
+
? issueColor(issueSeverity)
|
|
167
|
+
: "var(--color-semantic-info, #2563eb)";
|
|
168
|
+
const title =
|
|
169
|
+
kind === "comments"
|
|
170
|
+
? stringValue(entry.data?.excerpt) ?? stringValue(entry.data?.anchorLabel) ?? entry.id
|
|
171
|
+
: stringValue(entry.data?.title) ?? stringValue(entry.data?.label) ?? entry.id;
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
kind,
|
|
175
|
+
entryId: entry.id,
|
|
176
|
+
label: kind === "comments" ? "C" : "!",
|
|
177
|
+
shape: "badge",
|
|
178
|
+
title,
|
|
179
|
+
style: {
|
|
180
|
+
left: `${rect.leftPx - space.originLeftPx + Math.max(rect.widthPx, 0) + 4}px`,
|
|
181
|
+
top: `${rect.topPx - space.originTopPx}px`,
|
|
182
|
+
width: "18px",
|
|
183
|
+
height: "18px",
|
|
184
|
+
color,
|
|
185
|
+
borderColor: color,
|
|
186
|
+
zIndex: 9,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function redlineMarkerFromEntry(
|
|
192
|
+
entry: UiOverlayLaneEntry,
|
|
193
|
+
rect: GeometryRect,
|
|
194
|
+
space: OverlayCoordinateSpace,
|
|
195
|
+
): ReviewLaneMarker {
|
|
196
|
+
const color = revisionColor(entry);
|
|
197
|
+
return {
|
|
198
|
+
kind: "redlines",
|
|
199
|
+
entryId: entry.id,
|
|
200
|
+
label: "",
|
|
201
|
+
shape: "bar",
|
|
202
|
+
title:
|
|
203
|
+
stringValue(entry.data?.label) ??
|
|
204
|
+
stringValue(entry.data?.excerpt) ??
|
|
205
|
+
stringValue(entry.data?.revisionKind) ??
|
|
206
|
+
entry.id,
|
|
207
|
+
style: {
|
|
208
|
+
left: `${rect.leftPx - space.originLeftPx - REDLINE_BAR_LEFT_OFFSET_PX - REDLINE_BAR_WIDTH_PX}px`,
|
|
209
|
+
top: `${rect.topPx - space.originTopPx}px`,
|
|
210
|
+
width: `${REDLINE_BAR_WIDTH_PX}px`,
|
|
211
|
+
height: `${Math.max(rect.heightPx, 12)}px`,
|
|
212
|
+
backgroundColor: color,
|
|
213
|
+
borderColor: color,
|
|
214
|
+
borderRadius: "1px",
|
|
215
|
+
zIndex: 8,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function revisionColor(entry: UiOverlayLaneEntry): string {
|
|
221
|
+
const paletteIndex = numberValue(entry.data?.authorPaletteIndex);
|
|
222
|
+
if (paletteIndex !== null) {
|
|
223
|
+
return AUTHOR_PALETTE[paletteIndex % AUTHOR_PALETTE.length];
|
|
224
|
+
}
|
|
225
|
+
const seed = stringValue(entry.data?.authorId) ?? stringValue(entry.data?.revisionId) ?? entry.id;
|
|
226
|
+
return AUTHOR_PALETTE[hashString(seed) % AUTHOR_PALETTE.length];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function issueColor(severity: string | null): string {
|
|
230
|
+
switch (severity) {
|
|
231
|
+
case "high":
|
|
232
|
+
case "critical":
|
|
233
|
+
case "fatal":
|
|
234
|
+
return "var(--color-semantic-error, #dc2626)";
|
|
235
|
+
case "medium":
|
|
236
|
+
case "warning":
|
|
237
|
+
return "var(--color-semantic-warning, #d97706)";
|
|
238
|
+
default:
|
|
239
|
+
return "var(--color-semantic-info, #2563eb)";
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function stringValue(value: unknown): string | null {
|
|
244
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function numberValue(value: unknown): number | null {
|
|
248
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function hashString(value: string): number {
|
|
252
|
+
let hash = 0;
|
|
253
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
254
|
+
hash = (hash * 31 + value.charCodeAt(index)) >>> 0;
|
|
255
|
+
}
|
|
256
|
+
return hash;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export default TwReviewOverlayLaneMarkers;
|
|
@@ -120,6 +120,9 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
|
|
|
120
120
|
|
|
121
121
|
const getWorkflowScopeCardModel = React.useCallback(
|
|
122
122
|
(scopeId: string): ScopeCardModel | null =>
|
|
123
|
+
// Compatibility fallback for no-provider tests/headless mounts.
|
|
124
|
+
// Mounted editor surfaces must prefer `api.ui.scope.card(...)`
|
|
125
|
+
// through `getVisibleScopeCardModel`.
|
|
123
126
|
workflowFacet?.getAllScopeCardModels().find((m) => m.scopeId === scopeId) ??
|
|
124
127
|
null,
|
|
125
128
|
[workflowFacet],
|
|
@@ -177,7 +180,7 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
|
|
|
177
180
|
|
|
178
181
|
const projectorSpace: OverlayCoordinateSpace =
|
|
179
182
|
space ?? { originLeftPx: 0, originTopPx: 0 };
|
|
180
|
-
const positioned =
|
|
183
|
+
const positioned = resolveScopeCardPosition(
|
|
181
184
|
model.primaryAnchorRect,
|
|
182
185
|
projectorSpace,
|
|
183
186
|
viewportTopPx,
|
|
@@ -246,7 +249,7 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
|
|
|
246
249
|
* own shadow token tweaks) can key off to verify which placement the
|
|
247
250
|
* layer chose.
|
|
248
251
|
*/
|
|
249
|
-
function
|
|
252
|
+
export function resolveScopeCardPosition(
|
|
250
253
|
anchor: RenderFrameRect | null,
|
|
251
254
|
space: OverlayCoordinateSpace,
|
|
252
255
|
viewportTopPx: number,
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ApiV3Ui,
|
|
5
|
+
GeometryRect,
|
|
6
|
+
UiOverlayLaneEntry,
|
|
7
|
+
UiOverlayLaneKind,
|
|
8
|
+
UiOverlayLaneSnapshot,
|
|
9
|
+
} from "../../api/v3/ui/index.ts";
|
|
10
|
+
import { useUiApi } from "../ui-api-context.tsx";
|
|
11
|
+
import type { OverlayCoordinateSpace } from "./chrome-overlay-projector.ts";
|
|
12
|
+
|
|
13
|
+
type SubstrateLaneKind = Extract<
|
|
14
|
+
UiOverlayLaneKind,
|
|
15
|
+
| "selection"
|
|
16
|
+
| "caret"
|
|
17
|
+
| "field-scopes"
|
|
18
|
+
| "broad-scopes"
|
|
19
|
+
| "tables"
|
|
20
|
+
| "objects"
|
|
21
|
+
| "search"
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
const SUBSTRATE_LANE_KINDS: readonly SubstrateLaneKind[] = [
|
|
25
|
+
"selection",
|
|
26
|
+
"caret",
|
|
27
|
+
"field-scopes",
|
|
28
|
+
"broad-scopes",
|
|
29
|
+
"tables",
|
|
30
|
+
"objects",
|
|
31
|
+
"search",
|
|
32
|
+
];
|
|
33
|
+
const ZERO_SPACE: OverlayCoordinateSpace = { originLeftPx: 0, originTopPx: 0 };
|
|
34
|
+
|
|
35
|
+
export interface TwSubstrateOverlayLanesProps {
|
|
36
|
+
space?: OverlayCoordinateSpace;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* L11 painter for the non-review L10 overlay lanes.
|
|
41
|
+
*
|
|
42
|
+
* L10 owns lane snapshots and L05 owns resolved rects/anchors. This component
|
|
43
|
+
* only paints entries already exposed through `ui.overlays`; unresolved,
|
|
44
|
+
* unavailable, anchorless, and unprojected entries remain invisible.
|
|
45
|
+
*/
|
|
46
|
+
export function TwSubstrateOverlayLanes({
|
|
47
|
+
space = ZERO_SPACE,
|
|
48
|
+
}: TwSubstrateOverlayLanesProps): React.ReactElement | null {
|
|
49
|
+
const ui = useUiApi();
|
|
50
|
+
const [lanes, setLanes] = React.useState<Readonly<Record<SubstrateLaneKind, UiOverlayLaneSnapshot | null>>>(() =>
|
|
51
|
+
readSubstrateLanes(ui),
|
|
52
|
+
);
|
|
53
|
+
const [overlayRevision, setOverlayRevision] = React.useState(0);
|
|
54
|
+
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
if (!ui) {
|
|
57
|
+
setLanes(readSubstrateLanes(null));
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
setLanes(readSubstrateLanes(ui));
|
|
62
|
+
const unsubscribers: Array<() => void> = [];
|
|
63
|
+
|
|
64
|
+
for (const kind of SUBSTRATE_LANE_KINDS) {
|
|
65
|
+
try {
|
|
66
|
+
unsubscribers.push(
|
|
67
|
+
ui.overlays.subscribeLane(kind, (lane) => {
|
|
68
|
+
setLanes((current) => ({ ...current, [kind]: lane }));
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
} catch {
|
|
72
|
+
// Cold controllers report unavailable lanes through getLane.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
unsubscribers.push(
|
|
78
|
+
ui.overlays.subscribe(() => {
|
|
79
|
+
setOverlayRevision((revision) => revision + 1);
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
} catch {
|
|
83
|
+
// Lane updates still repaint; coarse overlay ticks are best-effort.
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
for (const unsubscribe of unsubscribers) unsubscribe();
|
|
88
|
+
};
|
|
89
|
+
}, [ui]);
|
|
90
|
+
|
|
91
|
+
const markers = React.useMemo(
|
|
92
|
+
() => resolveSubstrateLaneMarkers(ui, lanes, space),
|
|
93
|
+
[ui, lanes, overlayRevision, space],
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (markers.length === 0) return null;
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
aria-hidden="true"
|
|
101
|
+
className="wre-substrate-overlay-lanes pointer-events-none absolute inset-0"
|
|
102
|
+
data-substrate-overlay-lanes=""
|
|
103
|
+
data-overlay-revision={overlayRevision}
|
|
104
|
+
>
|
|
105
|
+
{markers.map((marker) => (
|
|
106
|
+
<div
|
|
107
|
+
className="wre-substrate-overlay-lane-marker pointer-events-none absolute"
|
|
108
|
+
data-substrate-lane-kind={marker.kind}
|
|
109
|
+
data-substrate-lane-entry-id={marker.entryId}
|
|
110
|
+
data-substrate-lane-shape={marker.shape}
|
|
111
|
+
key={marker.key}
|
|
112
|
+
style={marker.style}
|
|
113
|
+
title={marker.title}
|
|
114
|
+
/>
|
|
115
|
+
))}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface SubstrateLaneMarker {
|
|
121
|
+
kind: SubstrateLaneKind;
|
|
122
|
+
entryId: string;
|
|
123
|
+
key: string;
|
|
124
|
+
shape: "rect" | "caret" | "outline" | "handle";
|
|
125
|
+
title: string;
|
|
126
|
+
style: React.CSSProperties;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function resolveSubstrateLaneMarkers(
|
|
130
|
+
ui: ApiV3Ui | null,
|
|
131
|
+
lanes: Readonly<Record<SubstrateLaneKind, UiOverlayLaneSnapshot | null>>,
|
|
132
|
+
space: OverlayCoordinateSpace = ZERO_SPACE,
|
|
133
|
+
): SubstrateLaneMarker[] {
|
|
134
|
+
if (!ui) return [];
|
|
135
|
+
|
|
136
|
+
const markers: SubstrateLaneMarker[] = [];
|
|
137
|
+
for (const kind of SUBSTRATE_LANE_KINDS) {
|
|
138
|
+
const lane = lanes[kind];
|
|
139
|
+
if (!lane || lane.kind !== kind || lane.status === "unavailable") continue;
|
|
140
|
+
|
|
141
|
+
for (const entry of lane.entries) {
|
|
142
|
+
if (entry.status !== "resolved") continue;
|
|
143
|
+
const rects = resolveEntryRects(ui, entry);
|
|
144
|
+
rects.forEach((rect, index) => {
|
|
145
|
+
markers.push(markerFromEntry(kind, entry, rect, index, space));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return markers;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readSubstrateLanes(
|
|
153
|
+
ui: ApiV3Ui | null,
|
|
154
|
+
): Readonly<Record<SubstrateLaneKind, UiOverlayLaneSnapshot | null>> {
|
|
155
|
+
if (!ui) {
|
|
156
|
+
return {
|
|
157
|
+
selection: null,
|
|
158
|
+
caret: null,
|
|
159
|
+
"field-scopes": null,
|
|
160
|
+
"broad-scopes": null,
|
|
161
|
+
tables: null,
|
|
162
|
+
objects: null,
|
|
163
|
+
search: null,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
selection: ui.overlays.getLane("selection"),
|
|
168
|
+
caret: ui.overlays.getLane("caret"),
|
|
169
|
+
"field-scopes": ui.overlays.getLane("field-scopes"),
|
|
170
|
+
"broad-scopes": ui.overlays.getLane("broad-scopes"),
|
|
171
|
+
tables: ui.overlays.getLane("tables"),
|
|
172
|
+
objects: ui.overlays.getLane("objects"),
|
|
173
|
+
search: ui.overlays.getLane("search"),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function resolveEntryRects(ui: ApiV3Ui, entry: UiOverlayLaneEntry): readonly GeometryRect[] {
|
|
178
|
+
if (entry.rects && entry.rects.length > 0) return entry.rects;
|
|
179
|
+
if (!entry.anchor) return [];
|
|
180
|
+
const rect = ui.overlays.getAnchor(entry.anchor);
|
|
181
|
+
return rect ? [rect] : [];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function markerFromEntry(
|
|
185
|
+
kind: SubstrateLaneKind,
|
|
186
|
+
entry: UiOverlayLaneEntry,
|
|
187
|
+
rect: GeometryRect,
|
|
188
|
+
index: number,
|
|
189
|
+
space: OverlayCoordinateSpace,
|
|
190
|
+
): SubstrateLaneMarker {
|
|
191
|
+
const left = rect.leftPx - space.originLeftPx;
|
|
192
|
+
const top = rect.topPx - space.originTopPx;
|
|
193
|
+
const width = Math.max(rect.widthPx, kind === "caret" ? 2 : 1);
|
|
194
|
+
const height = Math.max(rect.heightPx, kind === "caret" ? 12 : 1);
|
|
195
|
+
const shape = markerShape(kind);
|
|
196
|
+
const color = markerColor(kind);
|
|
197
|
+
const title = stringValue(entry.data?.label) ?? stringValue(entry.data?.title) ?? entry.id;
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
kind,
|
|
201
|
+
entryId: entry.id,
|
|
202
|
+
key: `${kind}:${entry.id}:${index}`,
|
|
203
|
+
shape,
|
|
204
|
+
title,
|
|
205
|
+
style: markerStyle({ kind, shape, left, top, width, height, color }),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function markerShape(kind: SubstrateLaneKind): SubstrateLaneMarker["shape"] {
|
|
210
|
+
switch (kind) {
|
|
211
|
+
case "caret":
|
|
212
|
+
return "caret";
|
|
213
|
+
case "field-scopes":
|
|
214
|
+
case "broad-scopes":
|
|
215
|
+
case "tables":
|
|
216
|
+
return "outline";
|
|
217
|
+
case "objects":
|
|
218
|
+
return "handle";
|
|
219
|
+
default:
|
|
220
|
+
return "rect";
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function markerColor(kind: SubstrateLaneKind): string {
|
|
225
|
+
switch (kind) {
|
|
226
|
+
case "selection":
|
|
227
|
+
return "var(--color-selection, #93c5fd)";
|
|
228
|
+
case "caret":
|
|
229
|
+
return "var(--color-accent-primary, #2563eb)";
|
|
230
|
+
case "field-scopes":
|
|
231
|
+
return "var(--color-semantic-info, #2563eb)";
|
|
232
|
+
case "broad-scopes":
|
|
233
|
+
return "var(--color-semantic-success, #16a34a)";
|
|
234
|
+
case "tables":
|
|
235
|
+
return "var(--color-semantic-warning, #d97706)";
|
|
236
|
+
case "objects":
|
|
237
|
+
return "var(--color-accent-primary, #2563eb)";
|
|
238
|
+
case "search":
|
|
239
|
+
return "var(--color-search-highlight, #facc15)";
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function markerStyle(input: {
|
|
244
|
+
kind: SubstrateLaneKind;
|
|
245
|
+
shape: SubstrateLaneMarker["shape"];
|
|
246
|
+
left: number;
|
|
247
|
+
top: number;
|
|
248
|
+
width: number;
|
|
249
|
+
height: number;
|
|
250
|
+
color: string;
|
|
251
|
+
}): React.CSSProperties {
|
|
252
|
+
const base: React.CSSProperties = {
|
|
253
|
+
left: `${input.left}px`,
|
|
254
|
+
top: `${input.top}px`,
|
|
255
|
+
width: `${input.width}px`,
|
|
256
|
+
height: `${input.height}px`,
|
|
257
|
+
zIndex: zIndexForKind(input.kind),
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
switch (input.shape) {
|
|
261
|
+
case "caret":
|
|
262
|
+
return {
|
|
263
|
+
...base,
|
|
264
|
+
width: "2px",
|
|
265
|
+
minHeight: "12px",
|
|
266
|
+
backgroundColor: input.color,
|
|
267
|
+
borderRadius: "1px",
|
|
268
|
+
};
|
|
269
|
+
case "outline":
|
|
270
|
+
return {
|
|
271
|
+
...base,
|
|
272
|
+
border: `1px solid ${input.color}`,
|
|
273
|
+
borderRadius: input.kind === "tables" ? "2px" : "3px",
|
|
274
|
+
backgroundColor: "transparent",
|
|
275
|
+
};
|
|
276
|
+
case "handle":
|
|
277
|
+
return {
|
|
278
|
+
...base,
|
|
279
|
+
border: `1px solid ${input.color}`,
|
|
280
|
+
backgroundColor: "color-mix(in srgb, var(--color-bg-canvas, #ffffff) 75%, transparent)",
|
|
281
|
+
boxShadow: `0 0 0 1px ${input.color}`,
|
|
282
|
+
};
|
|
283
|
+
case "rect":
|
|
284
|
+
return {
|
|
285
|
+
...base,
|
|
286
|
+
backgroundColor:
|
|
287
|
+
input.kind === "search"
|
|
288
|
+
? "color-mix(in srgb, var(--color-search-highlight, #facc15) 45%, transparent)"
|
|
289
|
+
: "color-mix(in srgb, var(--color-selection, #93c5fd) 35%, transparent)",
|
|
290
|
+
borderRadius: "2px",
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function zIndexForKind(kind: SubstrateLaneKind): number {
|
|
296
|
+
switch (kind) {
|
|
297
|
+
case "caret":
|
|
298
|
+
return 12;
|
|
299
|
+
case "objects":
|
|
300
|
+
return 11;
|
|
301
|
+
case "selection":
|
|
302
|
+
return 5;
|
|
303
|
+
case "search":
|
|
304
|
+
return 4;
|
|
305
|
+
default:
|
|
306
|
+
return 7;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function stringValue(value: unknown): string | null {
|
|
311
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export default TwSubstrateOverlayLanes;
|
|
@@ -6,7 +6,10 @@ runtime API call, and `ChromePosture.debugMode` is production-forced to
|
|
|
6
6
|
`"off"`.
|
|
7
7
|
|
|
8
8
|
The supported local diagnostic surface is the mounted runtime REPL keyboard
|
|
9
|
-
shortcut.
|
|
9
|
+
shortcut. Open it with `Cmd/Ctrl+Alt+P`; its `Agent layer view` shares the
|
|
10
|
+
L01-L11 layer map with the harness debug pane, including `Alt+1`...`Alt+9`,
|
|
11
|
+
`Alt+0`, and `Alt+-` navigation plus snippets that evaluate against
|
|
12
|
+
`runtime`/`ref`. These components remain in the tree for internal harness/story
|
|
10
13
|
experiments only; do not wire them into `WordReviewEditor` or public API paths.
|
|
11
14
|
|
|
12
15
|
## Files
|