import type { SessionEventsConnectionState } from "../../hooks/use-session-events"; import { AlertTriangleIcon, CopyIcon, InfoIcon, Loader2Icon, RefreshCwIcon } from "lucide-react"; import { lazy, Suspense, useEffect, useState, type ReactNode } from "react"; import { createPortal } from "react-dom"; import { toast } from "../lib/toast"; import { apiErrorTechnicalFacts, isPermissionDenied, userErrorTextWithoutReference, } from "../../ui/api-error"; import { usePortalContainer } from "../../ui/portal-container"; import { cn } from "../lib/utils"; const RECONNECT_PILL_REVEAL_DELAY_MS = 1_500; // Startup code: the disclosure lives with the management UI primitives, so it // loads only once a load error actually has details to show. const TechnicalDetails = lazy(() => import("../../ui/error-message").then((module) => ({ default: module.TechnicalDetails, })), ); type ConnectionPillMeta = { label: string; dot: string; text: string }; const degradedConnectionStates: Partial> = { connecting: { label: "Connecting…", dot: "bg-status-running", text: "text-status-running" }, reconnecting: { label: "Reconnecting…", dot: "bg-status-running", text: "text-status-running" }, error: { label: "Stream error", dot: "bg-status-failed", text: "text-status-failed" }, }; /** One quiet presentation across bootstrap, access, route and history gates. * Named operation panels retain their existing treatment. Gates still own * when content is authorized; this component never retains data. */ export function LoadingPanel({ label }: { label?: string | undefined }) { const portalContainer = usePortalContainer(); if (label) { return (
{label}
); } const indicator = ( node?.getAnimations?.().forEach((animation) => { // Bootstrap/route boundaries remount this indicator. Share the // document timeline so its rotation does not restart at each one. animation.startTime = 0; }) } className="size-4 animate-spin motion-reduce:animate-none" /> Loading… ); return (
{typeof document === "undefined" ? indicator : createPortal(indicator, portalContainer ?? document.body)}
); } export function ProblemPanel(props: { title: string; description: ReactNode; action?: ReactNode | undefined; }) { return (

{props.title}

{props.description}

{props.action ?
{props.action}
: null}
); } /** * Connection health, shown only when it needs a word (doctrine D2). Healthy * states (live / idle / ended) render nothing — the session status badge is the * single persistent pill, so there is no "live" + "Idle" double-green. The pill * surfaces only while the stream is degraded, in sentence case. Routine HTTP/1 * stream cycling never mounts the reconnect pill, so its hidden content cannot * shift the surrounding header controls. */ export function ConnectionPill({ state }: { state: SessionEventsConnectionState }) { const meta = degradedConnectionStates[state]; if (!meta) { return null; } if (state === "reconnecting") { return ; } return ; } function DelayedReconnectPill({ meta }: { meta: ConnectionPillMeta }) { const [visible, setVisible] = useState(false); useEffect(() => { const timer = setTimeout(() => setVisible(true), RECONNECT_PILL_REVEAL_DELAY_MS); return () => clearTimeout(timer); }, []); return visible ? : null; } function ConnectionPillContent({ meta }: { meta: ConnectionPillMeta }) { return ( {meta.label} ); } export function InspectorSection({ title, children }: { title: string; children: ReactNode }) { return (

{title}

{children}
); } export function InfoRow({ label, value }: { label: string; value: ReactNode }) { const renderedValue = typeof value === "string" || typeof value === "number" ? ( {value} ) : ( value ); return (
{label} {renderedValue}
); } export function CopyableMono({ value }: { value: string }) { return ( ); } /** Standard page header: icon, title, blurb, and trailing actions. */ export function PageHeader(props: { icon: ReactNode; title: string; description?: string | undefined; actions?: ReactNode | undefined; className?: string | undefined; }) { return (

{props.icon} {props.title}

{props.description ? (

{props.description}

) : null}
{props.actions ? (
{props.actions}
) : null}
); } export function EmptyState({ children }: { children: ReactNode }) { return (
{children}
); } /** * Honest failed-load state for list surfaces. Renders the error with a retry * affordance instead of letting routes fall through to "No X yet…" copy when * the request failed. Says what to do in product words; the raw status and * request reference stay behind Technical details. A permission refusal is * not a failure: it reads calmly, names who can help, and offers no retry. */ export function LoadErrorState({ title, error, onRetry, }: { title: string; error?: Error | null | undefined; onRetry: () => void; }) { const denied = error ? isPermissionDenied(error) : false; const facts = error ? apiErrorTechnicalFacts(error) : []; return (
{denied ? (
); }