Components
Conversational UI
The canonical AI conversation surface for Distyl products. Composed from ChatShell, MessageBubble, ChatInput, and LoadingBubble — each independently usable. Not a modal or a drawer. A panel or inline surface that supports a full turn-based AI exchange.
Distyl-specific. This component has no external library counterpart or design-tool source yet. It is the canonical pattern for AI chat surfaces across Distyl products — Platform, Tower, and future implementations.
Preview
This preview is live — it's running Meno, Cognition's built-in assistant. Ask it anything about tokens, components, or system rules.
Ask Meno anything about Cognition
Tokens, components, and the rules that hold the system together.
Enter to send · Shift+Enter for newline
Variants
Ask Meno anything about Cognition
Tokens, components, and the rules that hold the system together.
Enter to send · Shift+Enter for newline
No props. Uses /api/chat with the default system prompt.
States
Empty
Ask Meno anything about Cognition
Tokens, components, and the rules that hold the system together.
Loading
You
Meno
Error
You
Meno
The error state is a danger-styled MessageBubble (the error prop) — alert icon, danger border, and danger surface. The message string is set by the catch block in ChatShell.
API
Sub-components
ChatInput
Auto-resizing message input with keyboard shortcuts. Composes Textarea and Button.
Enter to send · Shift+Enter for newline
import { ChatInput } from "@/components/ConversationalUI";
export function MyInput() {
return (
<ChatInput
onSend={(message) => console.log(message)}
placeholder="Ask something…"
/>
);
}MessageBubble
Renders a single conversation turn. User turns are plain text. Assistant turns parse markdown and fenced code blocks.
You
Meno
There are three primary risk signals:
First, the counterparty exposure in the derivatives book exceeds the 15% threshold set in the risk policy.
Second, two entities flagged as high-risk are connected to the primary party via undisclosed intermediaries.
Meno
Use the riskScore field on the Entity type to filter above threshold.
Meno
Here's a query to pull flagged entities:
const flagged = entities.filter(
(e) => e.riskScore > 0.7
);Meno
import { MessageBubble } from "@/components/ConversationalUI";
// User turn
<MessageBubble role="user" content="What are the risk signals?" />
// Assistant turn — parses markdown and code blocks automatically
<MessageBubble
role="assistant"
content="Here's the query:\n\n```typescript\nentities.filter(e => e.riskScore > 0.7)\n```"
/>LoadingBubble
Assistant thinking state. Uses the Distyl Spinner. No props — render it while awaiting a response.
Meno
No props. Render LoadingBubble while loading === true and remove it when the reply arrives.
import { LoadingBubble } from "@/components/ConversationalUI";
// Render while awaiting response
{loading && <LoadingBubble />}EmptyState
Zero-message state with prompt chips. Shown when the message list is empty. Chips call onChipClick with the chip text.
Ask Meno anything about Cognition
Tokens, components, and the rules that hold the system together.
import { EmptyState } from "@/components/ConversationalUI";
<EmptyState
onChipClick={(text) => handleSend(text)}
/>Don't and Do
Don't render ChatShell inside a Dialog or Sheet. It is a panel or inline surface — it does not block the canvas behind it. Don't hardcode a system prompt in the call site — pass it as the systemPrompt prop so each context owns its own behaviour.
// Panel — does not block the canvas
<div className="w-96 border-l border-border-default">
<ChatShell
systemPrompt="You are analyzing Tower pipeline data."
placeholder="Ask about this pipeline…"
/>
</div>
// With custom API layer (Tower, Platform)
<ChatShell
onSend={async (messages, userMessage) => {
const res = await myProductApi.chat(messages);
return res.reply;
}}
/>import { ChatShell } from "@/components/ConversationalUI";
export function ContextPanel() {
return (
<div className="flex h-full flex-col border-l border-border-default">
<ChatShell
systemPrompt="You are analyzing Distyl pipeline data."
placeholder="Ask about this context…"
/>
</div>
);
}