/* Copyright 2026 Marimo. All rights reserved. */ import { atom, useAtomValue, useSetAtom } from "jotai"; import { AlertCircleIcon, CheckCircle2Icon } from "lucide-react"; import type React from "react"; import { Spinner } from "@/components/icons/spinner"; import { Tooltip } from "@/components/ui/tooltip"; import { toast } from "@/components/ui/use-toast"; import { API } from "@/core/network/api"; import { connectionAtom } from "@/core/network/connection"; import type { LspHealthResponse, LspRestartRequest, LspRestartResponse, } from "@/core/network/types"; import { isWasm } from "@/core/wasm/utils"; import { isAppConnected } from "@/core/websocket/connection-utils"; import { useAsyncData } from "@/hooks/useAsyncData"; import { useInterval } from "@/hooks/useInterval"; const CHECK_LSP_HEALTH_INTERVAL_MS = 60_000; export const lspHealthAtom = atom(null); export const LspStatus: React.FC = () => { const connection = useAtomValue(connectionAtom).state; const setLspHealth = useSetAtom(lspHealthAtom); const { isFetching, data, refetch } = useAsyncData(async () => { if (isWasm() || !isAppConnected(connection)) { return null; } try { const health = await API.get("/lsp/health"); setLspHealth(health); return health; } catch { return null; } }, [connection]); useInterval(refetch, { delayMs: isAppConnected(connection) ? CHECK_LSP_HEALTH_INTERVAL_MS : null, whenVisible: true, }); const handleRestart = async () => { try { const result = await API.post( "/lsp/restart", {}, ); if (result.success) { toast({ title: "LSP Servers Restarted", description: result.restarted.length > 0 ? `Restarted: ${result.restarted.join(", ")}` : "No servers needed restart", }); } else { toast({ variant: "danger", title: "LSP Restart Failed", description: Object.entries(result.errors ?? {}) .map(([k, v]) => `${k}: ${v}`) .join("\n"), }); } // Refresh health status refetch(); } catch (error) { toast({ variant: "danger", title: "LSP Restart Failed", description: error instanceof Error ? error.message : "Unknown error", }); } }; // Don't show if no LSP servers are configured if (!data || data.servers.length === 0) { return null; } const getStatusIcon = () => { if (isFetching) { return ; } if (!data) { return ; } switch (data.status) { case "healthy": return ; case "degraded": return ; case "unhealthy": return ; } }; const getServerStatusDisplay = ( status: "starting" | "running" | "stopped" | "crashed" | "unresponsive", lastPingMs: number | null | undefined, ) => { switch (status) { case "running": return `✓ OK${lastPingMs == null ? "" : ` (${lastPingMs.toFixed(0)}ms)`}`; case "starting": return "⋯ Starting"; case "stopped": return "✗ Stopped"; case "crashed": return "✗ Crashed"; case "unresponsive": return "✗ Not responding"; } }; const getServerStatusColor = ( status: "starting" | "running" | "stopped" | "crashed" | "unresponsive", ) => { switch (status) { case "running": return "text-(--green-9)"; case "starting": return "text-(--yellow-11)"; default: return "text-(--red-9)"; } }; const tooltipContent = (
LSP Status
{data?.servers.map((server) => (
{server.serverId} {getServerStatusDisplay(server.status, server.lastPingMs)}
))}
{data?.status === "healthy" ? null : (
Click to restart failed servers
)}
); const handleClick = () => { if (data?.status !== "healthy") { void handleRestart(); } }; return ( ); };