@opengeni/react 6.1.0 → 7.0.0-canary.35625901699001
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/README.md +83 -2
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +1 -1
- package/dist/artifacts-presentation.js +1 -1
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +3 -3
- package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-LGH4IP47.js} +3 -3
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/{chunk-NOSYBS2D.js → chunk-2VHARTBL.js} +2 -1
- package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
- package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
- package/dist/chunk-5UONOQ3Y.js +635 -0
- package/dist/chunk-5UONOQ3Y.js.map +1 -0
- package/dist/{chunk-NYESIADM.js → chunk-AKOMC3VO.js} +3 -3
- package/dist/chunk-AKOMC3VO.js.map +1 -0
- package/dist/chunk-BRM6FVZA.js +302 -0
- package/dist/chunk-BRM6FVZA.js.map +1 -0
- package/dist/{chunk-KANFU34B.js → chunk-DBPVUGQT.js} +11 -2
- package/dist/chunk-DBPVUGQT.js.map +1 -0
- package/dist/chunk-FHIGTPME.js +1589 -0
- package/dist/chunk-FHIGTPME.js.map +1 -0
- package/dist/{chunk-5M6VTFJ5.js → chunk-GBPCZH3Y.js} +2 -2
- package/dist/{chunk-YBM6CQRU.js → chunk-GEN54DU5.js} +4 -4
- package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
- package/dist/chunk-HJ5VJ4JB.js.map +1 -0
- package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
- package/dist/{chunk-NLTS7JHI.js → chunk-JPHDWKZE.js} +5 -5
- package/dist/{chunk-TDSEILUH.js → chunk-MI6ALLLD.js} +1297 -1535
- package/dist/chunk-MI6ALLLD.js.map +1 -0
- package/dist/{chunk-ICA6R6TW.js → chunk-OWHPKAOV.js} +28 -20
- package/dist/chunk-OWHPKAOV.js.map +1 -0
- package/dist/{chunk-24M4YBCL.js → chunk-PNAVEK2U.js} +4 -4
- package/dist/{chunk-L2NBTKRX.js → chunk-TXCBE32L.js} +203 -19
- package/dist/chunk-TXCBE32L.js.map +1 -0
- package/dist/{chunk-45XEUVTK.js → chunk-UXE5SSNN.js} +293 -463
- package/dist/chunk-UXE5SSNN.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-VR4Z73EU.js} +203 -9
- package/dist/chunk-VR4Z73EU.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-XMXRI5JK.js} +65 -18
- package/dist/chunk-XMXRI5JK.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/chat-composer.d.ts +13 -3
- package/dist/components/markdown.d.ts +6 -3
- package/dist/components/message-timeline.d.ts +7 -2
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/session-capability-frame.d.ts +24 -0
- package/dist/components/session-chrome.d.ts +1 -2
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-conversation.d.ts +3 -0
- package/dist/components/session-mcp-capability-card.d.ts +22 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/composer.js +7 -7
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +5 -0
- package/dist/connect-chooser.d.ts +6 -0
- package/dist/connect-panel.d.ts +9 -2
- package/dist/connect-setup.d.ts +5 -1
- package/dist/connect.d.ts +5 -21
- package/dist/connect.js +1268 -584
- package/dist/connect.js.map +1 -1
- package/dist/connection-catalog.d.ts +6 -1
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +1 -0
- package/dist/connection-logo.d.ts +5 -2
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/hooks/use-codex-accounts.d.ts +11 -5
- package/dist/hooks/use-connect.d.ts +22 -0
- package/dist/hooks/use-session-events.d.ts +5 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-workspace-sessions.d.ts +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +771 -793
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
- package/dist/model-policy.d.ts +6 -2
- package/dist/model-policy.js +1 -1
- package/dist/{platform-activity-row-DUE2W74J.js → platform-activity-row-HLOH7FW4.js} +9 -4
- package/dist/platform-activity-row-HLOH7FW4.js.map +1 -0
- package/dist/plugin-discovery.d.ts +6 -0
- package/dist/{queue-surface-implementation-LK3S4BIW.js → queue-surface-implementation-3RIFK2BS.js} +4 -4
- package/dist/realtime.js +22 -22
- package/dist/realtime.js.map +1 -1
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +12 -0
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +14 -12
- package/dist/session.d.ts +1 -0
- package/dist/session.js +6 -4
- package/dist/skill-discovery.d.ts +11 -0
- package/dist/timeline/compaction-copy.d.ts +10 -0
- package/dist/timeline/projection.d.ts +3 -1
- package/dist/timeline/tool-display-name.d.ts +2 -1
- package/dist/timeline/turn-summary.d.ts +2 -2
- package/dist/timeline/types.d.ts +24 -2
- package/dist/{timeline-annotation-markers-DRSTLEM6.js → timeline-annotation-markers-OY6OWR3V.js} +2 -2
- package/dist/{timeline-annotation-selection-IHLUGVND.js → timeline-annotation-selection-BOXA2TIN.js} +2 -2
- package/dist/{timeline-annotations-dialog-6Q53N5FG.js → timeline-annotations-dialog-RGY3MHL3.js} +3 -3
- package/package.json +3 -3
- package/src/approvals.ts +8 -1
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +0 -9
- package/src/components/approval-surface.tsx +12 -2
- package/src/components/artifacts/document-editor.tsx +63 -6
- package/src/components/artifacts/presentation-editor.tsx +214 -9
- package/src/components/artifacts/spreadsheet-canvas.ts +3 -8
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +10 -11
- package/src/components/chat-composer.tsx +26 -3
- package/src/components/composer.tsx +10 -4
- package/src/components/human-input-form.tsx +12 -2
- package/src/components/markdown.tsx +133 -9
- package/src/components/message-timeline.tsx +104 -36
- package/src/components/model-picker.tsx +11 -3
- package/src/components/model-policy-picker-menu.tsx +10 -3
- package/src/components/preview-loading.tsx +154 -0
- package/src/components/sandbox-workspace.tsx +20 -36
- package/src/components/session-capability-frame.tsx +163 -0
- package/src/components/session-chrome.tsx +46 -77
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +10 -5
- package/src/components/session-mcp-capability-card.tsx +532 -0
- package/src/components/session-status.tsx +2 -2
- package/src/components/timeline-anchor.tsx +9 -8
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/user-message-body.tsx +7 -0
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +114 -52
- package/src/connect-chooser.tsx +171 -39
- package/src/connect-panel.tsx +73 -9
- package/src/connect-setup.tsx +72 -25
- package/src/connect.ts +13 -22
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +61 -39
- package/src/connection-discovery.tsx +235 -0
- package/src/connection-installed.tsx +2 -0
- package/src/connection-logo.tsx +18 -10
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/conversation-timeline.ts +1 -0
- package/src/discovery-cache.ts +42 -0
- package/src/hooks/use-codex-accounts.ts +33 -14
- package/src/hooks/use-composer.ts +4 -5
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-session-events.ts +114 -1
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-workspace-sessions.ts +10 -0
- package/src/index.ts +22 -2
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-policy.ts +21 -0
- package/src/plugin-details.tsx +8 -5
- package/src/plugin-discovery.tsx +92 -72
- package/src/realtime/realtime-control.tsx +6 -8
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +83 -0
- package/src/session-ui.ts +2 -0
- package/src/session.ts +1 -0
- package/src/skill-discovery.tsx +101 -56
- package/src/timeline/compaction-copy.ts +40 -0
- package/src/timeline/platform-activity-row.tsx +8 -3
- package/src/timeline/projection.ts +78 -2
- package/src/timeline/rolling-activity.tsx +4 -2
- package/src/timeline/shared.tsx +6 -1
- package/src/timeline/startup-timings.tsx +1 -1
- package/src/timeline/tool-display-name.ts +8 -6
- package/src/timeline/tool-renderers.tsx +5 -3
- package/src/timeline/turn-summary.tsx +17 -3
- package/src/timeline/types.ts +24 -2
- package/styles/compiled.css +325 -27
- package/styles/connect.css +90 -15
- package/styles/index.css +46 -0
- package/styles/session-capability.css +42 -0
- package/dist/chunk-45XEUVTK.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-IHQII5WO.js +0 -886
- package/dist/chunk-IHQII5WO.js.map +0 -1
- package/dist/chunk-KANFU34B.js.map +0 -1
- package/dist/chunk-L2NBTKRX.js.map +0 -1
- package/dist/chunk-NYESIADM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-TDSEILUH.js.map +0 -1
- package/dist/chunk-UMECRJRV.js.map +0 -1
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-DUE2W74J.js.map +0 -1
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-LGH4IP47.js.map} +0 -0
- /package/dist/{chunk-NOSYBS2D.js.map → chunk-2VHARTBL.js.map} +0 -0
- /package/dist/{chunk-5M6VTFJ5.js.map → chunk-GBPCZH3Y.js.map} +0 -0
- /package/dist/{chunk-YBM6CQRU.js.map → chunk-GEN54DU5.js.map} +0 -0
- /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
- /package/dist/{chunk-NLTS7JHI.js.map → chunk-JPHDWKZE.js.map} +0 -0
- /package/dist/{chunk-24M4YBCL.js.map → chunk-PNAVEK2U.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
- /package/dist/{queue-surface-implementation-LK3S4BIW.js.map → queue-surface-implementation-3RIFK2BS.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-DRSTLEM6.js.map → timeline-annotation-markers-OY6OWR3V.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-IHLUGVND.js.map → timeline-annotation-selection-BOXA2TIN.js.map} +0 -0
- /package/dist/{timeline-annotations-dialog-6Q53N5FG.js.map → timeline-annotations-dialog-RGY3MHL3.js.map} +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/** Private loading surface: never receives or executes the unfinished fence. */
|
|
4
|
+
export function PreviewLoading() {
|
|
5
|
+
const ref = useRef<HTMLCanvasElement>(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const canvas = ref.current;
|
|
8
|
+
if (!canvas) return;
|
|
9
|
+
let ctx: CanvasRenderingContext2D | null;
|
|
10
|
+
try {
|
|
11
|
+
ctx = canvas.getContext("2d");
|
|
12
|
+
} catch {
|
|
13
|
+
return; // The CSS grid and status remain when canvas is unavailable.
|
|
14
|
+
}
|
|
15
|
+
if (!ctx) return;
|
|
16
|
+
const context = ctx;
|
|
17
|
+
const motion = window.matchMedia?.("(prefers-reduced-motion: reduce)");
|
|
18
|
+
let frame: number | undefined;
|
|
19
|
+
let time = 2;
|
|
20
|
+
let last: number | undefined;
|
|
21
|
+
let disposed = false;
|
|
22
|
+
// Without visibility observation, keep a static surface rather than doing
|
|
23
|
+
// unbounded work for previews that may be outside the viewport.
|
|
24
|
+
let visible = false;
|
|
25
|
+
const canAnimate = () => !disposed && visible && !document.hidden && motion?.matches === false;
|
|
26
|
+
|
|
27
|
+
function draw() {
|
|
28
|
+
const w = canvas!.clientWidth;
|
|
29
|
+
const h = canvas!.clientHeight;
|
|
30
|
+
if (!w || !h || document.hidden || disposed) return;
|
|
31
|
+
const d = Math.min(window.devicePixelRatio || 1, 2);
|
|
32
|
+
if (canvas!.width !== Math.round(w * d) || canvas!.height !== Math.round(h * d)) {
|
|
33
|
+
canvas!.width = Math.round(w * d);
|
|
34
|
+
canvas!.height = Math.round(h * d);
|
|
35
|
+
}
|
|
36
|
+
const style = getComputedStyle(canvas!);
|
|
37
|
+
const ink = style.getPropertyValue("--_og-preview-ink").trim() || "#bca3ef";
|
|
38
|
+
const glowColor = style.getPropertyValue("--_og-preview-glow").trim() || "#8f6ccc";
|
|
39
|
+
context.setTransform(d, 0, 0, d, 0, 0);
|
|
40
|
+
context.clearRect(0, 0, w, h);
|
|
41
|
+
const cx = w * (0.5 + 0.13 * Math.sin(time * 0.21));
|
|
42
|
+
const cy = h * (0.5 + 0.12 * Math.cos(time * 0.27));
|
|
43
|
+
const glow = context.createRadialGradient(cx, cy, 0, cx, cy, Math.max(w, h) * 0.65);
|
|
44
|
+
glow.addColorStop(0, glowColor);
|
|
45
|
+
glow.addColorStop(1, "transparent");
|
|
46
|
+
context.globalAlpha = 0.09;
|
|
47
|
+
context.fillStyle = glow;
|
|
48
|
+
context.fillRect(0, 0, w, h);
|
|
49
|
+
const rows = Math.ceil(h / 18) + 7;
|
|
50
|
+
const cols = Math.ceil(w / 18) + 7;
|
|
51
|
+
const points = Array.from({ length: rows }, (_row, r) =>
|
|
52
|
+
Array.from({ length: cols }, (_col, c) => {
|
|
53
|
+
const x = (c - 3) * 18;
|
|
54
|
+
const y = (r - 3) * 18;
|
|
55
|
+
const dist = Math.hypot((x - cx) * 0.8, y - cy);
|
|
56
|
+
const wave = Math.sin(dist * 0.032 - time * 0.95);
|
|
57
|
+
return {
|
|
58
|
+
x: x + ((x - cx) / Math.max(dist, 1)) * wave * 4,
|
|
59
|
+
y: y + ((y - cy) / Math.max(dist, 1)) * wave * 4,
|
|
60
|
+
l: Math.pow((wave + 1) / 2, 5),
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
context.strokeStyle = ink;
|
|
65
|
+
context.fillStyle = ink;
|
|
66
|
+
context.lineWidth = 0.65;
|
|
67
|
+
points.forEach((row, r) =>
|
|
68
|
+
row.forEach((p, c) => {
|
|
69
|
+
context.globalAlpha = 0.035 + p.l * 0.055;
|
|
70
|
+
context.beginPath();
|
|
71
|
+
if (c + 1 < cols) {
|
|
72
|
+
context.moveTo(p.x, p.y);
|
|
73
|
+
context.lineTo(row[c + 1]!.x, row[c + 1]!.y);
|
|
74
|
+
}
|
|
75
|
+
if (r + 1 < rows) {
|
|
76
|
+
context.moveTo(p.x, p.y);
|
|
77
|
+
context.lineTo(points[r + 1]![c]!.x, points[r + 1]![c]!.y);
|
|
78
|
+
}
|
|
79
|
+
context.stroke();
|
|
80
|
+
context.globalAlpha = 0.15 + p.l * 0.55;
|
|
81
|
+
context.beginPath();
|
|
82
|
+
context.arc(p.x, p.y, 0.85 + p.l * 0.65, 0, Math.PI * 2);
|
|
83
|
+
context.fill();
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
context.globalAlpha = 1;
|
|
87
|
+
canvas!.dataset.painted = "true";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stop() {
|
|
91
|
+
if (frame !== undefined) window.cancelAnimationFrame(frame);
|
|
92
|
+
frame = undefined;
|
|
93
|
+
last = undefined;
|
|
94
|
+
}
|
|
95
|
+
function tick(stamp: number) {
|
|
96
|
+
frame = undefined;
|
|
97
|
+
if (!canAnimate()) return;
|
|
98
|
+
if (last !== undefined) time += Math.min((stamp - last) / 1000, 0.05);
|
|
99
|
+
last = stamp;
|
|
100
|
+
draw();
|
|
101
|
+
frame = window.requestAnimationFrame(tick);
|
|
102
|
+
}
|
|
103
|
+
function refresh() {
|
|
104
|
+
stop();
|
|
105
|
+
if (disposed || document.hidden || (!visible && intersection)) return;
|
|
106
|
+
draw();
|
|
107
|
+
if (canAnimate() && typeof window.requestAnimationFrame === "function") {
|
|
108
|
+
frame = window.requestAnimationFrame(tick);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const intersection =
|
|
112
|
+
typeof IntersectionObserver === "function"
|
|
113
|
+
? new IntersectionObserver(([entry]) => {
|
|
114
|
+
visible = entry?.isIntersecting === true;
|
|
115
|
+
refresh();
|
|
116
|
+
})
|
|
117
|
+
: undefined;
|
|
118
|
+
intersection?.observe(canvas);
|
|
119
|
+
const resize = typeof ResizeObserver === "function" ? new ResizeObserver(refresh) : undefined;
|
|
120
|
+
resize?.observe(canvas);
|
|
121
|
+
// Observe only ancestors, not the document subtree. This also updates the
|
|
122
|
+
// static reduced-motion canvas when a host changes its local theme.
|
|
123
|
+
const theme =
|
|
124
|
+
typeof MutationObserver === "function" ? new MutationObserver(refresh) : undefined;
|
|
125
|
+
for (let ancestor = canvas.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
126
|
+
theme?.observe(ancestor, {
|
|
127
|
+
attributes: true,
|
|
128
|
+
attributeFilter: ["class", "style", "data-og-theme"],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
motion?.addEventListener?.("change", refresh);
|
|
132
|
+
document.addEventListener("visibilitychange", refresh);
|
|
133
|
+
window.addEventListener("resize", refresh);
|
|
134
|
+
// Old hosts get one still frame; observed surfaces wait until onscreen.
|
|
135
|
+
if (!intersection) draw();
|
|
136
|
+
return () => {
|
|
137
|
+
disposed = true;
|
|
138
|
+
stop();
|
|
139
|
+
intersection?.disconnect();
|
|
140
|
+
resize?.disconnect();
|
|
141
|
+
theme?.disconnect();
|
|
142
|
+
motion?.removeEventListener?.("change", refresh);
|
|
143
|
+
document.removeEventListener("visibilitychange", refresh);
|
|
144
|
+
window.removeEventListener("resize", refresh);
|
|
145
|
+
};
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className="og-preview-loading">
|
|
150
|
+
<canvas ref={ref} aria-hidden="true" />
|
|
151
|
+
<span className="og-preview-loading-label">Preparing preview…</span>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
@@ -119,19 +119,6 @@ function sourceDrivenDefaultTab(
|
|
|
119
119
|
return null;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
function captureDegradedMessage(reason: string): string {
|
|
123
|
-
switch (reason) {
|
|
124
|
-
case "repository_discovery_timed_out":
|
|
125
|
-
return "Workspace capture is incomplete because repository discovery timed out. Live files remain authoritative.";
|
|
126
|
-
case "repository_discovery_result_limit_exceeded":
|
|
127
|
-
return "Workspace capture is incomplete because the repository limit was exceeded. Live files remain authoritative.";
|
|
128
|
-
case "repository_read_unavailable":
|
|
129
|
-
return "Workspace capture is incomplete because repository changes could not be read. Live files remain authoritative.";
|
|
130
|
-
default:
|
|
131
|
-
return "Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.";
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
122
|
function WorkbenchSurfaceLoading({ name }: { name: "Browser" | "Desktop" }) {
|
|
136
123
|
return (
|
|
137
124
|
<CenteredState
|
|
@@ -555,23 +542,6 @@ export function useSandboxWorkspaceTabs(
|
|
|
555
542
|
if (advertised.length > 0) return advertised;
|
|
556
543
|
return captureState.capture?.repos.map((repo) => repo.root) ?? [];
|
|
557
544
|
}, [capabilities?.Git.repos, captureState.capture]);
|
|
558
|
-
const notifiedCaptureDegradedReason = useRef<{
|
|
559
|
-
sessionId: string;
|
|
560
|
-
reason: string | null;
|
|
561
|
-
}>({
|
|
562
|
-
sessionId,
|
|
563
|
-
reason: null,
|
|
564
|
-
});
|
|
565
|
-
if (notifiedCaptureDegradedReason.current.sessionId !== sessionId) {
|
|
566
|
-
notifiedCaptureDegradedReason.current = { sessionId, reason: null };
|
|
567
|
-
}
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
const reason = captureState.degradedReason;
|
|
570
|
-
if (!reason || notifiedCaptureDegradedReason.current.reason === reason) return;
|
|
571
|
-
notifiedCaptureDegradedReason.current = { sessionId, reason };
|
|
572
|
-
onNotify?.({ kind: "error", message: captureDegradedMessage(reason) });
|
|
573
|
-
}, [sessionId, captureState.degradedReason, onNotify]);
|
|
574
|
-
|
|
575
545
|
const files = useSandboxFiles(sessionId, {
|
|
576
546
|
events,
|
|
577
547
|
// No passive Channel-A reads while cold, even after a conclusive capture
|
|
@@ -743,6 +713,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
743
713
|
comparison={changesComparison}
|
|
744
714
|
onComparisonChange={setChangesComparison}
|
|
745
715
|
captureAvailable={captureAvailable}
|
|
716
|
+
captureDegraded={captureState.degradedReason !== null}
|
|
746
717
|
captureRevision={captureState.revision}
|
|
747
718
|
capturePending={capturePending}
|
|
748
719
|
liveWorkspaceExpected={liveWorkspaceExpected}
|
|
@@ -928,6 +899,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
928
899
|
browserEnabled,
|
|
929
900
|
desktopEnabled,
|
|
930
901
|
captureAvailable,
|
|
902
|
+
captureState.degradedReason,
|
|
931
903
|
dirtyCount,
|
|
932
904
|
warmTerminal,
|
|
933
905
|
requestedFilePath,
|
|
@@ -1354,6 +1326,7 @@ function ChangesTabContent({
|
|
|
1354
1326
|
git,
|
|
1355
1327
|
comparison,
|
|
1356
1328
|
captureAvailable,
|
|
1329
|
+
captureDegraded,
|
|
1357
1330
|
captureRevision,
|
|
1358
1331
|
capturePending,
|
|
1359
1332
|
liveWorkspaceExpected,
|
|
@@ -1367,6 +1340,7 @@ function ChangesTabContent({
|
|
|
1367
1340
|
git: UseSandboxGitResult;
|
|
1368
1341
|
comparison: SandboxGitComparison;
|
|
1369
1342
|
captureAvailable: boolean;
|
|
1343
|
+
captureDegraded: boolean;
|
|
1370
1344
|
captureRevision: number | null;
|
|
1371
1345
|
capturePending: boolean;
|
|
1372
1346
|
liveWorkspaceExpected: boolean;
|
|
@@ -1460,6 +1434,8 @@ function ChangesTabContent({
|
|
|
1460
1434
|
}
|
|
1461
1435
|
|
|
1462
1436
|
if (!liveWorkspaceExpected && (comparison !== "working" || !captureAvailable)) {
|
|
1437
|
+
// Staged changes are live-only; a failed saved preview is not their blocker.
|
|
1438
|
+
const previewUnavailable = captureDegraded && !captureAvailable && comparison !== "staged";
|
|
1463
1439
|
return (
|
|
1464
1440
|
<CenteredState
|
|
1465
1441
|
icon={
|
|
@@ -1468,22 +1444,30 @@ function ChangesTabContent({
|
|
|
1468
1444
|
className="size-5 animate-spin motion-reduce:animate-none"
|
|
1469
1445
|
aria-hidden
|
|
1470
1446
|
/>
|
|
1447
|
+
) : previewUnavailable ? (
|
|
1448
|
+
<TriangleAlertIcon className="size-5" aria-hidden />
|
|
1471
1449
|
) : (
|
|
1472
1450
|
<CpuIcon className="size-5" aria-hidden />
|
|
1473
1451
|
)
|
|
1474
1452
|
}
|
|
1475
1453
|
>
|
|
1476
1454
|
<p className="text-og-sm font-medium text-og-fg">
|
|
1477
|
-
{workspaceWaking
|
|
1455
|
+
{workspaceWaking
|
|
1456
|
+
? "Waking workspace"
|
|
1457
|
+
: previewUnavailable
|
|
1458
|
+
? "Saved changes preview is unavailable"
|
|
1459
|
+
: "Workspace is resting"}
|
|
1478
1460
|
</p>
|
|
1479
1461
|
<p className="text-og-sm leading-5 text-og-fg-subtle">
|
|
1480
1462
|
{workspaceWaking
|
|
1481
1463
|
? "Connecting to the live working tree…"
|
|
1482
|
-
:
|
|
1483
|
-
? "
|
|
1484
|
-
: comparison === "
|
|
1485
|
-
? "Wake the sandbox to
|
|
1486
|
-
:
|
|
1464
|
+
: previewUnavailable
|
|
1465
|
+
? "Open the live workspace to view current changes."
|
|
1466
|
+
: comparison === "branch"
|
|
1467
|
+
? "Wake the sandbox to compare this branch with the remote default branch."
|
|
1468
|
+
: comparison === "staged"
|
|
1469
|
+
? "Wake the sandbox to inspect staged changes."
|
|
1470
|
+
: "No captured revision is available yet. Wake the sandbox to inspect uncommitted changes."}
|
|
1487
1471
|
</p>
|
|
1488
1472
|
{!workspaceWaking ? (
|
|
1489
1473
|
<DockActionButton onClick={onWake}>
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { CheckIcon, ShieldCheckIcon, XIcon } from "lucide-react";
|
|
2
|
+
import { Dialog } from "radix-ui";
|
|
3
|
+
import { useRef, useState, type ReactNode, type RefObject } from "react";
|
|
4
|
+
import { usePortalTokenStyle } from "../lib/use-portal-token-style";
|
|
5
|
+
|
|
6
|
+
export type SessionCapabilityFrameProps = {
|
|
7
|
+
name: string;
|
|
8
|
+
subtitle: string;
|
|
9
|
+
logo: string | null;
|
|
10
|
+
typeLabel: string;
|
|
11
|
+
description: string;
|
|
12
|
+
skill: boolean;
|
|
13
|
+
expanded: boolean;
|
|
14
|
+
complete: boolean;
|
|
15
|
+
actionLabel: string;
|
|
16
|
+
note: string;
|
|
17
|
+
onOpen(): void;
|
|
18
|
+
onClose(): void;
|
|
19
|
+
busy?: boolean;
|
|
20
|
+
opener?: RefObject<HTMLButtonElement | null>;
|
|
21
|
+
cardRef?: RefObject<HTMLElement | null>;
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
/** A catalogue row can own the opener while reusing the same details dialog. */
|
|
24
|
+
dialogOnly?: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function Mark({ src, name }: { src: string | null; name: string }) {
|
|
28
|
+
const [failed, setFailed] = useState<string | null>(null);
|
|
29
|
+
return (
|
|
30
|
+
<span className="og-session-capability-logo" aria-hidden>
|
|
31
|
+
{src && failed !== src ? (
|
|
32
|
+
<img src={src} alt="" loading="lazy" decoding="async" onError={() => setFailed(src)} />
|
|
33
|
+
) : (
|
|
34
|
+
name.trim().slice(0, 2).toUpperCase()
|
|
35
|
+
)}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The same conversation card in the console and embedded clients. Setup
|
|
41
|
+
* content owns the provider flow; this frame owns identity and protected focus. */
|
|
42
|
+
export function SessionCapabilityFrame({
|
|
43
|
+
name,
|
|
44
|
+
subtitle,
|
|
45
|
+
logo,
|
|
46
|
+
typeLabel,
|
|
47
|
+
description,
|
|
48
|
+
skill,
|
|
49
|
+
expanded,
|
|
50
|
+
complete,
|
|
51
|
+
actionLabel,
|
|
52
|
+
note,
|
|
53
|
+
onOpen,
|
|
54
|
+
onClose,
|
|
55
|
+
busy = false,
|
|
56
|
+
opener,
|
|
57
|
+
cardRef,
|
|
58
|
+
children,
|
|
59
|
+
dialogOnly = false,
|
|
60
|
+
}: SessionCapabilityFrameProps) {
|
|
61
|
+
const localOpener = useRef<HTMLButtonElement>(null);
|
|
62
|
+
const localCard = useRef<HTMLElement>(null);
|
|
63
|
+
const buttonRef = opener ?? localOpener;
|
|
64
|
+
const sectionRef = cardRef ?? localCard;
|
|
65
|
+
const portalStyle = usePortalTokenStyle(sectionRef.current);
|
|
66
|
+
const identity = (
|
|
67
|
+
<>
|
|
68
|
+
<Mark src={logo} name={name} />
|
|
69
|
+
<div className="og-session-capability-identity">
|
|
70
|
+
<h3>{name}</h3>
|
|
71
|
+
<span>{subtitle}</span>
|
|
72
|
+
</div>
|
|
73
|
+
<span className="og-session-capability-type">{typeLabel}</span>
|
|
74
|
+
</>
|
|
75
|
+
);
|
|
76
|
+
return (
|
|
77
|
+
<div className="og-session-capability">
|
|
78
|
+
<section
|
|
79
|
+
hidden={dialogOnly}
|
|
80
|
+
style={dialogOnly ? { display: "none" } : undefined}
|
|
81
|
+
ref={sectionRef}
|
|
82
|
+
tabIndex={-1}
|
|
83
|
+
aria-label={`${name} setup`}
|
|
84
|
+
data-state={complete ? "complete" : expanded ? "setup" : "suggested"}
|
|
85
|
+
className="og-session-capability-shell"
|
|
86
|
+
>
|
|
87
|
+
<div className="og-session-capability-header">
|
|
88
|
+
{identity}
|
|
89
|
+
{complete ? <CheckIcon size={16} aria-hidden /> : null}
|
|
90
|
+
</div>
|
|
91
|
+
{complete ? (
|
|
92
|
+
<p role="status" className="og-session-capability-copy">
|
|
93
|
+
{skill ? "Installed · Workspace" : "Connected · Available in this conversation"}
|
|
94
|
+
</p>
|
|
95
|
+
) : (
|
|
96
|
+
<>
|
|
97
|
+
<p className="og-session-capability-copy">{description}</p>
|
|
98
|
+
<div className="og-session-capability-actions">
|
|
99
|
+
<span>
|
|
100
|
+
<ShieldCheckIcon size={13} aria-hidden />
|
|
101
|
+
{skill ? "Guidance only · no account access" : "You choose what to authorize"}
|
|
102
|
+
</span>
|
|
103
|
+
<button
|
|
104
|
+
ref={buttonRef}
|
|
105
|
+
type="button"
|
|
106
|
+
onClick={onOpen}
|
|
107
|
+
aria-haspopup="dialog"
|
|
108
|
+
aria-expanded={expanded}
|
|
109
|
+
>
|
|
110
|
+
{actionLabel}
|
|
111
|
+
</button>
|
|
112
|
+
</div>
|
|
113
|
+
</>
|
|
114
|
+
)}
|
|
115
|
+
</section>
|
|
116
|
+
{!complete && !dialogOnly ? <p className="og-session-capability-note">{note}</p> : null}
|
|
117
|
+
<Dialog.Root
|
|
118
|
+
open={expanded && !complete}
|
|
119
|
+
onOpenChange={(open) => {
|
|
120
|
+
if (!open && !busy) onClose();
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
<Dialog.Portal>
|
|
124
|
+
<Dialog.Overlay className="og-root og-session-capability-overlay" style={portalStyle} />
|
|
125
|
+
<Dialog.Content
|
|
126
|
+
className="og-root og-session-capability-dialog"
|
|
127
|
+
style={portalStyle}
|
|
128
|
+
onEscapeKeyDown={(event) => {
|
|
129
|
+
if (busy) event.preventDefault();
|
|
130
|
+
}}
|
|
131
|
+
onInteractOutside={(event) => {
|
|
132
|
+
if (busy) event.preventDefault();
|
|
133
|
+
}}
|
|
134
|
+
onCloseAutoFocus={(event) => {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
const target = buttonRef.current?.isConnected
|
|
137
|
+
? buttonRef.current
|
|
138
|
+
: sectionRef.current;
|
|
139
|
+
if (target?.isConnected) target.focus();
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
<div className="og-session-capability-dialog-header">
|
|
143
|
+
<Mark src={logo} name={name} />
|
|
144
|
+
<div>
|
|
145
|
+
<Dialog.Title>{name}</Dialog.Title>
|
|
146
|
+
<Dialog.Description>
|
|
147
|
+
{typeLabel}
|
|
148
|
+
{subtitle ? ` · ${subtitle}` : ""}
|
|
149
|
+
</Dialog.Description>
|
|
150
|
+
</div>
|
|
151
|
+
{!busy ? (
|
|
152
|
+
<Dialog.Close aria-label="Close connection setup">
|
|
153
|
+
<XIcon size={18} />
|
|
154
|
+
</Dialog.Close>
|
|
155
|
+
) : null}
|
|
156
|
+
</div>
|
|
157
|
+
<div className="og-session-capability-dialog-body">{children}</div>
|
|
158
|
+
</Dialog.Content>
|
|
159
|
+
</Dialog.Portal>
|
|
160
|
+
</Dialog.Root>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|