Files
picoclaw/web/frontend/src/components/models/provider-icon.tsx
T
LC 548dc15acd refactor(models): unify provider metadata around backend catalog (#2896)
* feat(models): unify provider metadata around backend catalog

- Move shared provider metadata and alias normalization into backend-owned provider catalog
- Expose display, fetch, auth, and default model metadata through /api/models provider_options
- Replace frontend static provider registry with catalog-driven selection, validation, grouping, and fallback rendering
- Treat provider default api_base as placeholder and effective fetch/test base while keep submitted api_base separate from derived defaults
- Add model page retry handling, touched locale updates, and provider metadata assertions in backend tests

* fix(models): canonicalize backend provider aliases and common models

* fix(models): restore deepseek common model recommendations
2026-05-20 11:50:34 +08:00

55 lines
1.7 KiB
TypeScript

import { useMemo, useState } from "react"
import type { ProviderCatalogEntry } from "./provider-registry"
interface ProviderIconProps {
provider: Pick<ProviderCatalogEntry, "key" | "label" | "iconSlug" | "domain">
}
export function ProviderIcon({ provider }: ProviderIconProps) {
const [sourceIndex, setSourceIndex] = useState(0)
const [loadFailed, setLoadFailed] = useState(false)
const initial = provider.label.trim().charAt(0).toUpperCase() || "?"
const iconUrls = useMemo(() => {
const slug = provider.iconSlug
const domain = provider.domain
const urls: string[] = []
if (slug) {
urls.push(`https://cdn.simpleicons.org/${slug}`)
}
if (domain) {
urls.push(`https://www.google.com/s2/favicons?domain=${domain}&sz=64`)
}
return urls
}, [provider.domain, provider.iconSlug])
const iconUrl = iconUrls[sourceIndex]
if (!iconUrl || loadFailed) {
return (
<span className="inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-black/10 bg-white text-[9px] font-semibold text-black/70 dark:border-white/20 dark:text-white/70">
{initial}
</span>
)
}
return (
<span className="inline-flex size-4 shrink-0 items-center justify-center overflow-hidden rounded-sm border border-black/10 bg-white p-0.5 dark:border-white/20">
<img
src={iconUrl}
alt={`${provider.label} logo`}
className="size-full object-contain"
loading="lazy"
referrerPolicy="no-referrer"
onError={() => {
if (sourceIndex < iconUrls.length - 1) {
setSourceIndex((idx) => idx + 1)
return
}
setLoadFailed(true)
}}
/>
</span>
)
}