import { ActionIcon, Alert, Box, Button, Divider, Drawer, Group, Menu, Modal, ScrollArea, Stack, Text, Title, Tooltip, } from "@mantine/core"; import { useMediaQuery } from "@mantine/hooks"; import { IconCheck, IconClipboard, IconDots, IconX, } from "@tabler/icons-react"; import { useEffect, useRef, useState, type ReactNode } from "react"; import type { Task } from "../types"; import { ActivityTimeline } from "./ActivityTimeline"; import { GitHubLinks } from "./GitHubLinks"; import { OpenChatButton } from "./OpenChatButton"; import { RelativeTime } from "./RelativeTime"; import { StatusBadge } from "./StatusBadge"; import { UsagePanel } from "./UsagePanel"; export type TerminalStatus = "completed" | "failed"; export interface ManualTransitionResult { ok: boolean; rotateActionId?: boolean; } export function TaskDetail({ busy, error, highlightTurnRef, onClose, onCopy, onOpenCodex, onTransition, opened, task, notifications, notice, }: { busy: boolean; error: string | null; highlightTurnRef: string | null; onClose: () => void; onCopy: () => void; onOpenCodex: () => void; onTransition: (status: TerminalStatus, actionId: string) => Promise; opened: boolean; task: Task | null; notifications?: ReactNode; notice?: string | null; }) { const mobile = useMediaQuery("(max-width: 48em)"); const [confirmStatus, setConfirmStatus] = useState(null); useEffect(() => { if (!opened) setConfirmStatus(null); }, [opened]); useEffect(() => { if (opened && highlightTurnRef) { requestAnimationFrame(() => { document.querySelector(`[data-turn-ref="${CSS.escape(highlightTurnRef)}"]`)?.focus(); }); } }, [highlightTurnRef, opened, task?.turns?.length, task?.updatedAt]); useEffect(() => { if (confirmStatus && (!task || !manualTransitionTargets(task).includes(confirmStatus))) { setConfirmStatus(null); } }, [confirmStatus, task?.status]); if (!task) return null; const transitionTargets = manualTransitionTargets(task); const content = ( {notifications} {task.project.name} {task.title} } onClick={onCopy}>Copy Task ID {transitionTargets.includes("completed") && ( } onClick={() => setConfirmStatus("completed")}>Mark completed )} {transitionTargets.includes("failed") && ( } onClick={() => setConfirmStatus("failed")}>Mark failed )} {confirmStatus && ( setConfirmStatus(null)} onTransition={onTransition} status={confirmStatus} /> )} {notice && {notice}} {error && {error}}
Usage
Activity timeline
Original instruction {task.instruction}
Metadata
Task ID
{task.id}
Thread ID
{task.threadId ?? "—"}
Current turn ref
{task.turnRef ?? "—"}
Project path
{task.project.path}
Created
Updated
Updated by
{task.updatedBy ?? "—"}
); if (mobile) { return ( Task details} trapFocus zIndex={300} > {content} ); } return ( Task details} trapFocus zIndex={300} > {content} ); } function manualTransitionTargets(task: Task): TerminalStatus[] { return task.status === "completed" ? ["failed"] : task.status === "failed" ? ["completed"] : task.status === "working" || task.status === "needs_input" ? ["completed", "failed"] : []; } export function ManualTransitionConfirmation({ busy, onCancel, onTransition, status, }: { busy: boolean; onCancel: () => void; onTransition: (status: TerminalStatus, actionId: string) => Promise; status: TerminalStatus; }) { const actionId = useRef(crypto.randomUUID()); return ( This interrupts active work and appends an audited manual dashboard turn. ); }