Add searchable model picker
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
MoreHorizontalIcon,
|
||||
PencilIcon,
|
||||
RefreshCwIcon,
|
||||
SearchIcon,
|
||||
SquareIcon,
|
||||
WrenchIcon,
|
||||
} from "lucide-react";
|
||||
@@ -92,6 +93,8 @@ type ClawStoredSession = {
|
||||
|
||||
type ModelOption = {
|
||||
id: string;
|
||||
provider?: string;
|
||||
model_type?: string;
|
||||
};
|
||||
|
||||
type ContextBudget = {
|
||||
@@ -589,6 +592,19 @@ function ModelPickerButton({
|
||||
setModel: (model: string) => Promise<void>;
|
||||
};
|
||||
}) {
|
||||
const [query, setQuery] = useState("");
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
const filteredModels = useMemo(() => {
|
||||
if (!normalizedQuery) return status.models;
|
||||
return status.models.filter((model) => {
|
||||
const provider = model.provider ?? modelProvider(model.id);
|
||||
return (
|
||||
model.id.toLowerCase().includes(normalizedQuery) ||
|
||||
provider.toLowerCase().includes(normalizedQuery)
|
||||
);
|
||||
});
|
||||
}, [normalizedQuery, status.models]);
|
||||
|
||||
return (
|
||||
<PopoverPrimitive.Root>
|
||||
<PopoverPrimitive.Trigger asChild>
|
||||
@@ -608,13 +624,25 @@ function ModelPickerButton({
|
||||
side="top"
|
||||
align="end"
|
||||
sideOffset={10}
|
||||
className="z-50 w-72 rounded-2xl border bg-popover p-2 text-popover-foreground shadow-lg outline-none"
|
||||
className="z-50 w-[min(36rem,calc(100vw-2rem))] rounded-2xl border bg-popover p-2 text-popover-foreground shadow-lg outline-none"
|
||||
>
|
||||
<div className="px-2 py-1.5 font-medium text-muted-foreground text-xs">
|
||||
切换模型
|
||||
</div>
|
||||
<div className="relative mb-2">
|
||||
<SearchIcon className="-translate-y-1/2 pointer-events-none absolute top-1/2 left-2.5 size-3.5 text-muted-foreground" />
|
||||
<input
|
||||
type="search"
|
||||
value={query}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder="搜索模型或来源"
|
||||
className="h-8 w-full rounded-lg border bg-background pr-2.5 pl-8 text-sm outline-none transition-colors placeholder:text-muted-foreground focus:border-ring"
|
||||
/>
|
||||
</div>
|
||||
{status.models.length ? (
|
||||
status.models.map((model) => (
|
||||
<div className="h-[min(24rem,calc(100vh-12rem))] overflow-y-auto pr-1">
|
||||
{filteredModels.length ? (
|
||||
filteredModels.map((model) => (
|
||||
<button
|
||||
key={model.id}
|
||||
type="button"
|
||||
@@ -623,13 +651,19 @@ function ModelPickerButton({
|
||||
>
|
||||
<span className="min-w-0 flex-1 truncate">{model.id}</span>
|
||||
<span className="shrink-0 text-muted-foreground text-xs">
|
||||
{modelProvider(model.id)}
|
||||
{model.provider ?? modelProvider(model.id)}
|
||||
</span>
|
||||
{model.id === status.model ? (
|
||||
<CheckIcon className="size-3.5 shrink-0" />
|
||||
) : null}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<div className="px-2 py-8 text-center text-muted-foreground text-sm">
|
||||
没有匹配的模型
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-2 py-2 text-muted-foreground text-sm">
|
||||
模型列表读取中
|
||||
|
||||
Reference in New Issue
Block a user