import { cn } from "../lib/cn"; import { type ConnectionStatus, connectionStatusForState, type MachineState, } from "../types/machines"; import { useOpenGeniText, type OpenGeniText } from "../messages/runtime"; export type ConnectionStatusMeta = { label: string; dotClassName: string; badgeClassName: string; /** Live/transient states breathe; settled states hold still. */ pulse: boolean; }; /** Token-backed meta for the three connection-status pill values. */ export const CONNECTION_STATUS_META = ( t: OpenGeniText, ): Record => ({ online: { label: t("machineStatusPill.online"), dotClassName: "bg-og-status-running", badgeClassName: "text-og-status-running border-og-status-running/30 bg-og-status-running/10", pulse: false, }, reconnecting: { label: t("machineStatusPill.reconnecting"), dotClassName: "bg-og-status-waiting", badgeClassName: "text-og-status-waiting border-og-status-waiting/35 bg-og-status-waiting/10", pulse: true, }, offline: { label: t("machineStatusPill.offline"), dotClassName: "bg-og-status-failed", badgeClassName: "text-og-fg-subtle border-og-border bg-og-status-failed/10", pulse: false, }, }); export type MachineStateBadgeMeta = { label: string; badgeClassName: string; }; /** * The non-connection state badges that ride ALONGSIDE the connection pill — * `consent_required` / `display_unavailable` / `enrolling` describe a capability * limitation, not reachability, so they render as their own tinted chip. */ export const MACHINE_STATE_BADGE_META = ( t: OpenGeniText, ): Partial> => ({ consent_required: { label: t("machineStatusPill.consentRequired"), badgeClassName: "text-og-status-waiting border-og-status-waiting/35 bg-og-status-waiting/10", }, display_unavailable: { label: t("machineStatusPill.noDisplay"), badgeClassName: "text-og-fg-muted border-og-border bg-og-surface-2", }, enrolling: { label: t("machineStatusPill.enrolling"), badgeClassName: "text-og-accent border-og-accent/30 bg-og-accent-soft", }, }); export type ConnectionStatusPillProps = { status: ConnectionStatus; /** Override the label ("Online" -> "Connected", ...). */ label?: string | undefined; size?: "sm" | "md" | undefined; className?: string | undefined; }; /** * The connection-status pill (online / reconnecting / offline) surfaced across * the Machines dashboard, the dock header, and the session timeline. Reconnecting * breathes (the resiliency-as-headline blip), online/offline hold still. */ export function ConnectionStatusPill({ status, label, size = "md", className, }: ConnectionStatusPillProps) { const t = useOpenGeniText(); const meta = CONNECTION_STATUS_META(t)[status]; return ( {label ?? meta.label} ); } export type ConnectionDotProps = { status: ConnectionStatus; className?: string | undefined; }; /** Just the dot — for dense rows, the dock header, and timeline notices. */ export function ConnectionDot({ status, className }: ConnectionDotProps) { const t = useOpenGeniText(); const meta = CONNECTION_STATUS_META(t)[status]; return ( {meta.pulse ? ( ) : null} ); } export type MachineStatusPillProps = { /** The machine's full state — drives the connection pill + any state badge. */ state: MachineState; /** How many live sessions share this lease (renders a "Shared" chip when >1). */ sharedSessionCount?: number | undefined; size?: "sm" | "md" | undefined; className?: string | undefined; }; /** * The composite machine status surface: the connection pill PLUS any * consent/display/enrolling badge PLUS a shared-in-use chip. One component the * dashboard row, the dock header, and the timeline all reuse so the status * language is identical everywhere. */ export function MachineStatusPill({ state, sharedSessionCount, size = "md", className, }: MachineStatusPillProps) { const t = useOpenGeniText(); const stateBadge = MACHINE_STATE_BADGE_META(t)[state]; const shared = (sharedSessionCount ?? 0) > 1; return ( {stateBadge ? ( {stateBadge.label} ) : null} {shared ? ( {t("machineStatusPill.sharedCount", { count: sharedSessionCount ?? 0 })} ) : null} ); }