KQ-2055 · WIDGET/THREAD

Widget Thread

An assistant whose replies are small live controls — a quantity stepper, a compare table, a slot picker, a confirm card — each assembling out of a skeleton block by block, every block rising a nudge on springs.snap in a cascade. Whatever is chosen inside a widget lifts out and becomes the next sent message: a copy of the chosen thing travels to the new bubble on springs.glide, its box morphing from the control to the bubble as its fill warms to the bubble colour, while the widget fades to a read-only record with the choice ticked on springs.flick. Each widget is one tab stop with its own keyboard path (radiogroup, spinbutton, roving table buttons, roving confirm buttons), so Tab leaves it for the composer, where typing an option's name answers it too.

KQ-2055 · WIDGET/THREAD

Book a room

Coldbrook Desk · replies you can use

  1. You:

    Book a room for the Gaugeworks review on Thursday.

  2. Coldbrook Desk:

    How many people are coming?

    8people
    2–16 · arrows change it, Enter sends
    Chosen: 8 people.
  3. You:

    8 people

  4. Coldbrook Desk:

    For 8 people, three rooms are free on Thursday. Here's how they compare.

    Rooms free on Thursday
    Fennel2nd floor
    Larch3rd floor
    Quarry3rd floor
    Seats
    8
    10
    14
    Screen
    55 in
    75 in
    Projector
    Whiteboard
    Wall
    Glass
    None
    Video
    Yes
    Yes
    No

1 of 4 chosen · comparing rooms

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/widget-thread

Props

turns
WidgetTurn[]
default defaultWidgetTurns
The assistant's script; each turn's text and widget may be functions of the choices so far.
request
string
default "Book a room for the Gaugeworks review on Thursday."
The visitor's opening message.
defaultValue
WidgetChoices
default {}
Choices already made; the thread replays them and opens at the first open widget.
onValueChange
(choices: WidgetChoices) => void
default —
Fires from the choice that changed them, with every choice so far.
onWidgetSubmit
(widgetId: string, choice: WidgetChoice, choices: WidgetChoices) => void
default —
A widget was used.
onSend
(text: string) => void
default —
Free text was sent from the composer.
onRetry
(widgetId: string | null) => void
default —
Refresh on an expired widget, or Try again after a failed load (null).
widgetChrome
"card" | "tinted" | "plain"
default "card"
Bordered widget cards, accent-washed panels, or controls set straight into the reply.
density
"compact" | "regular" | "roomy"
default "regular"
Type and spacing.
accent
string
default "var(--accent-bright)"
Your bubbles, chosen states and the flight — any CSS colour.
now
number
default defaultWidgetNow
The current time in ms; widgets with expiresIn expire against it.
typingDelay
number
default 520
Milliseconds the assistant types before a reply assembles.
assistant
string
default "Coldbrook Desk"
Who is answering.
title
string
default "Book a room"
The surface's heading and accessible name.
status
"ready" | "loading" | "error"
default "ready"
Whether the thread has loaded.
height
number
default 520
Height of the surface in px; the thread scrolls inside it.
disabled
boolean
default false
Read only: nothing can be chosen or sent.
className
string
default —
Classes for the surface.