Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,11 @@ function AppContent() {

case "cc-agents":
return (
<CCAgents
onBack={() => handleViewChange("welcome")}
/>
<div className="flex-1 overflow-y-auto" style={{ height: '100vh' }}>
<CCAgents
onBack={() => handleViewChange("welcome")}
/>
</div>
);

case "editor":
Expand Down
34 changes: 25 additions & 9 deletions src/components/AgentsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Bot, Plus, Loader2, Play, Clock, CheckCircle, XCircle, Trash2, Import, ChevronDown, FileJson, Globe, Download } from 'lucide-react';
import { Bot, Plus, Loader2, Play, Clock, CheckCircle, XCircle, Trash2, Import, ChevronDown, FileJson, Globe, Download, Edit } from 'lucide-react';
import {
Dialog,
DialogContent,
Expand All @@ -17,7 +17,6 @@ import {
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Toast } from '@/components/ui/toast';
import { api, type Agent, type AgentRunWithMetrics } from '@/lib/api';
import { useTabState } from '@/hooks/useTabState';
Expand Down Expand Up @@ -135,6 +134,15 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
createCreateAgentTab();
};

const handleEditAgent = (agent: Agent) => {
// Close modal
onOpenChange(false);
// Dispatch custom event to open edit agent tab
window.dispatchEvent(new CustomEvent('open-edit-agent-tab', {
detail: { agent }
}));
};

const handleImportFromFile = async () => {
try {
const filePath = await openDialog({
Expand Down Expand Up @@ -197,7 +205,7 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl h-[600px] flex flex-col p-0">
<DialogContent className="max-w-4xl max-h-[90vh] flex flex-col p-0">
<DialogHeader className="px-6 pt-6">
<DialogTitle className="flex items-center gap-2">
<Bot className="w-5 h-5" />
Expand All @@ -222,10 +230,10 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
</TabsList>

<div className="flex-1 overflow-hidden">
<TabsContent value="agents" className="h-full m-0">
<ScrollArea className="h-full px-6 pb-6">
<TabsContent value="agents" className="h-full m-0 p-0">
<div className="max-h-[400px] overflow-y-scroll px-6 pb-6 scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100">
{/* Action buttons at the top */}
<div className="flex gap-2 mb-4 pt-4">
<div className="flex gap-2 mb-4 pt-4 sticky top-0 bg-background z-10">
<Button onClick={handleCreateAgent} className="flex-1">
<Plus className="w-4 h-4 mr-2" />
Create Agent
Expand Down Expand Up @@ -291,6 +299,14 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
)}
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="ghost"
onClick={() => handleEditAgent(agent)}
>
<Edit className="w-3 h-3 mr-1" />
Edit
</Button>
<Button
size="sm"
variant="ghost"
Expand Down Expand Up @@ -321,11 +337,11 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
))}
</div>
)}
</ScrollArea>
</div>
</TabsContent>

<TabsContent value="running" className="h-full m-0">
<ScrollArea className="h-full px-6 pb-6">
<div className="h-full overflow-y-auto px-6 pb-6">
{runningAgents.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full text-center">
<Clock className="w-12 h-12 text-muted-foreground mb-4" />
Expand Down Expand Up @@ -379,7 +395,7 @@ export const AgentsModal: React.FC<AgentsModalProps> = ({ open, onOpenChange })
</AnimatePresence>
</div>
)}
</ScrollArea>
</div>
</TabsContent>
</div>
</Tabs>
Expand Down
8 changes: 4 additions & 4 deletions src/components/CCAgents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
const [agentToDelete, setAgentToDelete] = useState<Agent | null>(null);
const [isDeleting, setIsDeleting] = useState(false);

const AGENTS_PER_PAGE = 9; // 3x3 grid
const AGENTS_PER_PAGE = 12; // Show all agents at once since we have scrolling

useEffect(() => {
loadAgents();
Expand Down Expand Up @@ -288,8 +288,8 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
// Removed viewRun case - now using modal preview in AgentRunsList

return (
<div className={cn("flex flex-col h-full bg-background", className)}>
<div className="w-full max-w-6xl mx-auto flex flex-col h-full p-6">
<div className={cn("bg-background", className)}>
<div className="w-full max-w-6xl mx-auto p-6">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: -20 }}
Expand Down Expand Up @@ -362,7 +362,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
)}

{/* Main Content */}
<div className="flex-1 overflow-y-auto">
<div>
<AnimatePresence mode="wait">
<motion.div
key="agents"
Expand Down
40 changes: 32 additions & 8 deletions src/components/CreateAgent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,41 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
onAgentCreated,
className,
}) => {
const [name, setName] = useState(agent?.name || "");
const [selectedIcon, setSelectedIcon] = useState<AgentIconName>((agent?.icon as AgentIconName) || "bot");
const [systemPrompt, setSystemPrompt] = useState(agent?.system_prompt || "");
const [defaultTask, setDefaultTask] = useState(agent?.default_task || "");
const [model, setModel] = useState(agent?.model || "sonnet");
// Check for editing agent in localStorage
const [editingAgent, setEditingAgent] = useState<Agent | undefined>(() => {
if (agent) return agent;
const stored = window.localStorage.getItem('editingAgent');
if (stored) {
// Don't remove immediately - will clean up after component mounts
return JSON.parse(stored);
}
return undefined;
});

// Clean up localStorage after component mounts
React.useEffect(() => {
if (editingAgent && !agent) {
// Small delay to ensure state is set
const timer = setTimeout(() => {
window.localStorage.removeItem('editingAgent');
}, 500);
return () => clearTimeout(timer);
}
}, [editingAgent, agent]);

const agentData = editingAgent || agent;

const [name, setName] = useState(agentData?.name || "");
const [selectedIcon, setSelectedIcon] = useState<AgentIconName>((agentData?.icon as AgentIconName) || "bot");
const [systemPrompt, setSystemPrompt] = useState(agentData?.system_prompt || "");
const [defaultTask, setDefaultTask] = useState(agentData?.default_task || "");
const [model, setModel] = useState(agentData?.model || "sonnet");
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null);
const [showIconPicker, setShowIconPicker] = useState(false);

const isEditMode = !!agent;
const isEditMode = !!agentData;

const handleSave = async () => {
if (!name.trim()) {
Expand All @@ -70,9 +94,9 @@ export const CreateAgent: React.FC<CreateAgentProps> = ({
setSaving(true);
setError(null);

if (isEditMode && agent.id) {
if (isEditMode && agentData.id) {
await api.updateAgent(
agent.id,
agentData.id,
name,
selectedIcon,
systemPrompt,
Expand Down
14 changes: 14 additions & 0 deletions src/components/TabContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,18 @@ export const TabContent: React.FC = () => {
createImportAgentTab();
};

const handleOpenEditAgentTab = (event: CustomEvent) => {
const { agent } = event.detail;
// Store agent in localStorage temporarily
window.localStorage.setItem('editingAgent', JSON.stringify(agent));
// Create a new create-agent tab
const tabId = createCreateAgentTab();
// Update the tab title to show it's editing
setTimeout(() => {
updateTab(tabId, { title: `Edit: ${agent.name}` });
}, 100);
};

const handleCloseTab = (event: CustomEvent) => {
const { tabId } = event.detail;
closeTab(tabId);
Expand Down Expand Up @@ -387,6 +399,7 @@ export const TabContent: React.FC = () => {
window.addEventListener('open-agent-execution', handleOpenAgentExecution as EventListener);
window.addEventListener('open-create-agent-tab', handleOpenCreateAgentTab);
window.addEventListener('open-import-agent-tab', handleOpenImportAgentTab);
window.addEventListener('open-edit-agent-tab', handleOpenEditAgentTab as EventListener);
window.addEventListener('close-tab', handleCloseTab as EventListener);
window.addEventListener('claude-session-selected', handleClaudeSessionSelected as EventListener);
return () => {
Expand All @@ -395,6 +408,7 @@ export const TabContent: React.FC = () => {
window.removeEventListener('open-agent-execution', handleOpenAgentExecution as EventListener);
window.removeEventListener('open-create-agent-tab', handleOpenCreateAgentTab);
window.removeEventListener('open-import-agent-tab', handleOpenImportAgentTab);
window.removeEventListener('open-edit-agent-tab', handleOpenEditAgentTab as EventListener);
window.removeEventListener('close-tab', handleCloseTab as EventListener);
window.removeEventListener('claude-session-selected', handleClaudeSessionSelected as EventListener);
};
Expand Down