@devicai/ui 0.57.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -0
- package/dist/cjs/api/assistantInfo.js.map +1 -1
- package/dist/cjs/api/client.js +17 -1
- package/dist/cjs/api/client.js.map +1 -1
- package/dist/cjs/api/types.js.map +1 -1
- package/dist/cjs/components/AICommandBar/AICommandBar.js +12 -8
- package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -1
- package/dist/cjs/components/AICommandBar/useAICommandBar.js +12 -6
- package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js +19 -15
- package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js +12 -6
- package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
- package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js +13 -7
- package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
- package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js +11 -5
- package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js +28 -13
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatInput.js +21 -15
- package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatMessages.js +54 -13
- package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/CompactionWidget.js +13 -4
- package/dist/cjs/components/ChatDrawer/CompactionWidget.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ConversationSelector.js +6 -4
- package/dist/cjs/components/ChatDrawer/ConversationSelector.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ErrorBoundary.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/GuardrailNotice.js +40 -0
- package/dist/cjs/components/ChatDrawer/GuardrailNotice.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js +3 -1
- package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/LimitBanner.js +19 -9
- package/dist/cjs/components/ChatDrawer/LimitBanner.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/QueueNotice.js +19 -7
- package/dist/cjs/components/ChatDrawer/QueueNotice.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/RecalledMemoriesWidget.js +17 -5
- package/dist/cjs/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ReferenceChip.js +3 -1
- package/dist/cjs/components/ChatDrawer/ReferenceChip.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ToolTimeline.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/UsageBar.js +23 -18
- package/dist/cjs/components/ChatDrawer/UsageBar.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/pastedText.js.map +1 -1
- package/dist/cjs/components/CoreMemoryModal/CoreMemoryModal.js +20 -9
- package/dist/cjs/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
- package/dist/cjs/components/Feedback/FeedbackModal.js +5 -1
- package/dist/cjs/components/Feedback/FeedbackModal.js.map +1 -1
- package/dist/cjs/components/Feedback/MessageActions.js +3 -1
- package/dist/cjs/components/Feedback/MessageActions.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/ConnectFieldsForm.js +12 -8
- package/dist/cjs/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsHint.js +5 -3
- package/dist/cjs/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js +12 -3
- package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsPanel.js +34 -24
- package/dist/cjs/components/IntegrationsModal/IntegrationsPanel.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js +15 -6
- package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/McpConnectForm.js +15 -7
- package/dist/cjs/components/IntegrationsModal/McpConnectForm.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/McpServersSection.js +32 -19
- package/dist/cjs/components/IntegrationsModal/McpServersSection.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/integrationChoice.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/useIntegrations.js +3 -1
- package/dist/cjs/components/IntegrationsModal/useIntegrations.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/useTenantMcp.js +3 -1
- package/dist/cjs/components/IntegrationsModal/useTenantMcp.js.map +1 -1
- package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js +63 -40
- package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
- package/dist/cjs/components/theme.js.map +1 -1
- package/dist/cjs/hooks/useDevicChat.js +21 -7
- package/dist/cjs/hooks/useDevicChat.js.map +1 -1
- package/dist/cjs/hooks/useModelInterface.js.map +1 -1
- package/dist/cjs/hooks/usePolling.js +117 -7
- package/dist/cjs/hooks/usePolling.js.map +1 -1
- package/dist/cjs/hooks/useSpeechRecording.js +8 -4
- package/dist/cjs/hooks/useSpeechRecording.js.map +1 -1
- package/dist/cjs/i18n/translate.js +48 -0
- package/dist/cjs/i18n/translate.js.map +1 -0
- package/dist/cjs/i18n/useTranslations.js +57 -0
- package/dist/cjs/i18n/useTranslations.js.map +1 -0
- package/dist/cjs/index.js +11 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/provider/DevicContext.js.map +1 -1
- package/dist/cjs/provider/DevicProvider.js +5 -1
- package/dist/cjs/provider/DevicProvider.js.map +1 -1
- package/dist/cjs/provider/sharedSession.js.map +1 -1
- package/dist/cjs/styles.css +1 -1
- package/dist/cjs/utils/avatar.js.map +1 -1
- package/dist/cjs/utils/consumeChatStream.js +44 -0
- package/dist/cjs/utils/consumeChatStream.js.map +1 -0
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/logger.js.map +1 -1
- package/dist/cjs/utils/toolGroups.js.map +1 -1
- package/dist/esm/api/assistantInfo.js.map +1 -1
- package/dist/esm/api/client.d.ts +5 -1
- package/dist/esm/api/client.js +17 -1
- package/dist/esm/api/client.js.map +1 -1
- package/dist/esm/api/types.d.ts +12 -1
- package/dist/esm/api/types.js.map +1 -1
- package/dist/esm/components/AICommandBar/AICommandBar.js +13 -9
- package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -1
- package/dist/esm/components/AICommandBar/AICommandBar.types.d.ts +16 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.d.ts +2 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.js +13 -7
- package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.js +19 -15
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.types.d.ts +16 -0
- package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.d.ts +2 -0
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js +13 -7
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.js +14 -8
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.types.d.ts +16 -0
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.d.ts +2 -0
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js +12 -6
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.js +29 -14
- package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +41 -0
- package/dist/esm/components/ChatDrawer/ChatInput.js +21 -15
- package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatMessages.d.ts +1 -1
- package/dist/esm/components/ChatDrawer/ChatMessages.js +54 -13
- package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
- package/dist/esm/components/ChatDrawer/CompactionWidget.js +13 -4
- package/dist/esm/components/ChatDrawer/CompactionWidget.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ConversationSelector.js +6 -4
- package/dist/esm/components/ChatDrawer/ConversationSelector.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ErrorBoundary.js.map +1 -1
- package/dist/esm/components/ChatDrawer/GuardrailNotice.d.ts +36 -0
- package/dist/esm/components/ChatDrawer/GuardrailNotice.js +38 -0
- package/dist/esm/components/ChatDrawer/GuardrailNotice.js.map +1 -0
- package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js +3 -1
- package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
- package/dist/esm/components/ChatDrawer/LimitBanner.js +19 -9
- package/dist/esm/components/ChatDrawer/LimitBanner.js.map +1 -1
- package/dist/esm/components/ChatDrawer/QueueNotice.js +19 -7
- package/dist/esm/components/ChatDrawer/QueueNotice.js.map +1 -1
- package/dist/esm/components/ChatDrawer/RecalledMemoriesWidget.js +17 -5
- package/dist/esm/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ReferenceChip.js +3 -1
- package/dist/esm/components/ChatDrawer/ReferenceChip.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ToolTimeline.js.map +1 -1
- package/dist/esm/components/ChatDrawer/UsageBar.js +23 -18
- package/dist/esm/components/ChatDrawer/UsageBar.js.map +1 -1
- package/dist/esm/components/ChatDrawer/index.d.ts +2 -0
- package/dist/esm/components/ChatDrawer/pastedText.js.map +1 -1
- package/dist/esm/components/CoreMemoryModal/CoreMemoryModal.js +20 -9
- package/dist/esm/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
- package/dist/esm/components/Feedback/FeedbackModal.js +5 -1
- package/dist/esm/components/Feedback/FeedbackModal.js.map +1 -1
- package/dist/esm/components/Feedback/MessageActions.js +3 -1
- package/dist/esm/components/Feedback/MessageActions.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/ConnectFieldsForm.js +12 -8
- package/dist/esm/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsHint.js +5 -3
- package/dist/esm/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js +12 -3
- package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsPanel.d.ts +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsPanel.js +34 -24
- package/dist/esm/components/IntegrationsModal/IntegrationsPanel.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsToggle.d.ts +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js +15 -6
- package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/McpConnectForm.js +15 -7
- package/dist/esm/components/IntegrationsModal/McpConnectForm.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/McpServersSection.js +32 -19
- package/dist/esm/components/IntegrationsModal/McpServersSection.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/integrationChoice.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/useIntegrations.js +3 -1
- package/dist/esm/components/IntegrationsModal/useIntegrations.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/useTenantMcp.js +3 -1
- package/dist/esm/components/IntegrationsModal/useTenantMcp.js.map +1 -1
- package/dist/esm/components/ThreadStateTag/ThreadStateTag.js +63 -40
- package/dist/esm/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
- package/dist/esm/components/theme.js.map +1 -1
- package/dist/esm/hooks/index.d.ts +1 -1
- package/dist/esm/hooks/useDevicChat.d.ts +6 -0
- package/dist/esm/hooks/useDevicChat.js +23 -9
- package/dist/esm/hooks/useDevicChat.js.map +1 -1
- package/dist/esm/hooks/useModelInterface.js.map +1 -1
- package/dist/esm/hooks/usePolling.d.ts +35 -0
- package/dist/esm/hooks/usePolling.js +114 -8
- package/dist/esm/hooks/usePolling.js.map +1 -1
- package/dist/esm/hooks/useSpeechRecording.js +8 -4
- package/dist/esm/hooks/useSpeechRecording.js.map +1 -1
- package/dist/esm/i18n/index.d.ts +4 -0
- package/dist/esm/i18n/translate.d.ts +23 -0
- package/dist/esm/i18n/translate.js +43 -0
- package/dist/esm/i18n/translate.js.map +1 -0
- package/dist/esm/i18n/types.d.ts +27 -0
- package/dist/esm/i18n/useTranslations.d.ts +32 -0
- package/dist/esm/i18n/useTranslations.js +53 -0
- package/dist/esm/i18n/useTranslations.js.map +1 -0
- package/dist/esm/index.d.ts +4 -2
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/provider/DevicContext.js.map +1 -1
- package/dist/esm/provider/DevicProvider.d.ts +1 -1
- package/dist/esm/provider/DevicProvider.js +5 -1
- package/dist/esm/provider/DevicProvider.js.map +1 -1
- package/dist/esm/provider/sharedSession.js.map +1 -1
- package/dist/esm/provider/types.d.ts +51 -0
- package/dist/esm/styles.css +1 -1
- package/dist/esm/utils/avatar.js.map +1 -1
- package/dist/esm/utils/consumeChatStream.d.ts +6 -0
- package/dist/esm/utils/consumeChatStream.js +42 -0
- package/dist/esm/utils/consumeChatStream.js.map +1 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/logger.js.map +1 -1
- package/dist/esm/utils/toolGroups.js.map +1 -1
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntegrationsPanel.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsPanel.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 type { DevicApiError } from \"../../api/client\";\nimport type {\n Integration,\n IntegrationAccount,\n IntegrationAuthScheme,\n IntegrationSetupRequired,\n} from \"../../api/types\";\nimport { isDarkTheme, themeVars, type DevicTheme } from \"../theme\";\nimport { ConnectFieldsForm } from \"./ConnectFieldsForm\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useIntegrations, type IntegrationsState } from \"./useIntegrations\";\nimport { McpServersSection } from \"./McpServersSection\";\nimport { useTenantMcp, type TenantMcpState } from \"./useTenantMcp\";\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 IntegrationsPanelProps {\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 /** Heading text, and the accessible name of the region. @default \"Connected apps\" */\n title?: string;\n /** Search field placeholder. @default \"Search connected apps\" */\n searchPlaceholder?: string;\n /** Called after an account is connected or disconnected. */\n onChange?: (integrations: Integration[]) => void;\n /**\n * Colours and font. Same names as the drawer's style options, and the drawer\n * passes its own down — a dialog opening in the default light palette over a\n * themed application is the one thing this must not do.\n *\n * Embedded in a page the same applies for a different reason: the panel\n * renders in its own variables and inherits nothing from the surface around\n * it, so a host with its own palette has to say so here.\n */\n theme?: DevicTheme;\n /**\n * Listing loaded elsewhere (see `useIntegrations`). The drawer already has to\n * load it to decide whether its button exists, and passing it down is what\n * keeps opening the modal from asking for the very same thing again.\n */\n state?: IntegrationsState;\n /**\n * MCP listing loaded elsewhere, same reasoning as `state`. When absent this\n * loads its own — and only while on screen, so an assistant that offers no\n * MCP servers costs one refusal and nothing more.\n */\n mcpState?: TenantMcpState;\n /**\n * Whether the panel is on screen. Renders nothing and asks for nothing while\n * false, without dropping what it already knows — which is how the modal\n * closes without paying for the whole listing again on the next open.\n *\n * @default true\n */\n active?: boolean;\n /**\n * The heading, with the plug icon and the title. Turn it off to put the\n * panel under a heading of your own.\n *\n * @default true\n */\n showHeader?: boolean;\n /** The search field. @default true */\n showSearch?: boolean;\n /**\n * The footer, with the privacy note and the Refresh button. Off, nothing else\n * re-reads the listing on demand — keep `state` yourself and call its\n * `refresh` if you drive it from your own control.\n *\n * @default true\n */\n showFooter?: boolean;\n /**\n * Shown as a close button in the header, and called when Escape is pressed.\n * The modal is what usually passes it; embedded, only pass it if the panel\n * is dismissible.\n */\n onClose?: () => void;\n className?: string;\n /**\n * Render inside the modal's backdrop, in a portal on `document.body`.\n *\n * This is what `IntegrationsModal` is, and that component is the one to use\n * — it exists here because both forms share every line of what follows.\n */\n dialog?: boolean;\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\nfunction SearchIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\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 <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\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 * The scheme the server would pick, given no explicit choice: the one asking\n * for least setup.\n *\n * Mirrors the engine's own preference deliberately. It is only used to decide\n * whether to open a popup or a form before asking, and being wrong is\n * recoverable — the form sends its scheme explicitly, and a redirect that\n * arrives anyway is still honoured.\n */\nfunction preferredScheme(\n schemes?: IntegrationAuthScheme[]\n): IntegrationAuthScheme | undefined {\n if (!schemes?.length) return undefined;\n const friction = (s: IntegrationAuthScheme) => (s.composioManaged ? 0 : 1);\n return [...schemes].sort((a, b) => friction(a) - friction(b))[0];\n}\n\nfunction matches(integration: Integration, query: string): boolean {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n return (\n integration.name.toLowerCase().includes(q) ||\n integration.app.toLowerCase().includes(q) ||\n (integration.description ?? \"\").toLowerCase().includes(q)\n );\n}\n\n/**\n * 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 * The same panel serves both shapes it is asked for. `IntegrationsModal` is\n * this with `dialog`, opened from the drawer's header; on its own it drops into\n * a settings page as an ordinary block, which is the form a host wants when\n * connecting apps is a section of their product rather than an interruption of\n * a chat.\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 IntegrationsPanel({\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n title = \"Connected apps\",\n searchPlaceholder = \"Search connected apps\",\n onChange,\n theme,\n state,\n mcpState,\n active = true,\n showHeader = true,\n showSearch = true,\n showFooter = true,\n onClose,\n className,\n dialog = false,\n}: IntegrationsPanelProps): JSX.Element | null {\n // Hooks cannot be skipped, so the fallback is always built and only fetches\n // when nobody handed a listing down.\n const own = useIntegrations({\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n enabled: active && !state,\n });\n const {\n integrations,\n loading,\n error: loadError,\n offered: appsOffered,\n refresh,\n client,\n scope,\n } = state ?? own;\n\n // Same shape as above: always built (hooks cannot be skipped), fetching only\n // when nobody handed a listing down.\n const ownMcp = useTenantMcp({\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n enabled: active && !mcpState,\n });\n const mcp = mcpState ?? ownMcp;\n\n /** Errors from connecting or disconnecting, kept apart from load failures. */\n const [actionError, setActionError] = useState<string | null>(null);\n // An assistant may offer MCP servers and no apps at all, and then the apps\n // endpoint answers \"not for you\" — an ordinary answer, not a failure. Showing\n // it would put a red panel above a section that is working perfectly. The\n // refusal is still surfaced when there is nothing else on offer, because then\n // an empty dialog with no explanation is worse.\n const appsRefusalIsNoise = !appsOffered && mcp.offered;\n const error = actionError ?? (appsRefusalIsNoise ? null : loadError);\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 /** The app whose credentials are being asked for, and every way it can be\n * connected. `setup` is present when the server named what was missing. */\n const [setupPrompt, setSetupPrompt] = useState<{\n app: string;\n schemes: IntegrationAuthScheme[];\n setup?: IntegrationSetupRequired;\n } | null>(null);\n /**\n * How each offered app can be connected, read once the listing arrives.\n *\n * Loaded ahead of the click rather than on it: knowing whether an app needs\n * a browser is what decides between opening a popup and opening a form, and\n * that decision has to be made *inside* the user's gesture — a popup opened\n * after an await is blocked.\n */\n const [authByApp, setAuthByApp] = useState<\n Record<string, IntegrationAuthScheme[]>\n >({});\n /** Failure from the last submit, shown inside the credentials dialog. */\n const [formError, setFormError] = useState<string | null>(null);\n const [query, setQuery] = useState(\"\");\n\n const visible = useMemo(\n () => integrations.filter((i) => matches(i, query)),\n [integrations, query]\n );\n\n // Resolved from the listing rather than stored in the prompt: the dialog\n // stays open across a refresh, and a copy taken when it opened would go\n // stale — showing \"Not connected\" on an app that just connected.\n const promptIntegration = useMemo(\n () => integrations.find((i) => i.app === setupPrompt?.app),\n [integrations, setupPrompt?.app]\n );\n\n // One request per offered app, once the listing is in. They are small,\n // cached server-side, and nothing waits on them: a click that lands before\n // its answer simply falls back to asking the server, as it did before.\n useEffect(() => {\n if (!active || !client || !integrations.length) return;\n let cancelled = false;\n void Promise.all(\n integrations.map(async (integration) => {\n try {\n const { schemes } = await client.getIntegrationAuth(\n integration.app,\n scope\n );\n if (!cancelled && schemes?.length) {\n setAuthByApp((prev) => ({ ...prev, [integration.app]: schemes }));\n }\n } catch {\n // Not knowing is survivable — the click path handles it.\n }\n })\n );\n return () => {\n cancelled = true;\n };\n }, [active, client, integrations, scope]);\n\n // Report the listing without making the caller's identity part of the\n // dependency: an inline arrow would fire this on every render.\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n useEffect(() => {\n if (integrations.length) onChangeRef.current?.(integrations);\n }, [integrations]);\n\n // Reopening starts clean, and re-reads: accounts may have been connected or\n // revoked elsewhere since the last look.\n const wasOpenRef = useRef(false);\n const openedBeforeRef = useRef(false);\n useEffect(() => {\n if (active && !wasOpenRef.current) {\n setBlockedUrl(null);\n setActionError(null);\n setSetupPrompt(null);\n setQuery(\"\");\n // The very first open of an uncontrolled modal is already covered by the\n // hook switching on; asking again here would double every first open.\n if (state || openedBeforeRef.current) void refresh();\n if (mcpState || openedBeforeRef.current) void mcp.refresh();\n openedBeforeRef.current = true;\n }\n wasOpenRef.current = active;\n // `state.refresh` is stable per scope; re-running on every render of the\n // owner is exactly what this must not do.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active]);\n\n // Escape closes — the credentials dialog first, when one is open. Closing\n // everything from under a half-typed key would be its own small disaster.\n //\n // Embedded in a page there is usually nothing to close after that, and the\n // key is left alone rather than swallowed: a panel that is part of the page\n // has no business answering for the whole document.\n useEffect(() => {\n if (!active) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n if (setupPrompt) {\n setSetupPrompt(null);\n setFormError(null);\n return;\n }\n onClose?.();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [active, onClose, setupPrompt]);\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 (!active) 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 }, [active, 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 /**\n * Connects an app, asking for credentials only if it needs them.\n *\n * Optimistic: the request goes out first, and the form appears only when the\n * server answers that something is missing. Most apps take an API key rather\n * than an OAuth round trip, so this is the path that used to fail outright.\n */\n const handleConnect = async (\n integration: Integration,\n values?: { authScheme: string; accountFields: Record<string, string> }\n ) => {\n if (!client || (busyApp && busyApp !== integration.app)) return;\n setActionError(null);\n setBlockedUrl(null);\n\n // Ask before doing anything when the app is known to take credentials and\n // no browser is involved. Connecting first would flash an empty popup and\n // spend a request that can only fail — the user has not been asked yet.\n const known = authByApp[integration.app];\n const scheme = values\n ? known?.find((s) => s.mode === values.authScheme)\n : preferredScheme(known);\n if (!values && scheme && !scheme.redirect && scheme.accountFields.length) {\n setSetupPrompt({ app: integration.app, schemes: known! });\n return;\n }\n\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 — browsers\n // only honour `window.open` inside the gesture that triggered it. Skipped\n // when the scheme in hand needs no browser at all.\n const popup =\n scheme && !scheme.redirect\n ? null\n : window.open(\n \"\",\n \"devic-oauth\",\n \"width=520,height=680,menubar=no,toolbar=no\"\n );\n\n try {\n const { connected, authorizationUrl } = await client.connectIntegration(\n integration.app,\n { ...scope, returnTo, ...values }\n );\n\n // Nothing to authorise: the key the user typed is the account. Close the\n // window that was opened in case it was needed and re-read the listing.\n if (connected || !authorizationUrl) {\n popup?.close();\n setSetupPrompt(null);\n finishConnect();\n return;\n }\n\n pendingRef.current = { app: integration.app, returnTo };\n setSetupPrompt(null);\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 setBusyApp(null);\n const message = err instanceof Error ? err.message : String(err);\n const setup = (err as DevicApiError)?.setupRequired;\n if (setup) {\n await openSetupForm(integration, setup);\n return;\n }\n // While the form is up its own failures belong in it, next to the fields\n // that caused them — a message behind a dialog is a message nobody reads.\n if (setupPrompt?.app === integration.app) setFormError(message);\n else setActionError(message);\n }\n };\n\n /**\n * Opens the credentials form for an app that needs one.\n *\n * `stage: \"app\"` is not the end user's to fix — it means the developer who\n * embedded this widget has not registered an application with the provider\n * yet. Asking a stranger for someone else's client secret would be both\n * useless and a good way to teach them to hand credentials to a form, so it\n * is reported as unavailable instead.\n */\n const openSetupForm = async (\n integration: Integration,\n setup: IntegrationSetupRequired\n ) => {\n if (setup.stage === \"app\") {\n setActionError(\n `${integration.name} is not available yet — it still needs to be set up ` +\n `by the app's provider.`\n );\n return;\n }\n try {\n const schemes =\n authByApp[integration.app] ??\n (await client!.getIntegrationAuth(integration.app, scope)).schemes;\n if (!schemes.length) throw new Error(setup.message);\n setAuthByApp((prev) => ({ ...prev, [integration.app]: schemes }));\n setFormError(null);\n setSetupPrompt({ app: integration.app, setup, schemes });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : String(err));\n }\n };\n\n const handleDisconnect = async (app: string, account: IntegrationAccount) => {\n if (!client || busyApp) return;\n setBusyApp(app);\n setActionError(null);\n try {\n await client.disconnectIntegration(account.id, scope);\n await refresh(true);\n } catch (err) {\n setActionError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusyApp(null);\n }\n };\n\n if (!active) return null;\n\n const dark = isDarkTheme(theme);\n\n const panel = (\n // The variables go here rather than on the backdrop, because this is the\n // one element both shapes have: as a dialog it renders in a portal that\n // inherits nothing from the drawer that opened it, and embedded it must\n // not inherit the host's either — the colours it draws in are the ones\n // `theme` names, and nothing else.\n <div\n className={[\n \"devic-int-panel\",\n dialog ? \"devic-int-modal\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n style={themeVars(theme)}\n data-dark={dark}\n role={dialog ? \"dialog\" : \"region\"}\n aria-modal={dialog || undefined}\n aria-label={title}\n onClick={dialog ? (e) => e.stopPropagation() : undefined}\n >\n {showHeader && (\n <div className=\"devic-int-header\">\n <h3 className=\"devic-int-title\">\n <PlugIcon />\n {title}\n </h3>\n {onClose && (\n <button\n className=\"devic-int-close\"\n onClick={onClose}\n type=\"button\"\n aria-label=\"Close\"\n >\n ×\n </button>\n )}\n </div>\n )}\n\n {showSearch && (\n <div className=\"devic-int-search\">\n <SearchIcon />\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={searchPlaceholder}\n aria-label={searchPlaceholder}\n autoComplete=\"off\"\n />\n </div>\n )}\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 // Silent while the MCP section is what this assistant offers: a\n // spinner labelled \"Loading apps\" over a list of servers describes\n // something that is not happening.\n appsRefusalIsNoise ? null : (\n <div className=\"devic-int-loading\">Loading apps…</div>\n )\n ) : integrations.length === 0 ? (\n // Silent when MCP servers are the whole offer: \"no apps available\"\n // over a list of servers reads as a broken panel.\n mcp.offered ? null : (\n <div className=\"devic-int-empty\">No apps available here yet.</div>\n )\n ) : visible.length === 0 ? (\n <div className=\"devic-int-empty\">\n No apps match “{query.trim()}”.\n </div>\n ) : (\n <div className=\"devic-int-grid\">\n {visible.map((integration) => {\n const cardState = stateOf(integration);\n const busy = busyApp === integration.app;\n return (\n <div\n key={integration.app}\n className=\"devic-int-card\"\n data-state={cardState.key}\n >\n <div className=\"devic-int-card-head\">\n <IntegrationLogo integration={integration} />\n <span className=\"devic-int-state\">\n <span\n className=\"devic-int-dot\"\n data-ok={cardState.key === \"connected\"}\n data-off={cardState.key === \"disconnected\"}\n aria-hidden=\"true\"\n />\n {cardState.label}\n </span>\n </div>\n\n <div className=\"devic-int-name\" title={integration.name}>\n {integration.name}\n </div>\n\n {integration.description && (\n <div\n className=\"devic-int-description\"\n title={integration.description}\n >\n {integration.description}\n </div>\n )}\n\n <button\n type=\"button\"\n className={`devic-int-btn devic-int-btn-block${\n cardState.key === \"connected\"\n ? \"\"\n : \" devic-int-btn-primary\"\n }`}\n onClick={() => handleConnect(integration)}\n disabled={busy || !!busyApp}\n title={\n cardState.key === \"connected\"\n ? \"Sign in with a different account. The one connected now is replaced.\"\n : undefined\n }\n >\n {busy\n ? \"Waiting…\"\n : cardState.key === \"disconnected\"\n ? \"Connect\"\n : cardState.key === \"reconnect\"\n ? \"Reconnect\"\n : // Not \"Add account\": one account per app is all\n // the assistant can use, and connecting again\n // retires the previous one.\n \"Switch account\"}\n </button>\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-unlink\"\n onClick={() =>\n handleDisconnect(integration.app, account)\n }\n disabled={!!busyApp}\n title=\"Disconnect this account\"\n aria-label={`Disconnect ${integration.name}`}\n >\n ×\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Below the apps, in the same panel: to the end user these are one\n question — what can I plug into this chat — and two panels would\n mean finding out which of them a thing lives in. */}\n {mcp.offered && (\n <div className=\"devic-int-body devic-int-body-secondary\">\n <McpServersSection state={mcp} theme={theme} query={query} />\n </div>\n )}\n\n {showFooter && (\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={() => {\n void refresh(true);\n void mcp.refresh();\n }}\n disabled={loading || mcp.loading || !!busyApp}\n >\n Refresh\n </button>\n </div>\n )}\n\n {/* Its own dialog, over this one: the fields and a provider's\n explanation of where to find a key do not fit in a card of a grid.\n Rendered inside the panel rather than beside it, so that a click on\n its backdrop is stopped here — reaching the modal's own backdrop is\n what used to close everything from under a half-typed key. */}\n {setupPrompt && promptIntegration && (\n <ConnectFieldsForm\n integration={promptIntegration}\n schemes={setupPrompt.schemes}\n initialScheme={setupPrompt.setup?.authScheme}\n onlyFields={setupPrompt.setup?.fields}\n submitting={busyApp === setupPrompt.app}\n error={formError}\n theme={theme}\n onCancel={() => {\n setSetupPrompt(null);\n setFormError(null);\n }}\n onSubmit={(values) => handleConnect(promptIntegration, values)}\n />\n )}\n </div>\n );\n\n if (!dialog) return panel;\n\n return createPortal(\n <div className=\"devic-int-overlay\" onClick={onClose}>\n {panel}\n </div>,\n document.body\n );\n}\n\nexport default IntegrationsPanel;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IntegrationsPanel.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsPanel.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 type { DevicApiError } from \"../../api/client\";\nimport type {\n Integration,\n IntegrationAccount,\n IntegrationAuthScheme,\n IntegrationSetupRequired,\n} from \"../../api/types\";\nimport { isDarkTheme, themeVars, type DevicTheme } from \"../theme\";\nimport { ConnectFieldsForm } from \"./ConnectFieldsForm\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport { useIntegrations, type IntegrationsState } from \"./useIntegrations\";\nimport { McpServersSection } from \"./McpServersSection\";\nimport { useTenantMcp, type TenantMcpState } from \"./useTenantMcp\";\nimport { useTranslations, type Translator } from \"../../i18n\";\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 IntegrationsPanelProps {\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 /** Heading text, and the accessible name of the region. @default \"Connected apps\" */\n title?: string;\n /** Search field placeholder. @default \"Search connected apps\" */\n searchPlaceholder?: string;\n /** Called after an account is connected or disconnected. */\n onChange?: (integrations: Integration[]) => void;\n /**\n * Colours and font. Same names as the drawer's style options, and the drawer\n * passes its own down — a dialog opening in the default light palette over a\n * themed application is the one thing this must not do.\n *\n * Embedded in a page the same applies for a different reason: the panel\n * renders in its own variables and inherits nothing from the surface around\n * it, so a host with its own palette has to say so here.\n */\n theme?: DevicTheme;\n /**\n * Listing loaded elsewhere (see `useIntegrations`). The drawer already has to\n * load it to decide whether its button exists, and passing it down is what\n * keeps opening the modal from asking for the very same thing again.\n */\n state?: IntegrationsState;\n /**\n * MCP listing loaded elsewhere, same reasoning as `state`. When absent this\n * loads its own — and only while on screen, so an assistant that offers no\n * MCP servers costs one refusal and nothing more.\n */\n mcpState?: TenantMcpState;\n /**\n * Whether the panel is on screen. Renders nothing and asks for nothing while\n * false, without dropping what it already knows — which is how the modal\n * closes without paying for the whole listing again on the next open.\n *\n * @default true\n */\n active?: boolean;\n /**\n * The heading, with the plug icon and the title. Turn it off to put the\n * panel under a heading of your own.\n *\n * @default true\n */\n showHeader?: boolean;\n /** The search field. @default true */\n showSearch?: boolean;\n /**\n * The footer, with the privacy note and the Refresh button. Off, nothing else\n * re-reads the listing on demand — keep `state` yourself and call its\n * `refresh` if you drive it from your own control.\n *\n * @default true\n */\n showFooter?: boolean;\n /**\n * Shown as a close button in the header, and called when Escape is pressed.\n * The modal is what usually passes it; embedded, only pass it if the panel\n * is dismissible.\n */\n onClose?: () => void;\n className?: string;\n /**\n * Render inside the modal's backdrop, in a portal on `document.body`.\n *\n * This is what `IntegrationsModal` is, and that component is the one to use\n * — it exists here because both forms share every line of what follows.\n */\n dialog?: boolean;\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\nfunction SearchIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\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 <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\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(\n t: Translator,\n integration: Integration\n): {\n key: \"connected\" | \"reconnect\" | \"disconnected\";\n label: string;\n} {\n if (integration.connected) {\n return { key: \"connected\", label: t(\"Connected\") };\n }\n if (integration.accounts.some((a) => a.needsReconnect)) {\n return { key: \"reconnect\", label: t(\"Needs reconnection\") };\n }\n return { key: \"disconnected\", label: t(\"Not connected\") };\n}\n\nfunction accountLabel(t: Translator, account: IntegrationAccount): string {\n // The status comes from the provider, so it is passed through the dictionary\n // as-is rather than assumed to be one of a known set.\n if (!account.connectedAt) return t(account.status.toLowerCase());\n const when = new Date(account.connectedAt);\n if (Number.isNaN(when.getTime())) return t(account.status.toLowerCase());\n return t(\"connected {date}\", { date: when.toLocaleDateString() });\n}\n\n/**\n * The scheme the server would pick, given no explicit choice: the one asking\n * for least setup.\n *\n * Mirrors the engine's own preference deliberately. It is only used to decide\n * whether to open a popup or a form before asking, and being wrong is\n * recoverable — the form sends its scheme explicitly, and a redirect that\n * arrives anyway is still honoured.\n */\nfunction preferredScheme(\n schemes?: IntegrationAuthScheme[]\n): IntegrationAuthScheme | undefined {\n if (!schemes?.length) return undefined;\n const friction = (s: IntegrationAuthScheme) => (s.composioManaged ? 0 : 1);\n return [...schemes].sort((a, b) => friction(a) - friction(b))[0];\n}\n\nfunction matches(integration: Integration, query: string): boolean {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n return (\n integration.name.toLowerCase().includes(q) ||\n integration.app.toLowerCase().includes(q) ||\n (integration.description ?? \"\").toLowerCase().includes(q)\n );\n}\n\n/**\n * 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 * The same panel serves both shapes it is asked for. `IntegrationsModal` is\n * this with `dialog`, opened from the drawer's header; on its own it drops into\n * a settings page as an ordinary block, which is the form a host wants when\n * connecting apps is a section of their product rather than an interruption of\n * a chat.\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 IntegrationsPanel({\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n title: titleProp,\n searchPlaceholder: searchPlaceholderProp,\n onChange,\n theme,\n state,\n mcpState,\n active = true,\n showHeader = true,\n showSearch = true,\n showFooter = true,\n onClose,\n className,\n dialog = false,\n}: IntegrationsPanelProps): JSX.Element | null {\n const t = useTranslations();\n // The host's own wording wins; with none, the defaults go through the\n // dictionary like every other text in the panel.\n const title = titleProp ?? t(\"Connected apps\");\n const searchPlaceholder = searchPlaceholderProp ?? t(\"Search connected apps\");\n\n // Hooks cannot be skipped, so the fallback is always built and only fetches\n // when nobody handed a listing down.\n const own = useIntegrations({\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n enabled: active && !state,\n });\n const {\n integrations,\n loading,\n error: loadError,\n offered: appsOffered,\n refresh,\n client,\n scope,\n } = state ?? own;\n\n // Same shape as above: always built (hooks cannot be skipped), fetching only\n // when nobody handed a listing down.\n const ownMcp = useTenantMcp({\n assistantId,\n tenantId,\n subtenantId,\n apiKey,\n baseUrl,\n enabled: active && !mcpState,\n });\n const mcp = mcpState ?? ownMcp;\n\n /** Errors from connecting or disconnecting, kept apart from load failures. */\n const [actionError, setActionError] = useState<string | null>(null);\n // An assistant may offer MCP servers and no apps at all, and then the apps\n // endpoint answers \"not for you\" — an ordinary answer, not a failure. Showing\n // it would put a red panel above a section that is working perfectly. The\n // refusal is still surfaced when there is nothing else on offer, because then\n // an empty dialog with no explanation is worse.\n const appsRefusalIsNoise = !appsOffered && mcp.offered;\n const error = actionError ?? (appsRefusalIsNoise ? null : loadError);\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 /** The app whose credentials are being asked for, and every way it can be\n * connected. `setup` is present when the server named what was missing. */\n const [setupPrompt, setSetupPrompt] = useState<{\n app: string;\n schemes: IntegrationAuthScheme[];\n setup?: IntegrationSetupRequired;\n } | null>(null);\n /**\n * How each offered app can be connected, read once the listing arrives.\n *\n * Loaded ahead of the click rather than on it: knowing whether an app needs\n * a browser is what decides between opening a popup and opening a form, and\n * that decision has to be made *inside* the user's gesture — a popup opened\n * after an await is blocked.\n */\n const [authByApp, setAuthByApp] = useState<\n Record<string, IntegrationAuthScheme[]>\n >({});\n /** Failure from the last submit, shown inside the credentials dialog. */\n const [formError, setFormError] = useState<string | null>(null);\n const [query, setQuery] = useState(\"\");\n\n const visible = useMemo(\n () => integrations.filter((i) => matches(i, query)),\n [integrations, query]\n );\n\n // Resolved from the listing rather than stored in the prompt: the dialog\n // stays open across a refresh, and a copy taken when it opened would go\n // stale — showing \"Not connected\" on an app that just connected.\n const promptIntegration = useMemo(\n () => integrations.find((i) => i.app === setupPrompt?.app),\n [integrations, setupPrompt?.app]\n );\n\n // One request per offered app, once the listing is in. They are small,\n // cached server-side, and nothing waits on them: a click that lands before\n // its answer simply falls back to asking the server, as it did before.\n useEffect(() => {\n if (!active || !client || !integrations.length) return;\n let cancelled = false;\n void Promise.all(\n integrations.map(async (integration) => {\n try {\n const { schemes } = await client.getIntegrationAuth(\n integration.app,\n scope\n );\n if (!cancelled && schemes?.length) {\n setAuthByApp((prev) => ({ ...prev, [integration.app]: schemes }));\n }\n } catch {\n // Not knowing is survivable — the click path handles it.\n }\n })\n );\n return () => {\n cancelled = true;\n };\n }, [active, client, integrations, scope]);\n\n // Report the listing without making the caller's identity part of the\n // dependency: an inline arrow would fire this on every render.\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n useEffect(() => {\n if (integrations.length) onChangeRef.current?.(integrations);\n }, [integrations]);\n\n // Reopening starts clean, and re-reads: accounts may have been connected or\n // revoked elsewhere since the last look.\n const wasOpenRef = useRef(false);\n const openedBeforeRef = useRef(false);\n useEffect(() => {\n if (active && !wasOpenRef.current) {\n setBlockedUrl(null);\n setActionError(null);\n setSetupPrompt(null);\n setQuery(\"\");\n // The very first open of an uncontrolled modal is already covered by the\n // hook switching on; asking again here would double every first open.\n if (state || openedBeforeRef.current) void refresh();\n if (mcpState || openedBeforeRef.current) void mcp.refresh();\n openedBeforeRef.current = true;\n }\n wasOpenRef.current = active;\n // `state.refresh` is stable per scope; re-running on every render of the\n // owner is exactly what this must not do.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active]);\n\n // Escape closes — the credentials dialog first, when one is open. Closing\n // everything from under a half-typed key would be its own small disaster.\n //\n // Embedded in a page there is usually nothing to close after that, and the\n // key is left alone rather than swallowed: a panel that is part of the page\n // has no business answering for the whole document.\n useEffect(() => {\n if (!active) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n if (setupPrompt) {\n setSetupPrompt(null);\n setFormError(null);\n return;\n }\n onClose?.();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [active, onClose, setupPrompt]);\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 (!active) 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 }, [active, 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 /**\n * Connects an app, asking for credentials only if it needs them.\n *\n * Optimistic: the request goes out first, and the form appears only when the\n * server answers that something is missing. Most apps take an API key rather\n * than an OAuth round trip, so this is the path that used to fail outright.\n */\n const handleConnect = async (\n integration: Integration,\n values?: { authScheme: string; accountFields: Record<string, string> }\n ) => {\n if (!client || (busyApp && busyApp !== integration.app)) return;\n setActionError(null);\n setBlockedUrl(null);\n\n // Ask before doing anything when the app is known to take credentials and\n // no browser is involved. Connecting first would flash an empty popup and\n // spend a request that can only fail — the user has not been asked yet.\n const known = authByApp[integration.app];\n const scheme = values\n ? known?.find((s) => s.mode === values.authScheme)\n : preferredScheme(known);\n if (!values && scheme && !scheme.redirect && scheme.accountFields.length) {\n setSetupPrompt({ app: integration.app, schemes: known! });\n return;\n }\n\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 — browsers\n // only honour `window.open` inside the gesture that triggered it. Skipped\n // when the scheme in hand needs no browser at all.\n const popup =\n scheme && !scheme.redirect\n ? null\n : window.open(\n \"\",\n \"devic-oauth\",\n \"width=520,height=680,menubar=no,toolbar=no\"\n );\n\n try {\n const { connected, authorizationUrl } = await client.connectIntegration(\n integration.app,\n { ...scope, returnTo, ...values }\n );\n\n // Nothing to authorise: the key the user typed is the account. Close the\n // window that was opened in case it was needed and re-read the listing.\n if (connected || !authorizationUrl) {\n popup?.close();\n setSetupPrompt(null);\n finishConnect();\n return;\n }\n\n pendingRef.current = { app: integration.app, returnTo };\n setSetupPrompt(null);\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 setBusyApp(null);\n const message = err instanceof Error ? err.message : String(err);\n const setup = (err as DevicApiError)?.setupRequired;\n if (setup) {\n await openSetupForm(integration, setup);\n return;\n }\n // While the form is up its own failures belong in it, next to the fields\n // that caused them — a message behind a dialog is a message nobody reads.\n if (setupPrompt?.app === integration.app) setFormError(message);\n else setActionError(message);\n }\n };\n\n /**\n * Opens the credentials form for an app that needs one.\n *\n * `stage: \"app\"` is not the end user's to fix — it means the developer who\n * embedded this widget has not registered an application with the provider\n * yet. Asking a stranger for someone else's client secret would be both\n * useless and a good way to teach them to hand credentials to a form, so it\n * is reported as unavailable instead.\n */\n const openSetupForm = async (\n integration: Integration,\n setup: IntegrationSetupRequired\n ) => {\n if (setup.stage === \"app\") {\n setActionError(\n t(\n \"{app} is not available yet — it still needs to be set up by the app's provider.\",\n { app: integration.name }\n )\n );\n return;\n }\n try {\n const schemes =\n authByApp[integration.app] ??\n (await client!.getIntegrationAuth(integration.app, scope)).schemes;\n if (!schemes.length) throw new Error(setup.message);\n setAuthByApp((prev) => ({ ...prev, [integration.app]: schemes }));\n setFormError(null);\n setSetupPrompt({ app: integration.app, setup, schemes });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : String(err));\n }\n };\n\n const handleDisconnect = async (app: string, account: IntegrationAccount) => {\n if (!client || busyApp) return;\n setBusyApp(app);\n setActionError(null);\n try {\n await client.disconnectIntegration(account.id, scope);\n await refresh(true);\n } catch (err) {\n setActionError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusyApp(null);\n }\n };\n\n if (!active) return null;\n\n const dark = isDarkTheme(theme);\n\n const panel = (\n // The variables go here rather than on the backdrop, because this is the\n // one element both shapes have: as a dialog it renders in a portal that\n // inherits nothing from the drawer that opened it, and embedded it must\n // not inherit the host's either — the colours it draws in are the ones\n // `theme` names, and nothing else.\n <div\n className={[\n \"devic-int-panel\",\n dialog ? \"devic-int-modal\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n style={themeVars(theme)}\n data-dark={dark}\n role={dialog ? \"dialog\" : \"region\"}\n aria-modal={dialog || undefined}\n aria-label={title}\n onClick={dialog ? (e) => e.stopPropagation() : undefined}\n >\n {showHeader && (\n <div className=\"devic-int-header\">\n <h3 className=\"devic-int-title\">\n <PlugIcon />\n {title}\n </h3>\n {onClose && (\n <button\n className=\"devic-int-close\"\n onClick={onClose}\n type=\"button\"\n aria-label={t(\"Close\")}\n >\n ×\n </button>\n )}\n </div>\n )}\n\n {showSearch && (\n <div className=\"devic-int-search\">\n <SearchIcon />\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={searchPlaceholder}\n aria-label={searchPlaceholder}\n autoComplete=\"off\"\n />\n </div>\n )}\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 {t(\"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 {t(\"Open the authorisation page\")}\n </a>{\" \"}\n {t(\"and come back — then use Refresh.\")}\n </div>\n )}\n\n {loading && integrations.length === 0 ? (\n // Silent while the MCP section is what this assistant offers: a\n // spinner labelled \"Loading apps\" over a list of servers describes\n // something that is not happening.\n appsRefusalIsNoise ? null : (\n <div className=\"devic-int-loading\">{t(\"Loading apps…\")}</div>\n )\n ) : integrations.length === 0 ? (\n // Silent when MCP servers are the whole offer: \"no apps available\"\n // over a list of servers reads as a broken panel.\n mcp.offered ? null : (\n <div className=\"devic-int-empty\">\n {t(\"No apps available here yet.\")}\n </div>\n )\n ) : visible.length === 0 ? (\n <div className=\"devic-int-empty\">\n {t(\"No apps match “{query}”.\", { query: query.trim() })}\n </div>\n ) : (\n <div className=\"devic-int-grid\">\n {visible.map((integration) => {\n const cardState = stateOf(t, integration);\n const busy = busyApp === integration.app;\n return (\n <div\n key={integration.app}\n className=\"devic-int-card\"\n data-state={cardState.key}\n >\n <div className=\"devic-int-card-head\">\n <IntegrationLogo integration={integration} />\n <span className=\"devic-int-state\">\n <span\n className=\"devic-int-dot\"\n data-ok={cardState.key === \"connected\"}\n data-off={cardState.key === \"disconnected\"}\n aria-hidden=\"true\"\n />\n {cardState.label}\n </span>\n </div>\n\n <div className=\"devic-int-name\" title={integration.name}>\n {integration.name}\n </div>\n\n {integration.description && (\n <div\n className=\"devic-int-description\"\n title={integration.description}\n >\n {integration.description}\n </div>\n )}\n\n <button\n type=\"button\"\n className={`devic-int-btn devic-int-btn-block${\n cardState.key === \"connected\"\n ? \"\"\n : \" devic-int-btn-primary\"\n }`}\n onClick={() => handleConnect(integration)}\n disabled={busy || !!busyApp}\n title={\n cardState.key === \"connected\"\n ? t(\n \"Sign in with a different account. The one connected now is replaced.\"\n )\n : undefined\n }\n >\n {busy\n ? t(\"Waiting…\")\n : cardState.key === \"disconnected\"\n ? t(\"Connect\")\n : cardState.key === \"reconnect\"\n ? t(\"Reconnect\")\n : // Not \"Add account\": one account per app is all\n // the assistant can use, and connecting again\n // retires the previous one.\n t(\"Switch account\")}\n </button>\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(t, account)}\n {account.needsReconnect && (\n <span className=\"devic-int-account-warn\">\n {\" \"}\n · {t(\"reconnect required\")}\n </span>\n )}\n </span>\n <button\n type=\"button\"\n className=\"devic-int-unlink\"\n onClick={() =>\n handleDisconnect(integration.app, account)\n }\n disabled={!!busyApp}\n title={t(\"Disconnect this account\")}\n aria-label={t(\"Disconnect {app}\", {\n app: integration.name,\n })}\n >\n ×\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Below the apps, in the same panel: to the end user these are one\n question — what can I plug into this chat — and two panels would\n mean finding out which of them a thing lives in. */}\n {mcp.offered && (\n <div className=\"devic-int-body devic-int-body-secondary\">\n <McpServersSection state={mcp} theme={theme} query={query} />\n </div>\n )}\n\n {showFooter && (\n <div className=\"devic-int-footer\">\n <span>\n {t(\"Only you can see and use the accounts you connect here.\")}\n </span>\n <button\n type=\"button\"\n className=\"devic-int-btn devic-int-btn-small\"\n onClick={() => {\n void refresh(true);\n void mcp.refresh();\n }}\n disabled={loading || mcp.loading || !!busyApp}\n >\n {t(\"Refresh\")}\n </button>\n </div>\n )}\n\n {/* Its own dialog, over this one: the fields and a provider's\n explanation of where to find a key do not fit in a card of a grid.\n Rendered inside the panel rather than beside it, so that a click on\n its backdrop is stopped here — reaching the modal's own backdrop is\n what used to close everything from under a half-typed key. */}\n {setupPrompt && promptIntegration && (\n <ConnectFieldsForm\n integration={promptIntegration}\n schemes={setupPrompt.schemes}\n initialScheme={setupPrompt.setup?.authScheme}\n onlyFields={setupPrompt.setup?.fields}\n submitting={busyApp === setupPrompt.app}\n error={formError}\n theme={theme}\n onCancel={() => {\n setSetupPrompt(null);\n setFormError(null);\n }}\n onSubmit={(values) => handleConnect(promptIntegration, values)}\n />\n )}\n </div>\n );\n\n if (!dialog) return panel;\n\n return createPortal(\n <div className=\"devic-int-overlay\" onClick={onClose}>\n {panel}\n </div>,\n document.body\n );\n}\n\nexport default IntegrationsPanel;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -50,5 +50,5 @@ export interface IntegrationsToggleProps {
|
|
|
50
50
|
* undo, which is the whole reason this exists next to the composer rather than
|
|
51
51
|
* inside the apps dialog.
|
|
52
52
|
*/
|
|
53
|
-
export declare function IntegrationsToggle({ state, mcp, disabled, onChange, onManage, label, dark, busy, loading, className, }: IntegrationsToggleProps): JSX.Element | null;
|
|
53
|
+
export declare function IntegrationsToggle({ state, mcp, disabled, onChange, onManage, label: labelProp, dark, busy, loading, className, }: IntegrationsToggleProps): JSX.Element | null;
|
|
54
54
|
export default IntegrationsToggle;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useMemo, useEffect } from 'react';
|
|
3
3
|
import { IntegrationLogo } from './IntegrationLogo.js';
|
|
4
|
+
import { useTranslations } from '../../i18n/useTranslations.js';
|
|
4
5
|
|
|
5
6
|
/** A plug, matching the weight of the attach and mic icons beside it. */
|
|
6
7
|
function PlugIcon() {
|
|
@@ -26,7 +27,9 @@ function McpBadge({ server }) {
|
|
|
26
27
|
* undo, which is the whole reason this exists next to the composer rather than
|
|
27
28
|
* inside the apps dialog.
|
|
28
29
|
*/
|
|
29
|
-
function IntegrationsToggle({ state, mcp, disabled, onChange, onManage, label
|
|
30
|
+
function IntegrationsToggle({ state, mcp, disabled, onChange, onManage, label: labelProp, dark = false, busy = false, loading = false, className = "", }) {
|
|
31
|
+
const t = useTranslations();
|
|
32
|
+
const label = labelProp ?? t("Apps in this chat");
|
|
30
33
|
const [open, setOpen] = useState(false);
|
|
31
34
|
const ref = useRef(null);
|
|
32
35
|
const connected = useMemo(() => state.integrations.filter((i) => i.connected), [state.integrations]);
|
|
@@ -87,16 +90,22 @@ function IntegrationsToggle({ state, mcp, disabled, onChange, onManage, label =
|
|
|
87
90
|
onChange(liveIds.filter((candidate) => next.has(candidate)));
|
|
88
91
|
};
|
|
89
92
|
return (jsxs("div", { className: `devic-int-toggle ${className}`.trim(), "data-dark": dark, ref: ref, children: [jsxs("button", { type: "button", className: "devic-input-btn devic-int-toggle-btn", onClick: () => setOpen((v) => !v), disabled: busy, title: offCount
|
|
90
|
-
?
|
|
91
|
-
: label, "aria-label": label, "aria-expanded": open, "aria-haspopup": "dialog", "data-some-off": offCount > 0 || undefined, children: [jsx(PlugIcon, {}), offCount > 0 && (jsx("span", { className: "devic-int-toggle-badge", "aria-hidden": "true", children: offCount }))] }), open && (jsxs("div", { className: "devic-int-toggle-popover", role: "dialog", "aria-label": label, children: [jsxs("div", { className: "devic-int-toggle-head", children: [jsx("strong", { children: label }),
|
|
93
|
+
? t("{label} — {count} switched off", { label, count: offCount })
|
|
94
|
+
: label, "aria-label": label, "aria-expanded": open, "aria-haspopup": "dialog", "data-some-off": offCount > 0 || undefined, children: [jsx(PlugIcon, {}), offCount > 0 && (jsx("span", { className: "devic-int-toggle-badge", "aria-hidden": "true", children: offCount }))] }), open && (jsxs("div", { className: "devic-int-toggle-popover", role: "dialog", "aria-label": label, children: [jsxs("div", { className: "devic-int-toggle-head", children: [jsx("strong", { children: label }), jsx("span", { children: hasMcp && !hasApps
|
|
95
|
+
? t("Switched off here, a server sits out your next message. It stays connected.")
|
|
96
|
+
: t("Switched off here, an app sits out your next message. It stays connected.") })] }), jsxs("ul", { className: "devic-int-toggle-list", children: [connected.map((integration) => (jsx("li", { children: jsxs("label", { className: "devic-int-toggle-row", children: [jsxs("span", { className: "devic-int-toggle-app", children: [jsx(IntegrationLogo, { integration: integration }), jsx("span", { className: "devic-int-toggle-name", children: integration.name })] }), jsx("input", { type: "checkbox", className: "devic-int-toggle-switch", checked: !off.has(integration.app), onChange: () => flip(integration.app), "aria-label": t("Use {app} in this chat", {
|
|
97
|
+
app: integration.name,
|
|
98
|
+
}) })] }) }, integration.app))), hasMcp && hasApps && (jsx("li", { className: "devic-int-toggle-group", "aria-hidden": "true", children: t("MCP servers") })), mcpServers.map((server) => {
|
|
92
99
|
const id = server.connection.toggleId;
|
|
93
|
-
return (jsx("li", { children: jsxs("label", { className: "devic-int-toggle-row", children: [jsxs("span", { className: "devic-int-toggle-app", children: [jsx(McpBadge, { server: server }), jsx("span", { className: "devic-int-toggle-name", children: server.name })] }), jsx("input", { type: "checkbox", className: "devic-int-toggle-switch", checked: !off.has(id), onChange: () => flip(id), "aria-label":
|
|
100
|
+
return (jsx("li", { children: jsxs("label", { className: "devic-int-toggle-row", children: [jsxs("span", { className: "devic-int-toggle-app", children: [jsx(McpBadge, { server: server }), jsx("span", { className: "devic-int-toggle-name", children: server.name })] }), jsx("input", { type: "checkbox", className: "devic-int-toggle-switch", checked: !off.has(id), onChange: () => flip(id), "aria-label": t("Use {server} in this chat", {
|
|
101
|
+
server: server.name,
|
|
102
|
+
}) })] }) }, id));
|
|
94
103
|
})] }), onManage && (jsx("button", { type: "button", className: "devic-int-toggle-manage", onClick: () => {
|
|
95
104
|
setOpen(false);
|
|
96
105
|
onManage();
|
|
97
106
|
}, children: hasMcp && !hasApps
|
|
98
|
-
? "Manage connected servers"
|
|
99
|
-
: "Manage connected apps" }))] }))] }));
|
|
107
|
+
? t("Manage connected servers")
|
|
108
|
+
: t("Manage connected apps") }))] }))] }));
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
export { IntegrationsToggle };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntegrationsToggle.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsToggle.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type JSX } from \"react\";\nimport type { Integration, TenantMcpServer } from \"../../api/types\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport type { IntegrationsState } from \"./useIntegrations\";\nimport type { TenantMcpState } from \"./useTenantMcp\";\nimport \"./IntegrationsModal.css\";\n\nexport interface IntegrationsToggleProps {\n /** Shared listing (see `useIntegrations`). */\n state: IntegrationsState;\n /**\n * The tenant's own MCP servers, when the assistant offers them (see\n * `useTenantMcp`). Their connected servers are listed under the apps and\n * switch off the same way — a server the end user connected is as much\n * theirs to leave out of a message as an account is.\n */\n mcp?: TenantMcpState;\n /**\n * What is switched off right now. App slugs, and the `toggleId` of a\n * connected MCP server — one list, because that is what the message carries\n * and what the server keeps for the rest of the turn.\n */\n disabled: string[];\n /** Called with the full new list of switched-off entries. */\n onChange: (disabled: string[]) => void;\n /** Opens the connected-apps modal, to connect one more. */\n onManage?: () => void;\n /** Tooltip and accessible name. @default \"Apps in this chat\" */\n label?: string;\n /** Light-on-dark, for a dark composer. */\n dark?: boolean;\n /** Disables the control while a message is in flight. */\n busy?: boolean;\n /**\n * Whether the requests that decide if this control exists are still running.\n *\n * Holds an inert plug in the row rather than letting the attach and mic\n * buttons shift sideways the moment the answer lands.\n */\n loading?: boolean;\n className?: string;\n}\n\n/** A plug, matching the weight of the attach and mic icons beside it. */\nfunction PlugIcon(): JSX.Element {\n return (\n <svg\n width=\"18\"\n height=\"18\"\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=\"M9 2v6\" />\n <path d=\"M15 2v6\" />\n <path d=\"M6 8h12v3a6 6 0 0 1-12 0z\" />\n <path d=\"M12 17v5\" />\n </svg>\n );\n}\n\n/** A server's own logo when it has one, its initial when it does not. */\nfunction McpBadge({ server }: { server: TenantMcpServer }): JSX.Element {\n if (server.logoUrl) {\n return (\n <img\n className=\"devic-int-toggle-mcp-logo\"\n src={server.logoUrl}\n alt=\"\"\n aria-hidden=\"true\"\n />\n );\n }\n return (\n <span\n className=\"devic-int-toggle-mcp-logo devic-int-toggle-mcp-fallback\"\n aria-hidden=\"true\"\n >\n {server.name.slice(0, 1).toUpperCase()}\n </span>\n );\n}\n\n/**\n * Which apps the assistant may reach on the next message.\n *\n * Only apps the end user has actually connected appear: an app they have not\n * set up is not something they can switch off, and listing it here would turn\n * this into a second, worse version of the connect dialog.\n *\n * The choice lives in the caller and travels with each message. Nothing is\n * remembered on the server and nothing is disconnected — switching Gmail off\n * for a question about a spreadsheet must not cost an OAuth round trip to\n * undo, which is the whole reason this exists next to the composer rather than\n * inside the apps dialog.\n */\nexport function IntegrationsToggle({\n state,\n mcp,\n disabled,\n onChange,\n onManage,\n label = \"Apps in this chat\",\n dark = false,\n busy = false,\n loading = false,\n className = \"\",\n}: IntegrationsToggleProps): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n const connected = useMemo(\n () => state.integrations.filter((i) => i.connected),\n [state.integrations]\n );\n\n // Connected and usable: one still waiting for its consent screen, or whose\n // last probe failed, contributes no tools, so switching it off would be\n // switching off nothing. It belongs in the panel that can fix it, not here.\n const mcpServers = useMemo(\n () =>\n (mcp?.servers ?? []).filter(\n (s) => s.connection?.status === \"active\" && !!s.connection.toggleId\n ),\n [mcp?.servers]\n );\n\n // Closing on an outside click and on Escape, the two ways every popover is\n // expected to close. Bound only while open so a drawer full of these costs\n // nothing when they are all shut.\n useEffect(() => {\n if (!open) return;\n const onPointerDown = (e: MouseEvent) => {\n if (!ref.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.stopPropagation();\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown, true);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown, true);\n };\n }, [open]);\n\n // An end user with nothing connected has nothing to switch: the button would\n // open an empty box. It appears the moment they connect their first app — or\n // their first MCP server, which is as switchable as an account is.\n const hasApps = state.offered && connected.length > 0;\n const hasMcp = !!mcp?.offered && mcpServers.length > 0;\n\n if (!hasApps && !hasMcp) {\n if (!loading) return null;\n // Still being decided: hold the slot, inert, so the row does not shift.\n return (\n <div className={`devic-int-toggle ${className}`.trim()} data-dark={dark}>\n <button\n type=\"button\"\n className=\"devic-input-btn devic-int-toggle-btn devic-int-toggle-btn-loading\"\n disabled\n aria-busy=\"true\"\n aria-label={`${label} (loading)`}\n title={label}\n >\n <PlugIcon />\n </button>\n </div>\n );\n }\n\n const off = new Set(disabled);\n\n /** Everything still on offer, so a stale entry can never survive a rewrite. */\n const liveIds = [\n ...connected.map((i) => i.app),\n ...mcpServers.map((s) => s.connection!.toggleId as string),\n ];\n const offCount = liveIds.filter((id) => off.has(id)).length;\n\n const flip = (id: string) => {\n const next = new Set(off);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n // Only what is still on offer is kept: carrying an entry for an app or a\n // server the user has since disconnected would silently switch it off again\n // if they ever connected it back.\n onChange(liveIds.filter((candidate) => next.has(candidate)));\n };\n\n return (\n <div\n className={`devic-int-toggle ${className}`.trim()}\n data-dark={dark}\n ref={ref}\n >\n <button\n type=\"button\"\n className=\"devic-input-btn devic-int-toggle-btn\"\n onClick={() => setOpen((v) => !v)}\n disabled={busy}\n title={\n offCount\n ? `${label} — ${offCount} switched off`\n : label\n }\n aria-label={label}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n data-some-off={offCount > 0 || undefined}\n >\n <PlugIcon />\n {offCount > 0 && (\n <span className=\"devic-int-toggle-badge\" aria-hidden=\"true\">\n {offCount}\n </span>\n )}\n </button>\n\n {open && (\n <div className=\"devic-int-toggle-popover\" role=\"dialog\" aria-label={label}>\n <div className=\"devic-int-toggle-head\">\n <strong>{label}</strong>\n <span>\n Switched off here, {hasMcp && !hasApps ? \"a server\" : \"an app\"}{\" \"}\n sits out your next message. It stays connected.\n </span>\n </div>\n\n <ul className=\"devic-int-toggle-list\">\n {connected.map((integration) => (\n <li key={integration.app}>\n <label className=\"devic-int-toggle-row\">\n <span className=\"devic-int-toggle-app\">\n <IntegrationLogo integration={integration} />\n <span className=\"devic-int-toggle-name\">\n {integration.name}\n </span>\n </span>\n <input\n type=\"checkbox\"\n className=\"devic-int-toggle-switch\"\n checked={!off.has(integration.app)}\n onChange={() => flip(integration.app)}\n aria-label={`Use ${integration.name} in this chat`}\n />\n </label>\n </li>\n ))}\n\n {/* Named apart only when both kinds are present: with servers\n alone the heading would label the entire list. */}\n {hasMcp && hasApps && (\n <li className=\"devic-int-toggle-group\" aria-hidden=\"true\">\n MCP servers\n </li>\n )}\n\n {mcpServers.map((server) => {\n const id = server.connection!.toggleId as string;\n return (\n <li key={id}>\n <label className=\"devic-int-toggle-row\">\n <span className=\"devic-int-toggle-app\">\n <McpBadge server={server} />\n <span className=\"devic-int-toggle-name\">\n {server.name}\n </span>\n </span>\n <input\n type=\"checkbox\"\n className=\"devic-int-toggle-switch\"\n checked={!off.has(id)}\n onChange={() => flip(id)}\n aria-label={`Use ${server.name} in this chat`}\n />\n </label>\n </li>\n );\n })}\n </ul>\n\n {onManage && (\n <button\n type=\"button\"\n className=\"devic-int-toggle-manage\"\n onClick={() => {\n setOpen(false);\n onManage();\n }}\n >\n {hasMcp && !hasApps\n ? \"Manage connected servers\"\n : \"Manage connected apps\"}\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport default IntegrationsToggle;\n"],"names":["_jsxs","_jsx"],"mappings":";;;;AA2CA;AACA,SAAS,QAAQ,GAAA;AACf,IAAA,QACEA,IAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EAAA,aAAA,EACV,MAAM,EAAA,QAAA,EAAA,CAElBC,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,QAAQ,EAAA,CAAG,EACnBA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,SAAS,EAAA,CAAG,EACpBA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,2BAA2B,EAAA,CAAG,EACtCA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,UAAU,EAAA,CAAG,CAAA,EAAA,CACjB;AAEV;AAEA;AACA,SAAS,QAAQ,CAAC,EAAE,MAAM,EAA+B,EAAA;AACvD,IAAA,IAAI,MAAM,CAAC,OAAO,EAAE;AAClB,QAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,2BAA2B,EACrC,GAAG,EAAE,MAAM,CAAC,OAAO,EACnB,GAAG,EAAC,EAAE,iBACM,MAAM,EAAA,CAClB;IAEN;IACA,QACEA,cACE,SAAS,EAAC,yDAAyD,EAAA,aAAA,EACvD,MAAM,EAAA,QAAA,EAEjB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,EAAA,CACjC;AAEX;AAEA;;;;;;;;;;;;AAYG;AACG,SAAU,kBAAkB,CAAC,EACjC,KAAK,EACL,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,KAAK,GAAG,mBAAmB,EAC3B,IAAI,GAAG,KAAK,EACZ,IAAI,GAAG,KAAK,EACZ,OAAO,GAAG,KAAK,EACf,SAAS,GAAG,EAAE,GACU,EAAA;IACxB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;AACvC,IAAA,MAAM,GAAG,GAAG,MAAM,CAAiB,IAAI,CAAC;AAExC,IAAA,MAAM,SAAS,GAAG,OAAO,CACvB,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EACnD,CAAC,KAAK,CAAC,YAAY,CAAC,CACrB;;;;IAKD,MAAM,UAAU,GAAG,OAAO,CACxB,MACE,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,EAAE,MAAM,CACzB,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CACpE,EACH,CAAC,GAAG,EAAE,OAAO,CAAC,CACf;;;;IAKD,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,CAAC,IAAI;YAAE;AACX,QAAA,MAAM,aAAa,GAAG,CAAC,CAAa,KAAI;YACtC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC;gBAAE,OAAO,CAAC,KAAK,CAAC;AAC9D,QAAA,CAAC;AACD,QAAA,MAAM,SAAS,GAAG,CAAC,CAAgB,KAAI;AACrC,YAAA,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ,EAAE;gBACtB,CAAC,CAAC,eAAe,EAAE;gBACnB,OAAO,CAAC,KAAK,CAAC;YAChB;AACF,QAAA,CAAC;AACD,QAAA,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,CAAC;QACrD,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC;AACrD,QAAA,OAAO,MAAK;AACV,YAAA,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,aAAa,CAAC;YACxD,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC;AAC1D,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;;;;IAKV,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;AACrD,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;AAEtD,IAAA,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,EAAE;AACvB,QAAA,IAAI,CAAC,OAAO;AAAE,YAAA,OAAO,IAAI;;AAEzB,QAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,CAAA,iBAAA,EAAoB,SAAS,EAAE,CAAC,IAAI,EAAE,EAAA,WAAA,EAAa,IAAI,EAAA,QAAA,EACrEA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,mEAAmE,EAC7E,QAAQ,EAAA,IAAA,EAAA,WAAA,EACE,MAAM,gBACJ,CAAA,EAAG,KAAK,YAAY,EAChC,KAAK,EAAE,KAAK,YAEZA,GAAA,CAAC,QAAQ,KAAG,EAAA,CACL,EAAA,CACL;IAEV;AAEA,IAAA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC;;AAG7B,IAAA,MAAM,OAAO,GAAG;AACd,QAAA,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC;AAC9B,QAAA,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,UAAW,CAAC,QAAkB,CAAC;KAC3D;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM;AAE3D,IAAA,MAAM,IAAI,GAAG,CAAC,EAAU,KAAI;AAC1B,QAAA,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC;AACzB,QAAA,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAAE,YAAA,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;;AAC5B,YAAA,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;;;;AAIjB,QAAA,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;AAC9D,IAAA,CAAC;IAED,QACED,cACE,SAAS,EAAE,oBAAoB,SAAS,CAAA,CAAE,CAAC,IAAI,EAAE,EAAA,WAAA,EACtC,IAAI,EACf,GAAG,EAAE,GAAG,EAAA,QAAA,EAAA,CAERA,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,sCAAsC,EAChD,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EACjC,QAAQ,EAAE,IAAI,EACd,KAAK,EACH;AACE,sBAAE,CAAA,EAAG,KAAK,CAAA,GAAA,EAAM,QAAQ,CAAA,aAAA;sBACtB,KAAK,EAAA,YAAA,EAEC,KAAK,mBACF,IAAI,EAAA,eAAA,EACL,QAAQ,EAAA,eAAA,EACP,QAAQ,GAAG,CAAC,IAAI,SAAS,EAAA,QAAA,EAAA,CAExCC,GAAA,CAAC,QAAQ,EAAA,EAAA,CAAG,EACX,QAAQ,GAAG,CAAC,KACXA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,wBAAwB,iBAAa,MAAM,EAAA,QAAA,EACxD,QAAQ,EAAA,CACJ,CACR,IACM,EAER,IAAI,KACHD,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,0BAA0B,EAAC,IAAI,EAAC,QAAQ,EAAA,YAAA,EAAa,KAAK,aACvEA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,uBAAuB,EAAA,QAAA,EAAA,CACpCC,0BAAS,KAAK,EAAA,CAAU,EACxBD,IAAA,CAAA,MAAA,EAAA,EAAA,QAAA,EAAA,CAAA,qBAAA,EACsB,MAAM,IAAI,CAAC,OAAO,GAAG,UAAU,GAAG,QAAQ,EAAE,GAAG,EAAA,iDAAA,CAAA,EAAA,CAE9D,CAAA,EAAA,CACH,EAENA,IAAA,CAAA,IAAA,EAAA,EAAI,SAAS,EAAC,uBAAuB,EAAA,QAAA,EAAA,CAClC,SAAS,CAAC,GAAG,CAAC,CAAC,WAAW,MACzBC,GAAA,CAAA,IAAA,EAAA,EAAA,QAAA,EACED,IAAA,CAAA,OAAA,EAAA,EAAO,SAAS,EAAC,sBAAsB,EAAA,QAAA,EAAA,CACrCA,eAAM,SAAS,EAAC,sBAAsB,EAAA,QAAA,EAAA,CACpCC,GAAA,CAAC,eAAe,EAAA,EAAC,WAAW,EAAE,WAAW,EAAA,CAAI,EAC7CA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,uBAAuB,EAAA,QAAA,EACpC,WAAW,CAAC,IAAI,GACZ,CAAA,EAAA,CACF,EACPA,eACE,IAAI,EAAC,UAAU,EACf,SAAS,EAAC,yBAAyB,EACnC,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,EAClC,QAAQ,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAA,YAAA,EACzB,CAAA,IAAA,EAAO,WAAW,CAAC,IAAI,eAAe,EAAA,CAClD,CAAA,EAAA,CACI,IAfD,WAAW,CAAC,GAAG,CAgBnB,CACN,CAAC,EAID,MAAM,IAAI,OAAO,KAChBA,GAAA,CAAA,IAAA,EAAA,EAAI,SAAS,EAAC,wBAAwB,EAAA,aAAA,EAAa,MAAM,EAAA,QAAA,EAAA,aAAA,EAAA,CAEpD,CACN,EAEA,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,KAAI;AACzB,gCAAA,MAAM,EAAE,GAAG,MAAM,CAAC,UAAW,CAAC,QAAkB;AAChD,gCAAA,QACEA,GAAA,CAAA,IAAA,EAAA,EAAA,QAAA,EACED,IAAA,CAAA,OAAA,EAAA,EAAO,SAAS,EAAC,sBAAsB,EAAA,QAAA,EAAA,CACrCA,IAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,sBAAsB,EAAA,QAAA,EAAA,CACpCC,GAAA,CAAC,QAAQ,EAAA,EAAC,MAAM,EAAE,MAAM,EAAA,CAAI,EAC5BA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,uBAAuB,EAAA,QAAA,EACpC,MAAM,CAAC,IAAI,EAAA,CACP,CAAA,EAAA,CACF,EACPA,GAAA,CAAA,OAAA,EAAA,EACE,IAAI,EAAC,UAAU,EACf,SAAS,EAAC,yBAAyB,EACnC,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EACrB,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,EAAA,YAAA,EACZ,OAAO,MAAM,CAAC,IAAI,CAAA,aAAA,CAAe,GAC7C,CAAA,EAAA,CACI,EAAA,EAfD,EAAE,CAgBN;AAET,4BAAA,CAAC,CAAC,CAAA,EAAA,CACC,EAEJ,QAAQ,KACPA,gBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,yBAAyB,EACnC,OAAO,EAAE,MAAK;4BACZ,OAAO,CAAC,KAAK,CAAC;AACd,4BAAA,QAAQ,EAAE;AACZ,wBAAA,CAAC,EAAA,QAAA,EAEA,MAAM,IAAI,CAAC;AACV,8BAAE;8BACA,uBAAuB,EAAA,CACpB,CACV,IACG,CACP,CAAA,EAAA,CACG;AAEV;;"}
|
|
1
|
+
{"version":3,"file":"IntegrationsToggle.js","sources":["../../../../src/components/IntegrationsModal/IntegrationsToggle.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type JSX } from \"react\";\nimport type { Integration, TenantMcpServer } from \"../../api/types\";\nimport { IntegrationLogo } from \"./IntegrationLogo\";\nimport type { IntegrationsState } from \"./useIntegrations\";\nimport type { TenantMcpState } from \"./useTenantMcp\";\nimport { useTranslations } from \"../../i18n\";\nimport \"./IntegrationsModal.css\";\n\nexport interface IntegrationsToggleProps {\n /** Shared listing (see `useIntegrations`). */\n state: IntegrationsState;\n /**\n * The tenant's own MCP servers, when the assistant offers them (see\n * `useTenantMcp`). Their connected servers are listed under the apps and\n * switch off the same way — a server the end user connected is as much\n * theirs to leave out of a message as an account is.\n */\n mcp?: TenantMcpState;\n /**\n * What is switched off right now. App slugs, and the `toggleId` of a\n * connected MCP server — one list, because that is what the message carries\n * and what the server keeps for the rest of the turn.\n */\n disabled: string[];\n /** Called with the full new list of switched-off entries. */\n onChange: (disabled: string[]) => void;\n /** Opens the connected-apps modal, to connect one more. */\n onManage?: () => void;\n /** Tooltip and accessible name. @default \"Apps in this chat\" */\n label?: string;\n /** Light-on-dark, for a dark composer. */\n dark?: boolean;\n /** Disables the control while a message is in flight. */\n busy?: boolean;\n /**\n * Whether the requests that decide if this control exists are still running.\n *\n * Holds an inert plug in the row rather than letting the attach and mic\n * buttons shift sideways the moment the answer lands.\n */\n loading?: boolean;\n className?: string;\n}\n\n/** A plug, matching the weight of the attach and mic icons beside it. */\nfunction PlugIcon(): JSX.Element {\n return (\n <svg\n width=\"18\"\n height=\"18\"\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=\"M9 2v6\" />\n <path d=\"M15 2v6\" />\n <path d=\"M6 8h12v3a6 6 0 0 1-12 0z\" />\n <path d=\"M12 17v5\" />\n </svg>\n );\n}\n\n/** A server's own logo when it has one, its initial when it does not. */\nfunction McpBadge({ server }: { server: TenantMcpServer }): JSX.Element {\n if (server.logoUrl) {\n return (\n <img\n className=\"devic-int-toggle-mcp-logo\"\n src={server.logoUrl}\n alt=\"\"\n aria-hidden=\"true\"\n />\n );\n }\n return (\n <span\n className=\"devic-int-toggle-mcp-logo devic-int-toggle-mcp-fallback\"\n aria-hidden=\"true\"\n >\n {server.name.slice(0, 1).toUpperCase()}\n </span>\n );\n}\n\n/**\n * Which apps the assistant may reach on the next message.\n *\n * Only apps the end user has actually connected appear: an app they have not\n * set up is not something they can switch off, and listing it here would turn\n * this into a second, worse version of the connect dialog.\n *\n * The choice lives in the caller and travels with each message. Nothing is\n * remembered on the server and nothing is disconnected — switching Gmail off\n * for a question about a spreadsheet must not cost an OAuth round trip to\n * undo, which is the whole reason this exists next to the composer rather than\n * inside the apps dialog.\n */\nexport function IntegrationsToggle({\n state,\n mcp,\n disabled,\n onChange,\n onManage,\n label: labelProp,\n dark = false,\n busy = false,\n loading = false,\n className = \"\",\n}: IntegrationsToggleProps): JSX.Element | null {\n const t = useTranslations();\n const label = labelProp ?? t(\"Apps in this chat\");\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n const connected = useMemo(\n () => state.integrations.filter((i) => i.connected),\n [state.integrations]\n );\n\n // Connected and usable: one still waiting for its consent screen, or whose\n // last probe failed, contributes no tools, so switching it off would be\n // switching off nothing. It belongs in the panel that can fix it, not here.\n const mcpServers = useMemo(\n () =>\n (mcp?.servers ?? []).filter(\n (s) => s.connection?.status === \"active\" && !!s.connection.toggleId\n ),\n [mcp?.servers]\n );\n\n // Closing on an outside click and on Escape, the two ways every popover is\n // expected to close. Bound only while open so a drawer full of these costs\n // nothing when they are all shut.\n useEffect(() => {\n if (!open) return;\n const onPointerDown = (e: MouseEvent) => {\n if (!ref.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.stopPropagation();\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown, true);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown, true);\n };\n }, [open]);\n\n // An end user with nothing connected has nothing to switch: the button would\n // open an empty box. It appears the moment they connect their first app — or\n // their first MCP server, which is as switchable as an account is.\n const hasApps = state.offered && connected.length > 0;\n const hasMcp = !!mcp?.offered && mcpServers.length > 0;\n\n if (!hasApps && !hasMcp) {\n if (!loading) return null;\n // Still being decided: hold the slot, inert, so the row does not shift.\n return (\n <div className={`devic-int-toggle ${className}`.trim()} data-dark={dark}>\n <button\n type=\"button\"\n className=\"devic-input-btn devic-int-toggle-btn devic-int-toggle-btn-loading\"\n disabled\n aria-busy=\"true\"\n aria-label={`${label} (loading)`}\n title={label}\n >\n <PlugIcon />\n </button>\n </div>\n );\n }\n\n const off = new Set(disabled);\n\n /** Everything still on offer, so a stale entry can never survive a rewrite. */\n const liveIds = [\n ...connected.map((i) => i.app),\n ...mcpServers.map((s) => s.connection!.toggleId as string),\n ];\n const offCount = liveIds.filter((id) => off.has(id)).length;\n\n const flip = (id: string) => {\n const next = new Set(off);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n // Only what is still on offer is kept: carrying an entry for an app or a\n // server the user has since disconnected would silently switch it off again\n // if they ever connected it back.\n onChange(liveIds.filter((candidate) => next.has(candidate)));\n };\n\n return (\n <div\n className={`devic-int-toggle ${className}`.trim()}\n data-dark={dark}\n ref={ref}\n >\n <button\n type=\"button\"\n className=\"devic-input-btn devic-int-toggle-btn\"\n onClick={() => setOpen((v) => !v)}\n disabled={busy}\n title={\n offCount\n ? t(\"{label} — {count} switched off\", { label, count: offCount })\n : label\n }\n aria-label={label}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n data-some-off={offCount > 0 || undefined}\n >\n <PlugIcon />\n {offCount > 0 && (\n <span className=\"devic-int-toggle-badge\" aria-hidden=\"true\">\n {offCount}\n </span>\n )}\n </button>\n\n {open && (\n <div className=\"devic-int-toggle-popover\" role=\"dialog\" aria-label={label}>\n <div className=\"devic-int-toggle-head\">\n <strong>{label}</strong>\n <span>\n {hasMcp && !hasApps\n ? t(\n \"Switched off here, a server sits out your next message. It stays connected.\"\n )\n : t(\n \"Switched off here, an app sits out your next message. It stays connected.\"\n )}\n </span>\n </div>\n\n <ul className=\"devic-int-toggle-list\">\n {connected.map((integration) => (\n <li key={integration.app}>\n <label className=\"devic-int-toggle-row\">\n <span className=\"devic-int-toggle-app\">\n <IntegrationLogo integration={integration} />\n <span className=\"devic-int-toggle-name\">\n {integration.name}\n </span>\n </span>\n <input\n type=\"checkbox\"\n className=\"devic-int-toggle-switch\"\n checked={!off.has(integration.app)}\n onChange={() => flip(integration.app)}\n aria-label={t(\"Use {app} in this chat\", {\n app: integration.name,\n })}\n />\n </label>\n </li>\n ))}\n\n {/* Named apart only when both kinds are present: with servers\n alone the heading would label the entire list. */}\n {hasMcp && hasApps && (\n <li className=\"devic-int-toggle-group\" aria-hidden=\"true\">\n {t(\"MCP servers\")}\n </li>\n )}\n\n {mcpServers.map((server) => {\n const id = server.connection!.toggleId as string;\n return (\n <li key={id}>\n <label className=\"devic-int-toggle-row\">\n <span className=\"devic-int-toggle-app\">\n <McpBadge server={server} />\n <span className=\"devic-int-toggle-name\">\n {server.name}\n </span>\n </span>\n <input\n type=\"checkbox\"\n className=\"devic-int-toggle-switch\"\n checked={!off.has(id)}\n onChange={() => flip(id)}\n aria-label={t(\"Use {server} in this chat\", {\n server: server.name,\n })}\n />\n </label>\n </li>\n );\n })}\n </ul>\n\n {onManage && (\n <button\n type=\"button\"\n className=\"devic-int-toggle-manage\"\n onClick={() => {\n setOpen(false);\n onManage();\n }}\n >\n {hasMcp && !hasApps\n ? t(\"Manage connected servers\")\n : t(\"Manage connected apps\")}\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport default IntegrationsToggle;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import { useState, useMemo } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { isDarkTheme, themeVars } from '../theme.js';
|
|
5
|
+
import { useTranslations } from '../../i18n/useTranslations.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* What an MCP server asks the end user for.
|
|
@@ -19,6 +20,7 @@ import { isDarkTheme, themeVars } from '../theme.js';
|
|
|
19
20
|
*/
|
|
20
21
|
function McpConnectForm({ server, submitting, error, callbackUrl, needsClientCredentials, theme, onCancel, onSubmit, }) {
|
|
21
22
|
const custom = !server;
|
|
23
|
+
const t = useTranslations();
|
|
22
24
|
const [url, setUrl] = useState(server?.url ?? "");
|
|
23
25
|
const [name, setName] = useState(server?.name ?? "");
|
|
24
26
|
const [key, setKey] = useState("");
|
|
@@ -32,8 +34,8 @@ function McpConnectForm({ server, submitting, error, callbackUrl, needsClientCre
|
|
|
32
34
|
// authenticates, and asking again would only invite a wrong answer.
|
|
33
35
|
const [mode, setMode] = useState(server?.authMode ?? "oauth");
|
|
34
36
|
const headerLabel = server?.headerName
|
|
35
|
-
?
|
|
36
|
-
: "API key";
|
|
37
|
+
? t("{header} value", { header: server.headerName })
|
|
38
|
+
: t("API key");
|
|
37
39
|
const urlInvalid = useMemo(() => custom && touched && !/^https:\/\/.+/i.test(url.trim()), [custom, touched, url]);
|
|
38
40
|
const keyInvalid = touched && mode === "header" && !key.trim();
|
|
39
41
|
const handleSubmit = (event) => {
|
|
@@ -60,17 +62,23 @@ function McpConnectForm({ server, submitting, error, callbackUrl, needsClientCre
|
|
|
60
62
|
},
|
|
61
63
|
});
|
|
62
64
|
};
|
|
63
|
-
const title = custom
|
|
65
|
+
const title = custom
|
|
66
|
+
? t("Add an MCP server")
|
|
67
|
+
: t("Connect {server}", { server: server.name });
|
|
64
68
|
return createPortal(jsx("div", { className: "devic-int-overlay devic-int-overlay-stacked", style: themeVars(theme), "data-dark": isDarkTheme(theme), onClick: onCancel, children: jsxs("form", { className: "devic-int-connect-dialog", role: "dialog", "aria-modal": "true", "aria-label": title, onClick: (e) => e.stopPropagation(), onSubmit: handleSubmit, children: [jsxs("div", { className: "devic-int-connect-header", children: [server?.logoUrl ? (jsx("img", { className: "devic-int-mcp-logo", src: server.logoUrl, alt: "", "aria-hidden": "true" })) : (jsx("span", { className: "devic-int-mcp-logo devic-int-mcp-logo-fallback", children: (server?.name ?? "M").slice(0, 1).toUpperCase() })), jsxs("div", { className: "devic-int-connect-heading", children: [jsx("strong", { children: title }), jsx("span", { children: mode === "oauth"
|
|
65
|
-
? "You'll be sent to the server to finish authorising."
|
|
69
|
+
? t("You'll be sent to the server to finish authorising.")
|
|
66
70
|
: mode === "header"
|
|
67
|
-
? "Your key goes straight to the server — only you can use it."
|
|
68
|
-
: "This server needs no credentials." })] }), jsx("button", { type: "button", className: "devic-int-close", onClick: onCancel, "aria-label": "Close", children: "\u00D7" })] }), jsxs("div", { className: "devic-int-connect-body", children: [error && jsx("div", { className: "devic-int-error", children: error }), custom && (jsxs(Fragment, { children: [jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: "Server URL" }), jsx("input", { autoFocus: true, type: "url", value: url, onChange: (e) => setUrl(e.target.value), placeholder: "https://mcp.example.com/sse", spellCheck: false, autoComplete: "off", "aria-invalid": urlInvalid || undefined, "data-invalid": urlInvalid || undefined }), jsx("small", { className: "devic-int-connect-hint", children: "Must be reachable over https on a public address." })] }), jsxs("label", { className: "devic-int-connect-field", children: [jsxs("span", { className: "devic-int-connect-label", children: ["Name ", jsx("em", { children: "(optional)" })] }), jsx("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "What you want to call it", autoComplete: "off" })] }), jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: "Authentication" }), jsxs("select", { value: mode, onChange: (e) => setMode(e.target.value), children: [jsx("option", { value: "oauth", children: "Sign in (OAuth)" }), jsx("option", { value: "header", children: "API key" }), jsx("option", { value: "none", children: "None" })] })] })] })), mode === "header" && (jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: headerLabel }), jsx("input", { autoFocus: !custom, type: "password", value: key, onChange: (e) => setKey(e.target.value), autoComplete: "new-password", spellCheck: false, "aria-invalid": keyInvalid || undefined, "data-invalid": keyInvalid || undefined })] })), mode === "oauth" && (custom || server?.allowClientCredentials) && (jsxs("div", { className: "devic-int-mcp-advanced", children: [needsClientCredentials && (jsx("div", { className: "devic-int-notice", children: "This server needs its own OAuth application. Register one with it and paste the details below." })), jsxs("button", { type: "button", className: "devic-int-mcp-disclosure", onClick: () => setShowClient((prev) => !prev), "aria-expanded": showClient, children: [showClient ? "▾" : "▸", " Use my own OAuth application"] }), showClient && (jsxs(Fragment, { children: [jsxs("label", { className: "devic-int-connect-field", children: [jsxs("span", { className: "devic-int-connect-label", children: ["Client ID ", jsx("em", { children: "(optional)" })] }), jsx("input", { type: "text", value: clientId, onChange: (e) => setClientId(e.target.value), autoComplete: "off", spellCheck: false })] }), jsxs("label", { className: "devic-int-connect-field", children: [jsxs("span", { className: "devic-int-connect-label", children: ["Client secret ", jsx("em", { children: "(optional)" })] }), jsx("input", { type: "password", value: clientSecret, onChange: (e) => setClientSecret(e.target.value), autoComplete: "new-password", spellCheck: false })] }), callbackUrl && (jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: "Authorised redirect URI" }), jsx("input", { readOnly: true, value: callbackUrl, spellCheck: false }), jsxs("small", { className: "devic-int-connect-hint", children: ["Add this to your OAuth application, exactly as shown.", " ", jsx("button", { type: "button", className: "devic-int-mcp-copy", onClick: () => {
|
|
71
|
+
? t("Your key goes straight to the server — only you can use it.")
|
|
72
|
+
: t("This server needs no credentials.") })] }), jsx("button", { type: "button", className: "devic-int-close", onClick: onCancel, "aria-label": t("Close"), children: "\u00D7" })] }), jsxs("div", { className: "devic-int-connect-body", children: [error && jsx("div", { className: "devic-int-error", children: error }), custom && (jsxs(Fragment, { children: [jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: t("Server URL") }), jsx("input", { autoFocus: true, type: "url", value: url, onChange: (e) => setUrl(e.target.value), placeholder: "https://mcp.example.com/sse", spellCheck: false, autoComplete: "off", "aria-invalid": urlInvalid || undefined, "data-invalid": urlInvalid || undefined }), jsx("small", { className: "devic-int-connect-hint", children: t("Must be reachable over https on a public address.") })] }), jsxs("label", { className: "devic-int-connect-field", children: [jsxs("span", { className: "devic-int-connect-label", children: [t("Name"), " ", jsx("em", { children: t("(optional)") })] }), jsx("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: t("What you want to call it"), autoComplete: "off" })] }), jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: t("Authentication") }), jsxs("select", { value: mode, onChange: (e) => setMode(e.target.value), children: [jsx("option", { value: "oauth", children: t("Sign in (OAuth)") }), jsx("option", { value: "header", children: t("API key") }), jsx("option", { value: "none", children: t("None") })] })] })] })), mode === "header" && (jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: headerLabel }), jsx("input", { autoFocus: !custom, type: "password", value: key, onChange: (e) => setKey(e.target.value), autoComplete: "new-password", spellCheck: false, "aria-invalid": keyInvalid || undefined, "data-invalid": keyInvalid || undefined })] })), mode === "oauth" && (custom || server?.allowClientCredentials) && (jsxs("div", { className: "devic-int-mcp-advanced", children: [needsClientCredentials && (jsx("div", { className: "devic-int-notice", children: t("This server needs its own OAuth application. Register one with it and paste the details below.") })), jsxs("button", { type: "button", className: "devic-int-mcp-disclosure", onClick: () => setShowClient((prev) => !prev), "aria-expanded": showClient, children: [showClient ? "▾" : "▸", " ", t("Use my own OAuth application")] }), showClient && (jsxs(Fragment, { children: [jsxs("label", { className: "devic-int-connect-field", children: [jsxs("span", { className: "devic-int-connect-label", children: [t("Client ID"), " ", jsx("em", { children: t("(optional)") })] }), jsx("input", { type: "text", value: clientId, onChange: (e) => setClientId(e.target.value), autoComplete: "off", spellCheck: false })] }), jsxs("label", { className: "devic-int-connect-field", children: [jsxs("span", { className: "devic-int-connect-label", children: [t("Client secret"), " ", jsx("em", { children: t("(optional)") })] }), jsx("input", { type: "password", value: clientSecret, onChange: (e) => setClientSecret(e.target.value), autoComplete: "new-password", spellCheck: false })] }), callbackUrl && (jsxs("label", { className: "devic-int-connect-field", children: [jsx("span", { className: "devic-int-connect-label", children: t("Authorised redirect URI") }), jsx("input", { readOnly: true, value: callbackUrl, spellCheck: false }), jsxs("small", { className: "devic-int-connect-hint", children: [t("Add this to your OAuth application, exactly as shown."), " ", jsx("button", { type: "button", className: "devic-int-mcp-copy", onClick: () => {
|
|
69
73
|
void navigator.clipboard
|
|
70
74
|
?.writeText(callbackUrl)
|
|
71
75
|
.then(() => setCopied(true))
|
|
72
76
|
.catch(() => undefined);
|
|
73
|
-
}, children: copied ? "Copied" : "Copy" })] })] }))] }))] }))] }), jsxs("div", { className: "devic-int-connect-actions", children: [jsx("button", { type: "button", className: "devic-int-btn", onClick: onCancel, disabled: submitting, children: "Cancel" }), jsx("button", { type: "submit", className: "devic-int-btn devic-int-btn-primary", disabled: submitting, children: submitting
|
|
77
|
+
}, children: copied ? t("Copied") : t("Copy") })] })] }))] }))] }))] }), jsxs("div", { className: "devic-int-connect-actions", children: [jsx("button", { type: "button", className: "devic-int-btn", onClick: onCancel, disabled: submitting, children: t("Cancel") }), jsx("button", { type: "submit", className: "devic-int-btn devic-int-btn-primary", disabled: submitting, children: submitting
|
|
78
|
+
? t("Connecting…")
|
|
79
|
+
: mode === "oauth"
|
|
80
|
+
? t("Continue")
|
|
81
|
+
: t("Connect") })] })] }) }), document.body);
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export { McpConnectForm };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpConnectForm.js","sources":["../../../../src/components/IntegrationsModal/McpConnectForm.tsx"],"sourcesContent":["import { useMemo, useState, type FormEvent, type JSX } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { TenantMcpAuthInput, TenantMcpServer } from \"../../api/types\";\nimport { isDarkTheme, themeVars, type DevicTheme } from \"../theme\";\n\nexport interface McpConnectFormProps {\n /**\n * The server being connected. `null` is the \"add your own\" case, where the\n * url and the name are the end user's to type.\n */\n server: TenantMcpServer | null;\n submitting?: boolean;\n error?: string | null;\n /**\n * The redirect URI the end user must authorise on their own OAuth\n * application, when the server told us it has no dynamic registration.\n */\n callbackUrl?: string;\n /** Open the client-credentials fields already expanded. */\n needsClientCredentials?: boolean;\n theme?: DevicTheme;\n onCancel: () => void;\n onSubmit: (values: {\n url?: string;\n name?: string;\n auth: TenantMcpAuthInput;\n }) => void;\n}\n\n/**\n * What an MCP server asks the end user for.\n *\n * Both ways in end here — a server the developer listed and one the user\n * brings — because the difference between them is only who typed the url.\n * Everything after that is the same question: a key, or a trip to a consent\n * screen.\n *\n * A dialog rather than something inside a card: the OAuth-application fields\n * carry a redirect URI the user has to copy somewhere else, which does not fit\n * in a column of a grid without becoming a stack of clipped text.\n *\n * Values go straight into the connection and are never kept here.\n */\nexport function McpConnectForm({\n server,\n submitting,\n error,\n callbackUrl,\n needsClientCredentials,\n theme,\n onCancel,\n onSubmit,\n}: McpConnectFormProps): JSX.Element {\n const custom = !server;\n const [url, setUrl] = useState(server?.url ?? \"\");\n const [name, setName] = useState(server?.name ?? \"\");\n const [key, setKey] = useState(\"\");\n const [clientId, setClientId] = useState(\"\");\n const [clientSecret, setClientSecret] = useState(\"\");\n const [showClient, setShowClient] = useState(!!needsClientCredentials);\n const [copied, setCopied] = useState(false);\n const [touched, setTouched] = useState(false);\n\n // A server the user brings could be anything, so the form offers both ways\n // and lets them say. One the developer listed already declared how it\n // authenticates, and asking again would only invite a wrong answer.\n const [mode, setMode] = useState<TenantMcpAuthInput[\"mode\"]>(\n server?.authMode ?? \"oauth\"\n );\n\n const headerLabel = server?.headerName\n ? `${server.headerName} value`\n : \"API key\";\n\n const urlInvalid = useMemo(\n () => custom && touched && !/^https:\\/\\/.+/i.test(url.trim()),\n [custom, touched, url]\n );\n const keyInvalid = touched && mode === \"header\" && !key.trim();\n\n const handleSubmit = (event: FormEvent) => {\n event.preventDefault();\n setTouched(true);\n if (submitting) return;\n if (custom && !/^https:\\/\\/.+/i.test(url.trim())) return;\n if (mode === \"header\" && !key.trim()) return;\n\n onSubmit({\n url: custom ? url.trim() : undefined,\n name: name.trim() || undefined,\n auth: {\n mode,\n headerValue: mode === \"header\" ? key.trim() : undefined,\n upstreamOAuth:\n mode === \"oauth\" && clientId.trim()\n ? {\n clientId: clientId.trim(),\n clientSecret: clientSecret.trim() || undefined,\n }\n : undefined,\n },\n });\n };\n\n const title = custom ? \"Add an MCP server\" : `Connect ${server.name}`;\n\n return createPortal(\n <div\n className=\"devic-int-overlay devic-int-overlay-stacked\"\n style={themeVars(theme)}\n data-dark={isDarkTheme(theme)}\n onClick={onCancel}\n >\n <form\n className=\"devic-int-connect-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onClick={(e) => e.stopPropagation()}\n onSubmit={handleSubmit}\n >\n <div className=\"devic-int-connect-header\">\n {server?.logoUrl ? (\n <img\n className=\"devic-int-mcp-logo\"\n src={server.logoUrl}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : (\n <span className=\"devic-int-mcp-logo devic-int-mcp-logo-fallback\">\n {(server?.name ?? \"M\").slice(0, 1).toUpperCase()}\n </span>\n )}\n <div className=\"devic-int-connect-heading\">\n <strong>{title}</strong>\n <span>\n {mode === \"oauth\"\n ? \"You'll be sent to the server to finish authorising.\"\n : mode === \"header\"\n ? \"Your key goes straight to the server — only you can use it.\"\n : \"This server needs no credentials.\"}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"devic-int-close\"\n onClick={onCancel}\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-int-connect-body\">\n {error && <div className=\"devic-int-error\">{error}</div>}\n\n {custom && (\n <>\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">Server URL</span>\n <input\n autoFocus\n type=\"url\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder=\"https://mcp.example.com/sse\"\n spellCheck={false}\n autoComplete=\"off\"\n aria-invalid={urlInvalid || undefined}\n data-invalid={urlInvalid || undefined}\n />\n <small className=\"devic-int-connect-hint\">\n Must be reachable over https on a public address.\n </small>\n </label>\n\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n Name <em>(optional)</em>\n </span>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"What you want to call it\"\n autoComplete=\"off\"\n />\n </label>\n\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">Authentication</span>\n <select\n value={mode}\n onChange={(e) =>\n setMode(e.target.value as TenantMcpAuthInput[\"mode\"])\n }\n >\n <option value=\"oauth\">Sign in (OAuth)</option>\n <option value=\"header\">API key</option>\n <option value=\"none\">None</option>\n </select>\n </label>\n </>\n )}\n\n {mode === \"header\" && (\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">{headerLabel}</span>\n <input\n autoFocus={!custom}\n type=\"password\"\n value={key}\n onChange={(e) => setKey(e.target.value)}\n autoComplete=\"new-password\"\n spellCheck={false}\n aria-invalid={keyInvalid || undefined}\n data-invalid={keyInvalid || undefined}\n />\n </label>\n )}\n\n {mode === \"oauth\" && (custom || server?.allowClientCredentials) && (\n <div className=\"devic-int-mcp-advanced\">\n {/* Not a detail to bury: a server whose authorization server has\n no dynamic client registration cannot be connected at all\n without these, and the message that says so arrives only\n after the first attempt. */}\n {needsClientCredentials && (\n <div className=\"devic-int-notice\">\n This server needs its own OAuth application. Register one with\n it and paste the details below.\n </div>\n )}\n <button\n type=\"button\"\n className=\"devic-int-mcp-disclosure\"\n onClick={() => setShowClient((prev) => !prev)}\n aria-expanded={showClient}\n >\n {showClient ? \"▾\" : \"▸\"} Use my own OAuth application\n </button>\n\n {showClient && (\n <>\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n Client ID <em>(optional)</em>\n </span>\n <input\n type=\"text\"\n value={clientId}\n onChange={(e) => setClientId(e.target.value)}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </label>\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n Client secret <em>(optional)</em>\n </span>\n <input\n type=\"password\"\n value={clientSecret}\n onChange={(e) => setClientSecret(e.target.value)}\n autoComplete=\"new-password\"\n spellCheck={false}\n />\n </label>\n {callbackUrl && (\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n Authorised redirect URI\n </span>\n <input readOnly value={callbackUrl} spellCheck={false} />\n <small className=\"devic-int-connect-hint\">\n Add this to your OAuth application, exactly as shown.{\" \"}\n <button\n type=\"button\"\n className=\"devic-int-mcp-copy\"\n onClick={() => {\n void navigator.clipboard\n ?.writeText(callbackUrl)\n .then(() => setCopied(true))\n .catch(() => undefined);\n }}\n >\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </small>\n </label>\n )}\n </>\n )}\n </div>\n )}\n </div>\n\n <div className=\"devic-int-connect-actions\">\n <button\n type=\"button\"\n className=\"devic-int-btn\"\n onClick={onCancel}\n disabled={submitting}\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n className=\"devic-int-btn devic-int-btn-primary\"\n disabled={submitting}\n >\n {submitting ? \"Connecting…\" : mode === \"oauth\" ? \"Continue\" : \"Connect\"}\n </button>\n </div>\n </form>\n </div>,\n document.body\n );\n}\n\nexport default McpConnectForm;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"McpConnectForm.js","sources":["../../../../src/components/IntegrationsModal/McpConnectForm.tsx"],"sourcesContent":["import { useMemo, useState, type FormEvent, type JSX } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { TenantMcpAuthInput, TenantMcpServer } from \"../../api/types\";\nimport { isDarkTheme, themeVars, type DevicTheme } from \"../theme\";\nimport { useTranslations } from \"../../i18n\";\n\nexport interface McpConnectFormProps {\n /**\n * The server being connected. `null` is the \"add your own\" case, where the\n * url and the name are the end user's to type.\n */\n server: TenantMcpServer | null;\n submitting?: boolean;\n error?: string | null;\n /**\n * The redirect URI the end user must authorise on their own OAuth\n * application, when the server told us it has no dynamic registration.\n */\n callbackUrl?: string;\n /** Open the client-credentials fields already expanded. */\n needsClientCredentials?: boolean;\n theme?: DevicTheme;\n onCancel: () => void;\n onSubmit: (values: {\n url?: string;\n name?: string;\n auth: TenantMcpAuthInput;\n }) => void;\n}\n\n/**\n * What an MCP server asks the end user for.\n *\n * Both ways in end here — a server the developer listed and one the user\n * brings — because the difference between them is only who typed the url.\n * Everything after that is the same question: a key, or a trip to a consent\n * screen.\n *\n * A dialog rather than something inside a card: the OAuth-application fields\n * carry a redirect URI the user has to copy somewhere else, which does not fit\n * in a column of a grid without becoming a stack of clipped text.\n *\n * Values go straight into the connection and are never kept here.\n */\nexport function McpConnectForm({\n server,\n submitting,\n error,\n callbackUrl,\n needsClientCredentials,\n theme,\n onCancel,\n onSubmit,\n}: McpConnectFormProps): JSX.Element {\n const custom = !server;\n const t = useTranslations();\n const [url, setUrl] = useState(server?.url ?? \"\");\n const [name, setName] = useState(server?.name ?? \"\");\n const [key, setKey] = useState(\"\");\n const [clientId, setClientId] = useState(\"\");\n const [clientSecret, setClientSecret] = useState(\"\");\n const [showClient, setShowClient] = useState(!!needsClientCredentials);\n const [copied, setCopied] = useState(false);\n const [touched, setTouched] = useState(false);\n\n // A server the user brings could be anything, so the form offers both ways\n // and lets them say. One the developer listed already declared how it\n // authenticates, and asking again would only invite a wrong answer.\n const [mode, setMode] = useState<TenantMcpAuthInput[\"mode\"]>(\n server?.authMode ?? \"oauth\"\n );\n\n const headerLabel = server?.headerName\n ? t(\"{header} value\", { header: server.headerName })\n : t(\"API key\");\n\n const urlInvalid = useMemo(\n () => custom && touched && !/^https:\\/\\/.+/i.test(url.trim()),\n [custom, touched, url]\n );\n const keyInvalid = touched && mode === \"header\" && !key.trim();\n\n const handleSubmit = (event: FormEvent) => {\n event.preventDefault();\n setTouched(true);\n if (submitting) return;\n if (custom && !/^https:\\/\\/.+/i.test(url.trim())) return;\n if (mode === \"header\" && !key.trim()) return;\n\n onSubmit({\n url: custom ? url.trim() : undefined,\n name: name.trim() || undefined,\n auth: {\n mode,\n headerValue: mode === \"header\" ? key.trim() : undefined,\n upstreamOAuth:\n mode === \"oauth\" && clientId.trim()\n ? {\n clientId: clientId.trim(),\n clientSecret: clientSecret.trim() || undefined,\n }\n : undefined,\n },\n });\n };\n\n const title = custom\n ? t(\"Add an MCP server\")\n : t(\"Connect {server}\", { server: server.name });\n\n return createPortal(\n <div\n className=\"devic-int-overlay devic-int-overlay-stacked\"\n style={themeVars(theme)}\n data-dark={isDarkTheme(theme)}\n onClick={onCancel}\n >\n <form\n className=\"devic-int-connect-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onClick={(e) => e.stopPropagation()}\n onSubmit={handleSubmit}\n >\n <div className=\"devic-int-connect-header\">\n {server?.logoUrl ? (\n <img\n className=\"devic-int-mcp-logo\"\n src={server.logoUrl}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : (\n <span className=\"devic-int-mcp-logo devic-int-mcp-logo-fallback\">\n {(server?.name ?? \"M\").slice(0, 1).toUpperCase()}\n </span>\n )}\n <div className=\"devic-int-connect-heading\">\n <strong>{title}</strong>\n <span>\n {mode === \"oauth\"\n ? t(\"You'll be sent to the server to finish authorising.\")\n : mode === \"header\"\n ? t(\n \"Your key goes straight to the server — only you can use it.\"\n )\n : t(\"This server needs no credentials.\")}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"devic-int-close\"\n onClick={onCancel}\n aria-label={t(\"Close\")}\n >\n ×\n </button>\n </div>\n\n <div className=\"devic-int-connect-body\">\n {error && <div className=\"devic-int-error\">{error}</div>}\n\n {custom && (\n <>\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Server URL\")}\n </span>\n <input\n autoFocus\n type=\"url\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder=\"https://mcp.example.com/sse\"\n spellCheck={false}\n autoComplete=\"off\"\n aria-invalid={urlInvalid || undefined}\n data-invalid={urlInvalid || undefined}\n />\n <small className=\"devic-int-connect-hint\">\n {t(\"Must be reachable over https on a public address.\")}\n </small>\n </label>\n\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Name\")} <em>{t(\"(optional)\")}</em>\n </span>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={t(\"What you want to call it\")}\n autoComplete=\"off\"\n />\n </label>\n\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Authentication\")}\n </span>\n <select\n value={mode}\n onChange={(e) =>\n setMode(e.target.value as TenantMcpAuthInput[\"mode\"])\n }\n >\n <option value=\"oauth\">{t(\"Sign in (OAuth)\")}</option>\n <option value=\"header\">{t(\"API key\")}</option>\n <option value=\"none\">{t(\"None\")}</option>\n </select>\n </label>\n </>\n )}\n\n {mode === \"header\" && (\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">{headerLabel}</span>\n <input\n autoFocus={!custom}\n type=\"password\"\n value={key}\n onChange={(e) => setKey(e.target.value)}\n autoComplete=\"new-password\"\n spellCheck={false}\n aria-invalid={keyInvalid || undefined}\n data-invalid={keyInvalid || undefined}\n />\n </label>\n )}\n\n {mode === \"oauth\" && (custom || server?.allowClientCredentials) && (\n <div className=\"devic-int-mcp-advanced\">\n {/* Not a detail to bury: a server whose authorization server has\n no dynamic client registration cannot be connected at all\n without these, and the message that says so arrives only\n after the first attempt. */}\n {needsClientCredentials && (\n <div className=\"devic-int-notice\">\n {t(\n \"This server needs its own OAuth application. Register one with it and paste the details below.\"\n )}\n </div>\n )}\n <button\n type=\"button\"\n className=\"devic-int-mcp-disclosure\"\n onClick={() => setShowClient((prev) => !prev)}\n aria-expanded={showClient}\n >\n {showClient ? \"▾\" : \"▸\"} {t(\"Use my own OAuth application\")}\n </button>\n\n {showClient && (\n <>\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Client ID\")} <em>{t(\"(optional)\")}</em>\n </span>\n <input\n type=\"text\"\n value={clientId}\n onChange={(e) => setClientId(e.target.value)}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </label>\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Client secret\")} <em>{t(\"(optional)\")}</em>\n </span>\n <input\n type=\"password\"\n value={clientSecret}\n onChange={(e) => setClientSecret(e.target.value)}\n autoComplete=\"new-password\"\n spellCheck={false}\n />\n </label>\n {callbackUrl && (\n <label className=\"devic-int-connect-field\">\n <span className=\"devic-int-connect-label\">\n {t(\"Authorised redirect URI\")}\n </span>\n <input readOnly value={callbackUrl} spellCheck={false} />\n <small className=\"devic-int-connect-hint\">\n {t(\n \"Add this to your OAuth application, exactly as shown.\"\n )}{\" \"}\n <button\n type=\"button\"\n className=\"devic-int-mcp-copy\"\n onClick={() => {\n void navigator.clipboard\n ?.writeText(callbackUrl)\n .then(() => setCopied(true))\n .catch(() => undefined);\n }}\n >\n {copied ? t(\"Copied\") : t(\"Copy\")}\n </button>\n </small>\n </label>\n )}\n </>\n )}\n </div>\n )}\n </div>\n\n <div className=\"devic-int-connect-actions\">\n <button\n type=\"button\"\n className=\"devic-int-btn\"\n onClick={onCancel}\n disabled={submitting}\n >\n {t(\"Cancel\")}\n </button>\n <button\n type=\"submit\"\n className=\"devic-int-btn devic-int-btn-primary\"\n disabled={submitting}\n >\n {submitting\n ? t(\"Connecting…\")\n : mode === \"oauth\"\n ? t(\"Continue\")\n : t(\"Connect\")}\n </button>\n </div>\n </form>\n </div>,\n document.body\n );\n}\n\nexport default McpConnectForm;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|