import { BarChart3, Check, Copy, Edit, Loader2, Minus, Play, Plus, Terminal, TestTube2, Trash2, Zap, } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import type { AppId } from "@/lib/api"; interface ProviderActionsProps { appId?: AppId; isCurrent: boolean; isInConfig?: boolean; isTesting?: boolean; isProxyTakeover?: boolean; isOmo?: boolean; onSwitch: () => void; onEdit: () => void; onDuplicate: () => void; onTest?: () => void; onConfigureUsage?: () => void; onDelete: () => void; onRemoveFromConfig?: () => void; onDisableOmo?: () => void; onOpenTerminal?: () => void; isAutoFailoverEnabled?: boolean; isInFailoverQueue?: boolean; onToggleFailover?: (enabled: boolean) => void; isOfficialBlockedByProxy?: boolean; // Hermes v12+ providers: dict overlay — edit/delete must go through Web UI isReadOnly?: boolean; // OpenClaw: default model isDefaultModel?: boolean; onSetAsDefault?: () => void; } // 主按钮的呈现状态。title 用于 disabled 态向用户解释为何不可点击; // 因 Button 基类带 disabled:pointer-events-none,title 必须挂在外层非禁用 // 的 wrapper 上才会在 hover 时显示(见下方 包裹)。 interface MainButtonState { disabled: boolean; variant: "default" | "secondary"; className: string; icon: JSX.Element; text: string; title?: string; } export function ProviderActions({ appId, isCurrent, isInConfig = false, isTesting, isProxyTakeover = false, isOmo = false, onSwitch, onEdit, onDuplicate, onTest, onConfigureUsage, onDelete, onRemoveFromConfig, onDisableOmo, onOpenTerminal, isAutoFailoverEnabled = false, isInFailoverQueue = false, onToggleFailover, isOfficialBlockedByProxy = false, isReadOnly = false, // OpenClaw: default model isDefaultModel = false, onSetAsDefault, }: ProviderActionsProps) { const { t } = useTranslation(); const iconButtonClass = "h-8 w-8 p-1"; // 累加模式应用(OpenCode 非 OMO / OpenClaw / Hermes) const isAdditiveMode = (appId === "opencode" && !isOmo) || appId === "openclaw" || appId === "hermes"; // 故障转移模式下的按钮逻辑(累加模式和 OMO 应用不支持故障转移) const isFailoverMode = !isAdditiveMode && !isOmo && isAutoFailoverEnabled && onToggleFailover; const handleMainButtonClick = () => { if (isOmo) { if (isCurrent) { onDisableOmo?.(); } else { onSwitch(); } } else if (isAdditiveMode) { // 累加模式:切换配置状态(添加/移除) if (isInConfig) { if (onRemoveFromConfig) { onRemoveFromConfig(); } else { onDelete(); } } else { onSwitch(); // 添加到配置 } } else if (isFailoverMode) { onToggleFailover(!isInFailoverQueue); } else { onSwitch(); } }; const getMainButtonState = (): MainButtonState => { if (isOmo) { if (isCurrent) { return { disabled: false, variant: "secondary" as const, className: "bg-gray-200 text-muted-foreground hover:bg-gray-200 hover:text-muted-foreground dark:bg-gray-700 dark:hover:bg-gray-700", icon: , text: t("provider.inUse"), }; } return { disabled: false, variant: "default" as const, className: "", icon: , text: t("provider.enable"), }; } // 累加模式(OpenCode 非 OMO / OpenClaw) if (isAdditiveMode) { if (isInConfig) { return { disabled: isDefaultModel === true, variant: "secondary" as const, className: cn( "bg-orange-100 text-orange-600 hover:bg-orange-200 dark:bg-orange-900/50 dark:text-orange-400 dark:hover:bg-orange-900/70", isDefaultModel && "opacity-40 cursor-not-allowed", ), icon: , text: t("provider.removeFromConfig", { defaultValue: "移除" }), }; } return { disabled: false, variant: "default" as const, className: "bg-emerald-500 hover:bg-emerald-600 dark:bg-emerald-600 dark:hover:bg-emerald-700", icon: , text: t("provider.addToConfig", { defaultValue: "添加" }), }; } if (isFailoverMode) { if (isInFailoverQueue) { return { disabled: false, variant: "secondary" as const, className: "bg-blue-100 text-blue-600 hover:bg-blue-200 dark:bg-blue-900/50 dark:text-blue-400 dark:hover:bg-blue-900/70", icon: , text: t("failover.inQueue", { defaultValue: "已加入" }), }; } return { disabled: false, variant: "default" as const, className: "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700", icon: , text: t("failover.addQueue", { defaultValue: "加入" }), }; } if (isCurrent) { return { disabled: true, variant: "secondary" as const, className: "bg-gray-200 text-muted-foreground hover:bg-gray-200 hover:text-muted-foreground dark:bg-gray-700 dark:hover:bg-gray-700", icon: , text: t("provider.inUse"), }; } if (isOfficialBlockedByProxy) { return { disabled: true, variant: "default" as const, className: "", icon: , text: t("provider.enable"), title: t("provider.blockedByProxyHint"), }; } return { disabled: false, variant: "default" as const, className: isProxyTakeover ? "bg-emerald-500 hover:bg-emerald-600 dark:bg-emerald-600 dark:hover:bg-emerald-700" : "", icon: , text: t("provider.enable"), }; }; const buttonState = getMainButtonState(); const canDelete = !isReadOnly && (isOmo || isAdditiveMode ? true : !isCurrent); const readOnlyHint = t("provider.managedByHermesHint", { defaultValue: "由 Hermes 管理,请在 Hermes Web UI 中编辑", }); return (
{(appId === "openclaw" || appId === "hermes") && isInConfig && onSetAsDefault && (() => { const activeLabel = appId === "hermes" ? t("provider.inUse", { defaultValue: "已在用" }) : t("provider.isDefault", { defaultValue: "当前默认" }); const inactiveLabel = appId === "hermes" ? t("provider.enable", { defaultValue: "启用" }) : t("provider.setAsDefault", { defaultValue: "设为默认" }); return ( ); })()} {/* wrapper span 承接 hover:disabled 按钮自身 pointer-events:none, 原生 title 与 cursor 都必须挂在未禁用的外层元素上才会生效 */}
{onOpenTerminal && ( )}
); }