import { useLiveValue } from "@/components/Flow/liveStore" import { cn } from "@/lib/utils" import { ICON_COLORS, ICONS } from "./icons" import { config } from "./ui/core/config" import type { WidgetProps } from "./widgets" /** One column of a forecast, as the `list` message declares it. */ type ForecastItem = { label?: string icon?: string value?: string | number color?: string } /** * What comes next, as a strip of columns over a `list` message. * * The shape is the widget's contract rather than a path per column: every item * is `{label, icon, value}` with an optional `color`, naming its icon from the * same vocabulary the icon widget offers, so a flow answering with a forecast * decides what a step is called and this only has to draw it. Later columns are * dimmed progressively — a forecast is less certain the further out it reads. */ export function ForecastWidget({ widget }: WidgetProps) { const cfg = config(widget) const message = cfg.message ? String(cfg.message) : "" const live = useLiveValue(message || undefined) if (!message) { return

Pick a message.

} const count = Number(cfg.count) const items: ForecastItem[] = (Array.isArray(live?.value) ? live.value : []) .filter((item): item is ForecastItem => !!item && typeof item === "object") .slice(0, Number.isFinite(count) ? count : 5) if (items.length === 0) { return

Nothing forecast.

} return (
{items.map((item, index) => { const Glyph = ICONS[item.icon ?? ""] ?? null return (
{item.label ?? ""} {Glyph ? ( ) : null} {item.value === null || item.value === undefined ? "" : String(item.value)}
) })}
) }