Demos / Your own UI · Halcyon
Halcyon
Your own UI: the UI-less client hands you the conversation as data, so every pixel is yours. In a sleep app, the agent’s breathing widget arrives as data and drives the app’s own orb.
Halcyon has no Mindset chat window. The page talks to its agent through the UI-less client and draws everything itself, including a breathing orb that becomes the agent's timer widget.
Start a conversation with no UI
Create a conversation with the agent's handle and a session callback, then listen to its events.
src/demos/shared/useDemoAgent.ts
const conv = mod.createAgentConversation({
agent: handle,
getSession: async () => {
…
unsub.current = conv.on((event) => {
dispatch({ type: 'event', event })
…A widget arrives as data
With no renderer, a widget call is just an event carrying the agent's arguments.
src/demos/shared/transcript.ts
case 'widget-payload': {
const kind = str(e.kind) || 'widget'
…Read the arguments
The page checks what the agent sent before using it.
src/demos/halcyon/breath.ts
const data = (item.doc as { data?: Record<string, unknown> } | null)?.data ?? {}
const minutes = toMinutes(data.minutes)
const pattern = toPattern(data.pattern)
if (minutes === null || pattern === null) return null
return { minutes, pattern }The orb becomes the timer
When a new timer arrives, the orb starts breathing to it. Nothing else changes the orb.
src/demos/halcyon/useBreathing.ts
const req = timerRequest(newest)
if (req) {
const startedAt = now()
setSession({ itemId: newest.id, pattern: PATTERNS[req.pattern], minutes: req.minutes, startedAt })
…
}