// ============================================================================ // Tool / engine icons — single source of truth // // Every engine + host-tool brand mark is defined exactly once here and reused // by Settings -> Engines, Settings -> Stack, the Wizard, and the chat model // picker. engines.ts pulls its icons from TOOL_ICONS; nothing redefines an SVG. // ============================================================================ export interface ToolIcon { light: string; dark: string; } // Git & Chrome are full-colour brand marks, so light and dark share one string. const GIT_ICON = ''; const CHROME_ICON = ''; export const TOOL_ICONS = { 'opencode': { light: '', dark: '' }, 'claude-code': { light: '', dark: '' }, 'copilot': { light: '', dark: '' }, 'codex': { light: '', dark: '' }, 'qwen': { light: '', dark: '' }, 'pi': { light: '', dark: '' }, 'cline': { light: '', dark: '' }, 'cursor': { light: '', dark: '' }, git: { light: GIT_ICON, dark: GIT_ICON }, chrome: { light: CHROME_ICON, dark: CHROME_ICON } } satisfies Record; // Stack tool id -> TOOL_ICONS key (engine tools map to their EngineType). const STACK_TOOL_ICON_KEY: Record = { claude: 'claude-code', opencode: 'opencode', copilot: 'copilot', codex: 'codex', qwen: 'qwen', pi: 'pi', cline: 'cline', cursor: 'cursor', git: 'git', chrome: 'chrome' }; /** Resolve the icon for a Stack tool id. */ export function getToolIcon(tool: string): ToolIcon | null { const key = STACK_TOOL_ICON_KEY[tool]; return key ? TOOL_ICONS[key] : null; } // ============================================================================ // Integration brand marks // // Keyed by PROVIDER ID, so a provider declares no icon field of its own and one // whose artwork has not been added yet falls back to a generic glyph instead of // blocking the entry. // // The same rule as above applies: an inline SVG with a light and a dark // variant, rendered with `{@html}`. A monochrome glyph tinted with the theme // foreground stops being a logo, so a provider goes in here only once its real // mark is available — never as a traced approximation. // ============================================================================ export const PROVIDER_ICONS: Record = { // Official marks, taken from each vendor's own site. GitHub's Octocat is one // artwork whose fill flips with the theme — its own brand guidance is // "black on light, white on dark", so the two variants differ only in fill. // Context7 ships a // light/dark pair (the wordmark is dropped — only the square mark is used at // this size). Firecrawl's flame is one artwork whose inner fill tracks the // surface behind it, so the two variants differ only in that fill; the source // viewBox is the site's layout box rather than the glyph's, so it is re-framed // on the mark's own bounding box or it renders small and off-centre at 24px. context7: { light: '', dark: '' }, firecrawl: { light: '', dark: '' }, // Vercel's mark is one triangle whose fill flips with the theme — their brand // guidance is black on light and white on dark, so the two variants differ // only in that fill, and the viewBox is the one their own asset pack ships // (1155x1000) rather than a re-typed approximation of it. // Supabase's mark is one artwork for both themes — the green reads on light // and dark alike, which is what their own guidance assumes. Its gradient ids // are namespaced: an inline SVG's `id` is document-global, so a bare // `paint0_linear` would be captured by whichever mark rendered first. supabase: { light: '', dark: '' }, vercel: { light: '', dark: '' }, // Neon ships one logomark in two greens — #37C38F for a light background, // #34D59A for a dark one — which is the pair, not a tint we chose. neon: { light: '', dark: '' }, github: { light: '', dark: '' } }; /** Resolve the brand mark for an integration provider, or null to fall back. */ export function getProviderIcon(providerId: string): ToolIcon | null { return PROVIDER_ICONS[providerId] ?? null; }