import { isRef, useArtifactUrl } from "@/lib/media" import { cn } from "@/lib/utils" import { useBoundValue } from "./dataContext" import { config, text } from "./ui/core/config" import type { WidgetProps } from "./widgets" /** * What a message's bytes look like: a frame, a clip, a segment. * * Media never travels as a message — the reference does. The bytes come down * the socket in front of it where the engine is holding the frame in memory, * and are fetched from the artifact store otherwise; either way this tile only * asks for what it is drawing. A camera serving its own stream is still played * from source: point `stream_url` at it and the browser does the work. */ export function MediaWidget({ widget }: WidgetProps) { const cfg = config(widget) const message = text(cfg.message) const stream = text(cfg.stream_url) const live = useBoundValue(message || undefined) const value = live?.value const ref = isRef(value) ? value : null const url = useArtifactUrl(ref, message || undefined) const kind = (ref?.media_type ?? text(cfg.dtype)).split("/")[0] const fit = text(cfg.fit) === "contain" ? "object-contain" : "object-cover" const label = widget.title || ref?.name || message // A camera serving its own stream is played from source: the engine carries // references at whatever rate the flow publishes them, which is not video. if (stream && kind !== "audio") { return ( {label} ) } if (!message && !stream) { return

Pick a message.

} if (!ref) { return

Nothing published yet.

} if (!url) { return

Loading…

} if (kind === "audio") { return ( ) } if (kind === "video") { return ( ) } return ( {label} ) }