619cd2d2dd
- 将 Build/Plan 下拉框和 Auto-approve 开关合并为三种模式 - Plan: 调用 plan agent,只读分析 - Ask: 调用 general agent,执行操作前需确认 - Auto: 调用 general agent,自动执行无需确认 - 点击按钮即可循环切换:Ask → Auto → Plan - 每种模式有独特的颜色和图标便于区分
153 lines
4.2 KiB
TypeScript
153 lines
4.2 KiB
TypeScript
/**
|
|
* Agent Mode Selector Component
|
|
*
|
|
* 在 ChatInput 左侧显示,点击切换三种模式:
|
|
* - Plan: 调用 plan agent,只读分析
|
|
* - Ask: 调用 general agent,执行操作需确认
|
|
* - Auto: 调用 general agent,自动执行无需确认
|
|
*/
|
|
|
|
import { FileSearch, MessageCircleQuestion, Zap } from 'lucide-react';
|
|
import clsx from 'clsx';
|
|
|
|
export type AgentModeType = 'build' | 'plan';
|
|
|
|
/** 组合模式:Plan / Ask / Auto */
|
|
export type CombinedModeType = 'plan' | 'ask' | 'auto';
|
|
|
|
interface AgentModeSelectorProps {
|
|
/** 当前模式 */
|
|
mode: AgentModeType;
|
|
/** 模式变更回调 */
|
|
onModeChange: (mode: AgentModeType) => void;
|
|
/** 是否自动授权文件写入/编辑 */
|
|
autoApprove: boolean;
|
|
/** 自动授权变更回调 */
|
|
onAutoApproveChange: (enabled: boolean) => void;
|
|
/** 是否禁用 */
|
|
disabled?: boolean;
|
|
}
|
|
|
|
const modeConfig: Record<
|
|
CombinedModeType,
|
|
{
|
|
label: string;
|
|
icon: typeof FileSearch;
|
|
color: string;
|
|
bgColor: string;
|
|
borderColor: string;
|
|
hoverBg: string;
|
|
description: string;
|
|
}
|
|
> = {
|
|
plan: {
|
|
label: 'Plan',
|
|
icon: FileSearch,
|
|
color: 'text-purple-500',
|
|
bgColor: 'bg-purple-500/10',
|
|
borderColor: 'border-purple-500/30',
|
|
hoverBg: 'hover:bg-purple-500/15',
|
|
description: '只读模式,仅分析和规划',
|
|
},
|
|
ask: {
|
|
label: 'Ask',
|
|
icon: MessageCircleQuestion,
|
|
color: 'text-blue-500',
|
|
bgColor: 'bg-blue-500/10',
|
|
borderColor: 'border-blue-500/30',
|
|
hoverBg: 'hover:bg-blue-500/15',
|
|
description: '执行操作前需要确认',
|
|
},
|
|
auto: {
|
|
label: 'Auto',
|
|
icon: Zap,
|
|
color: 'text-orange-500',
|
|
bgColor: 'bg-orange-500/10',
|
|
borderColor: 'border-orange-500/30',
|
|
hoverBg: 'hover:bg-orange-500/15',
|
|
description: '自动执行,无需确认',
|
|
},
|
|
};
|
|
|
|
// 模式切换顺序:Ask -> Auto -> Plan -> Ask ...
|
|
const modeOrder: CombinedModeType[] = ['ask', 'auto', 'plan'];
|
|
|
|
/**
|
|
* 根据 agentMode 和 autoApprove 计算当前组合模式
|
|
*/
|
|
function getCombinedMode(agentMode: AgentModeType, autoApprove: boolean): CombinedModeType {
|
|
if (agentMode === 'plan') return 'plan';
|
|
return autoApprove ? 'auto' : 'ask';
|
|
}
|
|
|
|
/**
|
|
* 根据组合模式计算 agentMode 和 autoApprove
|
|
*/
|
|
function getAgentConfig(combinedMode: CombinedModeType): {
|
|
agentMode: AgentModeType;
|
|
autoApprove: boolean;
|
|
} {
|
|
switch (combinedMode) {
|
|
case 'plan':
|
|
return { agentMode: 'plan', autoApprove: false };
|
|
case 'ask':
|
|
return { agentMode: 'build', autoApprove: false };
|
|
case 'auto':
|
|
return { agentMode: 'build', autoApprove: true };
|
|
}
|
|
}
|
|
|
|
export function AgentModeSelector({
|
|
mode,
|
|
onModeChange,
|
|
autoApprove,
|
|
onAutoApproveChange,
|
|
disabled = false,
|
|
}: AgentModeSelectorProps) {
|
|
// 计算当前组合模式
|
|
const currentCombinedMode = getCombinedMode(mode, autoApprove);
|
|
const currentConfig = modeConfig[currentCombinedMode];
|
|
const ModeIcon = currentConfig.icon;
|
|
|
|
// 点击切换到下一个模式
|
|
const handleToggle = () => {
|
|
if (disabled) return;
|
|
|
|
// 找到当前模式在顺序中的索引
|
|
const currentIndex = modeOrder.indexOf(currentCombinedMode);
|
|
// 切换到下一个模式
|
|
const nextIndex = (currentIndex + 1) % modeOrder.length;
|
|
const nextMode = modeOrder[nextIndex];
|
|
|
|
// 获取新的配置并更新
|
|
const { agentMode, autoApprove: newAutoApprove } = getAgentConfig(nextMode);
|
|
onModeChange(agentMode);
|
|
onAutoApproveChange(newAutoApprove);
|
|
};
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={handleToggle}
|
|
disabled={disabled}
|
|
title={`${currentConfig.label}: ${currentConfig.description}(点击切换)`}
|
|
className={clsx(
|
|
'flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg border transition-all duration-200',
|
|
'text-sm font-medium',
|
|
disabled
|
|
? 'opacity-50 cursor-not-allowed border-line bg-surface-subtle'
|
|
: [
|
|
currentConfig.bgColor,
|
|
currentConfig.borderColor,
|
|
currentConfig.hoverBg,
|
|
'cursor-pointer active:scale-95',
|
|
],
|
|
currentConfig.color
|
|
)}
|
|
>
|
|
<ModeIcon size={16} />
|
|
<span className="hidden sm:inline">{currentConfig.label}</span>
|
|
</button>
|
|
);
|
|
}
|