Skip to content

Commit 737d022

Browse files
committed
feat: add new UI components and utilities
- Introduced `Avatar` component for user profile images with fallback initials and size variations. - Added `Checkbox` component with controlled and uncontrolled states, including a check icon. - Created `Divider` component for visual separation with optional labels and orientations. - Implemented `Label` component for form labels with customizable styles. - Developed `Container`, `Section`, `Stack`, and `Grid` components for layout management with responsive design. - Added `Link` component with support for external links and different variants. - Created `Popover` component for hover cards with customizable content. - Implemented `RadioGroup` and `RadioGroupItem` components for grouped radio buttons with context management. - Developed `Sheet` component for modal-like overlays with customizable content and headers. - Added `Skeleton` components for loading states with various styles. - Created `Switch` component for toggle functionality with visual feedback. - Implemented `Tabs` component for tabbed navigation with context management. - Added `ThemeToggle` component for switching between light and dark themes. - Created `Typography` components including `Heading`, `Text`, `Prose`, `Code`, and `Kbd` for consistent text styling.
1 parent 621d44e commit 737d022

37 files changed

Lines changed: 2680 additions & 445 deletions

app/chat/AGENTS.md

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<!-- AGENTS-META {"title":"Chat App","version":"1.0.0","applies_to":"app/chat/","last_updated":"2025-11-29T00:00:00Z","status":"stable"} -->
2+
# App/Chat
3+
4+
## Overview
5+
6+
The `/chat` route provides a rich AI chat interface built with **AI Elements** (30 components) integrated with **26+ Mastra agents**. Uses AI SDK v5 patterns with streaming, reasoning display, tool execution, and source citations.
7+
8+
## Architecture
9+
10+
```mermaid
11+
graph TB
12+
subgraph ChatPage["app/chat/page.tsx"]
13+
ChatProvider["ChatProvider (Context)"]
14+
ChatProvider --> ChatHeader
15+
ChatProvider --> ChatMessages
16+
ChatProvider --> ChatInput
17+
end
18+
19+
subgraph Providers["providers/"]
20+
ChatContext["chat-context.tsx<br/>AI SDK v5 types"]
21+
end
22+
23+
subgraph Components["components/"]
24+
ChatHeader["chat-header.tsx<br/>ModelSelector"]
25+
ChatMessages["chat-messages.tsx<br/>Conversation/Message"]
26+
ChatInput["chat-input.tsx<br/>PromptInput"]
27+
AgentReasoning["agent-reasoning.tsx<br/>Reasoning/ChainOfThought"]
28+
AgentTools["agent-tools.tsx<br/>Tool display"]
29+
AgentSources["agent-sources.tsx<br/>Sources citations"]
30+
AgentArtifact["agent-artifact.tsx<br/>Code artifacts"]
31+
end
32+
33+
subgraph Config["config/"]
34+
AgentConfig["agents.ts<br/>26+ agent configs"]
35+
end
36+
37+
subgraph AIElements["AI Elements (imported)"]
38+
Conversation
39+
Message
40+
PromptInput
41+
ModelSelector
42+
Reasoning
43+
Tool
44+
Sources
45+
Artifact
46+
end
47+
48+
ChatPage --> Providers
49+
ChatPage --> Components
50+
Components --> Config
51+
Components --> AIElements
52+
```
53+
54+
## File Structure
55+
56+
```plaintext
57+
app/chat/
58+
├── page.tsx # Main chat page with ChatProvider
59+
├── providers/
60+
│ └── chat-context.tsx # React Context with AI SDK v5 types
61+
├── config/
62+
│ └── agents.ts # 26+ agent configurations with feature flags
63+
└── components/
64+
├── chat-header.tsx # Header with ModelSelector
65+
├── chat-messages.tsx # Message list with streaming
66+
├── chat-input.tsx # PromptInput with submit
67+
├── agent-reasoning.tsx # Reasoning/ChainOfThought display
68+
├── agent-tools.tsx # Tool invocation display
69+
├── agent-sources.tsx # Sources citations for research
70+
└── agent-artifact.tsx # Code/content artifacts
71+
```
72+
73+
## Key Patterns
74+
75+
### AI SDK v5 Compatibility
76+
77+
The chat uses AI SDK v5 types and patterns:
78+
79+
```typescript
80+
// Type imports
81+
import type { UIMessage, DynamicToolUIPart, TextUIPart, ReasoningUIPart } from "ai"
82+
import { isTextUIPart, isReasoningUIPart, isToolOrDynamicToolUIPart } from "ai"
83+
84+
// Message parts (NOT content)
85+
const textPart = message.parts?.find(isTextUIPart)
86+
const content = textPart?.text || ""
87+
88+
// Tool states: input-available, output-available, output-error
89+
const tools = message.parts?.filter(isToolOrDynamicToolUIPart)
90+
```
91+
92+
### Mastra Stream Chunk Types
93+
94+
```typescript
95+
// From @mastra/core/stream ChunkType:
96+
case "text-delta": // Streaming text content
97+
case "reasoning-delta": // Streaming reasoning (NOT "reasoning")
98+
case "tool-call": // Tool invocation started
99+
case "tool-result": // Tool execution complete
100+
case "source": // Research sources
101+
case "finish": // payload.output.usage.inputTokens
102+
```
103+
104+
### Agent Configuration
105+
106+
```typescript
107+
// config/agents.ts
108+
export interface AgentConfig {
109+
id: string
110+
name: string
111+
description: string
112+
category: 'core' | 'research' | 'content' | 'data' | 'financial' | 'diagram' | 'utility'
113+
features: {
114+
reasoning: boolean // Show Reasoning component
115+
chainOfThought: boolean // Show ChainOfThought steps
116+
tools: boolean // Show Tool invocations
117+
sources: boolean // Show Sources citations
118+
canvas: boolean // Show Canvas (future)
119+
artifacts: boolean // Show code artifacts
120+
fileUpload: boolean // Enable file attachments
121+
}
122+
}
123+
```
124+
125+
## Agent Categories
126+
127+
| Category | Agents | Count |
128+
|----------|--------|-------|
129+
| **Core** | weatherAgent, a2aCoordinatorAgent | 2 |
130+
| **Research** | researchAgent, researchPaperAgent, documentProcessingAgent, knowledgeIndexingAgent | 4 |
131+
| **Content** | copywriterAgent, editorAgent, contentStrategistAgent, scriptWriterAgent, reportAgent | 5 |
132+
| **Data** | dataExportAgent, dataIngestionAgent, dataTransformationAgent | 3 |
133+
| **Financial** | stockAnalysisAgent, chartTypeAdvisorAgent, chartDataProcessorAgent, chartGeneratorAgent, chartSupervisorAgent | 5 |
134+
| **Diagram** | csvToExcalidrawAgent, imageToCsvAgent, excalidrawValidatorAgent | 3 |
135+
| **Utility** | evaluationAgent, learningExtractionAgent, dane, sqlAgent | 4 |
136+
137+
## AI Elements Used
138+
139+
| Component | File | Purpose |
140+
|-----------|------|---------|
141+
| `Conversation`, `ConversationContent`, `ConversationEmptyState`, `ConversationScrollButton` | chat-messages.tsx | Message container |
142+
| `Message`, `MessageContent`, `MessageResponse`, `MessageToolbar`, `MessageActions`, `MessageAction` | chat-messages.tsx | Individual messages |
143+
| `PromptInput`, `PromptInputTextarea`, `PromptInputFooter`, `PromptInputSubmit` | chat-input.tsx | Input form |
144+
| `ModelSelector`, `ModelSelectorTrigger`, `ModelSelectorContent`, `ModelSelectorList`, `ModelSelectorGroup`, `ModelSelectorItem` | chat-header.tsx | Agent selection |
145+
| `Reasoning`, `ReasoningTrigger`, `ReasoningContent` | agent-reasoning.tsx | AI thinking display |
146+
| `Tool`, `ToolHeader`, `ToolContent`, `ToolInput`, `ToolOutput` | agent-tools.tsx | Tool execution |
147+
| `Sources`, `SourcesTrigger`, `SourcesContent`, `Source` | agent-sources.tsx | Citations |
148+
| `Artifact`, `ArtifactHeader`, `ArtifactContent`, `ArtifactActions` | agent-artifact.tsx | Code artifacts |
149+
| `Loader` | chat-messages.tsx | Loading indicator |
150+
| `CodeBlock`, `CodeBlockCopyButton` | agent-artifact.tsx | Syntax highlighting |
151+
152+
## Implementation Status
153+
154+
| Task | Status | Description |
155+
|------|--------|-------------|
156+
| AIEL-001 || ChatContext provider with AI SDK v5 |
157+
| AIEL-002 || Agent config system (26+ agents) |
158+
| AIEL-003 || ChatHeader with ModelSelector |
159+
| AIEL-004 || ChatMessages with streaming |
160+
| AIEL-005 || ChatInput with PromptInput |
161+
| AIEL-006 || Reasoning display |
162+
| AIEL-007 || Tool execution display |
163+
| AIEL-008 || Sources citations |
164+
| AIEL-009 || Context (token usage) tracking |
165+
| AIEL-010 || File upload support |
166+
| AIEL-011 || Artifact display |
167+
| AIEL-012 || Page integration complete |
168+
| AIEL-013 || E2E tests |
169+
170+
**Completion: 12/13 (92%)**
171+
172+
## Usage
173+
174+
```tsx
175+
// app/chat/page.tsx
176+
import { ChatProvider } from "./providers/chat-context"
177+
import { ChatHeader } from "./components/chat-header"
178+
import { ChatMessages } from "./components/chat-messages"
179+
import { ChatInput } from "./components/chat-input"
180+
181+
export default function ChatPage() {
182+
return (
183+
<ChatProvider defaultAgent="researchAgent">
184+
<main className="flex h-screen flex-col bg-background">
185+
<ChatHeader />
186+
<ChatMessages />
187+
<ChatInput />
188+
</main>
189+
</ChatProvider>
190+
)
191+
}
192+
```
193+
194+
## Remaining Work
195+
196+
1. **AIEL-013**: Create `app/chat/__tests__/chat.test.tsx` with Vitest (optional)
197+
198+
---
199+
200+
*Last updated: 2025-11-29*

app/chat/components/agent-tools.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,41 @@ import {
88
ToolOutput,
99
} from "@/src/components/ai-elements/tool"
1010
import type { ToolInvocationState } from "@/app/chat/providers/chat-context"
11-
import type { DynamicToolUIPart } from "ai"
11+
import type { DynamicToolUIPart, ToolUIPart } from "ai"
1212

1313
export interface AgentToolsProps {
1414
tools: (ToolInvocationState | DynamicToolUIPart)[]
1515
}
1616

17+
type ToolState = ToolUIPart["state"]
18+
1719
export function AgentTools({ tools }: AgentToolsProps) {
1820
if (!tools || tools.length === 0) return null
1921

2022
return (
21-
<div className="space-y-2">
23+
<div className="space-y-2 mt-2">
2224
{tools.map((tool) => {
2325
const t = tool as ToolInvocationState
24-
const toolName = t.toolName || t.type?.replace(/^(tool-|dynamic-tool)/, "") || "unknown"
25-
const toolType = `tool-${toolName}` as const
26+
const toolName = t.toolName || "unknown"
27+
const toolType = `tool-${toolName}` as ToolUIPart["type"]
28+
const toolState = t.state as ToolState
29+
30+
const hasOutput = toolState === "output-available" || toolState === "output-error"
31+
const errorText = toolState === "output-error" ? (t as unknown as { errorText?: string }).errorText : undefined
2632

2733
return (
2834
<Tool key={t.toolCallId} defaultOpen={false}>
2935
<ToolHeader
3036
title={toolName}
3137
type={toolType}
32-
state={t.state}
38+
state={toolState}
3339
/>
3440
<ToolContent>
3541
<ToolInput input={t.input} />
36-
{(t.state === "output-available" || t.state === "output-error") && (
42+
{hasOutput && (
3743
<ToolOutput
38-
output={t.state === "output-available" ? t.output : undefined}
39-
errorText={t.state === "output-error" ? t.errorText : undefined}
44+
output={toolState === "output-available" ? t.output : undefined}
45+
errorText={errorText}
4046
/>
4147
)}
4248
</ToolContent>

0 commit comments

Comments
 (0)