From 4fd71e60e9efa99d4dd13ee4553e41ef42199fe4 Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Thu, 18 Sep 2025 15:36:20 +0200 Subject: [PATCH 1/3] add icons to manage clients --- package.json | 3 +- pnpm-lock.yaml | 13 ++++- renderer/src/common/components/brand-icon.tsx | 53 +++++++++++++++++++ .../components/manage-clients-button.tsx | 16 ++++-- 4 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 renderer/src/common/components/brand-icon.tsx diff --git a/package.json b/package.json index f075b1192..5531ef545 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "keywords": [], "license": "Apache-2.0", "devDependencies": { - "ajv": "^8.17.1", "@electron-forge/cli": "^7.8.1", "@electron-forge/maker-base": "^7.8.1", "@electron-forge/maker-deb": "^7.8.1", @@ -63,6 +62,7 @@ "@types/unzipper": "^0.10.11", "@vitejs/plugin-react-swc": "^4.0.0", "@vitest/coverage-istanbul": "^3.1.4", + "ajv": "^8.17.1", "autoprefixer": "^10.4.21", "dotenv": "^17.0.0", "electron": "37.4.0", @@ -140,6 +140,7 @@ "regexp.escape": "^2.0.1", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", + "simple-icons": "^14.15.0", "sonner": "^2.0.3", "streamdown": "^1.2.0", "tailwind-merge": "^3.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2118b526e..982169148 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,6 +155,9 @@ importers: remark-math: specifier: ^6.0.0 version: 6.0.0 + simple-icons: + specifier: ^14.15.0 + version: 14.15.0 sonner: specifier: ^2.0.3 version: 2.0.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -6465,6 +6468,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-icons@14.15.0: + resolution: {integrity: sha512-yjlJ8skP4isCd4dQTDGFbv0hQgDIr57DbWaovEeHADPAZa5DBEAs+3ZnBb76ti+yz7/OzFI3SqTP1SKku1uhCw==} + engines: {node: '>=0.12.18'} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -12308,7 +12315,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -14528,6 +14535,8 @@ snapshots: signal-exit@4.1.0: {} + simple-icons@14.15.0: {} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 @@ -14549,7 +14558,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 socks: 2.8.7 transitivePeerDependencies: - supports-color diff --git a/renderer/src/common/components/brand-icon.tsx b/renderer/src/common/components/brand-icon.tsx new file mode 100644 index 000000000..95e257298 --- /dev/null +++ b/renderer/src/common/components/brand-icon.tsx @@ -0,0 +1,53 @@ +import type { ComponentProps } from 'react' +import { Package } from 'lucide-react' +// Import only the needed Simple Icons to keep bundle size small +// Each import is a JS object with { title, slug, hex, path, ... } +import visualStudioCode from 'simple-icons/icons/visualstudiocode.js' +import anthropic from 'simple-icons/icons/anthropic.js' +import sourcegraph from 'simple-icons/icons/sourcegraph.js' +import jetbrains from 'simple-icons/icons/jetbrains.js' + +type Props = { + name: string +} & Pick, 'className' | 'aria-hidden' | 'focusable'> + +type SimpleIcon = { title: string; path: string } + +// Map known client identifiers to Simple Icons data objects. +const ICONS: Record = { + // VS Code + vscode: visualStudioCode, + 'vscode-insider': visualStudioCode, + // Anthropic / Claude Code + 'claude-code': anthropic, + // Sourcegraph Amp (map derivatives to Sourcegraph brand) + 'amp-cli': sourcegraph, + 'amp-cursor': sourcegraph, + 'amp-windsurf': sourcegraph, + // JetBrains for windsorf plugin targeting JetBrains IDEs + 'windsurf-jetbrains': jetbrains, +} + +export function BrandIcon({ name, className, ...rest }: Props) { + const key = name.toLowerCase() + const icon = ICONS[key] + if (icon) { + return ( + + + + ) + } + + // Fallback to a generic lucide package icon when no brand is available + return +} + +export default BrandIcon diff --git a/renderer/src/features/clients/components/manage-clients-button.tsx b/renderer/src/features/clients/components/manage-clients-button.tsx index 654fcc050..f49c96499 100644 --- a/renderer/src/features/clients/components/manage-clients-button.tsx +++ b/renderer/src/features/clients/components/manage-clients-button.tsx @@ -4,6 +4,7 @@ import { Label } from '@/common/components/ui/label' import { Button } from '@/common/components/ui/button' import { Switch } from '@/common/components/ui/switch' import { Code } from 'lucide-react' +import BrandIcon from '@/common/components/brand-icon' import { z } from 'zod/v4' import { zodV4Resolver } from '@/common/lib/zod-v4-resolver' import { useManageClients } from '../hooks/use-manage-clients' @@ -67,7 +68,7 @@ export function ManageClientsButton({ return (
- +
+ + +
) })} From 4c8152b774d1ece7308f3b4334b71ffb2c08156c Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Thu, 18 Sep 2025 16:21:47 +0200 Subject: [PATCH 2/3] fix --- package.json | 3 +- pnpm-lock.yaml | 21 +++++---- renderer/src/common/components/brand-icon.tsx | 43 ++++--------------- 3 files changed, 23 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 5531ef545..52d4ca20f 100644 --- a/package.json +++ b/package.json @@ -136,11 +136,12 @@ "lucide-react": "^0.544.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "@icons-pack/react-simple-icons": "^13.7.0", "react-hook-form": "7.62.0", "regexp.escape": "^2.0.1", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", - "simple-icons": "^14.15.0", + "sonner": "^2.0.3", "streamdown": "^1.2.0", "tailwind-merge": "^3.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 982169148..ad9fc22aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@fontsource/space-mono': specifier: ^5.2.7 version: 5.2.8 + '@icons-pack/react-simple-icons': + specifier: ^13.7.0 + version: 13.7.0(react@19.1.1) '@modelcontextprotocol/sdk': specifier: ^1.17.2 version: 1.17.5 @@ -155,9 +158,6 @@ importers: remark-math: specifier: ^6.0.0 version: 6.0.0 - simple-icons: - specifier: ^14.15.0 - version: 14.15.0 sonner: specifier: ^2.0.3 version: 2.0.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -1036,6 +1036,11 @@ packages: '@iconify/utils@3.0.2': resolution: {integrity: sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==} + '@icons-pack/react-simple-icons@13.7.0': + resolution: {integrity: sha512-Vx5mnIm/3gD/9dpCfw/EdCXwzCswmvWnvMjL6zUJTbpk2PuyCdx5zSfiX8KQKYszD/1Z2mfaiBtqCxlHuDcpuA==} + peerDependencies: + react: ^16.13 || ^17 || ^18 || ^19 + '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -6468,10 +6473,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-icons@14.15.0: - resolution: {integrity: sha512-yjlJ8skP4isCd4dQTDGFbv0hQgDIr57DbWaovEeHADPAZa5DBEAs+3ZnBb76ti+yz7/OzFI3SqTP1SKku1uhCw==} - engines: {node: '>=0.12.18'} - simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -8293,6 +8294,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@icons-pack/react-simple-icons@13.7.0(react@19.1.1)': + dependencies: + react: 19.1.1 + '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.4 @@ -14535,8 +14540,6 @@ snapshots: signal-exit@4.1.0: {} - simple-icons@14.15.0: {} - simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 diff --git a/renderer/src/common/components/brand-icon.tsx b/renderer/src/common/components/brand-icon.tsx index 95e257298..fe4b3186e 100644 --- a/renderer/src/common/components/brand-icon.tsx +++ b/renderer/src/common/components/brand-icon.tsx @@ -1,50 +1,25 @@ -import type { ComponentProps } from 'react' +import type { ComponentProps, JSX } from 'react' import { Package } from 'lucide-react' -// Import only the needed Simple Icons to keep bundle size small -// Each import is a JS object with { title, slug, hex, path, ... } -import visualStudioCode from 'simple-icons/icons/visualstudiocode.js' -import anthropic from 'simple-icons/icons/anthropic.js' -import sourcegraph from 'simple-icons/icons/sourcegraph.js' -import jetbrains from 'simple-icons/icons/jetbrains.js' +import { SiAnthropic, SiJetbrains } from '@icons-pack/react-simple-icons' type Props = { name: string } & Pick, 'className' | 'aria-hidden' | 'focusable'> -type SimpleIcon = { title: string; path: string } +type IconComponent = (p: ComponentProps<'svg'>) => JSX.Element -// Map known client identifiers to Simple Icons data objects. -const ICONS: Record = { - // VS Code - vscode: visualStudioCode, - 'vscode-insider': visualStudioCode, +// Map known client identifiers to React Simple Icons components. +const ICONS: Record = { // Anthropic / Claude Code - 'claude-code': anthropic, - // Sourcegraph Amp (map derivatives to Sourcegraph brand) - 'amp-cli': sourcegraph, - 'amp-cursor': sourcegraph, - 'amp-windsurf': sourcegraph, + 'claude-code': (p) => , // JetBrains for windsorf plugin targeting JetBrains IDEs - 'windsurf-jetbrains': jetbrains, + 'windsurf-jetbrains': (p) => , } export function BrandIcon({ name, className, ...rest }: Props) { const key = name.toLowerCase() - const icon = ICONS[key] - if (icon) { - return ( - - - - ) - } + const Icon = ICONS[key] + if (Icon) return // Fallback to a generic lucide package icon when no brand is available return From 7bd12e460fc9089ebbde231d551deceedcee2c69 Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Fri, 19 Sep 2025 12:56:33 +0200 Subject: [PATCH 3/3] . --- package.json | 4 ++ pnpm-lock.yaml | 47 ++++++++++++++++++- renderer/src/common/components/brand-icon.tsx | 35 ++++++++++++++ .../components/manage-clients-button.tsx | 24 ++++++---- 4 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 renderer/src/common/components/brand-icon.tsx diff --git a/package.json b/package.json index f075b1192..64f23337e 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,10 @@ "lucide-react": "^0.544.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "@iconify/react": "^5.1.0", + "@iconify/icons-heroicons-outline": "^1.2.5", + "@iconify-json/vscode-icons": "^1.1.82", + "@iconify-json/simple-icons": "^1.1.119", "react-hook-form": "7.62.0", "regexp.escape": "^2.0.1", "remark-gfm": "^4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2118b526e..bf1eb803c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,18 @@ importers: '@fontsource/space-mono': specifier: ^5.2.7 version: 5.2.8 + '@iconify-json/simple-icons': + specifier: ^1.1.119 + version: 1.2.52 + '@iconify-json/vscode-icons': + specifier: ^1.1.82 + version: 1.2.30 + '@iconify/icons-heroicons-outline': + specifier: ^1.2.5 + version: 1.2.5 + '@iconify/react': + specifier: ^5.1.0 + version: 5.2.1(react@19.1.1) '@modelcontextprotocol/sdk': specifier: ^1.17.2 version: 1.17.5 @@ -1027,6 +1039,20 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify-json/simple-icons@1.2.52': + resolution: {integrity: sha512-c41YOMzBhl3hp58WJLxT+Qq3UhBd8GZAMkbS8ddlCuIGLW0COGe2YSfOA2+poA8/bxLhUQODRNjAy3KhiAOtzA==} + + '@iconify-json/vscode-icons@1.2.30': + resolution: {integrity: sha512-dlTOc8w4a8/QNumZzMve+APJa6xQVXPZwo8qBk/MaYfY42NPrQT83QXkbTWKDkuEu/xgHPXvKZZBL7Yy12vYQw==} + + '@iconify/icons-heroicons-outline@1.2.5': + resolution: {integrity: sha512-7t0FeK42WNbaBoTrtn+d830jMujaEsFZYobk5KT4o+vhXitV6Yq5Q32Gc6M1P30qn04QoFDD2RxMBufA9BaRbg==} + + '@iconify/react@5.2.1': + resolution: {integrity: sha512-37GDR3fYDZmnmUn9RagyaX+zca24jfVOMY8E1IXTqJuE8pxNtN51KWPQe3VODOWvuUurq7q9uUu3CFrpqj5Iqg==} + peerDependencies: + react: '>=16' + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -8271,6 +8297,23 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify-json/simple-icons@1.2.52': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/vscode-icons@1.2.30': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/icons-heroicons-outline@1.2.5': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/react@5.2.1(react@19.1.1)': + dependencies: + '@iconify/types': 2.0.0 + react: 19.1.1 + '@iconify/types@2.0.0': {} '@iconify/utils@3.0.2': @@ -12308,7 +12351,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -14549,7 +14592,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 socks: 2.8.7 transitivePeerDependencies: - supports-color diff --git a/renderer/src/common/components/brand-icon.tsx b/renderer/src/common/components/brand-icon.tsx new file mode 100644 index 000000000..3621977a4 --- /dev/null +++ b/renderer/src/common/components/brand-icon.tsx @@ -0,0 +1,35 @@ +import type { ComponentProps } from 'react' +import { Icon } from '@iconify/react' +import homeIcon from '@iconify/icons-heroicons-outline/home' +import vscodeIcons from '@iconify-json/vscode-icons/icons.json' +import simpleIcons from '@iconify-json/simple-icons/icons.json' + +type Props = { + name: string +} & Pick, 'className' | 'aria-hidden' | 'focusable'> + +const cursorRulesIcon = (vscodeIcons as any).icons?.['file-type-cursorrules'] +const claudeIcon = (simpleIcons as any).icons?.['claude'] + +const ICONS: Record = { + // Cursor editor + cursor: cursorRulesIcon, + // Claude (Anthropic) + 'claude-code': claudeIcon, +} + +export default function BrandIcon({ name, className, ...rest }: Props) { + const key = name.toLowerCase() + const icon = (ICONS[key] ?? homeIcon) as any + return ( + + ) +} diff --git a/renderer/src/features/clients/components/manage-clients-button.tsx b/renderer/src/features/clients/components/manage-clients-button.tsx index 654fcc050..7a9806412 100644 --- a/renderer/src/features/clients/components/manage-clients-button.tsx +++ b/renderer/src/features/clients/components/manage-clients-button.tsx @@ -4,6 +4,7 @@ import { Label } from '@/common/components/ui/label' import { Button } from '@/common/components/ui/button' import { Switch } from '@/common/components/ui/switch' import { Code } from 'lucide-react' +import BrandIcon from '@/common/components/brand-icon' import { z } from 'zod/v4' import { zodV4Resolver } from '@/common/lib/zod-v4-resolver' import { useManageClients } from '../hooks/use-manage-clients' @@ -12,12 +13,12 @@ import { useToastMutation } from '@/common/hooks/use-toast-mutation' interface ManageClientsButtonProps { groupName: string variant?: - | 'default' - | 'outline' - | 'secondary' - | 'ghost' - | 'link' - | 'destructive' + | 'default' + | 'outline' + | 'secondary' + | 'ghost' + | 'link' + | 'destructive' className?: string } @@ -67,7 +68,7 @@ export function ManageClientsButton({ return (
- +
+ + +
) })}