Assistant
The flagship Gremorie chat surface — a streaming conversation with reasoning, an inline ChartArtifact and sources, a floating B2B PromptInput composer, and a new-chat start state. Fully composed.
Overview
The Assistant is the flagship Gremorie block: a complete, production-ready chat surface in a single import. It ships two surfaces driven by the header's chat switcher:
- Conversation (
filled) — a streaming log of messages: assistant reasoning, the response, an inlineChartArtifact(chart ⇄ table toggle with working downloads), per-response actions and Sources, response branches, and hover actions on the user's message. - New chat (
empty) — the start state: a centered welcome over the same composer, before a conversation exists.
Both share the floating B2B PromptInput composer — @-mentions for
context, a token-usage Context meter, mode and model selectors, web search,
attachments, voice, and a status-aware submit. The conversation scrolls behind
the composer and under the header, dissolving through soft fades; the scrollbar
only appears while scrolling.
The block ships a self-contained local mock so it runs end to end the moment you
install it — wire onSubmit to your AI SDK (or any streaming endpoint) to make
it real.
This is a block — a fully-composed starting point you own. gremorie add block-assistant copies the source into your app; you then edit it (swap the
mock data, wire your endpoint) rather than consuming a fixed prop API.
Preview
Conversation
Gremorie can make mistakes. Check important info.
New chat
How can I help you today?
States
A single initialView prop picks the starting surface, and the header's chat
switcher flips between them at runtime.
| State | How you reach it |
|---|---|
filled (default) | The live conversation. Reached on load, by sending a message, or by picking a recent chat in the header menu. |
empty | The new-chat start. Reached via New chat in the header menu, or by setting initialView="empty". |
// Start on the conversation (default)
<Assistant />
// Start on the new-chat screen
<Assistant initialView="empty" />Sending a message or clicking a quick suggestion from the empty state drops the user into the conversation; choosing a recent chat returns to it.
Anatomy
Assistant data-size'd card; flips between two views
├─ header thread switcher + actions (DropdownMenu · Button)
│ New chat → empty · recent chat → filled
├─ Conversation (filled) auto-scroll log; scrollbar reveals only while scrolling
│ └─ ConversationContent
│ ├─ Message (user)
│ │ ├─ MessageContent
│ │ └─ MessageToolbar hover actions: MessageActions (Copy · Edit)
│ └─ Message (assistant)
│ └─ MessageBranch
│ ├─ MessageBranchContent
│ │ ├─ Reasoning
│ │ ├─ MessageResponse
│ │ └─ ChartArtifact inline chart with chart/table toggle + downloads
│ └─ MessageToolbar MessageActions (left) · Sources + MessageBranchSelector (right)
├─ start (empty) centered welcome heading over the composer
└─ PromptInput floating B2B composer (see Prompt Input)
├─ PromptInputHeader PromptInputMentions + Context usage meter
├─ PromptInputBody → PromptInputTextarea
└─ PromptInputFooter mode/model selects · web · attach · voice · submitInstallation
npx gremorie@latest add block-assistantpnpm dlx gremorie@latest add block-assistantyarn dlx gremorie@latest add block-assistantbunx --bun gremorie@latest add block-assistantUsage
Render it directly — it works immediately on the bundled mock:
import { Assistant } from '@/components/assistant';
export default function Page() {
return <Assistant />;
}To make it real, replace the mock handleSubmit in the installed source with
your AI SDK call and feed the streamed parts into the conversation:
import { useChat } from '@ai-sdk/react';
// inside the block, swap the local mock for:
const { messages, sendMessage, status } = useChat();
const handleSubmit = (message: PromptInputMessage) => {
if (!message.text.trim()) return;
sendMessage({ text: message.text });
};
// then map `messages` onto <Message>/<MessageResponse>, drive <PromptInputSubmit
// status={status} />, and stream <Reasoning isStreaming> while it runs.API
The block is a composed template, so the surface you tweak is mostly its source. The one prop it exposes:
<Assistant>
| Prop | Type | Default | Description |
|---|---|---|---|
initialView | 'filled' | 'empty' | 'filled' | Which surface to render first — the live conversation or the new-chat start. |
export type AssistantView = 'filled' | 'empty';Everything else (header actions, the mock conversation, mode/model presets, context items) lives in the installed source and is meant to be edited.
Composition
The block composes (condensed — the full, runnable source installs via the CLI above):
<TooltipProvider>
{/* AssistantHeader — thread switcher + actions; New chat toggles the view */}
<Conversation>
<ConversationContent>
<Message from="user">…</Message>
<Message from="assistant">
<MessageBranch>
<MessageBranchContent>
<Reasoning />
<MessageResponse />
<ChartArtifact />{' '}
{/* inline chart: chart/table toggle + downloads */}
</MessageBranchContent>
<MessageToolbar>
{/* MessageActions (left) · Sources + branch selector (right) */}
</MessageToolbar>
</MessageBranch>
</Message>
</ConversationContent>
</Conversation>
{/* AssistantComposer — the floating B2B PromptInput (shared by both states) */}
<PromptInput>
<PromptInputHeader>
<PromptInputMentions />
<Context>
<ContextTrigger />
<ContextContent>{/* usage breakdown + cost */}</ContextContent>
</Context>
</PromptInputHeader>
<PromptInputBody>
<PromptInputTextarea />
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>{/* mode + model selects */}</PromptInputTools>
<PromptInputTools className="gap-2">
<PromptInputTools>{/* search · attach · voice */}</PromptInputTools>
<PromptInputSubmit />
</PromptInputTools>
</PromptInputFooter>
</PromptInput>
</TooltipProvider>Customization
- Wire the model — replace the mock
handleSubmitwith the AI SDK'ssendMessage(or your streaming endpoint) and drivePromptInputSubmit status={status}. - Stream reasoning — render
ReasoningwithisStreamingwhile the model thinks; it auto-collapses on completion. - Swap the artifact — the inline
ChartArtifactis one preset; drop in any artifact (code, table, document) insideMessageBranchContent. - Tune the composer — adjust the
PromptInputMentionsitems, the mode/model presets, and theContextusage meter. See Prompt Input. - Sources — the per-response Sources button sits in the action bar; point it at your retrieved citations.
- New-chat content — edit the welcome heading and quick suggestions in the
emptybranch. - Server-streamed markdown —
MessageResponse(Streamdown) already renders markdown; feed it the streamed text.
Accessibility
- The header switcher, response actions, and composer controls are real buttons
with labels/tooltips; the icon-only ones carry
aria-label/sr-onlytext. - The conversation is a
role="log"region (viaConversation) so assistive tech announces new messages as they stream. - Focus styles use the
ringtoken; the user-message hover actions also reveal on:focus-withinfor keyboard users. - Color comes entirely from semantic tokens, so contrast holds across the themes
and dark mode (the card declares
text-card-foreground).