import { useState } from "react"; import type { TaskCheckpoint } from "../../lib/api"; import { forkTask, resumeFromCheckpoint } from "../../lib/api"; interface CheckpointCardProps { checkpoint: TaskCheckpoint; taskId: string; onActionComplete: () => void; } export function CheckpointCard({ checkpoint, taskId, onActionComplete }: CheckpointCardProps) { const [showActions, setShowActions] = useState(false); const [showForkDialog, setShowForkDialog] = useState(false); const [showResumeDialog, setShowResumeDialog] = useState(false); const [forkName, setForkName] = useState(`Fork from checkpoint ${checkpoint.checkpointNumber}`); const [forkPlan, setForkPlan] = useState(""); const [resumePlan, setResumePlan] = useState(""); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const handleFork = async () => { if (!forkName.trim() || !forkPlan.trim()) { setError("Name and plan are required"); return; } setIsLoading(true); setError(null); try { await forkTask(taskId, { forkFromType: "checkpoint", forkFromValue: String(checkpoint.checkpointNumber), newTaskName: forkName, newTaskPlan: forkPlan, includeConversation: true, }); setShowForkDialog(false); onActionComplete(); } catch (e) { setError(e instanceof Error ? e.message : "Failed to fork task"); } finally { setIsLoading(false); } }; const handleResume = async () => { if (!resumePlan.trim()) { setError("Plan is required"); return; } setIsLoading(true); setError(null); try { await resumeFromCheckpoint(taskId, checkpoint.id, { plan: resumePlan, includeConversation: true, }); setShowResumeDialog(false); onActionComplete(); } catch (e) { setError(e instanceof Error ? e.message : "Failed to resume from checkpoint"); } finally { setIsLoading(false); } }; return ( <>
{/* Checkpoint info */}
#{checkpoint.checkpointNumber} {checkpoint.commitSha.slice(0, 7)} on {checkpoint.branchName}
{checkpoint.message && (
{checkpoint.message}
)} {/* Files changed */} {checkpoint.filesChanged.length > 0 && (
{checkpoint.filesChanged.slice(0, 5).map((file, i) => ( {file.action} {file.path.split("/").pop()} ))} {checkpoint.filesChanged.length > 5 && ( +{checkpoint.filesChanged.length - 5} more )}
)} {/* Stats */}
+{checkpoint.linesAdded} -{checkpoint.linesRemoved} {new Date(checkpoint.createdAt).toLocaleString()}
{/* Actions button */}
{/* Actions dropdown */} {showActions && (
)}
{/* Fork dialog */} {showForkDialog && (

Fork from Checkpoint #{checkpoint.checkpointNumber}

{error && (
{error}
)}
setForkName(e.target.value)} className="w-full font-mono text-xs text-[#9bc3ff] bg-[#0a1525] border border-[rgba(117,170,252,0.25)] px-3 py-2 focus:border-[#3f6fb3] focus:outline-none" />