@opengeni/react 6.1.0 → 7.0.0-canary.35625901699001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -2
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +1 -1
- package/dist/artifacts-presentation.js +1 -1
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +3 -3
- package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-LGH4IP47.js} +3 -3
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/{chunk-NOSYBS2D.js → chunk-2VHARTBL.js} +2 -1
- package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
- package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
- package/dist/chunk-5UONOQ3Y.js +635 -0
- package/dist/chunk-5UONOQ3Y.js.map +1 -0
- package/dist/{chunk-NYESIADM.js → chunk-AKOMC3VO.js} +3 -3
- package/dist/chunk-AKOMC3VO.js.map +1 -0
- package/dist/chunk-BRM6FVZA.js +302 -0
- package/dist/chunk-BRM6FVZA.js.map +1 -0
- package/dist/{chunk-KANFU34B.js → chunk-DBPVUGQT.js} +11 -2
- package/dist/chunk-DBPVUGQT.js.map +1 -0
- package/dist/chunk-FHIGTPME.js +1589 -0
- package/dist/chunk-FHIGTPME.js.map +1 -0
- package/dist/{chunk-5M6VTFJ5.js → chunk-GBPCZH3Y.js} +2 -2
- package/dist/{chunk-YBM6CQRU.js → chunk-GEN54DU5.js} +4 -4
- package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
- package/dist/chunk-HJ5VJ4JB.js.map +1 -0
- package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
- package/dist/{chunk-NLTS7JHI.js → chunk-JPHDWKZE.js} +5 -5
- package/dist/{chunk-TDSEILUH.js → chunk-MI6ALLLD.js} +1297 -1535
- package/dist/chunk-MI6ALLLD.js.map +1 -0
- package/dist/{chunk-ICA6R6TW.js → chunk-OWHPKAOV.js} +28 -20
- package/dist/chunk-OWHPKAOV.js.map +1 -0
- package/dist/{chunk-24M4YBCL.js → chunk-PNAVEK2U.js} +4 -4
- package/dist/{chunk-L2NBTKRX.js → chunk-TXCBE32L.js} +203 -19
- package/dist/chunk-TXCBE32L.js.map +1 -0
- package/dist/{chunk-45XEUVTK.js → chunk-UXE5SSNN.js} +293 -463
- package/dist/chunk-UXE5SSNN.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-VR4Z73EU.js} +203 -9
- package/dist/chunk-VR4Z73EU.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-XMXRI5JK.js} +65 -18
- package/dist/chunk-XMXRI5JK.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/chat-composer.d.ts +13 -3
- package/dist/components/markdown.d.ts +6 -3
- package/dist/components/message-timeline.d.ts +7 -2
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/session-capability-frame.d.ts +24 -0
- package/dist/components/session-chrome.d.ts +1 -2
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-conversation.d.ts +3 -0
- package/dist/components/session-mcp-capability-card.d.ts +22 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/composer.js +7 -7
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +5 -0
- package/dist/connect-chooser.d.ts +6 -0
- package/dist/connect-panel.d.ts +9 -2
- package/dist/connect-setup.d.ts +5 -1
- package/dist/connect.d.ts +5 -21
- package/dist/connect.js +1268 -584
- package/dist/connect.js.map +1 -1
- package/dist/connection-catalog.d.ts +6 -1
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +1 -0
- package/dist/connection-logo.d.ts +5 -2
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/hooks/use-codex-accounts.d.ts +11 -5
- package/dist/hooks/use-connect.d.ts +22 -0
- package/dist/hooks/use-session-events.d.ts +5 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-workspace-sessions.d.ts +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +771 -793
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
- package/dist/model-policy.d.ts +6 -2
- package/dist/model-policy.js +1 -1
- package/dist/{platform-activity-row-DUE2W74J.js → platform-activity-row-HLOH7FW4.js} +9 -4
- package/dist/platform-activity-row-HLOH7FW4.js.map +1 -0
- package/dist/plugin-discovery.d.ts +6 -0
- package/dist/{queue-surface-implementation-LK3S4BIW.js → queue-surface-implementation-3RIFK2BS.js} +4 -4
- package/dist/realtime.js +22 -22
- package/dist/realtime.js.map +1 -1
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +12 -0
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +14 -12
- package/dist/session.d.ts +1 -0
- package/dist/session.js +6 -4
- package/dist/skill-discovery.d.ts +11 -0
- package/dist/timeline/compaction-copy.d.ts +10 -0
- package/dist/timeline/projection.d.ts +3 -1
- package/dist/timeline/tool-display-name.d.ts +2 -1
- package/dist/timeline/turn-summary.d.ts +2 -2
- package/dist/timeline/types.d.ts +24 -2
- package/dist/{timeline-annotation-markers-DRSTLEM6.js → timeline-annotation-markers-OY6OWR3V.js} +2 -2
- package/dist/{timeline-annotation-selection-IHLUGVND.js → timeline-annotation-selection-BOXA2TIN.js} +2 -2
- package/dist/{timeline-annotations-dialog-6Q53N5FG.js → timeline-annotations-dialog-RGY3MHL3.js} +3 -3
- package/package.json +3 -3
- package/src/approvals.ts +8 -1
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +0 -9
- package/src/components/approval-surface.tsx +12 -2
- package/src/components/artifacts/document-editor.tsx +63 -6
- package/src/components/artifacts/presentation-editor.tsx +214 -9
- package/src/components/artifacts/spreadsheet-canvas.ts +3 -8
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +10 -11
- package/src/components/chat-composer.tsx +26 -3
- package/src/components/composer.tsx +10 -4
- package/src/components/human-input-form.tsx +12 -2
- package/src/components/markdown.tsx +133 -9
- package/src/components/message-timeline.tsx +104 -36
- package/src/components/model-picker.tsx +11 -3
- package/src/components/model-policy-picker-menu.tsx +10 -3
- package/src/components/preview-loading.tsx +154 -0
- package/src/components/sandbox-workspace.tsx +20 -36
- package/src/components/session-capability-frame.tsx +163 -0
- package/src/components/session-chrome.tsx +46 -77
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +10 -5
- package/src/components/session-mcp-capability-card.tsx +532 -0
- package/src/components/session-status.tsx +2 -2
- package/src/components/timeline-anchor.tsx +9 -8
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/user-message-body.tsx +7 -0
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +114 -52
- package/src/connect-chooser.tsx +171 -39
- package/src/connect-panel.tsx +73 -9
- package/src/connect-setup.tsx +72 -25
- package/src/connect.ts +13 -22
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +61 -39
- package/src/connection-discovery.tsx +235 -0
- package/src/connection-installed.tsx +2 -0
- package/src/connection-logo.tsx +18 -10
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/conversation-timeline.ts +1 -0
- package/src/discovery-cache.ts +42 -0
- package/src/hooks/use-codex-accounts.ts +33 -14
- package/src/hooks/use-composer.ts +4 -5
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-session-events.ts +114 -1
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-workspace-sessions.ts +10 -0
- package/src/index.ts +22 -2
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-policy.ts +21 -0
- package/src/plugin-details.tsx +8 -5
- package/src/plugin-discovery.tsx +92 -72
- package/src/realtime/realtime-control.tsx +6 -8
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +83 -0
- package/src/session-ui.ts +2 -0
- package/src/session.ts +1 -0
- package/src/skill-discovery.tsx +101 -56
- package/src/timeline/compaction-copy.ts +40 -0
- package/src/timeline/platform-activity-row.tsx +8 -3
- package/src/timeline/projection.ts +78 -2
- package/src/timeline/rolling-activity.tsx +4 -2
- package/src/timeline/shared.tsx +6 -1
- package/src/timeline/startup-timings.tsx +1 -1
- package/src/timeline/tool-display-name.ts +8 -6
- package/src/timeline/tool-renderers.tsx +5 -3
- package/src/timeline/turn-summary.tsx +17 -3
- package/src/timeline/types.ts +24 -2
- package/styles/compiled.css +325 -27
- package/styles/connect.css +90 -15
- package/styles/index.css +46 -0
- package/styles/session-capability.css +42 -0
- package/dist/chunk-45XEUVTK.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-IHQII5WO.js +0 -886
- package/dist/chunk-IHQII5WO.js.map +0 -1
- package/dist/chunk-KANFU34B.js.map +0 -1
- package/dist/chunk-L2NBTKRX.js.map +0 -1
- package/dist/chunk-NYESIADM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-TDSEILUH.js.map +0 -1
- package/dist/chunk-UMECRJRV.js.map +0 -1
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-DUE2W74J.js.map +0 -1
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-LGH4IP47.js.map} +0 -0
- /package/dist/{chunk-NOSYBS2D.js.map → chunk-2VHARTBL.js.map} +0 -0
- /package/dist/{chunk-5M6VTFJ5.js.map → chunk-GBPCZH3Y.js.map} +0 -0
- /package/dist/{chunk-YBM6CQRU.js.map → chunk-GEN54DU5.js.map} +0 -0
- /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
- /package/dist/{chunk-NLTS7JHI.js.map → chunk-JPHDWKZE.js.map} +0 -0
- /package/dist/{chunk-24M4YBCL.js.map → chunk-PNAVEK2U.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
- /package/dist/{queue-surface-implementation-LK3S4BIW.js.map → queue-surface-implementation-3RIFK2BS.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-DRSTLEM6.js.map → timeline-annotation-markers-OY6OWR3V.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-IHLUGVND.js.map → timeline-annotation-selection-BOXA2TIN.js.map} +0 -0
- /package/dist/{timeline-annotations-dialog-6Q53N5FG.js.map → timeline-annotations-dialog-RGY3MHL3.js.map} +0 -0
package/src/connect-chooser.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { connectionServicePresentation } from "./connection-service-presentation";
|
|
1
2
|
import { ConnectionCatalog } from "./connection-catalog";
|
|
3
|
+
import { ConnectionLogo } from "./connection-logo";
|
|
2
4
|
import { useEffect, useState, type FormEvent } from "react";
|
|
3
|
-
import type { ConnectController, ConnectProvider } from "@opengeni/connect";
|
|
4
|
-
import { useConnect } from "./connect";
|
|
5
|
+
import type { ConnectAccount, ConnectController, ConnectProvider } from "@opengeni/connect";
|
|
6
|
+
import { useConnect } from "./hooks/use-connect";
|
|
5
7
|
|
|
6
8
|
export type ConnectChooserProps = {
|
|
7
9
|
controller: ConnectController;
|
|
@@ -9,6 +11,12 @@ export type ConnectChooserProps = {
|
|
|
9
11
|
returnUrl: string;
|
|
10
12
|
className?: string;
|
|
11
13
|
presentation?: "select" | "catalog";
|
|
14
|
+
/** Compact mechanism picker when service discovery is already shown by the host. */
|
|
15
|
+
customOnly?: boolean;
|
|
16
|
+
providerOnly?: boolean;
|
|
17
|
+
compact?: boolean;
|
|
18
|
+
/** Select a service from the unified discovery list. */
|
|
19
|
+
initialProviderId?: string;
|
|
12
20
|
};
|
|
13
21
|
|
|
14
22
|
/** Catalog readiness is supplied by the authenticated backend, never inferred
|
|
@@ -31,34 +39,76 @@ function ScopedChooser({
|
|
|
31
39
|
returnUrl,
|
|
32
40
|
className,
|
|
33
41
|
presentation = "select",
|
|
42
|
+
customOnly = false,
|
|
43
|
+
providerOnly = false,
|
|
44
|
+
compact = false,
|
|
45
|
+
initialProviderId = "",
|
|
34
46
|
}: ConnectChooserProps) {
|
|
35
47
|
const view = useConnect(controller);
|
|
36
48
|
const [catalog, setCatalog] = useState<ConnectProvider[] | null>(null);
|
|
49
|
+
const [accounts, setAccounts] = useState<ConnectAccount[]>([]);
|
|
37
50
|
const [failed, setFailed] = useState(false);
|
|
38
51
|
const [load, setLoad] = useState(0);
|
|
39
52
|
const [query, setQuery] = useState("");
|
|
40
|
-
const [providerId, setProviderId] = useState(
|
|
53
|
+
const [providerId, setProviderId] = useState(initialProviderId);
|
|
41
54
|
const [ownership, setOwnership] = useState("");
|
|
42
55
|
useEffect(() => {
|
|
43
56
|
const abort = new AbortController();
|
|
44
57
|
setCatalog(null);
|
|
58
|
+
setAccounts([]);
|
|
45
59
|
setFailed(false);
|
|
46
|
-
setProviderId(
|
|
60
|
+
setProviderId(initialProviderId);
|
|
47
61
|
setOwnership("");
|
|
48
62
|
// Promise boundary also handles a synchronously throwing injected transport.
|
|
49
63
|
void Promise.resolve()
|
|
50
64
|
.then(() => {
|
|
51
65
|
abort.signal.throwIfAborted();
|
|
52
|
-
return
|
|
66
|
+
return Promise.all([
|
|
67
|
+
controller.transport.catalog(controller.workspaceId, { signal: abort.signal }),
|
|
68
|
+
presentation === "catalog"
|
|
69
|
+
? controller.transport.accounts(controller.workspaceId, { signal: abort.signal })
|
|
70
|
+
: Promise.resolve([] as ConnectAccount[]),
|
|
71
|
+
]);
|
|
53
72
|
})
|
|
54
|
-
.then((providers) => {
|
|
55
|
-
if (!abort.signal.aborted)
|
|
73
|
+
.then(([providers, inventory]) => {
|
|
74
|
+
if (!abort.signal.aborted) {
|
|
75
|
+
setCatalog(
|
|
76
|
+
structuredClone(
|
|
77
|
+
providers.filter(
|
|
78
|
+
(entry) =>
|
|
79
|
+
entry.readiness === "available" &&
|
|
80
|
+
entry.ownership.length > 0 &&
|
|
81
|
+
(!providerOnly ||
|
|
82
|
+
(entry.family !== "mcp" &&
|
|
83
|
+
!entry.setup.some((kind) =>
|
|
84
|
+
["installation", "openapi", "graphql"].includes(kind),
|
|
85
|
+
))) &&
|
|
86
|
+
(!customOnly ||
|
|
87
|
+
entry.family === "mcp" ||
|
|
88
|
+
entry.setup.some((kind) =>
|
|
89
|
+
["installation", "openapi", "graphql"].includes(kind),
|
|
90
|
+
)),
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
setAccounts(structuredClone(inventory));
|
|
95
|
+
const initial = providers.find((entry) => entry.id === initialProviderId);
|
|
96
|
+
if (initial)
|
|
97
|
+
setOwnership(
|
|
98
|
+
initial.ownership.includes("personal") &&
|
|
99
|
+
["google", "microsoft"].includes(initial.family)
|
|
100
|
+
? "personal"
|
|
101
|
+
: initial.ownership.includes("workspace")
|
|
102
|
+
? "workspace"
|
|
103
|
+
: (initial.ownership[0] ?? ""),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
56
106
|
})
|
|
57
107
|
.catch(() => {
|
|
58
108
|
if (!abort.signal.aborted) setFailed(true);
|
|
59
109
|
});
|
|
60
110
|
return () => abort.abort();
|
|
61
|
-
}, [controller, load]);
|
|
111
|
+
}, [controller, load, presentation, customOnly, providerOnly, initialProviderId]);
|
|
62
112
|
const provider = catalog?.find((entry) => entry.id === providerId);
|
|
63
113
|
const canBegin =
|
|
64
114
|
provider?.readiness === "available" &&
|
|
@@ -76,6 +126,9 @@ function ScopedChooser({
|
|
|
76
126
|
setFailed(true);
|
|
77
127
|
}
|
|
78
128
|
};
|
|
129
|
+
// Optional adapters must not add a temporary section that disappears once
|
|
130
|
+
// readiness arrives. The primary discovery surface already owns loading.
|
|
131
|
+
if (compact && (!catalog || catalog.length === 0) && !failed) return null;
|
|
79
132
|
return (
|
|
80
133
|
<section
|
|
81
134
|
className={className}
|
|
@@ -92,42 +145,117 @@ function ScopedChooser({
|
|
|
92
145
|
{Boolean(catalog?.length) && (
|
|
93
146
|
<form onSubmit={submit}>
|
|
94
147
|
<fieldset disabled={view.busy}>
|
|
95
|
-
<legend
|
|
148
|
+
<legend hidden={customOnly || compact}>
|
|
149
|
+
{presentation === "catalog" ? "Add a connection" : "Provider and ownership"}
|
|
150
|
+
</legend>
|
|
96
151
|
{presentation === "catalog" && !provider ? (
|
|
97
152
|
<>
|
|
98
|
-
<label>
|
|
153
|
+
<label hidden={customOnly || compact}>
|
|
99
154
|
Search connections
|
|
100
155
|
<input
|
|
101
156
|
type="search"
|
|
157
|
+
placeholder="Find a service…"
|
|
102
158
|
value={query}
|
|
103
159
|
onChange={(event) => setQuery(event.target.value)}
|
|
104
160
|
/>
|
|
105
161
|
</label>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
162
|
+
{[
|
|
163
|
+
{
|
|
164
|
+
name: "Services",
|
|
165
|
+
entries: catalog!.filter(
|
|
166
|
+
(entry) =>
|
|
167
|
+
entry.readiness === "available" &&
|
|
168
|
+
entry.family !== "mcp" &&
|
|
169
|
+
!entry.setup.some((kind) =>
|
|
170
|
+
["installation", "openapi", "graphql"].includes(kind),
|
|
171
|
+
),
|
|
172
|
+
),
|
|
173
|
+
secondary: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "Custom connections",
|
|
177
|
+
entries: catalog!.filter(
|
|
178
|
+
(entry) =>
|
|
179
|
+
entry.readiness === "available" &&
|
|
180
|
+
(entry.family === "mcp" ||
|
|
181
|
+
entry.setup.some((kind) =>
|
|
182
|
+
["installation", "openapi", "graphql"].includes(kind),
|
|
183
|
+
)),
|
|
184
|
+
),
|
|
185
|
+
secondary: true,
|
|
186
|
+
},
|
|
187
|
+
].map((group) => {
|
|
188
|
+
const entries = group.entries.filter((entry) =>
|
|
189
|
+
entry.label.toLocaleLowerCase().includes(query.toLocaleLowerCase().trim()),
|
|
190
|
+
);
|
|
191
|
+
if (!entries.length) return null;
|
|
192
|
+
const rows = (
|
|
193
|
+
<ConnectionCatalog
|
|
194
|
+
services={entries.map((entry) => {
|
|
195
|
+
const connected = accounts.some(
|
|
196
|
+
(account) =>
|
|
197
|
+
account.providerId === entry.id && account.status === "connected",
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
id: entry.id,
|
|
201
|
+
name: entry.label,
|
|
202
|
+
logo: (
|
|
203
|
+
<ConnectionLogo
|
|
204
|
+
src={connectionServicePresentation(entry).logo}
|
|
205
|
+
name={entry.label}
|
|
206
|
+
/>
|
|
207
|
+
),
|
|
208
|
+
options: [
|
|
209
|
+
{
|
|
210
|
+
id: entry.id,
|
|
211
|
+
name: entry.label,
|
|
212
|
+
status: connected
|
|
213
|
+
? "Connected"
|
|
214
|
+
: entry.readiness === "available"
|
|
215
|
+
? "Connect"
|
|
216
|
+
: "Unavailable",
|
|
217
|
+
state: connected
|
|
218
|
+
? "added"
|
|
219
|
+
: entry.readiness === "available"
|
|
220
|
+
? "available"
|
|
221
|
+
: "unavailable",
|
|
222
|
+
connected,
|
|
223
|
+
onOpen: () => {
|
|
224
|
+
if (!view.busy) {
|
|
225
|
+
setProviderId(entry.id);
|
|
226
|
+
setOwnership(
|
|
227
|
+
entry.ownership.length === 1 ? entry.ownership[0]! : "",
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
};
|
|
234
|
+
})}
|
|
235
|
+
/>
|
|
236
|
+
);
|
|
237
|
+
return group.secondary && !customOnly ? (
|
|
238
|
+
<details
|
|
239
|
+
className="og-connect-secondary"
|
|
240
|
+
key={group.name}
|
|
241
|
+
open={query.trim() ? true : undefined}
|
|
242
|
+
>
|
|
243
|
+
<summary>
|
|
244
|
+
{group.name}
|
|
245
|
+
<span>{entries.length}</span>
|
|
246
|
+
</summary>
|
|
247
|
+
{rows}
|
|
248
|
+
</details>
|
|
249
|
+
) : (
|
|
250
|
+
<div key={group.name}>{rows}</div>
|
|
251
|
+
);
|
|
252
|
+
})}
|
|
253
|
+
{!catalog!.some((entry) =>
|
|
254
|
+
entry.label.toLocaleLowerCase().includes(query.toLocaleLowerCase().trim()),
|
|
255
|
+
) && <p role="status">No services match your search.</p>}
|
|
128
256
|
</>
|
|
129
257
|
) : null}
|
|
130
|
-
{presentation === "catalog" && provider ? (
|
|
258
|
+
{presentation === "catalog" && provider && !initialProviderId ? (
|
|
131
259
|
<button
|
|
132
260
|
type="button"
|
|
133
261
|
onClick={() => {
|
|
@@ -165,8 +293,14 @@ function ScopedChooser({
|
|
|
165
293
|
{provider && (
|
|
166
294
|
<>
|
|
167
295
|
<strong>{presentation === "catalog" ? provider.label : null}</strong>
|
|
296
|
+
{provider.readiness !== "available" && (
|
|
297
|
+
<p role="status">
|
|
298
|
+
{provider.reason ??
|
|
299
|
+
"This service is not available in this environment. Contact your administrator to enable it."}
|
|
300
|
+
</p>
|
|
301
|
+
)}
|
|
168
302
|
<label>
|
|
169
|
-
|
|
303
|
+
Who can use this connection?
|
|
170
304
|
<select
|
|
171
305
|
required
|
|
172
306
|
value={ownership}
|
|
@@ -177,9 +311,7 @@ function ScopedChooser({
|
|
|
177
311
|
</option>
|
|
178
312
|
{provider.ownership.map((choice) => (
|
|
179
313
|
<option key={choice} value={choice}>
|
|
180
|
-
{choice === "personal"
|
|
181
|
-
? "Personal — owned by you"
|
|
182
|
-
: "Workspace — shared connection"}
|
|
314
|
+
{choice === "personal" ? "Only me" : "Everyone in this workspace"}
|
|
183
315
|
</option>
|
|
184
316
|
))}
|
|
185
317
|
</select>
|
|
@@ -191,12 +323,12 @@ function ScopedChooser({
|
|
|
191
323
|
type="submit"
|
|
192
324
|
disabled={!canBegin}
|
|
193
325
|
>
|
|
194
|
-
|
|
326
|
+
Continue
|
|
195
327
|
</button>
|
|
196
328
|
</fieldset>
|
|
197
329
|
</form>
|
|
198
330
|
)}
|
|
199
|
-
<button type="button" disabled={view.busy} onClick={() => setLoad(load + 1)}>
|
|
331
|
+
<button hidden={!failed} type="button" disabled={view.busy} onClick={() => setLoad(load + 1)}>
|
|
200
332
|
Reload providers
|
|
201
333
|
</button>
|
|
202
334
|
</section>
|
package/src/connect-panel.tsx
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { ConnectAccounts } from "./connect-accounts";
|
|
2
2
|
import { ConnectChooser } from "./connect-chooser";
|
|
3
3
|
import { ConnectSetup, type ConnectSetupProps } from "./connect-setup";
|
|
4
|
+
import { useConnect } from "./hooks/use-connect";
|
|
5
|
+
import type { OpenGeniClient } from "@opengeni/sdk";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { connectionServicePresentation } from "./connection-service-presentation";
|
|
8
|
+
import { ConnectionDiscovery } from "./connection-discovery";
|
|
4
9
|
|
|
5
10
|
export type ConnectPanelProps = ConnectSetupProps & {
|
|
6
11
|
returnUrl: string;
|
|
7
|
-
|
|
12
|
+
/** Null omits the heading when the host already supplies one. */
|
|
13
|
+
title?: string | null;
|
|
8
14
|
showAccounts?: boolean;
|
|
15
|
+
/** Presentation only; the host backend owns acquisition admission. */
|
|
16
|
+
showCustomConnections?: boolean;
|
|
17
|
+
/** Include ready, non-custom adapters from the host's Connect catalogue. */
|
|
18
|
+
showProviderConnections?: boolean;
|
|
9
19
|
presentation?: "select" | "catalog";
|
|
20
|
+
client?: OpenGeniClient | undefined;
|
|
10
21
|
};
|
|
11
22
|
|
|
12
23
|
/** Optional composition over the same unstyled surfaces. Import
|
|
@@ -16,20 +27,73 @@ export function ConnectPanel({
|
|
|
16
27
|
returnUrl,
|
|
17
28
|
title = "Connections",
|
|
18
29
|
showAccounts = true,
|
|
30
|
+
showCustomConnections = true,
|
|
31
|
+
showProviderConnections = false,
|
|
19
32
|
presentation = "select",
|
|
20
33
|
className,
|
|
34
|
+
client,
|
|
21
35
|
...setup
|
|
22
36
|
}: ConnectPanelProps) {
|
|
37
|
+
const { attempt } = useConnect(setup.controller);
|
|
38
|
+
const [inventoryRevision, setInventoryRevision] = useState(0);
|
|
39
|
+
// Unknown/failed outcomes still need reconciliation or cancellation, not a
|
|
40
|
+
// competing new acquisition. Unmounting observers never cancels controller work.
|
|
41
|
+
const activeSetup =
|
|
42
|
+
attempt !== null && !["complete", "cancelled", "expired"].includes(attempt.state);
|
|
43
|
+
const serviceName = attempt
|
|
44
|
+
? connectionServicePresentation({ id: attempt.providerId, label: "" }).name
|
|
45
|
+
: "";
|
|
23
46
|
return (
|
|
24
47
|
<div className={["og-connect", className].filter(Boolean).join(" ")}>
|
|
25
|
-
<h2>{title}</h2>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
{title !== null && <h2>{title}</h2>}
|
|
49
|
+
{showAccounts && !activeSetup && (
|
|
50
|
+
<ConnectAccounts
|
|
51
|
+
key={inventoryRevision}
|
|
52
|
+
controller={setup.controller}
|
|
53
|
+
returnUrl={returnUrl}
|
|
54
|
+
client={client}
|
|
55
|
+
onDisconnected={() => setInventoryRevision((value) => value + 1)}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
{!activeSetup &&
|
|
59
|
+
(client && presentation === "catalog" ? (
|
|
60
|
+
<>
|
|
61
|
+
<ConnectionDiscovery
|
|
62
|
+
key={inventoryRevision}
|
|
63
|
+
controller={showProviderConnections ? setup.controller : undefined}
|
|
64
|
+
client={client}
|
|
65
|
+
workspaceId={setup.controller.workspaceId}
|
|
66
|
+
returnUrl={returnUrl}
|
|
67
|
+
onConfigured={() => setInventoryRevision((value) => value + 1)}
|
|
68
|
+
/>
|
|
69
|
+
{showCustomConnections && (
|
|
70
|
+
<details className="og-connect-secondary">
|
|
71
|
+
<summary>Custom connection</summary>
|
|
72
|
+
<ConnectChooser
|
|
73
|
+
customOnly
|
|
74
|
+
presentation={presentation}
|
|
75
|
+
controller={setup.controller}
|
|
76
|
+
returnUrl={returnUrl}
|
|
77
|
+
/>
|
|
78
|
+
</details>
|
|
79
|
+
)}
|
|
80
|
+
</>
|
|
81
|
+
) : (
|
|
82
|
+
<ConnectChooser
|
|
83
|
+
presentation={presentation}
|
|
84
|
+
controller={setup.controller}
|
|
85
|
+
returnUrl={returnUrl}
|
|
86
|
+
/>
|
|
87
|
+
))}
|
|
88
|
+
{activeSetup && (
|
|
89
|
+
<ConnectSetup
|
|
90
|
+
{...setup}
|
|
91
|
+
authorizeLabel={
|
|
92
|
+
setup.authorizeLabel ??
|
|
93
|
+
(serviceName ? `Continue to ${serviceName}` : "Authorize connection")
|
|
94
|
+
}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
33
97
|
</div>
|
|
34
98
|
);
|
|
35
99
|
}
|
package/src/connect-setup.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, type FormEvent } from "react";
|
|
2
2
|
import type { ConnectAttempt, ConnectController } from "@opengeni/connect";
|
|
3
|
-
import { useConnect } from "./connect";
|
|
3
|
+
import { useConnect } from "./hooks/use-connect";
|
|
4
4
|
|
|
5
5
|
const setupStatus: Record<ConnectAttempt["state"], string> = {
|
|
6
6
|
ready: "Ready to connect",
|
|
@@ -9,9 +9,9 @@ const setupStatus: Record<ConnectAttempt["state"], string> = {
|
|
|
9
9
|
provider_wait: "Waiting for authorization",
|
|
10
10
|
account_selection: "Choose an account",
|
|
11
11
|
resource_selection: "Choose resources",
|
|
12
|
-
preview: "
|
|
13
|
-
installing: "
|
|
14
|
-
connected_but_incomplete: "
|
|
12
|
+
preview: "Choose tools",
|
|
13
|
+
installing: "Adding selected tools…",
|
|
14
|
+
connected_but_incomplete: "Choose which tools to add",
|
|
15
15
|
complete: "Connection ready",
|
|
16
16
|
cancelled: "Setup cancelled",
|
|
17
17
|
expired: "Setup expired",
|
|
@@ -35,12 +35,16 @@ export type ConnectSetupProps = {
|
|
|
35
35
|
controller: ConnectController;
|
|
36
36
|
/** Called synchronously in the click handler so a host can open a popup. */
|
|
37
37
|
onAuthorize: (attempt: ConnectAttempt) => void | Promise<unknown>;
|
|
38
|
+
/** Called synchronously on Continue; hosts can reserve a popup before advancing. */
|
|
39
|
+
onSelectAccount?: (accountId: string) => void | Promise<unknown>;
|
|
38
40
|
/** Host pagination must retain selections across pages and submit the final set. */
|
|
39
41
|
onBrowseResources?: (attempt: ConnectAttempt) => void | Promise<unknown>;
|
|
40
42
|
className?: string;
|
|
43
|
+
/** Provider-specific label supplied by the embedding host. */
|
|
44
|
+
authorizeLabel?: string | undefined;
|
|
41
45
|
};
|
|
42
46
|
|
|
43
|
-
/**
|
|
47
|
+
/** Controller-backed setup surface, styled by opt-in connect.css. No provider secrets enter React state. Host owns
|
|
44
48
|
* chooser, controller lifetime, navigation and recovery after full redirect. */
|
|
45
49
|
export function ConnectSetup(props: ConnectSetupProps) {
|
|
46
50
|
// Attempt IDs/revisions are not a substitute for actor scope. A host can
|
|
@@ -55,9 +59,17 @@ export function ConnectSetup(props: ConnectSetupProps) {
|
|
|
55
59
|
return <ScopedSetup key={generation} {...props} />;
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
function ScopedSetup({
|
|
62
|
+
function ScopedSetup({
|
|
63
|
+
controller,
|
|
64
|
+
onAuthorize,
|
|
65
|
+
onBrowseResources,
|
|
66
|
+
onSelectAccount,
|
|
67
|
+
className,
|
|
68
|
+
authorizeLabel = "Authorize connection",
|
|
69
|
+
}: ConnectSetupProps) {
|
|
59
70
|
const view = useConnect(controller);
|
|
60
71
|
const [localError, setLocalError] = useState(false);
|
|
72
|
+
const [navigating, setNavigating] = useState(false);
|
|
61
73
|
const invoke = (operation: () => unknown | Promise<unknown>) => {
|
|
62
74
|
setLocalError(false);
|
|
63
75
|
try {
|
|
@@ -69,7 +81,10 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
69
81
|
const attempt = view.attempt;
|
|
70
82
|
if (!attempt)
|
|
71
83
|
return (
|
|
72
|
-
<section
|
|
84
|
+
<section
|
|
85
|
+
className={["og-connect-setup", className].filter(Boolean).join(" ")}
|
|
86
|
+
aria-label="Connection setup"
|
|
87
|
+
>
|
|
73
88
|
<p role="status">Choose a connection to begin.</p>
|
|
74
89
|
</section>
|
|
75
90
|
);
|
|
@@ -80,7 +95,7 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
80
95
|
event.preventDefault();
|
|
81
96
|
const form = event.currentTarget;
|
|
82
97
|
const data = new FormData(form);
|
|
83
|
-
if (view.busy) return;
|
|
98
|
+
if (view.busy || navigating) return;
|
|
84
99
|
const key = crypto.randomUUID();
|
|
85
100
|
if (action.type === "credentials") {
|
|
86
101
|
const values = Object.fromEntries(
|
|
@@ -94,7 +109,15 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
94
109
|
if (
|
|
95
110
|
action.accounts.some((account) => account.id === accountId && account.status !== "disabled")
|
|
96
111
|
)
|
|
97
|
-
invoke(() =>
|
|
112
|
+
invoke(async () => {
|
|
113
|
+
if (!onSelectAccount) return view.advance({ type: "account", accountId }, key);
|
|
114
|
+
setNavigating(true);
|
|
115
|
+
try {
|
|
116
|
+
await onSelectAccount(accountId);
|
|
117
|
+
} finally {
|
|
118
|
+
setNavigating(false);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
98
121
|
} else if (action.type === "select_resources") {
|
|
99
122
|
if (action.cursor) return;
|
|
100
123
|
const resourceIds = data.getAll("resource").map(String);
|
|
@@ -115,27 +138,47 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
115
138
|
}
|
|
116
139
|
};
|
|
117
140
|
return (
|
|
118
|
-
<section
|
|
119
|
-
|
|
120
|
-
|
|
141
|
+
<section
|
|
142
|
+
className={["og-connect-setup", className].filter(Boolean).join(" ")}
|
|
143
|
+
aria-label="Connection setup"
|
|
144
|
+
aria-busy={view.busy || navigating}
|
|
145
|
+
>
|
|
146
|
+
<div className="og-connect-setup-summary">
|
|
147
|
+
<p className="og-connect-setup-scope">
|
|
148
|
+
{attempt.ownership === "personal" ? "Personal connection" : "Workspace connection"}
|
|
149
|
+
</p>
|
|
150
|
+
<p role="status" className="og-connect-setup-status">
|
|
151
|
+
{setupStatus[attempt.state]}
|
|
152
|
+
</p>
|
|
153
|
+
<p className="og-connect-setup-description">
|
|
154
|
+
{attempt.ownership === "personal"
|
|
155
|
+
? "Only your work can use this connection."
|
|
156
|
+
: "Work in this workspace can use this connection."}
|
|
157
|
+
</p>
|
|
158
|
+
</div>
|
|
121
159
|
{attempt.account && <p>Account: {attempt.account.label}</p>}
|
|
122
160
|
{(localError || view.error || attempt.error) && (
|
|
123
161
|
<p role="alert">
|
|
124
162
|
{attempt.error?.code === "source_changed"
|
|
125
|
-
? "The integration source changed. Review the
|
|
163
|
+
? "The integration source changed. Review the updated tools before adding them."
|
|
126
164
|
: "Connection setup could not continue. Refresh its status before trying again."}
|
|
127
165
|
</p>
|
|
128
166
|
)}
|
|
129
167
|
{!terminal && (
|
|
130
168
|
<form key={`${attempt.id}:${attempt.revision}`} onSubmit={submit} autoComplete="off">
|
|
131
|
-
<fieldset disabled={view.busy}>
|
|
132
|
-
|
|
169
|
+
<fieldset disabled={view.busy || navigating}>
|
|
170
|
+
{["credentials", "select_account", "select_resources", "preview"].includes(
|
|
171
|
+
action.type,
|
|
172
|
+
) ? (
|
|
173
|
+
<legend>Connection details</legend>
|
|
174
|
+
) : null}
|
|
133
175
|
{attempt.state === "connected_but_incomplete" && action.type === "none" && (
|
|
134
176
|
<button
|
|
135
177
|
type="button"
|
|
178
|
+
className="og-connect-setup-primary"
|
|
136
179
|
onClick={() => invoke(() => view.advance({ type: "retry" }, crypto.randomUUID()))}
|
|
137
180
|
>
|
|
138
|
-
|
|
181
|
+
Choose tools
|
|
139
182
|
</button>
|
|
140
183
|
)}
|
|
141
184
|
{action.type === "credentials" &&
|
|
@@ -204,7 +247,7 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
204
247
|
)}
|
|
205
248
|
{action.type === "preview" && (
|
|
206
249
|
<>
|
|
207
|
-
<p>
|
|
250
|
+
<p>Choose the tools agents can use with this account.</p>
|
|
208
251
|
{action.operations.map((operation) => (
|
|
209
252
|
<label key={operation.id}>
|
|
210
253
|
<input type="checkbox" name="operation" value={operation.id} />
|
|
@@ -218,17 +261,19 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
218
261
|
) && (
|
|
219
262
|
<button
|
|
220
263
|
type="submit"
|
|
264
|
+
className="og-connect-setup-primary"
|
|
221
265
|
disabled={action.type === "select_resources" && Boolean(action.cursor)}
|
|
222
266
|
>
|
|
223
|
-
{action.type === "preview" ? "
|
|
267
|
+
{action.type === "preview" ? "Add selected tools" : "Continue"}
|
|
224
268
|
</button>
|
|
225
269
|
)}
|
|
226
270
|
{action.type === "authorize" && (
|
|
227
271
|
<button
|
|
228
272
|
type="button"
|
|
273
|
+
className="og-connect-setup-primary"
|
|
229
274
|
onClick={() => invoke(() => onAuthorize(structuredClone(attempt)))}
|
|
230
275
|
>
|
|
231
|
-
|
|
276
|
+
{navigating ? "Waiting for authorization…" : authorizeLabel}
|
|
232
277
|
</button>
|
|
233
278
|
)}
|
|
234
279
|
{action.type === "wait" && (
|
|
@@ -246,12 +291,14 @@ function ScopedSetup({ controller, onAuthorize, onBrowseResources, className }:
|
|
|
246
291
|
)}
|
|
247
292
|
</>
|
|
248
293
|
)}
|
|
249
|
-
<
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
294
|
+
<div className="og-connect-setup-secondary">
|
|
295
|
+
<button type="button" onClick={() => invoke(() => view.refresh())}>
|
|
296
|
+
Check status
|
|
297
|
+
</button>
|
|
298
|
+
<button type="button" onClick={() => invoke(() => view.cancel(crypto.randomUUID()))}>
|
|
299
|
+
Cancel setup
|
|
300
|
+
</button>
|
|
301
|
+
</div>
|
|
255
302
|
</fieldset>
|
|
256
303
|
</form>
|
|
257
304
|
)}
|
package/src/connect.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// Optional, unstyled React adapter over the shared headless controller.
|
|
2
2
|
// The host owns controller lifetime; unmounting one observer does not cancel an
|
|
3
3
|
// attempt another observer is displaying or a durable backend operation.
|
|
4
|
-
|
|
4
|
+
export { useConnect } from "./hooks/use-connect";
|
|
5
|
+
export {
|
|
6
|
+
CapabilityCatalogRow,
|
|
7
|
+
CapabilityCatalogIndicator,
|
|
8
|
+
type CapabilityCatalogRowProps,
|
|
9
|
+
type CapabilityCatalogStatus,
|
|
10
|
+
} from "./capability-catalog-row";
|
|
5
11
|
export {
|
|
6
12
|
SkillDiscovery,
|
|
7
13
|
type SkillDiscoveryProps,
|
|
@@ -9,11 +15,15 @@ export {
|
|
|
9
15
|
type SkillDiscoveryItem,
|
|
10
16
|
type SkillDiscoveryPage,
|
|
11
17
|
} from "./skill-discovery";
|
|
12
|
-
import type { ConnectController } from "@opengeni/connect";
|
|
13
18
|
export { ConnectSetup, type ConnectSetupProps } from "./connect-setup";
|
|
14
19
|
export { ConnectChooser, type ConnectChooserProps } from "./connect-chooser";
|
|
15
20
|
export { ConnectAccounts, type ConnectAccountsProps } from "./connect-accounts";
|
|
16
21
|
export { ConnectPanel, type ConnectPanelProps } from "./connect-panel";
|
|
22
|
+
export { ConnectionDiscovery, type ConnectionDiscoveryProps } from "./connection-discovery";
|
|
23
|
+
export {
|
|
24
|
+
McpConnectionCard,
|
|
25
|
+
type McpConnectionCardProps,
|
|
26
|
+
} from "./components/session-mcp-capability-card";
|
|
17
27
|
export { DeviceAuthorization, type DeviceAuthorizationProps } from "./device-authorization";
|
|
18
28
|
export {
|
|
19
29
|
IdentityLinkConsent,
|
|
@@ -34,26 +44,6 @@ export type {
|
|
|
34
44
|
ConnectTransport,
|
|
35
45
|
} from "@opengeni/connect";
|
|
36
46
|
|
|
37
|
-
export function useConnect(controller: ConnectController) {
|
|
38
|
-
const snapshot = useSyncExternalStore(
|
|
39
|
-
controller.subscribe,
|
|
40
|
-
controller.getSnapshot,
|
|
41
|
-
controller.getSnapshot,
|
|
42
|
-
);
|
|
43
|
-
const actions = useMemo(
|
|
44
|
-
() => ({
|
|
45
|
-
begin: controller.begin.bind(controller),
|
|
46
|
-
recover: controller.recover.bind(controller),
|
|
47
|
-
refresh: controller.refresh.bind(controller),
|
|
48
|
-
waitForAction: controller.waitForAction.bind(controller),
|
|
49
|
-
advance: controller.advance.bind(controller),
|
|
50
|
-
cancel: controller.cancel.bind(controller),
|
|
51
|
-
}),
|
|
52
|
-
[controller],
|
|
53
|
-
);
|
|
54
|
-
return useMemo(() => ({ ...snapshot, ...actions }), [snapshot, actions]);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
47
|
export {
|
|
58
48
|
ConnectionCatalog,
|
|
59
49
|
ConnectionServiceRow,
|
|
@@ -65,5 +55,6 @@ export {
|
|
|
65
55
|
export { ConnectionInstalled, type ConnectionInstalledItem } from "./connection-installed";
|
|
66
56
|
export { ConnectionTypePicker, type ConnectionType } from "./connection-type-picker";
|
|
67
57
|
export { ConnectionLogo } from "./connection-logo";
|
|
58
|
+
export { capabilityLogoFallback } from "./capability-logo-fallback";
|
|
68
59
|
export { PluginDiscovery, type PluginDiscoveryProps } from "./plugin-discovery";
|
|
69
60
|
export { PluginDetails } from "./plugin-details";
|