eldouma-graphics / src /components /ModelSwitcher.tsx
Moeeldouma's picture
Deploy Eldouma Graphics — Docker Space, bring-your-own-key demo
39d98a0 verified
Raw
History Blame Contribute Delete
2.79 kB
import { useEffect, useState } from "react";
import { Sparkles } from "lucide-react";
import { useStore } from "../store";
import { useAiModels } from "../lib/aiKeys";
// Brand-neutral model picker: shows our own tier names (Swift / Standard / Reasoning …) — never the
// underlying provider or raw model ids. In "bring your own key" mode the list comes from whatever the
// user's key unlocks (via useAiModels, which sends the key headers), so the switcher just works.
export function ModelSwitcher() {
const model = useStore((s) => s.model);
const setModel = useStore((s) => s.setModel);
const { models: all, defaults } = useAiModels();
const models = all.filter((m) => m.role === "chat");
const [open, setOpen] = useState(false);
// keep the selected model valid: if the current one isn't in the usable list (e.g. after connecting a
// different provider's key, or in a no-server-key demo), fall back to the resolved default / first model.
useEffect(() => {
if (!models.length) return;
if (!models.some((m) => m.id === model)) setModel(defaults.chat || models[0].id);
}, [models, defaults.chat, model, setModel]);
const cur = models.find((m) => m.id === model);
return (
<div className="relative">
<button
onClick={() => setOpen((o) => !o)}
className="flex items-center gap-1.5 rounded px-2 py-1 text-xs font-medium text-fg hover:bg-white/5"
title="Switch AI model"
>
<Sparkles size={12} className="text-primary" />
{cur?.label ?? "Model"}
<span className="text-fg-subtle">â–¾</span>
</button>
{open && (
<>
<div className="fixed inset-0 z-10" onClick={() => setOpen(false)} />
<div className="absolute bottom-full z-20 mb-1 max-h-80 w-60 overflow-y-auto rounded-lg border border-border bg-surface-raised p-1 shadow-lg">
<div className="px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.12em] text-fg-muted">AI model</div>
{models.length === 0 && (
<div className="px-2 py-2 text-[12px] text-fg-muted">No model available — connect an AI key.</div>
)}
{models.map((m) => (
<button
key={m.id}
onClick={() => { setModel(m.id); setOpen(false); }}
className={"flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[13px] text-fg hover:bg-primary/10 " + (m.id === model ? "bg-primary/15 font-medium" : "")}
>
<span className="flex-1 truncate">{m.label}</span>
{m.reasoning && <span className="rounded bg-white/10 px-1 text-[10px] text-fg-muted">reasoning</span>}
</button>
))}
</div>
</>
)}
</div>
);
}