@opengeni/react 4.0.2-canary.0 → 5.0.2-canary.0
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 +42 -0
- package/dist/artifacts.js +24 -285
- package/dist/artifacts.js.map +1 -1
- package/dist/chunk-BRFMGY4L.js +274 -0
- package/dist/chunk-BRFMGY4L.js.map +1 -0
- package/dist/chunk-IWIDV7SB.js +1 -0
- package/dist/chunk-IWIDV7SB.js.map +1 -0
- package/dist/{chunk-CQG7YUJB.js → chunk-WRVKY242.js} +1169 -1017
- package/dist/chunk-WRVKY242.js.map +1 -0
- package/dist/clipboard.js +1 -0
- package/dist/components/message-timeline.d.ts +7 -3
- package/dist/connect-accounts.d.ts +10 -0
- package/dist/connect-chooser.d.ts +11 -0
- package/dist/connect-panel.d.ts +10 -0
- package/dist/connect-setup.d.ts +12 -0
- package/dist/connect.d.ts +30 -0
- package/dist/connect.js +831 -0
- package/dist/connect.js.map +1 -0
- package/dist/device-authorization.d.ts +15 -0
- package/dist/identity-link-accounts.d.ts +11 -0
- package/dist/identity-link-consent.d.ts +18 -0
- package/dist/index.js +523 -663
- package/dist/index.js.map +1 -1
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +3 -1
- package/dist/sites-ui.d.ts +33 -0
- package/dist/sites.d.ts +3 -0
- package/dist/sites.js +288 -0
- package/dist/sites.js.map +1 -0
- package/package.json +16 -2
- package/src/components/message-timeline.tsx +22 -5
- package/src/components/sandbox-workspace.tsx +9 -2
- package/src/connect-accounts.tsx +171 -0
- package/src/connect-chooser.tsx +144 -0
- package/src/connect-panel.tsx +29 -0
- package/src/connect-setup.tsx +260 -0
- package/src/connect.ts +48 -0
- package/src/device-authorization.tsx +90 -0
- package/src/hooks/use-workspace-capture.ts +3 -2
- package/src/identity-link-accounts.tsx +144 -0
- package/src/identity-link-consent.tsx +192 -0
- package/src/session-ui.ts +2 -0
- package/src/sites-ui.tsx +385 -0
- package/src/sites.ts +3 -0
- package/src/timeline/tool-renderers.tsx +1 -1
- package/styles/connect.css +48 -0
- package/styles/connect.d.ts +1 -0
- package/dist/chunk-CQG7YUJB.js.map +0 -1
package/dist/session-ui.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { HumanInputForm } from "./components/human-input-form.js";
|
|
2
2
|
export type { HumanInputAnswerDraft, HumanInputFormMessages, HumanInputFormProps, } from "./components/human-input-form.js";
|
|
3
3
|
export { HumanInputSurface } from "./components/human-input-surface.js";
|
|
4
|
+
export { ApprovalSurface } from "./components/approval-surface.js";
|
|
5
|
+
export type { ApprovalSurfaceProps, ApprovalSurfaceMessages } from "./components/approval-surface.js";
|
|
4
6
|
export type { HumanInputSurfaceProps } from "./components/human-input-surface.js";
|
|
5
7
|
export { MessageTimeline, TimelineRow } from "./components/message-timeline.js";
|
|
6
8
|
export type { MessageTimelineProps } from "./components/message-timeline.js";
|
package/dist/session-ui.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ApprovalSurface,
|
|
2
3
|
BUILT_IN_TURN_SUMMARY_FACET_IDS,
|
|
3
4
|
HumanInputForm,
|
|
4
5
|
HumanInputSurface,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
sessionChromeGoalPillLabel,
|
|
14
15
|
sessionChromeGoalPillState,
|
|
15
16
|
userMessageLikelyNeedsDisclosure
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WRVKY242.js";
|
|
17
18
|
import "./chunk-YBM6CQRU.js";
|
|
18
19
|
import "./chunk-5M6VTFJ5.js";
|
|
19
20
|
import "./chunk-ZYRGEKWS.js";
|
|
@@ -33,6 +34,7 @@ import "./chunk-NEXLLTP4.js";
|
|
|
33
34
|
import "./chunk-VRBL3F5E.js";
|
|
34
35
|
import "./chunk-22KP6LR5.js";
|
|
35
36
|
export {
|
|
37
|
+
ApprovalSurface,
|
|
36
38
|
BUILT_IN_TURN_SUMMARY_FACET_IDS,
|
|
37
39
|
HumanInputForm,
|
|
38
40
|
HumanInputSurface,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { OpenGeniClient, WorkspaceArtifact, WorkspaceArtifactContentResponse, WorkspaceArtifactDetailResponse } from "@opengeni/sdk";
|
|
2
|
+
import { type PublishedHtmlArtifactToolBridge } from "./components/artifacts/published-html-artifact-frame.js";
|
|
3
|
+
/** Implement with an authenticated host proxy. Never put an organization key
|
|
4
|
+
* in browser props; replace the client object when the host actor changes. */
|
|
5
|
+
export type SiteClient = Pick<OpenGeniClient, "listWorkspaceArtifacts" | "getWorkspaceArtifact" | "getWorkspaceArtifactHtml" | "rollbackWorkspaceArtifact" | "setWorkspaceArtifactStatus">;
|
|
6
|
+
type SiteDisplayContent = Pick<WorkspaceArtifactContentResponse, "artifactId" | "versionId" | "html" | "requestedTools">;
|
|
7
|
+
type SiteScope = {
|
|
8
|
+
client: SiteClient;
|
|
9
|
+
workspaceId: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Shared native/embedded read boundary: pin content to the observed version,
|
|
13
|
+
* and never accept a response belonging to another workspace or Site. */
|
|
14
|
+
export declare function loadSiteSnapshot(client: Pick<SiteClient, "getWorkspaceArtifact" | "getWorkspaceArtifactHtml">, workspaceId: string, siteId: string, options?: {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
includeArchivedContent?: boolean;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
detail: WorkspaceArtifactDetailResponse;
|
|
19
|
+
content: SiteDisplayContent | null;
|
|
20
|
+
}>;
|
|
21
|
+
export type SiteListProps = SiteScope & {
|
|
22
|
+
status?: "active" | "archived";
|
|
23
|
+
onOpen: (site: WorkspaceArtifact) => void;
|
|
24
|
+
};
|
|
25
|
+
export type SiteDetailProps = SiteScope & {
|
|
26
|
+
siteId: string;
|
|
27
|
+
/** Presentation hint only: the backend still enforces artifacts:publish. */
|
|
28
|
+
canPublish?: boolean;
|
|
29
|
+
toolBridge?: PublishedHtmlArtifactToolBridge;
|
|
30
|
+
};
|
|
31
|
+
export declare function SiteList(props: SiteListProps): import("react").JSX.Element;
|
|
32
|
+
export declare function SiteDetail(props: SiteDetailProps): import("react").JSX.Element;
|
|
33
|
+
export {};
|
package/dist/sites.d.ts
ADDED
package/dist/sites.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PublishedHtmlArtifactFrame
|
|
3
|
+
} from "./chunk-BRFMGY4L.js";
|
|
4
|
+
|
|
5
|
+
// src/sites-ui.tsx
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
async function loadSiteSnapshot(client, workspaceId, siteId, options = {}) {
|
|
9
|
+
const requestOptions = options.signal ? { signal: options.signal } : {};
|
|
10
|
+
const detail = await client.getWorkspaceArtifact(workspaceId, siteId, requestOptions);
|
|
11
|
+
options.signal?.throwIfAborted();
|
|
12
|
+
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
13
|
+
throw new Error("Site scope mismatch");
|
|
14
|
+
const versionId = detail.artifact.currentVersion?.id;
|
|
15
|
+
const content = versionId && (detail.artifact.status === "active" || options.includeArchivedContent) ? {
|
|
16
|
+
artifactId: siteId,
|
|
17
|
+
versionId,
|
|
18
|
+
requestedTools: detail.artifact.currentVersion.requestedTools,
|
|
19
|
+
html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {
|
|
20
|
+
...requestOptions,
|
|
21
|
+
versionId
|
|
22
|
+
})
|
|
23
|
+
} : null;
|
|
24
|
+
options.signal?.throwIfAborted();
|
|
25
|
+
if (content && (content.artifactId !== siteId || content.versionId !== versionId))
|
|
26
|
+
throw new Error("Site content mismatch");
|
|
27
|
+
return { detail: structuredClone(detail), content: content ? structuredClone(content) : null };
|
|
28
|
+
}
|
|
29
|
+
function useScopeKey(values) {
|
|
30
|
+
const [scope, setScope] = useState(values);
|
|
31
|
+
const [generation, setGeneration] = useState(0);
|
|
32
|
+
if (values.some((value, index) => value !== scope[index])) {
|
|
33
|
+
setScope(values);
|
|
34
|
+
setGeneration(generation + 1);
|
|
35
|
+
}
|
|
36
|
+
return generation;
|
|
37
|
+
}
|
|
38
|
+
function SiteList(props) {
|
|
39
|
+
const key = useScopeKey([props.client, props.workspaceId, props.status]);
|
|
40
|
+
return /* @__PURE__ */ jsx(ScopedSiteList, { ...props }, key);
|
|
41
|
+
}
|
|
42
|
+
function ScopedSiteList({
|
|
43
|
+
client,
|
|
44
|
+
workspaceId,
|
|
45
|
+
status = "active",
|
|
46
|
+
onOpen,
|
|
47
|
+
className
|
|
48
|
+
}) {
|
|
49
|
+
const [page, setPage] = useState(null);
|
|
50
|
+
const [cursor, setCursor] = useState();
|
|
51
|
+
const [error, setError] = useState(false);
|
|
52
|
+
const [reload, setReload] = useState(0);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const abort = new AbortController();
|
|
55
|
+
setPage(null);
|
|
56
|
+
setError(false);
|
|
57
|
+
void Promise.resolve().then(
|
|
58
|
+
() => client.listWorkspaceArtifacts(workspaceId, {
|
|
59
|
+
status,
|
|
60
|
+
...cursor ? { cursor } : {},
|
|
61
|
+
limit: 50,
|
|
62
|
+
signal: abort.signal
|
|
63
|
+
})
|
|
64
|
+
).then((result) => {
|
|
65
|
+
if (!abort.signal.aborted) setPage(structuredClone(result));
|
|
66
|
+
}).catch(() => {
|
|
67
|
+
if (!abort.signal.aborted) setError(true);
|
|
68
|
+
});
|
|
69
|
+
return () => abort.abort();
|
|
70
|
+
}, [client, workspaceId, status, cursor, reload]);
|
|
71
|
+
return /* @__PURE__ */ jsxs("section", { className, "aria-label": "Sites", "aria-busy": !page && !error, children: [
|
|
72
|
+
error ? /* @__PURE__ */ jsx("p", { role: "alert", children: "Sites are unavailable or access has changed. Refresh to check access." }) : !page ? /* @__PURE__ */ jsx("p", { role: "status", children: "Loading Sites\u2026" }) : page.artifacts.length === 0 ? /* @__PURE__ */ jsxs("p", { role: "status", children: [
|
|
73
|
+
"No ",
|
|
74
|
+
status,
|
|
75
|
+
" Sites."
|
|
76
|
+
] }) : /* @__PURE__ */ jsx("ul", { children: page.artifacts.map((site) => /* @__PURE__ */ jsxs("li", { children: [
|
|
77
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => onOpen(structuredClone(site)), children: site.title }),
|
|
78
|
+
/* @__PURE__ */ jsx("p", { children: site.description }),
|
|
79
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
80
|
+
site.status,
|
|
81
|
+
" \xB7 revision ",
|
|
82
|
+
site.currentVersion?.revision ?? "unpublished"
|
|
83
|
+
] })
|
|
84
|
+
] }, site.id)) }),
|
|
85
|
+
page?.nextCursor && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCursor(page.nextCursor), children: "Next page" }),
|
|
86
|
+
cursor && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCursor(void 0), children: "First page" }),
|
|
87
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setReload((value) => value + 1), children: "Refresh Sites" })
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
function SiteDetail(props) {
|
|
91
|
+
const key = useScopeKey([props.client, props.workspaceId, props.siteId]);
|
|
92
|
+
return /* @__PURE__ */ jsx(ScopedSiteDetail, { ...props }, key);
|
|
93
|
+
}
|
|
94
|
+
function ScopedSiteDetail({
|
|
95
|
+
client,
|
|
96
|
+
workspaceId,
|
|
97
|
+
siteId,
|
|
98
|
+
canPublish = false,
|
|
99
|
+
toolBridge,
|
|
100
|
+
className
|
|
101
|
+
}) {
|
|
102
|
+
const [loaded, setLoaded] = useState(null);
|
|
103
|
+
const [error, setError] = useState(false);
|
|
104
|
+
const [reload, setReload] = useState(0);
|
|
105
|
+
const [busy, setBusy] = useState(false);
|
|
106
|
+
const [rollbackId, setRollbackId] = useState("");
|
|
107
|
+
const [confirmation, setConfirmation] = useState(null);
|
|
108
|
+
const mutation = useRef(null);
|
|
109
|
+
useEffect(() => () => mutation.current?.abort(), []);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const abort = new AbortController();
|
|
112
|
+
setLoaded(null);
|
|
113
|
+
setError(false);
|
|
114
|
+
setConfirmation(null);
|
|
115
|
+
setRollbackId("");
|
|
116
|
+
void Promise.resolve().then(async () => {
|
|
117
|
+
const snapshot = await loadSiteSnapshot(client, workspaceId, siteId, {
|
|
118
|
+
signal: abort.signal
|
|
119
|
+
});
|
|
120
|
+
if (!abort.signal.aborted) setLoaded(snapshot);
|
|
121
|
+
}).catch(() => {
|
|
122
|
+
if (!abort.signal.aborted) {
|
|
123
|
+
setLoaded(null);
|
|
124
|
+
setError(true);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return () => abort.abort();
|
|
128
|
+
}, [client, workspaceId, siteId, reload]);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (!loaded || busy) return;
|
|
131
|
+
const abort = new AbortController();
|
|
132
|
+
let checking = false;
|
|
133
|
+
const timer = setInterval(() => {
|
|
134
|
+
if (checking) return;
|
|
135
|
+
checking = true;
|
|
136
|
+
void Promise.resolve().then(() => client.getWorkspaceArtifact(workspaceId, siteId, { signal: abort.signal })).then((detail) => {
|
|
137
|
+
if (abort.signal.aborted) return;
|
|
138
|
+
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
139
|
+
throw new Error("Site scope mismatch");
|
|
140
|
+
if (detail.artifact.currentVersion?.id !== loaded.detail.artifact.currentVersion?.id || detail.artifact.status !== loaded.detail.artifact.status) {
|
|
141
|
+
setLoaded(null);
|
|
142
|
+
setReload((value) => value + 1);
|
|
143
|
+
}
|
|
144
|
+
}).catch(() => {
|
|
145
|
+
if (!abort.signal.aborted) {
|
|
146
|
+
setLoaded(null);
|
|
147
|
+
setError(true);
|
|
148
|
+
}
|
|
149
|
+
}).finally(() => {
|
|
150
|
+
checking = false;
|
|
151
|
+
});
|
|
152
|
+
}, 15e3);
|
|
153
|
+
return () => {
|
|
154
|
+
abort.abort();
|
|
155
|
+
clearInterval(timer);
|
|
156
|
+
};
|
|
157
|
+
}, [client, workspaceId, siteId, loaded, busy]);
|
|
158
|
+
const commit = async () => {
|
|
159
|
+
const current = loaded?.detail.artifact.currentVersion;
|
|
160
|
+
if (!canPublish || !loaded || !current || !confirmation || mutation.current) return;
|
|
161
|
+
const abort = new AbortController();
|
|
162
|
+
mutation.current = abort;
|
|
163
|
+
setBusy(true);
|
|
164
|
+
try {
|
|
165
|
+
const common = { expectedCurrentVersionId: current.id, idempotencyKey: crypto.randomUUID() };
|
|
166
|
+
if (confirmation === "rollback") {
|
|
167
|
+
if (!loaded.detail.versions.some((version) => version.id === rollbackId))
|
|
168
|
+
throw new Error("Unknown version");
|
|
169
|
+
await client.rollbackWorkspaceArtifact(
|
|
170
|
+
workspaceId,
|
|
171
|
+
siteId,
|
|
172
|
+
{ ...common, versionId: rollbackId, reason: "Restore explicitly selected Site version" },
|
|
173
|
+
{ signal: abort.signal }
|
|
174
|
+
);
|
|
175
|
+
} else {
|
|
176
|
+
await client.setWorkspaceArtifactStatus(
|
|
177
|
+
workspaceId,
|
|
178
|
+
siteId,
|
|
179
|
+
{
|
|
180
|
+
...common,
|
|
181
|
+
status: loaded.detail.artifact.status === "active" ? "archived" : "active",
|
|
182
|
+
reason: "Explicit Site status change"
|
|
183
|
+
},
|
|
184
|
+
{ signal: abort.signal }
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (!abort.signal.aborted) {
|
|
188
|
+
setLoaded(null);
|
|
189
|
+
setReload((value) => value + 1);
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
if (!abort.signal.aborted) {
|
|
193
|
+
setLoaded(null);
|
|
194
|
+
setError(true);
|
|
195
|
+
}
|
|
196
|
+
} finally {
|
|
197
|
+
if (!abort.signal.aborted) {
|
|
198
|
+
mutation.current = null;
|
|
199
|
+
setBusy(false);
|
|
200
|
+
setConfirmation(null);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const site = loaded?.detail.artifact;
|
|
205
|
+
return /* @__PURE__ */ jsxs(
|
|
206
|
+
"section",
|
|
207
|
+
{
|
|
208
|
+
className,
|
|
209
|
+
"aria-label": "Site details",
|
|
210
|
+
"aria-busy": busy || !loaded && !error,
|
|
211
|
+
children: [
|
|
212
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", children: "Site state could not be confirmed or access has changed. Refresh before continuing." }),
|
|
213
|
+
!loaded && !error && /* @__PURE__ */ jsx("p", { role: "status", children: "Loading Site\u2026" }),
|
|
214
|
+
site && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
215
|
+
/* @__PURE__ */ jsx("h2", { children: site.title }),
|
|
216
|
+
/* @__PURE__ */ jsx("p", { children: site.description }),
|
|
217
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
218
|
+
site.status,
|
|
219
|
+
" \xB7 revision ",
|
|
220
|
+
site.currentVersion?.revision ?? "unpublished"
|
|
221
|
+
] }),
|
|
222
|
+
loaded.content && /* @__PURE__ */ jsx(
|
|
223
|
+
PublishedHtmlArtifactFrame,
|
|
224
|
+
{
|
|
225
|
+
html: loaded.content.html,
|
|
226
|
+
title: site.title,
|
|
227
|
+
...toolBridge ? { toolBridge } : {}
|
|
228
|
+
}
|
|
229
|
+
),
|
|
230
|
+
/* @__PURE__ */ jsx("h3", { children: "Versions" }),
|
|
231
|
+
/* @__PURE__ */ jsx("ul", { children: loaded.detail.versions.map((version) => /* @__PURE__ */ jsxs("li", { children: [
|
|
232
|
+
"Revision ",
|
|
233
|
+
version.revision,
|
|
234
|
+
" \xB7 ",
|
|
235
|
+
version.createdAt,
|
|
236
|
+
version.id === site.currentVersion?.id ? " \xB7 current" : ""
|
|
237
|
+
] }, version.id)) }),
|
|
238
|
+
loaded.detail.versionsTruncated && /* @__PURE__ */ jsx("p", { children: "Older versions are not included in this response." }),
|
|
239
|
+
canPublish && site.currentVersion && /* @__PURE__ */ jsxs("fieldset", { disabled: busy, children: [
|
|
240
|
+
/* @__PURE__ */ jsx("legend", { children: "Manage Site" }),
|
|
241
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmation("status"), children: site.status === "active" ? "Archive Site" : "Restore Site" }),
|
|
242
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
243
|
+
"Version to restore",
|
|
244
|
+
/* @__PURE__ */ jsxs(
|
|
245
|
+
"select",
|
|
246
|
+
{
|
|
247
|
+
value: rollbackId,
|
|
248
|
+
onChange: (event) => {
|
|
249
|
+
setRollbackId(event.target.value);
|
|
250
|
+
setConfirmation(null);
|
|
251
|
+
},
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Choose a version" }),
|
|
254
|
+
loaded.detail.versions.filter((version) => version.id !== site.currentVersion?.id).map((version) => /* @__PURE__ */ jsxs("option", { value: version.id, children: [
|
|
255
|
+
"Revision ",
|
|
256
|
+
version.revision
|
|
257
|
+
] }, version.id))
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
] }),
|
|
262
|
+
/* @__PURE__ */ jsx(
|
|
263
|
+
"button",
|
|
264
|
+
{
|
|
265
|
+
type: "button",
|
|
266
|
+
disabled: !rollbackId,
|
|
267
|
+
onClick: () => setConfirmation("rollback"),
|
|
268
|
+
children: "Review rollback"
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
confirmation && /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Confirm Site change", children: [
|
|
272
|
+
/* @__PURE__ */ jsx("p", { children: confirmation === "rollback" ? "Make the selected version current?" : site.status === "active" ? "Archive this Site for workspace users?" : "Restore this Site for workspace users?" }),
|
|
273
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void commit(), children: "Confirm change" }),
|
|
274
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmation(null), children: "Keep current state" })
|
|
275
|
+
] })
|
|
276
|
+
] })
|
|
277
|
+
] }),
|
|
278
|
+
/* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => setReload((value) => value + 1), children: "Refresh Site" })
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
export {
|
|
284
|
+
SiteDetail,
|
|
285
|
+
SiteList,
|
|
286
|
+
loadSiteSnapshot
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=sites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sites-ui.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type {\n OpenGeniClient,\n WorkspaceArtifact,\n WorkspaceArtifactContentResponse,\n WorkspaceArtifactDetailResponse,\n WorkspaceArtifactListResponse,\n} from \"@opengeni/sdk\";\nimport {\n PublishedHtmlArtifactFrame,\n type PublishedHtmlArtifactToolBridge,\n} from \"./components/artifacts/published-html-artifact-frame\";\n\n/** Implement with an authenticated host proxy. Never put an organization key\n * in browser props; replace the client object when the host actor changes. */\nexport type SiteClient = Pick<\n OpenGeniClient,\n | \"listWorkspaceArtifacts\"\n | \"getWorkspaceArtifact\"\n | \"getWorkspaceArtifactHtml\"\n | \"rollbackWorkspaceArtifact\"\n | \"setWorkspaceArtifactStatus\"\n>;\ntype SiteDisplayContent = Pick<\n WorkspaceArtifactContentResponse,\n \"artifactId\" | \"versionId\" | \"html\" | \"requestedTools\"\n>;\ntype SiteScope = { client: SiteClient; workspaceId: string; className?: string };\n/** Shared native/embedded read boundary: pin content to the observed version,\n * and never accept a response belonging to another workspace or Site. */\nexport async function loadSiteSnapshot(\n client: Pick<SiteClient, \"getWorkspaceArtifact\" | \"getWorkspaceArtifactHtml\">,\n workspaceId: string,\n siteId: string,\n options: { signal?: AbortSignal; includeArchivedContent?: boolean } = {},\n): Promise<{\n detail: WorkspaceArtifactDetailResponse;\n content: SiteDisplayContent | null;\n}> {\n const requestOptions = options.signal ? { signal: options.signal } : {};\n const detail = await client.getWorkspaceArtifact(workspaceId, siteId, requestOptions);\n options.signal?.throwIfAborted();\n if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)\n throw new Error(\"Site scope mismatch\");\n const versionId = detail.artifact.currentVersion?.id;\n const content =\n versionId && (detail.artifact.status === \"active\" || options.includeArchivedContent)\n ? {\n artifactId: siteId,\n versionId,\n requestedTools: detail.artifact.currentVersion!.requestedTools,\n html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {\n ...requestOptions,\n versionId,\n }),\n }\n : null;\n options.signal?.throwIfAborted();\n if (content && (content.artifactId !== siteId || content.versionId !== versionId))\n throw new Error(\"Site content mismatch\");\n return { detail: structuredClone(detail), content: content ? structuredClone(content) : null };\n}\nexport type SiteListProps = SiteScope & {\n status?: \"active\" | \"archived\";\n onOpen: (site: WorkspaceArtifact) => void;\n};\nexport type SiteDetailProps = SiteScope & {\n siteId: string;\n /** Presentation hint only: the backend still enforces artifacts:publish. */\n canPublish?: boolean;\n toolBridge?: PublishedHtmlArtifactToolBridge;\n};\n\nfunction useScopeKey(values: readonly unknown[]) {\n const [scope, setScope] = useState(values);\n const [generation, setGeneration] = useState(0);\n if (values.some((value, index) => value !== scope[index])) {\n setScope(values);\n setGeneration(generation + 1);\n }\n return generation;\n}\n\nexport function SiteList(props: SiteListProps) {\n const key = useScopeKey([props.client, props.workspaceId, props.status]);\n return <ScopedSiteList key={key} {...props} />;\n}\nfunction ScopedSiteList({\n client,\n workspaceId,\n status = \"active\",\n onOpen,\n className,\n}: SiteListProps) {\n const [page, setPage] = useState<WorkspaceArtifactListResponse | null>(null);\n const [cursor, setCursor] = useState<string | undefined>();\n const [error, setError] = useState(false);\n const [reload, setReload] = useState(0);\n useEffect(() => {\n const abort = new AbortController();\n setPage(null);\n setError(false);\n void Promise.resolve()\n .then(() =>\n client.listWorkspaceArtifacts(workspaceId, {\n status,\n ...(cursor ? { cursor } : {}),\n limit: 50,\n signal: abort.signal,\n }),\n )\n .then((result) => {\n if (!abort.signal.aborted) setPage(structuredClone(result));\n })\n .catch(() => {\n if (!abort.signal.aborted) setError(true);\n });\n return () => abort.abort();\n }, [client, workspaceId, status, cursor, reload]);\n return (\n <section className={className} aria-label=\"Sites\" aria-busy={!page && !error}>\n {error ? (\n <p role=\"alert\">Sites are unavailable or access has changed. Refresh to check access.</p>\n ) : !page ? (\n <p role=\"status\">Loading Sites…</p>\n ) : page.artifacts.length === 0 ? (\n <p role=\"status\">No {status} Sites.</p>\n ) : (\n <ul>\n {page.artifacts.map((site) => (\n <li key={site.id}>\n <button type=\"button\" onClick={() => onOpen(structuredClone(site))}>\n {site.title}\n </button>\n <p>{site.description}</p>\n <span>\n {site.status} · revision {site.currentVersion?.revision ?? \"unpublished\"}\n </span>\n </li>\n ))}\n </ul>\n )}\n {page?.nextCursor && (\n <button type=\"button\" onClick={() => setCursor(page.nextCursor!)}>\n Next page\n </button>\n )}\n {cursor && (\n <button type=\"button\" onClick={() => setCursor(undefined)}>\n First page\n </button>\n )}\n <button type=\"button\" onClick={() => setReload((value) => value + 1)}>\n Refresh Sites\n </button>\n </section>\n );\n}\n\nexport function SiteDetail(props: SiteDetailProps) {\n const key = useScopeKey([props.client, props.workspaceId, props.siteId]);\n return <ScopedSiteDetail key={key} {...props} />;\n}\nfunction ScopedSiteDetail({\n client,\n workspaceId,\n siteId,\n canPublish = false,\n toolBridge,\n className,\n}: SiteDetailProps) {\n const [loaded, setLoaded] = useState<{\n detail: WorkspaceArtifactDetailResponse;\n content: SiteDisplayContent | null;\n } | null>(null);\n const [error, setError] = useState(false);\n const [reload, setReload] = useState(0);\n const [busy, setBusy] = useState(false);\n const [rollbackId, setRollbackId] = useState(\"\");\n const [confirmation, setConfirmation] = useState<\"status\" | \"rollback\" | null>(null);\n const mutation = useRef<AbortController | null>(null);\n useEffect(() => () => mutation.current?.abort(), []);\n useEffect(() => {\n const abort = new AbortController();\n setLoaded(null);\n setError(false);\n setConfirmation(null);\n setRollbackId(\"\");\n void Promise.resolve()\n .then(async () => {\n const snapshot = await loadSiteSnapshot(client, workspaceId, siteId, {\n signal: abort.signal,\n });\n if (!abort.signal.aborted) setLoaded(snapshot);\n })\n .catch(() => {\n if (!abort.signal.aborted) {\n setLoaded(null);\n setError(true);\n }\n });\n return () => abort.abort();\n }, [client, workspaceId, siteId, reload]);\n // Recheck live read authority without rebuilding the iframe on every tick.\n // No overlapping polls, and failures remove the document and tool bridge.\n useEffect(() => {\n if (!loaded || busy) return;\n const abort = new AbortController();\n let checking = false;\n const timer = setInterval(() => {\n if (checking) return;\n checking = true;\n void Promise.resolve()\n .then(() => client.getWorkspaceArtifact(workspaceId, siteId, { signal: abort.signal }))\n .then((detail) => {\n if (abort.signal.aborted) return;\n if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)\n throw new Error(\"Site scope mismatch\");\n if (\n detail.artifact.currentVersion?.id !== loaded.detail.artifact.currentVersion?.id ||\n detail.artifact.status !== loaded.detail.artifact.status\n ) {\n setLoaded(null);\n setReload((value) => value + 1);\n }\n })\n .catch(() => {\n if (!abort.signal.aborted) {\n setLoaded(null);\n setError(true);\n }\n })\n .finally(() => {\n checking = false;\n });\n }, 15_000);\n return () => {\n abort.abort();\n clearInterval(timer);\n };\n }, [client, workspaceId, siteId, loaded, busy]);\n const commit = async () => {\n const current = loaded?.detail.artifact.currentVersion;\n if (!canPublish || !loaded || !current || !confirmation || mutation.current) return;\n const abort = new AbortController();\n mutation.current = abort;\n setBusy(true);\n try {\n const common = { expectedCurrentVersionId: current.id, idempotencyKey: crypto.randomUUID() };\n if (confirmation === \"rollback\") {\n if (!loaded.detail.versions.some((version) => version.id === rollbackId))\n throw new Error(\"Unknown version\");\n await client.rollbackWorkspaceArtifact(\n workspaceId,\n siteId,\n { ...common, versionId: rollbackId, reason: \"Restore explicitly selected Site version\" },\n { signal: abort.signal },\n );\n } else {\n await client.setWorkspaceArtifactStatus(\n workspaceId,\n siteId,\n {\n ...common,\n status: loaded.detail.artifact.status === \"active\" ? \"archived\" : \"active\",\n reason: \"Explicit Site status change\",\n },\n { signal: abort.signal },\n );\n }\n if (!abort.signal.aborted) {\n setLoaded(null);\n setReload((value) => value + 1);\n }\n } catch {\n if (!abort.signal.aborted) {\n setLoaded(null);\n setError(true);\n }\n } finally {\n if (!abort.signal.aborted) {\n mutation.current = null;\n setBusy(false);\n setConfirmation(null);\n }\n }\n };\n const site = loaded?.detail.artifact;\n return (\n <section\n className={className}\n aria-label=\"Site details\"\n aria-busy={busy || (!loaded && !error)}\n >\n {error && (\n <p role=\"alert\">\n Site state could not be confirmed or access has changed. Refresh before continuing.\n </p>\n )}\n {!loaded && !error && <p role=\"status\">Loading Site…</p>}\n {site && (\n <>\n <h2>{site.title}</h2>\n <p>{site.description}</p>\n <p>\n {site.status} · revision {site.currentVersion?.revision ?? \"unpublished\"}\n </p>\n {loaded.content && (\n <PublishedHtmlArtifactFrame\n html={loaded.content.html}\n title={site.title}\n {...(toolBridge ? { toolBridge } : {})}\n />\n )}\n <h3>Versions</h3>\n <ul>\n {loaded.detail.versions.map((version) => (\n <li key={version.id}>\n Revision {version.revision} · {version.createdAt}\n {version.id === site.currentVersion?.id ? \" · current\" : \"\"}\n </li>\n ))}\n </ul>\n {loaded.detail.versionsTruncated && (\n <p>Older versions are not included in this response.</p>\n )}\n {canPublish && site.currentVersion && (\n <fieldset disabled={busy}>\n <legend>Manage Site</legend>\n <button type=\"button\" onClick={() => setConfirmation(\"status\")}>\n {site.status === \"active\" ? \"Archive Site\" : \"Restore Site\"}\n </button>\n <label>\n Version to restore\n <select\n value={rollbackId}\n onChange={(event) => {\n setRollbackId(event.target.value);\n setConfirmation(null);\n }}\n >\n <option value=\"\">Choose a version</option>\n {loaded.detail.versions\n .filter((version) => version.id !== site.currentVersion?.id)\n .map((version) => (\n <option key={version.id} value={version.id}>\n Revision {version.revision}\n </option>\n ))}\n </select>\n </label>\n <button\n type=\"button\"\n disabled={!rollbackId}\n onClick={() => setConfirmation(\"rollback\")}\n >\n Review rollback\n </button>\n {confirmation && (\n <div role=\"group\" aria-label=\"Confirm Site change\">\n <p>\n {confirmation === \"rollback\"\n ? \"Make the selected version current?\"\n : site.status === \"active\"\n ? \"Archive this Site for workspace users?\"\n : \"Restore this Site for workspace users?\"}\n </p>\n <button type=\"button\" onClick={() => void commit()}>\n Confirm change\n </button>\n <button type=\"button\" onClick={() => setConfirmation(null)}>\n Keep current state\n </button>\n </div>\n )}\n </fieldset>\n )}\n </>\n )}\n <button type=\"button\" disabled={busy} onClick={() => setReload((value) => value + 1)}>\n Refresh Site\n </button>\n </section>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.2-canary.0",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -21,6 +21,19 @@
|
|
|
21
21
|
"module": "./dist/index.js",
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"exports": {
|
|
24
|
+
"./sites": {
|
|
25
|
+
"types": "./dist/sites.d.ts",
|
|
26
|
+
"import": "./dist/sites.js"
|
|
27
|
+
},
|
|
28
|
+
"./connect.css": {
|
|
29
|
+
"types": "./styles/connect.d.ts",
|
|
30
|
+
"style": "./styles/connect.css",
|
|
31
|
+
"default": "./styles/connect.css"
|
|
32
|
+
},
|
|
33
|
+
"./connect": {
|
|
34
|
+
"types": "./dist/connect.d.ts",
|
|
35
|
+
"import": "./dist/connect.js"
|
|
36
|
+
},
|
|
24
37
|
".": {
|
|
25
38
|
"types": "./dist/index.d.ts",
|
|
26
39
|
"import": "./dist/index.js"
|
|
@@ -120,7 +133,8 @@
|
|
|
120
133
|
"@dnd-kit/utilities": "3.2.2",
|
|
121
134
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
122
135
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
123
|
-
"@opengeni/
|
|
136
|
+
"@opengeni/connect": "^0.2.0-canary.0",
|
|
137
|
+
"@opengeni/sdk": "^5.0.0-canary.0",
|
|
124
138
|
"clsx": "^2.1.1",
|
|
125
139
|
"dequal": "2.0.3",
|
|
126
140
|
"lucide-react": "^1.8.0",
|
|
@@ -157,6 +157,8 @@ export type MessageTimelineProps = {
|
|
|
157
157
|
* a Reconnect button without a handler to run it.
|
|
158
158
|
*/
|
|
159
159
|
onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
|
|
160
|
+
/** Host-owned inline connection setup. Return undefined to use the default recovery card. */
|
|
161
|
+
renderAuthNeeded?: ((item: AuthNeededItem) => ReactNode | undefined) | undefined;
|
|
160
162
|
/**
|
|
161
163
|
* Decide which durable authentication notices this timeline presents.
|
|
162
164
|
* Defaults to showing every notice. Embedded hosts can suppress notices for
|
|
@@ -407,6 +409,7 @@ export function MessageTimeline({
|
|
|
407
409
|
onOpenSession,
|
|
408
410
|
onMemoryClick,
|
|
409
411
|
onReconnect,
|
|
412
|
+
renderAuthNeeded,
|
|
410
413
|
shouldRenderAuthNeeded,
|
|
411
414
|
resolveProviderLogo,
|
|
412
415
|
toolRegistry = defaultToolRegistry,
|
|
@@ -910,6 +913,7 @@ export function MessageTimeline({
|
|
|
910
913
|
onOpenSession,
|
|
911
914
|
onMemoryClick,
|
|
912
915
|
onReconnect,
|
|
916
|
+
renderAuthNeeded,
|
|
913
917
|
resolveProviderLogo,
|
|
914
918
|
toolRegistry,
|
|
915
919
|
loadRetainedScreenshot,
|
|
@@ -925,6 +929,7 @@ export function MessageTimeline({
|
|
|
925
929
|
onMemoryClick,
|
|
926
930
|
onOpenSession,
|
|
927
931
|
onReconnect,
|
|
932
|
+
renderAuthNeeded,
|
|
928
933
|
renderMessageActions,
|
|
929
934
|
renderMessageText,
|
|
930
935
|
resolveProviderLogo,
|
|
@@ -2309,6 +2314,7 @@ type TimelineGroupBehaviorProps = {
|
|
|
2309
2314
|
onOpenSession: MessageTimelineProps["onOpenSession"];
|
|
2310
2315
|
onMemoryClick: MessageTimelineProps["onMemoryClick"];
|
|
2311
2316
|
onReconnect: MessageTimelineProps["onReconnect"];
|
|
2317
|
+
renderAuthNeeded: MessageTimelineProps["renderAuthNeeded"];
|
|
2312
2318
|
resolveProviderLogo: MessageTimelineProps["resolveProviderLogo"];
|
|
2313
2319
|
toolRegistry: ToolRegistry;
|
|
2314
2320
|
loadRetainedScreenshot: MessageTimelineProps["loadRetainedScreenshot"];
|
|
@@ -2373,6 +2379,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2373
2379
|
onOpenSession,
|
|
2374
2380
|
onMemoryClick,
|
|
2375
2381
|
onReconnect,
|
|
2382
|
+
renderAuthNeeded,
|
|
2376
2383
|
resolveProviderLogo,
|
|
2377
2384
|
toolRegistry,
|
|
2378
2385
|
loadRetainedScreenshot,
|
|
@@ -2393,6 +2400,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2393
2400
|
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
2394
2401
|
onMemoryClick?: ((memoryId: string) => void) | undefined;
|
|
2395
2402
|
onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
|
|
2403
|
+
/** Host-owned inline connection setup. Return undefined to use the default recovery card. */
|
|
2404
|
+
renderAuthNeeded?: ((item: AuthNeededItem) => ReactNode | undefined) | undefined;
|
|
2396
2405
|
resolveProviderLogo?: ((providerDomain: string) => string | null | undefined) | undefined;
|
|
2397
2406
|
toolRegistry: ToolRegistry;
|
|
2398
2407
|
loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
|
|
@@ -2550,6 +2559,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2550
2559
|
onOpenSession,
|
|
2551
2560
|
onMemoryClick,
|
|
2552
2561
|
onReconnect,
|
|
2562
|
+
renderAuthNeeded,
|
|
2553
2563
|
resolveProviderLogo,
|
|
2554
2564
|
toolRegistry,
|
|
2555
2565
|
loadRetainedScreenshot,
|
|
@@ -2565,6 +2575,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2565
2575
|
onOpenSession={onOpenSession}
|
|
2566
2576
|
onMemoryClick={onMemoryClick}
|
|
2567
2577
|
onReconnect={onReconnect}
|
|
2578
|
+
renderAuthNeeded={renderAuthNeeded}
|
|
2568
2579
|
resolveProviderLogo={resolveProviderLogo}
|
|
2569
2580
|
toolRegistry={toolRegistry}
|
|
2570
2581
|
loadRetainedScreenshot={loadRetainedScreenshot}
|
|
@@ -2610,6 +2621,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2610
2621
|
renderMessageActions={renderMessageActions}
|
|
2611
2622
|
renderMessageText={renderMessageText}
|
|
2612
2623
|
onReconnect={onReconnect}
|
|
2624
|
+
renderAuthNeeded={renderAuthNeeded}
|
|
2613
2625
|
resolveProviderLogo={resolveProviderLogo}
|
|
2614
2626
|
onOpenSession={onOpenSession}
|
|
2615
2627
|
loadVideoArtifactPlayback={loadVideoArtifactPlayback}
|
|
@@ -2862,6 +2874,7 @@ export function TimelineRow({
|
|
|
2862
2874
|
renderMessageActions,
|
|
2863
2875
|
renderMessageText,
|
|
2864
2876
|
onReconnect,
|
|
2877
|
+
renderAuthNeeded,
|
|
2865
2878
|
resolveProviderLogo,
|
|
2866
2879
|
onOpenSession,
|
|
2867
2880
|
loadVideoArtifactPlayback,
|
|
@@ -2872,6 +2885,8 @@ export function TimelineRow({
|
|
|
2872
2885
|
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
2873
2886
|
| undefined;
|
|
2874
2887
|
onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
|
|
2888
|
+
/** Host-owned inline connection setup. Return undefined to use the default recovery card. */
|
|
2889
|
+
renderAuthNeeded?: ((item: AuthNeededItem) => ReactNode | undefined) | undefined;
|
|
2875
2890
|
resolveProviderLogo?: ((providerDomain: string) => string | null | undefined) | undefined;
|
|
2876
2891
|
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
2877
2892
|
loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
|
|
@@ -2915,11 +2930,13 @@ export function TimelineRow({
|
|
|
2915
2930
|
return <CompactionRow item={item} />;
|
|
2916
2931
|
case "auth-needed":
|
|
2917
2932
|
return (
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2933
|
+
renderAuthNeeded?.(item) ?? (
|
|
2934
|
+
<AuthNeededRow
|
|
2935
|
+
item={item}
|
|
2936
|
+
onReconnect={onReconnect}
|
|
2937
|
+
resolveProviderLogo={resolveProviderLogo}
|
|
2938
|
+
/>
|
|
2939
|
+
)
|
|
2923
2940
|
);
|
|
2924
2941
|
default:
|
|
2925
2942
|
return null;
|
|
@@ -699,7 +699,8 @@ export function useSandboxWorkspaceTabs(
|
|
|
699
699
|
(captureState.fileCount ?? 0) > 0 ||
|
|
700
700
|
(captureState.capture?.repos.some((repo) => (repo.branchDiff?.length ?? 0) > 0) ?? false);
|
|
701
701
|
const captureUnavailable =
|
|
702
|
-
|
|
702
|
+
!captureState.loading &&
|
|
703
|
+
((captureState.fileCount === 0 && !captureHasChanges) || captureState.error !== null);
|
|
703
704
|
if (defaultTabRef.current.value === null) {
|
|
704
705
|
if (initialTab && (!isWorkbenchSurface(initialTab) || surfaceSet.has(initialTab))) {
|
|
705
706
|
defaultTabRef.current.value = initialTab;
|
|
@@ -711,7 +712,13 @@ export function useSandboxWorkspaceTabs(
|
|
|
711
712
|
);
|
|
712
713
|
} else if (captureUnavailable && initialWorkspaceTab(events) === WORKBENCH_TAB_CHANGES) {
|
|
713
714
|
defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);
|
|
714
|
-
} else if (
|
|
715
|
+
} else if (
|
|
716
|
+
!captureState.loading &&
|
|
717
|
+
(captureState.fileCount !== null || captureState.error !== null)
|
|
718
|
+
) {
|
|
719
|
+
// Metadata may arrive before a signed manifest. While that manifest is
|
|
720
|
+
// loading, neither a positive count nor zero (committed-only changes)
|
|
721
|
+
// proves an unavailable capture, so leave the default unresolved.
|
|
715
722
|
// No durable review surface exists. Files owns the explicit live-workspace
|
|
716
723
|
// gate; a user can open Changes deliberately if they need a fresh Git read.
|
|
717
724
|
defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
|