import { useEffect, useState } from "react"; import type { ClaudeSession, Project, ScheduledTask } from "../../../lib/types"; import { listClaudeSessions, listScheduledTasks, getSchedulerNotifications, resumeSessionCommand, } from "../../../lib/tauri-commands"; import type { useProjectActions } from "../../../hooks/useProjectActions"; import type { SaveState } from "../../../hooks/useSaveState"; import PermissionModeControl, { permissionModePatch, } from "../PermissionModeControl"; import CapabilityTiles from "./CapabilityTiles"; import ContainerMigrationBanner from "./ContainerMigrationBanner"; import type { ContainerMigration } from "../../../hooks/useContainerMigration"; import SaveIndicator from "../../ui/SaveIndicator"; import Button from "../../ui/Button"; import { formatAge } from "./format"; import type { ProjectHomeTabId } from "./ProjectHome"; const BACKEND_LABEL: Record = { anthropic: "Anthropic", bedrock: "AWS Bedrock", ollama: "Ollama", llama_cpp: "llama.cpp", open_ai_compatible: "OpenAI Compatible", }; interface Props { project: Project; save: (patch: Partial) => Promise; saveState: SaveState; actions: ReturnType; onOpenTab: (tab: ProjectHomeTabId) => void; /** Base-image staleness, run state and report. Owned by `ProjectHome`. */ migration: ContainerMigration; /** Migration mirrors Reset's gate: only offered on a stopped container. */ canMigrate: boolean; onOpenMigration: () => void; } export default function OverviewTab({ project, save, saveState, actions, onOpenTab, migration, canMigrate, onOpenMigration, }: Props) { const [sessions, setSessions] = useState([]); const [tasks, setTasks] = useState([]); const [notificationCount, setNotificationCount] = useState(0); const running = project.status === "running"; useEffect(() => { if (!running) { setSessions([]); setTasks([]); setNotificationCount(0); return; } let cancelled = false; // All three degrade to empty when the container is unreachable. listClaudeSessions(project.id) .then((s) => !cancelled && setSessions(s.slice(0, 4))) .catch(() => !cancelled && setSessions([])); listScheduledTasks(project.id) .then((t) => !cancelled && setTasks(t)) .catch(() => !cancelled && setTasks([])); getSchedulerNotifications(project.id) .then((n) => !cancelled && setNotificationCount(n.length)) .catch(() => !cancelled && setNotificationCount(0)); return () => { cancelled = true; }; }, [project.id, running, project.container_id]); const handleResume = async (session: ClaudeSession) => { try { const command = await resumeSessionCommand(project.id, session.id); await actions.openTerminalWithCommand(command, session.name ?? "resume"); } catch (e) { console.error("Failed to build the resume command:", e); } }; return (
{/* Permission mode — the hero control */}
save(permissionModePatch(mode))} />
Backend{" "} {BACKEND_LABEL[project.backend]} Docker access{" "} {project.allow_docker_access ? "ON" : "OFF"} Mission Control{" "} {project.mission_control_enabled ? "ON" : "OFF"}
{/* A container missing socat and bwrap is a capability statement, so the out-of-date warning sits directly above the capability inventory. */} actions.openTerminalWithCommand(command)} />
{/* Recent sessions */}

Recent sessions

{!running ? (

Start the container to list saved conversations.

) : sessions.length === 0 ? (

No sessions yet.

) : (
    {sessions.map((session) => (
  • {session.name ?? session.summary ?? session.id} {formatAge(session.last_modified) ?? ""}
  • ))}
)}
{/* Scheduled tasks */}

Scheduled tasks

{!running ? (

Start the container to list scheduled tasks.

) : tasks.length === 0 ? (

No scheduled tasks.

) : (
    {tasks.slice(0, 4).map((task) => (
  • {task.name} {task.at ?? task.schedule}
  • ))}
)} {notificationCount > 0 && ( )}
); }