feat(openclaw): add default model button and fix sessionsApi export

- Add "Enable as Default" button to OpenClaw provider cards
- Place enable button before add/remove button for better UX
- Disable remove button when provider is set as default
- Add sessionsApi re-export from api barrel file to fix white screen
- Add i18n keys for default model feature (zh/en/ja)
This commit is contained in:
Jason
2026-02-07 09:37:59 +08:00
parent 26a2fc4fe0
commit f1fbe324d1
10 changed files with 146 additions and 6 deletions
+31 -2
View File
@@ -10,6 +10,7 @@ import {
Terminal,
TestTube2,
Trash2,
Zap,
} from "lucide-react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
@@ -36,6 +37,9 @@ interface ProviderActionsProps {
isAutoFailoverEnabled?: boolean;
isInFailoverQueue?: boolean;
onToggleFailover?: (enabled: boolean) => void;
// OpenClaw: default model
isDefaultModel?: boolean;
onSetAsDefault?: () => void;
}
export function ProviderActions({
@@ -58,6 +62,9 @@ export function ProviderActions({
isAutoFailoverEnabled = false,
isInFailoverQueue = false,
onToggleFailover,
// OpenClaw: default model
isDefaultModel = false,
onSetAsDefault,
}: ProviderActionsProps) {
const { t } = useTranslation();
const iconButtonClass = "h-8 w-8 p-1";
@@ -120,10 +127,12 @@ export function ProviderActions({
if (isAdditiveMode) {
if (isInConfig) {
return {
disabled: false,
disabled: isDefaultModel === true,
variant: "secondary" as const,
className:
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: "移除" }),
};
@@ -191,6 +200,26 @@ export function ProviderActions({
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-[4.5rem] 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}
@@ -48,6 +48,9 @@ interface ProviderCardProps {
isInFailoverQueue?: boolean; // 是否在故障转移队列中
onToggleFailover?: (enabled: boolean) => void; // 切换故障转移队列
activeProviderId?: string; // 代理当前实际使用的供应商 ID(用于故障转移模式下标注绿色边框)
// OpenClaw: default model
isDefaultModel?: boolean;
onSetAsDefault?: () => void;
}
const extractApiUrl = (provider: Provider, fallbackText: string) => {
@@ -108,6 +111,9 @@ export function ProviderCard({
isInFailoverQueue = false,
onToggleFailover,
activeProviderId,
// OpenClaw: default model
isDefaultModel,
onSetAsDefault,
}: ProviderCardProps) {
const { t } = useTranslation();
@@ -386,6 +392,9 @@ export function ProviderCard({
isAutoFailoverEnabled={isAutoFailoverEnabled}
isInFailoverQueue={isInFailoverQueue}
onToggleFailover={onToggleFailover}
// OpenClaw: default model
isDefaultModel={isDefaultModel}
onSetAsDefault={onSetAsDefault}
/>
</div>
</div>
+32
View File
@@ -19,6 +19,7 @@ import { useQuery } from "@tanstack/react-query";
import type { Provider } from "@/types";
import type { AppId } from "@/lib/api";
import { providersApi } from "@/lib/api/providers";
import { openclawApi } from "@/lib/api/openclaw";
import { useDragSort } from "@/hooks/useDragSort";
import { ProviderCard } from "@/components/providers/ProviderCard";
import { ProviderEmptyState } from "@/components/providers/ProviderEmptyState";
@@ -51,6 +52,7 @@ interface ProviderListProps {
isProxyRunning?: boolean; // 代理服务运行状态
isProxyTakeover?: boolean; // 代理接管模式(Live配置已被接管)
activeProviderId?: string; // 代理当前实际使用的供应商 ID(用于故障转移模式下标注绿色边框)
onSetAsDefault?: (provider: Provider) => void; // OpenClaw: set as default model
}
export function ProviderList({
@@ -71,6 +73,7 @@ export function ProviderList({
isProxyRunning = false,
isProxyTakeover = false,
activeProviderId,
onSetAsDefault,
}: ProviderListProps) {
const { t } = useTranslation();
const { sortedProviders, sensors, handleDragEnd } = useDragSort(
@@ -105,6 +108,22 @@ export function ProviderList({
[appId, opencodeLiveIds, openclawLiveIds],
);
// OpenClaw: query default model to determine which provider is default
const { data: openclawDefaultModel } = useQuery({
queryKey: ["openclawDefaultModel"],
queryFn: () => openclawApi.getDefaultModel(),
enabled: appId === "openclaw",
});
const isProviderDefaultModel = useCallback(
(providerId: string): boolean => {
if (appId !== "openclaw" || !openclawDefaultModel?.primary) return false;
return openclawDefaultModel.primary.startsWith(providerId + "/");
},
[appId, openclawDefaultModel],
);
// 故障转移相关
const { data: isAutoFailoverEnabled } = useAutoFailoverEnabled(appId);
const { data: failoverQueue } = useFailoverQueue(appId);
const addToQueue = useAddToFailoverQueue();
@@ -253,6 +272,11 @@ export function ProviderList({
handleToggleFailover(provider.id, enabled)
}
activeProviderId={activeProviderId}
// OpenClaw: default model
isDefaultModel={isProviderDefaultModel(provider.id)}
onSetAsDefault={
onSetAsDefault ? () => onSetAsDefault(provider) : undefined
}
/>
);
})}
@@ -365,6 +389,9 @@ interface SortableProviderCardProps {
isInFailoverQueue: boolean;
onToggleFailover: (enabled: boolean) => void;
activeProviderId?: string;
// OpenClaw: default model
isDefaultModel?: boolean;
onSetAsDefault?: () => void;
}
function SortableProviderCard({
@@ -392,6 +419,8 @@ function SortableProviderCard({
isInFailoverQueue,
onToggleFailover,
activeProviderId,
isDefaultModel,
onSetAsDefault,
}: SortableProviderCardProps) {
const {
setNodeRef,
@@ -441,6 +470,9 @@ function SortableProviderCard({
isInFailoverQueue={isInFailoverQueue}
onToggleFailover={onToggleFailover}
activeProviderId={activeProviderId}
// OpenClaw: default model
isDefaultModel={isDefaultModel}
onSetAsDefault={onSetAsDefault}
/>
</div>
);