|
1 | 1 | /** |
2 | | - * Global model display-name mapping: prepends a visual icon for recognized model slugs. |
3 | | - * Used across all GUI surfaces (dropdowns, tables, badges) so the 5.6 trio is instantly |
4 | | - * distinguishable. |
| 2 | + * Global model display-name mapping: renders an inline SVG icon for recognized |
| 3 | + * model slugs. Used across all GUI surfaces (dropdowns, tables, badges) so the |
| 4 | + * 5.6 trio is instantly distinguishable. No emoji — Lucide-style SVG only. |
5 | 5 | */ |
| 6 | +import { createElement, type ReactNode } from "react"; |
| 7 | +import { IconSun, IconGlobe, IconMoon } from "./icons"; |
6 | 8 |
|
7 | | -const MODEL_ICONS: Record<string, string> = { |
8 | | - "gpt-5.6-sol": "\u2600\uFE0F", // ☀️ sun |
9 | | - "gpt-5.6-terra": "\uD83C\uDF0D", // 🌍 earth |
10 | | - "gpt-5.6-luna": "\uD83C\uDF19", // 🌙 moon |
| 9 | +type IconComponent = typeof IconSun; |
| 10 | + |
| 11 | +const MODEL_ICON_MAP: Record<string, IconComponent> = { |
| 12 | + "gpt-5.6-sol": IconSun, |
| 13 | + "gpt-5.6-terra": IconGlobe, |
| 14 | + "gpt-5.6-luna": IconMoon, |
11 | 15 | }; |
12 | 16 |
|
13 | | -/** Return a display label with an icon prefix for recognized slugs, else the raw slug. */ |
14 | | -export function modelLabel(slug: string): string { |
15 | | - // Check bare slug first, then try the tail after a provider prefix (e.g. "openrouter/openai/gpt-5.6-sol"). |
16 | | - const icon = MODEL_ICONS[slug] ?? MODEL_ICONS[slug.slice(slug.lastIndexOf("/") + 1)]; |
17 | | - return icon ? `${icon} ${slug}` : slug; |
| 17 | +const ICON_STYLE = { width: 14, height: 14, flexShrink: 0, verticalAlign: "text-bottom" as const, marginRight: 4 }; |
| 18 | + |
| 19 | +/** Resolve the bare slug from a potentially provider-prefixed id. */ |
| 20 | +function bareSlug(slug: string): string { |
| 21 | + return slug.slice(slug.lastIndexOf("/") + 1); |
| 22 | +} |
| 23 | + |
| 24 | +/** Return the icon component for a model slug, or null. */ |
| 25 | +function resolveIcon(slug: string): IconComponent | null { |
| 26 | + return MODEL_ICON_MAP[slug] ?? MODEL_ICON_MAP[bareSlug(slug)] ?? null; |
| 27 | +} |
| 28 | + |
| 29 | +/** Render a model name with an inline SVG icon prefix (ReactNode). Falls back to plain text. */ |
| 30 | +export function modelLabel(slug: string): ReactNode { |
| 31 | + const Icon = resolveIcon(slug); |
| 32 | + if (!Icon) return slug; |
| 33 | + return createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 } }, |
| 34 | + createElement(Icon, { style: ICON_STYLE }), |
| 35 | + slug, |
| 36 | + ); |
18 | 37 | } |
19 | 38 |
|
20 | | -/** Return just the icon for a slug, or empty string if none. */ |
21 | | -export function modelIcon(slug: string): string { |
22 | | - return MODEL_ICONS[slug] ?? MODEL_ICONS[slug.slice(slug.lastIndexOf("/") + 1)] ?? ""; |
| 39 | +/** True when this slug has a visual icon. */ |
| 40 | +export function hasModelIcon(slug: string): boolean { |
| 41 | + return resolveIcon(slug) !== null; |
23 | 42 | } |
0 commit comments