diff --git a/tools/ui/src/lib/components/app/backends/BackendIcon.svelte b/tools/ui/src/lib/components/app/backends/BackendIcon.svelte new file mode 100644 index 0000000000..d8dc6be131 --- /dev/null +++ b/tools/ui/src/lib/components/app/backends/BackendIcon.svelte @@ -0,0 +1,32 @@ + + +{#if preset} + +{:else if faviconUrl && !failedFavicons.has(faviconUrl)} + failedFavicons.add(faviconUrl)} + src={faviconUrl} + /> +{/if} diff --git a/tools/ui/src/lib/components/app/backends/index.ts b/tools/ui/src/lib/components/app/backends/index.ts index 162dc2d6fd..516a7177cf 100644 --- a/tools/ui/src/lib/components/app/backends/index.ts +++ b/tools/ui/src/lib/components/app/backends/index.ts @@ -1,5 +1,6 @@ export { default as BackendCard } from './BackendCard.svelte'; export { default as BackendForm } from './BackendForm.svelte'; +export { default as BackendIcon } from './BackendIcon.svelte'; export { default as BackendPresetCard } from './BackendPresetCard.svelte'; export { default as BackendPresetIcon } from './BackendPresetIcon.svelte'; export { default as DialogBackendForm } from './DialogBackendForm.svelte'; diff --git a/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte index 244ea38c92..92c4c2394c 100644 --- a/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte @@ -12,6 +12,7 @@ RotateCw } from '@lucide/svelte'; import { ActionIcon, ModelId } from '$lib/components/app'; + import { BackendIcon } from '$lib/components/app/backends'; import { HF_BASE_MODEL_TAG_REGEX, ICON_CLASS_DEFAULT } from '$lib/constants'; import { ModelCapability, ServerModelStatus } from '$lib/enums'; import { HuggingFaceService, ModelsService } from '$lib/services'; @@ -222,7 +223,13 @@ {#if !canLoad} - + + {#if rowBackend} +
+ +
+ {/if} {:else if isLoading}
diff --git a/tools/ui/src/lib/utils/backend.ts b/tools/ui/src/lib/utils/backend.ts index 4dcb281602..e719513e10 100644 --- a/tools/ui/src/lib/utils/backend.ts +++ b/tools/ui/src/lib/utils/backend.ts @@ -34,6 +34,23 @@ export function backendModelsUrl(backend: Backend): string { return joinBackendUrl(backend.baseUrl, backend.modelsPath ?? DEFAULT_BACKEND_MODELS_PATH); } +/** + * Favicon of a backend's root domain, for endpoints with no bundled mark. API + * hosts rarely serve one, so the subdomain is dropped: `api.z.ai` -> `z.ai`. + * Returns null when the URL carries no usable host. + */ +export function backendFaviconUrl(baseUrl: string): string | null { + try { + const { hostname, protocol } = new URL(baseUrl); + const labels = hostname.split('.'); + const root = labels.length > 2 ? labels.slice(-2).join('.') : hostname; + + return `${protocol}//${root}/favicon.ico`; + } catch { + return null; + } +} + /** * Preset a backend was created from, matched on origin and path so a saved * backend keeps its branding. Returns undefined for edited or custom URLs. diff --git a/tools/ui/src/lib/utils/index.ts b/tools/ui/src/lib/utils/index.ts index 15b33702a6..79c2c67fa9 100644 --- a/tools/ui/src/lib/utils/index.ts +++ b/tools/ui/src/lib/utils/index.ts @@ -26,6 +26,7 @@ export { ApiError, apiDelete, apiFetch, apiFetchWithParams, apiPost } from './ap export { validateApiKey } from './api-key-validation'; export { backendChatUrl, + backendFaviconUrl, backendModelsUrl, createLocalBackend, findBackendPreset,