@opengeni/react 4.0.2 → 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/src/sites-ui.tsx
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import type {
|
|
3
|
+
OpenGeniClient,
|
|
4
|
+
WorkspaceArtifact,
|
|
5
|
+
WorkspaceArtifactContentResponse,
|
|
6
|
+
WorkspaceArtifactDetailResponse,
|
|
7
|
+
WorkspaceArtifactListResponse,
|
|
8
|
+
} from "@opengeni/sdk";
|
|
9
|
+
import {
|
|
10
|
+
PublishedHtmlArtifactFrame,
|
|
11
|
+
type PublishedHtmlArtifactToolBridge,
|
|
12
|
+
} from "./components/artifacts/published-html-artifact-frame";
|
|
13
|
+
|
|
14
|
+
/** Implement with an authenticated host proxy. Never put an organization key
|
|
15
|
+
* in browser props; replace the client object when the host actor changes. */
|
|
16
|
+
export type SiteClient = Pick<
|
|
17
|
+
OpenGeniClient,
|
|
18
|
+
| "listWorkspaceArtifacts"
|
|
19
|
+
| "getWorkspaceArtifact"
|
|
20
|
+
| "getWorkspaceArtifactHtml"
|
|
21
|
+
| "rollbackWorkspaceArtifact"
|
|
22
|
+
| "setWorkspaceArtifactStatus"
|
|
23
|
+
>;
|
|
24
|
+
type SiteDisplayContent = Pick<
|
|
25
|
+
WorkspaceArtifactContentResponse,
|
|
26
|
+
"artifactId" | "versionId" | "html" | "requestedTools"
|
|
27
|
+
>;
|
|
28
|
+
type SiteScope = { client: SiteClient; workspaceId: string; className?: string };
|
|
29
|
+
/** Shared native/embedded read boundary: pin content to the observed version,
|
|
30
|
+
* and never accept a response belonging to another workspace or Site. */
|
|
31
|
+
export async function loadSiteSnapshot(
|
|
32
|
+
client: Pick<SiteClient, "getWorkspaceArtifact" | "getWorkspaceArtifactHtml">,
|
|
33
|
+
workspaceId: string,
|
|
34
|
+
siteId: string,
|
|
35
|
+
options: { signal?: AbortSignal; includeArchivedContent?: boolean } = {},
|
|
36
|
+
): Promise<{
|
|
37
|
+
detail: WorkspaceArtifactDetailResponse;
|
|
38
|
+
content: SiteDisplayContent | null;
|
|
39
|
+
}> {
|
|
40
|
+
const requestOptions = options.signal ? { signal: options.signal } : {};
|
|
41
|
+
const detail = await client.getWorkspaceArtifact(workspaceId, siteId, requestOptions);
|
|
42
|
+
options.signal?.throwIfAborted();
|
|
43
|
+
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
44
|
+
throw new Error("Site scope mismatch");
|
|
45
|
+
const versionId = detail.artifact.currentVersion?.id;
|
|
46
|
+
const content =
|
|
47
|
+
versionId && (detail.artifact.status === "active" || options.includeArchivedContent)
|
|
48
|
+
? {
|
|
49
|
+
artifactId: siteId,
|
|
50
|
+
versionId,
|
|
51
|
+
requestedTools: detail.artifact.currentVersion!.requestedTools,
|
|
52
|
+
html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {
|
|
53
|
+
...requestOptions,
|
|
54
|
+
versionId,
|
|
55
|
+
}),
|
|
56
|
+
}
|
|
57
|
+
: null;
|
|
58
|
+
options.signal?.throwIfAborted();
|
|
59
|
+
if (content && (content.artifactId !== siteId || content.versionId !== versionId))
|
|
60
|
+
throw new Error("Site content mismatch");
|
|
61
|
+
return { detail: structuredClone(detail), content: content ? structuredClone(content) : null };
|
|
62
|
+
}
|
|
63
|
+
export type SiteListProps = SiteScope & {
|
|
64
|
+
status?: "active" | "archived";
|
|
65
|
+
onOpen: (site: WorkspaceArtifact) => void;
|
|
66
|
+
};
|
|
67
|
+
export type SiteDetailProps = SiteScope & {
|
|
68
|
+
siteId: string;
|
|
69
|
+
/** Presentation hint only: the backend still enforces artifacts:publish. */
|
|
70
|
+
canPublish?: boolean;
|
|
71
|
+
toolBridge?: PublishedHtmlArtifactToolBridge;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function useScopeKey(values: readonly unknown[]) {
|
|
75
|
+
const [scope, setScope] = useState(values);
|
|
76
|
+
const [generation, setGeneration] = useState(0);
|
|
77
|
+
if (values.some((value, index) => value !== scope[index])) {
|
|
78
|
+
setScope(values);
|
|
79
|
+
setGeneration(generation + 1);
|
|
80
|
+
}
|
|
81
|
+
return generation;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function SiteList(props: SiteListProps) {
|
|
85
|
+
const key = useScopeKey([props.client, props.workspaceId, props.status]);
|
|
86
|
+
return <ScopedSiteList key={key} {...props} />;
|
|
87
|
+
}
|
|
88
|
+
function ScopedSiteList({
|
|
89
|
+
client,
|
|
90
|
+
workspaceId,
|
|
91
|
+
status = "active",
|
|
92
|
+
onOpen,
|
|
93
|
+
className,
|
|
94
|
+
}: SiteListProps) {
|
|
95
|
+
const [page, setPage] = useState<WorkspaceArtifactListResponse | null>(null);
|
|
96
|
+
const [cursor, setCursor] = useState<string | undefined>();
|
|
97
|
+
const [error, setError] = useState(false);
|
|
98
|
+
const [reload, setReload] = useState(0);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const abort = new AbortController();
|
|
101
|
+
setPage(null);
|
|
102
|
+
setError(false);
|
|
103
|
+
void Promise.resolve()
|
|
104
|
+
.then(() =>
|
|
105
|
+
client.listWorkspaceArtifacts(workspaceId, {
|
|
106
|
+
status,
|
|
107
|
+
...(cursor ? { cursor } : {}),
|
|
108
|
+
limit: 50,
|
|
109
|
+
signal: abort.signal,
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
112
|
+
.then((result) => {
|
|
113
|
+
if (!abort.signal.aborted) setPage(structuredClone(result));
|
|
114
|
+
})
|
|
115
|
+
.catch(() => {
|
|
116
|
+
if (!abort.signal.aborted) setError(true);
|
|
117
|
+
});
|
|
118
|
+
return () => abort.abort();
|
|
119
|
+
}, [client, workspaceId, status, cursor, reload]);
|
|
120
|
+
return (
|
|
121
|
+
<section className={className} aria-label="Sites" aria-busy={!page && !error}>
|
|
122
|
+
{error ? (
|
|
123
|
+
<p role="alert">Sites are unavailable or access has changed. Refresh to check access.</p>
|
|
124
|
+
) : !page ? (
|
|
125
|
+
<p role="status">Loading Sites…</p>
|
|
126
|
+
) : page.artifacts.length === 0 ? (
|
|
127
|
+
<p role="status">No {status} Sites.</p>
|
|
128
|
+
) : (
|
|
129
|
+
<ul>
|
|
130
|
+
{page.artifacts.map((site) => (
|
|
131
|
+
<li key={site.id}>
|
|
132
|
+
<button type="button" onClick={() => onOpen(structuredClone(site))}>
|
|
133
|
+
{site.title}
|
|
134
|
+
</button>
|
|
135
|
+
<p>{site.description}</p>
|
|
136
|
+
<span>
|
|
137
|
+
{site.status} · revision {site.currentVersion?.revision ?? "unpublished"}
|
|
138
|
+
</span>
|
|
139
|
+
</li>
|
|
140
|
+
))}
|
|
141
|
+
</ul>
|
|
142
|
+
)}
|
|
143
|
+
{page?.nextCursor && (
|
|
144
|
+
<button type="button" onClick={() => setCursor(page.nextCursor!)}>
|
|
145
|
+
Next page
|
|
146
|
+
</button>
|
|
147
|
+
)}
|
|
148
|
+
{cursor && (
|
|
149
|
+
<button type="button" onClick={() => setCursor(undefined)}>
|
|
150
|
+
First page
|
|
151
|
+
</button>
|
|
152
|
+
)}
|
|
153
|
+
<button type="button" onClick={() => setReload((value) => value + 1)}>
|
|
154
|
+
Refresh Sites
|
|
155
|
+
</button>
|
|
156
|
+
</section>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function SiteDetail(props: SiteDetailProps) {
|
|
161
|
+
const key = useScopeKey([props.client, props.workspaceId, props.siteId]);
|
|
162
|
+
return <ScopedSiteDetail key={key} {...props} />;
|
|
163
|
+
}
|
|
164
|
+
function ScopedSiteDetail({
|
|
165
|
+
client,
|
|
166
|
+
workspaceId,
|
|
167
|
+
siteId,
|
|
168
|
+
canPublish = false,
|
|
169
|
+
toolBridge,
|
|
170
|
+
className,
|
|
171
|
+
}: SiteDetailProps) {
|
|
172
|
+
const [loaded, setLoaded] = useState<{
|
|
173
|
+
detail: WorkspaceArtifactDetailResponse;
|
|
174
|
+
content: SiteDisplayContent | null;
|
|
175
|
+
} | null>(null);
|
|
176
|
+
const [error, setError] = useState(false);
|
|
177
|
+
const [reload, setReload] = useState(0);
|
|
178
|
+
const [busy, setBusy] = useState(false);
|
|
179
|
+
const [rollbackId, setRollbackId] = useState("");
|
|
180
|
+
const [confirmation, setConfirmation] = useState<"status" | "rollback" | null>(null);
|
|
181
|
+
const mutation = useRef<AbortController | null>(null);
|
|
182
|
+
useEffect(() => () => mutation.current?.abort(), []);
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
const abort = new AbortController();
|
|
185
|
+
setLoaded(null);
|
|
186
|
+
setError(false);
|
|
187
|
+
setConfirmation(null);
|
|
188
|
+
setRollbackId("");
|
|
189
|
+
void Promise.resolve()
|
|
190
|
+
.then(async () => {
|
|
191
|
+
const snapshot = await loadSiteSnapshot(client, workspaceId, siteId, {
|
|
192
|
+
signal: abort.signal,
|
|
193
|
+
});
|
|
194
|
+
if (!abort.signal.aborted) setLoaded(snapshot);
|
|
195
|
+
})
|
|
196
|
+
.catch(() => {
|
|
197
|
+
if (!abort.signal.aborted) {
|
|
198
|
+
setLoaded(null);
|
|
199
|
+
setError(true);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return () => abort.abort();
|
|
203
|
+
}, [client, workspaceId, siteId, reload]);
|
|
204
|
+
// Recheck live read authority without rebuilding the iframe on every tick.
|
|
205
|
+
// No overlapping polls, and failures remove the document and tool bridge.
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (!loaded || busy) return;
|
|
208
|
+
const abort = new AbortController();
|
|
209
|
+
let checking = false;
|
|
210
|
+
const timer = setInterval(() => {
|
|
211
|
+
if (checking) return;
|
|
212
|
+
checking = true;
|
|
213
|
+
void Promise.resolve()
|
|
214
|
+
.then(() => client.getWorkspaceArtifact(workspaceId, siteId, { signal: abort.signal }))
|
|
215
|
+
.then((detail) => {
|
|
216
|
+
if (abort.signal.aborted) return;
|
|
217
|
+
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
218
|
+
throw new Error("Site scope mismatch");
|
|
219
|
+
if (
|
|
220
|
+
detail.artifact.currentVersion?.id !== loaded.detail.artifact.currentVersion?.id ||
|
|
221
|
+
detail.artifact.status !== loaded.detail.artifact.status
|
|
222
|
+
) {
|
|
223
|
+
setLoaded(null);
|
|
224
|
+
setReload((value) => value + 1);
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
.catch(() => {
|
|
228
|
+
if (!abort.signal.aborted) {
|
|
229
|
+
setLoaded(null);
|
|
230
|
+
setError(true);
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
.finally(() => {
|
|
234
|
+
checking = false;
|
|
235
|
+
});
|
|
236
|
+
}, 15_000);
|
|
237
|
+
return () => {
|
|
238
|
+
abort.abort();
|
|
239
|
+
clearInterval(timer);
|
|
240
|
+
};
|
|
241
|
+
}, [client, workspaceId, siteId, loaded, busy]);
|
|
242
|
+
const commit = async () => {
|
|
243
|
+
const current = loaded?.detail.artifact.currentVersion;
|
|
244
|
+
if (!canPublish || !loaded || !current || !confirmation || mutation.current) return;
|
|
245
|
+
const abort = new AbortController();
|
|
246
|
+
mutation.current = abort;
|
|
247
|
+
setBusy(true);
|
|
248
|
+
try {
|
|
249
|
+
const common = { expectedCurrentVersionId: current.id, idempotencyKey: crypto.randomUUID() };
|
|
250
|
+
if (confirmation === "rollback") {
|
|
251
|
+
if (!loaded.detail.versions.some((version) => version.id === rollbackId))
|
|
252
|
+
throw new Error("Unknown version");
|
|
253
|
+
await client.rollbackWorkspaceArtifact(
|
|
254
|
+
workspaceId,
|
|
255
|
+
siteId,
|
|
256
|
+
{ ...common, versionId: rollbackId, reason: "Restore explicitly selected Site version" },
|
|
257
|
+
{ signal: abort.signal },
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
await client.setWorkspaceArtifactStatus(
|
|
261
|
+
workspaceId,
|
|
262
|
+
siteId,
|
|
263
|
+
{
|
|
264
|
+
...common,
|
|
265
|
+
status: loaded.detail.artifact.status === "active" ? "archived" : "active",
|
|
266
|
+
reason: "Explicit Site status change",
|
|
267
|
+
},
|
|
268
|
+
{ signal: abort.signal },
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
if (!abort.signal.aborted) {
|
|
272
|
+
setLoaded(null);
|
|
273
|
+
setReload((value) => value + 1);
|
|
274
|
+
}
|
|
275
|
+
} catch {
|
|
276
|
+
if (!abort.signal.aborted) {
|
|
277
|
+
setLoaded(null);
|
|
278
|
+
setError(true);
|
|
279
|
+
}
|
|
280
|
+
} finally {
|
|
281
|
+
if (!abort.signal.aborted) {
|
|
282
|
+
mutation.current = null;
|
|
283
|
+
setBusy(false);
|
|
284
|
+
setConfirmation(null);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
const site = loaded?.detail.artifact;
|
|
289
|
+
return (
|
|
290
|
+
<section
|
|
291
|
+
className={className}
|
|
292
|
+
aria-label="Site details"
|
|
293
|
+
aria-busy={busy || (!loaded && !error)}
|
|
294
|
+
>
|
|
295
|
+
{error && (
|
|
296
|
+
<p role="alert">
|
|
297
|
+
Site state could not be confirmed or access has changed. Refresh before continuing.
|
|
298
|
+
</p>
|
|
299
|
+
)}
|
|
300
|
+
{!loaded && !error && <p role="status">Loading Site…</p>}
|
|
301
|
+
{site && (
|
|
302
|
+
<>
|
|
303
|
+
<h2>{site.title}</h2>
|
|
304
|
+
<p>{site.description}</p>
|
|
305
|
+
<p>
|
|
306
|
+
{site.status} · revision {site.currentVersion?.revision ?? "unpublished"}
|
|
307
|
+
</p>
|
|
308
|
+
{loaded.content && (
|
|
309
|
+
<PublishedHtmlArtifactFrame
|
|
310
|
+
html={loaded.content.html}
|
|
311
|
+
title={site.title}
|
|
312
|
+
{...(toolBridge ? { toolBridge } : {})}
|
|
313
|
+
/>
|
|
314
|
+
)}
|
|
315
|
+
<h3>Versions</h3>
|
|
316
|
+
<ul>
|
|
317
|
+
{loaded.detail.versions.map((version) => (
|
|
318
|
+
<li key={version.id}>
|
|
319
|
+
Revision {version.revision} · {version.createdAt}
|
|
320
|
+
{version.id === site.currentVersion?.id ? " · current" : ""}
|
|
321
|
+
</li>
|
|
322
|
+
))}
|
|
323
|
+
</ul>
|
|
324
|
+
{loaded.detail.versionsTruncated && (
|
|
325
|
+
<p>Older versions are not included in this response.</p>
|
|
326
|
+
)}
|
|
327
|
+
{canPublish && site.currentVersion && (
|
|
328
|
+
<fieldset disabled={busy}>
|
|
329
|
+
<legend>Manage Site</legend>
|
|
330
|
+
<button type="button" onClick={() => setConfirmation("status")}>
|
|
331
|
+
{site.status === "active" ? "Archive Site" : "Restore Site"}
|
|
332
|
+
</button>
|
|
333
|
+
<label>
|
|
334
|
+
Version to restore
|
|
335
|
+
<select
|
|
336
|
+
value={rollbackId}
|
|
337
|
+
onChange={(event) => {
|
|
338
|
+
setRollbackId(event.target.value);
|
|
339
|
+
setConfirmation(null);
|
|
340
|
+
}}
|
|
341
|
+
>
|
|
342
|
+
<option value="">Choose a version</option>
|
|
343
|
+
{loaded.detail.versions
|
|
344
|
+
.filter((version) => version.id !== site.currentVersion?.id)
|
|
345
|
+
.map((version) => (
|
|
346
|
+
<option key={version.id} value={version.id}>
|
|
347
|
+
Revision {version.revision}
|
|
348
|
+
</option>
|
|
349
|
+
))}
|
|
350
|
+
</select>
|
|
351
|
+
</label>
|
|
352
|
+
<button
|
|
353
|
+
type="button"
|
|
354
|
+
disabled={!rollbackId}
|
|
355
|
+
onClick={() => setConfirmation("rollback")}
|
|
356
|
+
>
|
|
357
|
+
Review rollback
|
|
358
|
+
</button>
|
|
359
|
+
{confirmation && (
|
|
360
|
+
<div role="group" aria-label="Confirm Site change">
|
|
361
|
+
<p>
|
|
362
|
+
{confirmation === "rollback"
|
|
363
|
+
? "Make the selected version current?"
|
|
364
|
+
: site.status === "active"
|
|
365
|
+
? "Archive this Site for workspace users?"
|
|
366
|
+
: "Restore this Site for workspace users?"}
|
|
367
|
+
</p>
|
|
368
|
+
<button type="button" onClick={() => void commit()}>
|
|
369
|
+
Confirm change
|
|
370
|
+
</button>
|
|
371
|
+
<button type="button" onClick={() => setConfirmation(null)}>
|
|
372
|
+
Keep current state
|
|
373
|
+
</button>
|
|
374
|
+
</div>
|
|
375
|
+
)}
|
|
376
|
+
</fieldset>
|
|
377
|
+
)}
|
|
378
|
+
</>
|
|
379
|
+
)}
|
|
380
|
+
<button type="button" disabled={busy} onClick={() => setReload((value) => value + 1)}>
|
|
381
|
+
Refresh Site
|
|
382
|
+
</button>
|
|
383
|
+
</section>
|
|
384
|
+
);
|
|
385
|
+
}
|
package/src/sites.ts
ADDED
|
@@ -2362,7 +2362,7 @@ function GenericToolIcon({ name }: { name: string }) {
|
|
|
2362
2362
|
? FileSearchIcon
|
|
2363
2363
|
: leaf === "tool_search"
|
|
2364
2364
|
? PackageSearchIcon
|
|
2365
|
-
: leaf
|
|
2365
|
+
: leaf.startsWith("skill_")
|
|
2366
2366
|
? PlugIcon
|
|
2367
2367
|
: WrenchIcon;
|
|
2368
2368
|
return <Icon className={ICON_SIZE} />;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* Opt-in, scoped styles. No document-level resets or Tailwind dependency. */
|
|
2
|
+
.og-connect {
|
|
3
|
+
font-family: var(--og-font-sans, system-ui, sans-serif);
|
|
4
|
+
color: inherit;
|
|
5
|
+
line-height: 1.55;
|
|
6
|
+
max-width: 48rem;
|
|
7
|
+
min-width: 0;
|
|
8
|
+
}
|
|
9
|
+
.og-connect :where(*, *::before, *::after) { box-sizing: border-box; }
|
|
10
|
+
.og-connect :where(select, input, label) { min-width: 0; }
|
|
11
|
+
.og-connect select { width: 100%; }
|
|
12
|
+
.og-connect :where(section, form, fieldset, ul, li, [role="group"]) {
|
|
13
|
+
display: grid;
|
|
14
|
+
gap: .75rem;
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
.og-connect section { padding-block: 1rem; }
|
|
18
|
+
.og-connect section + section { border-top: 1px solid color-mix(in srgb, currentColor 20%, transparent); }
|
|
19
|
+
.og-connect fieldset { border: 0; padding: 0; margin: 0; }
|
|
20
|
+
.og-connect legend { font-weight: 600; padding-block-end: .5rem; }
|
|
21
|
+
.og-connect label { display: grid; gap: .375rem; }
|
|
22
|
+
.og-connect label:has(input[type="checkbox"], input[type="radio"]) { display: flex; align-items: center; gap: .75rem; min-height: 2.75rem; }
|
|
23
|
+
.og-connect :where(button, select, input:not([type="checkbox"]):not([type="radio"])) {
|
|
24
|
+
font: inherit;
|
|
25
|
+
color: inherit;
|
|
26
|
+
background: transparent;
|
|
27
|
+
border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
|
|
28
|
+
border-radius: var(--og-radius-sm, 6px);
|
|
29
|
+
min-height: 2.75rem;
|
|
30
|
+
padding: .5rem .75rem;
|
|
31
|
+
max-width: 100%;
|
|
32
|
+
}
|
|
33
|
+
.og-connect button { cursor: pointer; width: fit-content; text-align: start; }
|
|
34
|
+
.og-identity-link { display: grid; gap: 1rem; }
|
|
35
|
+
.og-identity-link dl { margin: 0; display: grid; gap: .25rem; }
|
|
36
|
+
.og-identity-link dt { font-weight: 600; }
|
|
37
|
+
.og-identity-link dd { margin: 0 0 .75rem; overflow-wrap: anywhere; }
|
|
38
|
+
.og-identity-link label:has(input[type="checkbox"]) { min-height: 2.75rem; align-items: center; gap: .75rem; }
|
|
39
|
+
.og-connect button:hover:not(:disabled) { background: color-mix(in srgb, currentColor 8%, transparent); }
|
|
40
|
+
.og-connect :disabled { opacity: .55; cursor: not-allowed; }
|
|
41
|
+
.og-connect :focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
|
|
42
|
+
.og-connect :where(p, h2) { margin: 0; overflow-wrap: anywhere; }
|
|
43
|
+
.og-connect ul { list-style: none; padding: 0; margin: 0; }
|
|
44
|
+
.og-connect li + li { padding-block-start: .75rem; border-top: 1px solid color-mix(in srgb, currentColor 15%, transparent); }
|
|
45
|
+
.og-connect [role="alert"] { border-inline-start: 3px solid currentColor; padding-inline-start: .75rem; }
|
|
46
|
+
@media (forced-colors: active) {
|
|
47
|
+
.og-connect :where(button, select, input, section, li) { border-color: ButtonText; }
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|