A complete AI chat with streaming replies, a composer that stops generation and follow-scroll.
A complete AI chat: replies stream from your AsyncStream with a smoothing reveal, a composer whose send button morphs to stop, suggestion blocks under a display headline on an empty thread, message actions and follow-scroll with a Latest pill.
$199 once, every piece included. See how installing works · Sign in
SwiftUI source you own, built on Apple frameworks only.
AsyncStream with a smoothing revealAI Assistant (brief), AI Assistant (recipe). Your agent builds around this screen in the same design.
AI Assistant (template)
How it behaves, what it handles, and the decisions behind it.
Hand the screen your thread and an onSend that returns an AsyncStream<String>; it appends the user message and an assistant reply, moves the reply through thinking, streaming and done (or error), and reveals tokens at an even cadence that speeds up with the backlog, so bursty providers still read as one smooth stream. The composer grows with the draft and its send button morphs into stop while generating; stop cancels the task and keeps what arrived. An empty thread shows suggestion chips and a quiet prompt; long-pressing a message offers copy, retry and delete. The list follows new tokens until the user scrolls up, then a "Latest" pill offers the way back.
thinking (breathing dots), streaming (text with the smoothing reveal), done, error(String) (inline card with a retry that re-runs the preceding prompt into the same row)..error when a reply fails. Reduce Motion snaps text in without the reveal and replaces springs with ease-outs. Reduce Transparency swaps the glass composer for an opaque surface.messages yourself; the screen only mutates the binding. The stream is cancelled if the screen disappears.AIChatScreenExample at the end of AIChatScreen.swift is the screen shown in the preview; copy it as a starting point.SPTheme from @Environment(\.spTheme), so install the design-system item with it. Set .environment(\.spTheme, YourTheme()) once at the root.tint defaults to color.accent and colors the user bubble, send button, thinking dots and empty-state glyph; bubble text and the send glyph use color.accentOn.color.bg, the composer uses spGlass, chips and the idle send button use color.fill, and the error card uses color.warning.type.body, type.subhead, type.footnote and type.title3; bubbles and the composer take radius.lg, the error card radius.md.Pieces that work well next to this one.