Skip to content

ChatPanel

A comprehensive chat interface system designed for AI assistant integration in editor applications. ChatPanel is the layout container, and the module exports a full suite of components for building rich conversational UIs: message lists, chat bubbles, input with attachments, typing indicators, tool call displays, code blocks, and empty states. Also includes hooks for managing message state, input behavior, and auto-scroll.

Live Preview

Import

import {
ChatPanel,
ChatMessageList,
ChatMessage,
ChatBubble,
ChatInput,
ChatTypingIndicator,
ChatToolCall,
ChatCodeBlock,
ChatAttachmentChip,
ChatContextChip,
ChatEmptyState,
ChatActionBar,
ChatInputToolbar,
useChatMessages,
useChatInput,
useChatScroll,
} from 'entangle-ui';

Usage

const { messages, appendMessage, updateMessage } = useChatMessages();
<ChatPanel density="comfortable">
<ChatMessageList
messages={messages}
emptyState={
<ChatEmptyState
title="How can I help?"
description="Ask me anything about your scene."
suggestions={['Create a cube', 'Change material', 'Add lighting']}
onSuggestionClick={s => handleSend(s)}
/>
}
/>
<ChatInput
onSubmit={value => handleSend(value)}
placeholder="Ask the assistant..."
streaming={isStreaming}
onStop={handleStop}
/>
</ChatPanel>;

Components

ChatPanel

The root layout container that controls spacing density. Wrap all other chat components inside it.

<ChatPanel density="comfortable">
{/* ChatMessageList, ChatInput, etc. */}
</ChatPanel>
DensityDescription
comfortableMore spacing and larger bubbles. Use for side panels and fullscreen.
compactTight spacing. Use for bottom panels and constrained areas.

ChatMessageList

Renders an array of messages with auto-scroll behavior. Accepts a custom renderMessage function for full control over individual message rendering.

<ChatMessageList
messages={messages}
autoScroll={true}
emptyState={<ChatEmptyState title="No messages yet" />}
renderMessage={(message, index) => (
<ChatMessage key={message.id} message={message} showTimestamp showAvatar />
)}
/>

ChatMessage

Renders a single message with avatar, timestamp, and action buttons. Supports a custom content renderer for markdown, LaTeX, or other rich formats.

<ChatMessage
message={message}
showTimestamp
showAvatar
actions={
<ChatActionBar>
<button onClick={handleCopy}>Copy</button>
<button onClick={handleRetry}>Retry</button>
</ChatActionBar>
}
renderContent={content => <MarkdownRenderer>{content}</MarkdownRenderer>}
/>

ChatBubble

Low-level bubble component with role-based alignment and coloring. Use for custom message layouts.

<ChatBubble role="user">Hello, can you help?</ChatBubble>
<ChatBubble role="assistant">Of course! What do you need?</ChatBubble>
<ChatBubble role="system">Session started</ChatBubble>

ChatInput

Multi-line input with submit/stop button, attachment chips, and configurable submit key. Auto-resizes from 1 line up to maxLines.

<ChatInput
value={input}
onChange={setInput}
onSubmit={handleSubmit}
onStop={handleStop}
streaming={isStreaming}
placeholder="Type a message..."
submitKey="enter"
maxLines={6}
attachments={attachments}
onRemoveAttachment={handleRemoveAttachment}
toolbar={
<ChatInputToolbar>
<button onClick={handleUpload}>Attach File</button>
</ChatInputToolbar>
}
/>

ChatTypingIndicator

Animated indicator shown while the assistant is generating a response.

<ChatTypingIndicator visible={isStreaming} label="Thinking..." variant="dots" />

ChatToolCall

Displays a tool/function invocation with expandable input/output details.

<ChatToolCall
toolCall={{
id: '1',
name: 'create_cube',
status: 'completed',
input: { size: 2 },
output: { nodeId: 'cube_01' },
durationMs: 120,
}}
collapsible
defaultExpanded={false}
/>

ChatCodeBlock

Code display with optional syntax highlighting, line numbers, copy button, and custom action buttons.

<ChatCodeBlock
code={`const mesh = new THREE.Mesh(geometry, material);`}
language="typescript"
copyable
lineNumbers
maxHeight={400}
actions={<button onClick={handleInsert}>Insert</button>}
/>

ChatAttachmentChip

Displays an attached file, image, code snippet, or selection as a chip.

<ChatAttachmentChip
attachment={{ id: '1', name: 'scene.glb', type: 'file', size: 1024000 }}
removable
onRemove={id => handleRemove(id)}
onClick={attachment => handlePreview(attachment)}
/>

ChatContextChip

Displays editor context (e.g., selected objects, active file) as a labeled chip.

<ChatContextChip
label="Selected"
items={['Cube', 'Sphere', 'Light']}
icon={<SelectionIcon />}
onDismiss={handleDismiss}
/>

ChatEmptyState

Shown when the message list is empty. Includes optional suggestion chips.

<ChatEmptyState
title="Start a conversation"
description="Ask me to modify your scene, generate code, or explain concepts."
suggestions={['Add a spotlight', 'Optimize materials', 'Export scene']}
onSuggestionClick={suggestion => handleSend(suggestion)}
/>

ChatActionBar

Horizontal row of action buttons shown below a message.

<ChatActionBar>
<button>Copy</button>
<button>Retry</button>
<button>Apply to Scene</button>
</ChatActionBar>

ChatInputToolbar

Horizontal row of action buttons rendered below the chat input area.

<ChatInputToolbar>
<button>Attach File</button>
<button>Add Context</button>
</ChatInputToolbar>

Hooks

useChatMessages

Manages the chat message array state with convenience methods.

const {
messages,
setMessages,
appendMessage,
updateMessage,
removeMessage,
clearMessages,
getMessage,
} = useChatMessages({
initialMessages: [],
maxMessages: 200,
});

useChatInput

Manages textarea input state with auto-resize and submit key handling.

const { value, setValue, clear, textareaRef, handleKeyDown, handleChange } =
useChatInput({
submitKey: 'enter',
maxLines: 6,
onSubmit: value => handleSend(value),
});

useChatScroll

Manages auto-scroll behavior for the message list, pausing when the user scrolls up.

const { scrollContainerRef, isAtBottom, hasNewMessages, scrollToBottom } =
useChatScroll({
messages,
enabled: true,
threshold: 100,
});

Props

ChatPanel

Prop Type Default Description
density 'comfortable' | 'compact' 'comfortable' Visual density of the chat layout. Comfortable has more spacing for side panels; compact is for constrained areas.
children * ReactNode Panel content, typically ChatMessageList and ChatInput.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.
ref Ref<HTMLDivElement> Ref to the root element.

ChatMessageList

Prop Type Default Description
messages * ChatMessageData[] Array of messages to render.
renderMessage (message: ChatMessageData, index: number) => ReactNode Custom renderer for individual messages.
emptyState ReactNode Content shown when the messages array is empty.
autoScroll boolean true Auto-scroll to bottom on new messages. Pauses when user scrolls up, resumes at bottom.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatMessage

Prop Type Default Description
message * ChatMessageData Message data object.
showTimestamp boolean Show timestamp below the message content.
showAvatar boolean Show avatar next to the message.
actions ReactNode Action buttons rendered below the message (typically a ChatActionBar).
renderContent (content: string) => ReactNode Custom content renderer for markdown, LaTeX, or other rich formats.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatInput

Prop Type Default Description
value string Current input value (controlled).
onChange (value: string) => void Change handler for controlled usage.
onSubmit (value: string, attachments: ChatAttachmentData[]) => void Called when the user submits the message.
onStop () => void Called when the user clicks the stop generation button.
placeholder string Placeholder text for the input.
streaming boolean false Whether the assistant is currently streaming. Shows stop button instead of send.
disabled boolean Disable the input.
submitKey 'enter' | 'ctrl+enter' 'enter' Key combination that submits the message. The other combination inserts a newline.
maxLines number 6 Maximum visible lines before the input scrolls.
attachments ChatAttachmentData[] Currently attached items shown as chips above the input.
onRemoveAttachment (attachmentId: string) => void Called when user removes an attachment chip.
prefix ReactNode Content rendered before the textarea (e.g., context chips).
suffix ReactNode Content rendered after the textarea (e.g., additional action buttons).
toolbar ReactNode Toolbar rendered below the input area (use ChatInputToolbar as the wrapper).
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatTypingIndicator

Prop Type Default Description
label string 'Thinking...' Label shown alongside the animation.
variant 'dots' | 'pulse' 'dots' Animation style: three animated dots or a pulsing bar.
visible boolean Whether the indicator is visible.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatToolCall

Prop Type Default Description
toolCall * ChatToolCallData Tool call data object with id, name, status, input, output, error, and duration.
collapsible boolean true Whether the input/output details can be expanded.
defaultExpanded boolean false Whether details are initially expanded.
icon ReactNode Custom icon for the tool (defaults to a wrench icon).
renderOutput (output: Record<string, unknown>) => ReactNode Custom renderer for the tool output.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatCodeBlock

Prop Type Default Description
code * string Code content to display.
language string Programming language for syntax highlighting.
copyable boolean true Show a copy-to-clipboard button.
lineNumbers boolean false Show line numbers.
maxHeight number 400 Maximum visible height in pixels before the block scrolls.
actions ReactNode Actions rendered in the code block header alongside the copy button.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatAttachmentChip

Prop Type Default Description
attachment * ChatAttachmentData Attachment data object.
onRemove (id: string) => void Called when the remove button is clicked.
onClick (attachment: ChatAttachmentData) => void Called when the chip itself is clicked (e.g., to preview).
removable boolean false Whether to show the remove button.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatContextChip

Prop Type Default Description
label * string Label describing the context (e.g., "Selected", "Active file").
items * string[] Items in this context group.
icon ReactNode Icon shown before the label.
onDismiss () => void Called when the chip is dismissed.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

ChatEmptyState

Prop Type Default Description
title string Main heading text.
description string Supporting description text.
icon ReactNode Icon or illustration.
suggestions string[] Quick-start suggestion chips that populate the input when clicked.
onSuggestionClick (suggestion: string) => void Called when a suggestion chip is clicked.
className string Additional CSS class names.
style CSSProperties Inline styles.
testId string Test identifier for automated testing.

Data Types

ChatMessageData

interface ChatMessageData {
id: string;
role: 'user' | 'assistant' | 'system' | 'tool';
content: string;
status: 'complete' | 'streaming' | 'error' | 'pending';
timestamp: string; // ISO 8601
attachments?: ChatAttachmentData[];
toolCalls?: ChatToolCallData[];
avatar?: string;
displayName?: string;
}

ChatAttachmentData

interface ChatAttachmentData {
id: string;
name: string;
type: 'file' | 'image' | 'code' | 'selection';
mimeType?: string;
size?: number;
thumbnailUrl?: string;
content?: string;
meta?: Record<string, unknown>;
}

ChatToolCallData

interface ChatToolCallData {
id: string;
name: string;
status: 'pending' | 'running' | 'completed' | 'error';
input?: Record<string, unknown>;
output?: Record<string, unknown>;
error?: string;
durationMs?: number;
}