This commit is contained in:
2025-08-06 15:39:05 +08:00
parent 351a79d54c
commit 6798be3b42
26 changed files with 1243 additions and 469 deletions

View File

@@ -40,6 +40,7 @@ import { AgentExecution } from "./AgentExecution";
import { AgentRunsList } from "./AgentRunsList";
import { GitHubAgentBrowser } from "./GitHubAgentBrowser";
import { ICON_MAP } from "./IconPicker";
import { useTranslation } from "@/hooks/useTranslation";
interface CCAgentsProps {
/**
@@ -64,6 +65,7 @@ export type AgentIconName = keyof typeof AGENT_ICONS;
* <CCAgents onBack={() => setView('home')} />
*/
export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
const { t } = useTranslation();
const [agents, setAgents] = useState<Agent[]>([]);
const [runs, setRuns] = useState<AgentRunWithMetrics[]>([]);
const [loading, setLoading] = useState(true);
@@ -94,8 +96,8 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
setAgents(agentsList);
} catch (err) {
console.error("Failed to load agents:", err);
setError("Failed to load agents");
setToast({ message: "Failed to load agents", type: "error" });
setError(t('agents.loadAgentsFailed'));
setToast({ message: t('agents.loadAgentsFailed'), type: "error" });
} finally {
setLoading(false);
}
@@ -132,12 +134,12 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
try {
setIsDeleting(true);
await api.deleteAgent(agentToDelete.id);
setToast({ message: "Agent deleted successfully", type: "success" });
setToast({ message: t('agents.agentDeleted'), type: "success" });
await loadAgents();
await loadRuns(); // Reload runs as they might be affected
} catch (err) {
console.error("Failed to delete agent:", err);
setToast({ message: "Failed to delete agent", type: "error" });
setToast({ message: t('agents.deleteFailed'), type: "error" });
} finally {
setIsDeleting(false);
setShowDeleteDialog(false);
@@ -166,13 +168,13 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
const handleAgentCreated = async () => {
setView("list");
await loadAgents();
setToast({ message: "Agent created successfully", type: "success" });
setToast({ message: t('agents.agentCreated'), type: "success" });
};
const handleAgentUpdated = async () => {
setView("list");
await loadAgents();
setToast({ message: "Agent updated successfully", type: "success" });
setToast({ message: t('agents.agentUpdated'), type: "success" });
};
// const handleRunClick = (run: AgentRunWithMetrics) => {
@@ -209,10 +211,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
filePath
});
setToast({ message: `Agent "${agent.name}" exported successfully`, type: "success" });
setToast({ message: t('agents.exportedSuccessfully', { name: agent.name }), type: "success" });
} catch (err) {
console.error("Failed to export agent:", err);
setToast({ message: "Failed to export agent", type: "error" });
setToast({ message: t('agents.exportFailed'), type: "error" });
}
};
@@ -235,11 +237,11 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
// Import the agent from the selected file
await api.importAgentFromFile(filePath as string);
setToast({ message: "Agent imported successfully", type: "success" });
setToast({ message: t('agents.importedSuccessfully'), type: "success" });
await loadAgents();
} catch (err) {
console.error("Failed to import agent:", err);
const errorMessage = err instanceof Error ? err.message : "Failed to import agent";
const errorMessage = err instanceof Error ? err.message : t('agents.importFailed');
setToast({ message: errorMessage, type: "error" });
}
};
@@ -308,9 +310,9 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<h1 className="text-2xl font-bold">CC Agents</h1>
<h1 className="text-2xl font-bold">{t('navigation.agents')}</h1>
<p className="text-sm text-muted-foreground">
Manage your Claude Code agents
{t('agents.manageAgents')}
</p>
</div>
</div>
@@ -323,18 +325,18 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
className="flex items-center gap-2"
>
<Download className="h-4 w-4" />
Import
{t('agents.import')}
<ChevronDown className="h-3 w-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleImportAgent}>
<FileJson className="h-4 w-4 mr-2" />
From File
{t('agents.importFromFile')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setShowGitHubBrowser(true)}>
<Globe className="h-4 w-4 mr-2" />
From GitHub
{t('agents.importFromGitHub')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -344,7 +346,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
className="flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Create CC Agent
{t('agents.createAgent')}
</Button>
</div>
</div>
@@ -381,13 +383,13 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
) : agents.length === 0 ? (
<div className="flex flex-col items-center justify-center h-64 text-center">
<Bot className="h-16 w-16 text-muted-foreground mb-4" />
<h3 className="text-lg font-medium mb-2">No agents yet</h3>
<h3 className="text-lg font-medium mb-2">{t('agents.noAgentsYet')}</h3>
<p className="text-sm text-muted-foreground mb-4">
Create your first CC Agent to get started
{t('agents.createFirstAgent')}
</p>
<Button onClick={() => setView("create")} size="default">
<Plus className="h-4 w-4 mr-2" />
Create CC Agent
{t('agents.createAgent')}
</Button>
</div>
) : (
@@ -411,7 +413,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
{agent.name}
</h3>
<p className="text-xs text-muted-foreground">
Created: {new Date(agent.created_at).toLocaleDateString()}
{t('agents.created')}: {new Date(agent.created_at).toLocaleDateString()}
</p>
</CardContent>
<CardFooter className="p-4 pt-0 flex justify-center gap-1 flex-wrap">
@@ -420,20 +422,20 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
variant="ghost"
onClick={() => handleExecuteAgent(agent)}
className="flex items-center gap-1"
title="Execute agent"
title={t('agents.executeAgentTitle')}
>
<Play className="h-3 w-3" />
Execute
{t('agents.execute')}
</Button>
<Button
size="sm"
variant="ghost"
onClick={() => handleEditAgent(agent)}
className="flex items-center gap-1"
title="Edit agent"
title={t('agents.editAgent')}
>
<Edit className="h-3 w-3" />
Edit
{t('app.edit')}
</Button>
<Button
size="sm"
@@ -443,17 +445,17 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
title="Export agent to .claudia.json"
>
<Upload className="h-3 w-3" />
Export
{t('agents.export')}
</Button>
<Button
size="sm"
variant="ghost"
onClick={() => handleDeleteAgent(agent)}
className="flex items-center gap-1 text-destructive hover:text-destructive"
title="Delete agent"
title={t('agents.deleteAgent')}
>
<Trash2 className="h-3 w-3" />
Delete
{t('app.delete')}
</Button>
</CardFooter>
</Card>
@@ -471,10 +473,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage === 1}
>
Previous
{t('app.previous')}
</Button>
<span className="flex items-center px-3 text-sm">
Page {currentPage} of {totalPages}
{t('app.page')} {currentPage} {t('app.of')} {totalPages}
</span>
<Button
size="sm"
@@ -482,7 +484,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
disabled={currentPage === totalPages}
>
Next
{t('app.next')}
</Button>
</div>
)}
@@ -495,7 +497,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
<div className="overflow-hidden">
<div className="flex items-center gap-2 mb-4">
<History className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold">Recent Executions</h2>
<h2 className="text-lg font-semibold">{t('agents.recentExecutions')}</h2>
</div>
{runsLoading ? (
<div className="flex items-center justify-center h-32">
@@ -531,7 +533,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
onImportSuccess={async () => {
setShowGitHubBrowser(false);
await loadAgents();
setToast({ message: "Agent imported successfully from GitHub", type: "success" });
setToast({ message: t('agents.importFromGitHubSuccess'), type: "success" });
}}
/>
@@ -541,11 +543,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Trash2 className="h-5 w-5 text-destructive" />
Delete Agent
{t('agents.deleteAgentTitle')}
</DialogTitle>
<DialogDescription>
Are you sure you want to delete the agent "{agentToDelete?.name}"?
This action cannot be undone and will permanently remove the agent and all its associated data.
{t('agents.deleteConfirmation', { name: agentToDelete?.name })}
</DialogDescription>
</DialogHeader>
<DialogFooter className="flex flex-col-reverse sm:flex-row sm:justify-end gap-2">
@@ -555,7 +556,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
disabled={isDeleting}
className="w-full sm:w-auto"
>
Cancel
{t('app.cancel')}
</Button>
<Button
variant="destructive"
@@ -566,12 +567,12 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
{isDeleting ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
Deleting...
{t('agents.deleting')}
</>
) : (
<>
<Trash2 className="h-4 w-4 mr-2" />
Delete Agent
{t('agents.deleteAgentButton')}
</>
)}
</Button>