feat(dashboard): add Cerebras / Groq / Vercel icons + cover all pi-ai catalog providers

Audited the pi-ai built-in model catalog provider IDs against the
ProviderIcon registry and filled every remaining gap.

New icons:
  --provider-cerebras    (#f1592a)  → CerebrasIcon (concentric arcs)
  --provider-groq        (#f55036)  → GroqIcon     (lightning bolt)
  --provider-vercel      (text)     → VercelIcon   (triangle wordmark)

New aliases routed to existing icons:
  minimax-cn               → MiniMaxIcon
  azure-openai-responses   → AzureIcon
  google-gemini-cli        → GeminiIcon
  google-generative-ai     → GeminiIcon
  opencode-go              → OpencodeIcon
  vercel-ai-gateway        → VercelIcon

After this commit every provider id emitted by `@mariozechner/pi-ai`'s
register-builtins (anthropic, openai, openai-codex, google,
google-vertex, google-gemini-cli, google-antigravity,
amazon-bedrock, mistral, openrouter, fireworks, cerebras, groq,
huggingface, kimi-coding, minimax, minimax-cn, zai, xai, opencode,
opencode-go, github-copilot, azure-openai-responses,
vercel-ai-gateway) plus the user-curated set (qwen, lm-studio,
hugging-face, alibaba/tongyi, hf, lmstudio…) renders a brand icon
instead of falling back to the generic Cpu glyph.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-27 19:22:42 -07:00
parent 13b502497c
commit bb340206bf
3 changed files with 130 additions and 0 deletions

View File

@@ -344,6 +344,76 @@ function AzureIcon({ size, color, label = "Azure" }: { size: number; color: stri
);
}
// Cerebras — concentric arcs forming a stylized "C", echoing the
// official wafer-scale brand mark.
function CerebrasIcon({ size, color, label = "Cerebras" }: { size: number; color: string; label?: string }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-testid="cerebras-icon"
aria-label={label}
>
<path
d="M19.5 6.5A8.5 8.5 0 1 0 19.5 17.5"
stroke={color}
strokeWidth="2.4"
strokeLinecap="round"
fill="none"
/>
<path
d="M16.5 9A4.6 4.6 0 1 0 16.5 15"
stroke={color}
strokeWidth="1.8"
strokeLinecap="round"
fill="none"
/>
<circle cx="11" cy="12" r="1.6" fill={color} />
</svg>
);
}
// Groq — stylized lightning-bolt pulse, evoking their fast-inference
// brand identity.
function GroqIcon({ size, color, label = "Groq" }: { size: number; color: string; label?: string }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-testid="groq-icon"
aria-label={label}
>
<path
d="M14 2 4 13h6l-2 9 10-11h-6z"
fill={color}
/>
</svg>
);
}
// Vercel — the iconic equilateral triangle wordmark.
function VercelIcon({ size, color, label = "Vercel" }: { size: number; color: string; label?: string }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-testid="vercel-icon"
aria-label={label}
>
<path d="M12 2 22 21H2z" fill={color} />
</svg>
);
}
// Fireworks AI — burst of dots radiating from a central nucleus, a
// monochrome simplification of their identity mark.
function FireworksIcon({ size, color, label = "Fireworks AI" }: { size: number; color: string; label?: string }) {
@@ -472,6 +542,8 @@ const providerConfig: Record<
vertex: { component: GeminiIcon, color: "var(--provider-gemini)", label: "Google Vertex AI" },
"google-cloud-code": { component: GeminiIcon, color: "var(--provider-gemini)", label: "Google Cloud Code" },
"cloud-code": { component: GeminiIcon, color: "var(--provider-gemini)", label: "Google Cloud Code" },
"google-gemini-cli": { component: GeminiIcon, color: "var(--provider-gemini)", label: "Google Gemini CLI" },
"google-generative-ai": { component: GeminiIcon, color: "var(--provider-gemini)", label: "Google Generative AI" },
// Monochrome marks use theme-aware text color for dark/light safety.
ollama: { component: OllamaIcon, color: "var(--text)" },
@@ -482,6 +554,7 @@ const providerConfig: Record<
openrouter: { component: OpenRouterIcon, color: "var(--provider-openrouter)" },
minimax: { component: MiniMaxIcon, color: "var(--provider-minimax)" },
"minimax-cn": { component: MiniMaxIcon, color: "var(--provider-minimax)", label: "MiniMax (CN)" },
zai: { component: ZaiIcon, color: "var(--provider-zai)" },
kimi: { component: KimiIcon, color: "var(--provider-kimi)" },
@@ -495,6 +568,7 @@ const providerConfig: Record<
grok: { component: XaiIcon, color: "var(--text)", label: "xAI" },
opencode: { component: OpencodeIcon, color: "var(--provider-opencode)" },
"opencode-go": { component: OpencodeIcon, color: "var(--provider-opencode)", label: "Opencode (Go)" },
qwen: { component: QwenIcon, color: "var(--provider-qwen)" },
"qwen-coder": { component: QwenIcon, color: "var(--provider-qwen)", label: "Qwen Coder" },
@@ -513,9 +587,17 @@ const providerConfig: Record<
azure: { component: AzureIcon, color: "var(--provider-azure)" },
"azure-openai": { component: AzureIcon, color: "var(--provider-azure)", label: "Azure OpenAI" },
"azure-openai-responses": { component: AzureIcon, color: "var(--provider-azure)", label: "Azure OpenAI" },
fireworks: { component: FireworksIcon, color: "var(--provider-fireworks)", label: "Fireworks AI" },
"fireworks-ai": { component: FireworksIcon, color: "var(--provider-fireworks)", label: "Fireworks AI" },
cerebras: { component: CerebrasIcon, color: "var(--provider-cerebras)" },
groq: { component: GroqIcon, color: "var(--provider-groq)" },
vercel: { component: VercelIcon, color: "var(--provider-vercel)" },
"vercel-ai-gateway": { component: VercelIcon, color: "var(--provider-vercel)", label: "Vercel AI Gateway" },
};
export function ProviderIcon({ provider, size = "sm" }: ProviderIconProps) {

View File

@@ -570,4 +570,49 @@ describe("ProviderIcon", () => {
unmount();
}
});
// ── pi-ai catalog gap fills (Cerebras, Groq, Vercel) and aliases for
// existing icons (minimax-cn, azure-openai-responses, google-gemini-cli,
// opencode-go).
it("renders Cerebras icon for cerebras provider", () => {
render(<ProviderIcon provider="cerebras" />);
expect(screen.getByTestId("cerebras-icon")).toBeInTheDocument();
});
it("renders Groq icon for groq provider", () => {
render(<ProviderIcon provider="groq" />);
expect(screen.getByTestId("groq-icon")).toBeInTheDocument();
});
it("renders Vercel icon for vercel and vercel-ai-gateway", () => {
for (const provider of ["vercel", "vercel-ai-gateway"]) {
const { unmount } = render(<ProviderIcon provider={provider} />);
expect(screen.getByTestId("vercel-icon")).toBeInTheDocument();
unmount();
}
});
it("aliases minimax-cn to the MiniMax icon", () => {
render(<ProviderIcon provider="minimax-cn" />);
expect(screen.getByTestId("minimax-icon")).toBeInTheDocument();
});
it("aliases azure-openai-responses to the Azure icon", () => {
render(<ProviderIcon provider="azure-openai-responses" />);
expect(screen.getByTestId("azure-icon")).toBeInTheDocument();
});
it("aliases google-gemini-cli and google-generative-ai to the Gemini icon", () => {
for (const provider of ["google-gemini-cli", "google-generative-ai"]) {
const { unmount } = render(<ProviderIcon provider={provider} />);
expect(screen.getByTestId("gemini-icon")).toBeInTheDocument();
unmount();
}
});
it("aliases opencode-go to the Opencode icon", () => {
render(<ProviderIcon provider="opencode-go" />);
expect(screen.getByTestId("opencode-icon")).toBeInTheDocument();
});
});