From e8b5b565a9b45dc2480b1e892c552b24a9498c93 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:19:49 +1100 Subject: [PATCH] chore(design-system): centralise MCP client assets (#43730) ## What kind of change does this PR introduce? Chore ## What is the current behavior? - We use the MCP client connect dialog in two places: Studio and Docs - We duplicate image assets for each client in each of those two places ## What is the new behavior? - Centralised assets - Consolidation and simplification as a result of it all being a single source-of-truth now ## To test - [ ] Everything works as it did before across both Studio and [Docs](https://supabase.com/docs/guides/getting-started/mcp) - [ ] All MCP client images load as expected --- apps/docs/features/ui/McpConfigPanel.tsx | 1 - .../img/mcp-clients/antigravity-icon.svg | 98 ----------------- .../interfaces/Connect/McpTabContent.tsx | 2 - .../content/steps/mcp/cursor/content.tsx | 3 +- .../img/mcp-clients/antigravity-icon.svg | 98 ----------------- .../public/img/mcp-clients/claude-icon.svg | 3 - .../public/img/mcp-clients/cursor-icon.svg | 1 - .../img/mcp-clients/factory-icon-dark.svg | 8 -- .../public/img/mcp-clients/factory-icon.svg | 1 - .../img/mcp-clients/gemini-cli-icon.svg | 100 ------------------ .../img/mcp-clients/goose-icon-dark.svg | 1 - .../public/img/mcp-clients/goose-icon.svg | 1 - .../public/img/mcp-clients/kiro-icon.svg | 11 -- .../img/mcp-clients/openai-icon-dark.svg | 3 - .../public/img/mcp-clients/openai-icon.svg | 3 - .../img/mcp-clients/opencode-icon-dark.svg | 1 - .../public/img/mcp-clients/opencode-icon.svg | 1 - .../public/img/mcp-clients/vscode-icon.svg | 41 ------- .../img/mcp-clients/windsurf-icon-dark.svg | 3 - .../public/img/mcp-clients/windsurf-icon.svg | 3 - .../src/McpUrlBuilder/McpConfigPanel.tsx | 4 - .../McpUrlBuilder/assets/antigravity-icon.svg | 1 + .../src/McpUrlBuilder/assets}/claude-icon.svg | 0 .../src/McpUrlBuilder/assets}/cursor-icon.svg | 0 .../assets}/factory-icon-dark.svg | 0 .../McpUrlBuilder/assets}/factory-icon.svg | 0 .../McpUrlBuilder/assets}/gemini-cli-icon.svg | 0 .../McpUrlBuilder/assets}/goose-icon-dark.svg | 0 .../src/McpUrlBuilder/assets}/goose-icon.svg | 0 .../src/McpUrlBuilder/assets}/kiro-icon.svg | 0 .../assets}/openai-icon-dark.svg | 0 .../src/McpUrlBuilder/assets}/openai-icon.svg | 0 .../assets}/opencode-icon-dark.svg | 0 .../McpUrlBuilder/assets}/opencode-icon.svg | 0 .../src/McpUrlBuilder/assets}/vscode-icon.svg | 0 .../assets}/windsurf-icon-dark.svg | 0 .../McpUrlBuilder/assets}/windsurf-icon.svg | 0 .../components/ClientSelectDropdown.tsx | 6 -- .../components/ConnectionIcon.tsx | 6 -- .../components/McpConfigurationDisplay.tsx | 3 - .../McpUrlBuilder/utils/getMcpButtonData.ts | 4 - .../McpUrlBuilder/utils/getMcpIconSrc.test.ts | 50 +++++++++ .../src/McpUrlBuilder/utils/getMcpIconSrc.ts | 14 +-- .../src/McpUrlBuilder/utils/mcpIconAssets.ts | 52 +++++++++ packages/ui-patterns/src/types/assets.d.ts | 5 + 45 files changed, 114 insertions(+), 414 deletions(-) delete mode 100644 apps/docs/public/img/mcp-clients/antigravity-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/antigravity-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/claude-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/cursor-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/factory-icon-dark.svg delete mode 100644 apps/studio/public/img/mcp-clients/factory-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/gemini-cli-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/goose-icon-dark.svg delete mode 100644 apps/studio/public/img/mcp-clients/goose-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/kiro-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/openai-icon-dark.svg delete mode 100644 apps/studio/public/img/mcp-clients/openai-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/opencode-icon-dark.svg delete mode 100644 apps/studio/public/img/mcp-clients/opencode-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/vscode-icon.svg delete mode 100644 apps/studio/public/img/mcp-clients/windsurf-icon-dark.svg delete mode 100644 apps/studio/public/img/mcp-clients/windsurf-icon.svg create mode 100644 packages/ui-patterns/src/McpUrlBuilder/assets/antigravity-icon.svg rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/claude-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/cursor-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/factory-icon-dark.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/factory-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/gemini-cli-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/goose-icon-dark.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/goose-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/kiro-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/openai-icon-dark.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/openai-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/opencode-icon-dark.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/opencode-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/vscode-icon.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/windsurf-icon-dark.svg (100%) rename {apps/docs/public/img/mcp-clients => packages/ui-patterns/src/McpUrlBuilder/assets}/windsurf-icon.svg (100%) create mode 100644 packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.test.ts create mode 100644 packages/ui-patterns/src/McpUrlBuilder/utils/mcpIconAssets.ts diff --git a/apps/docs/features/ui/McpConfigPanel.tsx b/apps/docs/features/ui/McpConfigPanel.tsx index 3376cd953e3..e3d29032384 100644 --- a/apps/docs/features/ui/McpConfigPanel.tsx +++ b/apps/docs/features/ui/McpConfigPanel.tsx @@ -322,7 +322,6 @@ export function McpConfigPanel() { : 'Project selection is only available for the hosted platform.'}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/studio/components/interfaces/Connect/McpTabContent.tsx b/apps/studio/components/interfaces/Connect/McpTabContent.tsx index b41b54e03c2..f5b0fdd28e1 100644 --- a/apps/studio/components/interfaces/Connect/McpTabContent.tsx +++ b/apps/studio/components/interfaces/Connect/McpTabContent.tsx @@ -1,6 +1,5 @@ import { IS_PLATFORM, useParams } from 'common' import Panel from 'components/ui/Panel' -import { BASE_PATH } from 'lib/constants' import { useTrack } from 'lib/telemetry/track' import { useTheme } from 'next-themes' import { useMemo, useState } from 'react' @@ -64,7 +63,6 @@ const McpTabContentInnerLoaded = ({ return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/studio/public/img/mcp-clients/claude-icon.svg b/apps/studio/public/img/mcp-clients/claude-icon.svg deleted file mode 100644 index 7b9c0252ed7..00000000000 --- a/apps/studio/public/img/mcp-clients/claude-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/studio/public/img/mcp-clients/cursor-icon.svg b/apps/studio/public/img/mcp-clients/cursor-icon.svg deleted file mode 100644 index 48f85eb4d5a..00000000000 --- a/apps/studio/public/img/mcp-clients/cursor-icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/studio/public/img/mcp-clients/factory-icon-dark.svg b/apps/studio/public/img/mcp-clients/factory-icon-dark.svg deleted file mode 100644 index 16b99b68b64..00000000000 --- a/apps/studio/public/img/mcp-clients/factory-icon-dark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/apps/studio/public/img/mcp-clients/factory-icon.svg b/apps/studio/public/img/mcp-clients/factory-icon.svg deleted file mode 100644 index 445aa4518c9..00000000000 --- a/apps/studio/public/img/mcp-clients/factory-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/studio/public/img/mcp-clients/gemini-cli-icon.svg b/apps/studio/public/img/mcp-clients/gemini-cli-icon.svg deleted file mode 100644 index 7e457a00a77..00000000000 --- a/apps/studio/public/img/mcp-clients/gemini-cli-icon.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/studio/public/img/mcp-clients/goose-icon-dark.svg b/apps/studio/public/img/mcp-clients/goose-icon-dark.svg deleted file mode 100644 index e37b75328ed..00000000000 --- a/apps/studio/public/img/mcp-clients/goose-icon-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/studio/public/img/mcp-clients/goose-icon.svg b/apps/studio/public/img/mcp-clients/goose-icon.svg deleted file mode 100644 index 1808de11575..00000000000 --- a/apps/studio/public/img/mcp-clients/goose-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/studio/public/img/mcp-clients/kiro-icon.svg b/apps/studio/public/img/mcp-clients/kiro-icon.svg deleted file mode 100644 index ef4abd2d85d..00000000000 --- a/apps/studio/public/img/mcp-clients/kiro-icon.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/apps/studio/public/img/mcp-clients/openai-icon-dark.svg b/apps/studio/public/img/mcp-clients/openai-icon-dark.svg deleted file mode 100644 index 73c2f3fc76d..00000000000 --- a/apps/studio/public/img/mcp-clients/openai-icon-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/studio/public/img/mcp-clients/openai-icon.svg b/apps/studio/public/img/mcp-clients/openai-icon.svg deleted file mode 100644 index 316440a338d..00000000000 --- a/apps/studio/public/img/mcp-clients/openai-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/studio/public/img/mcp-clients/opencode-icon-dark.svg b/apps/studio/public/img/mcp-clients/opencode-icon-dark.svg deleted file mode 100644 index b79c7332e20..00000000000 --- a/apps/studio/public/img/mcp-clients/opencode-icon-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/studio/public/img/mcp-clients/opencode-icon.svg b/apps/studio/public/img/mcp-clients/opencode-icon.svg deleted file mode 100644 index b79140a5070..00000000000 --- a/apps/studio/public/img/mcp-clients/opencode-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/studio/public/img/mcp-clients/vscode-icon.svg b/apps/studio/public/img/mcp-clients/vscode-icon.svg deleted file mode 100644 index c453e633f34..00000000000 --- a/apps/studio/public/img/mcp-clients/vscode-icon.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/studio/public/img/mcp-clients/windsurf-icon-dark.svg b/apps/studio/public/img/mcp-clients/windsurf-icon-dark.svg deleted file mode 100644 index 2e4e4e4921d..00000000000 --- a/apps/studio/public/img/mcp-clients/windsurf-icon-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/studio/public/img/mcp-clients/windsurf-icon.svg b/apps/studio/public/img/mcp-clients/windsurf-icon.svg deleted file mode 100644 index 386f8c035dc..00000000000 --- a/apps/studio/public/img/mcp-clients/windsurf-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/ui-patterns/src/McpUrlBuilder/McpConfigPanel.tsx b/packages/ui-patterns/src/McpUrlBuilder/McpConfigPanel.tsx index 7ae5e4767aa..ed0c2193ba9 100644 --- a/packages/ui-patterns/src/McpUrlBuilder/McpConfigPanel.tsx +++ b/packages/ui-patterns/src/McpUrlBuilder/McpConfigPanel.tsx @@ -12,7 +12,6 @@ import type { McpClient, McpOnCopyCallback } from './types' import { getMcpUrl } from './utils/getMcpUrl' export interface McpConfigPanelProps { - basePath: string baseUrl?: string projectRef?: string initialSelectedClient?: McpClient @@ -26,7 +25,6 @@ export interface McpConfigPanelProps { } export function McpConfigPanel({ - basePath, projectRef, initialSelectedClient, onClientSelect, @@ -109,7 +107,6 @@ export function McpConfigPanel({ clients={MCP_CLIENTS} selectedClient={selectedClient} onClientChange={handleClientChange} - basePath={basePath} theme={theme} />

@@ -124,7 +121,6 @@ export function McpConfigPanel({ diff --git a/apps/docs/public/img/mcp-clients/claude-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/claude-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/claude-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/claude-icon.svg diff --git a/apps/docs/public/img/mcp-clients/cursor-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/cursor-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/cursor-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/cursor-icon.svg diff --git a/apps/docs/public/img/mcp-clients/factory-icon-dark.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/factory-icon-dark.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/factory-icon-dark.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/factory-icon-dark.svg diff --git a/apps/docs/public/img/mcp-clients/factory-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/factory-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/factory-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/factory-icon.svg diff --git a/apps/docs/public/img/mcp-clients/gemini-cli-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/gemini-cli-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/gemini-cli-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/gemini-cli-icon.svg diff --git a/apps/docs/public/img/mcp-clients/goose-icon-dark.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/goose-icon-dark.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/goose-icon-dark.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/goose-icon-dark.svg diff --git a/apps/docs/public/img/mcp-clients/goose-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/goose-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/goose-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/goose-icon.svg diff --git a/apps/docs/public/img/mcp-clients/kiro-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/kiro-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/kiro-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/kiro-icon.svg diff --git a/apps/docs/public/img/mcp-clients/openai-icon-dark.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/openai-icon-dark.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/openai-icon-dark.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/openai-icon-dark.svg diff --git a/apps/docs/public/img/mcp-clients/openai-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/openai-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/openai-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/openai-icon.svg diff --git a/apps/docs/public/img/mcp-clients/opencode-icon-dark.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/opencode-icon-dark.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/opencode-icon-dark.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/opencode-icon-dark.svg diff --git a/apps/docs/public/img/mcp-clients/opencode-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/opencode-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/opencode-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/opencode-icon.svg diff --git a/apps/docs/public/img/mcp-clients/vscode-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/vscode-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/vscode-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/vscode-icon.svg diff --git a/apps/docs/public/img/mcp-clients/windsurf-icon-dark.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/windsurf-icon-dark.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/windsurf-icon-dark.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/windsurf-icon-dark.svg diff --git a/apps/docs/public/img/mcp-clients/windsurf-icon.svg b/packages/ui-patterns/src/McpUrlBuilder/assets/windsurf-icon.svg similarity index 100% rename from apps/docs/public/img/mcp-clients/windsurf-icon.svg rename to packages/ui-patterns/src/McpUrlBuilder/assets/windsurf-icon.svg diff --git a/packages/ui-patterns/src/McpUrlBuilder/components/ClientSelectDropdown.tsx b/packages/ui-patterns/src/McpUrlBuilder/components/ClientSelectDropdown.tsx index e4978036472..f11e0beaff1 100644 --- a/packages/ui-patterns/src/McpUrlBuilder/components/ClientSelectDropdown.tsx +++ b/packages/ui-patterns/src/McpUrlBuilder/components/ClientSelectDropdown.tsx @@ -20,7 +20,6 @@ import type { McpClient } from '../types' import { ConnectionIcon } from './ConnectionIcon' interface ClientSelectDropdownProps { - basePath: string theme?: 'light' | 'dark' label?: string clients: McpClient[] @@ -29,7 +28,6 @@ interface ClientSelectDropdownProps { } export const ClientSelectDropdown = ({ - basePath, theme = 'light', label = 'Client', clients, @@ -64,8 +62,6 @@ export const ClientSelectDropdown = ({

{selectedClient?.icon ? ( {client.icon ? ( { const src = getMcpClientIconSrc({ - basePath, - iconFolder, icon: connection, useDarkVariant: theme === 'dark', hasDistinctDarkIcon, diff --git a/packages/ui-patterns/src/McpUrlBuilder/components/McpConfigurationDisplay.tsx b/packages/ui-patterns/src/McpUrlBuilder/components/McpConfigurationDisplay.tsx index 13aef8b1f79..7ef4e4bc0c5 100644 --- a/packages/ui-patterns/src/McpUrlBuilder/components/McpConfigurationDisplay.tsx +++ b/packages/ui-patterns/src/McpUrlBuilder/components/McpConfigurationDisplay.tsx @@ -15,7 +15,6 @@ interface McpConfigurationDisplayProps { clientConfig: McpClientConfig className?: string theme?: 'light' | 'dark' - basePath: string onCopyCallback: (type?: McpOnCopyCallback) => void onInstallCallback?: () => void isPlatform?: boolean @@ -28,13 +27,11 @@ export function McpConfigurationDisplay({ clientConfig, className, theme = 'dark', - basePath, onCopyCallback, onInstallCallback, isPlatform, }: McpConfigurationDisplayProps) { const mcpButtonData = getMcpButtonData({ - basePath, theme, client: selectedClient, clientConfig, diff --git a/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpButtonData.ts b/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpButtonData.ts index 415ccd23de2..b3e386c2883 100644 --- a/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpButtonData.ts +++ b/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpButtonData.ts @@ -2,7 +2,6 @@ import type { McpClient, McpClientConfig } from '../types' import { getMcpClientIconSrc } from './getMcpIconSrc' interface GetMcpButtonDataOptions { - basePath: string theme?: 'light' | 'dark' client: McpClient clientConfig: McpClientConfig @@ -10,7 +9,6 @@ interface GetMcpButtonDataOptions { } export function getMcpButtonData({ - basePath, theme, client, clientConfig, @@ -22,8 +20,6 @@ export function getMcpButtonData({ // If the theme is dark, the button background will be light and vice-versa const imageSrc = getMcpClientIconSrc({ - basePath, - iconFolder: 'mcp-clients', icon: client.icon!, useDarkVariant: theme === 'light', hasDistinctDarkIcon: client.hasDistinctDarkIcon, diff --git a/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.test.ts b/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.test.ts new file mode 100644 index 00000000000..d5b2c9fbff7 --- /dev/null +++ b/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from 'vitest' + +import { getMcpClientIconSrc } from './getMcpIconSrc' + +describe('getMcpClientIconSrc', () => { + it('returns the default icon when only one asset variant exists', () => { + const lightSrc = getMcpClientIconSrc({ + icon: 'cursor', + useDarkVariant: false, + }) + const darkSrc = getMcpClientIconSrc({ + icon: 'cursor', + useDarkVariant: true, + }) + + expect(lightSrc).toBeTruthy() + expect(darkSrc).toBe(lightSrc) + }) + + it('returns the dark icon when a distinct dark variant exists', () => { + const lightSrc = getMcpClientIconSrc({ + icon: 'openai', + useDarkVariant: false, + hasDistinctDarkIcon: true, + }) + const darkSrc = getMcpClientIconSrc({ + icon: 'openai', + useDarkVariant: true, + hasDistinctDarkIcon: true, + }) + + expect(darkSrc).toBeTruthy() + expect(darkSrc).not.toBe(lightSrc) + }) + + it('falls back to the default icon when no distinct dark variant should be used', () => { + const lightSrc = getMcpClientIconSrc({ + icon: 'factory', + useDarkVariant: false, + hasDistinctDarkIcon: true, + }) + const src = getMcpClientIconSrc({ + icon: 'factory', + useDarkVariant: true, + hasDistinctDarkIcon: false, + }) + + expect(src).toBe(lightSrc) + }) +}) diff --git a/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.ts b/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.ts index bac98a6a6c1..45012cbcd52 100644 --- a/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.ts +++ b/packages/ui-patterns/src/McpUrlBuilder/utils/getMcpIconSrc.ts @@ -1,21 +1,17 @@ +import { getMcpClientIconAssetUrl } from './mcpIconAssets' + /** - * Returns the path to an MCP client icon. - * Light: {icon}-icon.svg, dark (when hasDistinctDarkIcon): {icon}-icon-dark.svg. - * Only uses the dark variant when useDarkVariant is true AND hasDistinctDarkIcon is set. + * Returns the URL to an imported MCP client icon asset. + * Dark variants are only used when the client has a distinct dark asset. */ export function getMcpClientIconSrc({ - basePath, - iconFolder, icon, useDarkVariant, hasDistinctDarkIcon, }: { - basePath: string - iconFolder: string icon: string useDarkVariant: boolean hasDistinctDarkIcon?: boolean }): string { - const suffix = useDarkVariant && hasDistinctDarkIcon ? '-dark' : '' - return `${basePath}/img/${iconFolder}/${icon.toLowerCase()}-icon${suffix}.svg` + return getMcpClientIconAssetUrl(icon, useDarkVariant && Boolean(hasDistinctDarkIcon)) } diff --git a/packages/ui-patterns/src/McpUrlBuilder/utils/mcpIconAssets.ts b/packages/ui-patterns/src/McpUrlBuilder/utils/mcpIconAssets.ts new file mode 100644 index 00000000000..9641d13370b --- /dev/null +++ b/packages/ui-patterns/src/McpUrlBuilder/utils/mcpIconAssets.ts @@ -0,0 +1,52 @@ +/// + +import antigravityIcon from '../assets/antigravity-icon.svg' +import claudeIcon from '../assets/claude-icon.svg' +import cursorIcon from '../assets/cursor-icon.svg' +import factoryDarkIcon from '../assets/factory-icon-dark.svg' +import factoryIcon from '../assets/factory-icon.svg' +import geminiCliIcon from '../assets/gemini-cli-icon.svg' +import gooseDarkIcon from '../assets/goose-icon-dark.svg' +import gooseIcon from '../assets/goose-icon.svg' +import kiroIcon from '../assets/kiro-icon.svg' +import openaiDarkIcon from '../assets/openai-icon-dark.svg' +import openaiIcon from '../assets/openai-icon.svg' +import opencodeDarkIcon from '../assets/opencode-icon-dark.svg' +import opencodeIcon from '../assets/opencode-icon.svg' +import vscodeIcon from '../assets/vscode-icon.svg' +import windsurfDarkIcon from '../assets/windsurf-icon-dark.svg' +import windsurfIcon from '../assets/windsurf-icon.svg' + +type ImportedAssetModule = string | { src: string } + +type McpClientIconAsset = { + light: ImportedAssetModule + dark: ImportedAssetModule +} + +const MCP_CLIENT_ICON_ASSETS = { + antigravity: { light: antigravityIcon, dark: antigravityIcon }, + claude: { light: claudeIcon, dark: claudeIcon }, + cursor: { light: cursorIcon, dark: cursorIcon }, + factory: { light: factoryIcon, dark: factoryDarkIcon }, + 'gemini-cli': { light: geminiCliIcon, dark: geminiCliIcon }, + goose: { light: gooseIcon, dark: gooseDarkIcon }, + kiro: { light: kiroIcon, dark: kiroIcon }, + openai: { light: openaiIcon, dark: openaiDarkIcon }, + opencode: { light: opencodeIcon, dark: opencodeDarkIcon }, + vscode: { light: vscodeIcon, dark: vscodeIcon }, + windsurf: { light: windsurfIcon, dark: windsurfDarkIcon }, +} satisfies Record + +export type McpClientIconName = keyof typeof MCP_CLIENT_ICON_ASSETS + +function normalizeAssetUrl(asset: ImportedAssetModule): string { + return typeof asset === 'string' ? asset : asset.src +} + +export function getMcpClientIconAssetUrl(icon: string, useDarkVariant: boolean): string { + const iconAssets = MCP_CLIENT_ICON_ASSETS[icon.toLowerCase() as McpClientIconName] + if (!iconAssets) return '' + + return normalizeAssetUrl(useDarkVariant ? iconAssets.dark : iconAssets.light) +} diff --git a/packages/ui-patterns/src/types/assets.d.ts b/packages/ui-patterns/src/types/assets.d.ts index a4a9b8157f7..adb1bc31dbb 100644 --- a/packages/ui-patterns/src/types/assets.d.ts +++ b/packages/ui-patterns/src/types/assets.d.ts @@ -2,3 +2,8 @@ declare module '*.css' { export const styles: Record export default styles } + +declare module '*.svg' { + const content: any + export default content +}