@opengeni/react 5.0.4 → 5.0.5-canary.1

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.
Files changed (60) hide show
  1. package/README.md +3 -0
  2. package/dist/{chunk-SCZ6SUBW.js → chunk-H63SQTGI.js} +1258 -1837
  3. package/dist/chunk-H63SQTGI.js.map +1 -0
  4. package/dist/chunk-HQV2I5HV.js +124 -0
  5. package/dist/chunk-HQV2I5HV.js.map +1 -0
  6. package/dist/chunk-M2HNWZYR.js +835 -0
  7. package/dist/chunk-M2HNWZYR.js.map +1 -0
  8. package/dist/{chunk-Z7WBDB4E.js → chunk-V3WX3TQG.js} +283 -391
  9. package/dist/chunk-V3WX3TQG.js.map +1 -0
  10. package/dist/components/message-timeline.d.ts +3 -1
  11. package/dist/components/sandbox-workspace.d.ts +5 -0
  12. package/dist/composer.js +2 -1
  13. package/dist/connect-chooser.d.ts +1 -0
  14. package/dist/connect-panel.d.ts +2 -1
  15. package/dist/connect.d.ts +7 -0
  16. package/dist/connect.js +792 -183
  17. package/dist/connect.js.map +1 -1
  18. package/dist/connection-catalog.d.ts +33 -0
  19. package/dist/connection-installed.d.ts +15 -0
  20. package/dist/connection-logo.d.ts +6 -0
  21. package/dist/connection-type-picker.d.ts +6 -0
  22. package/dist/device-authorization.d.ts +9 -1
  23. package/dist/index.js +39 -12
  24. package/dist/index.js.map +1 -1
  25. package/dist/plugin-details.d.ts +14 -0
  26. package/dist/plugin-discovery.d.ts +14 -0
  27. package/dist/session-ui.d.ts +3 -0
  28. package/dist/session-ui.js +113 -2
  29. package/dist/session-ui.js.map +1 -1
  30. package/dist/skill-discovery.d.ts +29 -0
  31. package/dist/timeline/activity-rail.d.ts +3 -1
  32. package/dist/timeline/genie-loading.d.ts +24 -0
  33. package/dist/timeline/startup-preference.d.ts +3 -0
  34. package/dist/timeline/startup-timings.d.ts +4 -0
  35. package/package.json +4 -3
  36. package/src/components/message-timeline.tsx +127 -20
  37. package/src/components/sandbox-workspace.tsx +29 -0
  38. package/src/connect-chooser.tsx +81 -21
  39. package/src/connect-panel.tsx +7 -1
  40. package/src/connect.ts +21 -0
  41. package/src/connection-catalog.tsx +153 -0
  42. package/src/connection-installed.tsx +45 -0
  43. package/src/connection-logo.tsx +29 -0
  44. package/src/connection-type-picker.tsx +36 -0
  45. package/src/device-authorization.tsx +9 -1
  46. package/src/hooks/use-codex-accounts.ts +1 -0
  47. package/src/plugin-details.tsx +182 -0
  48. package/src/plugin-discovery.tsx +161 -0
  49. package/src/session-ui.ts +3 -0
  50. package/src/skill-discovery.tsx +152 -0
  51. package/src/timeline/activity-rail.tsx +75 -4
  52. package/src/timeline/genie-loading.tsx +112 -0
  53. package/src/timeline/startup-preference.ts +33 -0
  54. package/src/timeline/startup-timings.tsx +145 -0
  55. package/src/timeline/turn-summary.tsx +8 -3
  56. package/styles/compiled.css +73 -0
  57. package/styles/connect.css +149 -1
  58. package/styles/index.css +15 -0
  59. package/dist/chunk-SCZ6SUBW.js.map +0 -1
  60. package/dist/chunk-Z7WBDB4E.js.map +0 -1
package/dist/connect.js CHANGED
@@ -1,14 +1,125 @@
1
1
  import "./chunk-IWIDV7SB.js";
2
+ import {
3
+ Markdown
4
+ } from "./chunk-M2HNWZYR.js";
2
5
  import {
3
6
  copyTextToClipboard
4
7
  } from "./chunk-JVYQRRC4.js";
8
+ import "./chunk-HQV2I5HV.js";
9
+ import "./chunk-VZTQ73XT.js";
10
+ import "./chunk-22KP6LR5.js";
5
11
 
6
12
  // src/connect.ts
7
13
  import { useMemo, useSyncExternalStore } from "react";
8
14
 
9
- // src/connect-setup.tsx
10
- import { useState } from "react";
15
+ // src/skill-discovery.tsx
16
+ import { useEffect, useState } from "react";
11
17
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
+ function SkillDiscovery(props) {
19
+ const query = props.query.trim();
20
+ return /* @__PURE__ */ jsxs("section", { className: "og-skill-discovery", "aria-label": "Discover skills", children: [
21
+ /* @__PURE__ */ jsxs("header", { children: [
22
+ /* @__PURE__ */ jsx("h3", { children: "Discover skills" }),
23
+ /* @__PURE__ */ jsx(
24
+ "a",
25
+ {
26
+ href: "https://skills.sh/",
27
+ target: "_blank",
28
+ rel: "noopener noreferrer",
29
+ className: "og-skill-discovery-browse",
30
+ children: "Browse skills.sh \u2197"
31
+ }
32
+ )
33
+ ] }),
34
+ !query && props.onSearch ? /* @__PURE__ */ jsx("button", { type: "button", className: "og-skill-discovery-search", onClick: props.onSearch, children: "Search skills" }) : null,
35
+ /* @__PURE__ */ jsx(DiscoveryResults, { ...props, query }, `${props.workspaceId}:${query}`)
36
+ ] });
37
+ }
38
+ var EMPTY_INSTALLED_SKILLS = [];
39
+ function DiscoveryResults({
40
+ client,
41
+ workspaceId,
42
+ query,
43
+ canManage,
44
+ onImport,
45
+ installedSkills = EMPTY_INSTALLED_SKILLS
46
+ }) {
47
+ const [result, setResult] = useState(null);
48
+ const [loading, setLoading] = useState(query.length >= 2);
49
+ const [error, setError] = useState(false);
50
+ const [retry, setRetry] = useState(0);
51
+ useEffect(() => {
52
+ let active = true;
53
+ setError(false);
54
+ if (query.length < 2) return;
55
+ setLoading(true);
56
+ let timer;
57
+ const search = async (attempt) => {
58
+ try {
59
+ const data = await client.searchPublicSkills(workspaceId, query);
60
+ if (!active) return;
61
+ setResult(data);
62
+ setLoading(false);
63
+ } catch (cause) {
64
+ if (!active) return;
65
+ const status = typeof cause === "object" && cause !== null && "status" in cause ? cause.status : void 0;
66
+ const transient = cause instanceof TypeError || typeof status === "number" && status >= 500 && status <= 599;
67
+ if (attempt === 0 && transient) {
68
+ timer = setTimeout(() => void search(1), 750);
69
+ return;
70
+ }
71
+ setError(true);
72
+ setLoading(false);
73
+ }
74
+ };
75
+ timer = setTimeout(() => void search(0), 300);
76
+ return () => {
77
+ active = false;
78
+ clearTimeout(timer);
79
+ };
80
+ }, [client, workspaceId, query, retry]);
81
+ return /* @__PURE__ */ jsxs("div", { "aria-busy": loading, children: [
82
+ /* @__PURE__ */ jsx("div", { className: "og-skill-discovery-grid", children: result?.items.map((skill) => {
83
+ const installed = installedSkills.some(
84
+ (entry) => entry.sourceUrl.replace(/\/$/, "").toLowerCase() === skill.url.toLowerCase() || entry.repositoryUrl.replace(/\/$/, "").toLowerCase() === `https://github.com/${skill.source}`.toLowerCase() && entry.name.toLowerCase() === skill.name.toLowerCase()
85
+ );
86
+ return /* @__PURE__ */ jsxs(
87
+ "button",
88
+ {
89
+ type: "button",
90
+ className: "og-skill-discovery-item",
91
+ disabled: !canManage,
92
+ onClick: () => onImport(skill.url),
93
+ children: [
94
+ /* @__PURE__ */ jsxs("span", { className: "og-skill-discovery-copy", children: [
95
+ /* @__PURE__ */ jsx("strong", { children: skill.name }),
96
+ /* @__PURE__ */ jsx("span", { children: skill.source }),
97
+ /* @__PURE__ */ jsxs("small", { children: [
98
+ skill.installs.toLocaleString(),
99
+ " installs"
100
+ ] })
101
+ ] }),
102
+ /* @__PURE__ */ jsx("span", { className: "og-skill-discovery-action", children: installed ? /* @__PURE__ */ jsxs(Fragment, { children: [
103
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2713 " }),
104
+ "Installed"
105
+ ] }) : !canManage ? "Admin required" : "Preview \u2192" })
106
+ ]
107
+ },
108
+ skill.id
109
+ );
110
+ }) }),
111
+ loading ? /* @__PURE__ */ jsx("p", { role: "status", children: "Loading skills\u2026" }) : null,
112
+ error ? /* @__PURE__ */ jsxs("div", { role: "alert", className: "og-skill-discovery-error", children: [
113
+ "Could not load skills.",
114
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setRetry((value) => value + 1), children: "Retry" })
115
+ ] }) : null,
116
+ result && !result.items.length && !loading && !error ? /* @__PURE__ */ jsx("p", { role: "status", children: "No skills found. Try a different search." }) : null
117
+ ] });
118
+ }
119
+
120
+ // src/connect-setup.tsx
121
+ import { useState as useState2 } from "react";
122
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
12
123
  var setupStatus = {
13
124
  ready: "Ready to connect",
14
125
  requires_user_action: "Authorize your account",
@@ -35,17 +146,17 @@ function deviceVerificationUrl(action) {
35
146
  }
36
147
  }
37
148
  function ConnectSetup(props) {
38
- const [scope, setScope] = useState(props.controller);
39
- const [generation, setGeneration] = useState(0);
149
+ const [scope, setScope] = useState2(props.controller);
150
+ const [generation, setGeneration] = useState2(0);
40
151
  if (scope !== props.controller) {
41
152
  setScope(props.controller);
42
153
  setGeneration(generation + 1);
43
154
  }
44
- return /* @__PURE__ */ jsx(ScopedSetup, { ...props }, generation);
155
+ return /* @__PURE__ */ jsx2(ScopedSetup, { ...props }, generation);
45
156
  }
46
157
  function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }) {
47
158
  const view = useConnect(controller);
48
- const [localError, setLocalError] = useState(false);
159
+ const [localError, setLocalError] = useState2(false);
49
160
  const invoke = (operation) => {
50
161
  setLocalError(false);
51
162
  try {
@@ -56,7 +167,7 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
56
167
  };
57
168
  const attempt = view.attempt;
58
169
  if (!attempt)
59
- return /* @__PURE__ */ jsx("section", { className, "aria-label": "Connection setup", children: /* @__PURE__ */ jsx("p", { role: "status", children: "Choose a connection to begin." }) });
170
+ return /* @__PURE__ */ jsx2("section", { className, "aria-label": "Connection setup", children: /* @__PURE__ */ jsx2("p", { role: "status", children: "Choose a connection to begin." }) });
60
171
  const action = attempt.nextAction;
61
172
  const verificationUrl = deviceVerificationUrl(action);
62
173
  const terminal = ["complete", "cancelled", "expired"].includes(attempt.state);
@@ -95,20 +206,20 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
95
206
  );
96
207
  }
97
208
  };
98
- return /* @__PURE__ */ jsxs("section", { className, "aria-label": "Connection setup", "aria-busy": view.busy, children: [
99
- /* @__PURE__ */ jsxs("p", { children: [
209
+ return /* @__PURE__ */ jsxs2("section", { className, "aria-label": "Connection setup", "aria-busy": view.busy, children: [
210
+ /* @__PURE__ */ jsxs2("p", { children: [
100
211
  "Ownership: ",
101
212
  attempt.ownership === "personal" ? "Personal" : "Workspace"
102
213
  ] }),
103
- /* @__PURE__ */ jsx("p", { role: "status", children: setupStatus[attempt.state] }),
104
- attempt.account && /* @__PURE__ */ jsxs("p", { children: [
214
+ /* @__PURE__ */ jsx2("p", { role: "status", children: setupStatus[attempt.state] }),
215
+ attempt.account && /* @__PURE__ */ jsxs2("p", { children: [
105
216
  "Account: ",
106
217
  attempt.account.label
107
218
  ] }),
108
- (localError || view.error || attempt.error) && /* @__PURE__ */ jsx("p", { role: "alert", children: attempt.error?.code === "source_changed" ? "The integration source changed. Review the new operations before installing." : "Connection setup could not continue. Refresh its status before trying again." }),
109
- !terminal && /* @__PURE__ */ jsx("form", { onSubmit: submit, autoComplete: "off", children: /* @__PURE__ */ jsxs("fieldset", { disabled: view.busy, children: [
110
- /* @__PURE__ */ jsx("legend", { children: "Connection details" }),
111
- attempt.state === "connected_but_incomplete" && action.type === "none" && /* @__PURE__ */ jsx(
219
+ (localError || view.error || attempt.error) && /* @__PURE__ */ jsx2("p", { role: "alert", children: attempt.error?.code === "source_changed" ? "The integration source changed. Review the new operations before installing." : "Connection setup could not continue. Refresh its status before trying again." }),
220
+ !terminal && /* @__PURE__ */ jsx2("form", { onSubmit: submit, autoComplete: "off", children: /* @__PURE__ */ jsxs2("fieldset", { disabled: view.busy, children: [
221
+ /* @__PURE__ */ jsx2("legend", { children: "Connection details" }),
222
+ attempt.state === "connected_but_incomplete" && action.type === "none" && /* @__PURE__ */ jsx2(
112
223
  "button",
113
224
  {
114
225
  type: "button",
@@ -116,12 +227,12 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
116
227
  children: "Review integration operations"
117
228
  }
118
229
  ),
119
- action.type === "credentials" && action.fields.map((field, i) => /* @__PURE__ */ jsxs("label", { children: [
230
+ action.type === "credentials" && action.fields.map((field, i) => /* @__PURE__ */ jsxs2("label", { children: [
120
231
  field.label,
121
- field.options ? /* @__PURE__ */ jsxs("select", { name: `field-${i}`, required: field.required, defaultValue: "", children: [
122
- /* @__PURE__ */ jsx("option", { value: "", children: field.required ? "Choose an option" : "No account (public service)" }),
123
- field.options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
124
- ] }) : /* @__PURE__ */ jsx(
232
+ field.options ? /* @__PURE__ */ jsxs2("select", { name: `field-${i}`, required: field.required, defaultValue: "", children: [
233
+ /* @__PURE__ */ jsx2("option", { value: "", children: field.required ? "Choose an option" : "No account (public service)" }),
234
+ field.options.map((option) => /* @__PURE__ */ jsx2("option", { value: option.value, children: option.label }, option.value))
235
+ ] }) : /* @__PURE__ */ jsx2(
125
236
  "input",
126
237
  {
127
238
  name: `field-${i}`,
@@ -131,11 +242,11 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
131
242
  }
132
243
  )
133
244
  ] }, field.name)),
134
- action.type === "select_account" && /* @__PURE__ */ jsxs("label", { children: [
245
+ action.type === "select_account" && /* @__PURE__ */ jsxs2("label", { children: [
135
246
  "Account",
136
- /* @__PURE__ */ jsxs("select", { name: "account", required: true, defaultValue: "", children: [
137
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Choose an account" }),
138
- action.accounts.map((account) => /* @__PURE__ */ jsxs(
247
+ /* @__PURE__ */ jsxs2("select", { name: "account", required: true, defaultValue: "", children: [
248
+ /* @__PURE__ */ jsx2("option", { value: "", disabled: true, children: "Choose an account" }),
249
+ action.accounts.map((account) => /* @__PURE__ */ jsxs2(
139
250
  "option",
140
251
  {
141
252
  value: account.id,
@@ -153,16 +264,16 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
153
264
  ))
154
265
  ] })
155
266
  ] }),
156
- action.type === "select_resources" && action.resources.map((resource) => /* @__PURE__ */ jsxs("label", { children: [
157
- /* @__PURE__ */ jsx("input", { type: "checkbox", name: "resource", value: resource.id }),
267
+ action.type === "select_resources" && action.resources.map((resource) => /* @__PURE__ */ jsxs2("label", { children: [
268
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", name: "resource", value: resource.id }),
158
269
  resource.label,
159
270
  " (",
160
271
  resource.kind,
161
272
  ")"
162
273
  ] }, resource.id)),
163
- action.type === "select_resources" && action.cursor && /* @__PURE__ */ jsxs(Fragment, { children: [
164
- /* @__PURE__ */ jsx("p", { children: "More resources are available. Review all pages before submitting a selection." }),
165
- onBrowseResources && /* @__PURE__ */ jsx(
274
+ action.type === "select_resources" && action.cursor && /* @__PURE__ */ jsxs2(Fragment2, { children: [
275
+ /* @__PURE__ */ jsx2("p", { children: "More resources are available. Review all pages before submitting a selection." }),
276
+ onBrowseResources && /* @__PURE__ */ jsx2(
166
277
  "button",
167
278
  {
168
279
  type: "button",
@@ -171,10 +282,10 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
171
282
  }
172
283
  )
173
284
  ] }),
174
- action.type === "preview" && /* @__PURE__ */ jsxs(Fragment, { children: [
175
- /* @__PURE__ */ jsx("p", { children: "Select the operations to install." }),
176
- action.operations.map((operation) => /* @__PURE__ */ jsxs("label", { children: [
177
- /* @__PURE__ */ jsx("input", { type: "checkbox", name: "operation", value: operation.id }),
285
+ action.type === "preview" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
286
+ /* @__PURE__ */ jsx2("p", { children: "Select the operations to install." }),
287
+ action.operations.map((operation) => /* @__PURE__ */ jsxs2("label", { children: [
288
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", name: "operation", value: operation.id }),
178
289
  operation.label,
179
290
  " (",
180
291
  operation.kind,
@@ -183,7 +294,7 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
183
294
  ] }),
184
295
  ["credentials", "select_account", "select_resources", "preview"].includes(
185
296
  action.type
186
- ) && /* @__PURE__ */ jsx(
297
+ ) && /* @__PURE__ */ jsx2(
187
298
  "button",
188
299
  {
189
300
  type: "submit",
@@ -191,7 +302,7 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
191
302
  children: action.type === "preview" ? "Install selected operations" : "Continue"
192
303
  }
193
304
  ),
194
- action.type === "authorize" && /* @__PURE__ */ jsx(
305
+ action.type === "authorize" && /* @__PURE__ */ jsx2(
195
306
  "button",
196
307
  {
197
308
  type: "button",
@@ -199,40 +310,128 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className })
199
310
  children: "Authorize connection"
200
311
  }
201
312
  ),
202
- action.type === "wait" && /* @__PURE__ */ jsxs(Fragment, { children: [
203
- action.userCode && /* @__PURE__ */ jsxs("p", { children: [
313
+ action.type === "wait" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
314
+ action.userCode && /* @__PURE__ */ jsxs2("p", { children: [
204
315
  "Verification code: ",
205
- /* @__PURE__ */ jsx("code", { children: action.userCode })
316
+ /* @__PURE__ */ jsx2("code", { children: action.userCode })
206
317
  ] }),
207
- /* @__PURE__ */ jsx("p", { children: "Complete provider authorization, then check the connection status." }),
208
- verificationUrl && /* @__PURE__ */ jsx("a", { href: verificationUrl, target: "_blank", rel: "noopener noreferrer", children: "Open provider verification page (new tab)" })
318
+ /* @__PURE__ */ jsx2("p", { children: "Complete provider authorization, then check the connection status." }),
319
+ verificationUrl && /* @__PURE__ */ jsx2("a", { href: verificationUrl, target: "_blank", rel: "noopener noreferrer", children: "Open provider verification page (new tab)" })
209
320
  ] }),
210
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => invoke(() => view.refresh()), children: "Check status" }),
211
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => invoke(() => view.cancel(crypto.randomUUID())), children: "Cancel setup" })
321
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => invoke(() => view.refresh()), children: "Check status" }),
322
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => invoke(() => view.cancel(crypto.randomUUID())), children: "Cancel setup" })
212
323
  ] }) }, `${attempt.id}:${attempt.revision}`)
213
324
  ] });
214
325
  }
215
326
 
327
+ // src/connection-catalog.tsx
328
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
329
+ function ConnectionDetailsAction({
330
+ option,
331
+ name = option.name
332
+ }) {
333
+ return /* @__PURE__ */ jsx3(
334
+ "button",
335
+ {
336
+ type: "button",
337
+ className: "og-connection-details-action",
338
+ "aria-label": `View ${name} details`,
339
+ onClick: option.onOpen,
340
+ children: /* @__PURE__ */ jsx3("span", { "aria-hidden": true, children: option.connected ? "\u203A" : "+" })
341
+ }
342
+ );
343
+ }
344
+ function ConnectionOptionRow({ option }) {
345
+ return /* @__PURE__ */ jsxs3("div", { className: "og-connection-catalog-action-row og-connection-option", children: [
346
+ /* @__PURE__ */ jsxs3("button", { type: "button", className: "og-connection-catalog-row", onClick: option.onOpen, children: [
347
+ /* @__PURE__ */ jsxs3("span", { className: "og-connection-catalog-copy", children: [
348
+ /* @__PURE__ */ jsx3("strong", { children: option.name }),
349
+ /* @__PURE__ */ jsx3("span", { children: option.description })
350
+ ] }),
351
+ /* @__PURE__ */ jsx3("span", { className: "og-connection-catalog-status", children: option.status })
352
+ ] }),
353
+ /* @__PURE__ */ jsx3("span", { className: "og-connection-option-action", children: option.action ?? /* @__PURE__ */ jsx3(ConnectionDetailsAction, { option }) })
354
+ ] });
355
+ }
356
+ function ConnectionServiceRow({ service }) {
357
+ if (!service.options.length) return null;
358
+ const connected = service.options.filter((option) => option.connected).length;
359
+ const identity = /* @__PURE__ */ jsxs3(Fragment3, { children: [
360
+ /* @__PURE__ */ jsx3("span", { className: "og-connection-catalog-logo", children: service.logo }),
361
+ /* @__PURE__ */ jsxs3("span", { className: "og-connection-catalog-copy", children: [
362
+ /* @__PURE__ */ jsx3("strong", { children: service.name }),
363
+ /* @__PURE__ */ jsx3("span", { children: service.options.length === 1 ? service.options[0]?.description : service.options.map((option) => option.name).join(" \xB7 ") })
364
+ ] }),
365
+ /* @__PURE__ */ jsx3("span", { className: "og-connection-catalog-status", children: service.options.length === 1 ? service.options[0]?.status : `${connected} of ${service.options.length} connected` })
366
+ ] });
367
+ return service.options.length === 1 ? /* @__PURE__ */ jsxs3("div", { className: "og-connection-catalog-action-row", children: [
368
+ /* @__PURE__ */ jsx3(
369
+ "button",
370
+ {
371
+ type: "button",
372
+ className: "og-connection-catalog-row",
373
+ onClick: service.options[0].onOpen,
374
+ children: identity
375
+ }
376
+ ),
377
+ service.options[0]?.action ?? /* @__PURE__ */ jsx3(ConnectionDetailsAction, { option: service.options[0], name: service.name })
378
+ ] }) : /* @__PURE__ */ jsxs3("details", { className: "og-connection-catalog-service", children: [
379
+ /* @__PURE__ */ jsxs3("summary", { className: "og-connection-catalog-row", children: [
380
+ identity,
381
+ /* @__PURE__ */ jsx3("span", { "aria-hidden": true, className: "og-connection-group-chevron", children: "\u2304" })
382
+ ] }),
383
+ /* @__PURE__ */ jsx3("div", { className: "og-connection-catalog-options", children: service.options.map((option) => /* @__PURE__ */ jsx3(ConnectionOptionRow, { option }, option.id)) })
384
+ ] });
385
+ }
386
+ function ConnectionCatalog({
387
+ services,
388
+ query = "",
389
+ columns = 1,
390
+ grouped = true,
391
+ className = "",
392
+ emptyMessage = "No connections match your search."
393
+ }) {
394
+ const search = query.trim().toLowerCase();
395
+ const entries = grouped ? services : services.flatMap(
396
+ (service) => service.options.map((option) => ({
397
+ ...service,
398
+ id: `${service.id}/${option.id}`,
399
+ name: service.options.length > 1 && option.name !== service.name ? `${service.name} \xB7 ${option.name}` : service.name,
400
+ options: [option]
401
+ }))
402
+ );
403
+ const visible = entries.filter(
404
+ (service) => [service.name, ...service.options.flatMap((option) => [option.name, option.description ?? ""])].join(" ").toLowerCase().includes(search)
405
+ );
406
+ return /* @__PURE__ */ jsx3("div", { className: `og-connection-catalog ${className}`, "data-columns": columns, children: visible.length ? visible.map((service) => /* @__PURE__ */ jsx3(ConnectionServiceRow, { service }, service.id)) : /* @__PURE__ */ jsx3("p", { role: "status", children: emptyMessage }) });
407
+ }
408
+
216
409
  // src/connect-chooser.tsx
217
- import { useEffect, useState as useState2 } from "react";
218
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
410
+ import { useEffect as useEffect2, useState as useState3 } from "react";
411
+ import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
219
412
  function ConnectChooser(props) {
220
- const [scope, setScope] = useState2(props.controller);
221
- const [generation, setGeneration] = useState2(0);
413
+ const [scope, setScope] = useState3(props.controller);
414
+ const [generation, setGeneration] = useState3(0);
222
415
  if (scope !== props.controller) {
223
416
  setScope(props.controller);
224
417
  setGeneration(generation + 1);
225
418
  }
226
- return /* @__PURE__ */ jsx2(ScopedChooser, { ...props }, generation);
419
+ return /* @__PURE__ */ jsx4(ScopedChooser, { ...props }, generation);
227
420
  }
228
- function ScopedChooser({ controller, returnUrl, className }) {
421
+ function ScopedChooser({
422
+ controller,
423
+ returnUrl,
424
+ className,
425
+ presentation = "select"
426
+ }) {
229
427
  const view = useConnect(controller);
230
- const [catalog, setCatalog] = useState2(null);
231
- const [failed, setFailed] = useState2(false);
232
- const [load, setLoad] = useState2(0);
233
- const [providerId, setProviderId] = useState2("");
234
- const [ownership, setOwnership] = useState2("");
235
- useEffect(() => {
428
+ const [catalog, setCatalog] = useState3(null);
429
+ const [failed, setFailed] = useState3(false);
430
+ const [load, setLoad] = useState3(0);
431
+ const [query, setQuery] = useState3("");
432
+ const [providerId, setProviderId] = useState3("");
433
+ const [ownership, setOwnership] = useState3("");
434
+ useEffect2(() => {
236
435
  const abort = new AbortController();
237
436
  setCatalog(null);
238
437
  setFailed(false);
@@ -260,21 +459,70 @@ function ScopedChooser({ controller, returnUrl, className }) {
260
459
  setFailed(true);
261
460
  }
262
461
  };
263
- return /* @__PURE__ */ jsxs2(
462
+ return /* @__PURE__ */ jsxs4(
264
463
  "section",
265
464
  {
266
465
  className,
267
466
  "aria-label": "Choose a connection",
268
467
  "aria-busy": view.busy || !catalog && !failed,
269
468
  children: [
270
- failed && /* @__PURE__ */ jsx2("p", { role: "alert", children: "Connections could not be loaded or started. Check setup status before retrying." }),
271
- !catalog && !failed && /* @__PURE__ */ jsx2("p", { role: "status", children: "Loading connections\u2026" }),
272
- catalog?.length === 0 && /* @__PURE__ */ jsx2("p", { role: "status", children: "No connections are available." }),
273
- Boolean(catalog?.length) && /* @__PURE__ */ jsx2("form", { onSubmit: submit, children: /* @__PURE__ */ jsxs2("fieldset", { disabled: view.busy, children: [
274
- /* @__PURE__ */ jsx2("legend", { children: "Provider and ownership" }),
275
- /* @__PURE__ */ jsxs2("label", { children: [
469
+ failed && /* @__PURE__ */ jsx4("p", { role: "alert", children: "Connections could not be loaded or started. Check setup status before retrying." }),
470
+ !catalog && !failed && /* @__PURE__ */ jsx4("p", { role: "status", children: "Loading connections\u2026" }),
471
+ catalog?.length === 0 && /* @__PURE__ */ jsx4("p", { role: "status", children: "No connections are available." }),
472
+ Boolean(catalog?.length) && /* @__PURE__ */ jsx4("form", { onSubmit: submit, children: /* @__PURE__ */ jsxs4("fieldset", { disabled: view.busy, children: [
473
+ /* @__PURE__ */ jsx4("legend", { children: "Provider and ownership" }),
474
+ presentation === "catalog" && !provider ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
475
+ /* @__PURE__ */ jsxs4("label", { children: [
476
+ "Search connections",
477
+ /* @__PURE__ */ jsx4(
478
+ "input",
479
+ {
480
+ type: "search",
481
+ value: query,
482
+ onChange: (event) => setQuery(event.target.value)
483
+ }
484
+ )
485
+ ] }),
486
+ /* @__PURE__ */ jsx4(
487
+ ConnectionCatalog,
488
+ {
489
+ query,
490
+ services: catalog.map((entry) => ({
491
+ id: entry.id,
492
+ name: entry.label,
493
+ options: [
494
+ {
495
+ id: entry.id,
496
+ name: entry.label,
497
+ description: entry.reason,
498
+ status: entry.readiness.replaceAll("_", " "),
499
+ connected: false,
500
+ onOpen: () => {
501
+ if (!view.busy) {
502
+ setProviderId(entry.id);
503
+ setOwnership(entry.ownership.length === 1 ? entry.ownership[0] : "");
504
+ }
505
+ }
506
+ }
507
+ ]
508
+ }))
509
+ }
510
+ )
511
+ ] }) : null,
512
+ presentation === "catalog" && provider ? /* @__PURE__ */ jsx4(
513
+ "button",
514
+ {
515
+ type: "button",
516
+ onClick: () => {
517
+ setProviderId("");
518
+ setOwnership("");
519
+ },
520
+ children: "Back to connections"
521
+ }
522
+ ) : null,
523
+ /* @__PURE__ */ jsxs4("label", { hidden: presentation === "catalog", children: [
276
524
  "Provider",
277
- /* @__PURE__ */ jsxs2(
525
+ /* @__PURE__ */ jsxs4(
278
526
  "select",
279
527
  {
280
528
  required: true,
@@ -284,8 +532,8 @@ function ScopedChooser({ controller, returnUrl, className }) {
284
532
  setOwnership("");
285
533
  },
286
534
  children: [
287
- /* @__PURE__ */ jsx2("option", { value: "", disabled: true, children: "Choose a provider" }),
288
- catalog.map((entry) => /* @__PURE__ */ jsxs2(
535
+ /* @__PURE__ */ jsx4("option", { value: "", disabled: true, children: "Choose a provider" }),
536
+ catalog.map((entry) => /* @__PURE__ */ jsxs4(
289
537
  "option",
290
538
  {
291
539
  value: entry.id,
@@ -305,51 +553,62 @@ function ScopedChooser({ controller, returnUrl, className }) {
305
553
  }
306
554
  )
307
555
  ] }),
308
- provider && /* @__PURE__ */ jsxs2("label", { children: [
309
- "Ownership",
310
- /* @__PURE__ */ jsxs2(
311
- "select",
312
- {
313
- required: true,
314
- value: ownership,
315
- onChange: (event) => setOwnership(event.target.value),
316
- children: [
317
- /* @__PURE__ */ jsx2("option", { value: "", disabled: true, children: "Choose ownership" }),
318
- provider.ownership.map((choice) => /* @__PURE__ */ jsx2("option", { value: choice, children: choice === "personal" ? "Personal \u2014 owned by you" : "Workspace \u2014 shared connection" }, choice))
319
- ]
320
- }
321
- )
556
+ provider && /* @__PURE__ */ jsxs4(Fragment4, { children: [
557
+ /* @__PURE__ */ jsx4("strong", { children: presentation === "catalog" ? provider.label : null }),
558
+ /* @__PURE__ */ jsxs4("label", { children: [
559
+ "Ownership",
560
+ /* @__PURE__ */ jsxs4(
561
+ "select",
562
+ {
563
+ required: true,
564
+ value: ownership,
565
+ onChange: (event) => setOwnership(event.target.value),
566
+ children: [
567
+ /* @__PURE__ */ jsx4("option", { value: "", disabled: true, children: "Choose ownership" }),
568
+ provider.ownership.map((choice) => /* @__PURE__ */ jsx4("option", { value: choice, children: choice === "personal" ? "Personal \u2014 owned by you" : "Workspace \u2014 shared connection" }, choice))
569
+ ]
570
+ }
571
+ )
572
+ ] })
322
573
  ] }),
323
- /* @__PURE__ */ jsx2("button", { type: "submit", disabled: !canBegin, children: "Start setup" })
574
+ /* @__PURE__ */ jsx4(
575
+ "button",
576
+ {
577
+ hidden: presentation === "catalog" && !provider,
578
+ type: "submit",
579
+ disabled: !canBegin,
580
+ children: "Start setup"
581
+ }
582
+ )
324
583
  ] }) }),
325
- /* @__PURE__ */ jsx2("button", { type: "button", disabled: view.busy, onClick: () => setLoad(load + 1), children: "Reload providers" })
584
+ /* @__PURE__ */ jsx4("button", { type: "button", disabled: view.busy, onClick: () => setLoad(load + 1), children: "Reload providers" })
326
585
  ]
327
586
  }
328
587
  );
329
588
  }
330
589
 
331
590
  // src/connect-accounts.tsx
332
- import { useEffect as useEffect2, useRef, useState as useState3 } from "react";
333
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
591
+ import { useEffect as useEffect3, useRef, useState as useState4 } from "react";
592
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
334
593
  function ConnectAccounts(props) {
335
- const [scope, setScope] = useState3(props.controller);
336
- const [generation, setGeneration] = useState3(0);
594
+ const [scope, setScope] = useState4(props.controller);
595
+ const [generation, setGeneration] = useState4(0);
337
596
  if (scope !== props.controller) {
338
597
  setScope(props.controller);
339
598
  setGeneration(generation + 1);
340
599
  }
341
- return /* @__PURE__ */ jsx3(ScopedAccounts, { ...props }, generation);
600
+ return /* @__PURE__ */ jsx5(ScopedAccounts, { ...props }, generation);
342
601
  }
343
602
  function ScopedAccounts({ controller, className, returnUrl }) {
344
603
  const view = useConnect(controller);
345
- const [accounts, setAccounts] = useState3(null);
346
- const [selected, setSelected] = useState3(null);
347
- const [busy, setBusy] = useState3(false);
348
- const [error, setError] = useState3(false);
349
- const [reload, setReload] = useState3(0);
604
+ const [accounts, setAccounts] = useState4(null);
605
+ const [selected, setSelected] = useState4(null);
606
+ const [busy, setBusy] = useState4(false);
607
+ const [error, setError] = useState4(false);
608
+ const [reload, setReload] = useState4(0);
350
609
  const mutation = useRef(null);
351
- useEffect2(() => () => mutation.current?.abort(), []);
352
- useEffect2(() => {
610
+ useEffect3(() => () => mutation.current?.abort(), []);
611
+ useEffect3(() => {
353
612
  const abort = new AbortController();
354
613
  setAccounts(null);
355
614
  setSelected(null);
@@ -406,18 +665,18 @@ function ScopedAccounts({ controller, className, returnUrl }) {
406
665
  setError(true);
407
666
  }
408
667
  };
409
- return /* @__PURE__ */ jsxs3(
668
+ return /* @__PURE__ */ jsxs5(
410
669
  "section",
411
670
  {
412
671
  className,
413
672
  "aria-label": "Connected accounts",
414
673
  "aria-busy": busy || view.busy || !accounts && !error,
415
674
  children: [
416
- error && /* @__PURE__ */ jsx3("p", { role: "alert", children: "Account state could not be confirmed. Reload before trying again." }),
417
- !accounts && !error && /* @__PURE__ */ jsx3("p", { role: "status", children: "Loading accounts\u2026" }),
418
- accounts?.length === 0 && /* @__PURE__ */ jsx3("p", { role: "status", children: "No connected accounts." }),
419
- accounts && accounts.length > 0 && /* @__PURE__ */ jsx3("ul", { children: accounts.map((account) => /* @__PURE__ */ jsxs3("li", { children: [
420
- /* @__PURE__ */ jsxs3("span", { children: [
675
+ error && /* @__PURE__ */ jsx5("p", { role: "alert", children: "Account state could not be confirmed. Reload before trying again." }),
676
+ !accounts && !error && /* @__PURE__ */ jsx5("p", { role: "status", children: "Loading accounts\u2026" }),
677
+ accounts?.length === 0 && /* @__PURE__ */ jsx5("p", { role: "status", children: "No connected accounts." }),
678
+ accounts && accounts.length > 0 && /* @__PURE__ */ jsx5("ul", { children: accounts.map((account) => /* @__PURE__ */ jsxs5("li", { children: [
679
+ /* @__PURE__ */ jsxs5("span", { children: [
421
680
  account.label,
422
681
  " \u2014 ",
423
682
  account.providerId,
@@ -427,7 +686,7 @@ function ScopedAccounts({ controller, className, returnUrl }) {
427
686
  " ",
428
687
  account.status.replaceAll("_", " ")
429
688
  ] }),
430
- returnUrl && /* @__PURE__ */ jsxs3(
689
+ returnUrl && /* @__PURE__ */ jsxs5(
431
690
  "button",
432
691
  {
433
692
  type: "button",
@@ -439,9 +698,9 @@ function ScopedAccounts({ controller, className, returnUrl }) {
439
698
  ]
440
699
  }
441
700
  ),
442
- selected === `${account.providerId}:${account.id}` ? /* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": `Disconnect ${account.label}`, children: [
443
- /* @__PURE__ */ jsx3("p", { children: "This removes local OpenGeni access. It does not revoke consent at the provider." }),
444
- /* @__PURE__ */ jsx3(
701
+ selected === `${account.providerId}:${account.id}` ? /* @__PURE__ */ jsxs5("div", { role: "group", "aria-label": `Disconnect ${account.label}`, children: [
702
+ /* @__PURE__ */ jsx5("p", { children: "This removes local OpenGeni access. It does not revoke consent at the provider." }),
703
+ /* @__PURE__ */ jsx5(
445
704
  "button",
446
705
  {
447
706
  type: "button",
@@ -450,8 +709,8 @@ function ScopedAccounts({ controller, className, returnUrl }) {
450
709
  children: "Confirm disconnect"
451
710
  }
452
711
  ),
453
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: busy, onClick: () => setSelected(null), children: "Keep account" })
454
- ] }) : /* @__PURE__ */ jsxs3(
712
+ /* @__PURE__ */ jsx5("button", { type: "button", disabled: busy, onClick: () => setSelected(null), children: "Keep account" })
713
+ ] }) : /* @__PURE__ */ jsxs5(
455
714
  "button",
456
715
  {
457
716
  type: "button",
@@ -464,38 +723,46 @@ function ScopedAccounts({ controller, className, returnUrl }) {
464
723
  }
465
724
  )
466
725
  ] }, `${account.providerId}:${account.id}`)) }),
467
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: busy, onClick: () => setReload((value) => value + 1), children: "Reload accounts" })
726
+ /* @__PURE__ */ jsx5("button", { type: "button", disabled: busy, onClick: () => setReload((value) => value + 1), children: "Reload accounts" })
468
727
  ]
469
728
  }
470
729
  );
471
730
  }
472
731
 
473
732
  // src/connect-panel.tsx
474
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
733
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
475
734
  function ConnectPanel({
476
735
  returnUrl,
477
736
  title = "Connections",
478
737
  showAccounts = true,
738
+ presentation = "select",
479
739
  className,
480
740
  ...setup
481
741
  }) {
482
- return /* @__PURE__ */ jsxs4("div", { className: ["og-connect", className].filter(Boolean).join(" "), children: [
483
- /* @__PURE__ */ jsx4("h2", { children: title }),
484
- /* @__PURE__ */ jsx4(ConnectChooser, { controller: setup.controller, returnUrl }),
485
- /* @__PURE__ */ jsx4(ConnectSetup, { ...setup }),
486
- showAccounts && /* @__PURE__ */ jsx4(ConnectAccounts, { controller: setup.controller, returnUrl })
742
+ return /* @__PURE__ */ jsxs6("div", { className: ["og-connect", className].filter(Boolean).join(" "), children: [
743
+ /* @__PURE__ */ jsx6("h2", { children: title }),
744
+ /* @__PURE__ */ jsx6(
745
+ ConnectChooser,
746
+ {
747
+ presentation,
748
+ controller: setup.controller,
749
+ returnUrl
750
+ }
751
+ ),
752
+ /* @__PURE__ */ jsx6(ConnectSetup, { ...setup }),
753
+ showAccounts && /* @__PURE__ */ jsx6(ConnectAccounts, { controller: setup.controller, returnUrl })
487
754
  ] });
488
755
  }
489
756
 
490
757
  // src/device-authorization.tsx
491
- import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
492
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
758
+ import { useEffect as useEffect4, useRef as useRef2, useState as useState5 } from "react";
759
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
493
760
  function DeviceAuthorization(props) {
494
- const [copied, setCopied] = useState4(false);
495
- const [copyError, setCopyError] = useState4(false);
761
+ const [copied, setCopied] = useState5(false);
762
+ const [copyError, setCopyError] = useState5(false);
496
763
  const generation = useRef2(0);
497
764
  const timer = useRef2(null);
498
- useEffect3(() => {
765
+ useEffect4(() => {
499
766
  generation.current++;
500
767
  setCopied(false);
501
768
  setCopyError(false);
@@ -534,11 +801,12 @@ function DeviceAuthorization(props) {
534
801
  href = props.verificationUri;
535
802
  } catch {
536
803
  }
537
- return /* @__PURE__ */ jsxs5("section", { className: `og-connect ${props.className ?? ""}`, "aria-label": "Device authorization", children: [
538
- /* @__PURE__ */ jsx5("p", { children: props.description ?? `Enter this code at ${props.providerLabel ?? "the provider"}.` }),
539
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.5rem", alignItems: "center" }, children: [
540
- /* @__PURE__ */ jsx5("code", { ...props.codeAttributes, style: { overflowWrap: "anywhere" }, children: props.userCode }),
541
- /* @__PURE__ */ jsx5(
804
+ if (props.render) return props.render({ copied, copyError, copy, verificationHref: href });
805
+ return /* @__PURE__ */ jsxs7("section", { className: `og-connect ${props.className ?? ""}`, "aria-label": "Device authorization", children: [
806
+ /* @__PURE__ */ jsx7("p", { children: props.description ?? `Enter this code at ${props.providerLabel ?? "the provider"}.` }),
807
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.5rem", alignItems: "center" }, children: [
808
+ /* @__PURE__ */ jsx7("code", { ...props.codeAttributes, style: { overflowWrap: "anywhere" }, children: props.userCode }),
809
+ /* @__PURE__ */ jsx7(
542
810
  "button",
543
811
  {
544
812
  type: "button",
@@ -547,19 +815,19 @@ function DeviceAuthorization(props) {
547
815
  children: copied ? "Copied" : "Copy code"
548
816
  }
549
817
  ),
550
- href ? /* @__PURE__ */ jsxs5("a", { href, target: "_blank", rel: "noopener noreferrer", children: [
818
+ href ? /* @__PURE__ */ jsxs7("a", { href, target: "_blank", rel: "noopener noreferrer", children: [
551
819
  "Open ",
552
820
  props.providerLabel ?? "auth page"
553
- ] }) : /* @__PURE__ */ jsx5("span", { role: "alert", children: "Authorization address unavailable." })
821
+ ] }) : /* @__PURE__ */ jsx7("span", { role: "alert", children: "Authorization address unavailable." })
554
822
  ] }),
555
- copyError ? /* @__PURE__ */ jsx5("p", { role: "alert", children: "Couldn't copy the code. Copy it manually instead." }) : null,
556
- /* @__PURE__ */ jsx5("p", { role: "status", children: "Waiting for authorization\u2026" })
823
+ copyError ? /* @__PURE__ */ jsx7("p", { role: "alert", children: "Couldn't copy the code. Copy it manually instead." }) : null,
824
+ /* @__PURE__ */ jsx7("p", { role: "status", children: "Waiting for authorization\u2026" })
557
825
  ] });
558
826
  }
559
827
 
560
828
  // src/identity-link-consent.tsx
561
- import { useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
562
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
829
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState6 } from "react";
830
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
563
831
  function IdentityLinkConsent({
564
832
  client,
565
833
  workspaceId,
@@ -568,14 +836,14 @@ function IdentityLinkConsent({
568
836
  className,
569
837
  onComplete
570
838
  }) {
571
- const [link, setLink] = useState5(null);
572
- const [preview, setPreview] = useState5(null);
573
- const [selected, setSelected] = useState5([]);
574
- const [busy, setBusy] = useState5(false);
575
- const [error, setError] = useState5(false);
576
- const [retry, setRetry] = useState5(0);
839
+ const [link, setLink] = useState6(null);
840
+ const [preview, setPreview] = useState6(null);
841
+ const [selected, setSelected] = useState6([]);
842
+ const [busy, setBusy] = useState6(false);
843
+ const [error, setError] = useState6(false);
844
+ const [retry, setRetry] = useState6(0);
577
845
  const generation = useRef3(0);
578
- useEffect4(() => {
846
+ useEffect5(() => {
579
847
  const current = ++generation.current;
580
848
  setLink(null);
581
849
  setPreview(null);
@@ -619,43 +887,43 @@ function IdentityLinkConsent({
619
887
  if (generation.current === current) setBusy(false);
620
888
  }
621
889
  }
622
- return /* @__PURE__ */ jsxs6(
890
+ return /* @__PURE__ */ jsxs8(
623
891
  "section",
624
892
  {
625
893
  className: ["og-connect og-identity-link", className].filter(Boolean).join(" "),
626
894
  "aria-label": "Link your account",
627
895
  "aria-busy": busy,
628
896
  children: [
629
- /* @__PURE__ */ jsx6("h2", { children: "Link your OpenGeni account" }),
630
- /* @__PURE__ */ jsx6("p", { children: "Only continue if you started this request in a product you trust. Linking lets that product act as your account with the permissions you select. It does not merge accounts or move existing work." }),
631
- preview && /* @__PURE__ */ jsxs6("dl", { children: [
632
- /* @__PURE__ */ jsx6("dt", { children: "Organization" }),
633
- /* @__PURE__ */ jsx6("dd", { children: preview.organizationId }),
634
- /* @__PURE__ */ jsx6("dt", { children: "Product user" }),
635
- /* @__PURE__ */ jsxs6("dd", { children: [
897
+ /* @__PURE__ */ jsx8("h2", { children: "Link your OpenGeni account" }),
898
+ /* @__PURE__ */ jsx8("p", { children: "Only continue if you started this request in a product you trust. Linking lets that product act as your account with the permissions you select. It does not merge accounts or move existing work." }),
899
+ preview && /* @__PURE__ */ jsxs8("dl", { children: [
900
+ /* @__PURE__ */ jsx8("dt", { children: "Organization" }),
901
+ /* @__PURE__ */ jsx8("dd", { children: preview.organizationId }),
902
+ /* @__PURE__ */ jsx8("dt", { children: "Product user" }),
903
+ /* @__PURE__ */ jsxs8("dd", { children: [
636
904
  preview.externalIdentity.externalId,
637
905
  " (",
638
906
  preview.externalIdentity.source,
639
907
  ")"
640
908
  ] }),
641
- /* @__PURE__ */ jsx6("dt", { children: "Your OpenGeni identity" }),
642
- /* @__PURE__ */ jsx6("dd", { children: preview.nativeSubjectId })
909
+ /* @__PURE__ */ jsx8("dt", { children: "Your OpenGeni identity" }),
910
+ /* @__PURE__ */ jsx8("dd", { children: preview.nativeSubjectId })
643
911
  ] }),
644
- error && /* @__PURE__ */ jsxs6("div", { role: "alert", children: [
912
+ error && /* @__PURE__ */ jsxs8("div", { role: "alert", children: [
645
913
  "This request could not be completed. It may have expired, changed, or require you to sign in to the right organization.",
646
- !link && /* @__PURE__ */ jsx6("button", { type: "button", disabled: busy, onClick: () => setRetry((value) => value + 1), children: "Try again" })
914
+ !link && /* @__PURE__ */ jsx8("button", { type: "button", disabled: busy, onClick: () => setRetry((value) => value + 1), children: "Try again" })
647
915
  ] }),
648
- !link && busy && /* @__PURE__ */ jsx6("p", { role: "status", children: "Loading the account-link request\u2026" }),
649
- link?.status === "pending" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
650
- /* @__PURE__ */ jsxs6("p", { children: [
916
+ !link && busy && /* @__PURE__ */ jsx8("p", { role: "status", children: "Loading the account-link request\u2026" }),
917
+ link?.status === "pending" && /* @__PURE__ */ jsxs8(Fragment5, { children: [
918
+ /* @__PURE__ */ jsxs8("p", { children: [
651
919
  link.expiresAt ? `Access ends ${new Date(link.expiresAt).toLocaleString()}.` : "Access lasts until you or the product revoke this link.",
652
920
  " ",
653
921
  "Your current permissions still apply."
654
922
  ] }),
655
- /* @__PURE__ */ jsxs6("fieldset", { disabled: busy, children: [
656
- /* @__PURE__ */ jsx6("legend", { children: "Allow these permissions" }),
657
- link.permissions.map((permission) => /* @__PURE__ */ jsxs6("label", { children: [
658
- /* @__PURE__ */ jsx6(
923
+ /* @__PURE__ */ jsxs8("fieldset", { disabled: busy, children: [
924
+ /* @__PURE__ */ jsx8("legend", { children: "Allow these permissions" }),
925
+ link.permissions.map((permission) => /* @__PURE__ */ jsxs8("label", { children: [
926
+ /* @__PURE__ */ jsx8(
659
927
  "input",
660
928
  {
661
929
  type: "checkbox",
@@ -665,10 +933,10 @@ function IdentityLinkConsent({
665
933
  )
666
934
  }
667
935
  ),
668
- /* @__PURE__ */ jsx6("span", { children: permission.replaceAll(":", " \xB7 ") })
936
+ /* @__PURE__ */ jsx8("span", { children: permission.replaceAll(":", " \xB7 ") })
669
937
  ] }, permission))
670
938
  ] }),
671
- /* @__PURE__ */ jsx6(
939
+ /* @__PURE__ */ jsx8(
672
940
  "button",
673
941
  {
674
942
  type: "button",
@@ -677,34 +945,34 @@ function IdentityLinkConsent({
677
945
  children: busy ? "Saving\u2026" : "Allow selected access"
678
946
  }
679
947
  ),
680
- /* @__PURE__ */ jsx6("p", { children: "You can close this page without granting access." })
948
+ /* @__PURE__ */ jsx8("p", { children: "You can close this page without granting access." })
681
949
  ] }),
682
- link?.status === "active" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
683
- /* @__PURE__ */ jsx6("p", { role: "status", children: "Account linked. Return to your product to continue." }),
684
- /* @__PURE__ */ jsx6("button", { type: "button", disabled: busy, onClick: () => void mutate(true), children: "Revoke this link" })
950
+ link?.status === "active" && /* @__PURE__ */ jsxs8(Fragment5, { children: [
951
+ /* @__PURE__ */ jsx8("p", { role: "status", children: "Account linked. Return to your product to continue." }),
952
+ /* @__PURE__ */ jsx8("button", { type: "button", disabled: busy, onClick: () => void mutate(true), children: "Revoke this link" })
685
953
  ] }),
686
- link?.status === "revoked" && /* @__PURE__ */ jsx6("p", { role: "status", children: "Link revoked. Your OpenGeni account and existing work are unchanged." }),
687
- link?.status === "expired" && /* @__PURE__ */ jsx6("p", { role: "status", children: "This request has expired. Start a new request from your product." })
954
+ link?.status === "revoked" && /* @__PURE__ */ jsx8("p", { role: "status", children: "Link revoked. Your OpenGeni account and existing work are unchanged." }),
955
+ link?.status === "expired" && /* @__PURE__ */ jsx8("p", { role: "status", children: "This request has expired. Start a new request from your product." })
688
956
  ]
689
957
  }
690
958
  );
691
959
  }
692
960
 
693
961
  // src/identity-link-accounts.tsx
694
- import { useEffect as useEffect5, useRef as useRef4, useState as useState6 } from "react";
695
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
962
+ import { useEffect as useEffect6, useRef as useRef4, useState as useState7 } from "react";
963
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
696
964
  function IdentityLinkAccounts({
697
965
  client,
698
966
  workspaceId
699
967
  }) {
700
- const [links, setLinks] = useState6([]);
701
- const [cursor, setCursor] = useState6(null);
702
- const [busy, setBusy] = useState6(true);
703
- const [error, setError] = useState6(false);
704
- const [revision, setRevision] = useState6(0);
968
+ const [links, setLinks] = useState7([]);
969
+ const [cursor, setCursor] = useState7(null);
970
+ const [busy, setBusy] = useState7(true);
971
+ const [error, setError] = useState7(false);
972
+ const [revision, setRevision] = useState7(0);
705
973
  const generation = useRef4(0);
706
974
  const inFlight = useRef4(false);
707
- useEffect5(() => {
975
+ useEffect6(() => {
708
976
  const current = ++generation.current;
709
977
  inFlight.current = true;
710
978
  setLinks([]);
@@ -761,43 +1029,375 @@ function IdentityLinkAccounts({
761
1029
  }
762
1030
  }
763
1031
  }
764
- return /* @__PURE__ */ jsxs7(
1032
+ return /* @__PURE__ */ jsxs9(
765
1033
  "section",
766
1034
  {
767
1035
  className: "og-connect og-identity-link",
768
1036
  "aria-label": "Linked product access",
769
1037
  "aria-busy": busy,
770
1038
  children: [
771
- /* @__PURE__ */ jsx7("h2", { children: "Linked product access" }),
772
- /* @__PURE__ */ jsx7("p", { children: "Manage products allowed to act as your account in this organization. Revoking a link stops future linked access; it does not delete your work or undo an operation already started." }),
773
- error && /* @__PURE__ */ jsxs7("div", { role: "alert", children: [
1039
+ /* @__PURE__ */ jsx9("h2", { children: "Linked product access" }),
1040
+ /* @__PURE__ */ jsx9("p", { children: "Manage products allowed to act as your account in this organization. Revoking a link stops future linked access; it does not delete your work or undo an operation already started." }),
1041
+ error && /* @__PURE__ */ jsxs9("div", { role: "alert", children: [
774
1042
  "Could not update account links. Reload to see current access.",
775
- /* @__PURE__ */ jsx7("button", { type: "button", disabled: busy, onClick: () => setRevision((value) => value + 1), children: "Reload links" })
1043
+ /* @__PURE__ */ jsx9("button", { type: "button", disabled: busy, onClick: () => setRevision((value) => value + 1), children: "Reload links" })
776
1044
  ] }),
777
- busy && /* @__PURE__ */ jsx7("p", { role: "status", children: "Loading account links\u2026" }),
778
- !busy && !error && links.length === 0 && /* @__PURE__ */ jsx7("p", { children: "No products have linked access to this account." }),
779
- /* @__PURE__ */ jsx7("ul", { children: links.map((link) => /* @__PURE__ */ jsxs7("li", { children: [
780
- /* @__PURE__ */ jsxs7("p", { children: [
1045
+ busy && /* @__PURE__ */ jsx9("p", { role: "status", children: "Loading account links\u2026" }),
1046
+ !busy && !error && links.length === 0 && /* @__PURE__ */ jsx9("p", { children: "No products have linked access to this account." }),
1047
+ /* @__PURE__ */ jsx9("ul", { children: links.map((link) => /* @__PURE__ */ jsxs9("li", { children: [
1048
+ /* @__PURE__ */ jsxs9("p", { children: [
781
1049
  link.externalIdentity?.source || "Product identity",
782
1050
  " ",
783
- /* @__PURE__ */ jsx7("code", { children: link.externalIdentity?.externalId ?? link.externalIdentityId })
1051
+ /* @__PURE__ */ jsx9("code", { children: link.externalIdentity?.externalId ?? link.externalIdentityId })
784
1052
  ] }),
785
- /* @__PURE__ */ jsxs7("p", { children: [
1053
+ /* @__PURE__ */ jsxs9("p", { children: [
786
1054
  "Status: ",
787
1055
  link.status,
788
1056
  ".",
789
1057
  " ",
790
1058
  link.expiresAt ? `Expires ${new Date(link.expiresAt).toLocaleString()}.` : "No automatic expiry."
791
1059
  ] }),
792
- /* @__PURE__ */ jsx7("p", { children: link.permissions.join(", ") }),
793
- (link.status === "active" || link.status === "pending") && /* @__PURE__ */ jsx7("button", { type: "button", disabled: busy, onClick: () => void act(link), children: "Revoke access" })
1060
+ /* @__PURE__ */ jsx9("p", { children: link.permissions.join(", ") }),
1061
+ (link.status === "active" || link.status === "pending") && /* @__PURE__ */ jsx9("button", { type: "button", disabled: busy, onClick: () => void act(link), children: "Revoke access" })
794
1062
  ] }, link.id)) }),
795
- cursor && /* @__PURE__ */ jsx7("button", { type: "button", disabled: busy, onClick: () => void act(), children: "Load more links" })
1063
+ cursor && /* @__PURE__ */ jsx9("button", { type: "button", disabled: busy, onClick: () => void act(), children: "Load more links" })
796
1064
  ]
797
1065
  }
798
1066
  );
799
1067
  }
800
1068
 
1069
+ // src/connection-installed.tsx
1070
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1071
+ function ConnectionInstalled({
1072
+ items,
1073
+ title = "Connected",
1074
+ className = ""
1075
+ }) {
1076
+ if (!items.length) return null;
1077
+ return /* @__PURE__ */ jsxs10("section", { "aria-label": title, className: `og-connection-installed ${className}`, children: [
1078
+ /* @__PURE__ */ jsxs10("h2", { children: [
1079
+ title,
1080
+ " ",
1081
+ /* @__PURE__ */ jsx10("span", { children: items.length })
1082
+ ] }),
1083
+ /* @__PURE__ */ jsx10("div", { children: items.map((item) => /* @__PURE__ */ jsxs10(
1084
+ "button",
1085
+ {
1086
+ type: "button",
1087
+ onClick: item.onOpen,
1088
+ title: item.status,
1089
+ "aria-label": `${item.name} \xB7 ${item.status}`,
1090
+ children: [
1091
+ item.icon,
1092
+ /* @__PURE__ */ jsx10("span", { children: item.name }),
1093
+ item.needsAttention ? /* @__PURE__ */ jsx10("span", { "aria-label": "Needs attention", children: "!" }) : null
1094
+ ]
1095
+ },
1096
+ item.id
1097
+ )) })
1098
+ ] });
1099
+ }
1100
+
1101
+ // src/connection-type-picker.tsx
1102
+ import { useId } from "react";
1103
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1104
+ function ConnectionTypePicker({
1105
+ value,
1106
+ onChange,
1107
+ disabled = false
1108
+ }) {
1109
+ const name = useId();
1110
+ return /* @__PURE__ */ jsxs11("fieldset", { className: "og-connection-type-picker", disabled, children: [
1111
+ /* @__PURE__ */ jsx11("legend", { children: "Connection type" }),
1112
+ [
1113
+ ["mcp", "MCP server"],
1114
+ ["openapi", "OpenAPI"],
1115
+ ["graphql", "GraphQL"]
1116
+ ].map(([type, label]) => /* @__PURE__ */ jsxs11("label", { "data-selected": value === type, children: [
1117
+ /* @__PURE__ */ jsx11(
1118
+ "input",
1119
+ {
1120
+ type: "radio",
1121
+ name,
1122
+ value: type,
1123
+ checked: value === type,
1124
+ onChange: () => onChange(type)
1125
+ }
1126
+ ),
1127
+ label
1128
+ ] }, type))
1129
+ ] });
1130
+ }
1131
+
1132
+ // src/connection-logo.tsx
1133
+ import { useState as useState8 } from "react";
1134
+ import { jsx as jsx12 } from "react/jsx-runtime";
1135
+ function ConnectionLogo({
1136
+ src,
1137
+ name,
1138
+ size = 40
1139
+ }) {
1140
+ const [failedSrc, setFailedSrc] = useState8(null);
1141
+ return /* @__PURE__ */ jsx12("span", { className: "og-connection-logo", "aria-hidden": "true", style: { width: size, height: size }, children: src && src !== failedSrc ? /* @__PURE__ */ jsx12("img", { src, alt: "", loading: "lazy", decoding: "async", onError: () => setFailedSrc(src) }) : /* @__PURE__ */ jsx12("span", { children: name.split(/\s+/).map((part) => part[0]).join("").slice(0, 2).toUpperCase() }) });
1142
+ }
1143
+
1144
+ // src/plugin-discovery.tsx
1145
+ import { useEffect as useEffect7, useRef as useRef5, useState as useState9 } from "react";
1146
+ import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1147
+ function PluginDiscovery(props) {
1148
+ const [provider, setProvider] = useState9("");
1149
+ return /* @__PURE__ */ jsxs12("section", { className: "og-plugin-discovery", "aria-label": "Discover plugins", children: [
1150
+ /* @__PURE__ */ jsxs12("header", { children: [
1151
+ /* @__PURE__ */ jsx13("h3", { children: "Browse plugins" }),
1152
+ /* @__PURE__ */ jsx13("div", { className: "og-plugin-filters", role: "group", "aria-label": "Plugin registry", children: [
1153
+ { value: "", label: "All" },
1154
+ { value: "openai", label: "OpenAI plugin registry" },
1155
+ { value: "anthropic", label: "Anthropic plugin registry" }
1156
+ ].map((option) => /* @__PURE__ */ jsx13(
1157
+ "button",
1158
+ {
1159
+ type: "button",
1160
+ "aria-pressed": provider === option.value,
1161
+ onClick: () => setProvider(option.value),
1162
+ children: option.label
1163
+ },
1164
+ option.value
1165
+ )) })
1166
+ ] }),
1167
+ /* @__PURE__ */ jsx13(
1168
+ Results,
1169
+ {
1170
+ ...props,
1171
+ provider
1172
+ },
1173
+ `${props.workspaceId}:${props.query}:${provider}`
1174
+ )
1175
+ ] });
1176
+ }
1177
+ function Results({
1178
+ client,
1179
+ workspaceId,
1180
+ query,
1181
+ provider,
1182
+ onOpen
1183
+ }) {
1184
+ const [items, setItems] = useState9([]);
1185
+ const [offset, setOffset] = useState9(0);
1186
+ const [next, setNext] = useState9(null);
1187
+ const [total, setTotal] = useState9(0);
1188
+ const [loading, setLoading] = useState9(true);
1189
+ const [error, setError] = useState9(false);
1190
+ const [retry, setRetry] = useState9(0);
1191
+ const sentinel = useRef5(null);
1192
+ useEffect7(() => {
1193
+ let active = true;
1194
+ setLoading(true);
1195
+ setError(false);
1196
+ const timer = setTimeout(
1197
+ () => {
1198
+ void client.discoverPlugins(workspaceId, { query, provider, offset }).then(
1199
+ (page) => {
1200
+ if (!active) return;
1201
+ setItems((previous) => offset ? [...previous, ...page.items] : page.items);
1202
+ setTotal(page.total);
1203
+ setNext(page.nextOffset);
1204
+ setLoading(false);
1205
+ },
1206
+ () => {
1207
+ if (active) {
1208
+ setError(true);
1209
+ setLoading(false);
1210
+ }
1211
+ }
1212
+ );
1213
+ },
1214
+ offset ? 0 : 200
1215
+ );
1216
+ return () => {
1217
+ active = false;
1218
+ clearTimeout(timer);
1219
+ };
1220
+ }, [client, workspaceId, query, provider, offset, retry]);
1221
+ useEffect7(() => {
1222
+ if (loading || error || next === null || !sentinel.current) return;
1223
+ const observer = new IntersectionObserver(
1224
+ (entries) => {
1225
+ if (entries.some((entry) => entry.isIntersecting)) setOffset(next);
1226
+ },
1227
+ { rootMargin: "200px" }
1228
+ );
1229
+ observer.observe(sentinel.current);
1230
+ return () => observer.disconnect();
1231
+ }, [loading, error, next]);
1232
+ return /* @__PURE__ */ jsxs12(Fragment6, { children: [
1233
+ !loading && !error ? /* @__PURE__ */ jsx13("p", { role: "status", children: total ? `${total} plugins` : "No matching plugins. Try another search or registry." }) : null,
1234
+ /* @__PURE__ */ jsx13("div", { className: "og-plugin-discovery-grid", children: items.map((item) => /* @__PURE__ */ jsxs12(
1235
+ "button",
1236
+ {
1237
+ className: "og-plugin-discovery-row",
1238
+ type: "button",
1239
+ onClick: () => onOpen(item),
1240
+ children: [
1241
+ item.logoUrl ? /* @__PURE__ */ jsx13(
1242
+ "img",
1243
+ {
1244
+ src: item.logoUrl,
1245
+ alt: "",
1246
+ loading: "lazy",
1247
+ onError: (event) => {
1248
+ event.currentTarget.style.display = "none";
1249
+ }
1250
+ }
1251
+ ) : null,
1252
+ /* @__PURE__ */ jsxs12("span", { className: "og-plugin-copy", children: [
1253
+ /* @__PURE__ */ jsxs12("span", { className: "og-plugin-title", children: [
1254
+ /* @__PURE__ */ jsx13("strong", { children: item.displayName.replace(/-/g, " ") }),
1255
+ /* @__PURE__ */ jsx13("small", { children: item.provider === "openai" ? "OpenAI plugin registry" : "Anthropic plugin registry" })
1256
+ ] }),
1257
+ /* @__PURE__ */ jsx13("span", { className: "og-plugin-description", children: item.description }),
1258
+ item.category ? /* @__PURE__ */ jsx13("span", { className: "og-plugin-category", children: item.category }) : null
1259
+ ] }),
1260
+ /* @__PURE__ */ jsx13("span", { className: "og-plugin-open", "aria-hidden": "true", children: "\u203A" })
1261
+ ]
1262
+ },
1263
+ item.id
1264
+ )) }),
1265
+ loading ? /* @__PURE__ */ jsx13("p", { role: "status", children: "Loading plugins\u2026" }) : null,
1266
+ error ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
1267
+ "Couldn\u2019t load plugins.",
1268
+ " ",
1269
+ /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => setRetry((value) => value + 1), children: "Retry" })
1270
+ ] }) : null,
1271
+ /* @__PURE__ */ jsx13("div", { ref: sentinel, children: next !== null && !loading && !error ? /* @__PURE__ */ jsx13("button", { className: "og-plugin-more", type: "button", onClick: () => setOffset(next), children: "Show more plugins" }) : null })
1272
+ ] });
1273
+ }
1274
+
1275
+ // src/plugin-details.tsx
1276
+ import { useState as useState10 } from "react";
1277
+ import { pluginMcpUnavailableReason } from "@opengeni/sdk";
1278
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1279
+ var componentLabels = {
1280
+ skills: "Skills",
1281
+ mcp: "MCP connections",
1282
+ apps: "Hosted apps",
1283
+ hooks: "Hooks",
1284
+ agents: "Agents",
1285
+ commands: "Commands",
1286
+ lsp: "Language servers"
1287
+ };
1288
+ var EMPTY_CONNECTIONS = {};
1289
+ function PluginDetails({
1290
+ item,
1291
+ onInstall,
1292
+ busy = false,
1293
+ installed = false,
1294
+ error,
1295
+ connections = EMPTY_CONNECTIONS,
1296
+ onConnect
1297
+ }) {
1298
+ const supported = (server) => pluginMcpUnavailableReason(server) === null;
1299
+ const contentsUnknown = item.skills == null || item.mcpServers == null;
1300
+ const installableCount = (item.skills?.length ?? 0) + (item.mcpServers?.filter(supported).length ?? 0);
1301
+ const exceedsInstallLimit = installableCount > 64;
1302
+ const installable = installableCount > 0 && !exceedsInstallLimit;
1303
+ const [expanded, setExpanded] = useState10(false);
1304
+ const [logoFailed, setLogoFailed] = useState10(false);
1305
+ const description = item.longDescription || item.description;
1306
+ const lengthy = description.length > 700;
1307
+ const registryLabel = item.provider === "openai" ? "OpenAI plugin registry" : item.provider === "anthropic" ? "Anthropic plugin registry" : "Custom source";
1308
+ return /* @__PURE__ */ jsxs13("article", { className: "og-plugin-details", children: [
1309
+ /* @__PURE__ */ jsxs13("header", { className: "og-plugin-details-header", children: [
1310
+ item.logoUrl && !logoFailed ? /* @__PURE__ */ jsx14("img", { src: item.logoUrl, alt: "", onError: () => setLogoFailed(true) }) : null,
1311
+ /* @__PURE__ */ jsxs13("div", { children: [
1312
+ /* @__PURE__ */ jsx14("h2", { children: item.displayName.replace(/-/g, " ") }),
1313
+ item.author?.name ? /* @__PURE__ */ jsxs13("p", { children: [
1314
+ "By ",
1315
+ item.author.name
1316
+ ] }) : null
1317
+ ] })
1318
+ ] }),
1319
+ /* @__PURE__ */ jsxs13("div", { className: "og-plugin-details-meta", children: [
1320
+ /* @__PURE__ */ jsx14("span", { children: registryLabel }),
1321
+ item.category ? /* @__PURE__ */ jsx14("span", { children: item.category }) : null,
1322
+ item.version ? /* @__PURE__ */ jsxs13("span", { children: [
1323
+ "v",
1324
+ item.version
1325
+ ] }) : null
1326
+ ] }),
1327
+ /* @__PURE__ */ jsxs13("section", { className: "og-plugin-details-members", children: [
1328
+ /* @__PURE__ */ jsxs13("h3", { children: [
1329
+ "Skills ",
1330
+ /* @__PURE__ */ jsx14("span", { children: item.skills?.length ?? "\u2014" })
1331
+ ] }),
1332
+ item.skills?.length ? /* @__PURE__ */ jsx14("ul", { children: item.skills.map((skill) => /* @__PURE__ */ jsx14("li", { children: /* @__PURE__ */ jsxs13("a", { href: skill.sourceUrl, target: "_blank", rel: "noopener noreferrer", children: [
1333
+ /* @__PURE__ */ jsx14("strong", { children: skill.name }),
1334
+ /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", children: "\u2197" })
1335
+ ] }) }, skill.sourceUrl)) }) : /* @__PURE__ */ jsx14("p", { children: item.skills ? "No bundled skills." : "Contents haven\u2019t been indexed for this repository." }),
1336
+ /* @__PURE__ */ jsxs13("h3", { children: [
1337
+ "MCP servers ",
1338
+ /* @__PURE__ */ jsx14("span", { children: item.mcpServers?.length ?? "\u2014" })
1339
+ ] }),
1340
+ item.mcpServers?.length ? /* @__PURE__ */ jsx14("ul", { children: item.mcpServers.map((server) => {
1341
+ const connected = Boolean(server.endpoint && connections[server.endpoint]);
1342
+ const unavailableReason = connected ? null : pluginMcpUnavailableReason(server);
1343
+ return /* @__PURE__ */ jsxs13(
1344
+ "li",
1345
+ {
1346
+ className: unavailableReason ? "og-plugin-member-disabled" : void 0,
1347
+ children: [
1348
+ /* @__PURE__ */ jsxs13("div", { className: "og-plugin-server-heading", children: [
1349
+ /* @__PURE__ */ jsx14("strong", { children: server.name }),
1350
+ !unavailableReason && onConnect ? /* @__PURE__ */ jsx14("button", { type: "button", disabled: busy, onClick: () => onConnect(server), children: connected ? "Manage" : "Connect" }) : null
1351
+ ] }),
1352
+ /* @__PURE__ */ jsxs13("small", { children: [
1353
+ connected ? "Connected \xB7 " : "",
1354
+ unavailableReason ?? server.endpoint
1355
+ ] })
1356
+ ]
1357
+ },
1358
+ server.name
1359
+ );
1360
+ }) }) : /* @__PURE__ */ jsx14("p", { children: item.mcpServers ? "No bundled MCP servers." : "Server details haven\u2019t been indexed." }),
1361
+ item.components?.some((component) => !["skills", "mcp"].includes(component)) ? /* @__PURE__ */ jsxs13("div", { className: "og-plugin-details-other", children: [
1362
+ "Not installed here:",
1363
+ " ",
1364
+ item.components.filter((component) => !["skills", "mcp"].includes(component)).map((component) => componentLabels[component] ?? component).join(", ")
1365
+ ] }) : null
1366
+ ] }),
1367
+ /* @__PURE__ */ jsxs13("section", { className: "og-plugin-details-overview", "aria-label": "Overview", children: [
1368
+ /* @__PURE__ */ jsx14("div", { className: lengthy && !expanded ? "og-plugin-details-excerpt" : void 0, children: /* @__PURE__ */ jsx14(Markdown, { children: description }) }),
1369
+ lengthy ? /* @__PURE__ */ jsx14(
1370
+ "button",
1371
+ {
1372
+ type: "button",
1373
+ className: "og-plugin-details-more",
1374
+ "aria-expanded": expanded,
1375
+ onClick: () => setExpanded((value) => !value),
1376
+ children: expanded ? "Show less" : "Read more"
1377
+ }
1378
+ ) : null
1379
+ ] }),
1380
+ /* @__PURE__ */ jsxs13("footer", { children: [
1381
+ item.sourceUrl ? /* @__PURE__ */ jsxs13("a", { href: item.sourceUrl, target: "_blank", rel: "noopener noreferrer", children: [
1382
+ "View repository ",
1383
+ /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", children: "\u2197" })
1384
+ ] }) : null,
1385
+ onInstall ? /* @__PURE__ */ jsx14(
1386
+ "button",
1387
+ {
1388
+ className: "og-plugin-install",
1389
+ type: "button",
1390
+ disabled: !installable || busy || installed,
1391
+ onClick: onInstall,
1392
+ children: installed ? "Installed" : busy ? "Installing\u2026" : "Install plugin"
1393
+ }
1394
+ ) : null,
1395
+ error ? /* @__PURE__ */ jsx14("p", { role: "alert", children: error }) : null,
1396
+ exceedsInstallLimit ? /* @__PURE__ */ jsx14("p", { children: "This plugin exceeds the current 64-component installation limit." }) : contentsUnknown ? /* @__PURE__ */ jsx14("p", { children: "Plugin contents are unavailable. Installation is disabled until the repository can be inspected." }) : !installable ? /* @__PURE__ */ jsx14("p", { children: "No installable skills or remote MCP servers." }) : /* @__PURE__ */ jsx14("p", { children: "Installs skills and adds connection references. Connect each server separately." })
1397
+ ] })
1398
+ ] });
1399
+ }
1400
+
801
1401
  // src/connect.ts
802
1402
  function useConnect(controller) {
803
1403
  const snapshot = useSyncExternalStore(
@@ -823,9 +1423,18 @@ export {
823
1423
  ConnectChooser,
824
1424
  ConnectPanel,
825
1425
  ConnectSetup,
1426
+ ConnectionCatalog,
1427
+ ConnectionInstalled,
1428
+ ConnectionLogo,
1429
+ ConnectionOptionRow,
1430
+ ConnectionServiceRow,
1431
+ ConnectionTypePicker,
826
1432
  DeviceAuthorization,
827
1433
  IdentityLinkAccounts,
828
1434
  IdentityLinkConsent,
1435
+ PluginDetails,
1436
+ PluginDiscovery,
1437
+ SkillDiscovery,
829
1438
  useConnect
830
1439
  };
831
1440
  //# sourceMappingURL=connect.js.map