import { ChevronRight } from "lucide-react" import { useState } from "react" import type { DType } from "@/client" import { Marquee } from "@/components/Common/Marquee" import { describeArtifact, isRef, useArtifactUrl } from "@/lib/media" import { cn } from "@/lib/utils" /** * How much of a structured value is worth unfolding in a side panel. * * The cap sits on the `pre` itself rather than on a `ScrollArea`: that one * sizes its viewport in percent, which resolves to the content's own height * against a box that has only a maximum, so a long value spills out of the * panel or popover and paints over whatever is below it. */ const MAX_HEIGHT = "max-h-48" function count(n: number, one: string, many = `${one}s`): string { return `${n} ${n === 1 ? one : many}` } /** * What a structured value *is*, in the space a value would have taken. * * The shape is what you want while wiring — that this port carries a record of * three fields, or a checkpoint of sixty kilobytes — and the contents are what * you want once something looks wrong. Serialising the whole thing into a line * answers the second question badly and the first one not at all. */ function summarize(value: object, dtype?: DType): string { if (Array.isArray(value)) { return `${dtype ?? "list"} · ${count(value.length, "item")}` } const record = value as Record if (isRef(record)) return describeArtifact(record) if (Array.isArray(record.lines)) { const points = record.lines.reduce( (total: number, line: unknown) => total + (Array.isArray((line as { points?: unknown[] })?.points) ? ((line as { points: unknown[] }).points.length as number) : 0), 0, ) return `series · ${count(record.lines.length, "line")}, ${count(points, "point")}` } return `${dtype ?? "record"} · ${count(Object.keys(record).length, "field")}` } /** * What a message is carrying: its shape at rest, its contents on request. * * A scalar simply reads, scrolling itself when it is longer than the room it * was given. Anything structured shows what it is and unfolds when asked — * which is what keeps a panel four hundred pixels wide from being pushed open * by one checkpoint reference. */ /** * The frame itself, where the value is one. * * A port carrying an image says `image/png · frame.png · 48kB`, which is the * right answer while wiring and the wrong one while pointing a camera. Drawn * small: this is a side panel, and the tile is where a frame is looked at. */ function Thumbnail({ value, name }: { value: object; name?: string }) { const url = useArtifactUrl(value, name) if (!url) return null return ( {String((value ) } export function ValuePreview({ value, name, dtype, defaultOpen = false, className, }: { value: unknown /** * The message this value is on. Only needed to draw a live frame: it is what * asks the engine to push the bytes for it. */ name?: string dtype?: DType /** Start unfolded, where there is room for it — an inspector, not a row. */ defaultOpen?: boolean className?: string }) { const [open, setOpen] = useState(defaultOpen) const structured = value !== null && typeof value === "object" const image = structured && isRef(value) && (value.media_type ?? "").startsWith("image/") if (!structured) { return ( ) } return (