Implement comprehensive icon selection system for provider customization: ## New Components ### ProviderIcon (src/components/ProviderIcon.tsx) - Render SVG icons by name with automatic fallback - Display provider initials when icon not found - Support custom sizing via size prop - Use dangerouslySetInnerHTML for inline SVG rendering ### IconPicker (src/components/IconPicker.tsx) - Grid-based icon selection with visual preview - Real-time search filtering by name and keywords - Integration with icon metadata for display names - Responsive grid layout (6-10 columns based on screen) ### ColorPicker (src/components/ColorPicker.tsx) - 12 preset colors for quick selection - Native color input for custom color picking - Hex input field for precise color entry - Visual feedback for selected color state ## Icon Assets (src/icons/extracted/) - 38 high-quality SVG icons for AI providers and platforms - Includes: OpenAI, Claude, DeepSeek, Qwen, Kimi, Gemini, etc. - Cloud platforms: AWS, Azure, Google Cloud, Cloudflare - Auto-generated index.ts with getIcon/hasIcon helpers - Metadata system with searchable keywords per icon ## Build Scripts - scripts/extract-icons.js: Extract icons from simple-icons - scripts/generate-icon-index.js: Generate TypeScript index file
316 lines
6.7 KiB
TypeScript
316 lines
6.7 KiB
TypeScript
// Icon metadata for search and categorization
|
|
import { IconMetadata } from "@/types/icon";
|
|
|
|
export const iconMetadata: Record<string, IconMetadata> = {
|
|
alibaba: {
|
|
name: "alibaba",
|
|
displayName: "Alibaba",
|
|
category: "ai-provider",
|
|
keywords: ["qwen", "tongyi"],
|
|
defaultColor: "#FF6A00",
|
|
},
|
|
anthropic: {
|
|
name: "anthropic",
|
|
displayName: "Anthropic",
|
|
category: "ai-provider",
|
|
keywords: ["claude"],
|
|
defaultColor: "#D4915D",
|
|
},
|
|
aws: {
|
|
name: "aws",
|
|
displayName: "AWS",
|
|
category: "cloud",
|
|
keywords: ["amazon", "cloud"],
|
|
defaultColor: "#FF9900",
|
|
},
|
|
azure: {
|
|
name: "azure",
|
|
displayName: "Azure",
|
|
category: "cloud",
|
|
keywords: ["microsoft", "cloud"],
|
|
defaultColor: "#0078D4",
|
|
},
|
|
baidu: {
|
|
name: "baidu",
|
|
displayName: "Baidu",
|
|
category: "ai-provider",
|
|
keywords: ["ernie", "wenxin"],
|
|
defaultColor: "#2932E1",
|
|
},
|
|
bytedance: {
|
|
name: "bytedance",
|
|
displayName: "bytedance",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
chatglm: {
|
|
name: "chatglm",
|
|
displayName: "chatglm",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
claude: {
|
|
name: "claude",
|
|
displayName: "Claude",
|
|
category: "ai-provider",
|
|
keywords: ["anthropic"],
|
|
defaultColor: "#D4915D",
|
|
},
|
|
cloudflare: {
|
|
name: "cloudflare",
|
|
displayName: "Cloudflare",
|
|
category: "cloud",
|
|
keywords: ["cloudflare", "cdn"],
|
|
defaultColor: "#F38020",
|
|
},
|
|
cohere: {
|
|
name: "cohere",
|
|
displayName: "Cohere",
|
|
category: "ai-provider",
|
|
keywords: ["cohere"],
|
|
defaultColor: "#39594D",
|
|
},
|
|
copilot: {
|
|
name: "copilot",
|
|
displayName: "copilot",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
deepseek: {
|
|
name: "deepseek",
|
|
displayName: "DeepSeek",
|
|
category: "ai-provider",
|
|
keywords: ["deep", "seek"],
|
|
defaultColor: "#1E88E5",
|
|
},
|
|
doubao: {
|
|
name: "doubao",
|
|
displayName: "doubao",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
gemini: {
|
|
name: "gemini",
|
|
displayName: "Gemini",
|
|
category: "ai-provider",
|
|
keywords: ["google"],
|
|
defaultColor: "#4285F4",
|
|
},
|
|
gemma: {
|
|
name: "gemma",
|
|
displayName: "gemma",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
github: {
|
|
name: "github",
|
|
displayName: "GitHub",
|
|
category: "tool",
|
|
keywords: ["git", "version control"],
|
|
defaultColor: "#181717",
|
|
},
|
|
githubcopilot: {
|
|
name: "githubcopilot",
|
|
displayName: "githubcopilot",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
google: {
|
|
name: "google",
|
|
displayName: "Google",
|
|
category: "ai-provider",
|
|
keywords: ["gemini", "bard"],
|
|
defaultColor: "#4285F4",
|
|
},
|
|
googlecloud: {
|
|
name: "googlecloud",
|
|
displayName: "googlecloud",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
grok: {
|
|
name: "grok",
|
|
displayName: "grok",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
huawei: {
|
|
name: "huawei",
|
|
displayName: "Huawei",
|
|
category: "cloud",
|
|
keywords: ["huawei", "cloud"],
|
|
defaultColor: "#FF0000",
|
|
},
|
|
huggingface: {
|
|
name: "huggingface",
|
|
displayName: "Hugging Face",
|
|
category: "ai-provider",
|
|
keywords: ["huggingface", "hf"],
|
|
defaultColor: "#FFD21E",
|
|
},
|
|
hunyuan: {
|
|
name: "hunyuan",
|
|
displayName: "hunyuan",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
kimi: {
|
|
name: "kimi",
|
|
displayName: "Kimi",
|
|
category: "ai-provider",
|
|
keywords: ["moonshot"],
|
|
defaultColor: "#6366F1",
|
|
},
|
|
meta: {
|
|
name: "meta",
|
|
displayName: "Meta",
|
|
category: "ai-provider",
|
|
keywords: ["facebook", "llama"],
|
|
defaultColor: "#0081FB",
|
|
},
|
|
midjourney: {
|
|
name: "midjourney",
|
|
displayName: "midjourney",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
minimax: {
|
|
name: "minimax",
|
|
displayName: "MiniMax",
|
|
category: "ai-provider",
|
|
keywords: ["minimax"],
|
|
defaultColor: "#FF6B6B",
|
|
},
|
|
mistral: {
|
|
name: "mistral",
|
|
displayName: "Mistral",
|
|
category: "ai-provider",
|
|
keywords: ["mistral"],
|
|
defaultColor: "#FF7000",
|
|
},
|
|
notion: {
|
|
name: "notion",
|
|
displayName: "notion",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
ollama: {
|
|
name: "ollama",
|
|
displayName: "ollama",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
openai: {
|
|
name: "openai",
|
|
displayName: "OpenAI",
|
|
category: "ai-provider",
|
|
keywords: ["gpt", "chatgpt"],
|
|
defaultColor: "#00A67E",
|
|
},
|
|
palm: {
|
|
name: "palm",
|
|
displayName: "palm",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
perplexity: {
|
|
name: "perplexity",
|
|
displayName: "Perplexity",
|
|
category: "ai-provider",
|
|
keywords: ["perplexity"],
|
|
defaultColor: "#20808D",
|
|
},
|
|
qwen: {
|
|
name: "qwen",
|
|
displayName: "qwen",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
stability: {
|
|
name: "stability",
|
|
displayName: "stability",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
tencent: {
|
|
name: "tencent",
|
|
displayName: "Tencent",
|
|
category: "ai-provider",
|
|
keywords: ["hunyuan"],
|
|
defaultColor: "#00A4FF",
|
|
},
|
|
vercel: {
|
|
name: "vercel",
|
|
displayName: "vercel",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
wenxin: {
|
|
name: "wenxin",
|
|
displayName: "wenxin",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
xai: {
|
|
name: "xai",
|
|
displayName: "xai",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
yi: {
|
|
name: "yi",
|
|
displayName: "yi",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
zeroone: {
|
|
name: "zeroone",
|
|
displayName: "zeroone",
|
|
category: "other",
|
|
keywords: [],
|
|
defaultColor: "currentColor",
|
|
},
|
|
zhipu: {
|
|
name: "zhipu",
|
|
displayName: "Zhipu AI",
|
|
category: "ai-provider",
|
|
keywords: ["chatglm", "glm"],
|
|
defaultColor: "#0F62FE",
|
|
},
|
|
};
|
|
|
|
export function getIconMetadata(name: string): IconMetadata | undefined {
|
|
return iconMetadata[name.toLowerCase()];
|
|
}
|
|
|
|
export function searchIcons(query: string): string[] {
|
|
const lowerQuery = query.toLowerCase();
|
|
return Object.values(iconMetadata)
|
|
.filter(
|
|
(meta) =>
|
|
meta.name.includes(lowerQuery) ||
|
|
meta.displayName.toLowerCase().includes(lowerQuery) ||
|
|
meta.keywords.some((k) => k.includes(lowerQuery)),
|
|
)
|
|
.map((meta) => meta.name);
|
|
}
|