import { useState } from "react";
import type { TaskCheckpoint } from "../../lib/api";
import { rewindTask, resumeSupervisor, rewindSupervisorConversation } from "../../lib/api";
interface ResumeControlsProps {
taskId: string;
contractId: string | null;
checkpoints: TaskCheckpoint[];
onActionComplete: () => void;
}
export function ResumeControls({
taskId,
contractId,
checkpoints,
onActionComplete,
}: ResumeControlsProps) {
const [showRewindDialog, setShowRewindDialog] = useState(false);
const [showSupervisorDialog, setShowSupervisorDialog] = useState(false);
const [selectedCheckpoint, setSelectedCheckpoint] = useState<string>("");
const [preserveMode, setPreserveMode] = useState<"discard" | "create_branch">("create_branch");
const [branchName, setBranchName] = useState("");
const [resumeMode, setResumeMode] = useState<"continue" | "restart_phase">("continue");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleRewindTask = async () => {
if (!selectedCheckpoint) {
setError("Select a checkpoint");
return;
}
setIsLoading(true);
setError(null);
try {
await rewindTask(taskId, {
checkpointId: selectedCheckpoint,
preserveMode,
branchName: preserveMode === "create_branch" ? branchName || undefined : undefined,
});
setShowRewindDialog(false);
onActionComplete();
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to rewind task");
} finally {
setIsLoading(false);
}
};
const handleResumeSupervisor = async () => {
if (!contractId) return;
setIsLoading(true);
setError(null);
try {
await resumeSupervisor(contractId, {
resumeMode,
});
setShowSupervisorDialog(false);
onActionComplete();
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to resume supervisor");
} finally {
setIsLoading(false);
}
};
const handleRewindConversation = async () => {
if (!contractId) return;
setIsLoading(true);
setError(null);
try {
await rewindSupervisorConversation(contractId, {
byMessageCount: 1, // Rewind by 1 message
});
onActionComplete();
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to rewind conversation");
} finally {
setIsLoading(false);
}
};
return (
<>
<div className="shrink-0 p-3 border-t border-[rgba(117,170,252,0.15)] bg-[rgba(0,0,0,0.2)] flex items-center gap-2">
{/* Task controls */}
{checkpoints.length > 0 && (
<button
onClick={() => setShowRewindDialog(true)}
className="px-3 py-1.5 font-mono text-[10px] uppercase text-yellow-400 border border-yellow-400/30 hover:bg-yellow-400/10 transition-colors"
>
Rewind Code
</button>
)}
{/* Supervisor controls */}
{contractId && (
<>
<button
onClick={() => setShowSupervisorDialog(true)}
className="px-3 py-1.5 font-mono text-[10px] uppercase text-cyan-400 border border-cyan-400/30 hover:bg-cyan-400/10 transition-colors"
>
Resume Supervisor
</button>
<button
onClick={handleRewindConversation}
disabled={isLoading}
className="px-3 py-1.5 font-mono text-[10px] uppercase text-orange-400 border border-orange-400/30 hover:bg-orange-400/10 transition-colors disabled:opacity-50"
>
Undo Last Message
</button>
</>
)}
</div>
{/* Rewind Task Dialog */}
{showRewindDialog && (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50">
<div className="bg-[#0d1b2d] border border-[rgba(117,170,252,0.35)] max-w-lg w-full mx-4">
<div className="p-4 border-b border-[rgba(117,170,252,0.15)] flex justify-between items-center">
<h2 className="font-mono text-sm uppercase tracking-wide text-[#9bc3ff]">
Rewind Task Code
</h2>
<button
onClick={() => setShowRewindDialog(false)}
className="text-[#7788aa] hover:text-[#9bc3ff] transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="p-4 space-y-4">
{error && (
<div className="p-2 bg-red-400/10 border border-red-400/30 font-mono text-xs text-red-400">
{error}
</div>
)}
<div>
<label className="block font-mono text-[10px] text-[#7788aa] uppercase mb-1">
Checkpoint
</label>
<select
value={selectedCheckpoint}
onChange={(e) => setSelectedCheckpoint(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"
>
<option value="">Select checkpoint...</option>
{checkpoints.map((cp) => (
<option key={cp.id} value={cp.id}>
#{cp.checkpointNumber} - {cp.message || cp.commitSha.slice(0, 7)}
</option>
))}
</select>
</div>
<div>
<label className="block font-mono text-[10px] text-[#7788aa] uppercase mb-1">
Preserve Current Code
</label>
<div className="flex gap-4">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="preserveMode"
checked={preserveMode === "create_branch"}
onChange={() => setPreserveMode("create_branch")}
className="text-[#3f6fb3]"
/>
<span className="font-mono text-xs text-[#9bc3ff]">Create branch</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="preserveMode"
checked={preserveMode === "discard"}
onChange={() => setPreserveMode("discard")}
className="text-[#3f6fb3]"
/>
<span className="font-mono text-xs text-[#9bc3ff]">Discard</span>
</label>
</div>
</div>
{preserveMode === "create_branch" && (
<div>
<label className="block font-mono text-[10px] text-[#7788aa] uppercase mb-1">
Branch Name (optional)
</label>
<input
type="text"
value={branchName}
onChange={(e) => setBranchName(e.target.value)}
placeholder="Auto-generated if empty"
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"
/>
</div>
)}
<div className="flex justify-end gap-2">
<button
onClick={() => setShowRewindDialog(false)}
className="px-4 py-2 font-mono text-xs uppercase text-[#7788aa] border border-[rgba(117,170,252,0.25)] hover:text-[#9bc3ff] transition-colors"
>
Cancel
</button>
<button
onClick={handleRewindTask}
disabled={isLoading || !selectedCheckpoint}
className="px-4 py-2 font-mono text-xs uppercase text-white bg-yellow-600 border border-yellow-500 hover:bg-yellow-500 transition-colors disabled:opacity-50"
>
{isLoading ? "Rewinding..." : "Rewind"}
</button>
</div>
</div>
</div>
</div>
)}
{/* Resume Supervisor Dialog */}
{showSupervisorDialog && (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50">
<div className="bg-[#0d1b2d] border border-[rgba(117,170,252,0.35)] max-w-lg w-full mx-4">
<div className="p-4 border-b border-[rgba(117,170,252,0.15)] flex justify-between items-center">
<h2 className="font-mono text-sm uppercase tracking-wide text-[#9bc3ff]">
Resume Supervisor
</h2>
<button
onClick={() => setShowSupervisorDialog(false)}
className="text-[#7788aa] hover:text-[#9bc3ff] transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="p-4 space-y-4">
{error && (
<div className="p-2 bg-red-400/10 border border-red-400/30 font-mono text-xs text-red-400">
{error}
</div>
)}
<div>
<label className="block font-mono text-[10px] text-[#7788aa] uppercase mb-2">
Resume Mode
</label>
<div className="space-y-2">
<label className="flex items-start gap-2 cursor-pointer p-2 border border-[rgba(117,170,252,0.15)] hover:border-[rgba(117,170,252,0.25)] transition-colors">
<input
type="radio"
name="resumeMode"
checked={resumeMode === "continue"}
onChange={() => setResumeMode("continue")}
className="mt-0.5 text-[#3f6fb3]"
/>
<div>
<span className="font-mono text-xs text-[#9bc3ff]">Continue</span>
<p className="font-mono text-[10px] text-[#7788aa] mt-0.5">
Resume with existing conversation context
</p>
</div>
</label>
<label className="flex items-start gap-2 cursor-pointer p-2 border border-[rgba(117,170,252,0.15)] hover:border-[rgba(117,170,252,0.25)] transition-colors">
<input
type="radio"
name="resumeMode"
checked={resumeMode === "restart_phase"}
onChange={() => setResumeMode("restart_phase")}
className="mt-0.5 text-[#3f6fb3]"
/>
<div>
<span className="font-mono text-xs text-[#9bc3ff]">Restart Phase</span>
<p className="font-mono text-[10px] text-[#7788aa] mt-0.5">
Clear conversation but keep phase progress
</p>
</div>
</label>
</div>
</div>
<div className="flex justify-end gap-2">
<button
onClick={() => setShowSupervisorDialog(false)}
className="px-4 py-2 font-mono text-xs uppercase text-[#7788aa] border border-[rgba(117,170,252,0.25)] hover:text-[#9bc3ff] transition-colors"
>
Cancel
</button>
<button
onClick={handleResumeSupervisor}
disabled={isLoading}
className="px-4 py-2 font-mono text-xs uppercase text-white bg-cyan-600 border border-cyan-500 hover:bg-cyan-500 transition-colors disabled:opacity-50"
>
{isLoading ? "Resuming..." : "Resume"}
</button>
</div>
</div>
</div>
</div>
)}
</>
);
}