63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
import * as Dialog from "@radix-ui/react-dialog";
|
|
import { Loader2 } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { Button } from "./ui/button";
|
|
import { spawnOrchestrator } from "../lib/spawn-orchestrator";
|
|
import { isOrchestratorSession } from "../types/workspace";
|
|
import type { WorkspaceSession } from "../types/workspace";
|
|
|
|
type RestoreUnavailableDialogProps = {
|
|
open: boolean;
|
|
session: WorkspaceSession;
|
|
onOpenChange: (open: boolean) => void;
|
|
onRecreated: (newOrchestratorId: string) => void;
|
|
};
|
|
|
|
export function RestoreUnavailableDialog({ open, session, onOpenChange, onRecreated }: RestoreUnavailableDialogProps) {
|
|
const [busy, setBusy] = useState(false);
|
|
const [error, setError] = useState<string | undefined>();
|
|
const orchestrator = isOrchestratorSession(session);
|
|
|
|
const recreate = async () => {
|
|
setBusy(true);
|
|
setError(undefined);
|
|
try {
|
|
const id = await spawnOrchestrator(session.workspaceId, "restore_dialog", true);
|
|
onOpenChange(false);
|
|
onRecreated(id);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Failed to create orchestrator");
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog.Root open={open} onOpenChange={onOpenChange}>
|
|
<Dialog.Portal>
|
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/50" />
|
|
<Dialog.Content className="fixed left-1/2 top-1/2 z-50 w-[420px] -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-surface p-5 shadow-lg">
|
|
<Dialog.Title className="text-sm font-medium text-foreground">Session can no longer be restored</Dialog.Title>
|
|
<Dialog.Description className="mt-2 text-[13px] text-muted-foreground">
|
|
{orchestrator
|
|
? "This orchestrator has no saved agent session to resume. You can create a new orchestrator on the same branch; its committed work is preserved and the old worktree is cleaned."
|
|
: "This session has no saved agent session or prompt to resume from."}
|
|
</Dialog.Description>
|
|
{error && <div className="mt-3 text-[12px] text-destructive">{error}</div>}
|
|
<div className="mt-4 flex justify-end gap-2">
|
|
<Button variant="ghost" onClick={() => onOpenChange(false)} disabled={busy}>
|
|
{orchestrator ? "Cancel" : "Close"}
|
|
</Button>
|
|
{orchestrator && (
|
|
<Button onClick={recreate} disabled={busy}>
|
|
{busy && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
Create new orchestrator
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</Dialog.Content>
|
|
</Dialog.Portal>
|
|
</Dialog.Root>
|
|
);
|
|
}
|