import type { ReactNode } from "react"; import { Card, CardAction, CardContent, CardHeader, CardTitle } from "./components/card"; import { Skeleton } from "./components/skeleton"; import { ToggleGroup, ToggleGroupItem } from "./components/toggle-group"; import type { Loaded } from "./lib/api"; import { cn } from "./lib/cn"; export function Panel({ title, aside, className, children, }: { title?: string; aside?: ReactNode; className?: string; children: ReactNode; }) { return ( {title && ( {title} {aside && {aside}} )} {children} ); } export interface SegOption { value: T; label: string; } export function Seg({ options, value, onPick, label, }: { options: readonly SegOption[]; value: T; onPick: (next: T) => void; label: string; }) { return ( next && onPick(next as T)} > {options.map((option) => ( {option.label} ))} ); } /** * A screen whose frame stays put: the title and whatever toolbar it carries * hold their place, and only `children` scrolls. Children are handed a region * that is already the right height, so a table inside it scrolls its own rows * rather than the window. */ export function Screen({ title, aside, toolbar, children, }: { title: string; aside?: ReactNode; toolbar?: ReactNode; children: ReactNode; }) { return (

{title}

{aside}
{toolbar}
{children}
); } /** The scroll region itself — one per screen, and the only thing that moves. */ export function Scroller({ className, children, }: { className?: string; children: ReactNode; }) { return
{children}
; } export function Pending({ value }: { value: Loaded }) { if (value.state === "loading") return (
reading
); if (value.state === "error") return (
{value.message}
); return null; } export function Empty({ children }: { children: ReactNode }) { return
{children}
; }