翻译为中文
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Linux os:ubuntu-latest rust-target:x86_64-unknown-linux-gnu]) (push) Waiting to run
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Windows os:windows-latest rust-target:x86_64-pc-windows-msvc]) (push) Waiting to run
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:macOS os:macos-latest rust-target:x86_64-apple-darwin]) (push) Waiting to run
Build Test / Build Test Summary (push) Blocked by required conditions

This commit is contained in:
2025-07-03 13:03:10 +08:00
parent ee565ac80f
commit 57dc05d49b
17 changed files with 346 additions and 346 deletions
+14 -14
View File
@@ -81,7 +81,7 @@ function App() {
setProjects(projectList);
} catch (err) {
console.error("Failed to load projects:", err);
setError("Failed to load projects. Please ensure ~/.claude directory exists.");
setError("加载项目失败。请确保 ~/.claude 目录存在。");
} finally {
setLoading(false);
}
@@ -99,7 +99,7 @@ function App() {
setSelectedProject(project);
} catch (err) {
console.error("Failed to load sessions:", err);
setError("Failed to load sessions for this project.");
setError("加载此项目的会话失败。");
} finally {
setLoading(false);
}
@@ -144,9 +144,9 @@ function App() {
// Check if we're navigating away from an active Claude session
if (view === "claude-code-session" && isClaudeStreaming && activeClaudeSessionId) {
const shouldLeave = window.confirm(
"Claude is still responding. If you navigate away, Claude will continue running in the background.\n\n" +
"You can return to this session from the Projects view.\n\n" +
"Do you want to continue?"
"Claude 仍在响应中。如果您离开此页面,Claude 将继续在后台运行。\n\n" +
"您可以从项目视图返回到此会话。\n\n" +
"是否继续?"
);
if (!shouldLeave) {
@@ -172,7 +172,7 @@ function App() {
>
<h1 className="text-4xl font-bold tracking-tight">
<span className="rotating-symbol"></span>
Welcome to Claudia
欢迎使用 Claudia
</h1>
</motion.div>
@@ -190,7 +190,7 @@ function App() {
>
<div className="h-full flex flex-col items-center justify-center p-8">
<Bot className="h-16 w-16 mb-4 text-primary" />
<h2 className="text-xl font-semibold">CC Agents</h2>
<h2 className="text-xl font-semibold">CC 智能助手</h2>
</div>
</Card>
</motion.div>
@@ -207,7 +207,7 @@ function App() {
>
<div className="h-full flex flex-col items-center justify-center p-8">
<FolderCode className="h-16 w-16 mb-4 text-primary" />
<h2 className="text-xl font-semibold">CC Projects</h2>
<h2 className="text-xl font-semibold">CC 项目</h2>
</div>
</Card>
</motion.div>
@@ -255,12 +255,12 @@ function App() {
onClick={() => handleViewChange("welcome")}
className="mb-4"
>
← Back to Home
← 返回主页
</Button>
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">CC Projects</h1>
<h1 className="text-3xl font-bold tracking-tight">CC 项目</h1>
<p className="mt-1 text-sm text-muted-foreground">
Browse your Claude Code sessions
浏览您的 Claude Code 会话
</p>
</div>
</motion.div>
@@ -322,7 +322,7 @@ function App() {
className="w-full"
>
<Plus className="mr-2 h-4 w-4" />
New Claude Code session
新建 Claude Code 会话
</Button>
</motion.div>
@@ -338,7 +338,7 @@ function App() {
) : (
<div className="py-8 text-center">
<p className="text-sm text-muted-foreground">
No projects found in ~/.claude/projects
在 ~/.claude/projects 中未找到项目
</p>
</div>
)}
@@ -413,7 +413,7 @@ function App() {
open={showClaudeBinaryDialog}
onOpenChange={setShowClaudeBinaryDialog}
onSuccess={() => {
setToast({ message: "Claude binary path saved successfully", type: "success" });
setToast({ message: "Claude 二进制文件路径保存成功", type: "success" });
// Trigger a refresh of the Claude version check
window.location.reload();
}}
+53 -53
View File
@@ -251,7 +251,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
const selected = await open({
directory: true,
multiple: false,
title: "Select Project Directory"
title: "选择项目目录"
});
if (selected) {
@@ -262,7 +262,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
console.error("Failed to select directory:", err);
// More detailed error logging
const errorMessage = err instanceof Error ? err.message : String(err);
setError(`Failed to select directory: ${errorMessage}`);
setError(`选择目录失败: ${errorMessage}`);
}
};
@@ -306,14 +306,14 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
setIsRunning(false);
setExecutionStartTime(null);
if (!event.payload) {
setError("Agent execution failed");
setError("智能体执行失败");
}
});
const cancelUnlisten = await listen<boolean>(`agent-cancelled:${executionRunId}`, () => {
setIsRunning(false);
setExecutionStartTime(null);
setError("Agent execution was cancelled");
setError("智能体执行已取消");
});
unlistenRefs.current = [outputUnlisten, errorUnlisten, completeUnlisten, cancelUnlisten];
@@ -327,7 +327,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
type: "result",
subtype: "error",
is_error: true,
result: `Failed to execute agent: ${err instanceof Error ? err.message : 'Unknown error'}`,
result: `智能体执行失败: ${err instanceof Error ? err.message : '未知错误'}`,
duration_ms: 0,
usage: {
input_tokens: 0,
@@ -366,7 +366,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
type: "result",
subtype: "error",
is_error: true,
result: "Execution stopped by user",
result: "执行已被用户停止",
duration_ms: elapsedTime * 1000,
usage: {
input_tokens: totalTokens,
@@ -384,7 +384,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
type: "result",
subtype: "error",
is_error: true,
result: `Failed to stop execution: ${err instanceof Error ? err.message : 'Unknown error'}`,
result: `停止执行失败: ${err instanceof Error ? err.message : '未知错误'}`,
duration_ms: elapsedTime * 1000,
usage: {
input_tokens: totalTokens,
@@ -398,7 +398,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
if (isRunning) {
// Show confirmation dialog before navigating away during execution
const shouldLeave = window.confirm(
"An agent is currently running. If you navigate away, the agent will continue running in the background. You can view running sessions in the 'Running Sessions' tab within CC Agents.\n\nDo you want to continue?"
"智能体正在运行中。如果您离开此页面,智能体将继续在后台运行。您可以在 CC Agents 的 '运行中的会话' 标签页中查看运行状态。\n\n您要继续吗?"
);
if (!shouldLeave) {
return;
@@ -420,63 +420,63 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
};
const handleCopyAsMarkdown = async () => {
let markdown = `# Agent Execution: ${agent.name}\n\n`;
markdown += `**Task:** ${task}\n`;
markdown += `**Model:** ${model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}\n`;
markdown += `**Date:** ${new Date().toISOString()}\n\n`;
let markdown = `# 智能体执行: ${agent.name}\n\n`;
markdown += `**任务:** ${task}\n`;
markdown += `**模型:** ${model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}\n`;
markdown += `**日期:** ${new Date().toISOString()}\n\n`;
markdown += `---\n\n`;
for (const msg of messages) {
if (msg.type === "system" && msg.subtype === "init") {
markdown += `## System Initialization\n\n`;
markdown += `- Session ID: \`${msg.session_id || 'N/A'}\`\n`;
markdown += `- Model: \`${msg.model || 'default'}\`\n`;
if (msg.cwd) markdown += `- Working Directory: \`${msg.cwd}\`\n`;
if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(', ')}\n`;
markdown += `## 系统初始化\n\n`;
markdown += `- 会话ID: \`${msg.session_id || '无'}\`\n`;
markdown += `- 模型: \`${msg.model || '默认'}\`\n`;
if (msg.cwd) markdown += `- 工作目录: \`${msg.cwd}\`\n`;
if (msg.tools?.length) markdown += `- 工具: ${msg.tools.join(', ')}\n`;
markdown += `\n`;
} else if (msg.type === "assistant" && msg.message) {
markdown += `## Assistant\n\n`;
markdown += `## 助手\n\n`;
for (const content of msg.message.content || []) {
if (content.type === "text") {
markdown += `${content.text}\n\n`;
} else if (content.type === "tool_use") {
markdown += `### Tool: ${content.name}\n\n`;
markdown += `### 工具: ${content.name}\n\n`;
markdown += `\`\`\`json\n${JSON.stringify(content.input, null, 2)}\n\`\`\`\n\n`;
}
}
if (msg.message.usage) {
markdown += `*Tokens: ${msg.message.usage.input_tokens} in, ${msg.message.usage.output_tokens} out*\n\n`;
markdown += `*令牌: ${msg.message.usage.input_tokens} 输入, ${msg.message.usage.output_tokens} 输出*\n\n`;
}
} else if (msg.type === "user" && msg.message) {
markdown += `## User\n\n`;
markdown += `## 用户\n\n`;
for (const content of msg.message.content || []) {
if (content.type === "text") {
markdown += `${content.text}\n\n`;
} else if (content.type === "tool_result") {
markdown += `### Tool Result\n\n`;
markdown += `### 工具结果\n\n`;
markdown += `\`\`\`\n${content.content}\n\`\`\`\n\n`;
}
}
} else if (msg.type === "result") {
markdown += `## Execution Result\n\n`;
markdown += `## 执行结果\n\n`;
if (msg.result) {
markdown += `${msg.result}\n\n`;
}
if (msg.error) {
markdown += `**Error:** ${msg.error}\n\n`;
markdown += `**错误:** ${msg.error}\n\n`;
}
if (msg.cost_usd !== undefined) {
markdown += `- **Cost:** $${msg.cost_usd.toFixed(4)} USD\n`;
markdown += `- **费用:** $${msg.cost_usd.toFixed(4)} 美元\n`;
}
if (msg.duration_ms !== undefined) {
markdown += `- **Duration:** ${(msg.duration_ms / 1000).toFixed(2)}s\n`;
markdown += `- **持续时间:** ${(msg.duration_ms / 1000).toFixed(2)}秒\n`;
}
if (msg.num_turns !== undefined) {
markdown += `- **Turns:** ${msg.num_turns}\n`;
markdown += `- **轮次:** ${msg.num_turns}\n`;
}
if (msg.usage) {
const total = msg.usage.input_tokens + msg.usage.output_tokens;
markdown += `- **Total Tokens:** ${total} (${msg.usage.input_tokens} in, ${msg.usage.output_tokens} out)\n`;
markdown += `- **总令牌数:** ${total} (${msg.usage.input_tokens} 输入, ${msg.usage.output_tokens} 输出)\n`;
}
}
}
@@ -520,12 +520,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{isRunning && (
<div className="flex items-center gap-1">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span className="text-xs text-green-600 font-medium">Running</span>
<span className="text-xs text-green-600 font-medium">运行中</span>
</div>
)}
</div>
<p className="text-xs text-muted-foreground">
{isRunning ? "Click back to return to main menu - view in CC Agents > Running Sessions" : "Execute CC Agent"}
{isRunning ? "点击返回主菜单 - 在 CC Agents > 运行中的会话 中查看" : "执行 CC 智能体"}
</p>
</div>
</div>
@@ -541,7 +541,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="flex items-center gap-2"
>
<Maximize2 className="h-4 w-4" />
Fullscreen
全屏
</Button>
<Popover
trigger={
@@ -551,7 +551,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="flex items-center gap-2"
>
<Copy className="h-4 w-4" />
Copy Output
复制输出
<ChevronDown className="h-3 w-3" />
</Button>
}
@@ -563,7 +563,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="w-full justify-start"
onClick={handleCopyAsJsonl}
>
Copy as JSONL
复制为 JSONL
</Button>
<Button
variant="ghost"
@@ -571,7 +571,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="w-full justify-start"
onClick={handleCopyAsMarkdown}
>
Copy as Markdown
复制为 Markdown
</Button>
</div>
}
@@ -603,12 +603,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{/* Project Path */}
<div className="space-y-2">
<Label>Project Path</Label>
<Label>项目路径</Label>
<div className="flex gap-2">
<Input
value={projectPath}
onChange={(e) => setProjectPath(e.target.value)}
placeholder="Select or enter project path"
placeholder="选择或输入项目路径"
disabled={isRunning}
className="flex-1"
/>
@@ -625,7 +625,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{/* Model Selection */}
<div className="space-y-2">
<Label>Model</Label>
<Label>模型</Label>
<div className="flex gap-3">
<button
type="button"
@@ -683,12 +683,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{/* Task Input */}
<div className="space-y-2">
<Label>Task</Label>
<Label>任务</Label>
<div className="flex gap-2">
<Input
value={task}
onChange={(e) => setTask(e.target.value)}
placeholder="Enter the task for the agent"
placeholder="输入智能体的任务"
disabled={isRunning}
className="flex-1"
onKeyPress={(e) => {
@@ -705,12 +705,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{isRunning ? (
<>
<StopCircle className="mr-2 h-4 w-4" />
Stop
停止
</>
) : (
<>
<Play className="mr-2 h-4 w-4" />
Execute
执行
</>
)}
</Button>
@@ -741,9 +741,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{messages.length === 0 && !isRunning && (
<div className="flex flex-col items-center justify-center h-full text-center">
<Terminal className="h-16 w-16 text-muted-foreground mb-4" />
<h3 className="text-lg font-medium mb-2">Ready to Execute</h3>
<h3 className="text-lg font-medium mb-2">准备执行</h3>
<p className="text-sm text-muted-foreground">
Select a project path and enter a task to run the agent
选择项目路径并输入任务以运行智能体
</p>
</div>
)}
@@ -752,7 +752,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
<div className="flex items-center justify-center h-full">
<div className="flex items-center gap-3">
<Loader2 className="h-6 w-6 animate-spin" />
<span className="text-sm text-muted-foreground">Initializing agent...</span>
<span className="text-sm text-muted-foreground">初始化智能体中...</span>
</div>
</div>
)}
@@ -806,11 +806,11 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
<div className="flex items-center justify-between p-4 border-b border-border">
<div className="flex items-center gap-2">
{renderIcon()}
<h2 className="text-lg font-semibold">{agent.name} - Output</h2>
<h2 className="text-lg font-semibold">{agent.name} - 输出</h2>
{isRunning && (
<div className="flex items-center gap-1">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span className="text-xs text-green-600 font-medium">Running</span>
<span className="text-xs text-green-600 font-medium">运行中</span>
</div>
)}
</div>
@@ -823,7 +823,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="flex items-center gap-2"
>
<Copy className="h-4 w-4" />
Copy Output
复制输出
<ChevronDown className="h-3 w-3" />
</Button>
}
@@ -835,7 +835,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="w-full justify-start"
onClick={handleCopyAsJsonl}
>
Copy as JSONL
复制为 JSONL
</Button>
<Button
variant="ghost"
@@ -843,7 +843,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="w-full justify-start"
onClick={handleCopyAsMarkdown}
>
Copy as Markdown
复制为 Markdown
</Button>
</div>
}
@@ -858,7 +858,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="flex items-center gap-2"
>
<X className="h-4 w-4" />
Close
关闭
</Button>
</div>
</div>
@@ -883,9 +883,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{messages.length === 0 && !isRunning && (
<div className="flex flex-col items-center justify-center h-full text-center">
<Terminal className="h-16 w-16 text-muted-foreground mb-4" />
<h3 className="text-lg font-medium mb-2">Ready to Execute</h3>
<h3 className="text-lg font-medium mb-2">准备执行</h3>
<p className="text-sm text-muted-foreground">
Select a project path and enter a task to run the agent
选择项目路径并输入任务以运行智能体
</p>
</div>
)}
@@ -894,7 +894,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
<div className="flex items-center justify-center h-full">
<div className="flex items-center gap-3">
<Loader2 className="h-6 w-6 animate-spin" />
<span className="text-sm text-muted-foreground">Initializing agent...</span>
<span className="text-sm text-muted-foreground">初始化智能体中...</span>
</div>
</div>
)}
+36 -36
View File
@@ -96,8 +96,8 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
setAgents(agentsList);
} catch (err) {
console.error("Failed to load agents:", err);
setError("Failed to load agents");
setToast({ message: "Failed to load agents", type: "error" });
setError("加载智能体失败");
setToast({ message: "加载智能体失败", type: "error" });
} finally {
setLoading(false);
}
@@ -134,12 +134,12 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
try {
setIsDeleting(true);
await api.deleteAgent(agentToDelete.id);
setToast({ message: "Agent deleted successfully", type: "success" });
setToast({ message: "智能体删除成功", type: "success" });
await loadAgents();
await loadRuns(); // Reload runs as they might be affected
} catch (err) {
console.error("Failed to delete agent:", err);
setToast({ message: "Failed to delete agent", type: "error" });
setToast({ message: "删除智能体失败", type: "error" });
} finally {
setIsDeleting(false);
setShowDeleteDialog(false);
@@ -168,13 +168,13 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
const handleAgentCreated = async () => {
setView("list");
await loadAgents();
setToast({ message: "Agent created successfully", type: "success" });
setToast({ message: "智能体创建成功", type: "success" });
};
const handleAgentUpdated = async () => {
setView("list");
await loadAgents();
setToast({ message: "Agent updated successfully", type: "success" });
setToast({ message: "智能体更新成功", type: "success" });
};
// const handleRunClick = (run: AgentRunWithMetrics) => {
@@ -211,10 +211,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
filePath
});
setToast({ message: `Agent "${agent.name}" exported successfully`, type: "success" });
setToast({ message: `智能体 "${agent.name}" 导出成功`, type: "success" });
} catch (err) {
console.error("Failed to export agent:", err);
setToast({ message: "Failed to export agent", type: "error" });
setToast({ message: "导出智能体失败", type: "error" });
}
};
@@ -237,7 +237,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
// Import the agent from the selected file
await api.importAgentFromFile(filePath as string);
setToast({ message: "Agent imported successfully", type: "success" });
setToast({ message: "智能体导入成功", type: "success" });
await loadAgents();
} catch (err) {
console.error("Failed to import agent:", err);
@@ -310,9 +310,9 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<h1 className="text-2xl font-bold">CC Agents</h1>
<h1 className="text-2xl font-bold">CC 智能体</h1>
<p className="text-sm text-muted-foreground">
Manage your Claude Code agents
管理您的 Claude Code 智能体
</p>
</div>
</div>
@@ -325,18 +325,18 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
className="flex items-center gap-2"
>
<Download className="h-4 w-4" />
Import
导入
<ChevronDown className="h-3 w-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleImportAgent}>
<FileJson className="h-4 w-4 mr-2" />
From File
从文件
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setShowGitHubBrowser(true)}>
<Globe className="h-4 w-4 mr-2" />
From GitHub
从 GitHub
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -346,7 +346,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
className="flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Create CC Agent
创建 CC 智能体
</Button>
</div>
</div>
@@ -377,7 +377,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
>
<div className="flex items-center gap-2">
<Bot className="h-4 w-4" />
Agents
智能体
</div>
</button>
<button
@@ -391,7 +391,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
>
<div className="flex items-center gap-2">
<Play className="h-4 w-4" />
Running Sessions
运行中的会话
</div>
</button>
</nav>
@@ -418,13 +418,13 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
) : agents.length === 0 ? (
<div className="flex flex-col items-center justify-center h-64 text-center">
<Bot className="h-16 w-16 text-muted-foreground mb-4" />
<h3 className="text-lg font-medium mb-2">No agents yet</h3>
<h3 className="text-lg font-medium mb-2">暂无智能体</h3>
<p className="text-sm text-muted-foreground mb-4">
Create your first CC Agent to get started
创建您的第一个 CC 智能体开始使用
</p>
<Button onClick={() => setView("create")} size="default">
<Plus className="h-4 w-4 mr-2" />
Create CC Agent
创建 CC 智能体
</Button>
</div>
) : (
@@ -448,7 +448,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
{agent.name}
</h3>
<p className="text-xs text-muted-foreground">
Created: {new Date(agent.created_at).toLocaleDateString()}
创建时间:{new Date(agent.created_at).toLocaleDateString()}
</p>
</CardContent>
<CardFooter className="p-4 pt-0 flex justify-center gap-1 flex-wrap">
@@ -460,7 +460,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
title="Execute agent"
>
<Play className="h-3 w-3" />
Execute
执行
</Button>
<Button
size="sm"
@@ -470,7 +470,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
title="Edit agent"
>
<Edit className="h-3 w-3" />
Edit
编辑
</Button>
<Button
size="sm"
@@ -480,7 +480,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
title="Export agent to .claudia.json"
>
<Upload className="h-3 w-3" />
Export
导出
</Button>
<Button
size="sm"
@@ -490,7 +490,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
title="Delete agent"
>
<Trash2 className="h-3 w-3" />
Delete
删除
</Button>
</CardFooter>
</Card>
@@ -508,10 +508,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage === 1}
>
Previous
上一页
</Button>
<span className="flex items-center px-3 text-sm">
Page {currentPage} of {totalPages}
第 {currentPage} 页,共 {totalPages} 页
</span>
<Button
size="sm"
@@ -519,7 +519,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
disabled={currentPage === totalPages}
>
Next
下一页
</Button>
</div>
)}
@@ -532,7 +532,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
<div className="overflow-hidden">
<div className="flex items-center gap-2 mb-4">
<History className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold">Recent Executions</h2>
<h2 className="text-lg font-semibold">最近执行</h2>
</div>
{runsLoading ? (
<div className="flex items-center justify-center h-32">
@@ -582,7 +582,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
onImportSuccess={async () => {
setShowGitHubBrowser(false);
await loadAgents();
setToast({ message: "Agent imported successfully from GitHub", type: "success" });
setToast({ message: "从 GitHub 导入智能体成功", type: "success" });
}}
/>
@@ -592,11 +592,11 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Trash2 className="h-5 w-5 text-destructive" />
Delete Agent
删除智能体
</DialogTitle>
<DialogDescription>
Are you sure you want to delete the agent "{agentToDelete?.name}"?
This action cannot be undone and will permanently remove the agent and all its associated data.
您确定要删除智能体 "{agentToDelete?.name}" 吗?
此操作无法撤销,将永久删除该智能体及其所有相关数据。
</DialogDescription>
</DialogHeader>
<DialogFooter className="flex flex-col-reverse sm:flex-row sm:justify-end gap-2">
@@ -606,7 +606,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
disabled={isDeleting}
className="w-full sm:w-auto"
>
Cancel
取消
</Button>
<Button
variant="destructive"
@@ -617,12 +617,12 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
{isDeleting ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
Deleting...
删除中...
</>
) : (
<>
<Trash2 className="h-4 w-4 mr-2" />
Delete Agent
删除智能体
</>
)}
</Button>
+13 -13
View File
@@ -39,7 +39,7 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C
const handleSave = async () => {
if (!selectedInstallation) {
onError("Please select a Claude installation");
onError("请选择一个 Claude 安装");
return;
}
@@ -50,7 +50,7 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C
onOpenChange(false);
} catch (error) {
console.error("Failed to save Claude binary path:", error);
onError(error instanceof Error ? error.message : "Failed to save Claude binary path");
onError(error instanceof Error ? error.message : "保存 Claude 二进制路径失败");
} finally {
setIsValidating(false);
}
@@ -62,29 +62,29 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FileQuestion className="w-5 h-5" />
Select Claude Code Installation
选择 Claude Code 安装
</DialogTitle>
<DialogDescription className="space-y-3 mt-4">
{checkingInstallations ? (
<div className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
<span className="ml-2 text-sm text-muted-foreground">Searching for Claude installations...</span>
<span className="ml-2 text-sm text-muted-foreground">正在搜索 Claude 安装...</span>
</div>
) : hasInstallations ? (
<p>
Multiple Claude Code installations were found on your system.
Please select which one you'd like to use.
在您的系统中发现了多个 Claude Code 安装。
请选择您想要使用的版本。
</p>
) : (
<>
<p>
Claude Code was not found in any of the common installation locations.
Please install Claude Code to continue.
在常见安装位置未找到 Claude Code。
请安装 Claude Code 以继续。
</p>
<div className="flex items-center gap-2 p-3 bg-muted rounded-md">
<AlertCircle className="w-4 h-4 text-muted-foreground" />
<p className="text-sm text-muted-foreground">
<span className="font-medium">Searched locations:</span> PATH, /usr/local/bin,
<span className="font-medium">搜索位置:</span> PATH, /usr/local/bin,
/opt/homebrew/bin, ~/.nvm/versions/node/*/bin, ~/.claude/local, ~/.local/bin
</p>
</div>
@@ -94,7 +94,7 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C
<div className="flex items-center gap-2 p-3 bg-muted rounded-md">
<Terminal className="w-4 h-4 text-muted-foreground" />
<p className="text-sm text-muted-foreground">
<span className="font-medium">Tip:</span> You can install Claude Code using{" "}
<span className="font-medium">提示:</span> 您可以使用以下命令安装 Claude Code{" "}
<code className="px-1 py-0.5 bg-black/10 dark:bg-white/10 rounded">npm install -g @claude</code>
</p>
</div>
@@ -118,20 +118,20 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C
className="mr-auto"
>
<ExternalLink className="w-4 h-4 mr-2" />
Installation Guide
安装指南
</Button>
<Button
variant="outline"
onClick={() => onOpenChange(false)}
disabled={isValidating}
>
Cancel
取消
</Button>
<Button
onClick={handleSave}
disabled={isValidating || !selectedInstallation || !hasInstallations}
>
{isValidating ? "Validating..." : hasInstallations ? "Save Selection" : "No Installations Found"}
{isValidating ? "验证中..." : hasInstallations ? "保存选择" : "未找到安装"}
</Button>
</DialogFooter>
</DialogContent>
+20 -20
View File
@@ -56,12 +56,12 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
const handleSave = async () => {
if (!name.trim()) {
setError("Agent name is required");
setError("智能体名称为必填项");
return;
}
if (!systemPrompt.trim()) {
setError("System prompt is required");
setError("系统提示词为必填项");
return;
}
@@ -91,9 +91,9 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
onAgentCreated();
} catch (err) {
console.error("Failed to save agent:", err);
setError(isEditMode ? "Failed to update agent" : "Failed to create agent");
setError(isEditMode ? "更新智能体失败" : "创建智能体失败");
setToast({
message: isEditMode ? "Failed to update agent" : "Failed to create agent",
message: isEditMode ? "更新智能体失败" : "创建智能体失败",
type: "error"
});
} finally {
@@ -107,7 +107,7 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
systemPrompt !== (agent?.system_prompt || "") ||
defaultTask !== (agent?.default_task || "") ||
model !== (agent?.model || "sonnet")) &&
!confirm("You have unsaved changes. Are you sure you want to leave?")) {
!confirm("您有未保存的更改。您确定要离开吗?")) {
return;
}
onBack();
@@ -134,10 +134,10 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
</Button>
<div>
<h2 className="text-lg font-semibold">
{isEditMode ? "Edit CC Agent" : "Create CC Agent"}
{isEditMode ? "编辑 CC 智能体" : "创建 CC 智能体"}
</h2>
<p className="text-xs text-muted-foreground">
{isEditMode ? "Update your Claude Code agent" : "Create a new Claude Code agent"}
{isEditMode ? "更新您的 Claude Code 智能体" : "创建一个新的 Claude Code 智能体"}
</p>
</div>
</div>
@@ -152,7 +152,7 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
) : (
<Save className="mr-2 h-4 w-4" />
)}
{saving ? "Saving..." : "Save"}
{saving ? "保存中..." : "保存"}
</Button>
</motion.div>
@@ -178,24 +178,24 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
{/* Basic Information */}
<div className="space-y-4">
<div>
<h3 className="text-sm font-medium mb-4">Basic Information</h3>
<h3 className="text-sm font-medium mb-4">基本信息</h3>
</div>
{/* Name and Icon */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="name">Agent Name</Label>
<Label htmlFor="name">智能体名称</Label>
<Input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g., Code Assistant"
placeholder="例如:代码助手"
required
/>
</div>
<div className="space-y-2">
<Label>Agent Icon</Label>
<Label>智能体图标</Label>
<div
onClick={() => setShowIconPicker(true)}
className="h-10 px-3 py-2 bg-background border border-input rounded-md cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors flex items-center justify-between"
@@ -218,7 +218,7 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
{/* Model Selection */}
<div className="space-y-2">
<Label>Model</Label>
<Label>模型</Label>
<div className="flex flex-col sm:flex-row gap-3">
<button
type="button"
@@ -242,7 +242,7 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
</div>
<div className="text-left">
<div className="text-sm font-semibold">Claude 4 Sonnet</div>
<div className="text-xs opacity-80">Faster, efficient for most tasks</div>
<div className="text-xs opacity-80">速度快,适用于大多数任务</div>
</div>
</div>
</button>
@@ -269,7 +269,7 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
</div>
<div className="text-left">
<div className="text-sm font-semibold">Claude 4 Opus</div>
<div className="text-xs opacity-80">More capable, better for complex tasks</div>
<div className="text-xs opacity-80">功能更强,适用于复杂任务</div>
</div>
</div>
</button>
@@ -278,17 +278,17 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
{/* Default Task */}
<div className="space-y-2">
<Label htmlFor="default-task">Default Task (Optional)</Label>
<Label htmlFor="default-task">默认任务(可选)</Label>
<Input
id="default-task"
type="text"
placeholder="e.g., Review this code for security issues"
placeholder="例如:检查代码安全问题"
value={defaultTask}
onChange={(e) => setDefaultTask(e.target.value)}
className="max-w-md"
/>
<p className="text-xs text-muted-foreground">
This will be used as the default task placeholder when executing the agent
这将作为执行智能体时的默认任务占位符
</p>
</div>
@@ -296,9 +296,9 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
{/* System Prompt Editor */}
<div className="space-y-2">
<Label>System Prompt</Label>
<Label>系统提示词</Label>
<p className="text-xs text-muted-foreground mb-2">
Define the behavior and capabilities of your CC Agent
定义您的 CC 智能体的行为和能力
</p>
<div className="rounded-lg border border-border overflow-hidden shadow-sm" data-color-mode="dark">
<MDEditor
+21 -21
View File
@@ -74,35 +74,35 @@ type ThinkingModeConfig = {
const THINKING_MODES: ThinkingModeConfig[] = [
{
id: "auto",
name: "Auto",
description: "Let Claude decide",
name: "自动",
description: "让 Claude 决定",
level: 0
},
{
id: "think",
name: "Think",
description: "Basic reasoning",
name: "基础思考",
description: "基本推理",
level: 1,
phrase: "think"
},
{
id: "think_hard",
name: "Think Hard",
description: "Deeper analysis",
name: "深度思考",
description: "更深入的分析",
level: 2,
phrase: "think hard"
},
{
id: "think_harder",
name: "Think Harder",
description: "Extensive reasoning",
name: "专业思考",
description: "广泛的推理",
level: 3,
phrase: "think harder"
},
{
id: "ultrathink",
name: "Ultrathink",
description: "Maximum computation",
name: "超级思考",
description: "最大计算能力",
level: 4,
phrase: "ultrathink"
}
@@ -138,13 +138,13 @@ const MODELS: Model[] = [
{
id: "sonnet",
name: "Claude 4 Sonnet",
description: "Faster, efficient for most tasks",
description: "更快速,适用于大多数任务",
icon: <Zap className="h-4 w-4" />
},
{
id: "opus",
name: "Claude 4 Opus",
description: "More capable, better for complex tasks",
description: "更强大,适用于复杂任务",
icon: <Sparkles className="h-4 w-4" />
}
];
@@ -490,7 +490,7 @@ const FloatingPromptInputInner = (
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between">
<h3 className="text-sm font-medium">Compose your prompt</h3>
<h3 className="text-sm font-medium">编写您的提示词</h3>
<Button
variant="ghost"
size="icon"
@@ -514,7 +514,7 @@ const FloatingPromptInputInner = (
ref={expandedTextareaRef}
value={prompt}
onChange={handleTextChange}
placeholder="Type your prompt here..."
placeholder="请输入您的提示词..."
className="min-h-[200px] resize-none"
disabled={disabled}
onDragEnter={handleDrag}
@@ -526,7 +526,7 @@ const FloatingPromptInputInner = (
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<span className="text-xs text-muted-foreground">Model:</span>
<span className="text-xs text-muted-foreground">模型:</span>
<Button
variant="outline"
size="sm"
@@ -539,7 +539,7 @@ const FloatingPromptInputInner = (
</div>
<div className="flex items-center gap-2">
<span className="text-xs text-muted-foreground">Thinking:</span>
<span className="text-xs text-muted-foreground">思考:</span>
<Popover
trigger={
<TooltipProvider>
@@ -558,7 +558,7 @@ const FloatingPromptInputInner = (
</Button>
</TooltipTrigger>
<TooltipContent>
<p className="font-medium">{THINKING_MODES.find(m => m.id === selectedThinkingMode)?.name || "Auto"}</p>
<p className="font-medium">{THINKING_MODES.find(m => m.id === selectedThinkingMode)?.name || "自动"}</p>
<p className="text-xs text-muted-foreground">{THINKING_MODES.find(m => m.id === selectedThinkingMode)?.description}</p>
</TooltipContent>
</Tooltip>
@@ -708,7 +708,7 @@ const FloatingPromptInputInner = (
</Button>
</TooltipTrigger>
<TooltipContent>
<p className="font-medium">{THINKING_MODES.find(m => m.id === selectedThinkingMode)?.name || "Auto"}</p>
<p className="font-medium">{THINKING_MODES.find(m => m.id === selectedThinkingMode)?.name || "自动"}</p>
<p className="text-xs text-muted-foreground">{THINKING_MODES.find(m => m.id === selectedThinkingMode)?.description}</p>
</TooltipContent>
</Tooltip>
@@ -756,7 +756,7 @@ const FloatingPromptInputInner = (
value={prompt}
onChange={handleTextChange}
onKeyDown={handleKeyDown}
placeholder={dragActive ? "Drop images here..." : "Ask Claude anything..."}
placeholder={dragActive ? "将图片拖放至此..." : "向 Claude 提问任何问题..."}
disabled={disabled}
className={cn(
"min-h-[44px] max-h-[120px] resize-none pr-10",
@@ -799,7 +799,7 @@ const FloatingPromptInputInner = (
{isLoading ? (
<>
<Square className="h-4 w-4 mr-1" />
Stop
停止
</>
) : (
<Send className="h-4 w-4" />
@@ -808,7 +808,7 @@ const FloatingPromptInputInner = (
</div>
<div className="mt-2 text-xs text-muted-foreground">
Press Enter to send, Shift+Enter for new line{projectPath?.trim() && ", @ to mention files, drag & drop images"}
按回车发送,Shift+回车换行{projectPath?.trim() && ",@ 符号提及文件,拖放图片"}
</div>
</div>
</div>
+32 -32
View File
@@ -97,12 +97,12 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
*/
const handleAddStdioServer = async () => {
if (!stdioName.trim()) {
onError("Server name is required");
onError("服务器名称为必填项");
return;
}
if (!stdioCommand.trim()) {
onError("Command is required");
onError("命令为必填项");
return;
}
@@ -142,7 +142,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
onError(result.message);
}
} catch (error) {
onError("Failed to add server");
onError("添加服务器失败");
console.error("Failed to add stdio server:", error);
} finally {
setSaving(false);
@@ -154,12 +154,12 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
*/
const handleAddSseServer = async () => {
if (!sseName.trim()) {
onError("Server name is required");
onError("服务器名称为必填项");
return;
}
if (!sseUrl.trim()) {
onError("URL is required");
onError("URL 为必填项");
return;
}
@@ -195,7 +195,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
onError(result.message);
}
} catch (error) {
onError("Failed to add server");
onError("添加服务器失败");
console.error("Failed to add SSE server:", error);
} finally {
setSaving(false);
@@ -209,7 +209,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
return (
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium">Environment Variables</Label>
<Label className="text-sm font-medium">环境变量</Label>
<Button
variant="outline"
size="sm"
@@ -217,7 +217,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
className="gap-2"
>
<Plus className="h-3 w-3" />
Add Variable
添加变量
</Button>
</div>
@@ -257,9 +257,9 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
return (
<div className="p-6 space-y-6">
<div>
<h3 className="text-base font-semibold">Add MCP Server</h3>
<h3 className="text-base font-semibold">添加 MCP 服务器</h3>
<p className="text-sm text-muted-foreground mt-1">
Configure a new Model Context Protocol server
配置新的 Model Context Protocol 服务器
</p>
</div>
@@ -280,7 +280,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
<Card className="p-6 space-y-6">
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="stdio-name">Server Name</Label>
<Label htmlFor="stdio-name">服务器名称</Label>
<Input
id="stdio-name"
placeholder="my-server"
@@ -288,12 +288,12 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
onChange={(e) => setStdioName(e.target.value)}
/>
<p className="text-xs text-muted-foreground">
A unique name to identify this server
用于标识此服务器的唯一名称
</p>
</div>
<div className="space-y-2">
<Label htmlFor="stdio-command">Command</Label>
<Label htmlFor="stdio-command">命令</Label>
<Input
id="stdio-command"
placeholder="/path/to/server"
@@ -302,12 +302,12 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
className="font-mono"
/>
<p className="text-xs text-muted-foreground">
The command to execute the server
执行服务器的命令
</p>
</div>
<div className="space-y-2">
<Label htmlFor="stdio-args">Arguments (optional)</Label>
<Label htmlFor="stdio-args">参数(可选)</Label>
<Input
id="stdio-args"
placeholder="arg1 arg2 arg3"
@@ -316,19 +316,19 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
className="font-mono"
/>
<p className="text-xs text-muted-foreground">
Space-separated command arguments
空格分隔的命令参数
</p>
</div>
<div className="space-y-2">
<Label htmlFor="stdio-scope">Scope</Label>
<Label htmlFor="stdio-scope">作用域</Label>
<SelectComponent
value={stdioScope}
onValueChange={(value: string) => setStdioScope(value)}
options={[
{ value: "local", label: "Local (this project only)" },
{ value: "project", label: "Project (shared via .mcp.json)" },
{ value: "user", label: "User (all projects)" },
{ value: "local", label: "本地(仅限此项目)" },
{ value: "project", label: "项目(通过 .mcp.json 共享)" },
{ value: "user", label: "用户(所有项目)" },
]}
/>
</div>
@@ -345,12 +345,12 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
{saving ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Adding Server...
正在添加服务器...
</>
) : (
<>
<Plus className="h-4 w-4" />
Add Stdio Server
添加 Stdio 服务器
</>
)}
</Button>
@@ -363,7 +363,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
<Card className="p-6 space-y-6">
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="sse-name">Server Name</Label>
<Label htmlFor="sse-name">服务器名称</Label>
<Input
id="sse-name"
placeholder="sse-server"
@@ -371,7 +371,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
onChange={(e) => setSseName(e.target.value)}
/>
<p className="text-xs text-muted-foreground">
A unique name to identify this server
用于标识此服务器的唯一名称
</p>
</div>
@@ -385,19 +385,19 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
className="font-mono"
/>
<p className="text-xs text-muted-foreground">
The SSE endpoint URL
SSE 端点 URL
</p>
</div>
<div className="space-y-2">
<Label htmlFor="sse-scope">Scope</Label>
<Label htmlFor="sse-scope">作用域</Label>
<SelectComponent
value={sseScope}
onValueChange={(value: string) => setSseScope(value)}
options={[
{ value: "local", label: "Local (this project only)" },
{ value: "project", label: "Project (shared via .mcp.json)" },
{ value: "user", label: "User (all projects)" },
{ value: "local", label: "本地(仅限此项目)" },
{ value: "project", label: "项目(通过 .mcp.json 共享)" },
{ value: "user", label: "用户(所有项目)" },
]}
/>
</div>
@@ -414,12 +414,12 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
{saving ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Adding Server...
正在添加服务器...
</>
) : (
<>
<Plus className="h-4 w-4" />
Add SSE Server
添加 SSE 服务器
</>
)}
</Button>
@@ -433,7 +433,7 @@ export const MCPAddServer: React.FC<MCPAddServerProps> = ({
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm font-medium">
<Info className="h-4 w-4 text-primary" />
<span>Example Commands</span>
<span>命令示例</span>
</div>
<div className="space-y-2 text-xs text-muted-foreground">
<div className="font-mono bg-background p-2 rounded">
+10 -10
View File
@@ -54,7 +54,7 @@ export const MCPManager: React.FC<MCPManagerProps> = ({
setServers(serverList);
} catch (err) {
console.error("MCPManager: Failed to load MCP servers:", err);
setError("Failed to load MCP servers. Make sure Claude Code is installed.");
setError("无法加载 MCP 服务器。请确保已安装 Claude Code。");
} finally {
setLoading(false);
}
@@ -65,7 +65,7 @@ export const MCPManager: React.FC<MCPManagerProps> = ({
*/
const handleServerAdded = () => {
loadServers();
setToast({ message: "MCP server added successfully!", type: "success" });
setToast({ message: "MCP 服务器添加成功!", type: "success" });
setActiveTab("servers");
};
@@ -74,7 +74,7 @@ export const MCPManager: React.FC<MCPManagerProps> = ({
*/
const handleServerRemoved = (name: string) => {
setServers(prev => prev.filter(s => s.name !== name));
setToast({ message: `Server "${name}" removed successfully!`, type: "success" });
setToast({ message: `服务器 "${name}" 删除成功!`, type: "success" });
};
/**
@@ -84,12 +84,12 @@ export const MCPManager: React.FC<MCPManagerProps> = ({
loadServers();
if (failed === 0) {
setToast({
message: `Successfully imported ${imported} server${imported > 1 ? 's' : ''}!`,
message: `成功导入 ${imported} 个服务器!`,
type: "success"
});
} else {
setToast({
message: `Imported ${imported} server${imported > 1 ? 's' : ''}, ${failed} failed`,
message: `已导入 ${imported} 个服务器,${failed} 个失败`,
type: "error"
});
}
@@ -117,10 +117,10 @@ export const MCPManager: React.FC<MCPManagerProps> = ({
<div>
<h2 className="text-lg font-semibold flex items-center gap-2">
<Network className="h-5 w-5 text-blue-500" />
MCP Servers
MCP 服务器
</h2>
<p className="text-xs text-muted-foreground">
Manage Model Context Protocol servers
管理 Model Context Protocol 服务器
</p>
</div>
</div>
@@ -152,15 +152,15 @@ export const MCPManager: React.FC<MCPManagerProps> = ({
<TabsList className="grid w-full max-w-md grid-cols-3">
<TabsTrigger value="servers" className="gap-2">
<Network className="h-4 w-4 text-blue-500" />
Servers
服务器
</TabsTrigger>
<TabsTrigger value="add" className="gap-2">
<Plus className="h-4 w-4 text-green-500" />
Add Server
添加服务器
</TabsTrigger>
<TabsTrigger value="import" className="gap-2">
<Download className="h-4 w-4 text-purple-500" />
Import/Export
导入/导出
</TabsTrigger>
</TabsList>
+16 -16
View File
@@ -157,11 +157,11 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
const getScopeDisplayName = (scope: string) => {
switch (scope) {
case "local":
return "Local (Project-specific)";
return "本地(项目特定)";
case "project":
return "Project (Shared via .mcp.json)";
return "项目(通过 .mcp.json 共享)";
case "user":
return "User (All projects)";
return "用户(所有项目)";
default:
return scope;
}
@@ -193,7 +193,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
{server.status?.running && (
<Badge variant="outline" className="gap-1 flex-shrink-0 border-green-500/50 text-green-600 bg-green-500/10">
<CheckCircle className="h-3 w-3" />
Running
运行中
</Badge>
)}
</div>
@@ -210,7 +210,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
className="h-6 px-2 text-xs hover:bg-primary/10"
>
<ChevronDown className="h-3 w-3 mr-1" />
Show full
展开全部
</Button>
</div>
)}
@@ -225,7 +225,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
{Object.keys(server.env).length > 0 && !isExpanded && (
<div className="flex items-center gap-1 text-xs text-muted-foreground pl-9">
<span>Environment variables: {Object.keys(server.env).length}</span>
<span>环境变量:{Object.keys(server.env).length}</span>
</div>
)}
</div>
@@ -272,7 +272,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
{server.command && (
<div className="space-y-1">
<div className="flex items-center justify-between">
<p className="text-xs font-medium text-muted-foreground">Command</p>
<p className="text-xs font-medium text-muted-foreground">命令</p>
<div className="flex items-center gap-1">
<Button
variant="ghost"
@@ -281,7 +281,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
className="h-6 px-2 text-xs hover:bg-primary/10"
>
<Copy className="h-3 w-3 mr-1" />
{isCopied ? "Copied!" : "Copy"}
{isCopied ? "已复制!" : "复制"}
</Button>
<Button
variant="ghost"
@@ -290,7 +290,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
className="h-6 px-2 text-xs hover:bg-primary/10"
>
<ChevronUp className="h-3 w-3 mr-1" />
Hide
隐藏
</Button>
</div>
</div>
@@ -302,7 +302,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
{server.args && server.args.length > 0 && (
<div className="space-y-1">
<p className="text-xs font-medium text-muted-foreground">Arguments</p>
<p className="text-xs font-medium text-muted-foreground">参数</p>
<div className="text-xs font-mono bg-muted/50 p-2 rounded space-y-1">
{server.args.map((arg, idx) => (
<div key={idx} className="break-all">
@@ -325,7 +325,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
{Object.keys(server.env).length > 0 && (
<div className="space-y-1">
<p className="text-xs font-medium text-muted-foreground">Environment Variables</p>
<p className="text-xs font-medium text-muted-foreground">环境变量</p>
<div className="text-xs font-mono bg-muted/50 p-2 rounded space-y-1">
{Object.entries(server.env).map(([key, value]) => (
<div key={key} className="break-all">
@@ -357,9 +357,9 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-base font-semibold">Configured Servers</h3>
<h3 className="text-base font-semibold">已配置的服务器</h3>
<p className="text-sm text-muted-foreground">
{servers.length} server{servers.length !== 1 ? "s" : ""} configured
已配置 {servers.length} 个服务器
</p>
</div>
<Button
@@ -369,7 +369,7 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
className="gap-2 hover:bg-primary/10 hover:text-primary hover:border-primary/50"
>
<RefreshCw className="h-4 w-4" />
Refresh
刷新
</Button>
</div>
@@ -379,9 +379,9 @@ export const MCPServerList: React.FC<MCPServerListProps> = ({
<div className="p-4 bg-primary/10 rounded-full mb-4">
<Network className="h-12 w-12 text-primary" />
</div>
<p className="text-muted-foreground mb-2 font-medium">No MCP servers configured</p>
<p className="text-muted-foreground mb-2 font-medium">没有配置 MCP 服务器</p>
<p className="text-sm text-muted-foreground">
Add a server to get started with Model Context Protocol
添加服务器以开始使用 Model Context Protocol
</p>
</div>
) : (
+1 -1
View File
@@ -76,7 +76,7 @@ export const ProjectList: React.FC<ProjectListProps> = ({
<p className="text-sm truncate">{project.path}</p>
<div className="flex items-center space-x-3 text-xs text-muted-foreground">
<span>
{project.sessions.length} session{project.sessions.length !== 1 ? 's' : ''}
{project.sessions.length} 个会话
</span>
<div className="flex items-center space-x-1">
<Clock className="h-3 w-3" />
+20 -20
View File
@@ -28,7 +28,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
setRunningSessions(sessions);
} catch (error) {
console.error('Failed to load running sessions:', error);
setToast({ message: 'Failed to load running sessions', type: 'error' });
setToast({ message: '加载运行中的会话失败', type: 'error' });
} finally {
setLoading(false);
}
@@ -41,10 +41,10 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
await api.cleanupFinishedProcesses();
// Then reload the list
await loadRunningSessions();
setToast({ message: 'Running sessions list has been updated', type: 'success' });
setToast({ message: '运行中的会话列表已更新', type: 'success' });
} catch (error) {
console.error('Failed to refresh sessions:', error);
setToast({ message: 'Failed to refresh sessions', type: 'error' });
setToast({ message: '刷新会话失败', type: 'error' });
} finally {
setRefreshing(false);
}
@@ -54,15 +54,15 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
try {
const success = await api.killAgentSession(runId);
if (success) {
setToast({ message: `${agentName} session has been stopped`, type: 'success' });
setToast({ message: `${agentName} 会话已停止`, type: 'success' });
// Refresh the list after killing
await loadRunningSessions();
} else {
setToast({ message: 'Session may have already finished', type: 'error' });
setToast({ message: '会话可能已经结束', type: 'error' });
}
} catch (error) {
console.error('Failed to kill session:', error);
setToast({ message: 'Failed to terminate session', type: 'error' });
setToast({ message: '终止会话失败', type: 'error' });
}
};
@@ -78,9 +78,9 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
const getStatusBadge = (status: string) => {
switch (status) {
case 'running':
return <Badge variant="default" className="bg-green-100 text-green-800 border-green-200">Running</Badge>;
return <Badge variant="default" className="bg-green-100 text-green-800 border-green-200">运行中</Badge>;
case 'pending':
return <Badge variant="secondary">Pending</Badge>;
return <Badge variant="secondary">等待中</Badge>;
default:
return <Badge variant="outline">{status}</Badge>;
}
@@ -104,7 +104,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
<div className={`flex items-center justify-center p-8 ${className}`}>
<div className="flex items-center space-x-2">
<RefreshCw className="h-4 w-4 animate-spin" />
<span>Loading running sessions...</span>
<span>正在加载运行中的会话...</span>
</div>
</div>
);
@@ -125,7 +125,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
</Button>
)}
<Play className="h-5 w-5" />
<h2 className="text-lg font-semibold">Running Agent Sessions</h2>
<h2 className="text-lg font-semibold">运行中的代理会话</h2>
<Badge variant="secondary">{runningSessions.length}</Badge>
</div>
<Button
@@ -136,7 +136,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
className="flex items-center space-x-2"
>
<RefreshCw className={`h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />
<span>Refresh</span>
<span>刷新</span>
</Button>
</div>
@@ -145,7 +145,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
<CardContent className="flex items-center justify-center p-8">
<div className="text-center space-y-2">
<Clock className="h-8 w-8 mx-auto text-muted-foreground" />
<p className="text-muted-foreground">No agent sessions are currently running</p>
<p className="text-muted-foreground">当前没有正在运行的代理会话</p>
</div>
</CardContent>
</Card>
@@ -187,7 +187,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
className="flex items-center space-x-2"
>
<Eye className="h-4 w-4" />
<span>View Output</span>
<span>查看输出</span>
</Button>
<Button
variant="destructive"
@@ -196,7 +196,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
className="flex items-center space-x-2"
>
<Square className="h-4 w-4" />
<span>Stop</span>
<span>停止</span>
</Button>
</div>
</div>
@@ -204,28 +204,28 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
<CardContent className="pt-0">
<div className="space-y-2">
<div>
<p className="text-sm text-muted-foreground">Task</p>
<p className="text-sm text-muted-foreground">任务</p>
<p className="text-sm font-medium truncate">{session.task}</p>
</div>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Model</p>
<p className="text-muted-foreground">模型</p>
<p className="font-medium">{session.model}</p>
</div>
<div>
<p className="text-muted-foreground">Duration</p>
<p className="text-muted-foreground">持续时间</p>
<p className="font-medium">
{session.process_started_at
? formatDuration(session.process_started_at)
: 'Unknown'
: '未知'
}
</p>
</div>
</div>
<div>
<p className="text-sm text-muted-foreground">Project Path</p>
<p className="text-sm text-muted-foreground">项目路径</p>
<p className="text-xs font-mono bg-muted px-2 py-1 rounded truncate">
{session.project_path}
</p>
@@ -233,7 +233,7 @@ export function RunningSessionsView({ className, showBackButton = false, onBack
{session.session_id && (
<div>
<p className="text-sm text-muted-foreground">Session ID</p>
<p className="text-sm text-muted-foreground">会话ID</p>
<p className="text-xs font-mono bg-muted px-2 py-1 rounded truncate">
{session.session_id}
</p>
+3 -3
View File
@@ -89,7 +89,7 @@ export const SessionList: React.FC<SessionListProps> = ({
<div className="flex-1 min-w-0">
<h2 className="text-base font-medium truncate">{projectPath}</h2>
<p className="text-xs text-muted-foreground">
{sessions.length} session{sessions.length !== 1 ? 's' : ''}
{sessions.length} 个会话
</p>
</div>
</motion.div>
@@ -149,7 +149,7 @@ export const SessionList: React.FC<SessionListProps> = ({
<div className="space-y-1">
<div className="flex items-center space-x-1 text-xs text-muted-foreground">
<MessageSquare className="h-3 w-3" />
<span>First message:</span>
<span>首条消息:</span>
</div>
<p className="text-xs line-clamp-2 text-foreground/80">
{truncateText(getFirstLine(session.first_message), 100)}
@@ -173,7 +173,7 @@ export const SessionList: React.FC<SessionListProps> = ({
{session.todo_data && (
<div className="flex items-center space-x-1">
<Calendar className="h-3 w-3" />
<span>Has todo</span>
<span>有待办事项</span>
</div>
)}
</div>
+54 -54
View File
@@ -145,7 +145,7 @@ export const Settings: React.FC<SettingsProps> = ({
}
} catch (err) {
console.error("Failed to load settings:", err);
setError("Failed to load settings. Please ensure ~/.claude directory exists.");
setError("加载设置失败。请确保 ~/.claude 目录存在。");
setSettings({});
} finally {
setLoading(false);
@@ -187,11 +187,11 @@ export const Settings: React.FC<SettingsProps> = ({
setBinaryPathChanged(false);
}
setToast({ message: "Settings saved successfully!", type: "success" });
setToast({ message: "设置保存成功!", type: "success" });
} catch (err) {
console.error("Failed to save settings:", err);
setError("Failed to save settings.");
setToast({ message: "Failed to save settings", type: "error" });
setError("保存设置失败。");
setToast({ message: "保存设置失败", type: "error" });
} finally {
setSaving(false);
}
@@ -302,9 +302,9 @@ export const Settings: React.FC<SettingsProps> = ({
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<h2 className="text-lg font-semibold">Settings</h2>
<h2 className="text-lg font-semibold">设置</h2>
<p className="text-xs text-muted-foreground">
Configure Claude Code preferences
配置 Claude Code 偏好设置
</p>
</div>
</div>
@@ -318,12 +318,12 @@ export const Settings: React.FC<SettingsProps> = ({
{saving ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Saving...
保存中...
</>
) : (
<>
<Save className="h-4 w-4" />
Save Settings
保存设置
</>
)}
</Button>
@@ -355,19 +355,19 @@ export const Settings: React.FC<SettingsProps> = ({
<TabsList className="mb-6">
<TabsTrigger value="general" className="gap-2">
<Settings2 className="h-4 w-4 text-slate-500" />
General
常规
</TabsTrigger>
<TabsTrigger value="permissions" className="gap-2">
<Shield className="h-4 w-4 text-amber-500" />
Permissions
权限
</TabsTrigger>
<TabsTrigger value="environment" className="gap-2">
<Terminal className="h-4 w-4 text-blue-500" />
Environment
环境
</TabsTrigger>
<TabsTrigger value="advanced" className="gap-2">
<Code className="h-4 w-4 text-purple-500" />
Advanced
高级
</TabsTrigger>
</TabsList>
@@ -375,15 +375,15 @@ export const Settings: React.FC<SettingsProps> = ({
<TabsContent value="general" className="space-y-6">
<Card className="p-6 space-y-6">
<div>
<h3 className="text-base font-semibold mb-4">General Settings</h3>
<h3 className="text-base font-semibold mb-4">常规设置</h3>
<div className="space-y-4">
{/* Include Co-authored By */}
<div className="flex items-center justify-between">
<div className="space-y-0.5 flex-1">
<Label htmlFor="coauthored">Include "Co-authored by Claude"</Label>
<Label htmlFor="coauthored">包含"Co-authored by Claude"</Label>
<p className="text-xs text-muted-foreground">
Add Claude attribution to git commits and pull requests
在 git 提交和拉取请求中添加 Claude 署名
</p>
</div>
<Switch
@@ -396,9 +396,9 @@ export const Settings: React.FC<SettingsProps> = ({
{/* Verbose Output */}
<div className="flex items-center justify-between">
<div className="space-y-0.5 flex-1">
<Label htmlFor="verbose">Verbose Output</Label>
<Label htmlFor="verbose">详细输出</Label>
<p className="text-xs text-muted-foreground">
Show full bash and command outputs
显示完整的 bash 和命令输出
</p>
</div>
<Switch
@@ -410,7 +410,7 @@ export const Settings: React.FC<SettingsProps> = ({
{/* Cleanup Period */}
<div className="space-y-2">
<Label htmlFor="cleanup">Chat Transcript Retention (days)</Label>
<Label htmlFor="cleanup">聊天记录保留期(天)</Label>
<Input
id="cleanup"
type="number"
@@ -423,16 +423,16 @@ export const Settings: React.FC<SettingsProps> = ({
}}
/>
<p className="text-xs text-muted-foreground">
How long to retain chat transcripts locally (default: 30 days)
本地保留聊天记录的时长(默认:30 天)
</p>
</div>
{/* Claude Binary Path Selector */}
<div className="space-y-4">
<div>
<Label className="text-sm font-medium mb-2 block">Claude Code Installation</Label>
<Label className="text-sm font-medium mb-2 block">Claude Code 安装</Label>
<p className="text-xs text-muted-foreground mb-4">
Select which Claude Code installation to use
选择要使用的 Claude Code 安装版本
</p>
</div>
<ClaudeVersionSelector
@@ -441,7 +441,7 @@ export const Settings: React.FC<SettingsProps> = ({
/>
{binaryPathChanged && (
<p className="text-xs text-amber-600 dark:text-amber-400">
⚠️ Claude binary path has been changed. Remember to save your settings.
⚠️ Claude 二进制路径已更改。请记得保存您的设置。
</p>
)}
</div>
@@ -455,16 +455,16 @@ export const Settings: React.FC<SettingsProps> = ({
<Card className="p-6">
<div className="space-y-6">
<div>
<h3 className="text-base font-semibold mb-2">Permission Rules</h3>
<h3 className="text-base font-semibold mb-2">权限规则</h3>
<p className="text-sm text-muted-foreground mb-4">
Control which tools Claude Code can use without manual approval
控制 Claude Code 可以在无需手动批准的情况下使用哪些工具
</p>
</div>
{/* Allow Rules */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium text-green-500">Allow Rules</Label>
<Label className="text-sm font-medium text-green-500">允许规则</Label>
<Button
variant="outline"
size="sm"
@@ -472,13 +472,13 @@ export const Settings: React.FC<SettingsProps> = ({
className="gap-2 hover:border-green-500/50 hover:text-green-500"
>
<Plus className="h-3 w-3" />
Add Rule
添加规则
</Button>
</div>
<div className="space-y-2">
{allowRules.length === 0 ? (
<p className="text-xs text-muted-foreground py-2">
No allow rules configured. Claude will ask for approval for all tools.
未配置允许规则。Claude 将对所有工具请求批准。
</p>
) : (
allowRules.map((rule) => (
@@ -489,7 +489,7 @@ export const Settings: React.FC<SettingsProps> = ({
className="flex items-center gap-2"
>
<Input
placeholder="e.g., Bash(npm run test:*)"
placeholder="例如:Bash(npm run test:*)"
value={rule.value}
onChange={(e) => updatePermissionRule("allow", rule.id, e.target.value)}
className="flex-1"
@@ -511,7 +511,7 @@ export const Settings: React.FC<SettingsProps> = ({
{/* Deny Rules */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium text-red-500">Deny Rules</Label>
<Label className="text-sm font-medium text-red-500">拒绝规则</Label>
<Button
variant="outline"
size="sm"
@@ -519,13 +519,13 @@ export const Settings: React.FC<SettingsProps> = ({
className="gap-2 hover:border-red-500/50 hover:text-red-500"
>
<Plus className="h-3 w-3" />
Add Rule
添加规则
</Button>
</div>
<div className="space-y-2">
{denyRules.length === 0 ? (
<p className="text-xs text-muted-foreground py-2">
No deny rules configured.
未配置拒绝规则。
</p>
) : (
denyRules.map((rule) => (
@@ -536,7 +536,7 @@ export const Settings: React.FC<SettingsProps> = ({
className="flex items-center gap-2"
>
<Input
placeholder="e.g., Bash(curl:*)"
placeholder="例如:Bash(curl:*)"
value={rule.value}
onChange={(e) => updatePermissionRule("deny", rule.id, e.target.value)}
className="flex-1"
@@ -557,14 +557,14 @@ export const Settings: React.FC<SettingsProps> = ({
<div className="pt-2 space-y-2">
<p className="text-xs text-muted-foreground">
<strong>Examples:</strong>
<strong>示例:</strong>
</p>
<ul className="text-xs text-muted-foreground space-y-1 ml-4">
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Bash</code> - Allow all bash commands</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Bash(npm run build)</code> - Allow exact command</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Bash(npm run test:*)</code> - Allow commands with prefix</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Read(~/.zshrc)</code> - Allow reading specific file</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Edit(docs/**)</code> - Allow editing files in docs directory</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Bash</code> - 允许所有 bash 命令</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Bash(npm run build)</code> - 允许精确命令</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Bash(npm run test:*)</code> - 允许带前缀的命令</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Read(~/.zshrc)</code> - 允许读取特定文件</li>
<li>• <code className="px-1 py-0.5 rounded bg-green-500/10 text-green-600 dark:text-green-400">Edit(docs/**)</code> - 允许编辑 docs 目录中的文件</li>
</ul>
</div>
</div>
@@ -577,9 +577,9 @@ export const Settings: React.FC<SettingsProps> = ({
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h3 className="text-base font-semibold">Environment Variables</h3>
<h3 className="text-base font-semibold">环境变量</h3>
<p className="text-sm text-muted-foreground mt-1">
Environment variables applied to every Claude Code session
应用于每个 Claude Code 会话的环境变量
</p>
</div>
<Button
@@ -589,14 +589,14 @@ export const Settings: React.FC<SettingsProps> = ({
className="gap-2"
>
<Plus className="h-3 w-3" />
Add Variable
添加变量
</Button>
</div>
<div className="space-y-3">
{envVars.length === 0 ? (
<p className="text-xs text-muted-foreground py-2">
No environment variables configured.
未配置环境变量。
</p>
) : (
envVars.map((envVar) => (
@@ -607,14 +607,14 @@ export const Settings: React.FC<SettingsProps> = ({
className="flex items-center gap-2"
>
<Input
placeholder="KEY"
placeholder="键名"
value={envVar.key}
onChange={(e) => updateEnvVar(envVar.id, "key", e.target.value)}
className="flex-1 font-mono text-sm"
/>
<span className="text-muted-foreground">=</span>
<Input
placeholder="value"
placeholder="值"
value={envVar.value}
onChange={(e) => updateEnvVar(envVar.id, "value", e.target.value)}
className="flex-1 font-mono text-sm"
@@ -634,12 +634,12 @@ export const Settings: React.FC<SettingsProps> = ({
<div className="pt-2 space-y-2">
<p className="text-xs text-muted-foreground">
<strong>Common variables:</strong>
<strong>常用变量:</strong>
</p>
<ul className="text-xs text-muted-foreground space-y-1 ml-4">
<li>• <code className="px-1 py-0.5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400">CLAUDE_CODE_ENABLE_TELEMETRY</code> - Enable/disable telemetry (0 or 1)</li>
<li>• <code className="px-1 py-0.5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400">ANTHROPIC_MODEL</code> - Custom model name</li>
<li>• <code className="px-1 py-0.5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400">DISABLE_COST_WARNINGS</code> - Disable cost warnings (1)</li>
<li>• <code className="px-1 py-0.5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400">CLAUDE_CODE_ENABLE_TELEMETRY</code> - 启用/禁用遥测 (0 或 1)</li>
<li>• <code className="px-1 py-0.5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400">ANTHROPIC_MODEL</code> - 自定义模型名称</li>
<li>• <code className="px-1 py-0.5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400">DISABLE_COST_WARNINGS</code> - 禁用成本警告 (1)</li>
</ul>
</div>
</div>
@@ -650,15 +650,15 @@ export const Settings: React.FC<SettingsProps> = ({
<Card className="p-6">
<div className="space-y-6">
<div>
<h3 className="text-base font-semibold mb-4">Advanced Settings</h3>
<h3 className="text-base font-semibold mb-4">高级设置</h3>
<p className="text-sm text-muted-foreground mb-6">
Additional configuration options for advanced users
为高级用户提供的额外配置选项
</p>
</div>
{/* API Key Helper */}
<div className="space-y-2">
<Label htmlFor="apiKeyHelper">API Key Helper Script</Label>
<Label htmlFor="apiKeyHelper">API 密钥辅助脚本</Label>
<Input
id="apiKeyHelper"
placeholder="/path/to/generate_api_key.sh"
@@ -666,18 +666,18 @@ export const Settings: React.FC<SettingsProps> = ({
onChange={(e) => updateSetting("apiKeyHelper", e.target.value || undefined)}
/>
<p className="text-xs text-muted-foreground">
Custom script to generate auth values for API requests
用于为 API 请求生成身份验证值的自定义脚本
</p>
</div>
{/* Raw JSON Editor */}
<div className="space-y-2">
<Label>Raw Settings (JSON)</Label>
<Label>原始设置 (JSON)</Label>
<div className="p-3 rounded-md bg-muted font-mono text-xs overflow-x-auto whitespace-pre-wrap">
<pre>{JSON.stringify(settings, null, 2)}</pre>
</div>
<p className="text-xs text-muted-foreground">
This shows the raw JSON that will be saved to ~/.claude/settings.json
这显示了将保存到 ~/.claude/settings.json 的原始 JSON 数据
</p>
</div>
</div>
+7 -7
View File
@@ -87,7 +87,7 @@ export const Topbar: React.FC<TopbarProps> = ({
return (
<div className="flex items-center space-x-2 text-xs">
<Circle className="h-3 w-3 animate-pulse text-muted-foreground" />
<span className="text-muted-foreground">Checking...</span>
<span className="text-muted-foreground">检查中...</span>
</div>
);
}
@@ -125,7 +125,7 @@ export const Topbar: React.FC<TopbarProps> = ({
trigger={statusContent}
content={
<div className="space-y-3 max-w-xs">
<p className="text-sm font-medium">Claude Code not found</p>
<p className="text-sm font-medium">未找到 Claude Code</p>
<div className="rounded-md bg-muted p-3">
<pre className="text-xs font-mono whitespace-pre-wrap">
{versionStatus.output}
@@ -137,7 +137,7 @@ export const Topbar: React.FC<TopbarProps> = ({
className="w-full"
onClick={onSettingsClick}
>
Select Claude Installation
选择 Claude 安装
</Button>
<a
href="https://www.anthropic.com/claude-code"
@@ -145,7 +145,7 @@ export const Topbar: React.FC<TopbarProps> = ({
rel="noopener noreferrer"
className="flex items-center space-x-1 text-xs text-primary hover:underline"
>
<span>Install Claude Code</span>
<span>安装 Claude Code</span>
<ExternalLink className="h-3 w-3" />
</a>
</div>
@@ -180,7 +180,7 @@ export const Topbar: React.FC<TopbarProps> = ({
className="text-xs"
>
<BarChart3 className="mr-2 h-3 w-3" />
Usage Dashboard
使用情况仪表板
</Button>
<Button
@@ -210,7 +210,7 @@ export const Topbar: React.FC<TopbarProps> = ({
className="text-xs"
>
<Settings className="mr-2 h-3 w-3" />
Settings
设置
</Button>
<Button
@@ -218,7 +218,7 @@ export const Topbar: React.FC<TopbarProps> = ({
size="icon"
onClick={onInfoClick}
className="h-8 w-8"
title="About"
title="关于"
>
<Info className="h-4 w-4" />
</Button>
+44 -44
View File
@@ -82,7 +82,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
setSessionStats(sessionData);
} catch (err) {
console.error("Failed to load usage stats:", err);
setError("Failed to load usage statistics. Please try again.");
setError("加载使用统计失败,请重试。");
} finally {
setLoading(false);
}
@@ -98,7 +98,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
};
const formatNumber = (num: number): string => {
return new Intl.NumberFormat('en-US').format(num);
return new Intl.NumberFormat('zh-CN').format(num);
};
const formatTokens = (num: number): string => {
@@ -146,9 +146,9 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<h1 className="text-lg font-semibold">Usage Dashboard</h1>
<h1 className="text-lg font-semibold">使用情况仪表板</h1>
<p className="text-xs text-muted-foreground">
Track your Claude Code usage and costs
跟踪您的 Claude Code 使用情况和费用
</p>
</div>
</div>
@@ -165,7 +165,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
onClick={() => setSelectedDateRange(range)}
className="text-xs"
>
{range === "all" ? "All Time" : range === "7d" ? "Last 7 Days" : "Last 30 Days"}
{range === "all" ? "全部时间" : range === "7d" ? "最近 7 天" : "最近 30 天"}
</Button>
))}
</div>
@@ -179,7 +179,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<div className="flex items-center justify-center h-full">
<div className="text-center">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground mx-auto mb-4" />
<p className="text-sm text-muted-foreground">Loading usage statistics...</p>
<p className="text-sm text-muted-foreground">正在加载使用统计...</p>
</div>
</div>
) : error ? (
@@ -187,7 +187,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<div className="text-center max-w-md">
<p className="text-sm text-destructive mb-4">{error}</p>
<Button onClick={loadUsageStats} size="sm">
Try Again
重试
</Button>
</div>
</div>
@@ -204,7 +204,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<Card className="p-4 shimmer-hover">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground">Total Cost</p>
<p className="text-xs text-muted-foreground">总费用</p>
<p className="text-2xl font-bold mt-1">
{formatCurrency(stats.total_cost)}
</p>
@@ -217,7 +217,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<Card className="p-4 shimmer-hover">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground">Total Sessions</p>
<p className="text-xs text-muted-foreground">总会话数</p>
<p className="text-2xl font-bold mt-1">
{formatNumber(stats.total_sessions)}
</p>
@@ -230,7 +230,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<Card className="p-4 shimmer-hover">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground">Total Tokens</p>
<p className="text-xs text-muted-foreground">总令牌数</p>
<p className="text-2xl font-bold mt-1">
{formatTokens(stats.total_tokens)}
</p>
@@ -243,7 +243,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<Card className="p-4 shimmer-hover">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground">Avg Cost/Session</p>
<p className="text-xs text-muted-foreground">平均会话费用</p>
<p className="text-2xl font-bold mt-1">
{formatCurrency(
stats.total_sessions > 0
@@ -260,32 +260,32 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{/* Tabs for different views */}
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-5">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="models">By Model</TabsTrigger>
<TabsTrigger value="projects">By Project</TabsTrigger>
<TabsTrigger value="sessions">By Session</TabsTrigger>
<TabsTrigger value="timeline">Timeline</TabsTrigger>
<TabsTrigger value="overview">概览</TabsTrigger>
<TabsTrigger value="models">按模型</TabsTrigger>
<TabsTrigger value="projects">按项目</TabsTrigger>
<TabsTrigger value="sessions">按会话</TabsTrigger>
<TabsTrigger value="timeline">时间线</TabsTrigger>
</TabsList>
{/* Overview Tab */}
<TabsContent value="overview" className="space-y-4">
<Card className="p-6">
<h3 className="text-sm font-semibold mb-4">Token Breakdown</h3>
<h3 className="text-sm font-semibold mb-4">令牌分布</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<p className="text-xs text-muted-foreground">Input Tokens</p>
<p className="text-xs text-muted-foreground">输入令牌</p>
<p className="text-lg font-semibold">{formatTokens(stats.total_input_tokens)}</p>
</div>
<div>
<p className="text-xs text-muted-foreground">Output Tokens</p>
<p className="text-xs text-muted-foreground">输出令牌</p>
<p className="text-lg font-semibold">{formatTokens(stats.total_output_tokens)}</p>
</div>
<div>
<p className="text-xs text-muted-foreground">Cache Write</p>
<p className="text-xs text-muted-foreground">缓存写入</p>
<p className="text-lg font-semibold">{formatTokens(stats.total_cache_creation_tokens)}</p>
</div>
<div>
<p className="text-xs text-muted-foreground">Cache Read</p>
<p className="text-xs text-muted-foreground">缓存读取</p>
<p className="text-lg font-semibold">{formatTokens(stats.total_cache_read_tokens)}</p>
</div>
</div>
@@ -294,7 +294,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{/* Quick Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card className="p-6">
<h3 className="text-sm font-semibold mb-4">Most Used Models</h3>
<h3 className="text-sm font-semibold mb-4">常用模型</h3>
<div className="space-y-3">
{stats.by_model.slice(0, 3).map((model) => (
<div key={model.model} className="flex items-center justify-between">
@@ -303,7 +303,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{getModelDisplayName(model.model)}
</Badge>
<span className="text-xs text-muted-foreground">
{model.session_count} sessions
{model.session_count} 个会话
</span>
</div>
<span className="text-sm font-medium">
@@ -315,7 +315,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
</Card>
<Card className="p-6">
<h3 className="text-sm font-semibold mb-4">Top Projects</h3>
<h3 className="text-sm font-semibold mb-4">热门项目</h3>
<div className="space-y-3">
{stats.by_project.slice(0, 3).map((project) => (
<div key={project.project_path} className="flex items-center justify-between">
@@ -324,7 +324,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{project.project_path}
</span>
<span className="text-xs text-muted-foreground">
{project.session_count} sessions
{project.session_count} 个会话
</span>
</div>
<span className="text-sm font-medium">
@@ -340,7 +340,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{/* Models Tab */}
<TabsContent value="models">
<Card className="p-6">
<h3 className="text-sm font-semibold mb-4">Usage by Model</h3>
<h3 className="text-sm font-semibold mb-4">按模型使用情况</h3>
<div className="space-y-4">
{stats.by_model.map((model) => (
<div key={model.model} className="space-y-2">
@@ -353,7 +353,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{getModelDisplayName(model.model)}
</Badge>
<span className="text-sm text-muted-foreground">
{model.session_count} sessions
{model.session_count} 个会话
</span>
</div>
<span className="text-sm font-semibold">
@@ -362,19 +362,19 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
</div>
<div className="grid grid-cols-4 gap-2 text-xs">
<div>
<span className="text-muted-foreground">Input: </span>
<span className="text-muted-foreground">输入: </span>
<span className="font-medium">{formatTokens(model.input_tokens)}</span>
</div>
<div>
<span className="text-muted-foreground">Output: </span>
<span className="text-muted-foreground">输出: </span>
<span className="font-medium">{formatTokens(model.output_tokens)}</span>
</div>
<div>
<span className="text-muted-foreground">Cache W: </span>
<span className="text-muted-foreground">缓存写: </span>
<span className="font-medium">{formatTokens(model.cache_creation_tokens)}</span>
</div>
<div>
<span className="text-muted-foreground">Cache R: </span>
<span className="text-muted-foreground">缓存读: </span>
<span className="font-medium">{formatTokens(model.cache_read_tokens)}</span>
</div>
</div>
@@ -387,7 +387,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{/* Projects Tab */}
<TabsContent value="projects">
<Card className="p-6">
<h3 className="text-sm font-semibold mb-4">Usage by Project</h3>
<h3 className="text-sm font-semibold mb-4">按项目使用情况</h3>
<div className="space-y-3">
{stats.by_project.map((project) => (
<div key={project.project_path} className="flex items-center justify-between py-2 border-b border-border last:border-0">
@@ -397,17 +397,17 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
</span>
<div className="flex items-center space-x-3 mt-1">
<span className="text-xs text-muted-foreground">
{project.session_count} sessions
{project.session_count} 个会话
</span>
<span className="text-xs text-muted-foreground">
{formatTokens(project.total_tokens)} tokens
{formatTokens(project.total_tokens)} 个令牌
</span>
</div>
</div>
<div className="text-right">
<p className="text-sm font-semibold">{formatCurrency(project.total_cost)}</p>
<p className="text-xs text-muted-foreground">
{formatCurrency(project.total_cost / project.session_count)}/session
{formatCurrency(project.total_cost / project.session_count)}/会话
</p>
</div>
</div>
@@ -419,7 +419,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{/* Sessions Tab */}
<TabsContent value="sessions">
<Card className="p-6">
<h3 className="text-sm font-semibold mb-4">Usage by Session</h3>
<h3 className="text-sm font-semibold mb-4">按会话使用情况</h3>
<div className="space-y-3">
{sessionStats?.map((session) => (
<div key={`${session.project_path}-${session.project_name}`} className="flex items-center justify-between py-2 border-b border-border last:border-0">
@@ -451,7 +451,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<Card className="p-6">
<h3 className="text-sm font-semibold mb-6 flex items-center space-x-2">
<Calendar className="h-4 w-4" />
<span>Daily Usage</span>
<span>每日使用情况</span>
</h3>
{stats.by_date.length > 0 ? (() => {
const maxCost = Math.max(...stats.by_date.map(d => d.total_cost), 0);
@@ -471,7 +471,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{stats.by_date.slice().reverse().map((day) => {
const heightPercent = maxCost > 0 ? (day.total_cost / maxCost) * 100 : 0;
const date = new Date(day.date.replace(/-/g, '/'));
const formattedDate = date.toLocaleDateString('en-US', {
const formattedDate = date.toLocaleDateString('zh-CN', {
weekday: 'short',
month: 'short',
day: 'numeric'
@@ -484,13 +484,13 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<div className="bg-background border border-border rounded-lg shadow-lg p-3 whitespace-nowrap">
<p className="text-sm font-semibold">{formattedDate}</p>
<p className="text-sm text-muted-foreground mt-1">
Cost: {formatCurrency(day.total_cost)}
费用: {formatCurrency(day.total_cost)}
</p>
<p className="text-xs text-muted-foreground">
{formatTokens(day.total_tokens)} tokens
{formatTokens(day.total_tokens)} 个令牌
</p>
<p className="text-xs text-muted-foreground">
{day.models_used.length} model{day.models_used.length !== 1 ? 's' : ''}
{day.models_used.length} 个模型
</p>
</div>
<div className="absolute top-full left-1/2 transform -translate-x-1/2 -mt-1">
@@ -508,7 +508,7 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
<div
className="absolute left-1/2 top-full mt-1 -translate-x-1/2 text-xs text-muted-foreground -rotate-45 origin-top-left whitespace-nowrap pointer-events-none"
>
{date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
{date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })}
</div>
</div>
);
@@ -517,13 +517,13 @@ export const UsageDashboard: React.FC<UsageDashboardProps> = ({ onBack }) => {
{/* X-axis label */}
<div className="mt-8 text-center text-xs text-muted-foreground">
Daily Usage Over Time
每日使用趋势
</div>
</div>
)
})() : (
<div className="text-center py-8 text-sm text-muted-foreground">
No usage data available for the selected period
所选时间段内没有可用的使用数据
</div>
)}
</Card>
+1 -1
View File
@@ -43,7 +43,7 @@ const DialogContent = React.forwardRef<
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
<span className="sr-only">关闭</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
+1 -1
View File
@@ -55,7 +55,7 @@ export const Pagination: React.FC<PaginationProps> = ({
</Button>
<span className="text-sm text-muted-foreground">
Page {currentPage} of {totalPages}
第 {currentPage} 页,共 {totalPages} 页
</span>
<Button