优化页面,国际化细节内容
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { api, type Agent } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
@@ -81,6 +82,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
onBack,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [projectPath, setProjectPath] = useState("");
|
||||
const [task, setTask] = useState(agent.default_task || "");
|
||||
const [model, setModel] = useState(agent.model || "sonnet");
|
||||
@@ -452,7 +454,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?"
|
||||
t('agents.agentRunningWarning')
|
||||
);
|
||||
if (!shouldLeave) {
|
||||
return;
|
||||
@@ -476,7 +478,7 @@ 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 += `**Model:** ${model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}\n`;
|
||||
markdown += `**Date:** ${new Date().toISOString()}\n\n`;
|
||||
markdown += `---\n\n`;
|
||||
|
||||
@@ -572,9 +574,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
{renderIcon()}
|
||||
</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">
|
||||
{model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}
|
||||
{model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -587,7 +589,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
disabled={messages.length === 0}
|
||||
>
|
||||
<Maximize2 className="h-4 w-4 mr-2" />
|
||||
Fullscreen
|
||||
{t('agents.fullscreen')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -612,12 +614,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
|
||||
{/* Project Path */}
|
||||
<div className="space-y-2">
|
||||
<Label>Project Path</Label>
|
||||
<Label>{t('agents.projectPath')}</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={projectPath}
|
||||
onChange={(e) => setProjectPath(e.target.value)}
|
||||
placeholder="Select or enter project path"
|
||||
placeholder={t('agents.selectProjectPath')}
|
||||
disabled={isRunning}
|
||||
className="flex-1"
|
||||
/>
|
||||
@@ -633,17 +635,17 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
variant="outline"
|
||||
onClick={handleOpenHooksDialog}
|
||||
disabled={isRunning || !projectPath}
|
||||
title="Configure hooks"
|
||||
title={t('agents.configureHooks')}
|
||||
>
|
||||
<Settings2 className="h-4 w-4 mr-2" />
|
||||
Hooks
|
||||
{t('agents.hooks')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Model Selection */}
|
||||
<div className="space-y-2">
|
||||
<Label>Model</Label>
|
||||
<Label>{t('agents.model')}</Label>
|
||||
<div className="flex gap-3">
|
||||
<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>
|
||||
<span>Claude 4 Opus</span>
|
||||
<span>Claude 4.1 Opus</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -701,12 +703,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
|
||||
{/* Task Input */}
|
||||
<div className="space-y-2">
|
||||
<Label>Task</Label>
|
||||
<Label>{t('app.task')}</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={task}
|
||||
onChange={(e) => setTask(e.target.value)}
|
||||
placeholder="Enter the task for the agent"
|
||||
placeholder={t('agents.enterTask')}
|
||||
disabled={isRunning}
|
||||
className="flex-1"
|
||||
onKeyPress={(e) => {
|
||||
@@ -723,12 +725,12 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
{isRunning ? (
|
||||
<>
|
||||
<StopCircle className="mr-2 h-4 w-4" />
|
||||
Stop
|
||||
{t('agents.stop')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Play className="mr-2 h-4 w-4" />
|
||||
Execute
|
||||
{t('agents.execute')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@@ -759,9 +761,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">{t('agents.readyToExecute')}</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Select a project path and enter a task to run the agent
|
||||
{t('agents.selectProjectPathAndTask')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -841,7 +843,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
Copy Output
|
||||
{t('app.copyOutput')}
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
}
|
||||
@@ -853,7 +855,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -861,7 +863,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -901,9 +903,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">{t('agents.readyToExecute')}</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Select a project path and enter a task to run the agent
|
||||
{t('agents.selectProjectPathAndTask')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -957,7 +959,7 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
|
||||
>
|
||||
<DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Configure Hooks</DialogTitle>
|
||||
<DialogTitle>{t('agents.configureHooks')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Configure hooks that run before, during, and after tool executions. Changes are saved immediately.
|
||||
</DialogDescription>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useRef, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
Maximize2,
|
||||
@@ -57,6 +58,7 @@ export function AgentRunOutputViewer({
|
||||
tabId,
|
||||
className
|
||||
}: AgentRunOutputViewerProps) {
|
||||
const { t } = useTranslation();
|
||||
const { updateTabTitle, updateTabStatus } = useTabState();
|
||||
const [run, setRun] = useState<AgentRunWithMetrics | null>(null);
|
||||
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
|
||||
@@ -330,7 +332,7 @@ export function AgentRunOutputViewer({
|
||||
if (!run) return;
|
||||
let markdown = `# Agent Execution: ${run.agent_name}\n\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`;
|
||||
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`;
|
||||
@@ -566,7 +568,7 @@ export function AgentRunOutputViewer({
|
||||
</p>
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground mt-2">
|
||||
<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>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
@@ -611,7 +613,7 @@ export function AgentRunOutputViewer({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -619,7 +621,7 @@ export function AgentRunOutputViewer({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -723,7 +725,7 @@ export function AgentRunOutputViewer({
|
||||
size="sm"
|
||||
>
|
||||
<Copy className="h-4 w-4 mr-2" />
|
||||
Copy Output
|
||||
{t('app.copyOutput')}
|
||||
<ChevronDown className="h-3 w-3 ml-2" />
|
||||
</Button>
|
||||
}
|
||||
@@ -735,7 +737,7 @@ export function AgentRunOutputViewer({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -743,7 +745,7 @@ export function AgentRunOutputViewer({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Popover } from "@/components/ui/popover";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { api, type AgentRunWithMetrics } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { formatISOTimestamp } from "@/lib/date-utils";
|
||||
@@ -48,6 +49,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
|
||||
onBack,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [run, setRun] = useState<AgentRunWithMetrics | null>(null);
|
||||
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -288,7 +290,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
Copy Output
|
||||
{t('app.copyOutput')}
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
}
|
||||
@@ -300,7 +302,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -308,7 +310,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -327,7 +329,7 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
|
||||
<h3 className="text-sm font-medium">Task:</h3>
|
||||
<p className="text-sm text-muted-foreground flex-1">{run.task}</p>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Play, Clock, Hash, Bot } from "lucide-react";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Pagination } from "@/components/ui/pagination";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { formatISOTimestamp } from "@/lib/date-utils";
|
||||
import type { AgentRunWithMetrics } from "@/lib/api";
|
||||
@@ -41,6 +42,7 @@ export const AgentRunsList: React.FC<AgentRunsListProps> = ({
|
||||
onRunClick,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const { createAgentTab } = useTabState();
|
||||
|
||||
@@ -91,7 +93,7 @@ export const AgentRunsList: React.FC<AgentRunsListProps> = ({
|
||||
return (
|
||||
<div className={cn("text-center py-8 text-muted-foreground", className)}>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
<span>Started: {formatISOTimestamp(run.created_at)}</span>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Popover } from "@/components/ui/popover";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { api, type Session } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
@@ -76,6 +77,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
className,
|
||||
onStreamingChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [projectPath, setProjectPath] = useState(initialProjectPath || session?.project_path || "");
|
||||
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -1127,7 +1129,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
const messagesList = (
|
||||
<div
|
||||
ref={parentRef}
|
||||
className="flex-1 overflow-y-auto relative pb-40"
|
||||
className="flex-1 overflow-y-auto relative pb-24"
|
||||
style={{
|
||||
contain: 'strict',
|
||||
}}
|
||||
@@ -1247,7 +1249,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
}
|
||||
|
||||
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">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
@@ -1268,7 +1270,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5 text-muted-foreground" />
|
||||
<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">
|
||||
{projectPath ? `${projectPath}` : "No project selected"}
|
||||
</p>
|
||||
@@ -1300,13 +1302,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
{showSettings && (
|
||||
<CheckpointSettings
|
||||
sessionId={effectiveSession?.id || ''}
|
||||
projectId={effectiveSession?.project_id || ''}
|
||||
projectPath={projectPath}
|
||||
/>
|
||||
)}
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -1320,7 +1315,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Checkpoint Settings</p>
|
||||
<p>{t('checkpoint.checkpointSettingsTitle')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
@@ -1352,7 +1347,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
Copy Output
|
||||
{t('app.copyOutput')}
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
}
|
||||
@@ -1364,7 +1359,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
onClick={handleCopyAsMarkdown}
|
||||
className="w-full justify-start"
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -1372,7 +1367,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
onClick={handleCopyAsJsonl}
|
||||
className="w-full justify-start"
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -1554,7 +1549,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
)}
|
||||
|
||||
<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"
|
||||
)}>
|
||||
<FloatingPromptInput
|
||||
@@ -1603,7 +1598,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
<div className="h-full flex flex-col">
|
||||
{/* Timeline Header */}
|
||||
<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
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
@@ -1682,6 +1677,12 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
{showSettings && effectiveSession && (
|
||||
<Dialog open={showSettings} onOpenChange={setShowSettings}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('checkpoint.checkpointSettingsTitle')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t('app.checkpointingWarning')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<CheckpointSettings
|
||||
sessionId={effectiveSession.id}
|
||||
projectId={effectiveSession.project_id}
|
||||
@@ -1699,7 +1700,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
<DialogHeader>
|
||||
<DialogTitle>Slash Commands</DialogTitle>
|
||||
<DialogDescription>
|
||||
Manage project-specific slash commands for {projectPath}
|
||||
{t('slashCommands.manageProjectCommands')} {projectPath}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
|
||||
@@ -271,7 +271,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-sm font-semibold">Claude 4.1 Opus</div>
|
||||
<div className="text-xs opacity-80">{t('agents.opusDescription')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { SlashCommandPicker } from "./SlashCommandPicker";
|
||||
import { ImagePreview } from "./ImagePreview";
|
||||
import { type FileEntry, type SlashCommand } from "@/lib/api";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { useTranslation } from "@/hooks/useTranslation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface FloatingPromptInputProps {
|
||||
/**
|
||||
@@ -110,7 +110,7 @@ const MODELS: Model[] = [
|
||||
},
|
||||
{
|
||||
id: "opus",
|
||||
name: "Claude 4 Opus",
|
||||
name: "Claude 4.1 Opus",
|
||||
description: "More capable, better for complex tasks",
|
||||
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 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">{t('app.model')}:</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -871,7 +871,7 @@ const FloatingPromptInputInner = (
|
||||
{/* Fixed Position Input Bar */}
|
||||
<div
|
||||
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",
|
||||
className
|
||||
)}
|
||||
@@ -890,8 +890,8 @@ const FloatingPromptInputInner = (
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="p-4">
|
||||
<div className="flex items-end gap-3">
|
||||
<div className="p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Model Picker */}
|
||||
<Popover
|
||||
trigger={
|
||||
@@ -899,7 +899,7 @@ const FloatingPromptInputInner = (
|
||||
variant="outline"
|
||||
size="default"
|
||||
disabled={disabled}
|
||||
className="gap-2 min-w-[180px] justify-start"
|
||||
className="gap-2 min-w-[160px] h-10 justify-start"
|
||||
>
|
||||
{selectedModelData.icon}
|
||||
<span className="flex-1 text-left">{selectedModelData.name}</span>
|
||||
@@ -948,7 +948,7 @@ const FloatingPromptInputInner = (
|
||||
variant="outline"
|
||||
size="default"
|
||||
disabled={disabled}
|
||||
className="gap-2"
|
||||
className="gap-2 h-10"
|
||||
>
|
||||
<Brain className="h-4 w-4" />
|
||||
<ThinkingModeIndicator
|
||||
@@ -1009,7 +1009,7 @@ const FloatingPromptInputInner = (
|
||||
placeholder={dragActive ? t('messages.dropImagesHere') : t('messages.askClaudeAnything')}
|
||||
disabled={disabled}
|
||||
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"
|
||||
)}
|
||||
rows={1}
|
||||
@@ -1056,7 +1056,7 @@ const FloatingPromptInputInner = (
|
||||
disabled={isLoading ? false : (!prompt.trim() || disabled)}
|
||||
variant={isLoading ? "destructive" : "default"}
|
||||
size="default"
|
||||
className="min-w-[60px]"
|
||||
className="min-w-[60px] h-10"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { api, type ProcessInfo, type Session } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatISOTimestamp } from "@/lib/date-utils";
|
||||
|
||||
interface RunningClaudeSessionsProps {
|
||||
@@ -25,6 +26,7 @@ export const RunningClaudeSessions: React.FC<RunningClaudeSessionsProps> = ({
|
||||
onSessionClick,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [runningSessions, setRunningSessions] = useState<ProcessInfo[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -144,11 +146,11 @@ export const RunningClaudeSessions: React.FC<RunningClaudeSessionsProps> = ({
|
||||
</p>
|
||||
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||
<span>Started: {formatISOTimestamp(session.started_at)}</span>
|
||||
<span>Model: {session.model}</span>
|
||||
<span>{t('app.started')}: {formatISOTimestamp(session.started_at)}</span>
|
||||
<span>{t('app.model')}: {session.model}</span>
|
||||
{session.task && (
|
||||
<span className="truncate max-w-[200px]" title={session.task}>
|
||||
Task: {session.task}
|
||||
{t('app.task')}: {session.task}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { api } from '@/lib/api';
|
||||
import { useOutputCache } from '@/lib/outputCache';
|
||||
import type { AgentRun } from '@/lib/api';
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { StreamMessage } from './StreamMessage';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
|
||||
@@ -38,6 +39,7 @@ export interface ClaudeStreamMessage {
|
||||
}
|
||||
|
||||
export function SessionOutputViewer({ session, onClose, className }: SessionOutputViewerProps) {
|
||||
const { t } = useTranslation();
|
||||
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
|
||||
const [rawJsonlOutput, setRawJsonlOutput] = useState<string[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -410,7 +412,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsFullscreen(!isFullscreen)}
|
||||
title="Fullscreen"
|
||||
title={t('agents.fullscreen')}
|
||||
>
|
||||
{isFullscreen ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />}
|
||||
</Button>
|
||||
@@ -422,7 +424,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
Copy Output
|
||||
{t('app.copyOutput')}
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
}
|
||||
@@ -434,7 +436,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -442,7 +444,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -567,7 +569,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
Copy Output
|
||||
{t('app.copyOutput')}
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
}
|
||||
@@ -579,7 +581,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -587,7 +589,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
className="w-full justify-start"
|
||||
onClick={handleCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import type { SlashCommand } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTrackEvent, useFeatureAdoptionTracking } from "@/hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface SlashCommandPickerProps {
|
||||
/**
|
||||
@@ -79,6 +80,7 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
|
||||
initialQuery = "",
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [commands, setCommands] = useState<SlashCommand[]>([]);
|
||||
const [filteredCommands, setFilteredCommands] = useState<SlashCommand[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -241,7 +243,7 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
|
||||
const groupedCommands = filteredCommands.reduce((acc, cmd) => {
|
||||
let key: string;
|
||||
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") {
|
||||
key = cmd.namespace ? `Project Commands: ${cmd.namespace}` : "Project Commands";
|
||||
} else {
|
||||
@@ -278,10 +280,10 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<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 && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Searching: "{searchQuery}"
|
||||
{t('slashCommands.searching')}: "{searchQuery}"
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -474,7 +476,7 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
|
||||
{Object.entries(groupedCommands).map(([groupKey, groupCommands]) => (
|
||||
<div key={groupKey}>
|
||||
<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}
|
||||
</h3>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import {
|
||||
Database,
|
||||
@@ -81,6 +82,7 @@ interface QueryResult {
|
||||
* StorageTab component - A beautiful SQLite database viewer/editor
|
||||
*/
|
||||
export const StorageTab: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [tables, setTables] = useState<TableInfo[]>([]);
|
||||
const [selectedTable, setSelectedTable] = useState<string>("");
|
||||
const [tableData, setTableData] = useState<TableData | null>(null);
|
||||
@@ -934,7 +936,7 @@ export const StorageTab: React.FC = () => {
|
||||
{loading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
"Execute"
|
||||
t('agents.execute')
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
GitBranch,
|
||||
@@ -57,6 +58,7 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
|
||||
onCheckpointCreated,
|
||||
className
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [timeline, setTimeline] = useState<SessionTimeline | null>(null);
|
||||
const [selectedCheckpoint, setSelectedCheckpoint] = useState<Checkpoint | null>(null);
|
||||
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">
|
||||
<AlertCircle className="h-4 w-4 text-yellow-600 mt-0.5" />
|
||||
<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">
|
||||
Checkpointing may affect directory structure or cause data loss. Use with caution.
|
||||
{t('app.checkpointingWarning')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -425,10 +427,10 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<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 && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{timeline.totalCheckpoints} checkpoints
|
||||
{timeline.totalCheckpoints} {t('app.checkpoints')}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
@@ -459,7 +461,7 @@ export const TimelineNavigator: React.FC<TimelineNavigatorProps> = ({
|
||||
</div>
|
||||
) : (
|
||||
<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>
|
||||
)}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
Hash,
|
||||
} from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
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
|
||||
*/
|
||||
export const ReadResultWidget: React.FC<{ content: string; filePath?: string }> = ({ content, filePath }) => {
|
||||
const { t } = useTranslation();
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const { theme } = useTheme();
|
||||
const syntaxTheme = getClaudeSyntaxTheme(theme);
|
||||
@@ -510,7 +512,7 @@ export const ReadResultWidget: React.FC<{ content: string; filePath?: string }>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileText className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span className="text-xs font-mono text-muted-foreground">
|
||||
{filePath || "File content"}
|
||||
{filePath || t('app.fileContent')}
|
||||
</span>
|
||||
{isLargeFile && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
@@ -572,6 +574,7 @@ export const ReadResultWidget: React.FC<{ content: string; filePath?: string }>
|
||||
* Widget for Glob tool
|
||||
*/
|
||||
export const GlobWidget: React.FC<{ pattern: string; result?: any }> = ({ pattern, result }) => {
|
||||
const { t } = useTranslation();
|
||||
// Extract result content if available
|
||||
let resultContent = '';
|
||||
let isError = false;
|
||||
@@ -597,7 +600,7 @@ export const GlobWidget: React.FC<{ pattern: string; result?: any }> = ({ patter
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 p-3 rounded-lg bg-muted/50">
|
||||
<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">
|
||||
{pattern}
|
||||
</code>
|
||||
@@ -874,6 +877,7 @@ export const GrepWidget: React.FC<{
|
||||
exclude?: string;
|
||||
result?: any;
|
||||
}> = ({ pattern, include, path, exclude, result }) => {
|
||||
const { t } = useTranslation();
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
|
||||
// Extract result content if available
|
||||
@@ -927,7 +931,7 @@ export const GrepWidget: React.FC<{
|
||||
<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">
|
||||
<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 && (
|
||||
<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" />
|
||||
@@ -1804,6 +1808,7 @@ export const SystemInitializedWidget: React.FC<{
|
||||
cwd?: string;
|
||||
tools?: string[];
|
||||
}> = ({ sessionId, model, cwd, tools = [] }) => {
|
||||
const { t } = useTranslation();
|
||||
const [mcpExpanded, setMcpExpanded] = useState(false);
|
||||
|
||||
// Separate regular tools from MCP tools
|
||||
@@ -1880,14 +1885,14 @@ export const SystemInitializedWidget: React.FC<{
|
||||
<div className="flex items-start gap-3">
|
||||
<Settings className="h-5 w-5 text-blue-500 mt-0.5" />
|
||||
<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 */}
|
||||
<div className="space-y-2">
|
||||
{sessionId && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<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">
|
||||
{sessionId}
|
||||
</code>
|
||||
@@ -1897,7 +1902,7 @@ export const SystemInitializedWidget: React.FC<{
|
||||
{model && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<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">
|
||||
{model}
|
||||
</code>
|
||||
@@ -1907,7 +1912,7 @@ export const SystemInitializedWidget: React.FC<{
|
||||
{cwd && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<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">
|
||||
{cwd}
|
||||
</code>
|
||||
@@ -1921,7 +1926,7 @@ export const SystemInitializedWidget: React.FC<{
|
||||
<div className="flex items-center gap-2">
|
||||
<Wrench className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
Available Tools ({regularTools.length})
|
||||
{t('app.availableTools')} ({regularTools.length})
|
||||
</span>
|
||||
</div>
|
||||
<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"
|
||||
>
|
||||
<Package className="h-3.5 w-3.5" />
|
||||
<span>MCP Services ({mcpTools.length})</span>
|
||||
<span>{t('app.mcpServices')} ({mcpTools.length})</span>
|
||||
<ChevronDown className={cn(
|
||||
"h-3 w-3 transition-transform",
|
||||
mcpExpanded && "rotate-180"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
ArrowLeft,
|
||||
@@ -51,6 +52,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
|
||||
onSlashCommandsSettings,
|
||||
setCopyPopoverOpen
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
@@ -70,7 +72,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<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>
|
||||
|
||||
{projectPath && (
|
||||
@@ -125,7 +127,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
|
||||
className="w-full justify-start"
|
||||
onClick={onCopyAsJsonl}
|
||||
>
|
||||
Copy as JSONL
|
||||
{t('app.copyAsJsonl')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -133,7 +135,7 @@ export const SessionHeader: React.FC<SessionHeaderProps> = React.memo(({
|
||||
className="w-full justify-start"
|
||||
onClick={onCopyAsMarkdown}
|
||||
>
|
||||
Copy as Markdown
|
||||
{t('app.copyAsMarkdown')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user