@devicai/ui 0.37.1 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,241 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useMemo, useState, useCallback, useRef, useEffect } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { DevicApiClient } from '../../api/client.js';
5
+ import { useOptionalDevicContext } from '../../provider/DevicContext.js';
6
+
7
+ function PlugIcon() {
8
+ return (jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [jsx("path", { d: "M12 22v-5" }), jsx("path", { d: "M9 8V2" }), jsx("path", { d: "M15 8V2" }), jsx("path", { d: "M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z" })] }));
9
+ }
10
+ /** A random value tying an OAuth round trip to the window that started it. */
11
+ function newNonce() {
12
+ const c = typeof crypto !== "undefined" ? crypto : undefined;
13
+ if (c?.randomUUID)
14
+ return c.randomUUID();
15
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
16
+ }
17
+ /** How the card describes an app at a glance. */
18
+ function stateOf(integration) {
19
+ if (integration.connected)
20
+ return { key: "connected", label: "Connected" };
21
+ if (integration.accounts.some((a) => a.needsReconnect)) {
22
+ return { key: "reconnect", label: "Needs reconnection" };
23
+ }
24
+ return { key: "disconnected", label: "Not connected" };
25
+ }
26
+ function accountLabel(account) {
27
+ if (!account.connectedAt)
28
+ return account.status.toLowerCase();
29
+ const when = new Date(account.connectedAt);
30
+ if (Number.isNaN(when.getTime()))
31
+ return account.status.toLowerCase();
32
+ return `connected ${when.toLocaleDateString()}`;
33
+ }
34
+ /**
35
+ * Modal where the END USER of an application manages their *own* third-party
36
+ * accounts: the apps the developer offered to tenants of this assistant, each
37
+ * with the accounts this tenant has connected, and the buttons to add or
38
+ * remove one.
39
+ *
40
+ * Backed by `/api/v1/tenant-integrations`, which resolves the tenant
41
+ * server-side, so what is listed here is only ever this tenant's — never the
42
+ * workspace-wide accounts an admin connected, and never another tenant's.
43
+ *
44
+ * Connecting opens the provider's consent screen in a popup. The popup is
45
+ * opened empty *before* the request that produces its URL, because browsers
46
+ * only honour `window.open` inside the gesture that triggered it: opening it
47
+ * after the round trip is what gets it blocked. When it is blocked anyway, the
48
+ * URL is offered as a link instead.
49
+ */
50
+ function IntegrationsModal({ isOpen, onClose, assistantId, tenantId, subtenantId, apiKey, baseUrl, title = "Connected apps", onChange, }) {
51
+ const context = useOptionalDevicContext();
52
+ const resolvedApiKey = apiKey || context?.apiKey;
53
+ const resolvedBaseUrl = baseUrl || context?.baseUrl || "https://api.devic.ai";
54
+ const resolvedTenantId = tenantId || context?.tenantId;
55
+ const resolvedSubtenantId = subtenantId || context?.subtenantId;
56
+ const client = useMemo(() => resolvedApiKey
57
+ ? new DevicApiClient({
58
+ apiKey: resolvedApiKey,
59
+ baseUrl: resolvedBaseUrl,
60
+ })
61
+ : null, [resolvedApiKey, resolvedBaseUrl]);
62
+ const scopeOptions = useMemo(() => ({
63
+ assistantId,
64
+ tenantId: resolvedTenantId || undefined,
65
+ subtenantId: resolvedSubtenantId || undefined,
66
+ }), [assistantId, resolvedTenantId, resolvedSubtenantId]);
67
+ const [loading, setLoading] = useState(false);
68
+ const [integrations, setIntegrations] = useState([]);
69
+ const [error, setError] = useState(null);
70
+ /** App slug with a connect/disconnect in flight, so only its card is busy. */
71
+ const [busyApp, setBusyApp] = useState(null);
72
+ /** Authorization URL surfaced as a link when the popup was blocked. */
73
+ const [blockedUrl, setBlockedUrl] = useState(null);
74
+ /**
75
+ * Apps whose logo failed to load. The URL comes from the provider and is
76
+ * fetched from whatever host it names, so a broken one is a matter of when,
77
+ * not if — and a broken-image icon in someone else's product looks like our
78
+ * bug.
79
+ */
80
+ const [brokenLogos, setBrokenLogos] = useState([]);
81
+ const refresh = useCallback(async (dropCache = false) => {
82
+ if (!client) {
83
+ setError("API key not configured");
84
+ return;
85
+ }
86
+ setLoading(true);
87
+ setError(null);
88
+ try {
89
+ // The server caches a scope's connections briefly; after an OAuth round
90
+ // trip that cache is exactly one step behind, so drop it first.
91
+ if (dropCache) {
92
+ await client.refreshIntegrations(scopeOptions).catch(() => undefined);
93
+ }
94
+ const list = await client.getIntegrations(scopeOptions);
95
+ setIntegrations(list);
96
+ onChange?.(list);
97
+ }
98
+ catch (err) {
99
+ setError(err instanceof Error ? err.message : String(err));
100
+ }
101
+ finally {
102
+ setLoading(false);
103
+ }
104
+ }, [client, scopeOptions, onChange]);
105
+ // Fetch on every open: accounts may have been connected or revoked elsewhere.
106
+ const wasOpenRef = useRef(false);
107
+ useEffect(() => {
108
+ if (isOpen && !wasOpenRef.current) {
109
+ setBlockedUrl(null);
110
+ void refresh();
111
+ }
112
+ wasOpenRef.current = isOpen;
113
+ }, [isOpen, refresh]);
114
+ // Escape closes
115
+ useEffect(() => {
116
+ if (!isOpen)
117
+ return;
118
+ const onKey = (e) => {
119
+ if (e.key === "Escape")
120
+ onClose();
121
+ };
122
+ document.addEventListener("keydown", onKey);
123
+ return () => document.removeEventListener("keydown", onKey);
124
+ }, [isOpen, onClose]);
125
+ /** The round trip currently in flight, if any. */
126
+ const pendingRef = useRef(null);
127
+ const popupRef = useRef(null);
128
+ const pollRef = useRef(null);
129
+ const finishConnect = useCallback(() => {
130
+ if (pollRef.current !== null) {
131
+ window.clearInterval(pollRef.current);
132
+ pollRef.current = null;
133
+ }
134
+ pendingRef.current = null;
135
+ popupRef.current = null;
136
+ setBusyApp(null);
137
+ void refresh(true);
138
+ }, [refresh]);
139
+ // The callback page tells us it is done. Treat the message as a nudge, never
140
+ // as the result: what is displayed comes from re-reading the server, so a
141
+ // forged message can at worst cause one redundant fetch.
142
+ useEffect(() => {
143
+ if (!isOpen)
144
+ return;
145
+ const onMessage = (event) => {
146
+ const data = event.data;
147
+ if (!data || data.source !== "devic")
148
+ return;
149
+ if (data.type !== "integration-connected")
150
+ return;
151
+ const pending = pendingRef.current;
152
+ if (!pending || data.returnTo !== pending.returnTo)
153
+ return;
154
+ popupRef.current?.close();
155
+ finishConnect();
156
+ };
157
+ window.addEventListener("message", onMessage);
158
+ return () => window.removeEventListener("message", onMessage);
159
+ }, [isOpen, finishConnect]);
160
+ // Stop polling if the modal goes away mid-flow.
161
+ useEffect(() => () => {
162
+ if (pollRef.current !== null)
163
+ window.clearInterval(pollRef.current);
164
+ }, []);
165
+ const handleConnect = async (integration) => {
166
+ if (!client || busyApp)
167
+ return;
168
+ setError(null);
169
+ setBlockedUrl(null);
170
+ setBusyApp(integration.app);
171
+ const nonce = newNonce();
172
+ const returnTo = `${window.location.origin}/?devic_oauth=${nonce}`;
173
+ // Opened empty inside the click, navigated once the URL is known.
174
+ const popup = window.open("", "devic-oauth", "width=520,height=680,menubar=no,toolbar=no");
175
+ try {
176
+ const { authorizationUrl } = await client.connectIntegration(integration.app, { ...scopeOptions, returnTo });
177
+ pendingRef.current = { app: integration.app, returnTo };
178
+ if (popup && !popup.closed) {
179
+ popupRef.current = popup;
180
+ popup.location.href = authorizationUrl;
181
+ // The user may close the popup without the callback ever posting back
182
+ // — a cancelled consent screen, or a provider that lands somewhere
183
+ // else. Watching for the close is what keeps the card from staying
184
+ // busy forever.
185
+ pollRef.current = window.setInterval(() => {
186
+ if (popup.closed)
187
+ finishConnect();
188
+ }, 700);
189
+ }
190
+ else {
191
+ // Blocked (Safari, in-app browsers, extensions): hand over the URL.
192
+ setBlockedUrl({ app: integration.app, url: authorizationUrl });
193
+ setBusyApp(null);
194
+ }
195
+ }
196
+ catch (err) {
197
+ popup?.close();
198
+ pendingRef.current = null;
199
+ setError(err instanceof Error ? err.message : String(err));
200
+ setBusyApp(null);
201
+ }
202
+ };
203
+ const handleDisconnect = async (app, account) => {
204
+ if (!client || busyApp)
205
+ return;
206
+ setBusyApp(app);
207
+ setError(null);
208
+ try {
209
+ await client.disconnectIntegration(account.id, scopeOptions);
210
+ await refresh(true);
211
+ }
212
+ catch (err) {
213
+ setError(err instanceof Error ? err.message : String(err));
214
+ }
215
+ finally {
216
+ setBusyApp(null);
217
+ }
218
+ };
219
+ if (!isOpen)
220
+ return null;
221
+ return createPortal(jsx("div", { className: "devic-int-overlay", onClick: onClose, children: jsxs("div", { className: "devic-int-modal", role: "dialog", "aria-modal": "true", "aria-label": title, onClick: (e) => e.stopPropagation(), children: [jsxs("div", { className: "devic-int-header", children: [jsxs("h3", { className: "devic-int-title", children: [jsx(PlugIcon, {}), title] }), jsx("button", { className: "devic-int-close", onClick: onClose, type: "button", "aria-label": "Close", children: "\u00D7" })] }), jsxs("div", { className: "devic-int-body", children: [error && jsx("div", { className: "devic-int-error", children: error }), blockedUrl && (jsxs("div", { className: "devic-int-notice", children: ["Your browser blocked the pop-up.", " ", jsx("a", { href: blockedUrl.url, target: "_blank", rel: "noopener noreferrer", onClick: () => {
222
+ pendingRef.current = null;
223
+ setBlockedUrl(null);
224
+ }, children: "Open the authorisation page" }), " ", "and come back \u2014 then use Refresh."] })), loading && integrations.length === 0 ? (jsx("div", { className: "devic-int-loading", children: "Loading apps\u2026" })) : integrations.length === 0 ? (jsx("div", { className: "devic-int-empty", children: "No apps available here yet." })) : (integrations.map((integration) => {
225
+ const state = stateOf(integration);
226
+ const busy = busyApp === integration.app;
227
+ return (jsxs("div", { className: "devic-int-card", "data-state": state.key, children: [jsxs("div", { className: "devic-int-card-head", children: [integration.logo &&
228
+ !brokenLogos.includes(integration.app) ? (jsx("img", { className: "devic-int-logo", src: integration.logo, alt: "", "aria-hidden": "true", onError: () => setBrokenLogos((prev) => prev.includes(integration.app)
229
+ ? prev
230
+ : [...prev, integration.app]) })) : (jsx("span", { className: "devic-int-logo devic-int-logo-fallback", children: integration.name.charAt(0).toUpperCase() })), jsxs("div", { className: "devic-int-card-text", children: [jsx("div", { className: "devic-int-name", children: integration.name }), jsx("div", { className: "devic-int-state", children: state.label })] }), jsx("button", { type: "button", className: `devic-int-btn${state.key === "connected" ? "" : " devic-int-btn-primary"}`, onClick: () => handleConnect(integration), disabled: busy || !!busyApp, children: busy
231
+ ? "Waiting…"
232
+ : state.key === "disconnected"
233
+ ? "Connect"
234
+ : state.key === "reconnect"
235
+ ? "Reconnect"
236
+ : "Add account" })] }), integration.description && (jsx("div", { className: "devic-int-description", children: integration.description })), integration.accounts.length > 0 && (jsx("ul", { className: "devic-int-accounts", children: integration.accounts.map((account) => (jsxs("li", { className: "devic-int-account", children: [jsx("span", { className: "devic-int-dot", "data-ok": !account.needsReconnect, "aria-hidden": "true" }), jsxs("span", { className: "devic-int-account-label", children: [accountLabel(account), account.needsReconnect && (jsxs("span", { className: "devic-int-account-warn", children: [" ", "\u00B7 reconnect required"] }))] }), jsx("button", { type: "button", className: "devic-int-btn devic-int-btn-small devic-int-btn-danger", onClick: () => handleDisconnect(integration.app, account), disabled: !!busyApp, children: "Disconnect" })] }, account.id))) }))] }, integration.app));
237
+ }))] }), jsxs("div", { className: "devic-int-footer", children: [jsx("span", { children: "Only you can see and use the accounts you connect here." }), jsx("button", { type: "button", className: "devic-int-btn devic-int-btn-small", onClick: () => void refresh(true), disabled: loading || !!busyApp, children: "Refresh" })] })] }) }), document.body);
238
+ }
239
+
240
+ export { IntegrationsModal };
241
+ //# sourceMappingURL=IntegrationsModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegrationsModal.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsModal.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { DevicApiClient } from \"../../api/client\";\nimport type { Integration, IntegrationAccount } from \"../../api/types\";\nimport { useOptionalDevicContext } from \"../../provider\";\nimport \"./IntegrationsModal.css\";\n\n/** Message the OAuth callback page posts back to this window when it is done. */\ninterface CallbackMessage {\n source?: string;\n type?: string;\n app?: string;\n status?: string;\n returnTo?: string;\n}\n\nexport interface IntegrationsModalProps {\n /** Whether the modal is visible. */\n isOpen: boolean;\n onClose: () => void;\n /** Assistant whose offered apps are shown. */\n assistantId: string;\n /** Tenant of the end user (falls back to the provider's tenantId). */\n tenantId?: string;\n /** Subtenant of the end user (falls back to the provider's subtenantId). */\n subtenantId?: string;\n /** API key override (falls back to the provider's). */\n apiKey?: string;\n /** Base URL override (falls back to the provider's). */\n baseUrl?: string;\n /** Modal title. @default \"Connected apps\" */\n title?: string;\n /** Called after an account is connected or disconnected. */\n onChange?: (integrations: Integration[]) => void;\n}\n\nfunction PlugIcon(): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M12 22v-5\" />\n <path d=\"M9 8V2\" />\n <path d=\"M15 8V2\" />\n <path d=\"M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z\" />\n </svg>\n );\n}\n\n/** A random value tying an OAuth round trip to the window that started it. */\nfunction newNonce(): string {\n const c = typeof crypto !== \"undefined\" ? crypto : undefined;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/** How the card describes an app at a glance. */\nfunction stateOf(integration: Integration): {\n key: \"connected\" | \"reconnect\" | \"disconnected\";\n label: string;\n} {\n if (integration.connected) return { key: \"connected\", label: \"Connected\" };\n if (integration.accounts.some((a) => a.needsReconnect)) {\n return { key: \"reconnect\", label: \"Needs reconnection\" };\n }\n return { key: \"disconnected\", label: \"Not connected\" };\n}\n\nfunction accountLabel(account: IntegrationAccount): string {\n if (!account.connectedAt) return account.status.toLowerCase();\n const when = new Date(account.connectedAt);\n if (Number.isNaN(when.getTime())) return account.status.toLowerCase();\n return `connected ${when.toLocaleDateString()}`;\n}\n\n/**\n * Modal where the END USER of an application manages their *own* third-party\n * accounts: the apps the developer offered to tenants of this assistant, each\n * with the accounts this tenant has connected, and the buttons to add or\n * remove one.\n *\n * Backed by `/api/v1/tenant-integrations`, which resolves the tenant\n * server-side, so what is listed here is only ever this tenant's — never the\n * workspace-wide accounts an admin connected, and never another tenant's.\n *\n * Connecting opens the provider's consent screen in a popup. The popup is\n * opened empty *before* the request that produces its URL, because browsers\n * only honour `window.open` inside the gesture that triggered it: opening it\n * after the round trip is what gets it blocked. When it is blocked anyway, the\n * URL is offered as a link instead.\n */\nexport function IntegrationsModal({\n isOpen,\n onClose,\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n title = \"Connected apps\",\n onChange,\n}: IntegrationsModalProps): JSX.Element | null {\n const context = useOptionalDevicContext();\n const resolvedApiKey = apiKey || context?.apiKey;\n const resolvedBaseUrl = baseUrl || context?.baseUrl || \"https://api.devic.ai\";\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedSubtenantId = subtenantId || context?.subtenantId;\n\n const client = useMemo(\n () =>\n resolvedApiKey\n ? new DevicApiClient({\n apiKey: resolvedApiKey,\n baseUrl: resolvedBaseUrl,\n })\n : null,\n [resolvedApiKey, resolvedBaseUrl]\n );\n\n const scopeOptions = useMemo(\n () => ({\n assistantId,\n tenantId: resolvedTenantId || undefined,\n subtenantId: resolvedSubtenantId || undefined,\n }),\n [assistantId, resolvedTenantId, resolvedSubtenantId]\n );\n\n const [loading, setLoading] = useState(false);\n const [integrations, setIntegrations] = useState<Integration[]>([]);\n const [error, setError] = useState<string | null>(null);\n /** App slug with a connect/disconnect in flight, so only its card is busy. */\n const [busyApp, setBusyApp] = useState<string | null>(null);\n /** Authorization URL surfaced as a link when the popup was blocked. */\n const [blockedUrl, setBlockedUrl] = useState<{ app: string; url: string } | null>(\n null\n );\n /**\n * Apps whose logo failed to load. The URL comes from the provider and is\n * fetched from whatever host it names, so a broken one is a matter of when,\n * not if — and a broken-image icon in someone else's product looks like our\n * bug.\n */\n const [brokenLogos, setBrokenLogos] = useState<string[]>([]);\n\n const refresh = useCallback(\n async (dropCache = false) => {\n if (!client) {\n setError(\"API key not configured\");\n return;\n }\n setLoading(true);\n setError(null);\n try {\n // The server caches a scope's connections briefly; after an OAuth round\n // trip that cache is exactly one step behind, so drop it first.\n if (dropCache) {\n await client.refreshIntegrations(scopeOptions).catch(() => undefined);\n }\n const list = await client.getIntegrations(scopeOptions);\n setIntegrations(list);\n onChange?.(list);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n },\n [client, scopeOptions, onChange]\n );\n\n // Fetch on every open: accounts may have been connected or revoked elsewhere.\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (isOpen && !wasOpenRef.current) {\n setBlockedUrl(null);\n void refresh();\n }\n wasOpenRef.current = isOpen;\n }, [isOpen, refresh]);\n\n // Escape closes\n useEffect(() => {\n if (!isOpen) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [isOpen, onClose]);\n\n /** The round trip currently in flight, if any. */\n const pendingRef = useRef<{ app: string; returnTo: string } | null>(null);\n const popupRef = useRef<Window | null>(null);\n const pollRef = useRef<number | null>(null);\n\n const finishConnect = useCallback(() => {\n if (pollRef.current !== null) {\n window.clearInterval(pollRef.current);\n pollRef.current = null;\n }\n pendingRef.current = null;\n popupRef.current = null;\n setBusyApp(null);\n void refresh(true);\n }, [refresh]);\n\n // The callback page tells us it is done. Treat the message as a nudge, never\n // as the result: what is displayed comes from re-reading the server, so a\n // forged message can at worst cause one redundant fetch.\n useEffect(() => {\n if (!isOpen) return;\n const onMessage = (event: MessageEvent) => {\n const data = event.data as CallbackMessage | undefined;\n if (!data || data.source !== \"devic\") return;\n if (data.type !== \"integration-connected\") return;\n const pending = pendingRef.current;\n if (!pending || data.returnTo !== pending.returnTo) return;\n popupRef.current?.close();\n finishConnect();\n };\n window.addEventListener(\"message\", onMessage);\n return () => window.removeEventListener(\"message\", onMessage);\n }, [isOpen, finishConnect]);\n\n // Stop polling if the modal goes away mid-flow.\n useEffect(\n () => () => {\n if (pollRef.current !== null) window.clearInterval(pollRef.current);\n },\n []\n );\n\n const handleConnect = async (integration: Integration) => {\n if (!client || busyApp) return;\n setError(null);\n setBlockedUrl(null);\n setBusyApp(integration.app);\n\n const nonce = newNonce();\n const returnTo = `${window.location.origin}/?devic_oauth=${nonce}`;\n // Opened empty inside the click, navigated once the URL is known.\n const popup = window.open(\n \"\",\n \"devic-oauth\",\n \"width=520,height=680,menubar=no,toolbar=no\"\n );\n\n try {\n const { authorizationUrl } = await client.connectIntegration(\n integration.app,\n { ...scopeOptions, returnTo }\n );\n pendingRef.current = { app: integration.app, returnTo };\n if (popup && !popup.closed) {\n popupRef.current = popup;\n popup.location.href = authorizationUrl;\n // The user may close the popup without the callback ever posting back\n // — a cancelled consent screen, or a provider that lands somewhere\n // else. Watching for the close is what keeps the card from staying\n // busy forever.\n pollRef.current = window.setInterval(() => {\n if (popup.closed) finishConnect();\n }, 700);\n } else {\n // Blocked (Safari, in-app browsers, extensions): hand over the URL.\n setBlockedUrl({ app: integration.app, url: authorizationUrl });\n setBusyApp(null);\n }\n } catch (err) {\n popup?.close();\n pendingRef.current = null;\n setError(err instanceof Error ? err.message : String(err));\n setBusyApp(null);\n }\n };\n\n const handleDisconnect = async (app: string, account: IntegrationAccount) => {\n if (!client || busyApp) return;\n setBusyApp(app);\n setError(null);\n try {\n await client.disconnectIntegration(account.id, scopeOptions);\n await refresh(true);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusyApp(null);\n }\n };\n\n if (!isOpen) return null;\n\n return createPortal(\n <div className=\"devic-int-overlay\" onClick={onClose}>\n <div\n className=\"devic-int-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"devic-int-header\">\n <h3 className=\"devic-int-title\">\n <PlugIcon />\n {title}\n </h3>\n <button\n className=\"devic-int-close\"\n onClick={onClose}\n type=\"button\"\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-int-body\">\n {error && <div className=\"devic-int-error\">{error}</div>}\n\n {blockedUrl && (\n <div className=\"devic-int-notice\">\n Your browser blocked the pop-up.{\" \"}\n <a\n href={blockedUrl.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={() => {\n pendingRef.current = null;\n setBlockedUrl(null);\n }}\n >\n Open the authorisation page\n </a>{\" \"}\n and come back — then use Refresh.\n </div>\n )}\n\n {loading && integrations.length === 0 ? (\n <div className=\"devic-int-loading\">Loading apps…</div>\n ) : integrations.length === 0 ? (\n <div className=\"devic-int-empty\">\n No apps available here yet.\n </div>\n ) : (\n integrations.map((integration) => {\n const state = stateOf(integration);\n const busy = busyApp === integration.app;\n return (\n <div\n key={integration.app}\n className=\"devic-int-card\"\n data-state={state.key}\n >\n <div className=\"devic-int-card-head\">\n {integration.logo &&\n !brokenLogos.includes(integration.app) ? (\n <img\n className=\"devic-int-logo\"\n src={integration.logo}\n alt=\"\"\n aria-hidden=\"true\"\n onError={() =>\n setBrokenLogos((prev) =>\n prev.includes(integration.app)\n ? prev\n : [...prev, integration.app]\n )\n }\n />\n ) : (\n <span className=\"devic-int-logo devic-int-logo-fallback\">\n {integration.name.charAt(0).toUpperCase()}\n </span>\n )}\n <div className=\"devic-int-card-text\">\n <div className=\"devic-int-name\">{integration.name}</div>\n <div className=\"devic-int-state\">{state.label}</div>\n </div>\n <button\n type=\"button\"\n className={`devic-int-btn${\n state.key === \"connected\" ? \"\" : \" devic-int-btn-primary\"\n }`}\n onClick={() => handleConnect(integration)}\n disabled={busy || !!busyApp}\n >\n {busy\n ? \"Waiting…\"\n : state.key === \"disconnected\"\n ? \"Connect\"\n : state.key === \"reconnect\"\n ? \"Reconnect\"\n : \"Add account\"}\n </button>\n </div>\n\n {integration.description && (\n <div className=\"devic-int-description\">\n {integration.description}\n </div>\n )}\n\n {integration.accounts.length > 0 && (\n <ul className=\"devic-int-accounts\">\n {integration.accounts.map((account) => (\n <li key={account.id} className=\"devic-int-account\">\n <span\n className=\"devic-int-dot\"\n data-ok={!account.needsReconnect}\n aria-hidden=\"true\"\n />\n <span className=\"devic-int-account-label\">\n {accountLabel(account)}\n {account.needsReconnect && (\n <span className=\"devic-int-account-warn\">\n {\" \"}\n · reconnect required\n </span>\n )}\n </span>\n <button\n type=\"button\"\n className=\"devic-int-btn devic-int-btn-small devic-int-btn-danger\"\n onClick={() =>\n handleDisconnect(integration.app, account)\n }\n disabled={!!busyApp}\n >\n Disconnect\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n })\n )}\n </div>\n\n <div className=\"devic-int-footer\">\n <span>Only you can see and use the accounts you connect here.</span>\n <button\n type=\"button\"\n className=\"devic-int-btn devic-int-btn-small\"\n onClick={() => void refresh(true)}\n disabled={loading || !!busyApp}\n >\n Refresh\n </button>\n </div>\n </div>\n </div>,\n document.body\n );\n}\n\nexport default IntegrationsModal;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { IntegrationsModal, default } from "./IntegrationsModal";
2
+ export type { IntegrationsModalProps } from "./IntegrationsModal";
@@ -4,6 +4,8 @@ export { ChatDrawer, ChatMessages, ChatInput, ToolTimeline, ConversationSelector
4
4
  export type { ChatDrawerProps, ChatDrawerOptions, ChatDrawerHandle, ChatMessagesProps, ChatInputProps, CustomPromptBoxProps, MessageBubbleRenderer, MessageBubbleRendererProps, ToolTimelineProps, AllowedFileTypes, ConversationSelectorProps, HandoffSubagentWidgetProps, ReferenceChipProps, ReferenceChipVariant, SuggestedMessage, UsageBarProps, UsageBarDisplay, UsageBarData, LimitBannerProps, RecalledMemoriesWidgetProps, RecalledMemoriesRenderer, RecalledMemoriesRendererProps, } from './components/ChatDrawer';
5
5
  export { CoreMemoryModal } from './components/CoreMemoryModal';
6
6
  export type { CoreMemoryModalProps } from './components/CoreMemoryModal';
7
+ export { IntegrationsModal } from './components/IntegrationsModal';
8
+ export type { IntegrationsModalProps } from './components/IntegrationsModal';
7
9
  export { ThreadStateTag } from './components/ThreadStateTag';
8
10
  export type { ThreadStateTagProps, StateConfig } from './components/ThreadStateTag';
9
11
  export { AICommandBar, useAICommandBar, formatShortcut } from './components/AICommandBar';
@@ -17,7 +19,7 @@ export type { UseDevicChatOptions, UseDevicChatResult, UsePollingOptions, UsePol
17
19
  export { DevicApiClient, DevicApiError } from './api/client';
18
20
  export type { DevicApiClientConfig } from './api/client';
19
21
  export { AgentThreadState, } from './api/types';
20
- export type { ChatMessage, ChatFile, MessageContent, ToolCall, ToolCallResponse, ProcessMessageDto, AssistantResponse, AsyncResponse, RealtimeChatHistory, RealtimeStatus, ChatHistory, AssistantSpecialization, ModelInterfaceTool, ModelInterfaceToolSchema, ResponseWidgetProps, ResponseWidgetConfig, PreviousMessage, ApiError, ConversationSummary, FeedbackSubmission, FeedbackEntry, AgentThreadDto, AgentTaskDto, AgentDto, HandOffToolResponse, ToolGroupCall, ToolGroupConfig, WhisperTranscriptionResponse, TenantLimitExceeded, TenantUsage, TenantUsageRule, TenantUsageHistoryRow, TenantUsageHistoryQuery, RecalledMemoryRecord, RecalledMemoryFact, RecalledMemoryEntity, RecalledMemoryTurn, CoreMemorySnapshot, CoreMemoryEntry, CoreMemoryLimits, CoreMemoryList, } from './api/types';
22
+ export type { ChatMessage, ChatFile, MessageContent, ToolCall, ToolCallResponse, ProcessMessageDto, AssistantResponse, AsyncResponse, RealtimeChatHistory, RealtimeStatus, ChatHistory, AssistantSpecialization, ModelInterfaceTool, ModelInterfaceToolSchema, ResponseWidgetProps, ResponseWidgetConfig, PreviousMessage, ApiError, ConversationSummary, FeedbackSubmission, FeedbackEntry, AgentThreadDto, AgentTaskDto, AgentDto, HandOffToolResponse, ToolGroupCall, ToolGroupConfig, WhisperTranscriptionResponse, TenantLimitExceeded, TenantUsage, TenantUsageRule, TenantUsageHistoryRow, TenantUsageHistoryQuery, RecalledMemoryRecord, RecalledMemoryFact, RecalledMemoryEntity, RecalledMemoryTurn, CoreMemorySnapshot, CoreMemoryEntry, CoreMemoryLimits, CoreMemoryList, Integration, IntegrationAccount, } from './api/types';
21
23
  export { MessageActions, FeedbackModal } from './components/Feedback';
22
24
  export type { MessageActionsProps, FeedbackModalProps, FeedbackState, FeedbackTheme } from './components/Feedback';
23
25
  export { generateId, deepMerge, debounce, throttle, formatFileSize, storage, segmentToolCalls } from './utils';
package/dist/esm/index.js CHANGED
@@ -11,6 +11,7 @@ export { UsageBar } from './components/ChatDrawer/UsageBar.js';
11
11
  export { LimitBanner } from './components/ChatDrawer/LimitBanner.js';
12
12
  export { RecalledMemoriesWidget } from './components/ChatDrawer/RecalledMemoriesWidget.js';
13
13
  export { CoreMemoryModal } from './components/CoreMemoryModal/CoreMemoryModal.js';
14
+ export { IntegrationsModal } from './components/IntegrationsModal/IntegrationsModal.js';
14
15
  export { ThreadStateTag } from './components/ThreadStateTag/ThreadStateTag.js';
15
16
  export { AICommandBar } from './components/AICommandBar/AICommandBar.js';
16
17
  export { formatShortcut, useAICommandBar } from './components/AICommandBar/useAICommandBar.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}