docs: add docs (#100)

This commit is contained in:
Marcus Schiesser
2025-05-29 16:18:00 +07:00
committed by GitHub
parent 09d6ec694b
commit 4d3ddbf952
13 changed files with 3830 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@llamaindex/chat-ui-docs': patch
---
Add comprehensive MDX documentation covering all components, widgets, hooks, annotations, and examples
+649
View File
@@ -0,0 +1,649 @@
---
title: Annotations
description: Working with rich content annotations for multimedia and interactive chat experiences
---
Annotations are the key to creating rich, interactive chat experiences beyond simple text. They allow you to embed images, files, sources, events, and custom content types directly into chat messages.
## Annotation System Overview
Annotations are structured data attached to messages that widgets can render as rich content. The system supports both built-in annotation types and custom annotations for domain-specific content.
### Message Structure with Annotations
```typescript
interface Message {
id: string
role: 'user' | 'assistant' | 'system'
content: string
annotations?: JSONValue[]
}
```
### Built-in Annotation Types
The library provides several built-in annotation types:
- **IMAGE** - Image data with URLs
- **DOCUMENT_FILE** - File attachments and metadata
- **SOURCES** - Citation and source references
- **EVENTS** - Process events and function calls
- **AGENT_EVENTS** - Agent-specific events with progress
- **ARTIFACT** - Interactive code and document artifacts
- **SUGGESTED_QUESTIONS** - Follow-up question suggestions
## Image Annotations
Display images within chat messages with zoom and preview functionality.
### Creating Image Annotations
```typescript
// Server-side: Add image annotation to message
const imageAnnotation = {
type: 'IMAGE',
data: {
url: 'https://example.com/chart.png',
alt: 'Sales chart for Q4 2024',
},
}
return {
role: 'assistant',
content: 'Here is the sales chart you requested:',
annotations: [imageAnnotation],
}
```
### Streaming Image Annotations
```typescript
// In your API route
const stream = new ReadableStream({
start(controller) {
// Send text content first
controller.enqueue(
encoder.encode('0:"Here is the chart you requested:\\n"\\n')
)
// Send image annotation
const annotation = {
type: 'IMAGE',
data: {
url: '/api/generated-chart.png',
alt: 'Generated sales chart',
},
}
controller.enqueue(encoder.encode(`8:${JSON.stringify([annotation])}\\n`))
controller.close()
},
})
```
### Client-side Rendering
Images automatically render when using the content components:
```tsx
<ChatMessage message={message}>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Image />{' '}
{/* Automatically renders IMAGE annotations */}
</ChatMessage.Content>
</ChatMessage>
```
## File Annotations
Display file attachments with download links and preview capabilities.
### Document File Annotations
```typescript
const fileAnnotation = {
type: 'DOCUMENT_FILE',
data: {
files: [
{
id: 'doc1',
name: 'quarterly-report.pdf',
type: 'application/pdf',
url: '/files/quarterly-report.pdf',
size: 2048576, // 2MB in bytes
metadata: {
title: 'Q4 2024 Quarterly Report',
author: 'Finance Team',
pages: 25,
},
},
{
id: 'doc2',
name: 'data-analysis.csv',
type: 'text/csv',
url: '/files/data-analysis.csv',
size: 1024000,
metadata: {
rows: 5000,
columns: 12,
},
},
],
},
}
```
### File Upload Integration
```typescript
// Handle file uploads in your API
export async function POST(request: Request) {
const formData = await request.formData()
const files = formData.getAll('files') as File[]
const fileAnnotations = await Promise.all(
files.map(async file => {
const url = await uploadFile(file) // Your upload logic
return {
type: 'DOCUMENT_FILE',
data: {
files: [
{
id: generateId(),
name: file.name,
type: file.type,
url,
size: file.size,
},
],
},
}
})
)
return streamResponse(content, fileAnnotations)
}
```
## Source Annotations
Display citations and source references with document grouping.
### Creating Source Annotations
```typescript
const sourceAnnotation = {
type: 'sources',
data: {
nodes: [
{
id: 'source1',
url: '/documents/research-paper.pdf',
metadata: {
title: 'Machine Learning in Healthcare',
author: 'Dr. Jane Smith',
page_number: 15,
section: 'Methodology',
published_date: '2024-01-15',
},
},
{
id: 'source2',
url: '/documents/clinical-study.pdf',
metadata: {
title: 'Clinical Trial Results',
author: 'Medical Research Institute',
page_number: 8,
figure: 'Figure 3.2',
},
},
],
},
}
```
### Citation in Content
Reference sources directly in your content using citation syntax:
```typescript
const content = `
Based on recent research [^1], machine learning shows promising results
in medical diagnosis. The clinical trial data [^2] supports these findings
with a 95% accuracy rate.
[^1]: Machine Learning in Healthcare, p. 15
[^2]: Clinical Trial Results, Figure 3.2
`
return {
role: 'assistant',
content,
annotations: [sourceAnnotation],
}
```
## Event Annotations
Display process events, function calls, and system activities.
### Basic Events
```typescript
const eventAnnotation = {
type: 'events',
data: [
{
type: 'function_call',
name: 'search_database',
args: {
query: 'machine learning papers',
limit: 10,
},
result: 'Found 8 relevant papers',
timestamp: '2024-01-15T10:30:00Z',
},
{
type: 'tool_use',
name: 'calculate_statistics',
args: {
dataset: 'user_engagement',
},
result: {
mean: 4.2,
median: 4.1,
std_dev: 0.8,
},
},
],
}
```
### Agent Events with Progress
```typescript
const agentEventAnnotation = {
type: 'agent_events',
data: {
agent_name: 'Research Assistant',
total_steps: 4,
current_step: 2,
progress: 50,
events: [
{
step: 1,
name: 'Search Documents',
status: 'completed',
result: 'Found 15 relevant documents',
},
{
step: 2,
name: 'Analyze Content',
status: 'in_progress',
progress: 75,
},
{
step: 3,
name: 'Generate Summary',
status: 'pending',
},
{
step: 4,
name: 'Create Recommendations',
status: 'pending',
},
],
},
}
```
## Artifact Annotations
Create interactive code and document artifacts that users can edit.
### Code Artifacts
```typescript
const codeArtifact = {
type: 'artifact',
data: {
type: 'code',
data: {
title: 'Data Analysis Script',
file_name: 'analyze_data.py',
language: 'python',
code: `
import pandas as pd
import matplotlib.pyplot as plt
def analyze_sales_data(file_path):
# Load data
df = pd.read_csv(file_path)
# Calculate monthly totals
monthly_sales = df.groupby('month')['sales'].sum()
# Create visualization
plt.figure(figsize=(10, 6))
monthly_sales.plot(kind='bar')
plt.title('Monthly Sales Analysis')
plt.ylabel('Sales ($)')
plt.show()
return monthly_sales
# Usage
sales_data = analyze_sales_data('sales.csv')
print(sales_data)
`,
},
},
}
```
### Document Artifacts
```typescript
const documentArtifact = {
type: 'artifact',
data: {
type: 'document',
data: {
title: 'Project Proposal',
content: `
# AI-Powered Analytics Platform
## Executive Summary
This proposal outlines the development of an AI-powered analytics platform
designed to help businesses make data-driven decisions.
## Key Features
- **Real-time Data Processing**: Stream analytics with sub-second latency
- **Machine Learning Models**: Automated insight generation
- **Interactive Dashboards**: Self-service analytics for business users
## Implementation Timeline
### Phase 1 (Months 1-3)
- Core platform development
- Basic ML model integration
### Phase 2 (Months 4-6)
- Advanced analytics features
- Dashboard creation tools
## Budget Estimate
Total project cost: $250,000
`,
},
},
}
```
## Suggested Questions
Provide interactive follow-up questions to guide the conversation.
```typescript
const suggestedQuestionsAnnotation = {
type: 'suggested_questions',
data: {
questions: [
'Can you explain the methodology in more detail?',
'What are the potential limitations of this approach?',
'How does this compare to traditional methods?',
'What would be the next steps for implementation?',
],
},
}
```
## Custom Annotations
Create domain-specific annotations for specialized content.
### Weather Widget Example
```typescript
// Define custom annotation type
interface WeatherAnnotation {
type: 'weather'
data: {
location: string
temperature: number
condition: string
humidity: number
windSpeed: number
forecast?: Array<{
day: string
high: number
low: number
condition: string
}>
}
}
// Create annotation
const weatherAnnotation: WeatherAnnotation = {
type: 'weather',
data: {
location: 'San Francisco, CA',
temperature: 22,
condition: 'sunny',
humidity: 65,
windSpeed: 12,
forecast: [
{ day: 'Tomorrow', high: 24, low: 18, condition: 'cloudy' },
{ day: 'Wednesday', high: 26, low: 20, condition: 'sunny' },
],
},
}
```
### Custom Widget Implementation
```tsx
import { useChatMessage, getCustomAnnotations } from '@llamaindex/chat-ui'
interface WeatherData {
location: string
temperature: number
condition: string
humidity: number
windSpeed: number
}
export function CustomWeatherWidget() {
const { message } = useChatMessage()
const weatherData = getCustomAnnotations<WeatherData>(
message.annotations,
'weather'
)
if (!weatherData[0]) return null
const data = weatherData[0]
return (
<div className="my-4 rounded-lg border border-blue-200 bg-blue-50 p-4">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-blue-100">
<WeatherIcon condition={data.condition} />
</div>
<div className="flex-1">
<h3 className="font-semibold text-blue-900">{data.location}</h3>
<div className="flex items-center gap-4 text-sm text-blue-700">
<span className="text-2xl font-bold">{data.temperature}°C</span>
<span>{data.condition}</span>
</div>
</div>
</div>
<div className="mt-3 grid grid-cols-2 gap-4 text-sm text-blue-600">
<div className="flex items-center gap-2">
<span>💧 Humidity:</span>
<span className="font-medium">{data.humidity}%</span>
</div>
<div className="flex items-center gap-2">
<span>🌬️ Wind:</span>
<span className="font-medium">{data.windSpeed} km/h</span>
</div>
</div>
</div>
)
}
function WeatherIcon({ condition }: { condition: string }) {
const iconMap: Record<string, string> = {
sunny: '☀️',
cloudy: '☁️',
rainy: '🌧️',
snowy: '❄️',
stormy: '⛈️',
}
return (
<span className="text-2xl">{iconMap[condition.toLowerCase()] || '🌤️'}</span>
)
}
```
### Using Custom Widgets
```tsx
import { ChatMessage } from '@llamaindex/chat-ui'
import { CustomWeatherWidget } from './custom-weather-widget'
function MessageWithCustomWidgets({ message }) {
return (
<ChatMessage message={message}>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Image />
<ChatMessage.Content.Source />
<CustomWeatherWidget /> {/* Add custom widget */}
</ChatMessage.Content>
</ChatMessage>
)
}
```
## Annotation Utilities
### getCustomAnnotations
Extract custom annotations from a message:
```tsx
import { getCustomAnnotations } from '@llamaindex/chat-ui'
function useWeatherData() {
const { message } = useChatMessage()
return getCustomAnnotations<WeatherData>(message.annotations, 'weather')
}
```
## Server-side Implementation
### Streaming Annotations
```typescript
// API route for streaming with annotations
export async function POST(request: Request) {
const { messages } = await request.json()
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder()
// Send initial text
controller.enqueue(
encoder.encode('0:"Let me analyze the weather data for you.\\n"\\n')
)
// Process and get weather data
const weatherData = await getWeatherData(location)
// Send weather annotation
const annotation = {
type: 'weather',
data: weatherData,
}
controller.enqueue(encoder.encode(`8:${JSON.stringify([annotation])}\\n`))
// Send follow-up text
controller.enqueue(
encoder.encode('0:"Would you like a detailed forecast?"\\n')
)
controller.close()
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'X-Vercel-AI-Data-Stream': 'v1',
},
})
}
```
### Annotation Processing
```typescript
// Process different annotation types
function createAnnotations(data: any) {
const annotations = []
// Add image if generated
if (data.imageUrl) {
annotations.push({
type: 'IMAGE',
data: {
url: data.imageUrl,
alt: data.imageDescription,
},
})
}
// Add sources if referenced
if (data.sources?.length > 0) {
annotations.push({
type: 'sources',
data: {
nodes: data.sources.map(source => ({
id: source.id,
url: source.url,
metadata: source.metadata,
})),
},
})
}
// Add custom weather data
if (data.weather) {
annotations.push({
type: 'weather',
data: data.weather,
})
}
return annotations
}
```
## Next Steps
- [Artifacts](./artifacts) - Learn about interactive code and document artifacts
- [Widgets](./widgets) - Explore widget implementation details
- [Examples](./examples) - See complete annotation examples
- [Customization](./customization) - Style and customize annotation appearance
+580
View File
@@ -0,0 +1,580 @@
---
title: Artifacts
description: Interactive code and document editing with version management
---
Artifacts are interactive, editable content that appears in the chat canvas. They allow users to view, edit, and interact with generated code and documents directly within the chat interface, similar to OpenAI's ChatGPT canvas feature.
## Artifact System Overview
The artifact system consists of:
- **Artifact Annotations** - Data structure for artifact content
- **ChatCanvas** - Side panel for displaying artifacts
- **Artifact Viewers** - Components for rendering different artifact types
- **Version Management** - Track changes and revisions
- **Edit Capabilities** - In-place editing with syntax highlighting
### Artifact Types
The system supports two main artifact types:
1. **Code Artifacts** - Interactive code editing with syntax highlighting
2. **Document Artifacts** - Rich text document editing with markdown support
## Code Artifacts
Code artifacts provide interactive code editing with full syntax highlighting and language support.
### Creating Code Artifacts
```typescript
// Server-side: Create code artifact annotation
const codeArtifact = {
type: 'artifact',
data: {
type: 'code',
data: {
title: 'Data Visualization Script',
file_name: 'visualize_data.py',
language: 'python',
code: `
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def create_sales_chart(data_file):
"""Create a sales visualization chart."""
# Load the data
df = pd.read_csv(data_file)
# Prepare the data
monthly_sales = df.groupby('month')['revenue'].sum()
# Create the visualization
plt.figure(figsize=(12, 6))
bars = plt.bar(monthly_sales.index, monthly_sales.values,
color='steelblue', alpha=0.8)
# Customize the chart
plt.title('Monthly Sales Revenue', fontsize=16, fontweight='bold')
plt.xlabel('Month', fontsize=12)
plt.ylabel('Revenue ($)', fontsize=12)
plt.xticks(rotation=45)
# Add value labels on bars
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height,
f'${height:,.0f}', ha='center', va='bottom')
plt.tight_layout()
plt.grid(True, alpha=0.3)
plt.show()
return monthly_sales
# Example usage
if __name__ == "__main__":
sales_data = create_sales_chart('sales_data.csv')
print(f"Total annual revenue: ${sales_data.sum():,.2f}")
`
}
}
}
```
### Streaming Code Artifacts
```typescript
// In your API route
export async function POST(request: Request) {
const { messages } = await request.json()
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder()
// Send initial response
controller.enqueue(
encoder.encode(
'0:"I\'ll create a Python script for data visualization:\\n"\\n'
)
)
// Generate code (could be from LLM)
const generatedCode = await generateCodeWithLLM(messages)
// Send code artifact
const artifact = {
type: 'artifact',
data: {
type: 'code',
data: {
title: 'Data Visualization Script',
file_name: 'chart_generator.py',
language: 'python',
code: generatedCode,
},
},
}
controller.enqueue(encoder.encode(`8:${JSON.stringify([artifact])}\\n`))
// Send follow-up text
controller.enqueue(
encoder.encode(
'0:"You can edit this code directly in the canvas. Would you like me to explain any part of it?"\\n'
)
)
controller.close()
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'X-Vercel-AI-Data-Stream': 'v1',
},
})
}
```
### Supported Languages
The code artifact system supports syntax highlighting for:
- **JavaScript/TypeScript** - `.js`, `.ts`, `.jsx`, `.tsx`
- **Python** - `.py`, `.pyx`
- **CSS/SCSS** - `.css`, `.scss`, `.sass`
- **HTML** - `.html`, `.htm`
- **JSON** - `.json`
- **Markdown** - `.md`, `.mdx`
- **SQL** - `.sql`
- **Shell/Bash** - `.sh`, `.bash`
### Code Artifact Features
```tsx
// The canvas automatically provides these features:
// - Syntax highlighting
// - Line numbers
// - Code folding
// - Search and replace
// - Auto-indentation
// - Bracket matching
// - Error highlighting (for supported languages)
```
## Document Artifacts
Document artifacts provide rich text editing with markdown support and real-time preview.
### Creating Document Artifacts
```typescript
const documentArtifact = {
type: 'artifact',
data: {
type: 'document',
data: {
title: 'API Integration Guide',
content: `
# API Integration Guide
## Overview
This guide walks you through integrating our REST API into your application.
## Authentication
All API requests require authentication using API keys:
\`\`\`bash
curl -H "Authorization: Bearer YOUR_API_KEY" \\
https://api.example.com/v1/users
\`\`\`
## Endpoints
### User Management
#### Get User Profile
\`\`\`
GET /v1/users/{userId}
\`\`\`
**Response:**
\`\`\`json
{
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
}
\`\`\`
#### Update User Profile
\`\`\`
PUT /v1/users/{userId}
Content-Type: application/json
{
"name": "John Smith",
"email": "john.smith@example.com"
}
\`\`\`
### Data Operations
#### List Records
\`\`\`
GET /v1/records?limit=20&offset=0
\`\`\`
**Query Parameters:**
- \`limit\`: Number of records to return (default: 20, max: 100)
- \`offset\`: Number of records to skip (default: 0)
- \`sort\`: Sort field (default: created_at)
- \`order\`: Sort order (asc/desc, default: desc)
## Error Handling
The API uses standard HTTP status codes:
| Code | Description |
|------|-------------|
| 200 | Success |
| 400 | Bad Request |
| 401 | Unauthorized |
| 404 | Not Found |
| 500 | Internal Server Error |
**Error Response Format:**
\`\`\`json
{
"error": {
"code": "INVALID_REQUEST",
"message": "The request is missing required parameters",
"details": {
"missing_fields": ["name", "email"]
}
}
}
\`\`\`
## Rate Limiting
- **Free Tier**: 100 requests per hour
- **Pro Tier**: 1,000 requests per hour
- **Enterprise**: Custom limits
Rate limit headers are included in all responses:
\`\`\`
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
\`\`\`
## SDKs and Libraries
We provide official SDKs for popular languages:
### JavaScript/Node.js
\`\`\`bash
npm install @example/api-client
\`\`\`
\`\`\`javascript
import { ApiClient } from '@example/api-client'
const client = new ApiClient({
apiKey: 'your-api-key'
})
const user = await client.users.get('user_123')
console.log(user)
\`\`\`
### Python
\`\`\`bash
pip install example-api-client
\`\`\`
\`\`\`python
from example_api import ApiClient
client = ApiClient(api_key='your-api-key')
user = client.users.get('user_123')
print(user)
\`\`\`
## Best Practices
1. **Always use HTTPS** for API requests
2. **Store API keys securely** - never commit them to version control
3. **Implement retry logic** with exponential backoff
4. **Cache responses** when appropriate to reduce API calls
5. **Monitor rate limits** and implement proper handling
## Webhooks
Configure webhooks to receive real-time notifications:
\`\`\`json
{
"url": "https://yourapp.com/webhooks/api",
"events": ["user.created", "user.updated", "user.deleted"],
"secret": "webhook-secret-key"
}
\`\`\`
## Support
- **Documentation**: https://docs.example.com
- **Support Email**: support@example.com
- **Status Page**: https://status.example.com
Happy coding! 🚀
`,
},
},
}
```
### Document Features
Document artifacts provide:
- **Rich Text Editing** - WYSIWYG editor with markdown output
- **Live Preview** - Real-time rendering of markdown content
- **Formatting Tools** - Bold, italic, headers, lists, links
- **Code Blocks** - Syntax-highlighted code snippets
- **Tables** - Table creation and editing
- **Math Support** - LaTeX math rendering
- **Image Embedding** - Image insertion and management
## Canvas Integration
### Basic Canvas Setup
```tsx
import { ChatSection, ChatCanvas } from '@llamaindex/chat-ui'
function ChatWithCanvas() {
const handler = useChat({ api: '/api/chat' })
return (
<ChatSection handler={handler} className="flex h-full">
<div className="flex-1">
<ChatMessages />
<ChatInput />
</div>
<ChatCanvas className="w-1/2 border-l" />
</ChatSection>
)
}
```
### Canvas Auto-Show
The canvas automatically appears when artifacts are present:
```tsx
// Canvas appears automatically when message contains artifacts
<ChatMessage message={messageWithArtifact}>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Artifact /> {/* Triggers canvas display */}
</ChatMessage.Content>
</ChatMessage>
```
## Version Management
Artifacts support version tracking and restoration:
### Version History
```tsx
import { useChatCanvas } from '@llamaindex/chat-ui'
function ArtifactVersions() {
const { currentArtifact, updateArtifact } = useChatCanvas()
if (!currentArtifact) return null
const versions = currentArtifact.versions || []
const restoreVersion = (version: any) => {
updateArtifact({
...currentArtifact,
content: version.content,
versions: [
...versions,
{
id: generateId(),
content: currentArtifact.content,
timestamp: new Date().toISOString(),
description: 'Current version backup',
},
],
})
}
return (
<div className="space-y-2">
<h3 className="font-semibold">Version History</h3>
{versions.map((version, index) => (
<div key={version.id} className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">
Version {versions.length - index}
</p>
<p className="text-xs text-gray-500">
{new Date(version.timestamp).toLocaleString()}
</p>
</div>
<button
onClick={() => restoreVersion(version)}
className="text-sm text-blue-600 hover:underline"
>
Restore
</button>
</div>
))}
</div>
)
}
```
### Save Changes
```tsx
function SaveArtifactChanges() {
const { currentArtifact, updateArtifact } = useChatCanvas()
const [hasChanges, setHasChanges] = useState(false)
const saveChanges = () => {
if (!currentArtifact || !hasChanges) return
// Create version backup
const newVersion = {
id: generateId(),
content: currentArtifact.originalContent,
timestamp: new Date().toISOString(),
description: 'Auto-saved version',
}
updateArtifact({
...currentArtifact,
versions: [...(currentArtifact.versions || []), newVersion],
originalContent: currentArtifact.content,
})
setHasChanges(false)
}
return (
<button
onClick={saveChanges}
disabled={!hasChanges}
className="rounded bg-blue-600 px-3 py-1 text-white disabled:opacity-50"
>
{hasChanges ? 'Save Changes' : 'Saved'}
</button>
)
}
```
## Artifact Actions
### Export Functionality
```tsx
function ArtifactExport() {
const { currentArtifact } = useChatCanvas()
const exportAsFile = () => {
if (!currentArtifact) return
const content =
currentArtifact.type === 'code'
? currentArtifact.code
: currentArtifact.content
const filename =
currentArtifact.type === 'code'
? currentArtifact.file_name
: `${currentArtifact.title}.md`
const blob = new Blob([content], {
type: 'text/plain;charset=utf-8',
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
}
return (
<button
onClick={exportAsFile}
className="flex items-center gap-2 rounded border px-3 py-1 text-sm"
>
<DownloadIcon className="h-4 w-4" />
Export
</button>
)
}
```
### Copy to Clipboard
```tsx
import { useCopyToClipboard } from '@llamaindex/chat-ui'
function CopyArtifact() {
const { currentArtifact } = useChatCanvas()
const { copyToClipboard, isCopied } = useCopyToClipboard()
const handleCopy = () => {
if (!currentArtifact) return
const content =
currentArtifact.type === 'code'
? currentArtifact.code
: currentArtifact.content
copyToClipboard(content)
}
return (
<button
onClick={handleCopy}
className="flex items-center gap-2 rounded border px-3 py-1 text-sm"
>
<CopyIcon className="h-4 w-4" />
{isCopied ? 'Copied!' : 'Copy'}
</button>
)
}
```
## Next Steps
- [Examples](./examples) - See complete artifact implementations
- [Customization](./customization) - Style and customize artifact appearance
- [Widgets](./widgets) - Explore related widget functionality
- [Annotations](./annotations) - Understand the annotation system
+423
View File
@@ -0,0 +1,423 @@
---
title: Core Components
description: Detailed guide to the main chat interface components
---
The core components form the foundation of any chat interface built with LlamaIndex Chat UI. These components work together to provide a complete chat experience.
## ChatSection
The `ChatSection` is the root component that provides context and layout for all other chat components.
### Basic Usage
```tsx
import { ChatSection } from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
function MyChat() {
const handler = useChat({ api: '/api/chat' })
return <ChatSection handler={handler} />
}
```
### Props
```typescript
interface ChatSectionProps {
handler: ChatHandler
className?: string
children?: React.ReactNode
}
```
- **handler**: Chat handler from `useChat` or custom implementation
- **className**: Custom CSS classes for styling
- **children**: Custom layout (defaults to `ChatMessages` + `ChatInput`)
### Default Layout
When no children are provided, `ChatSection` renders:
```tsx
<>
<ChatMessages />
<ChatInput />
</>
```
### Custom Layout
```tsx
<ChatSection handler={handler} className="flex-row gap-4">
<div className="flex-1">
<ChatMessages />
<ChatInput />
</div>
<ChatCanvas className="w-1/3" />
</ChatSection>
```
## ChatMessages
The `ChatMessages` component displays the conversation history with automatic scrolling and loading states.
### Basic Usage
```tsx
import { ChatMessages } from '@llamaindex/chat-ui'
function MessageHistory() {
return (
<ChatMessages>
<ChatMessages.List>
{/* Custom message rendering */}
</ChatMessages.List>
</ChatMessages>
)
}
```
### Sub-components
#### ChatMessages.List
Contains the actual message list with scroll management:
```tsx
<ChatMessages.List className="px-4 py-6">
{messages.map((message, index) => (
<ChatMessage key={index} message={message} />
))}
</ChatMessages.List>
```
#### ChatMessages.Loading
Shows loading indicator when chat is processing:
```tsx
<ChatMessages.Loading>
<div className="animate-pulse">Thinking...</div>
</ChatMessages.Loading>
```
#### ChatMessages.Empty
Displays when no messages exist:
```tsx
<ChatMessages.Empty
heading="Welcome to Chat"
subheading="Start a conversation by typing below"
/>
```
#### ChatMessages.Actions
Action buttons for the message list:
```tsx
<ChatMessages.Actions>
<button onClick={reload}>Reload</button>
<button onClick={stop}>Stop</button>
</ChatMessages.Actions>
```
### Features
- **Auto-scroll** - Automatically scrolls to new messages
- **Loading states** - Shows when chat is processing
- **Empty state** - Customizable welcome message
- **Performance** - Optimized for large message histories
## ChatMessage
Individual message component with full annotation support and role-based rendering.
### Basic Usage
```tsx
import { ChatMessage } from '@llamaindex/chat-ui'
function CustomMessage({ message, isLast }) {
return (
<ChatMessage message={message} isLast={isLast}>
<ChatMessage.Avatar>
<UserAvatar role={message.role} />
</ChatMessage.Avatar>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Artifact />
</ChatMessage.Content>
<ChatMessage.Actions />
</ChatMessage>
)
}
```
### Props
```typescript
interface ChatMessageProps {
message: Message
isLast?: boolean
className?: string
children?: React.ReactNode
}
```
### Sub-components
#### ChatMessage.Avatar
User/assistant avatar display:
```tsx
<ChatMessage.Avatar>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-500">
{message.role === 'user' ? 'U' : 'AI'}
</div>
</ChatMessage.Avatar>
```
#### ChatMessage.Content
Main content area with annotation support:
```tsx
<ChatMessage.Content isLoading={isLoading} append={append}>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Image />
<ChatMessage.Content.Artifact />
<ChatMessage.Content.Source />
<ChatMessage.Content.Event />
<ChatMessage.Content.AgentEvent />
<ChatMessage.Content.DocumentFile />
<ChatMessage.Content.SuggestedQuestions />
</ChatMessage.Content>
```
#### ChatMessage.Actions
Message-level actions (copy, regenerate, etc.):
```tsx
<ChatMessage.Actions>
<button onClick={copyMessage}>Copy</button>
<button onClick={regenerate}>Regenerate</button>
</ChatMessage.Actions>
```
### Content Types
The content system supports multiple annotation types:
- **Markdown** - Rich text with LaTeX support
- **Image** - Image display with preview
- **Artifact** - Interactive code/document editing
- **Source** - Citation and source links
- **Event** - Process events and status
- **AgentEvent** - Agent-specific events with progress
- **DocumentFile** - File attachments
- **SuggestedQuestions** - Follow-up question suggestions
## ChatInput
Input component with file upload support and keyboard shortcuts.
### Basic Usage
```tsx
import { ChatInput } from '@llamaindex/chat-ui'
function MessageInput() {
return (
<ChatInput>
<ChatInput.Form>
<ChatInput.Field placeholder="Type your message..." />
<ChatInput.Upload />
<ChatInput.Submit />
</ChatInput.Form>
</ChatInput>
)
}
```
### Sub-components
#### ChatInput.Form
Form wrapper with submit handling:
```tsx
<ChatInput.Form onSubmit={handleSubmit}>
{/* Input components */}
</ChatInput.Form>
```
#### ChatInput.Field
Auto-resizing textarea with keyboard shortcuts:
```tsx
<ChatInput.Field
placeholder="Type your message..."
disabled={isLoading}
className="min-h-12 max-h-32"
/>
```
**Keyboard Shortcuts:**
- `Enter` - Submit message
- `Shift + Enter` - New line
- `Escape` - Clear input
#### ChatInput.Upload
File upload trigger:
```tsx
<ChatInput.Upload>
<button type="button">
<PaperclipIcon className="h-4 w-4" />
</button>
</ChatInput.Upload>
```
#### ChatInput.Submit
Submit button with loading state:
```tsx
<ChatInput.Submit>
<button type="submit" disabled={isLoading}>
<SendIcon className="h-4 w-4" />
</button>
</ChatInput.Submit>
```
### Features
- **Auto-resize** - Textarea grows with content
- **File upload** - Drag & drop or click to upload
- **Keyboard shortcuts** - Intuitive message sending
- **Loading states** - Disabled during processing
- **Validation** - Built-in input validation
## ChatCanvas
Side panel component for displaying interactive artifacts like code and documents.
### Basic Usage
```tsx
import { ChatCanvas } from '@llamaindex/chat-ui'
function ChatWithCanvas() {
return (
<div className="flex h-full">
<div className="flex-1">
<ChatMessages />
<ChatInput />
</div>
<ChatCanvas className="w-1/2" />
</div>
)
}
```
### Features
- **Slide Animation** - Smooth slide-in when artifacts appear
- **Version Management** - Track changes to artifacts
- **Edit Support** - Interactive editing for code and documents
- **Responsive** - Adapts to different screen sizes
### Artifact Types
#### Code Artifacts
```tsx
<ChatCanvas>
<ChatCanvas.CodeArtifact
filename="example.py"
language="python"
code="print('Hello, world!')"
/>
</ChatCanvas>
```
#### Document Artifacts
```tsx
<ChatCanvas>
<ChatCanvas.DocumentArtifact
title="Meeting Notes"
content="# Meeting Summary\n\n..."
/>
</ChatCanvas>
```
## Component Composition
### Full Custom Layout
```tsx
function AdvancedChat() {
const handler = useChat({ api: '/api/chat' })
return (
<ChatSection handler={handler} className="flex h-full">
<div className="flex flex-1 flex-col">
<header className="border-b p-4">
<h1>My Chat App</h1>
</header>
<ChatMessages className="flex-1">
<ChatMessages.List className="p-4">
{/* Custom message rendering */}
</ChatMessages.List>
<ChatMessages.Loading>
<CustomLoader />
</ChatMessages.Loading>
</ChatMessages>
<div className="border-t p-4">
<ChatInput>
<ChatInput.Form className="flex gap-2">
<ChatInput.Field className="flex-1" />
<ChatInput.Upload />
<ChatInput.Submit />
</ChatInput.Form>
</ChatInput>
</div>
</div>
<ChatCanvas className="w-1/2 border-l" />
</ChatSection>
)
}
```
## Context Access
All components have access to chat context through hooks:
```tsx
import { useChatUI, useChatMessage } from '@llamaindex/chat-ui'
function CustomComponent() {
const { messages, isLoading, append } = useChatUI()
const { message } = useChatMessage() // Only in message context
// Component logic
}
```
## Next Steps
- [Widgets](./widgets) - Learn about specialized content widgets
- [Annotations](./annotations) - Implement rich content support
- [Hooks](./hooks) - Understand the hook system
- [Customization](./customization) - Style and theme the components
+718
View File
@@ -0,0 +1,718 @@
---
title: Customization
description: Style and customize the appearance and behavior of chat components
---
LlamaIndex Chat UI is built with customization in mind. You can style components, override default behaviors, and create custom themes to match your application's design system.
## Styling System
The library uses Tailwind CSS for styling and provides several customization approaches:
### CSS Variables
The library exposes CSS variables for easy theming:
```css
/* Custom theme variables */
:root {
--chat-primary: #3b82f6;
--chat-secondary: #6b7280;
--chat-background: #ffffff;
--chat-surface: #f9fafb;
--chat-border: #e5e7eb;
--chat-text: #1f2937;
--chat-text-secondary: #6b7280;
--chat-success: #10b981;
--chat-warning: #f59e0b;
--chat-error: #ef4444;
}
/* Dark theme */
[data-theme="dark"] {
--chat-primary: #60a5fa;
--chat-secondary: #9ca3af;
--chat-background: #111827;
--chat-surface: #1f2937;
--chat-border: #374151;
--chat-text: #f9fafb;
--chat-text-secondary: #d1d5db;
}
```
### Component-Level Styling
Override component styles using className props:
```tsx
import { ChatSection, ChatMessages, ChatInput } from '@llamaindex/chat-ui'
function CustomStyledChat() {
return (
<ChatSection
handler={handler}
className="bg-gradient-to-b from-blue-50 to-white"
>
<ChatMessages className="bg-white/80 backdrop-blur rounded-lg shadow-lg">
<ChatMessages.List className="space-y-6 p-6">
{/* Custom message rendering */}
</ChatMessages.List>
</ChatMessages>
<ChatInput className="bg-white border-2 border-blue-200 rounded-xl p-4">
<ChatInput.Form className="flex items-end gap-3">
<ChatInput.Field
className="flex-1 border-none bg-gray-50 rounded-lg px-4 py-2"
placeholder="Ask me anything..."
/>
<ChatInput.Submit className="bg-blue-600 hover:bg-blue-700 text-white rounded-lg px-4 py-2">
Send
</ChatInput.Submit>
</ChatInput.Form>
</ChatInput>
</ChatSection>
)
}
```
## Message Customization
### Custom Message Layout
```tsx
import { ChatMessage, useChatMessage } from '@llamaindex/chat-ui'
function CustomMessageLayout() {
const { message, isLast } = useChatMessage()
return (
<ChatMessage
message={message}
isLast={isLast}
className={`
${message.role === 'user' ? 'ml-8' : 'mr-8'}
transition-all duration-200 hover:shadow-md
`}
>
<div className={`
flex gap-3 p-4 rounded-2xl
${message.role === 'user'
? 'bg-blue-600 text-white ml-auto'
: 'bg-gray-100 text-gray-900'
}
`}>
<ChatMessage.Avatar>
<CustomAvatar role={message.role} />
</ChatMessage.Avatar>
<div className="flex-1">
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Image />
<ChatMessage.Content.Source />
</ChatMessage.Content>
<ChatMessage.Actions>
<CustomMessageActions />
</ChatMessage.Actions>
</div>
</div>
</ChatMessage>
)
}
function CustomAvatar({ role }: { role: string }) {
const avatarClass = role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-300 text-gray-700'
return (
<div className={`
h-10 w-10 rounded-full flex items-center justify-center
text-sm font-semibold ${avatarClass}
`}>
{role === 'user' ? '👤' : '🤖'}
</div>
)
}
```
### Message Role Styling
```tsx
function RoleBasedMessage() {
const { message } = useChatMessage()
const roleStyles = {
user: {
container: 'justify-end',
bubble: 'bg-blue-600 text-white rounded-l-2xl rounded-tr-2xl',
maxWidth: 'max-w-[80%]'
},
assistant: {
container: 'justify-start',
bubble: 'bg-white border shadow-sm rounded-r-2xl rounded-tl-2xl',
maxWidth: 'max-w-[85%]'
},
system: {
container: 'justify-center',
bubble: 'bg-yellow-50 border border-yellow-200 rounded-lg text-yellow-800',
maxWidth: 'max-w-[70%]'
}
}
const styles = roleStyles[message.role] || roleStyles.assistant
return (
<div className={`flex ${styles.container} mb-4`}>
<div className={`${styles.bubble} ${styles.maxWidth} p-4`}>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
</ChatMessage.Content>
</div>
</div>
)
}
```
## Input Customization
### Custom Input Design
```tsx
import { ChatInput, useChatInput } from '@llamaindex/chat-ui'
function CustomChatInput() {
const { input, setInput, handleSubmit, isLoading } = useChatInput()
return (
<div className="relative border-t bg-white p-4">
<div className="mx-auto max-w-4xl">
<ChatInput>
<ChatInput.Form className="relative flex items-end gap-3">
{/* Custom input field with enhanced styling */}
<div className="relative flex-1">
<ChatInput.Field
className="
w-full resize-none rounded-2xl border border-gray-300
bg-white px-4 py-3 pr-12 text-sm
focus:border-blue-500 focus:outline-none focus:ring-2
focus:ring-blue-500/20 disabled:opacity-50
max-h-32 min-h-[44px]
"
placeholder="Type your message..."
disabled={isLoading}
/>
{/* Character counter */}
<div className="absolute bottom-1 right-12 text-xs text-gray-400">
{input.length}/2000
</div>
</div>
{/* Upload button */}
<ChatInput.Upload>
<button
type="button"
className="
flex h-11 w-11 items-center justify-center rounded-xl
border border-gray-300 bg-white hover:bg-gray-50
transition-colors disabled:opacity-50
"
disabled={isLoading}
>
<PaperclipIcon className="h-5 w-5 text-gray-600" />
</button>
</ChatInput.Upload>
{/* Submit button */}
<ChatInput.Submit>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="
flex h-11 w-11 items-center justify-center rounded-xl
bg-blue-600 text-white hover:bg-blue-700
transition-colors disabled:opacity-50 disabled:cursor-not-allowed
"
>
{isLoading ? (
<LoadingSpinner className="h-5 w-5" />
) : (
<SendIcon className="h-5 w-5" />
)}
</button>
</ChatInput.Submit>
</ChatInput.Form>
</ChatInput>
</div>
</div>
)
}
```
### Input with Suggestions
```tsx
function InputWithSuggestions() {
const [suggestions, setSuggestions] = useState([])
const { input, setInput } = useChatInput()
const commonSuggestions = [
"How can I help you today?",
"Explain this concept",
"Write some code for",
"Summarize this document"
]
const filteredSuggestions = commonSuggestions.filter(s =>
s.toLowerCase().includes(input.toLowerCase()) && input.length > 0
)
return (
<div className="relative">
<ChatInput>
<ChatInput.Form>
<ChatInput.Field
onFocus={() => setSuggestions(filteredSuggestions)}
onBlur={() => setTimeout(() => setSuggestions([]), 150)}
/>
<ChatInput.Submit />
</ChatInput.Form>
</ChatInput>
{suggestions.length > 0 && (
<div className="absolute bottom-full left-0 right-0 mb-2">
<div className="bg-white border rounded-lg shadow-lg max-h-32 overflow-y-auto">
{suggestions.map((suggestion, index) => (
<button
key={index}
className="w-full px-3 py-2 text-left hover:bg-gray-50 text-sm"
onClick={() => {
setInput(suggestion)
setSuggestions([])
}}
>
{suggestion}
</button>
))}
</div>
</div>
)}
</div>
)
}
```
## Canvas Customization
### Custom Canvas Layout
```tsx
import { ChatCanvas, useChatCanvas } from '@llamaindex/chat-ui'
function CustomCanvas() {
const { currentArtifact, isVisible, hideCanvas } = useChatCanvas()
if (!isVisible || !currentArtifact) return null
return (
<div className="w-1/2 bg-gray-50 border-l flex flex-col">
{/* Custom header */}
<div className="flex items-center justify-between p-4 border-b bg-white">
<div>
<h2 className="font-semibold text-gray-900">
{currentArtifact.title}
</h2>
<p className="text-sm text-gray-500">
{currentArtifact.type === 'code' ? 'Code Editor' : 'Document'}
</p>
</div>
<div className="flex items-center gap-2">
<CustomCanvasActions />
<button
onClick={hideCanvas}
className="p-1 hover:bg-gray-100 rounded"
>
<XIcon className="h-5 w-5" />
</button>
</div>
</div>
{/* Custom content area */}
<div className="flex-1 overflow-hidden">
<ChatCanvas className="h-full">
{/* Canvas content renders here */}
</ChatCanvas>
</div>
{/* Custom footer */}
<div className="border-t bg-white p-3">
<div className="flex items-center justify-between text-sm text-gray-500">
<span>
{currentArtifact.type === 'code'
? `${currentArtifact.language} • ${currentArtifact.file_name}`
: 'Markdown Document'
}
</span>
<span>
Last modified: {new Date().toLocaleTimeString()}
</span>
</div>
</div>
</div>
)
}
```
## Widget Styling
### Custom Markdown Styling
```tsx
import { Markdown } from '@llamaindex/chat-ui/widgets'
function CustomMarkdown({ children }: { children: string }) {
return (
<div className="prose prose-sm max-w-none">
<style jsx>{`
.prose {
--tw-prose-body: #374151;
--tw-prose-headings: #111827;
--tw-prose-links: #3b82f6;
--tw-prose-bold: #111827;
--tw-prose-code: #dc2626;
--tw-prose-pre-bg: #f3f4f6;
--tw-prose-pre-code: #374151;
}
.prose code {
background: #f3f4f6;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-size: 0.875em;
}
.prose blockquote {
border-left: 4px solid #3b82f6;
background: #eff6ff;
padding: 1rem;
margin: 1rem 0;
}
`}</style>
<Markdown>{children}</Markdown>
</div>
)
}
```
### Custom Code Block Styling
```tsx
import { CodeBlock } from '@llamaindex/chat-ui/widgets'
function StyledCodeBlock({ code, language, filename }: {
code: string
language: string
filename?: string
}) {
return (
<div className="my-4 overflow-hidden rounded-lg border border-gray-200">
{filename && (
<div className="flex items-center justify-between bg-gray-50 px-4 py-2">
<span className="text-sm font-medium text-gray-700">
{filename}
</span>
<span className="text-xs text-gray-500 uppercase">
{language}
</span>
</div>
)}
<div className="relative">
<CodeBlock
code={code}
language={language}
className="bg-gray-900 text-gray-100"
showLineNumbers={true}
/>
{/* Custom copy button */}
<button
className="absolute top-2 right-2 p-2 bg-gray-800 hover:bg-gray-700 rounded"
onClick={() => navigator.clipboard.writeText(code)}
>
<CopyIcon className="h-4 w-4 text-gray-300" />
</button>
</div>
</div>
)
}
```
## Theme System
### Theme Provider
```tsx
import { createContext, useContext, useState } from 'react'
interface Theme {
mode: 'light' | 'dark'
primaryColor: string
borderRadius: 'none' | 'sm' | 'md' | 'lg'
fontSize: 'sm' | 'base' | 'lg'
}
const ThemeContext = createContext<{
theme: Theme
updateTheme: (updates: Partial<Theme>) => void
}>({
theme: {
mode: 'light',
primaryColor: 'blue',
borderRadius: 'md',
fontSize: 'base'
},
updateTheme: () => {}
})
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>({
mode: 'light',
primaryColor: 'blue',
borderRadius: 'md',
fontSize: 'base'
})
const updateTheme = (updates: Partial<Theme>) => {
setTheme(prev => ({ ...prev, ...updates }))
}
return (
<ThemeContext.Provider value={{ theme, updateTheme }}>
<div
className={`theme-${theme.mode} theme-${theme.primaryColor}`}
data-theme={theme.mode}
data-radius={theme.borderRadius}
data-font-size={theme.fontSize}
>
{children}
</div>
</ThemeContext.Provider>
)
}
export const useTheme = () => useContext(ThemeContext)
```
### Themed Components
```tsx
function ThemedChatSection() {
const { theme } = useTheme()
const handler = useChat({ api: '/api/chat' })
const themeClasses = {
light: 'bg-white text-gray-900',
dark: 'bg-gray-900 text-white'
}
const radiusClasses = {
none: 'rounded-none',
sm: 'rounded-sm',
md: 'rounded-md',
lg: 'rounded-lg'
}
const fontSizeClasses = {
sm: 'text-sm',
base: 'text-base',
lg: 'text-lg'
}
return (
<ChatSection
handler={handler}
className={`
${themeClasses[theme.mode]}
${fontSizeClasses[theme.fontSize]}
transition-all duration-200
`}
>
<ChatMessages
className={`
${radiusClasses[theme.borderRadius]}
border border-current/10
`}
/>
<ChatInput
className={`
${radiusClasses[theme.borderRadius]}
border border-current/20
`}
/>
</ChatSection>
)
}
```
## Animation and Transitions
### Message Animations
```tsx
import { motion, AnimatePresence } from 'framer-motion'
function AnimatedMessages() {
const { messages } = useChatUI()
return (
<AnimatePresence>
{messages.map((message, index) => (
<motion.div
key={message.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3, delay: index * 0.1 }}
>
<ChatMessage message={message}>
{/* Message content */}
</ChatMessage>
</motion.div>
))}
</AnimatePresence>
)
}
```
### Typing Indicator
```tsx
function TypingIndicator() {
const { isLoading } = useChatUI()
if (!isLoading) return null
return (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
className="flex items-center gap-2 p-4"
>
<div className="flex gap-1">
{[0, 1, 2].map((i) => (
<motion.div
key={i}
className="h-2 w-2 bg-gray-400 rounded-full"
animate={{
scale: [1, 1.2, 1],
opacity: [0.5, 1, 0.5]
}}
transition={{
duration: 1.5,
repeat: Infinity,
delay: i * 0.2
}}
/>
))}
</div>
<span className="text-sm text-gray-500">AI is typing...</span>
</motion.div>
)
}
```
## Responsive Design
### Mobile-First Layout
```tsx
function ResponsiveChatLayout() {
const [isMobile, setIsMobile] = useState(false)
useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768)
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
return (
<ChatSection
handler={handler}
className={isMobile ? 'flex-col h-full' : 'flex-row h-full'}
>
<div className={isMobile ? 'flex-1' : 'flex-1 flex flex-col'}>
<ChatMessages className={isMobile ? 'flex-1' : 'flex-1'} />
<ChatInput className={isMobile ? 'sticky bottom-0' : ''} />
</div>
{!isMobile && <ChatCanvas className="w-1/2" />}
</ChatSection>
)
}
```
## Accessibility Customization
### Enhanced Accessibility
```tsx
function AccessibleChat() {
const { messages, isLoading } = useChatUI()
return (
<div
role="log"
aria-live="polite"
aria-label="Chat conversation"
className="relative"
>
<ChatMessages>
<ChatMessages.List
role="log"
aria-busy={isLoading}
>
{messages.map((message, index) => (
<div
key={message.id}
role="article"
aria-label={`Message from ${message.role}`}
tabIndex={0}
className="focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<ChatMessage message={message} />
</div>
))}
</ChatMessages.List>
</ChatMessages>
<ChatInput>
<ChatInput.Form>
<ChatInput.Field
aria-label="Type your message"
aria-describedby="chat-input-help"
/>
<div id="chat-input-help" className="sr-only">
Press Enter to send, Shift+Enter for new line
</div>
<ChatInput.Submit aria-label="Send message" />
</ChatInput.Form>
</ChatInput>
</div>
)
}
```
## Next Steps
- [Examples](./examples) - See complete customization examples
- [Core Components](./core-components) - Understand component structure for customization
- [Widgets](./widgets) - Customize widget appearance and behavior
- [Hooks](./hooks) - Use hooks for dynamic customization
+17
View File
@@ -0,0 +1,17 @@
---
title: Examples
description: Real-world implementation examples and use cases
---
This section provides complete, working examples that demonstrate how to implement various features and use cases with LlamaIndex Chat UI.
## Examples
[Examples](https://github.com/run-llama/chat-ui/tree/main/examples)
## Next Steps
- Explore the [NextJS Example](https://github.com/run-llama/chat-ui/tree/main/examples/nextjs) in the repository
- Check out the [FastAPI Example](https://github.com/run-llama/chat-ui/tree/main/examples/fastapi) for backend integration
- Review the [Core Components](./core-components) documentation for detailed API references
- See [Customization](./customization) for styling and theming options
+282
View File
@@ -0,0 +1,282 @@
---
title: Getting Started
description: Learn how to install and set up LlamaIndex Chat UI in your project
---
This guide will help you get started with LlamaIndex Chat UI, from installation to building your first chat interface.
## Quick Start
The fastest way to add a chatbot to your project is using the Shadcn CLI command:
```shell
npx shadcn@latest add https://ui.llamaindex.ai/r/chat.json
```
## Manual Installation
Install the package using your preferred package manager:
```shell
npm install @llamaindex/chat-ui
yarn add @llamaindex/chat-ui
pnpm add @llamaindex/chat-ui
bun add @llamaindex/chat-ui
```
## Peer Dependencies
Chat UI requires React 18+:
```shell
npm install react react-dom
```
## Basic Setup
### 1. Configure Tailwind CSS
**For Tailwind CSS version 4.x**, update `globals.css` to include the chat-ui components (update the relative path to node_modules if necessary):
```css
@source '../node_modules/@llamaindex/chat-ui/**/*.{ts,tsx}';
```
**For Tailwind CSS version 3.x**, add the following to your `tailwind.config.ts` file:
```javascript
// tailwind.config.js
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./node_modules/@llamaindex/chat-ui/**/*.{ts,tsx}',
],
// ... rest of config
}
```
### 2. Import Styles
Import the CSS styles in your app's root file (e.g., `_app.tsx` or `layout.tsx`):
```tsx
import '@llamaindex/chat-ui/styles/markdown.css' // code, latex and custom markdown styling
import '@llamaindex/chat-ui/styles/pdf.css' // pdf styling
```
The `markdown.css` file includes styling for code blocks using [highlight.js](https://highlightjs.org/) with the `atom-one-dark` theme by default, [katex](https://katex.org/) for latex, and [pdf-viewer](https://github.com/run-llama/pdf-viewer) for PDF files. You can use any highlight.js theme by copying [their CSS](https://github.com/highlightjs/highlight.js/tree/main/src/styles/) to your project and importing it.
### 3. Create a Chat API Route
Set up an API route to handle chat requests. Here's an example using Next.js:
```typescript
// app/api/chat/route.ts
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
const { messages } = await request.json()
// Your chat logic here
const response = await generateChatResponse(messages)
return new Response(response, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'X-Vercel-AI-Data-Stream': 'v1',
},
})
}
```
### 4. Create Your Chat Component
The easiest way to get started is to connect the whole `ChatSection` component with `useChat` hook from [vercel/ai](https://github.com/vercel/ai):
```tsx
'use client'
import { ChatSection } from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
export default function Chat() {
const handler = useChat({
api: '/api/chat',
})
return (
<div className="h-screen">
<ChatSection handler={handler} />
</div>
)
}
```
## Component Composition
Components are designed to be composable. You can use them as is with the simple `ChatSection`, or extend them with your own children components:
```tsx
import { ChatSection, ChatMessages, ChatInput } from '@llamaindex/chat-ui'
import LlamaCloudSelector from './components/LlamaCloudSelector' // your custom component
import { useChat } from 'ai/react'
const ChatExample = () => {
const handler = useChat()
return (
<ChatSection handler={handler}>
<ChatMessages />
<ChatInput>
<ChatInput.Form className="bg-lime-500">
<ChatInput.Field type="textarea" />
<ChatInput.Upload />
<LlamaCloudSelector /> {/* custom component */}
<ChatInput.Submit />
</ChatInput.Form>
</ChatInput>
</ChatSection>
)
}
```
Your custom component can use the `useChatUI` hook to send additional data to the chat API endpoint:
```tsx
import { useChatUI } from '@llamaindex/chat-ui'
const LlamaCloudSelector = () => {
const { requestData, setRequestData } = useChatUI()
return (
<div>
<select
value={requestData?.model}
onChange={e => setRequestData({ model: e.target.value })}
>
<option value="llama-3.1-70b-instruct">Pipeline 1</option>
<option value="llama-3.1-8b-instruct">Pipeline 2</option>
</select>
</div>
)
}
```
## Styling
### Components
`chat-ui` components are based on [shadcn](https://ui.shadcn.com/) components using Tailwind CSS.
You can override the default styles by changing CSS variables in the `globals.css` file of your Tailwind CSS configuration. For example, to change the background and foreground colors:
```css
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
}
```
For a list of all available CSS variables, please refer to the [Shadcn Theme Config](https://ui.shadcn.com/themes).
Additionally, you can also override each component's styles by setting custom classes in the `className` prop. For example, setting the background color of the `ChatInput.Form` component:
```tsx
import { ChatSection, ChatMessages, ChatInput } from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
const ChatExample = () => {
const handler = useChat()
return (
<ChatSection handler={handler}>
<ChatMessages />
<ChatInput>
<ChatInput.Preview />
<ChatInput.Form className="bg-lime-500">
<ChatInput.Field type="textarea" />
<ChatInput.Upload />
<ChatInput.Submit />
</ChatInput.Form>
</ChatInput>
</ChatSection>
)
}
```
## Advanced Features
### Custom Layout
For more control over the layout, compose components manually:
```tsx
import {
ChatSection,
ChatMessages,
ChatInput,
ChatCanvas,
} from '@llamaindex/chat-ui'
function CustomChat() {
const handler = useChat({ api: '/api/chat' })
return (
<ChatSection handler={handler} className="flex-row gap-4">
<div className="flex-1">
<ChatMessages />
<ChatInput />
</div>
<ChatCanvas className="w-1/3" />
</ChatSection>
)
}
```
### With Initial Messages
Provide initial context or welcome messages:
```tsx
const handler = useChat({
api: '/api/chat',
initialMessages: [
{
id: '1',
role: 'assistant',
content: 'Hello! How can I help you today?',
},
],
})
```
### Language Renderer Support
For any language that the LLM generates, you can specify a custom renderer to render the output. For example, you can render mermaid code as SVG using a custom renderer.
## Next Steps
Now that you have a basic chat interface running:
1. **Explore Components** - Learn about [Core Components](./core-components) for customization
2. **Add Rich Content** - Implement [Annotations](./annotations) for images, files, and sources
3. **Enable Artifacts** - Set up [Artifacts](./artifacts) for interactive code and documents
4. **Customize Styling** - Read the [Customization](./customization) guide for theming
## Troubleshooting
### Common Issues
**Styles not loading**: Make sure you've imported the CSS files in your app root and configured Tailwind CSS properly.
**TypeScript errors**: Ensure you have the correct peer dependencies and TypeScript configuration.
**Build errors**: Check that your bundler supports the package's export conditions.
**Chat not working**: Verify your API route is returning the correct response format for the Vercel AI SDK.
### Getting Help
- Check the [Examples](./examples) for working implementations
- Review the component documentation for detailed API references
- Open an issue on GitHub for bugs or feature requests
+504
View File
@@ -0,0 +1,504 @@
---
title: Hooks
description: Understanding the hook system for accessing chat state and functionality
---
The hook system provides access to chat state, context, and functionality throughout the component tree. These hooks enable deep customization and integration with the chat interface.
## Context Hooks
### useChatUI
The primary hook for accessing chat state and handlers to modify the chat state.
```tsx
import { useChatUI } from '@llamaindex/chat-ui'
function CustomChatComponent() {
const {
messages,
input,
setInput,
isLoading,
error,
append,
reload,
stop,
setMessages,
requestData,
setRequestData,
} = useChatUI()
const handleSendMessage = async () => {
await append({
role: 'user',
content: input,
})
}
return (
<div>
<p>Messages: {messages.length}</p>
<p>Loading: {isLoading ? 'Yes' : 'No'}</p>
{error && <p>Error: {error.message}</p>}
</div>
)
}
```
**Returned Properties:**
- `messages` - Array of chat messages
- `input` - Current input value
- `setInput` - Function to update input
- `isLoading` - Loading state boolean
- `error` - Error object if any
- `append` - Function to add message
- `reload` - Function to reload last message
- `stop` - Function to stop current generation
- `setMessages` - Function to update message array
- `requestData` - Additional request data
- `setRequestData` - Function to update request data
### useChatMessage
Access the current message context within message components.
```tsx
import { useChatMessage } from '@llamaindex/chat-ui'
function CustomMessageContent() {
const { message, isLast } = useChatMessage()
return (
<div>
<p>Role: {message.role}</p>
<p>Content: {message.content}</p>
<p>Is last message: {isLast ? 'Yes' : 'No'}</p>
{message.annotations && (
<p>Has annotations: {message.annotations.length}</p>
)}
</div>
)
}
```
**Returned Properties:**
- `message` - Current message object
- `isLast` - Boolean indicating if this is the last message
### useChatInput
Access input form state and handlers.
```tsx
import { useChatInput } from '@llamaindex/chat-ui'
function CustomInputField() {
const {
input,
setInput,
handleSubmit,
isLoading,
onFileUpload,
uploadedFiles,
} = useChatInput()
return (
<form onSubmit={handleSubmit}>
<textarea
value={input}
onChange={e => setInput(e.target.value)}
disabled={isLoading}
placeholder="Type your message..."
/>
<p>Uploaded files: {uploadedFiles.length}</p>
</form>
)
}
```
**Returned Properties:**
- `input` - Current input value
- `setInput` - Function to update input
- `handleSubmit` - Form submit handler
- `isLoading` - Loading state
- `onFileUpload` - File upload handler
- `uploadedFiles` - Array of uploaded files
### useChatMessages
Access messages list state and handlers.
```tsx
import { useChatMessages } from '@llamaindex/chat-ui'
function CustomMessageList() {
const { messages, isLoading, reload, stop, isEmpty, scrollToBottom } =
useChatMessages()
return (
<div>
<div className="messages">
{messages.map((msg, i) => (
<div key={i}>{msg.content}</div>
))}
</div>
{isEmpty && <p>No messages yet</p>}
<button onClick={reload} disabled={isLoading}>
Reload
</button>
<button onClick={stop}>Stop</button>
<button onClick={scrollToBottom}>Scroll to Bottom</button>
</div>
)
}
```
**Returned Properties:**
- `messages` - Array of messages
- `isLoading` - Loading state
- `reload` - Reload last message
- `stop` - Stop generation
- `isEmpty` - Boolean if no messages
- `scrollToBottom` - Function to scroll to bottom
### useChatCanvas
Access artifact canvas state and handlers.
```tsx
import { useChatCanvas } from '@llamaindex/chat-ui'
function CustomCanvasControls() {
const {
currentArtifact,
isVisible,
showCanvas,
hideCanvas,
updateArtifact,
artifacts,
} = useChatCanvas()
return (
<div>
<p>Canvas visible: {isVisible ? 'Yes' : 'No'}</p>
<p>Current artifact: {currentArtifact?.title || 'None'}</p>
<p>Total artifacts: {artifacts.length}</p>
<button onClick={showCanvas}>Show Canvas</button>
<button onClick={hideCanvas}>Hide Canvas</button>
</div>
)
}
```
**Returned Properties:**
- `currentArtifact` - Currently displayed artifact
- `isVisible` - Canvas visibility state
- `showCanvas` - Function to show canvas
- `hideCanvas` - Function to hide canvas
- `updateArtifact` - Function to update artifact
- `artifacts` - Array of all artifacts
## Utility Hooks
### useFile
Handle file uploads and processing.
```tsx
import { useFile } from '@llamaindex/chat-ui'
function FileUploadComponent() {
const {
uploadFile,
uploadFiles,
isUploading,
uploadedFiles,
removeFile,
clearFiles,
} = useFile({
maxSize: 10 * 1024 * 1024, // 10MB
accept: ['image/*', 'application/pdf'],
multiple: true,
})
const handleFileSelect = async (files: FileList) => {
await uploadFiles(Array.from(files))
}
return (
<div>
<input
type="file"
multiple
onChange={e => handleFileSelect(e.target.files!)}
/>
{isUploading && <p>Uploading...</p>}
<div>
{uploadedFiles.map((file, i) => (
<div key={i}>
<span>{file.name}</span>
<button onClick={() => removeFile(i)}>Remove</button>
</div>
))}
</div>
<button onClick={clearFiles}>Clear All</button>
</div>
)
}
```
**Options:**
- `maxSize` - Maximum file size in bytes
- `accept` - Array of accepted MIME types
- `multiple` - Allow multiple files
- `onUpload` - Upload completion callback
- `onError` - Error callback
**Returned Properties:**
- `uploadFile` - Upload single file
- `uploadFiles` - Upload multiple files
- `isUploading` - Upload state
- `uploadedFiles` - Array of uploaded files
- `removeFile` - Remove file by index
- `clearFiles` - Clear all files
### useCopyToClipboard
Copy content to clipboard with feedback.
```tsx
import { useCopyToClipboard } from '@llamaindex/chat-ui'
function CopyButton({ content }) {
const { copyToClipboard, isCopied } = useCopyToClipboard({
timeout: 2000, // Reset after 2 seconds
})
const handleCopy = () => {
copyToClipboard(content)
}
return (
<button
onClick={handleCopy}
className={isCopied ? 'text-green-600' : 'text-gray-600'}
>
{isCopied ? 'Copied!' : 'Copy'}
</button>
)
}
```
**Options:**
- `timeout` - Time before resetting copied state
**Returned Properties:**
- `copyToClipboard` - Function to copy text
- `isCopied` - Boolean indicating if recently copied
## Hook Composition
### Custom Chat Hook
Create custom hooks by composing existing ones:
```tsx
import { useChatUI, useChatMessages } from '@llamaindex/chat-ui'
function useCustomChat() {
const chatUI = useChatUI()
const messages = useChatMessages()
const sendMessageWithMetadata = async (content: string, metadata: any) => {
await chatUI.append(
{
role: 'user',
content,
},
{ data: metadata }
)
}
const getLastAssistantMessage = () => {
return chatUI.messages.filter(m => m.role === 'assistant').pop()
}
return {
...chatUI,
...messages,
sendMessageWithMetadata,
getLastAssistantMessage,
}
}
```
### Message Analytics Hook
Track message interactions:
```tsx
import { useChatMessage, useChatUI } from '@llamaindex/chat-ui'
function useMessageAnalytics() {
const { message } = useChatMessage()
const { requestData, setRequestData } = useChatUI()
const trackMessageView = () => {
// Track message viewed
console.log('Message viewed:', message.id)
}
const trackMessageCopy = () => {
// Track message copied
setRequestData({
...requestData,
copiedMessages: [...(requestData?.copiedMessages || []), message.id],
})
}
const trackMessageRegenerate = () => {
// Track message regenerated
console.log('Message regenerated:', message.id)
}
return {
trackMessageView,
trackMessageCopy,
trackMessageRegenerate,
}
}
```
## Hook Usage Patterns
### Conditional Rendering
```tsx
function ConditionalComponent() {
const { isLoading, error, messages } = useChatUI()
if (isLoading) return <LoadingSpinner />
if (error) return <ErrorMessage error={error} />
if (messages.length === 0) return <EmptyState />
return <MessageList />
}
```
### Event Handling
```tsx
function EventHandler() {
const { append, setRequestData } = useChatUI()
const handleQuestionClick = async (question: string) => {
// Track question selection
setRequestData({ selectedQuestion: question })
// Send message
await append({
role: 'user',
content: question,
})
}
return <SuggestedQuestions onQuestionSelect={handleQuestionClick} />
}
```
### Data Transformation
```tsx
function MessageStats() {
const { messages } = useChatUI()
const stats = useMemo(() => {
const userMessages = messages.filter(m => m.role === 'user').length
const assistantMessages = messages.filter(
m => m.role === 'assistant'
).length
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0)
return { userMessages, assistantMessages, totalChars }
}, [messages])
return (
<div>
<p>User messages: {stats.userMessages}</p>
<p>Assistant messages: {stats.assistantMessages}</p>
<p>Total characters: {stats.totalChars}</p>
</div>
)
}
```
## Hook Context Boundaries
### Context Availability
Different hooks are available in different contexts:
```tsx
function App() {
return (
<ChatSection handler={handler}>
{/* useChatUI available here */}
<ChatMessages>
{/* useChatMessages available here */}
<ChatMessages.List>
{messages.map(message => (
<ChatMessage key={message.id} message={message}>
{/* useChatMessage available here */}
<CustomMessageContent />
</ChatMessage>
))}
</ChatMessages.List>
</ChatMessages>
<ChatInput>
{/* useChatInput available here */}
<CustomInputField />
</ChatInput>
</ChatSection>
)
}
```
### Error Boundaries
Handle hook errors gracefully:
```tsx
function SafeHookUsage() {
try {
const { messages } = useChatUI()
return <MessageList messages={messages} />
} catch (error) {
console.error('Hook error:', error)
return <ErrorFallback />
}
}
```
## Next Steps
- [Annotations](./annotations) - Learn how hooks work with annotation data
- [Customization](./customization) - Use hooks for custom styling and behavior
- [Examples](./examples) - See complete examples using hooks
- [Core Components](./core-components) - Understand component-hook relationships
+89
View File
@@ -0,0 +1,89 @@
---
title: LlamaIndex Chat UI
description: A React component library for building modern chat interfaces with LLM applications
---
LlamaIndex Chat UI is a comprehensive React component library designed for building sophisticated chat interfaces in LLM applications. It provides pre-built, customizable components that integrate seamlessly with any backend by using the Vercel's [Streaming Protocol](https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol).
## Key Features
- **Complete Chat Interface** - Full-featured chat components with message history, input, and OpenAI-style canvas
- **Rich Annotations** - Support for images, files, sources, events, and custom annotations
- **Interactive Artifacts** - Code and document artifacts with editing and version management
- **File Upload Support** - Built-in handling for multiple file types (PDF, images, documents)
- **Beautiful** - Built on shadcn/ui for beautiful UI
- **Composable Architecture** - Highly customizable components that can be composed together
- **TypeScript First** - Full type safety and excellent developer experience
- **Vercel Streaming Protocol** - Designed to work seamlessly with `useChat` hook in the frontend and any backend that implements the protocol
## Core Components
- **ChatSection** - Main container that provides context and layout
- **ChatMessages** - Scrollable message history with loading states
- **ChatMessage** - Individual message display with role-based styling
- **ChatInput** - Input interface with file upload and keyboard shortcuts
- **ChatCanvas** - Side panel for displaying interactive code or document artifacts
## Widget System
The library includes a comprehensive widget system for handling various content types:
- **Content Widgets** - Markdown, code blocks, image display
- **Annotation Widgets** - Sources, events, suggested questions
- **Interactive Widgets** - File upload, document editing, code editing
## Getting Started
```shell
npm install @llamaindex/chat-ui
yarn add @llamaindex/chat-ui
pnpm add @llamaindex/chat-ui
```
## Quick Example
```tsx
import { ChatSection } from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
export default function MyChat() {
const handler = useChat({
api: '/api/chat',
})
return <ChatSection handler={handler} />
}
```
This creates a complete chat interface with:
- Message history display
- User input with file upload
- Loading states and error handling
- Support for rich content and annotations
## Architecture
The library follows a composable architecture where you can:
1. **Use the complete solution** - `ChatSection` provides everything out of the box
2. **Compose custom layouts** - Mix and match components for custom designs
3. **Extend with widgets** - Add specialized content handling
4. **Create custom annotations** - Build domain-specific content types
## Integration
Chat UI is designed to work with any chat backend that implements Vercel's [Streaming Protocol](https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol), to simplify getting started we provide examples for:
- **NextJS** - [NextJS Example](https://github.com/run-llama/chat-ui/tree/main/examples/nextjs)
- **FastAPI** - [FastAPI Example](https://github.com/run-llama/chat-ui/tree/main/examples/fastapi)
## Next Steps
- [Getting Started](./getting-started) - Installation and basic setup
- [Core Components](./core-components) - Detailed guide to main components
- [Widgets](./widgets) - Comprehensive widget documentation
- [Annotations](./annotations) - Working with rich content
- [Examples](./examples) - Real-world implementation examples
+17
View File
@@ -0,0 +1,17 @@
{
"title": "Chat UI",
"description": "React component library for building chat interfaces",
"root": true,
"defaultOpen": true,
"pages": [
"index",
"getting-started",
"core-components",
"widgets",
"hooks",
"annotations",
"artifacts",
"customization",
"examples"
]
}
+535
View File
@@ -0,0 +1,535 @@
---
title: Widgets
description: Comprehensive guide to specialized content widgets for rich chat experiences
---
Widgets are specialized components for displaying and interacting with different types of content in chat messages. They provide rich functionality beyond simple text, enabling multimedia, interactive elements, and custom annotations.
## Content Widgets
### Markdown
Renders rich text with LaTeX math support, syntax highlighting, and citations.
```tsx
import { Markdown } from '@llamaindex/chat-ui/widgets'
function RichText({ content }) {
return (
<Markdown>
{content}
</Markdown>
)
}
```
**Features:**
- **LaTeX Math** - Inline and block math rendering with KaTeX
- **Code Highlighting** - Syntax highlighting with highlight.js
- **Citations** - Clickable citation links
- **Custom Renderers** - Extensible rendering pipeline
**Example Content:**
```markdown
# Mathematical Formula
The quadratic formula is: $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$
## Code Example
```python
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
```
This algorithm has O(2^n) complexity [^1].
[^1]: Introduction to Algorithms, Chapter 15
```
### CodeBlock
Displays syntax-highlighted code with copy functionality.
```tsx
import { CodeBlock } from '@llamaindex/chat-ui/widgets'
function CodeDisplay({ code, language, filename }) {
return (
<CodeBlock
code={code}
language={language}
filename={filename}
showLineNumbers={true}
/>
)
}
```
**Props:**
- `code` - Source code string
- `language` - Programming language for highlighting
- `filename` - Optional filename display
- `showLineNumbers` - Show/hide line numbers
### CodeEditor
Interactive code editor with multiple language support.
```tsx
import { CodeEditor } from '@llamaindex/chat-ui/widgets'
function InteractiveCode({ initialCode, language, onChange }) {
return (
<CodeEditor
value={initialCode}
language={language}
onChange={onChange}
theme="dark"
extensions={['search', 'fold']}
/>
)
}
```
**Supported Languages:**
- JavaScript/TypeScript
- Python
- CSS/SCSS
- HTML
- JSON
- Markdown
**Features:**
- **Syntax Highlighting** - Real-time highlighting
- **Auto-completion** - Language-aware suggestions
- **Code Folding** - Collapse code blocks
- **Search & Replace** - Built-in search functionality
- **Multiple Themes** - Light and dark themes
### DocumentEditor
Rich text document editor with markdown support.
```tsx
import { DocumentEditor } from '@llamaindex/chat-ui/widgets'
function DocumentEdit({ content, onChange, title }) {
return (
<DocumentEditor
value={content}
onChange={onChange}
title={title}
placeholder="Start writing..."
/>
)
}
```
**Features:**
- **WYSIWYG Editing** - Visual editing with markdown output
- **Formatting Tools** - Bold, italic, lists, headers
- **Link Support** - Insert and edit links
- **Image Support** - Embed images
- **Live Preview** - Real-time markdown preview
## Annotation Widgets
### ChatImage
Displays images with preview and zoom functionality.
```tsx
import { ChatImage } from '@llamaindex/chat-ui/widgets'
// From annotation data
const imageAnnotation = {
type: 'IMAGE',
data: {
url: 'https://example.com/image.jpg',
alt: 'Description of the image'
}
}
function ImageDisplay() {
return <ChatImage /> // Automatically renders from context
}
```
**Features:**
- **Zoom & Pan** - Interactive image viewing
- **Lazy Loading** - Performance optimization
- **Alt Text** - Accessibility support
- **Error Handling** - Graceful fallback for broken images
### ChatFiles
Displays file attachments with download and preview.
```tsx
import { ChatFiles } from '@llamaindex/chat-ui/widgets'
// From annotation data
const fileAnnotation = {
type: 'DOCUMENT_FILE',
data: {
files: [
{
id: '1',
name: 'report.pdf',
type: 'application/pdf',
url: '/files/report.pdf',
size: 1024000
}
]
}
}
function FileDisplay() {
return <ChatFiles /> // Automatically renders from context
}
```
**Supported File Types:**
- **PDF** - Inline viewer
- **Images** - Thumbnail preview
- **Text Files** - Content preview
- **CSV** - Data table preview
- **Word Documents** - Document preview
### ChatSources
Displays source citations with document grouping.
```tsx
import { ChatSources } from '@llamaindex/chat-ui/widgets'
// From annotation data
const sourceAnnotation = {
type: 'sources',
data: {
nodes: [
{
id: '1',
url: '/documents/paper.pdf',
metadata: {
title: 'Research Paper',
page_number: 5
}
}
]
}
}
function SourceDisplay() {
return <ChatSources /> // Automatically renders from context
}
```
**Features:**
- **Document Grouping** - Groups citations by document
- **Page Numbers** - Shows specific page references
- **Click to View** - Opens source documents
- **Metadata Display** - Shows title, author, date
### ChatEvents
Displays collapsible process events and status updates.
```tsx
import { ChatEvents } from '@llamaindex/chat-ui/widgets'
// From annotation data
const eventAnnotation = {
type: 'events',
data: [
{
type: 'function_call',
name: 'search_documents',
args: { query: 'machine learning' },
result: 'Found 15 relevant documents'
}
]
}
function EventDisplay() {
return <ChatEvents /> // Automatically renders from context
}
```
### ChatAgentEvents
Displays agent-specific events with progress tracking.
```tsx
import { ChatAgentEvents } from '@llamaindex/chat-ui/widgets'
// From annotation data
const agentAnnotation = {
type: 'agent_events',
data: {
agent_name: 'Research Assistant',
progress: 75,
current_step: 'Analyzing documents',
events: [
{ step: 'Searching', status: 'completed' },
{ step: 'Analyzing', status: 'in_progress' },
{ step: 'Summarizing', status: 'pending' }
]
}
}
function AgentEventDisplay() {
return <ChatAgentEvents /> // Automatically renders from context
}
```
### SuggestedQuestions
Interactive follow-up question suggestions.
```tsx
import { SuggestedQuestions } from '@llamaindex/chat-ui/widgets'
// From annotation data
const suggestionsAnnotation = {
type: 'suggested_questions',
data: {
questions: [
'Can you explain this in more detail?',
'What are the practical applications?',
'How does this compare to other approaches?'
]
}
}
function QuestionSuggestions() {
return <SuggestedQuestions /> // Automatically renders from context
}
```
### StarterQuestions
Initial conversation starters for empty chat states.
```tsx
import { StarterQuestions } from '@llamaindex/chat-ui/widgets'
const starterQuestions = [
'How can I improve my code?',
'Explain machine learning concepts',
'Help me debug this error',
'What are best practices for React?'
]
function ChatStarters() {
return (
<StarterQuestions
questions={starterQuestions}
onQuestionSelect={handleQuestionSelect}
/>
)
}
```
## Utility Widgets
### FileUploader
Drag-and-drop file upload with validation.
```tsx
import { FileUploader } from '@llamaindex/chat-ui/widgets'
function UploadArea({ onFilesSelected }) {
return (
<FileUploader
accept={{
'image/*': ['.png', '.jpg', '.jpeg'],
'application/pdf': ['.pdf'],
'text/*': ['.txt', '.md']
}}
maxSize={10 * 1024 * 1024} // 10MB
onFiles={onFilesSelected}
multiple={true}
>
<div className="border-2 border-dashed p-8 text-center">
<p>Drag files here or click to upload</p>
</div>
</FileUploader>
)
}
```
**Features:**
- **Drag & Drop** - Intuitive file selection
- **File Validation** - Type and size checking
- **Multiple Files** - Batch upload support
- **Progress Tracking** - Upload progress display
- **Error Handling** - Validation error messages
### ImagePreview
Image preview with zoom and manipulation.
```tsx
import { ImagePreview } from '@llamaindex/chat-ui/widgets'
function PreviewImage({ src, alt }) {
return (
<ImagePreview
src={src}
alt={alt}
className="max-w-md"
enableZoom={true}
showControls={true}
/>
)
}
```
### DocumentInfo
Document metadata display with formatting.
```tsx
import { DocumentInfo } from '@llamaindex/chat-ui/widgets'
function DocInfo({ document }) {
return (
<DocumentInfo
title={document.title}
author={document.author}
date={document.date}
pages={document.pages}
size={document.size}
/>
)
}
```
### Citation
Individual citation component with linking.
```tsx
import { Citation } from '@llamaindex/chat-ui/widgets'
function CitationLink({ source, index }) {
return (
<Citation
number={index + 1}
title={source.title}
url={source.url}
metadata={source.metadata}
onClick={() => openSource(source)}
/>
)
}
```
## Widget Integration
### Automatic Rendering
Widgets automatically render based on message annotations:
```tsx
import { ChatMessage } from '@llamaindex/chat-ui'
function MessageWithWidgets({ message }) {
return (
<ChatMessage message={message}>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Image /> {/* Renders ChatImage */}
<ChatMessage.Content.Source /> {/* Renders ChatSources */}
<ChatMessage.Content.Event /> {/* Renders ChatEvents */}
</ChatMessage.Content>
</ChatMessage>
)
}
```
### Manual Widget Usage
Use widgets independently for custom layouts:
```tsx
import {
Markdown,
CodeBlock,
ChatImage,
SuggestedQuestions
} from '@llamaindex/chat-ui/widgets'
function CustomMessageLayout({ message }) {
return (
<div className="space-y-4">
<Markdown>{message.content}</Markdown>
{message.code && (
<CodeBlock
code={message.code}
language="python"
/>
)}
{message.imageUrl && (
<ChatImage
src={message.imageUrl}
alt="Generated image"
/>
)}
<SuggestedQuestions
questions={message.suggestions}
/>
</div>
)
}
```
### Custom Widget Creation
Create custom widgets by following the pattern:
```tsx
import { useChatMessage, getCustomAnnotations } from '@llamaindex/chat-ui'
interface WeatherData {
location: string
temperature: number
condition: string
}
function CustomWeatherWidget() {
const { message } = useChatMessage()
const weatherData = getCustomAnnotations<WeatherData>(
message.annotations,
'weather'
)
if (!weatherData[0]) return null
const data = weatherData[0]
return (
<div className="rounded-lg bg-blue-50 p-4">
<h3 className="font-semibold">{data.location}</h3>
<p className="text-2xl">{data.temperature}°C</p>
<p className="text-sm text-gray-600">{data.condition}</p>
</div>
)
}
```
## Next Steps
- [Annotations](./annotations) - Learn how to create and send annotation data
- [Artifacts](./artifacts) - Implement interactive code and document artifacts
- [Hooks](./hooks) - Understand the widget hook system
- [Customization](./customization) - Style and customize widget appearance
+10
View File
@@ -0,0 +1,10 @@
{
"name": "@llamaindex/chat-ui-docs",
"version": "0.0.1",
"files": [
"chat-ui"
],
"publishConfig": {
"access": "public"
}
}
+1
View File
@@ -2,3 +2,4 @@ packages:
- "apps/*"
- "packages/*"
- "examples/**"
- "docs/*"