优化页面,国际化细节内容

This commit is contained in:
2025-08-09 04:18:12 +08:00
parent f5e326bde7
commit f682dd7a5b
17 changed files with 223 additions and 98 deletions

View File

@@ -26,6 +26,7 @@ import {
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { useTranslation } from "react-i18next";
import { api, type Agent } from "@/lib/api"; import { api, type Agent } from "@/lib/api";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
@@ -81,6 +82,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
onBack, onBack,
className, className,
}) => { }) => {
const { t } = useTranslation();
const [projectPath, setProjectPath] = useState(""); const [projectPath, setProjectPath] = useState("");
const [task, setTask] = useState(agent.default_task || ""); const [task, setTask] = useState(agent.default_task || "");
const [model, setModel] = useState(agent.model || "sonnet"); const [model, setModel] = useState(agent.model || "sonnet");
@@ -452,7 +454,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
if (isRunning) { if (isRunning) {
// Show confirmation dialog before navigating away during execution // Show confirmation dialog before navigating away during execution
const shouldLeave = window.confirm( 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?" t('agents.agentRunningWarning')
); );
if (!shouldLeave) { if (!shouldLeave) {
return; return;
@@ -476,7 +478,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
const handleCopyAsMarkdown = async () => { const handleCopyAsMarkdown = async () => {
let markdown = `# Agent Execution: ${agent.name}\n\n`; let markdown = `# Agent Execution: ${agent.name}\n\n`;
markdown += `**Task:** ${task}\n`; markdown += `**Task:** ${task}\n`;
markdown += `**Model:** ${model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}\n`; markdown += `**Model:** ${model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}\n`;
markdown += `**Date:** ${new Date().toISOString()}\n\n`; markdown += `**Date:** ${new Date().toISOString()}\n\n`;
markdown += `---\n\n`; markdown += `---\n\n`;
@@ -572,9 +574,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{renderIcon()} {renderIcon()}
</div> </div>
<div> <div>
<h1 className="text-xl font-bold">Execute: {agent.name}</h1> <h1 className="text-xl font-bold">{t('agents.execute')}: {agent.name}</h1>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} {model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}
</p> </p>
</div> </div>
</div> </div>
@@ -587,7 +589,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
disabled={messages.length === 0} disabled={messages.length === 0}
> >
<Maximize2 className="h-4 w-4 mr-2" /> <Maximize2 className="h-4 w-4 mr-2" />
Fullscreen {t('agents.fullscreen')}
</Button> </Button>
</div> </div>
</div> </div>
@@ -612,12 +614,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{/* Project Path */} {/* Project Path */}
<div className="space-y-2"> <div className="space-y-2">
<Label>Project Path</Label> <Label>{t('agents.projectPath')}</Label>
<div className="flex gap-2"> <div className="flex gap-2">
<Input <Input
value={projectPath} value={projectPath}
onChange={(e) => setProjectPath(e.target.value)} onChange={(e) => setProjectPath(e.target.value)}
placeholder="Select or enter project path" placeholder={t('agents.selectProjectPath')}
disabled={isRunning} disabled={isRunning}
className="flex-1" className="flex-1"
/> />
@@ -633,17 +635,17 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
variant="outline" variant="outline"
onClick={handleOpenHooksDialog} onClick={handleOpenHooksDialog}
disabled={isRunning || !projectPath} disabled={isRunning || !projectPath}
title="Configure hooks" title={t('agents.configureHooks')}
> >
<Settings2 className="h-4 w-4 mr-2" /> <Settings2 className="h-4 w-4 mr-2" />
Hooks {t('agents.hooks')}
</Button> </Button>
</div> </div>
</div> </div>
{/* Model Selection */} {/* Model Selection */}
<div className="space-y-2"> <div className="space-y-2">
<Label>Model</Label> <Label>{t('agents.model')}</Label>
<div className="flex gap-3"> <div className="flex gap-3">
<button <button
type="button" type="button"
@@ -693,7 +695,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
<div className="w-1.5 h-1.5 rounded-full bg-primary-foreground" /> <div className="w-1.5 h-1.5 rounded-full bg-primary-foreground" />
)} )}
</div> </div>
<span>Claude 4 Opus</span> <span>Claude 4.1 Opus</span>
</div> </div>
</button> </button>
</div> </div>
@@ -701,12 +703,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{/* Task Input */} {/* Task Input */}
<div className="space-y-2"> <div className="space-y-2">
<Label>Task</Label> <Label>{t('app.task')}</Label>
<div className="flex gap-2"> <div className="flex gap-2">
<Input <Input
value={task} value={task}
onChange={(e) => setTask(e.target.value)} onChange={(e) => setTask(e.target.value)}
placeholder="Enter the task for the agent" placeholder={t('agents.enterTask')}
disabled={isRunning} disabled={isRunning}
className="flex-1" className="flex-1"
onKeyPress={(e) => { onKeyPress={(e) => {
@@ -723,12 +725,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{isRunning ? ( {isRunning ? (
<> <>
<StopCircle className="mr-2 h-4 w-4" /> <StopCircle className="mr-2 h-4 w-4" />
Stop {t('agents.stop')}
</> </>
) : ( ) : (
<> <>
<Play className="mr-2 h-4 w-4" /> <Play className="mr-2 h-4 w-4" />
Execute {t('agents.execute')}
</> </>
)} )}
</Button> </Button>
@@ -759,9 +761,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{messages.length === 0 && !isRunning && ( {messages.length === 0 && !isRunning && (
<div className="flex flex-col items-center justify-center h-full text-center"> <div className="flex flex-col items-center justify-center h-full text-center">
<Terminal className="h-16 w-16 text-muted-foreground mb-4" /> <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">{t('agents.readyToExecute')}</h3>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Select a project path and enter a task to run the agent {t('agents.selectProjectPathAndTask')}
</p> </p>
</div> </div>
)} )}
@@ -841,7 +843,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy Output {t('app.copyOutput')}
<ChevronDown className="h-3 w-3" /> <ChevronDown className="h-3 w-3" />
</Button> </Button>
} }
@@ -853,7 +855,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -861,7 +863,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }
@@ -901,9 +903,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
{messages.length === 0 && !isRunning && ( {messages.length === 0 && !isRunning && (
<div className="flex flex-col items-center justify-center h-full text-center"> <div className="flex flex-col items-center justify-center h-full text-center">
<Terminal className="h-16 w-16 text-muted-foreground mb-4" /> <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">{t('agents.readyToExecute')}</h3>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Select a project path and enter a task to run the agent {t('agents.selectProjectPathAndTask')}
</p> </p>
</div> </div>
)} )}
@@ -957,7 +959,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
> >
<DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col"> <DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col">
<DialogHeader> <DialogHeader>
<DialogTitle>Configure Hooks</DialogTitle> <DialogTitle>{t('agents.configureHooks')}</DialogTitle>
<DialogDescription> <DialogDescription>
Configure hooks that run before, during, and after tool executions. Changes are saved immediately. Configure hooks that run before, during, and after tool executions. Changes are saved immediately.
</DialogDescription> </DialogDescription>

View File

@@ -1,4 +1,5 @@
import { useState, useEffect, useRef, useMemo } from 'react'; import { useState, useEffect, useRef, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { import {
Maximize2, Maximize2,
@@ -57,6 +58,7 @@ export function AgentRunOutputViewer({
tabId, tabId,
className className
}: AgentRunOutputViewerProps) { }: AgentRunOutputViewerProps) {
const { t } = useTranslation();
const { updateTabTitle, updateTabStatus } = useTabState(); const { updateTabTitle, updateTabStatus } = useTabState();
const [run, setRun] = useState<AgentRunWithMetrics | null>(null); const [run, setRun] = useState<AgentRunWithMetrics | null>(null);
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]); const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
@@ -330,7 +332,7 @@ export function AgentRunOutputViewer({
if (!run) return; if (!run) return;
let markdown = `# Agent Execution: ${run.agent_name}\n\n`; let markdown = `# Agent Execution: ${run.agent_name}\n\n`;
markdown += `**Task:** ${run.task}\n`; markdown += `**Task:** ${run.task}\n`;
markdown += `**Model:** ${run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}\n`; markdown += `**Model:** ${run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}\n`;
markdown += `**Date:** ${formatISOTimestamp(run.created_at)}\n`; markdown += `**Date:** ${formatISOTimestamp(run.created_at)}\n`;
if (run.metrics?.duration_ms) markdown += `**Duration:** ${(run.metrics.duration_ms / 1000).toFixed(2)}s\n`; if (run.metrics?.duration_ms) markdown += `**Duration:** ${(run.metrics.duration_ms / 1000).toFixed(2)}s\n`;
if (run.metrics?.total_tokens) markdown += `**Total Tokens:** ${run.metrics.total_tokens}\n`; if (run.metrics?.total_tokens) markdown += `**Total Tokens:** ${run.metrics.total_tokens}\n`;
@@ -566,7 +568,7 @@ export function AgentRunOutputViewer({
</p> </p>
<div className="flex items-center gap-3 text-xs text-muted-foreground mt-2"> <div className="flex items-center gap-3 text-xs text-muted-foreground mt-2">
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} {run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}
</Badge> </Badge>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Clock className="h-3 w-3" /> <Clock className="h-3 w-3" />
@@ -611,7 +613,7 @@ export function AgentRunOutputViewer({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -619,7 +621,7 @@ export function AgentRunOutputViewer({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }
@@ -723,7 +725,7 @@ export function AgentRunOutputViewer({
size="sm" size="sm"
> >
<Copy className="h-4 w-4 mr-2" /> <Copy className="h-4 w-4 mr-2" />
Copy Output {t('app.copyOutput')}
<ChevronDown className="h-3 w-3 ml-2" /> <ChevronDown className="h-3 w-3 ml-2" />
</Button> </Button>
} }
@@ -735,7 +737,7 @@ export function AgentRunOutputViewer({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -743,7 +745,7 @@ export function AgentRunOutputViewer({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }

View File

@@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Popover } from "@/components/ui/popover"; import { Popover } from "@/components/ui/popover";
import { useTranslation } from "react-i18next";
import { api, type AgentRunWithMetrics } from "@/lib/api"; import { api, type AgentRunWithMetrics } from "@/lib/api";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { formatISOTimestamp } from "@/lib/date-utils"; import { formatISOTimestamp } from "@/lib/date-utils";
@@ -48,6 +49,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
onBack, onBack,
className, className,
}) => { }) => {
const { t } = useTranslation();
const [run, setRun] = useState<AgentRunWithMetrics | null>(null); const [run, setRun] = useState<AgentRunWithMetrics | null>(null);
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]); const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -288,7 +290,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy Output {t('app.copyOutput')}
<ChevronDown className="h-3 w-3" /> <ChevronDown className="h-3 w-3" />
</Button> </Button>
} }
@@ -300,7 +302,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -308,7 +310,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }
@@ -327,7 +329,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
<h3 className="text-sm font-medium">Task:</h3> <h3 className="text-sm font-medium">Task:</h3>
<p className="text-sm text-muted-foreground flex-1">{run.task}</p> <p className="text-sm text-muted-foreground flex-1">{run.task}</p>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} {run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}
</Badge> </Badge>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { Play, Clock, Hash, Bot } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Pagination } from "@/components/ui/pagination"; import { Pagination } from "@/components/ui/pagination";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { formatISOTimestamp } from "@/lib/date-utils"; import { formatISOTimestamp } from "@/lib/date-utils";
import type { AgentRunWithMetrics } from "@/lib/api"; import type { AgentRunWithMetrics } from "@/lib/api";
@@ -41,6 +42,7 @@ export const AgentRunsList: React.FC<AgentRunsListProps> = ({
onRunClick, onRunClick,
className, className,
}) => { }) => {
const { t } = useTranslation();
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const { createAgentTab } = useTabState(); const { createAgentTab } = useTabState();
@@ -91,7 +93,7 @@ export const AgentRunsList: React.FC<AgentRunsListProps> = ({
return ( return (
<div className={cn("text-center py-8 text-muted-foreground", className)}> <div className={cn("text-center py-8 text-muted-foreground", className)}>
<Play className="h-8 w-8 mx-auto mb-2 opacity-50" /> <Play className="h-8 w-8 mx-auto mb-2 opacity-50" />
<p className="text-sm">No execution history yet</p> <p className="text-sm">{t('agents.noExecutionHistory')}</p>
</div> </div>
); );
} }

View File

@@ -361,7 +361,7 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
<div className="flex items-center gap-4 mt-2 text-xs text-muted-foreground"> <div className="flex items-center gap-4 mt-2 text-xs text-muted-foreground">
<span>Started: {formatISOTimestamp(run.created_at)}</span> <span>Started: {formatISOTimestamp(run.created_at)}</span>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} {run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}
</Badge> </Badge>
</div> </div>
</div> </div>

View File

@@ -17,6 +17,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Popover } from "@/components/ui/popover"; import { Popover } from "@/components/ui/popover";
import { useTranslation } from "react-i18next";
import { api, type Session } from "@/lib/api"; import { api, type Session } from "@/lib/api";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
@@ -76,6 +77,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
className, className,
onStreamingChange, onStreamingChange,
}) => { }) => {
const { t } = useTranslation();
const [projectPath, setProjectPath] = useState(initialProjectPath || session?.project_path || ""); const [projectPath, setProjectPath] = useState(initialProjectPath || session?.project_path || "");
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]); const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -1127,7 +1129,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
const messagesList = ( const messagesList = (
<div <div
ref={parentRef} ref={parentRef}
className="flex-1 overflow-y-auto relative pb-40" className="flex-1 overflow-y-auto relative pb-24"
style={{ style={{
contain: 'strict', contain: 'strict',
}} }}
@@ -1247,7 +1249,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
} }
return ( return (
<div className={cn("flex flex-col h-full bg-background", className)}> <div className={cn("flex flex-col h-full bg-background relative", className)}>
<div className="w-full h-full flex flex-col"> <div className="w-full h-full flex flex-col">
{/* Header */} {/* Header */}
<motion.div <motion.div
@@ -1268,7 +1270,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Terminal className="h-5 w-5 text-muted-foreground" /> <Terminal className="h-5 w-5 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<h1 className="text-xl font-bold">Claude Code Session</h1> <h1 className="text-xl font-bold">{t('app.claudeCodeSession')}</h1>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{projectPath ? `${projectPath}` : "No project selected"} {projectPath ? `${projectPath}` : "No project selected"}
</p> </p>
@@ -1300,13 +1302,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
</Button> </Button>
)} )}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{showSettings && (
<CheckpointSettings
sessionId={effectiveSession?.id || ''}
projectId={effectiveSession?.project_id || ''}
projectPath={projectPath}
/>
)}
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@@ -1320,7 +1315,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>Checkpoint Settings</p> <p>{t('checkpoint.checkpointSettingsTitle')}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
@@ -1352,7 +1347,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy Output {t('app.copyOutput')}
<ChevronDown className="h-3 w-3" /> <ChevronDown className="h-3 w-3" />
</Button> </Button>
} }
@@ -1364,7 +1359,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
className="w-full justify-start" className="w-full justify-start"
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -1372,7 +1367,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
className="w-full justify-start" className="w-full justify-start"
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
</div> </div>
} }
@@ -1554,7 +1549,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
)} )}
<div className={cn( <div className={cn(
"fixed bottom-0 left-0 right-0 transition-all duration-300 z-50", "absolute bottom-0 left-0 right-0 transition-all duration-300 z-50",
showTimeline && "sm:right-96" showTimeline && "sm:right-96"
)}> )}>
<FloatingPromptInput <FloatingPromptInput
@@ -1603,7 +1598,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
{/* Timeline Header */} {/* Timeline Header */}
<div className="flex items-center justify-between p-4 border-b border-border"> <div className="flex items-center justify-between p-4 border-b border-border">
<h3 className="text-lg font-semibold">Session Timeline</h3> <h3 className="text-lg font-semibold">{t('app.sessionTimeline')}</h3>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
@@ -1682,6 +1677,12 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
{showSettings && effectiveSession && ( {showSettings && effectiveSession && (
<Dialog open={showSettings} onOpenChange={setShowSettings}> <Dialog open={showSettings} onOpenChange={setShowSettings}>
<DialogContent className="max-w-2xl"> <DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>{t('checkpoint.checkpointSettingsTitle')}</DialogTitle>
<DialogDescription>
{t('app.checkpointingWarning')}
</DialogDescription>
</DialogHeader>
<CheckpointSettings <CheckpointSettings
sessionId={effectiveSession.id} sessionId={effectiveSession.id}
projectId={effectiveSession.project_id} projectId={effectiveSession.project_id}
@@ -1699,7 +1700,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
<DialogHeader> <DialogHeader>
<DialogTitle>Slash Commands</DialogTitle> <DialogTitle>Slash Commands</DialogTitle>
<DialogDescription> <DialogDescription>
Manage project-specific slash commands for {projectPath} {t('slashCommands.manageProjectCommands')} {projectPath}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">

View File

@@ -271,7 +271,7 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
)} )}
</div> </div>
<div className="text-left"> <div className="text-left">
<div className="text-sm font-semibold">Claude 4 Opus</div> <div className="text-sm font-semibold">Claude 4.1 Opus</div>
<div className="text-xs opacity-80">{t('agents.opusDescription')}</div> <div className="text-xs opacity-80">{t('agents.opusDescription')}</div>
</div> </div>
</div> </div>

View File

@@ -20,7 +20,7 @@ import { SlashCommandPicker } from "./SlashCommandPicker";
import { ImagePreview } from "./ImagePreview"; import { ImagePreview } from "./ImagePreview";
import { type FileEntry, type SlashCommand } from "@/lib/api"; import { type FileEntry, type SlashCommand } from "@/lib/api";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { useTranslation } from "@/hooks/useTranslation"; import { useTranslation } from "react-i18next";
interface FloatingPromptInputProps { interface FloatingPromptInputProps {
/** /**
@@ -110,7 +110,7 @@ const MODELS: Model[] = [
}, },
{ {
id: "opus", id: "opus",
name: "Claude 4 Opus", name: "Claude 4.1 Opus",
description: "More capable, better for complex tasks", description: "More capable, better for complex tasks",
icon: <Sparkles className="h-4 w-4" /> icon: <Sparkles className="h-4 w-4" />
} }
@@ -775,7 +775,7 @@ const FloatingPromptInputInner = (
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-xs text-muted-foreground">Model:</span> <span className="text-xs text-muted-foreground">{t('app.model')}:</span>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@@ -871,7 +871,7 @@ const FloatingPromptInputInner = (
{/* Fixed Position Input Bar */} {/* Fixed Position Input Bar */}
<div <div
className={cn( className={cn(
"fixed bottom-0 left-0 right-0 z-40 bg-background border-t border-border", "absolute bottom-0 left-0 right-0 z-40 bg-background/98 backdrop-blur-sm border-t border-border/40",
dragActive && "ring-2 ring-primary ring-offset-2", dragActive && "ring-2 ring-primary ring-offset-2",
className className
)} )}
@@ -890,8 +890,8 @@ const FloatingPromptInputInner = (
/> />
)} )}
<div className="p-4"> <div className="p-3">
<div className="flex items-end gap-3"> <div className="flex items-center gap-2">
{/* Model Picker */} {/* Model Picker */}
<Popover <Popover
trigger={ trigger={
@@ -899,7 +899,7 @@ const FloatingPromptInputInner = (
variant="outline" variant="outline"
size="default" size="default"
disabled={disabled} disabled={disabled}
className="gap-2 min-w-[180px] justify-start" className="gap-2 min-w-[160px] h-10 justify-start"
> >
{selectedModelData.icon} {selectedModelData.icon}
<span className="flex-1 text-left">{selectedModelData.name}</span> <span className="flex-1 text-left">{selectedModelData.name}</span>
@@ -948,7 +948,7 @@ const FloatingPromptInputInner = (
variant="outline" variant="outline"
size="default" size="default"
disabled={disabled} disabled={disabled}
className="gap-2" className="gap-2 h-10"
> >
<Brain className="h-4 w-4" /> <Brain className="h-4 w-4" />
<ThinkingModeIndicator <ThinkingModeIndicator
@@ -1009,7 +1009,7 @@ const FloatingPromptInputInner = (
placeholder={dragActive ? t('messages.dropImagesHere') : t('messages.askClaudeAnything')} placeholder={dragActive ? t('messages.dropImagesHere') : t('messages.askClaudeAnything')}
disabled={disabled} disabled={disabled}
className={cn( className={cn(
"min-h-[44px] max-h-[120px] resize-none pr-10", "min-h-[40px] max-h-[120px] resize-none pr-10 py-2",
dragActive && "border-primary" dragActive && "border-primary"
)} )}
rows={1} rows={1}
@@ -1056,7 +1056,7 @@ const FloatingPromptInputInner = (
disabled={isLoading ? false : (!prompt.trim() || disabled)} disabled={isLoading ? false : (!prompt.trim() || disabled)}
variant={isLoading ? "destructive" : "default"} variant={isLoading ? "destructive" : "default"}
size="default" size="default"
className="min-w-[60px]" className="min-w-[60px] h-10"
> >
{isLoading ? ( {isLoading ? (
<> <>

View File

@@ -5,6 +5,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { api, type ProcessInfo, type Session } from "@/lib/api"; import { api, type ProcessInfo, type Session } from "@/lib/api";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useTranslation } from "react-i18next";
import { formatISOTimestamp } from "@/lib/date-utils"; import { formatISOTimestamp } from "@/lib/date-utils";
interface RunningClaudeSessionsProps { interface RunningClaudeSessionsProps {
@@ -25,6 +26,7 @@ export const RunningClaudeSessions: React.FC<RunningClaudeSessionsProps> = ({
onSessionClick, onSessionClick,
className, className,
}) => { }) => {
const { t } = useTranslation();
const [runningSessions, setRunningSessions] = useState<ProcessInfo[]>([]); const [runningSessions, setRunningSessions] = useState<ProcessInfo[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -144,11 +146,11 @@ export const RunningClaudeSessions: React.FC<RunningClaudeSessionsProps> = ({
</p> </p>
<div className="flex items-center gap-3 text-xs text-muted-foreground"> <div className="flex items-center gap-3 text-xs text-muted-foreground">
<span>Started: {formatISOTimestamp(session.started_at)}</span> <span>{t('app.started')}: {formatISOTimestamp(session.started_at)}</span>
<span>Model: {session.model}</span> <span>{t('app.model')}: {session.model}</span>
{session.task && ( {session.task && (
<span className="truncate max-w-[200px]" title={session.task}> <span className="truncate max-w-[200px]" title={session.task}>
Task: {session.task} {t('app.task')}: {session.task}
</span> </span>
)} )}
</div> </div>

View File

@@ -10,6 +10,7 @@ import { api } from '@/lib/api';
import { useOutputCache } from '@/lib/outputCache'; import { useOutputCache } from '@/lib/outputCache';
import type { AgentRun } from '@/lib/api'; import type { AgentRun } from '@/lib/api';
import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import { useTranslation } from 'react-i18next';
import { StreamMessage } from './StreamMessage'; import { StreamMessage } from './StreamMessage';
import { ErrorBoundary } from './ErrorBoundary'; import { ErrorBoundary } from './ErrorBoundary';
@@ -38,6 +39,7 @@ export interface ClaudeStreamMessage {
} }
export function SessionOutputViewer({ session, onClose, className }: SessionOutputViewerProps) { export function SessionOutputViewer({ session, onClose, className }: SessionOutputViewerProps) {
const { t } = useTranslation();
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]); const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
const [rawJsonlOutput, setRawJsonlOutput] = useState<string[]>([]); const [rawJsonlOutput, setRawJsonlOutput] = useState<string[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -410,7 +412,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setIsFullscreen(!isFullscreen)} onClick={() => setIsFullscreen(!isFullscreen)}
title="Fullscreen" title={t('agents.fullscreen')}
> >
{isFullscreen ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />} {isFullscreen ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />}
</Button> </Button>
@@ -422,7 +424,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy Output {t('app.copyOutput')}
<ChevronDown className="h-3 w-3" /> <ChevronDown className="h-3 w-3" />
</Button> </Button>
} }
@@ -434,7 +436,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -442,7 +444,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }
@@ -567,7 +569,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy Output {t('app.copyOutput')}
<ChevronDown className="h-3 w-3" /> <ChevronDown className="h-3 w-3" />
</Button> </Button>
} }
@@ -579,7 +581,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsJsonl} onClick={handleCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -587,7 +589,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
className="w-full justify-start" className="w-full justify-start"
onClick={handleCopyAsMarkdown} onClick={handleCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }

View File

@@ -19,6 +19,7 @@ import {
import type { SlashCommand } from "@/lib/api"; import type { SlashCommand } from "@/lib/api";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useTrackEvent, useFeatureAdoptionTracking } from "@/hooks"; import { useTrackEvent, useFeatureAdoptionTracking } from "@/hooks";
import { useTranslation } from "react-i18next";
interface SlashCommandPickerProps { interface SlashCommandPickerProps {
/** /**
@@ -79,6 +80,7 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
initialQuery = "", initialQuery = "",
className, className,
}) => { }) => {
const { t } = useTranslation();
const [commands, setCommands] = useState<SlashCommand[]>([]); const [commands, setCommands] = useState<SlashCommand[]>([]);
const [filteredCommands, setFilteredCommands] = useState<SlashCommand[]>([]); const [filteredCommands, setFilteredCommands] = useState<SlashCommand[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@@ -241,7 +243,7 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
const groupedCommands = filteredCommands.reduce((acc, cmd) => { const groupedCommands = filteredCommands.reduce((acc, cmd) => {
let key: string; let key: string;
if (cmd.scope === "user") { if (cmd.scope === "user") {
key = cmd.namespace ? `User Commands: ${cmd.namespace}` : "User Commands"; key = cmd.namespace ? `${t('slashCommands.userCommands')}: ${cmd.namespace}` : t('slashCommands.userCommands');
} else if (cmd.scope === "project") { } else if (cmd.scope === "project") {
key = cmd.namespace ? `Project Commands: ${cmd.namespace}` : "Project Commands"; key = cmd.namespace ? `Project Commands: ${cmd.namespace}` : "Project Commands";
} else { } else {
@@ -278,10 +280,10 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Command className="h-4 w-4 text-muted-foreground" /> <Command className="h-4 w-4 text-muted-foreground" />
<span className="text-sm font-medium">Slash Commands</span> <span className="text-sm font-medium">{t('slashCommands.slashCommands')}</span>
{searchQuery && ( {searchQuery && (
<span className="text-xs text-muted-foreground"> <span className="text-xs text-muted-foreground">
Searching: "{searchQuery}" {t('slashCommands.searching')}: "{searchQuery}"
</span> </span>
)} )}
</div> </div>
@@ -474,7 +476,7 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
{Object.entries(groupedCommands).map(([groupKey, groupCommands]) => ( {Object.entries(groupedCommands).map(([groupKey, groupCommands]) => (
<div key={groupKey}> <div key={groupKey}>
<h3 className="text-xs font-medium text-muted-foreground uppercase tracking-wider px-3 mb-1 flex items-center gap-2"> <h3 className="text-xs font-medium text-muted-foreground uppercase tracking-wider px-3 mb-1 flex items-center gap-2">
{groupKey.startsWith("User Commands") && <User className="h-3 w-3" />} {groupKey.startsWith(t('slashCommands.userCommands')) && <User className="h-3 w-3" />}
{groupKey.startsWith("Project Commands") && <Building2 className="h-3 w-3" />} {groupKey.startsWith("Project Commands") && <Building2 className="h-3 w-3" />}
{groupKey} {groupKey}
</h3> </h3>

View File

@@ -1,4 +1,5 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { import {
Database, Database,
@@ -81,6 +82,7 @@ interface QueryResult {
* StorageTab component - A beautiful SQLite database viewer/editor * StorageTab component - A beautiful SQLite database viewer/editor
*/ */
export const StorageTab: React.FC = () => { export const StorageTab: React.FC = () => {
const { t } = useTranslation();
const [tables, setTables] = useState<TableInfo[]>([]); const [tables, setTables] = useState<TableInfo[]>([]);
const [selectedTable, setSelectedTable] = useState<string>(""); const [selectedTable, setSelectedTable] = useState<string>("");
const [tableData, setTableData] = useState<TableData | null>(null); const [tableData, setTableData] = useState<TableData | null>(null);
@@ -934,7 +936,7 @@ export const StorageTab: React.FC = () => {
{loading ? ( {loading ? (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
) : ( ) : (
"Execute" t('agents.execute')
)} )}
</Button> </Button>
</DialogFooter> </DialogFooter>

View File

@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { import {
GitBranch, GitBranch,
@@ -57,6 +58,7 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
onCheckpointCreated, onCheckpointCreated,
className className
}) => { }) => {
const { t } = useTranslation();
const [timeline, setTimeline] = useState<SessionTimeline | null>(null); const [timeline, setTimeline] = useState<SessionTimeline | null>(null);
const [selectedCheckpoint, setSelectedCheckpoint] = useState<Checkpoint | null>(null); const [selectedCheckpoint, setSelectedCheckpoint] = useState<Checkpoint | null>(null);
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(new Set()); const [expandedNodes, setExpandedNodes] = useState<Set<string>>(new Set());
@@ -413,9 +415,9 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<AlertCircle className="h-4 w-4 text-yellow-600 mt-0.5" /> <AlertCircle className="h-4 w-4 text-yellow-600 mt-0.5" />
<div className="text-xs"> <div className="text-xs">
<p className="font-medium text-yellow-600">Experimental Feature</p> <p className="font-medium text-yellow-600">{t('app.experimentalFeature')}</p>
<p className="text-yellow-600/80"> <p className="text-yellow-600/80">
Checkpointing may affect directory structure or cause data loss. Use with caution. {t('app.checkpointingWarning')}
</p> </p>
</div> </div>
</div> </div>
@@ -425,10 +427,10 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<GitBranch className="h-5 w-5 text-muted-foreground" /> <GitBranch className="h-5 w-5 text-muted-foreground" />
<h3 className="text-sm font-medium">Timeline</h3> <h3 className="text-sm font-medium">{t('app.timeline')}</h3>
{timeline && ( {timeline && (
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{timeline.totalCheckpoints} checkpoints {timeline.totalCheckpoints} {t('app.checkpoints')}
</Badge> </Badge>
)} )}
</div> </div>
@@ -459,7 +461,7 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
</div> </div>
) : ( ) : (
<div className="text-center py-8 text-sm text-muted-foreground"> <div className="text-center py-8 text-sm text-muted-foreground">
{isLoading ? "Loading timeline..." : "No checkpoints yet"} {isLoading ? t('app.loadingTimeline') : t('app.noCheckpointsYet')}
</div> </div>
)} )}

View File

@@ -49,6 +49,7 @@ import {
Hash, Hash,
} from "lucide-react"; } from "lucide-react";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { getClaudeSyntaxTheme } from "@/lib/claudeSyntaxTheme"; import { getClaudeSyntaxTheme } from "@/lib/claudeSyntaxTheme";
@@ -400,6 +401,7 @@ export const ReadWidget: React.FC<{ filePath: string; result?: any }> = ({ fileP
* Widget for Read tool result - shows file content with line numbers * Widget for Read tool result - shows file content with line numbers
*/ */
export const ReadResultWidget: React.FC<{ content: string; filePath?: string }> = ({ content, filePath }) => { export const ReadResultWidget: React.FC<{ content: string; filePath?: string }> = ({ content, filePath }) => {
const { t } = useTranslation();
const [isExpanded, setIsExpanded] = useState(false); const [isExpanded, setIsExpanded] = useState(false);
const { theme } = useTheme(); const { theme } = useTheme();
const syntaxTheme = getClaudeSyntaxTheme(theme); const syntaxTheme = getClaudeSyntaxTheme(theme);
@@ -510,7 +512,7 @@ export const ReadResultWidget: React.FC<{ content: string; filePath?: string }>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<FileText className="h-3.5 w-3.5 text-muted-foreground" /> <FileText className="h-3.5 w-3.5 text-muted-foreground" />
<span className="text-xs font-mono text-muted-foreground"> <span className="text-xs font-mono text-muted-foreground">
{filePath || "File content"} {filePath || t('app.fileContent')}
</span> </span>
{isLargeFile && ( {isLargeFile && (
<span className="text-xs text-muted-foreground"> <span className="text-xs text-muted-foreground">
@@ -572,6 +574,7 @@ export const ReadResultWidget: React.FC<{ content: string; filePath?: string }>
* Widget for Glob tool * Widget for Glob tool
*/ */
export const GlobWidget: React.FC<{ pattern: string; result?: any }> = ({ pattern, result }) => { export const GlobWidget: React.FC<{ pattern: string; result?: any }> = ({ pattern, result }) => {
const { t } = useTranslation();
// Extract result content if available // Extract result content if available
let resultContent = ''; let resultContent = '';
let isError = false; let isError = false;
@@ -597,7 +600,7 @@ export const GlobWidget: React.FC<{ pattern: string; result?: any }> = ({ patter
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2 p-3 rounded-lg bg-muted/50"> <div className="flex items-center gap-2 p-3 rounded-lg bg-muted/50">
<Search className="h-4 w-4 text-primary" /> <Search className="h-4 w-4 text-primary" />
<span className="text-sm">Searching for pattern:</span> <span className="text-sm">{t('app.searchingForPattern')}:</span>
<code className="text-sm font-mono bg-background px-2 py-0.5 rounded"> <code className="text-sm font-mono bg-background px-2 py-0.5 rounded">
{pattern} {pattern}
</code> </code>
@@ -874,6 +877,7 @@ export const GrepWidget: React.FC<{
exclude?: string; exclude?: string;
result?: any; result?: any;
}> = ({ pattern, include, path, exclude, result }) => { }> = ({ pattern, include, path, exclude, result }) => {
const { t } = useTranslation();
const [isExpanded, setIsExpanded] = useState(true); const [isExpanded, setIsExpanded] = useState(true);
// Extract result content if available // Extract result content if available
@@ -927,7 +931,7 @@ export const GrepWidget: React.FC<{
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2 p-3 rounded-lg bg-gradient-to-r from-emerald-500/10 to-teal-500/10 border border-emerald-500/20"> <div className="flex items-center gap-2 p-3 rounded-lg bg-gradient-to-r from-emerald-500/10 to-teal-500/10 border border-emerald-500/20">
<Search className="h-4 w-4 text-emerald-500" /> <Search className="h-4 w-4 text-emerald-500" />
<span className="text-sm font-medium">Searching with grep</span> <span className="text-sm font-medium">{t('app.searchingWithGrep')}</span>
{!result && ( {!result && (
<div className="ml-auto flex items-center gap-1 text-xs text-muted-foreground"> <div className="ml-auto flex items-center gap-1 text-xs text-muted-foreground">
<div className="h-2 w-2 bg-emerald-500 rounded-full animate-pulse" /> <div className="h-2 w-2 bg-emerald-500 rounded-full animate-pulse" />
@@ -1804,6 +1808,7 @@ export const SystemInitializedWidget: React.FC<{
cwd?: string; cwd?: string;
tools?: string[]; tools?: string[];
}> = ({ sessionId, model, cwd, tools = [] }) => { }> = ({ sessionId, model, cwd, tools = [] }) => {
const { t } = useTranslation();
const [mcpExpanded, setMcpExpanded] = useState(false); const [mcpExpanded, setMcpExpanded] = useState(false);
// Separate regular tools from MCP tools // Separate regular tools from MCP tools
@@ -1880,14 +1885,14 @@ export const SystemInitializedWidget: React.FC<{
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Settings className="h-5 w-5 text-blue-500 mt-0.5" /> <Settings className="h-5 w-5 text-blue-500 mt-0.5" />
<div className="flex-1 space-y-4"> <div className="flex-1 space-y-4">
<h4 className="font-semibold text-sm">System Initialized</h4> <h4 className="font-semibold text-sm">{t('app.systemInitialized')}</h4>
{/* Session Info */} {/* Session Info */}
<div className="space-y-2"> <div className="space-y-2">
{sessionId && ( {sessionId && (
<div className="flex items-center gap-2 text-xs"> <div className="flex items-center gap-2 text-xs">
<Fingerprint className="h-3.5 w-3.5 text-muted-foreground" /> <Fingerprint className="h-3.5 w-3.5 text-muted-foreground" />
<span className="text-muted-foreground">Session ID:</span> <span className="text-muted-foreground">{t('app.sessionId')}:</span>
<code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded"> <code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded">
{sessionId} {sessionId}
</code> </code>
@@ -1897,7 +1902,7 @@ export const SystemInitializedWidget: React.FC<{
{model && ( {model && (
<div className="flex items-center gap-2 text-xs"> <div className="flex items-center gap-2 text-xs">
<Cpu className="h-3.5 w-3.5 text-muted-foreground" /> <Cpu className="h-3.5 w-3.5 text-muted-foreground" />
<span className="text-muted-foreground">Model:</span> <span className="text-muted-foreground">{t('app.model')}:</span>
<code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded"> <code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded">
{model} {model}
</code> </code>
@@ -1907,7 +1912,7 @@ export const SystemInitializedWidget: React.FC<{
{cwd && ( {cwd && (
<div className="flex items-center gap-2 text-xs"> <div className="flex items-center gap-2 text-xs">
<FolderOpen className="h-3.5 w-3.5 text-muted-foreground" /> <FolderOpen className="h-3.5 w-3.5 text-muted-foreground" />
<span className="text-muted-foreground">Working Directory:</span> <span className="text-muted-foreground">{t('app.workingDirectory')}:</span>
<code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded break-all"> <code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded break-all">
{cwd} {cwd}
</code> </code>
@@ -1921,7 +1926,7 @@ export const SystemInitializedWidget: React.FC<{
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Wrench className="h-3.5 w-3.5 text-muted-foreground" /> <Wrench className="h-3.5 w-3.5 text-muted-foreground" />
<span className="text-xs font-medium text-muted-foreground"> <span className="text-xs font-medium text-muted-foreground">
Available Tools ({regularTools.length}) {t('app.availableTools')} ({regularTools.length})
</span> </span>
</div> </div>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
@@ -1950,7 +1955,7 @@ export const SystemInitializedWidget: React.FC<{
className="flex items-center gap-2 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors" className="flex items-center gap-2 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors"
> >
<Package className="h-3.5 w-3.5" /> <Package className="h-3.5 w-3.5" />
<span>MCP Services ({mcpTools.length})</span> <span>{t('app.mcpServices')} ({mcpTools.length})</span>
<ChevronDown className={cn( <ChevronDown className={cn(
"h-3 w-3 transition-transform", "h-3 w-3 transition-transform",
mcpExpanded && "rotate-180" mcpExpanded && "rotate-180"

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { import {
ArrowLeft, ArrowLeft,
@@ -51,6 +52,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
onSlashCommandsSettings, onSlashCommandsSettings,
setCopyPopoverOpen setCopyPopoverOpen
}) => { }) => {
const { t } = useTranslation();
return ( return (
<motion.div <motion.div
initial={{ opacity: 0, y: -20 }} initial={{ opacity: 0, y: -20 }}
@@ -70,7 +72,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Terminal className="h-5 w-5 text-primary" /> <Terminal className="h-5 w-5 text-primary" />
<span className="font-semibold">Claude Code Session</span> <span className="font-semibold">{t('app.claudeCodeSession')}</span>
</div> </div>
{projectPath && ( {projectPath && (
@@ -125,7 +127,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
className="w-full justify-start" className="w-full justify-start"
onClick={onCopyAsJsonl} onClick={onCopyAsJsonl}
> >
Copy as JSONL {t('app.copyAsJsonl')}
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@@ -133,7 +135,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
className="w-full justify-start" className="w-full justify-start"
onClick={onCopyAsMarkdown} onClick={onCopyAsMarkdown}
> >
Copy as Markdown {t('app.copyAsMarkdown')}
</Button> </Button>
</div> </div>
} }

View File

@@ -34,7 +34,34 @@
"of": "of", "of": "of",
"loading": "Loading...", "loading": "Loading...",
"from": "from", "from": "from",
"retry": "Retry" "retry": "Retry",
"model": "Model",
"projectPath": "Project Path",
"task": "Task",
"started": "Started",
"output": "Output",
"copyOutput": "Copy Output",
"copyAsMarkdown": "Copy as Markdown",
"copyAsJsonl": "Copy as JSONL",
"close": "Close",
"systemInitialized": "System Initialized",
"availableTools": "Available Tools",
"workingDirectory": "Working Directory",
"sessionId": "Session ID",
"mcpServices": "MCP Services",
"searchingWithGrep": "Searching with grep",
"searchingForPattern": "Searching for pattern",
"fileContent": "File content",
"yesterday": "Yesterday",
"commands": "Commands",
"claudeCodeSession": "Claude Code Session",
"experimentalFeature": "Experimental Feature",
"checkpointingWarning": "Checkpointing may affect directory structure or cause data loss. Use with caution.",
"timeline": "Timeline",
"noCheckpointsYet": "No checkpoints yet",
"sessionTimeline": "Session Timeline",
"checkpoints": "checkpoints",
"loadingTimeline": "Loading timeline..."
}, },
"navigation": { "navigation": {
"projects": "CC Projects", "projects": "CC Projects",
@@ -80,6 +107,14 @@
"editAgent": "Edit Agent", "editAgent": "Edit Agent",
"deleteAgent": "Delete Agent", "deleteAgent": "Delete Agent",
"executeAgent": "Execute Agent", "executeAgent": "Execute Agent",
"execute": "Execute",
"readyToExecute": "Ready to Execute",
"task": "Task",
"enterTask": "Enter the task for the agent",
"hooks": "Hooks",
"configureHooks": "Configure Hooks",
"fullscreen": "Fullscreen",
"stop": "Stop",
"agentName": "Agent Name", "agentName": "Agent Name",
"agentNameRequired": "Agent name is required", "agentNameRequired": "Agent name is required",
"agentIcon": "Agent Icon", "agentIcon": "Agent Icon",
@@ -102,6 +137,8 @@
"systemPromptRequired": "System prompt is required", "systemPromptRequired": "System prompt is required",
"defaultTask": "Default Task", "defaultTask": "Default Task",
"model": "Model", "model": "Model",
"projectPath": "Project Path",
"selectProjectPath": "Select or enter project path",
"permissions": "Permissions", "permissions": "Permissions",
"fileAccess": "File Access", "fileAccess": "File Access",
"networkAccess": "Network Access", "networkAccess": "Network Access",
@@ -119,7 +156,9 @@
"updateFailed": "Failed to update agent", "updateFailed": "Failed to update agent",
"basicInformation": "Basic Information", "basicInformation": "Basic Information",
"optional": "Optional", "optional": "Optional",
"sonnetName": "Claude 4 Sonnet",
"sonnetDescription": "Faster, efficient for most tasks", "sonnetDescription": "Faster, efficient for most tasks",
"opusName": "Claude 4.1 Opus",
"opusDescription": "More capable, better for complex tasks", "opusDescription": "More capable, better for complex tasks",
"defaultTaskDescription": "This will be used as the default task placeholder when executing the agent", "defaultTaskDescription": "This will be used as the default task placeholder when executing the agent",
"systemPromptDescription": "Define the behavior and capabilities of your CC Agent", "systemPromptDescription": "Define the behavior and capabilities of your CC Agent",
@@ -128,6 +167,15 @@
"createFirstAgent": "Create your first CC Agent to get started", "createFirstAgent": "Create your first CC Agent to get started",
"created": "Created", "created": "Created",
"execute": "Execute", "execute": "Execute",
"readyToExecute": "Ready to Execute",
"enterTask": "Enter the task for the agent",
"hooks": "Hooks",
"configureHooks": "Configure Hooks",
"fullscreen": "Fullscreen",
"stop": "Stop",
"selectProjectPathAndTask": "Select a project path and enter a task to run the agent",
"noExecutionHistory": "No execution history yet",
"agentRunningWarning": "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?",
"export": "Export", "export": "Export",
"import": "Import", "import": "Import",
"importFromFile": "From File", "importFromFile": "From File",
@@ -155,6 +203,8 @@
}, },
"slashCommands": { "slashCommands": {
"slashCommands": "Slash Commands", "slashCommands": "Slash Commands",
"globalSearch": "Global Search",
"searching": "Searching",
"projectSlashCommands": "Project Slash Commands", "projectSlashCommands": "Project Slash Commands",
"createCustomCommandsProject": "Create custom commands for this project", "createCustomCommandsProject": "Create custom commands for this project",
"createCustomCommandsWorkflow": "Create custom commands to streamline your workflow", "createCustomCommandsWorkflow": "Create custom commands to streamline your workflow",
@@ -162,6 +212,8 @@
"allCommands": "All Commands", "allCommands": "All Commands",
"project": "Project", "project": "Project",
"user": "User", "user": "User",
"userCommands": "User Commands",
"manageProjectCommands": "Manage project-specific slash commands for",
"noCommandsFound": "No commands found", "noCommandsFound": "No commands found",
"noProjectCommandsYet": "No project commands created yet", "noProjectCommandsYet": "No project commands created yet",
"noCommandsYet": "No commands created yet", "noCommandsYet": "No commands created yet",

View File

@@ -31,7 +31,34 @@
"page": "页面", "page": "页面",
"of": "共", "of": "共",
"loading": "加载中...", "loading": "加载中...",
"from": "从" "from": "从",
"model": "模型",
"projectPath": "项目路径",
"task": "任务",
"started": "开始时间",
"output": "输出",
"copyOutput": "复制输出",
"copyAsMarkdown": "复制为 Markdown",
"copyAsJsonl": "复制为 JSONL",
"close": "关闭",
"systemInitialized": "系统已初始化",
"availableTools": "可用工具",
"workingDirectory": "工作目录",
"sessionId": "会话 ID",
"mcpServices": "MCP 服务",
"searchingWithGrep": "使用 grep 搜索",
"searchingForPattern": "搜索模式",
"fileContent": "文件内容",
"yesterday": "昨天",
"commands": "命令",
"claudeCodeSession": "Claude Code 会话",
"experimentalFeature": "实验性功能",
"checkpointingWarning": "检查点可能会影响目录结构或导致数据丢失。请谨慎使用。",
"timeline": "时间线",
"noCheckpointsYet": "尚无检查点",
"sessionTimeline": "会话时间线",
"checkpoints": "个检查点",
"loadingTimeline": "加载时间线中..."
}, },
"navigation": { "navigation": {
"projects": "Claude Code 项目", "projects": "Claude Code 项目",
@@ -99,6 +126,8 @@
"systemPromptRequired": "系统提示为必填项", "systemPromptRequired": "系统提示为必填项",
"defaultTask": "默认任务", "defaultTask": "默认任务",
"model": "模型", "model": "模型",
"projectPath": "项目路径",
"selectProjectPath": "选择或输入项目路径",
"permissions": "权限", "permissions": "权限",
"fileAccess": "文件访问", "fileAccess": "文件访问",
"networkAccess": "网络访问", "networkAccess": "网络访问",
@@ -116,7 +145,9 @@
"updateFailed": "更新智能体失败", "updateFailed": "更新智能体失败",
"basicInformation": "基本信息", "basicInformation": "基本信息",
"optional": "可选", "optional": "可选",
"sonnetName": "Claude 4 Sonnet",
"sonnetDescription": "更快,适用于大多数任务", "sonnetDescription": "更快,适用于大多数任务",
"opusName": "Claude 4.1 Opus",
"opusDescription": "功能更强,适用于复杂任务", "opusDescription": "功能更强,适用于复杂任务",
"defaultTaskDescription": "执行智能体时将用作默认任务占位符", "defaultTaskDescription": "执行智能体时将用作默认任务占位符",
"systemPromptDescription": "定义您的 CC 智能体的行为和功能", "systemPromptDescription": "定义您的 CC 智能体的行为和功能",
@@ -125,6 +156,15 @@
"createFirstAgent": "创建您的第一个 CC 智能体来开始", "createFirstAgent": "创建您的第一个 CC 智能体来开始",
"created": "创建于", "created": "创建于",
"execute": "执行", "execute": "执行",
"readyToExecute": "准备执行",
"enterTask": "输入智能体的任务",
"hooks": "钩子",
"configureHooks": "配置钩子",
"fullscreen": "全屏",
"stop": "停止",
"selectProjectPathAndTask": "选择项目路径并输入任务以运行智能体",
"noExecutionHistory": "尚无执行历史",
"agentRunningWarning": "智能体正在运行。如果您离开此页面,智能体将在后台继续运行。您可以在 CC Agents 中的“运行中的会话”选项卡中查看运行中的会话。\n\n是否继续",
"export": "导出", "export": "导出",
"import": "导入", "import": "导入",
"importFromFile": "从文件导入", "importFromFile": "从文件导入",
@@ -152,6 +192,8 @@
}, },
"slashCommands": { "slashCommands": {
"slashCommands": "斜杠命令", "slashCommands": "斜杠命令",
"globalSearch": "全局搜索",
"searching": "搜索中",
"projectSlashCommands": "项目斜杠命令", "projectSlashCommands": "项目斜杠命令",
"createCustomCommandsProject": "为此项目创建自定义命令", "createCustomCommandsProject": "为此项目创建自定义命令",
"createCustomCommandsWorkflow": "创建自定义命令来简化您的工作流程", "createCustomCommandsWorkflow": "创建自定义命令来简化您的工作流程",
@@ -159,6 +201,8 @@
"allCommands": "所有命令", "allCommands": "所有命令",
"project": "项目", "project": "项目",
"user": "用户", "user": "用户",
"userCommands": "用户命令",
"manageProjectCommands": "管理项目特定的斜杠命令",
"noCommandsFound": "未找到命令", "noCommandsFound": "未找到命令",
"noProjectCommandsYet": "尚未创建项目命令", "noProjectCommandsYet": "尚未创建项目命令",
"noCommandsYet": "尚未创建命令", "noCommandsYet": "尚未创建命令",
@@ -519,6 +563,9 @@
"noAgentDataSpecified": "未指定智能体数据", "noAgentDataSpecified": "未指定智能体数据",
"importAgentComingSoon": "导入智能体功能即将推出...", "importAgentComingSoon": "导入智能体功能即将推出...",
"unknownTabType": "未知的标签页类型", "unknownTabType": "未知的标签页类型",
"typeYourPromptHere": "在此输入您的提示...",
"dropImagesHere": "在此放置图片...",
"askClaudeAnything": "询问 Claude 任何问题...",
"selectClaudeCodeInstallation": "选择 Claude Code 安装", "selectClaudeCodeInstallation": "选择 Claude Code 安装",
"multipleInstallationsFound": "在您的系统上找到了多个 Claude Code 安装。请选择您要使用的安装。", "multipleInstallationsFound": "在您的系统上找到了多个 Claude Code 安装。请选择您要使用的安装。",
"claudeCodeNotFoundDialog": "在常见安装位置中未找到 Claude Code。请安装 Claude Code 以继续。", "claudeCodeNotFoundDialog": "在常见安装位置中未找到 Claude Code。请安装 Claude Code 以继续。",