一键导入
heimdall-chat
Heimdall component guide for Chat: ChatMessage, ToolBlock, ThinkingBlock, ChatDivider, ChatSuggestions, ChatComposer, ChatContainer
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Heimdall component guide for Chat: ChatMessage, ToolBlock, ThinkingBlock, ChatDivider, ChatSuggestions, ChatComposer, ChatContainer
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Heimdall component guide for Charts: Sparkline, LineChart, BarChart, BarV, BarH, StackedBar, Donut, PieChart, Heatmap, StatusTimeline, ProgressBar, MetricRow
Heimdall component guide for Data Display: StatTile, StatGrid, Table, KVGrid, InspectorPanel
Heimdall component guide for Graph: GraphCanvas, GraphNode, GraphEdge, GraphInspector, TopologyNode, HierarchyRow, HierarchyTree
Heimdall component guide for Inputs: TextInput, TextArea, NumberInput, Select, TriState, Field, FilterDropdown, EntityPicker, KeyValueEditor, OrderedList, RelationshipBuilder
Heimdall component guide for Layout: Panel, SplitPane
Heimdall component guide for Navigation: NavItem, Sidebar, Topbar, TabBar
| name | heimdall-chat |
| description | Heimdall component guide for Chat: ChatMessage, ToolBlock, ThinkingBlock, ChatDivider, ChatSuggestions, ChatComposer, ChatContainer |
Import all components from @tinkermonkey/heimdall-ui. Import the CSS once at your app entry point:
import '@tinkermonkey/heimdall-ui/css'
A single chat turn (user or bot) with optional inline tool call and reasoning blocks.
import { ChatMessage } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
role | 'user' | 'bot' | required | Message source |
senderName | string | required | Display name |
timestamp | string | required | Formatted time string |
body | ReactNode | required | Message content |
avatar | string | — | URL (renders <img>) or text (renders as initials) |
badge | string | — | Bot role badge (e.g. 'EXECUTOR') rendered monospace |
toolBlock | ToolBlockData | — | Inline tool call visualization |
thinkingBlock | ThinkingBlockData | — | Collapsible reasoning block |
...HTMLAttributes | Standard div attributes |
interface ToolBlockData {
name: string
status: 'running' | 'success' | 'error'
output?: Array<{ key?: string; value: string }>
}
interface ThinkingBlockData {
content: string
}
// User message
<ChatMessage role="user" senderName="You" timestamp="10:30 AM" body="Can you analyze this dataset?" />
// Bot with badge, tool call, and thinking
<ChatMessage
role="bot"
senderName="Assistant"
badge="EXECUTOR"
timestamp="10:31 AM"
body="Running analysis..."
toolBlock={{
name: 'query_database',
status: 'success',
output: [
{ key: 'rows', value: '1,234' },
{ key: 'duration', value: '245ms' },
],
}}
thinkingBlock={{
content: `Step 1: Load dataset\nStep 2: Calculate metrics\nStep 3: Return results`,
}}
/>
http://, https://, or / render as <img>; otherwise rendered as text initialsavatar provided, uses first 2 uppercase characters of senderNametoolBlock and thinkingBlock can be used independently or together on the same messageCollapsible block showing a tool/function call name, execution status, and optional key/value output.
import { ToolBlock } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | required | Tool call name |
status | 'running' | 'success' | 'error' | required | Execution status |
output | Array<{ key?: string; value: string }> | [] | Key/value output rows |
defaultCollapsed | boolean | false | Initial collapsed state |
onToggleCollapsed | (collapsed: boolean) => void | — | Collapse/expand callback |
...HTMLAttributes | Standard div attributes |
// Running (no output yet)
<ToolBlock name="query_database" status="running" />
// Success with output
<ToolBlock
name="query_database"
status="success"
output={[
{ key: 'rows', value: '1,234' },
{ key: 'duration', value: '245ms' },
]}
/>
// Error
<ToolBlock
name="fetch_schema"
status="error"
output={[{ value: 'Connection timeout after 30s' }]}
/>
// Pre-collapsed for historical messages
<ToolBlock name="old_tool_call" status="success" defaultCollapsed output={[{ key: 'result', value: 'ok' }]} />
key on output rows is optional — rows with only value render without a key columnCollapsible monospace block for displaying model reasoning or step-by-step thought processes.
import { ThinkingBlock } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
content | string | required | Reasoning text |
label | string | 'thinking' | Header label (displayed uppercase) |
defaultCollapsed | boolean | false | Initial collapsed state |
onToggleCollapsed | (collapsed: boolean) => void | — | Collapse/expand callback |
...HTMLAttributes | Standard div attributes |
<ThinkingBlock
content={`Breaking down the problem:\n1. Load the dataset\n2. Calculate metrics\n3. Return results`}
/>
// Pre-collapsed with custom label
<ThinkingBlock
label="reasoning"
content={longReasoningText}
defaultCollapsed
/>
white-space: pre-wrap — newlines in the string are preserved as line breaksHorizontal rule with centered label for separating conversation sections (dates, sessions).
import { ChatDivider } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | required | Center label text |
...HTMLAttributes | Standard div attributes |
<ChatDivider label="May 18, 2026" />
<ChatDivider label="New session" />
role="separator" with aria-label={label}; the visual label span is aria-hidden="true" to avoid double-readingRow of quick-reply suggestion chips. Selecting one disables all others.
import { ChatSuggestions } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
suggestions | string[] | required | Chip labels |
onSelect | (suggestion: string) => void | required | Called when a chip is clicked |
selected | string | — | Currently selected chip; when set, others are disabled |
aria-label | string | 'Suggestions' | Group accessible label |
...HTMLAttributes | Standard div attributes |
const [selected, setSelected] = useState<string | undefined>()
<ChatSuggestions
suggestions={['Show me the plan', 'Approve & run', 'Cancel']}
selected={selected}
onSelect={setSelected}
/>
null if suggestions is empty — no DOM element renderedselected is set, unselected chips are disabled but still rendered (not hidden)onSelect is removed from inherited HTMLAttributes to avoid a name conflict; always use the explicit propRich message input with context chips, file attachment previews, and submit controls. Enter submits; Shift+Enter inserts a newline.
import { ChatComposer } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | required | Controlled input value |
onChange | (value: string) => void | required | Called on every keystroke |
onSubmit | (value: string, contextItems: ContextItem[]) => void | required | Called on Enter or send button |
placeholder | string | 'Type a message...' | Textarea placeholder |
onContextChange | (items: ContextItem[]) => void | — | Called when context chip removed |
onAttachmentChange | (attachments: Attachment[]) => void | — | Called when files added/removed |
scopeLabel | string | — | Active bot label shown in toolbar |
contextItems | ContextItem[] | [] | Context chips to display |
attachments | Attachment[] | [] | Attached files to display |
disabled | boolean | false | Disables textarea and buttons |
loading | boolean | false | Disables send button only |
label | string | 'Message' | Textarea aria-label |
interface ContextItem {
id: string
label: string
}
interface Attachment {
id: string
name: string
size?: number
}
// Minimal
const [value, setValue] = useState('')
<ChatComposer
value={value}
onChange={setValue}
onSubmit={(v) => { sendMessage(v); setValue('') }}
/>
// With context items and attachments
const [value, setValue] = useState('')
const [context, setContext] = useState<ContextItem[]>([{ id: 'schema', label: 'schema.json' }])
const [attachments, setAttachments] = useState<Attachment[]>([])
<ChatComposer
value={value}
onChange={setValue}
onSubmit={(v, ctx) => { sendMessage(v, ctx); setValue('') }}
scopeLabel="assistant"
contextItems={context}
onContextChange={setContext}
attachments={attachments}
onAttachmentChange={setAttachments}
loading={isSending}
/>
value.trim() is empty, disabled={true}, or loading={true}rows={2} minimumonSubmit receives both the value and current contextItemsFull chat panel composing bot tabs, scrollable message thread, and a composer slot.
import { ChatContainer } from '@tinkermonkey/heimdall-ui'
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | required | Thread content (ChatMessage, ChatDivider, etc.) |
bots | BotTab[] | [] | Bot tab definitions |
activeBotId | string | — | Currently selected bot tab ID |
onBotChange | (botId: string) => void | — | Called when a bot tab is clicked |
autoScroll | boolean | true | Scroll to bottom when children change |
composer | ReactNode | — | Composer slot (typically ChatComposer) |
...HTMLAttributes | Standard div attributes |
interface BotTab {
id: string
label: string
role: string
status: 'idle' | 'busy' | 'healthy' | 'error'
}
// Single-bot with composer
<ChatContainer
composer={
<ChatComposer
value={composerValue}
onChange={setComposerValue}
onSubmit={(v) => { sendMessage(v); setComposerValue('') }}
/>
}
>
<ChatDivider label="May 26, 2026" />
<ChatMessage role="user" senderName="You" timestamp="10:30 AM" body="What's the status?" />
<ChatMessage role="bot" senderName="Assistant" badge="EXECUTOR" timestamp="10:31 AM" body="All systems go." />
</ChatContainer>
// Multi-bot with tab switching
const BOTS: BotTab[] = [
{ id: 'assistant', label: 'Assistant', role: 'EXECUTOR', status: 'idle' },
{ id: 'analyzer', label: 'Analyzer', role: 'ANALYST', status: 'healthy' },
]
<ChatContainer
bots={BOTS}
activeBotId={activeBotId}
onBotChange={setActiveBotId}
composer={<ChatComposer value={value} onChange={setValue} onSubmit={handleSubmit} />}
>
{messages.map(msg =>
msg.type === 'divider'
? <ChatDivider key={msg.id} label={msg.label} />
: <ChatMessage key={msg.id} {...msg} />
)}
</ChatContainer>
autoScroll fires on every children change — if the parent re-renders without content changes, scroll still firesrole="log" and aria-live="polite" for live region announcementsbots is empty or not provided, no tab bar is rendered