When a refine agent crashes, the Retry dialog now extracts the user_instruction from the agent's stored prompt and pre-fills the textarea, so users can re-run with the same instruction without retyping it.
135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
import { useState } from "react";
|
|
import { Sparkles } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
} from "@/components/ui/dialog";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
|
|
interface RefineSpawnDialogProps {
|
|
/** Button text to show in the trigger */
|
|
triggerText: string;
|
|
/** Dialog title */
|
|
title: string;
|
|
/** Dialog description */
|
|
description: string;
|
|
/** Whether to show the instruction textarea */
|
|
showInstructionInput?: boolean;
|
|
/** Placeholder text for the instruction textarea */
|
|
instructionPlaceholder?: string;
|
|
/** Pre-populate the instruction field (e.g. from a crashed agent's original instruction) */
|
|
defaultInstruction?: string;
|
|
/** Whether the spawn mutation is pending */
|
|
isSpawning: boolean;
|
|
/** Error message if spawn failed */
|
|
error?: string;
|
|
/** Called when the user wants to spawn */
|
|
onSpawn: (instruction?: string) => void;
|
|
/** Custom trigger button (optional) */
|
|
trigger?: React.ReactNode;
|
|
}
|
|
|
|
export function RefineSpawnDialog({
|
|
triggerText,
|
|
title,
|
|
description,
|
|
showInstructionInput = true,
|
|
instructionPlaceholder = "What should the agent focus on? (optional)",
|
|
defaultInstruction,
|
|
isSpawning,
|
|
error,
|
|
onSpawn,
|
|
trigger,
|
|
}: RefineSpawnDialogProps) {
|
|
const [showDialog, setShowDialog] = useState(false);
|
|
const [instruction, setInstruction] = useState("");
|
|
|
|
const handleSpawn = () => {
|
|
const finalInstruction = showInstructionInput && instruction.trim()
|
|
? instruction.trim()
|
|
: undefined;
|
|
onSpawn(finalInstruction);
|
|
};
|
|
|
|
const openDialog = () => {
|
|
setInstruction(defaultInstruction ?? "");
|
|
setShowDialog(true);
|
|
};
|
|
|
|
const handleOpenChange = (open: boolean) => {
|
|
if (open) {
|
|
setInstruction(defaultInstruction ?? "");
|
|
} else {
|
|
setInstruction("");
|
|
}
|
|
setShowDialog(open);
|
|
};
|
|
|
|
const defaultTrigger = (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={openDialog}
|
|
className="gap-1.5"
|
|
>
|
|
<Sparkles className="h-3.5 w-3.5" />
|
|
{triggerText}
|
|
</Button>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{trigger ? (
|
|
<div onClick={openDialog}>
|
|
{trigger}
|
|
</div>
|
|
) : (
|
|
defaultTrigger
|
|
)}
|
|
|
|
<Dialog open={showDialog} onOpenChange={handleOpenChange}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>{title}</DialogTitle>
|
|
<DialogDescription>{description}</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
{showInstructionInput && (
|
|
<Textarea
|
|
placeholder={instructionPlaceholder}
|
|
value={instruction}
|
|
onChange={(e) => setInstruction(e.target.value)}
|
|
rows={3}
|
|
/>
|
|
)}
|
|
|
|
<DialogFooter>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setShowDialog(false)}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={handleSpawn}
|
|
disabled={isSpawning}
|
|
>
|
|
{isSpawning ? "Starting..." : "Start"}
|
|
</Button>
|
|
</DialogFooter>
|
|
|
|
{error && (
|
|
<p className="text-xs text-destructive">
|
|
{error}
|
|
</p>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
} |