diff --git a/package.json b/package.json index a939d5b..a0481b4 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^5.2.2", + "@lobehub/icons-static-svg": "^1.73.0", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9aebb80..b7e013f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@hookform/resolvers': specifier: ^5.2.2 version: 5.2.2(react-hook-form@7.65.0(react@18.3.1)) + '@lobehub/icons-static-svg': + specifier: ^1.73.0 + version: 1.73.0 '@radix-ui/react-checkbox': specifier: ^1.3.3 version: 1.3.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -609,6 +612,9 @@ packages: '@lezer/markdown@1.6.0': resolution: {integrity: sha512-AXb98u3M6BEzTnreBnGtQaF7xFTiMA92Dsy5tqEjpacbjRxDSFdN4bKJo9uvU4cEEOS7D2B9MT7kvDgOEIzJSw==} + '@lobehub/icons-static-svg@1.73.0': + resolution: {integrity: sha512-ydKUCDoopdmulbjDZo/gppaODd5Ju5nPneVcN9A5dAz9IJZUMkLms8bqostMLrqcdMQ8resKjLuV9RhJaWhaag==} + '@marijn/find-cluster-break@1.0.2': resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} @@ -2839,6 +2845,8 @@ snapshots: '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 + '@lobehub/icons-static-svg@1.73.0': {} + '@marijn/find-cluster-break@1.0.2': {} '@mswjs/interceptors@0.40.0': diff --git a/scripts/filter-icons.js b/scripts/filter-icons.js new file mode 100644 index 0000000..d7448c6 --- /dev/null +++ b/scripts/filter-icons.js @@ -0,0 +1,95 @@ +const fs = require('fs'); +const path = require('path'); + +const ICONS_DIR = path.join(__dirname, '../src/icons/extracted'); + +// List of "Famous" icons to keep +// Based on common AI providers and tools +const KEEP_LIST = [ + // AI Providers + 'openai', 'anthropic', 'claude', 'google', 'gemini', 'gemma', 'palm', + 'microsoft', 'azure', 'copilot', 'meta', 'llama', + 'alibaba', 'qwen', 'tencent', 'hunyuan', 'baidu', 'wenxin', + 'bytedance', 'doubao', 'deepseek', 'moonshot', 'kimi', + 'zhipu', 'chatglm', 'glm', 'minimax', 'mistral', 'cohere', + 'perplexity', 'huggingface', 'midjourney', 'stability', + 'xai', 'grok', 'yi', 'zeroone', 'ollama', + + // Cloud/Tools + 'aws', 'googlecloud', 'huawei', 'cloudflare', + 'github', 'githubcopilot', 'vercel', 'notion', 'discord', + 'gitlab', 'docker', 'kubernetes', 'vscode', 'settings', 'folder', 'file', 'link' +]; + +// Get all SVG files +const files = fs.readdirSync(ICONS_DIR).filter(file => file.endsWith('.svg')); + +console.log(`Scanning ${files.length} files...`); + +let keptCount = 0; +let deletedCount = 0; +let renamedCount = 0; + +// First pass: Identify files to keep and prefer color versions +const fileMap = {}; // name -> { hasColor: bool, hasMono: bool } + +files.forEach(file => { + const isColor = file.endsWith('-color.svg'); + const baseName = isColor ? file.replace('-color.svg', '') : file.replace('.svg', ''); + + if (!fileMap[baseName]) { + fileMap[baseName] = { hasColor: false, hasMono: false }; + } + + if (isColor) { + fileMap[baseName].hasColor = true; + } else { + fileMap[baseName].hasMono = true; + } +}); + +// Second pass: Process files +Object.keys(fileMap).forEach(baseName => { + const info = fileMap[baseName]; + const shouldKeep = KEEP_LIST.includes(baseName); + + if (!shouldKeep) { + // Delete both versions if not in keep list + if (info.hasColor) { + fs.unlinkSync(path.join(ICONS_DIR, `${baseName}-color.svg`)); + deletedCount++; + } + if (info.hasMono) { + fs.unlinkSync(path.join(ICONS_DIR, `${baseName}.svg`)); + deletedCount++; + } + return; + } + + // If keeping, prefer color + if (info.hasColor) { + // Rename color version to base version (overwrite mono if exists) + const colorPath = path.join(ICONS_DIR, `${baseName}-color.svg`); + const targetPath = path.join(ICONS_DIR, `${baseName}.svg`); + + try { + // If mono exists, it will be overwritten/replaced + fs.renameSync(colorPath, targetPath); + renamedCount++; + keptCount++; + } catch (e) { + console.error(`Error renaming ${baseName}:`, e); + } + } else if (info.hasMono) { + // Keep mono if no color version + keptCount++; + } +}); + +console.log(`\nCleanup complete:`); +console.log(`- Kept: ${keptCount}`); +console.log(`- Deleted: ${deletedCount}`); +console.log(`- Renamed (Color -> Standard): ${renamedCount}`); + +// Regenerate index and metadata +require('./generate-icon-index.js'); diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index c0bc1cd..d244599 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -40,6 +40,9 @@ export interface ProviderPreset { endpointCandidates?: string[]; // 新增:视觉主题配置 theme?: PresetTheme; + // 图标配置 + icon?: string; // 图标名称 + iconColor?: string; // 图标颜色 } export const providerPresets: ProviderPreset[] = [ @@ -56,6 +59,8 @@ export const providerPresets: ProviderPreset[] = [ backgroundColor: "#D97757", textColor: "#FFFFFF", }, + icon: "anthropic", + iconColor: "#D4915D", }, { name: "DeepSeek",