Files
CC-Switch/src/components/providers/ProviderActions.tsx
T
Jason 357d32c17e fix(i18n): clarify OpenClaw default model button labels
Rename "Enable/Default" to "Set Default/Current Default" across
zh/en/ja locales and widen button to fit longer text.
2026-02-20 11:11:10 +08:00

316 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
BarChart3,
Check,
Copy,
Edit,
// Loader2, // Hidden: stream check feature disabled
Minus,
Play,
Plus,
Terminal,
// TestTube2, // Hidden: stream check feature disabled
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;
isLastOmo?: 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;
// OpenClaw: default model
isDefaultModel?: boolean;
onSetAsDefault?: () => void;
}
export function ProviderActions({
appId,
isCurrent,
isInConfig = false,
isTesting: _isTesting, // Hidden: stream check feature disabled
isProxyTakeover = false,
isOmo = false,
isLastOmo = false,
onSwitch,
onEdit,
onDuplicate,
onTest: _onTest, // Hidden: stream check feature disabled
onConfigureUsage,
onDelete,
onRemoveFromConfig,
onDisableOmo,
onOpenTerminal,
isAutoFailoverEnabled = false,
isInFailoverQueue = false,
onToggleFailover,
// OpenClaw: default model
isDefaultModel = false,
onSetAsDefault,
}: ProviderActionsProps) {
const { t } = useTranslation();
const iconButtonClass = "h-8 w-8 p-1";
// 累加模式应用(OpenCode 非 OMO 和 OpenClaw
const isAdditiveMode =
(appId === "opencode" && !isOmo) || appId === "openclaw";
// 故障转移模式下的按钮逻辑(累加模式和 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 = () => {
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: <Check className="h-4 w-4" />,
text: t("provider.inUse"),
};
}
return {
disabled: false,
variant: "default" as const,
className: "",
icon: <Play className="h-4 w-4" />,
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: <Minus className="h-4 w-4" />,
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: <Plus className="h-4 w-4" />,
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: <Check className="h-4 w-4" />,
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: <Plus className="h-4 w-4" />,
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: <Check className="h-4 w-4" />,
text: t("provider.inUse"),
};
}
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: <Play className="h-4 w-4" />,
text: t("provider.enable"),
};
};
const buttonState = getMainButtonState();
const canDelete = isOmo
? !(isLastOmo && isCurrent)
: isAdditiveMode
? true
: !isCurrent;
return (
<div className="flex items-center gap-1.5">
{appId === "openclaw" && isInConfig && onSetAsDefault && (
<Button
size="sm"
variant={isDefaultModel ? "secondary" : "default"}
onClick={isDefaultModel ? undefined : onSetAsDefault}
disabled={isDefaultModel}
className={cn(
"w-fit px-2.5",
isDefaultModel
? "bg-gray-200 text-muted-foreground dark:bg-gray-700 opacity-60 cursor-not-allowed"
: "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700",
)}
>
<Zap className="h-4 w-4" />
{isDefaultModel
? t("provider.isDefault", { defaultValue: "当前默认" })
: t("provider.setAsDefault", { defaultValue: "设为默认" })}
</Button>
)}
<Button
size="sm"
variant={buttonState.variant}
onClick={handleMainButtonClick}
disabled={buttonState.disabled}
className={cn("w-[4.5rem] px-2.5", buttonState.className)}
>
{buttonState.icon}
{buttonState.text}
</Button>
<div className="flex items-center gap-1">
<Button
size="icon"
variant="ghost"
onClick={onEdit}
title={t("common.edit")}
className={iconButtonClass}
>
<Edit className="h-4 w-4" />
</Button>
<Button
size="icon"
variant="ghost"
onClick={onDuplicate}
title={t("provider.duplicate")}
className={iconButtonClass}
>
<Copy className="h-4 w-4" />
</Button>
{/* Hidden: stream check feature disabled
{onTest && (
<Button
size="icon"
variant="ghost"
onClick={onTest}
disabled={isTesting}
title={t("modelTest.testProvider", "测试模型")}
className={iconButtonClass}
>
{isTesting ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<TestTube2 className="h-4 w-4" />
)}
</Button>
)}
*/}
<Button
size="icon"
variant="ghost"
onClick={onConfigureUsage}
title={t("provider.configureUsage")}
className={iconButtonClass}
>
<BarChart3 className="h-4 w-4" />
</Button>
{onOpenTerminal && (
<Button
size="icon"
variant="ghost"
onClick={onOpenTerminal}
title={t("provider.openTerminal", "打开终端")}
className={cn(
iconButtonClass,
"hover:text-emerald-600 dark:hover:text-emerald-400",
)}
>
<Terminal className="h-4 w-4" />
</Button>
)}
<Button
size="icon"
variant="ghost"
onClick={canDelete ? onDelete : undefined}
title={t("common.delete")}
className={cn(
iconButtonClass,
canDelete && "hover:text-red-500 dark:hover:text-red-400",
!canDelete && "opacity-40 cursor-not-allowed text-muted-foreground",
)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
);
}