Files
CC-Switch/src/icons/extracted/metadata.ts
T
Calcium-Ion 8fe5c1041a feat: add Universal Provider feature (#348)
* feat: add Universal Provider feature

- Add Universal Provider data structures and type definitions
- Implement backend CRUD operations and sync functionality
- Add frontend UI components (UniversalProviderPanel, Card, FormModal)
- Add NewAPI icon and preset configuration
- Support cross-app (Claude/Codex/Gemini) configuration sync
- Add website URL field for providers
- Implement real-time refresh via event notifications
- Add i18n support (Chinese/English/Japanese)

* feat: integrate universal provider presets into add provider dialog

- Add universal provider presets (NewAPI, Custom Gateway) to preset selector
- Show universal presets with Layers icon badge in preset selector
- Open UniversalProviderFormModal when universal preset is clicked
- Pass initialPreset to auto-fill form when opened from add dialog
- Add i18n keys for addSuccess/addFailed messages
- Keep separate universal provider panel for management

* refactor: move universal provider management to add dialog

- Remove Layers button from main navigation header
- Add 'Manage' button next to universal provider presets
- Open UniversalProviderPanel from within add provider dialog
- Add i18n keys for 'manage' in all locales

* style: display universal provider presets on separate line

- Move universal provider section to a new row with border separator
- Add label '统一供应商:' to clarify the section

* style: unify universal provider label style with preset label

- Use FormLabel component for consistent styling
- Add background to 'Manage' button matching preset buttons
- Update icon size and button padding for consistency

* feat: add sync functionality and JSON preview for Universal Provider

* fix: add missing in_failover_queue field to Provider structs

After rebasing to main, the Provider struct gained a new
`in_failover_queue` field. This fix adds the missing field
to the three to_*_provider() methods in UniversalProvider.

* refactor: redesign AddProviderDialog with tab-based layout

- Add tabs to separate app-specific providers and universal providers
- Move "Add Universal Provider" button from panel header to footer
- Remove unused handleAdd callback and clean up imports
- Update emptyHint i18n text to reference the footer button

* fix: append /v1 suffix to Codex base_url in Universal Provider

Codex uses OpenAI-compatible API which requires the /v1 endpoint suffix.
The Universal Provider now automatically appends /v1 to base_url when
generating Codex provider config if not already present.

- Handle trailing slashes to avoid double slashes
- Apply fix to both backend (to_codex_provider) and frontend preview

* feat: auto-sync universal provider to apps on creation

Previously, users had to manually click sync after adding a universal
provider. Now it automatically syncs to Claude/Codex/Gemini on creation,
providing a smoother user experience.

---------

Co-authored-by: Jason <farion1231@gmail.com>
2025-12-26 22:47:24 +08:00

365 lines
7.9 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",
},
newapi: {
name: "newapi",
displayName: "newapi",
category: "other",
keywords: [],
defaultColor: "currentColor",
},
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: "currentColor",
},
packycode: {
name: "packycode",
displayName: "PackyCode",
category: "ai-provider",
keywords: ["packycode", "packy", "packyapi"],
defaultColor: "currentColor",
},
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",
},
openrouter: {
name: "openrouter",
displayName: "OpenRouter",
category: "ai-provider",
keywords: ["openrouter", "router", "aggregator"],
defaultColor: "#6566F1",
},
longcat: {
name: "longcat",
displayName: "LongCat",
category: "ai-provider",
keywords: ["longcat", "long", "cat"],
defaultColor: "#29E154",
},
modelscope: {
name: "modelscope",
displayName: "ModelScope",
category: "ai-provider",
keywords: ["modelscope", "alibaba", "scope"],
defaultColor: "#624AFF",
},
aihubmix: {
name: "aihubmix",
displayName: "AiHubMix",
category: "ai-provider",
keywords: ["aihubmix", "hub", "mix", "aggregator"],
defaultColor: "#006FFB",
},
xiaomimimo: {
name: "xiaomimimo",
displayName: "Xiaomi MiMo",
category: "ai-provider",
keywords: ["xiaomimimo", "xiaomi", "mimo"],
defaultColor: "#000000",
},
};
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);
}