import { useEffect, useState } from "react"; import { get, post } from "../api"; import { useT } from "../i18n"; import { confirmDialog, toast } from "../dialogs"; import { Icon } from "./Icon"; import { Spinner } from "./Activity"; import type { Session } from "../types"; interface WtStatus { worktree: { repo: string; path: string; branch: string; base: string } | null; dirty_files?: number; commits?: string[]; diffstat?: string; } /** Chip + panel for a session working in an isolated git worktree. */ export function WorktreeChip({ session, busy }: { session: Session; busy: boolean }) { const t = useT(); const wt = session.worktree as WtStatus["worktree"] | undefined; const [open, setOpen] = useState(false); const [status, setStatus] = useState(); const [working, setWorking] = useState(); useEffect(() => { if (!open) return; get(`/sessions/${session.id}/worktree`) .then(setStatus) .catch((e) => toast(e.message)); }, [open, session.id, busy]); if (!wt) return null; const act = async (action: "merge" | "remove") => { const msg = action === "merge" ? t("worktree.mergeConfirm", { branch: wt.branch, base: wt.base }) : t("worktree.removeConfirm", { branch: wt.branch }); if (!(await confirmDialog({ message: msg, danger: action === "remove" }))) return; setWorking(action); try { await post(`/sessions/${session.id}/worktree`, { action }); toast(t(action === "merge" ? "worktree.merged" : "worktree.removed", { base: wt.base }), "success"); if (action === "merge") setStatus(await get(`/sessions/${session.id}/worktree`)); else setOpen(false); } catch (e: any) { toast(e.message); } finally { setWorking(undefined); } }; return ( {open && (
{t("worktree.hint", { base: wt.base })}
{!status ? ( ) : ( <>
{t("worktree.dirty", { n: status.dirty_files ?? 0 })} {t("worktree.commits", { n: status.commits?.length ?? 0 })} {status.diffstat && {status.diffstat}}
{!!status.commits?.length && (
{status.commits.slice(0, 8).join("\n")}
)} )}
{busy &&
{t("worktree.busy")}
}
)}
); }