diff --git a/apps/ui/src/components/ui/provider-icon.tsx b/apps/ui/src/components/ui/provider-icon.tsx index 9a53249d..e036c597 100644 --- a/apps/ui/src/components/ui/provider-icon.tsx +++ b/apps/ui/src/components/ui/provider-icon.tsx @@ -15,6 +15,9 @@ const PROVIDER_ICON_KEYS = { nova: 'nova', meta: 'meta', mistral: 'mistral', + minimax: 'minimax', + glm: 'glm', + bigpickle: 'bigpickle', } as const; type ProviderIconKey = keyof typeof PROVIDER_ICON_KEYS; @@ -87,6 +90,22 @@ const PROVIDER_ICON_DEFINITIONS: Record viewBox: '0 0 24 24', path: '', }, + minimax: { + viewBox: '0 0 24 24', + // Official MiniMax logo from lobehub/lobe-icons + path: 'M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z', + }, + glm: { + viewBox: '0 0 24 24', + // Official Z.ai logo from lobehub/lobe-icons (GLM provider) + path: 'M12.105 2L9.927 4.953H.653L2.83 2h9.276zM23.254 19.048L21.078 22h-9.242l2.174-2.952h9.244zM24 2L9.264 22H0L14.736 2H24z', + }, + bigpickle: { + viewBox: '0 0 24 24', + // Big Pickle logo - stylized shape with dots + path: 'M8 4c-2.21 0-4 1.79-4 4v8c0 2.21 1.79 4 4 4h8c2.21 0 4-1.79 4-4V8c0-2.21-1.79-4-4-4H8zm0 2h8c1.103 0 2 .897 2 2v8c0 1.103-.897 2-2 2H8c-1.103 0-2-.897-2-2V8c0-1.103.897-2 2-2zm2 3a1 1 0 100 2 1 1 0 000-2zm4 0a1 1 0 100 2 1 1 0 000-2zm-4 4a1 1 0 100 2 1 1 0 000-2zm4 0a1 1 0 100 2 1 1 0 000-2z', + fill: '#4ADE80', + }, }; export interface ProviderIconProps extends Omit, 'viewBox'> { @@ -280,6 +299,83 @@ export function MetaIcon({ className, title, ...props }: { className?: string; t ); } +export function MiniMaxIcon({ + className, + title, + ...props +}: { + className?: string; + title?: string; +}) { + const hasAccessibleLabel = Boolean(title); + + return ( + + {title && {title}} + + + ); +} + +export function GlmIcon({ className, title, ...props }: { className?: string; title?: string }) { + const hasAccessibleLabel = Boolean(title); + + return ( + + {title && {title}} + + + ); +} + +export function BigPickleIcon({ + className, + title, + ...props +}: { + className?: string; + title?: string; +}) { + const hasAccessibleLabel = Boolean(title); + + return ( + + {title && {title}} + + + ); +} + export const PROVIDER_ICON_COMPONENTS: Record< ModelProvider, ComponentType<{ className?: string }> @@ -299,33 +395,50 @@ function getUnderlyingModelIcon(model?: AgentModel | string): ProviderIconKey { const modelStr = typeof model === 'string' ? model.toLowerCase() : model; - // Check for OpenCode models (opencode/, amazon-bedrock/, opencode-*) - if (modelStr.includes('opencode')) { - // For OpenCode models, check which specific provider - if (modelStr.includes('amazon-bedrock')) { - // Bedrock-hosted models - detect the specific provider - if (modelStr.includes('anthropic') || modelStr.includes('claude')) { - return 'anthropic'; - } - if (modelStr.includes('deepseek')) { - return 'deepseek'; - } - if (modelStr.includes('nova')) { - return 'nova'; - } - if (modelStr.includes('meta') || modelStr.includes('llama')) { - return 'meta'; - } - if (modelStr.includes('mistral')) { - return 'mistral'; - } - if (modelStr.includes('qwen')) { - return 'qwen'; - } - // Default for Bedrock - return 'opencode'; + // Check for Amazon Bedrock models first (amazon-bedrock/...) + if (modelStr.startsWith('amazon-bedrock/')) { + // Bedrock-hosted models - detect the specific provider + if (modelStr.includes('anthropic') || modelStr.includes('claude')) { + return 'anthropic'; } - // Native OpenCode models (opencode/big-pickle, etc.) + if (modelStr.includes('deepseek')) { + return 'deepseek'; + } + if (modelStr.includes('nova')) { + return 'nova'; + } + if (modelStr.includes('meta') || modelStr.includes('llama')) { + return 'meta'; + } + if (modelStr.includes('mistral')) { + return 'mistral'; + } + if (modelStr.includes('qwen')) { + return 'qwen'; + } + // Default for unknown Bedrock models + return 'opencode'; + } + + // Check for native OpenCode models (opencode/...) + if (modelStr.startsWith('opencode/')) { + // Native OpenCode models - check specific model types + if (modelStr.includes('big-pickle')) { + return 'bigpickle'; + } + if (modelStr.includes('grok')) { + return 'grok'; + } + if (modelStr.includes('glm')) { + return 'glm'; + } + if (modelStr.includes('gpt-5-nano') || modelStr.includes('nano')) { + return 'openai'; // GPT-5 Nano uses OpenAI icon + } + if (modelStr.includes('minimax')) { + return 'minimax'; + } + // Default for other OpenCode models return 'opencode'; } @@ -371,6 +484,9 @@ export function getProviderIconForModel( nova: NovaIcon, meta: MetaIcon, mistral: MistralIcon, + minimax: MiniMaxIcon, + glm: GlmIcon, + bigpickle: BigPickleIcon, }; return iconMap[iconKey] || AnthropicIcon; diff --git a/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx b/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx index c14a6370..e8a93480 100644 --- a/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx +++ b/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx @@ -37,6 +37,7 @@ import { QwenIcon, MistralIcon, MetaIcon, + getProviderIconForModel, } from '@/components/ui/provider-icon'; import { Button } from '@/components/ui/button'; import { @@ -513,27 +514,8 @@ export function PhaseModelSelector({ const isSelected = selectedModel === model.id; const isFavorite = favoriteModels.includes(model.id); - // Get the appropriate icon based on provider - const ProviderIcon = (() => { - switch (model.provider) { - case 'opencode': - return OpenCodeIcon; - case 'amazon-bedrock-anthropic': - return AnthropicIcon; - case 'amazon-bedrock-deepseek': - return DeepSeekIcon; - case 'amazon-bedrock-amazon': - return NovaIcon; - case 'amazon-bedrock-meta': - return MetaIcon; - case 'amazon-bedrock-mistral': - return MistralIcon; - case 'amazon-bedrock-qwen': - return QwenIcon; - default: - return OpenCodeIcon; - } - })(); + // Get the appropriate icon based on the specific model ID + const ProviderIcon = getProviderIconForModel(model.id); return ( { - switch (provider) { - case 'opencode': - return OpenCodeIcon; - case 'amazon-bedrock-anthropic': - return AnthropicIcon; - case 'amazon-bedrock-deepseek': - return DeepSeekIcon; - case 'amazon-bedrock-amazon': - return NovaIcon; - case 'amazon-bedrock-meta': - return MetaIcon; - case 'amazon-bedrock-mistral': - return MistralIcon; - case 'amazon-bedrock-qwen': - return QwenIcon; - default: - return OpenCodeIcon; - } +function getModelIcon(modelId: OpencodeModelId): ComponentType<{ className?: string }> { + return getProviderIconForModel(modelId); } /** @@ -146,11 +130,11 @@ export function OpencodeModelConfiguration({ {enabledOpencodeModels.map((modelId) => { const model = OPENCODE_MODEL_CONFIG_MAP[modelId]; if (!model) return null; - const ProviderIconComponent = getProviderIcon(model.provider); + const ModelIconComponent = getModelIcon(modelId); return (
- + {model.label}
@@ -167,7 +151,9 @@ export function OpencodeModelConfiguration({ const models = modelsByProvider[provider]; if (!models || models.length === 0) return null; - const ProviderIconComponent = getProviderIcon(provider); + // Use the first model's icon as the provider icon + const ProviderIconComponent = + models.length > 0 ? getModelIcon(models[0].id) : OpenCodeIcon; return (