import React, { useState, useEffect } from "react"; import { motion } from "framer-motion"; import { Play, Loader2, Terminal, AlertCircle } from "lucide-react"; 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 { /** * Callback when a running session is clicked to resume */ onSessionClick?: (session: Session) => void; /** * Optional className for styling */ className?: string; } /** * Component to display currently running Claude sessions */ export const RunningClaudeSessions: React.FC = ({ onSessionClick, className, }) => { const { t } = useTranslation(); const [runningSessions, setRunningSessions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { loadRunningSessions(); // Poll for updates every 5 seconds const interval = setInterval(loadRunningSessions, 5000); return () => clearInterval(interval); }, []); const loadRunningSessions = async () => { try { const sessions = await api.listRunningClaudeSessions(); setRunningSessions(sessions); setError(null); } catch (err) { console.error("Failed to load running sessions:", err); setError(t('runningSessions.loadFailed')); } finally { setLoading(false); } }; const handleResumeSession = (processInfo: ProcessInfo) => { // Extract session ID from process type if ('ClaudeSession' in processInfo.process_type) { const sessionId = processInfo.process_type.ClaudeSession.session_id; // Create a minimal session object for resumption const session: Session = { id: sessionId, project_id: processInfo.project_path.replace(/[^a-zA-Z0-9]/g, '-'), project_path: processInfo.project_path, created_at: new Date(processInfo.started_at).getTime() / 1000, }; // Emit event to navigate to the session const event = new CustomEvent('claude-session-selected', { detail: { session, projectPath: processInfo.project_path } }); window.dispatchEvent(event); onSessionClick?.(session); } }; if (loading && runningSessions.length === 0) { return (
); } if (error) { return (
{error}
); } if (runningSessions.length === 0) { return null; } return (

{t('runningSessions.title')}

{t('runningSessions.countRunning', { count: runningSessions.length })}
{runningSessions.map((session) => { const sessionId = 'ClaudeSession' in session.process_type ? session.process_type.ClaudeSession.session_id : null; if (!sessionId) return null; return ( handleResumeSession(session)} >

{sessionId.substring(0, 20)}...

{t('runningSessions.running')}

{session.project_path}

{t('app.started')}: {formatISOTimestamp(session.started_at)} {t('app.model')}: {session.model} {session.task && ( {t('app.task')}: {session.task} )}
); })}
); };