import { AudioLines, Check, Film, Flame, Image, MessageSquareText, Network, Settings2, Sparkles } from "lucide-react";
import { Button, Modal } from "antd";
import { useEffect, useState, type ReactNode } from "react";
import { ModelIcon } from "@/components/model-picker";
import { modelProtocolLabel, type ModelProtocol, type ModelProtocolDefinition, type ProtocolCapability } from "@/lib/model-protocols";
import { cn } from "@/lib/utils";
export type ModelCapabilityChoice = ProtocolCapability;
const capabilityChoices: Array<{
value: ModelCapabilityChoice;
label: string;
description: string;
icon: ReactNode;
brands: string[];
}> = [
{ value: "text", label: "文本", description: "对话与推理", icon: , brands: ["openai", "deepseek", "glm"] },
{ value: "image", label: "图片", description: "生成与编辑", icon: , brands: ["openai", "gemini"] },
{ value: "video", label: "视频", description: "生成与续写", icon: , brands: ["grok", "gemini"] },
{ value: "audio", label: "音频", description: "语音与音效", icon: , brands: ["openai"] },
];
type PickerDensity = "comfortable" | "compact";
export function CapabilityCardPicker({ value, onChange, density = "comfortable" }: { value?: ModelCapabilityChoice; onChange?: (value: ModelCapabilityChoice) => void; density?: PickerDensity }) {
return (
{capabilityChoices.map((item) => {
const selected = value === item.value;
return (
);
})}
);
}
export function ProtocolCardPicker({
capability,
value,
onChange,
density = "comfortable",
protocols = [],
}: {
capability?: ModelCapabilityChoice;
value?: ModelProtocol;
onChange?: (value: ModelProtocol) => void;
density?: PickerDensity;
protocols?: ModelProtocolDefinition[];
}) {
const availableProtocols = protocols.filter((item) => item.capability === capability && item.enabled !== false);
return (
{availableProtocols.map((protocol) => {
const selected = value === protocol.value;
return (
);
})}
);
}
export function ModelCapabilityProtocolModal({
value,
onChange,
protocols = [],
}: {
value: { capability: ModelCapabilityChoice; protocol: ModelProtocol };
onChange: (value: { capability: ModelCapabilityChoice; protocol: ModelProtocol }) => void;
protocols?: ModelProtocolDefinition[];
}) {
const [open, setOpen] = useState(false);
const [draft, setDraft] = useState(value);
useEffect(() => {
if (!open) setDraft(value);
}, [open, value]);
const updateCapability = (capability: ModelCapabilityChoice) => {
const protocol = draft.protocol && protocols.some((item) => item.value === draft.protocol && item.capability === capability) ? draft.protocol : protocols.find((item) => item.capability === capability)?.value || "";
setDraft({ capability, protocol });
};
return (
<>
}
onClick={() => {
setDraft(value);
setOpen(true);
}}
>
{capabilityLabel(value.capability)} · {modelProtocolLabel(value.protocol, protocols)}
setOpen(false)}
okText="应用配置"
cancelText="取消"
onOk={() => {
onChange(draft);
setOpen(false);
}}
>
请求协议
setDraft((current) => ({ ...current, protocol }))} />
>
);
}
function ProtocolBrandMark({ protocol, compact = false }: { protocol: ModelProtocolDefinition; compact?: boolean }) {
const iconSize = compact ? "size-6" : "size-8";
const vendor = `${protocol.vendor || ""} ${protocol.label}`.toLowerCase();
if (vendor.includes("jimeng") || vendor.includes("即梦"))
return (
);
if (vendor.includes("volcengine") || vendor.includes("火山方舟"))
return (
);
if (vendor.includes("newapi") || vendor.includes("novita"))
return (
);
const brand = vendor.includes("gemini") || vendor.includes("google") ? "gemini" : vendor.includes("grok") || vendor.includes("xai") ? "grok" : vendor.includes("openai") ? "openai" : "openai";
return ;
}
function BrandIconRow({ models, compact = false, className }: { models: string[]; compact?: boolean; className?: string }) {
return (
{models.map((model) => (
))}
);
}
function modelBrandLabel(model: string) {
const labels: Record = { openai: "OpenAI", deepseek: "DeepSeek", glm: "智谱 GLM", gemini: "Google Gemini", grok: "xAI Grok" };
return labels[model] || model;
}
function capabilityLabel(value: ModelCapabilityChoice) {
return { text: "文本", image: "图片", video: "视频", audio: "音频" }[value];
}