{"_id":"@10play/claude-agent-sdk-ui","_rev":"3-955cd23257c271867b1cb1210f99cd48","name":"@10play/claude-agent-sdk-ui","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.2":{"name":"@10play/claude-agent-sdk-ui","version":"0.1.2","keywords":["react","ui","components","claude","agent","sdk","typescript","tailwind"],"author":{"name":"10play"},"license":"MIT","_id":"@10play/claude-agent-sdk-ui@0.1.2","maintainers":[{"name":"17amir17","email":"amir@10play.dev"},{"name":"guy353","email":"guy353@gmail.com"}],"homepage":"https://github.com/10play/claude-agent-sdk-ui#readme","bugs":{"url":"https://github.com/10play/claude-agent-sdk-ui/issues"},"dist":{"shasum":"e7e4678044d306d5f8de3d37f41807b739e455e5","tarball":"https://registry.npmjs.org/@10play/claude-agent-sdk-ui/-/claude-agent-sdk-ui-0.1.2.tgz","fileCount":138,"integrity":"sha512-OlTPPTFGUs0ZSm7rhLmKH4XdoKdJQyIAEHuIn4wgRqOWxYZka55l6FgMgOWypaZ833EcpGddf1T4TcGVzcGwmA==","signatures":[{"sig":"MEYCIQCydBtunECZx28X2k3MIg3UgVRvAyXjw5vNS963bKTx1wIhANEhAVpmPpBCEpkzOvSIzABFRRLYVQMTMLNtzYbtyeFm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3390344},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"},"./styles.css":"./dist/index.css"},"gitHead":"62da368d61741dc520ca2fbe43db52f001b512da","scripts":{"dev":"vite build --watch","lint":"eslint .","build":"vite build","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"_npmUser":{"name":"guy353","email":"guy353@gmail.com"},"repository":{"url":"git+https://github.com/10play/claude-agent-sdk-ui.git","type":"git"},"_npmVersion":"10.9.0","description":"React UI components library for Claude Agent SDK","directories":{},"_nodeVersion":"22.11.0","dependencies":{"zod":"^3.24.1","shiki":"^1.0.0","@tiptap/pm":"^2.8.0","@tiptap/react":"^2.8.0","tiptap-markdown":"^0.8.10","@anthropic-ai/sdk":"^0.71.2","@tiptap/starter-kit":"^2.8.0","regenerator-runtime":"^0.14.1","@radix-ui/react-slot":"^1.0.2","@uiw/react-json-view":"^2.0.0-alpha.41","@10play/tiptap-stagger":"^0.0.6","react-speech-recognition":"^3.10.0","@anthropic-ai/claude-agent-sdk":"^0.2.0"},"_hasShrinkwrap":false,"devDependencies":{"clsx":"^2.0.0","vite":"^5.0.8","react":"^18.2.0","postcss":"^8.4.32","react-dom":"^18.2.0","typescript":"^5.2.2","tailwindcss":"^3.3.6","@tiptap/core":"^2.8.0","@types/react":"^18.2.43","autoprefixer":"^10.4.16","lucide-react":"^0.294.0","tailwind-merge":"^2.1.0","vite-plugin-dts":"^3.6.4","@types/react-dom":"^18.2.17","tailwindcss-animate":"^1.0.7","@vitejs/plugin-react":"^4.2.1","class-variance-authority":"^0.7.0","@anthropic-ai/claude-agent-sdk":"^0.2.15"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0","lucide-react":">=0.294.0"},"_npmOperationalInternal":{"tmp":"tmp/claude-agent-sdk-ui_0.1.2_1769703405631_0.38605366169815647","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@10play/claude-agent-sdk-ui","version":"0.1.3","keywords":["react","ui","components","claude","agent","sdk","typescript","tailwind"],"author":{"name":"10play"},"license":"MIT","_id":"@10play/claude-agent-sdk-ui@0.1.3","maintainers":[{"name":"17amir17","email":"amir@10play.dev"},{"name":"guy353","email":"guy353@gmail.com"}],"homepage":"https://github.com/10play/claude-agent-sdk-ui#readme","bugs":{"url":"https://github.com/10play/claude-agent-sdk-ui/issues"},"dist":{"shasum":"86cfad335e1631942ab732cf9e042adef951caf3","tarball":"https://registry.npmjs.org/@10play/claude-agent-sdk-ui/-/claude-agent-sdk-ui-0.1.3.tgz","fileCount":138,"integrity":"sha512-jh2h4czQtEKC4RVM67JL3Kcv+6sU3YkUjmQRQ5/NeL38eUbUJHDmYNTV02VIGdjjlsZEPdjzvpt6gtJVbBDetA==","signatures":[{"sig":"MEUCIQDna6VifERYRVM21vpGT/XVkpMdL/UmLQMBwQCyPvLJDAIgLSbYQMUMF0yfG8kQ0ruHAaPMBuJZXE9UYRpMMJYtccE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3390809},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"},"./styles.css":"./dist/index.css"},"gitHead":"c1ef2c4a93a6e01b5a1d83dece73ab0bbe1c27fd","scripts":{"dev":"vite build --watch","lint":"eslint .","build":"vite build","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:50cf5410-9ee4-4687-b4f7-ba9b8b788fac"}},"repository":{"url":"git+https://github.com/10play/claude-agent-sdk-ui.git","type":"git"},"_npmVersion":"11.6.2","description":"React UI components library for Claude Agent SDK","directories":{},"_nodeVersion":"24.13.0","dependencies":{"zod":"^3.24.1","shiki":"^1.0.0","@tiptap/pm":"^2.8.0","@tiptap/react":"^2.8.0","tiptap-markdown":"^0.8.10","@anthropic-ai/sdk":"^0.71.2","@tiptap/starter-kit":"^2.8.0","regenerator-runtime":"^0.14.1","@radix-ui/react-slot":"^1.0.2","@uiw/react-json-view":"^2.0.0-alpha.41","@10play/tiptap-stagger":"^0.0.6","react-speech-recognition":"^3.10.0","@anthropic-ai/claude-agent-sdk":"^0.2.0"},"_hasShrinkwrap":false,"devDependencies":{"clsx":"^2.0.0","vite":"^5.0.8","react":"^18.2.0","postcss":"^8.4.32","react-dom":"^18.2.0","typescript":"^5.2.2","tailwindcss":"^3.3.6","@tiptap/core":"^2.8.0","@types/react":"^18.2.43","autoprefixer":"^10.4.16","lucide-react":"^0.294.0","tailwind-merge":"^2.1.0","vite-plugin-dts":"^3.6.4","@types/react-dom":"^18.2.17","tailwindcss-animate":"^1.0.7","@vitejs/plugin-react":"^4.2.1","class-variance-authority":"^0.7.0","@anthropic-ai/claude-agent-sdk":"^0.2.15"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0","lucide-react":">=0.294.0"},"_npmOperationalInternal":{"tmp":"tmp/claude-agent-sdk-ui_0.1.3_1770115405402_0.4533942100755879","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@10play/claude-agent-sdk-ui","version":"0.1.4","description":"React UI components library for Claude Agent SDK","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"},"./server":{"import":"./dist/server.js","require":"./dist/server.cjs","types":"./dist/server.d.ts"},"./styles.css":"./dist/index.css"},"scripts":{"build":"vite build","dev":"vite build --watch","lint":"eslint .","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"keywords":["react","ui","components","claude","agent","sdk","typescript","tailwind"],"author":{"name":"10play"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/10play/claude-agent-sdk-ui.git"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0","lucide-react":">=0.294.0"},"devDependencies":{"@anthropic-ai/claude-agent-sdk":"^0.2.15","@tiptap/core":"^2.8.0","@types/react":"^18.2.43","@types/react-dom":"^18.2.17","@vitejs/plugin-react":"^4.2.1","autoprefixer":"^10.4.16","class-variance-authority":"^0.7.0","clsx":"^2.0.0","lucide-react":"^0.294.0","postcss":"^8.4.32","react":"^18.2.0","react-dom":"^18.2.0","tailwind-merge":"^2.1.0","tailwindcss":"^3.3.6","tailwindcss-animate":"^1.0.7","typescript":"^5.2.2","vite":"^5.0.8","vite-plugin-dts":"^3.6.4"},"dependencies":{"@10play/tiptap-stagger":"^0.0.6","@anthropic-ai/claude-agent-sdk":"^0.2.0","@anthropic-ai/sdk":"^0.71.2","@radix-ui/react-slot":"^1.0.2","@tiptap/pm":"^2.8.0","@tiptap/react":"^2.8.0","@tiptap/starter-kit":"^2.8.0","@uiw/react-json-view":"^2.0.0-alpha.41","react-speech-recognition":"^3.10.0","regenerator-runtime":"^0.14.1","shiki":"^1.0.0","tiptap-markdown":"^0.8.10","zod":"^3.24.1"},"gitHead":"e5cd5b40598f17ca32a75b0521784ce33542dbee","_id":"@10play/claude-agent-sdk-ui@0.1.4","bugs":{"url":"https://github.com/10play/claude-agent-sdk-ui/issues"},"homepage":"https://github.com/10play/claude-agent-sdk-ui#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-EPoakBMCdQX9TXtvSyZwcCwES54RpYvLaPfF7Efb7Zki9kepRzNK18RRhpjKQ/ar5CbVSfAQ8lldv8g/hbIm0g==","shasum":"d150b045ff171ca6fa32e3ade8782a2aee098f2c","tarball":"https://registry.npmjs.org/@10play/claude-agent-sdk-ui/-/claude-agent-sdk-ui-0.1.4.tgz","fileCount":138,"unpackedSize":3390809,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHl4dUss5e0eW3MdmgyJzXCYYC1cUgHfQV3E4EsxFNhKAiABkB1ooWYQkRd5Zfb74TOGG3zqtnYaQBBSSE2HMakO0A=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:50cf5410-9ee4-4687-b4f7-ba9b8b788fac"}},"directories":{},"maintainers":[{"name":"17amir17","email":"amir@10play.dev"},{"name":"guy353","email":"guy353@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/claude-agent-sdk-ui_0.1.4_1770117181996_0.35271437415630236"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-29T16:16:45.430Z","modified":"2026-02-03T11:13:02.441Z","0.1.2":"2026-01-29T16:16:45.852Z","0.1.3":"2026-02-03T10:43:25.702Z","0.1.4":"2026-02-03T11:13:02.283Z"},"bugs":{"url":"https://github.com/10play/claude-agent-sdk-ui/issues"},"author":{"name":"10play"},"license":"MIT","homepage":"https://github.com/10play/claude-agent-sdk-ui#readme","keywords":["react","ui","components","claude","agent","sdk","typescript","tailwind"],"repository":{"type":"git","url":"git+https://github.com/10play/claude-agent-sdk-ui.git"},"description":"React UI components library for Claude Agent SDK","maintainers":[{"name":"17amir17","email":"amir@10play.dev"},{"name":"guy353","email":"guy353@gmail.com"}],"readme":"# Claude Agent SDK UI\n\nReact UI components library for building chat applications with the Claude Agent SDK.\n\n[![npm version](https://img.shields.io/npm/v/@10play/claude-agent-sdk-ui.svg)](https://www.npmjs.com/package/@10play/claude-agent-sdk-ui)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/10play/claude-agent-sdk-ui/blob/main/packages/claude-agent-sdk-ui/LICENSE)\n\n## 📚 Table of Contents\n\n- [Features](#features)\n- [Quick Start](#quick-start-30-seconds)\n- [Component Overview](#component-overview)\n- [Custom Components](#custom-components)\n- [API Reference](#api-reference)\n- [Component Architecture](#component-architecture--composition)\n- [Styling & Customization](#styling--customization)\n- [Backend API Specification](#backend-api-specification)\n- [Common Patterns and Recipes](#common-patterns-and-recipes)\n- [Performance Optimization](#performance-optimization)\n- [Troubleshooting](#troubleshooting)\n- [Requirements](#requirements)\n\n---\n\n## Features\n\n- 🎨 **Pre-built Components** - Ready-to-use chat interface with streaming support\n- 🔧 **Flexible Architecture** - Use the full app or individual components\n- ✨ **Custom Components** - Override default message rendering with your own components\n- 🎭 **Theme Support** - Built-in light/dark mode with customization\n- 📎 **Rich Attachments** - Support for images and documents\n- 🎤 **Speech-to-Text** - Built-in voice input support\n- 🛠️ **Tool Visualization** - Display tool calls and results\n- 💬 **Command Autocomplete** - Slash commands for enhanced UX\n- 📦 **TypeScript First** - Full type safety with exported types\n- ♿ **Accessible** - Built with accessibility in mind\n\n---\n\n## Quick Start (30 seconds)\n\n### 1. Install\n\n```bash\nnpm install @10play/claude-agent-sdk-ui react react-dom lucide-react regenerator-runtime\n```\n\n> **Note:** The Claude Agent SDK is bundled automatically—no need to install it separately!\n\n### 2. Add Polyfills & Styles\n\n```tsx\n// In your app entry point (main.tsx or App.tsx)\nimport 'regenerator-runtime/runtime'  // Required for speech-to-text\nimport '@10play/claude-agent-sdk-ui/styles.css'\n```\n\n### 3. Use FullChatApp\n\n```tsx\nimport { FullChatApp } from '@10play/claude-agent-sdk-ui'\n\nfunction App() {\n  return (\n    <FullChatApp\n      apiBaseUrl=\"http://localhost:4001/api\"\n      websocketUrl=\"ws://localhost:4001/ws\"\n    />\n  )\n}\n```\n\nThat's it! You now have a fully functional chat application with session management, streaming, and tool support.\n\n---\n\n## Component Overview\n\nThe library provides three levels of abstraction:\n\n### Level 1: Complete Application (Easiest)\n\n**`<FullChatApp />`** - A complete, batteries-included chat application\n\n```tsx\nimport { FullChatApp } from '@10play/claude-agent-sdk-ui'\n\n<FullChatApp\n  apiBaseUrl=\"http://localhost:4001/api\"\n  websocketUrl=\"ws://localhost:4001/ws\"\n  defaultTheme=\"dark\"\n  supportImages={true}\n  supportDocuments={true}\n/>\n```\n\nIncludes:\n- Session list and management\n- Chat interface with streaming\n- Tool control panel\n- Settings modal\n- Theme toggle\n- Import/export functionality\n\n### Level 2: Core Chat Interface (More Control)\n\n**`<ChatInterface />`** - Core chat UI without session management\n\n```tsx\nimport { ChatInterface, useSessions } from '@10play/claude-agent-sdk-ui'\n\nfunction MyChat() {\n  const { currentSession, ... } = useSessions({ sessionApi })\n\n  return (\n    <ChatInterface\n      session={currentSession}\n      websocketUrl=\"ws://localhost:4001/ws\"\n      apiBaseUrl=\"http://localhost:4001/api\"\n      onSessionUpdate={updateSession}\n      supportImages={true}\n    />\n  )\n}\n```\n\n### Level 3: Individual Components (Full Customization)\n\nBuild your own interface with granular components:\n\n```tsx\nimport {\n  MessageList,\n  MessageInput,\n  StreamingMessage,\n  ToolCallMessage,\n  ToolResultMessage,\n  ThemeToggle\n} from '@10play/claude-agent-sdk-ui'\n\nfunction CustomChat() {\n  return (\n    <div>\n      <ThemeToggle />\n      <MessageList messages={messages}>\n        {messages.map(msg => (\n          <StreamingMessage key={msg.id} event={msg} />\n        ))}\n      </MessageList>\n      <MessageInput onSendMessage={handleSend} />\n    </div>\n  )\n}\n```\n\n---\n\n## Custom Components\n\nThe library supports **custom component overrides**, allowing you to replace default message rendering components with your own implementations while maintaining full type safety and integration with the rest of the UI.\n\n### Why Use Custom Components?\n\n- 🎨 **Branding** - Match your company's design system\n- ✨ **Enhanced Features** - Add custom interactions like click-to-copy or inline editing\n- 🎭 **Different Layouts** - Customize message bubble styles and positioning\n- ♿ **Accessibility** - Add enhanced keyboard navigation or screen reader support\n- 🔌 **Integration** - Connect messages to your existing component library\n\n### Available Component Overrides\n\nYou can override any of these message rendering components:\n\n- `AssistantMessage` - Assistant text messages\n- `ToolCallMessage` - Tool invocation messages\n- `ToolResultMessage` - Tool execution results\n- `SystemMessage` - System-level messages\n- `StreamingMessage` - Streaming events\n\n### Basic Usage\n\n```tsx\nimport { FullChatApp, type CustomComponents } from '@10play/claude-agent-sdk-ui'\n\n// Define your custom components\nconst customComponents: CustomComponents = {\n  AssistantMessage: MyCustomAssistantMessage,\n  ToolCallMessage: MyCustomToolCallMessage,\n  // ... other components\n}\n\nfunction App() {\n  return (\n    <FullChatApp\n      apiBaseUrl=\"http://localhost:4001/api\"\n      websocketUrl=\"ws://localhost:4001/ws\"\n      components={customComponents}\n    />\n  )\n}\n```\n\n### Creating a Custom Component\n\nAll custom components receive properly typed props from the SDK:\n\n```tsx\nimport type { CustomAssistantMessageProps } from '@10play/claude-agent-sdk-ui'\nimport { MarkdownEditor } from '@10play/claude-agent-sdk-ui'\n\nexport function MyCustomAssistantMessage({\n  content,\n  isStreaming\n}: CustomAssistantMessageProps) {\n  return (\n    <div className=\"my-custom-message\">\n      <div className=\"custom-header\">\n        🤖 AI Assistant\n      </div>\n      <MarkdownEditor\n        content={content}\n        isStreaming={isStreaming}\n        className=\"custom-content\"\n      />\n    </div>\n  )\n}\n```\n\n### Component Props Types\n\nThe library exports all component prop types for type safety:\n\n```tsx\nimport type {\n  CustomAssistantMessageProps,\n  CustomToolCallMessageProps,\n  CustomToolResultMessageProps,\n  CustomSystemMessageProps,\n  CustomStreamingMessageProps\n} from '@10play/claude-agent-sdk-ui'\n```\n\n### Using with ChatInterface\n\nCustom components work at all levels of the API:\n\n```tsx\nimport { ChatInterface, type CustomComponents } from '@10play/claude-agent-sdk-ui'\n\nconst customComponents: CustomComponents = {\n  AssistantMessage: MyCustomAssistantMessage,\n}\n\nfunction MyChat() {\n  return (\n    <ChatInterface\n      session={session}\n      websocketUrl={wsUrl}\n      apiBaseUrl={apiUrl}\n      onSessionUpdate={updateSession}\n      components={customComponents}\n    />\n  )\n}\n```\n\n### Advanced Example: Custom Tool Result Message\n\n```tsx\nimport type { CustomToolResultMessageProps } from '@10play/claude-agent-sdk-ui'\nimport { CollapsibleCodeBlock } from '@10play/claude-agent-sdk-ui'\n\nexport function MyCustomToolResultMessage({\n  toolResult,\n  toolName,\n  timestamp\n}: CustomToolResultMessageProps) {\n  // Extract content\n  let content = ''\n  if (typeof toolResult.content === 'string') {\n    content = toolResult.content\n  } else if (Array.isArray(toolResult.content)) {\n    content = toolResult.content\n      .map(item => item.text || JSON.stringify(item))\n      .join('\\n')\n  }\n\n  return (\n    <div className=\"my-tool-result\">\n      <div className=\"tool-header\">\n        <span className=\"tool-name\">{toolName}</span>\n        {toolResult.is_error && (\n          <span className=\"error-badge\">Error</span>\n        )}\n        <span className=\"timestamp\">\n          {new Date(timestamp).toLocaleTimeString()}\n        </span>\n      </div>\n\n      <CollapsibleCodeBlock\n        content={content}\n        language=\"text\"\n        isError={toolResult.is_error}\n      />\n\n      {/* Add custom actions */}\n      <button onClick={() => navigator.clipboard.writeText(content)}>\n        Copy Result\n      </button>\n    </div>\n  )\n}\n```\n\n### Reusing Default Components\n\nYou can import and reuse the default components in your custom implementations:\n\n```tsx\nimport {\n  AssistantMessage,\n  ToolCallMessage,\n  MarkdownEditor,\n  CollapsibleCodeBlock\n} from '@10play/claude-agent-sdk-ui'\n\n// Wrap the default component with additional features\nexport function EnhancedAssistantMessage(props) {\n  return (\n    <div className=\"enhanced-wrapper\">\n      <button onClick={() => console.log('Message clicked')}>\n        ⭐\n      </button>\n      <AssistantMessage {...props} />\n    </div>\n  )\n}\n```\n\n### Notes\n\n- All component overrides are **optional** - only override what you need\n- Custom components receive the **same props** as the default components\n- The library handles all message routing and state management\n- You can use any React components, hooks, and patterns in your custom components\n\n---\n\n## API Reference\n\n### Main Components\n\n#### `<FullChatApp />`\n\nComplete chat application with all features enabled.\n\n**Props:**\n\n| Prop | Type | Required | Default | Description |\n|------|------|----------|---------|-------------|\n| `apiBaseUrl` | `string` | ✅ | - | REST API endpoint (e.g., `\"http://localhost:4001/api\"`) |\n| `websocketUrl` | `string` | ✅ | - | WebSocket endpoint (e.g., `\"ws://localhost:4001/ws\"`) |\n| `defaultTheme` | `'light' \\| 'dark'` | ❌ | `'light'` | Initial theme |\n| `supportImages` | `boolean` | ❌ | `true` | Enable image attachments |\n| `supportDocuments` | `boolean` | ❌ | `true` | Enable document attachments (PDF, TXT) |\n| `defaultSettings` | `SettingsConfig` | ❌ | See below | Default model and tool settings |\n| `components` | `CustomComponents` | ❌ | `undefined` | Custom component overrides for message rendering |\n\n**Default Settings:**\n\n```typescript\n{\n  model: 'claude-sonnet-4-5-20250929',\n  allowedTools: ['Read', 'Edit', 'Bash', 'WebSearch', 'Write'],\n  maxBudgetUsd: undefined\n}\n```\n\n---\n\n#### `<ChatInterface />`\n\nCore chat UI component.\n\n**Props:**\n\n| Prop | Type | Required | Description |\n|------|------|----------|-------------|\n| `session` | `ChatSession \\| null` | ✅ | Current chat session |\n| `websocketUrl` | `string` | ✅ | WebSocket endpoint |\n| `apiBaseUrl` | `string` | ✅ | REST API endpoint |\n| `onSessionUpdate` | `(session: ChatSession) => void` | ✅ | Callback when session updates |\n| `onMessagesChange` | `(messages: any[]) => void` | ❌ | Callback when messages change |\n| `supportImages` | `boolean` | ❌ | Enable image attachments |\n| `supportDocuments` | `boolean` | ❌ | Enable document attachments |\n| `inputPlaceholder` | `string` | ❌ | Custom input placeholder text |\n| `components` | `CustomComponents` | ❌ | Custom component overrides for message rendering |\n\n---\n\n#### Message Input Components\n\nThree variants available for different use cases:\n\n**`<MessageInput />`** - Simple text-only input\n\n```tsx\nimport { MessageInput } from '@10play/claude-agent-sdk-ui'\n\n<MessageInput\n  onSendMessage={(text) => console.log(text)}\n  disabled={false}\n  placeholder=\"Type a message...\"\n/>\n```\n\n**`<MessageInputWithImages />`** - Text + image attachments\n\n```tsx\nimport { MessageInputWithImages } from '@10play/claude-agent-sdk-ui'\n\n<MessageInputWithImages\n  onSendMessage={(content) => {\n    console.log(content.text)\n    console.log(content.images) // ImageAttachment[]\n  }}\n  disabled={false}\n/>\n```\n\n**`<MessageInputWithAttachments />`** - Text + images + documents + speech\n\n```tsx\nimport { MessageInputWithAttachments } from '@10play/claude-agent-sdk-ui'\n\n<MessageInputWithAttachments\n  onSendMessage={(content) => {\n    console.log(content.text)\n    console.log(content.images)     // ImageAttachment[]\n    console.log(content.documents)  // DocumentAttachment[]\n  }}\n  disabled={false}\n  supportImages={true}\n  supportDocuments={true}\n/>\n```\n\n---\n\n#### Message Display Components\n\n**`<StreamingMessage />`** - Displays streaming message events\n\n```tsx\nimport { StreamingMessage } from '@10play/claude-agent-sdk-ui'\nimport type { StreamEvent } from '@anthropic-ai/claude-agent-sdk'\n\n<StreamingMessage event={streamEvent} />\n```\n\n**`<SystemMessage />`** - System-level messages\n\n```tsx\nimport { SystemMessage } from '@10play/claude-agent-sdk-ui'\n\n<SystemMessage content=\"System initialized\" />\n```\n\n**`<ToolCallMessage />`** - Tool invocation display\n\n```tsx\nimport { ToolCallMessage } from '@10play/claude-agent-sdk-ui'\n\n<ToolCallMessage\n  name=\"Read\"\n  input={{ file_path: \"/path/to/file.txt\" }}\n/>\n```\n\n**`<ToolResultMessage />`** - Tool execution results\n\n```tsx\nimport { ToolResultMessage } from '@10play/claude-agent-sdk-ui'\n\n<ToolResultMessage\n  name=\"Read\"\n  result={fileContents}\n  isError={false}\n/>\n```\n\n---\n\n### Hooks\n\n#### `useChatSession`\n\nMain hook for chat functionality with real-time WebSocket streaming.\n\n```tsx\nimport { useChatSession } from '@10play/claude-agent-sdk-ui'\n\nconst {\n  messages,           // Current messages\n  isStreaming,        // Is currently streaming\n  sendMessage,        // Send a message\n  stopStream,         // Stop current stream\n  clearMessages,      // Clear all messages\n  processingState     // Processing status\n} = useChatSession({\n  session,\n  websocketUrl: 'ws://localhost:4001/ws',\n  onMessagesChange: (msgs) => console.log(msgs)\n})\n```\n\n#### `useSessions`\n\nSession management hook.\n\n```tsx\nimport { useSessions, createSessionApi } from '@10play/claude-agent-sdk-ui'\n\nconst sessionApi = createSessionApi('http://localhost:4001/api')\n\nconst {\n  sessions,           // All sessions\n  currentSession,     // Current active session\n  loading,            // Loading state\n  error,              // Error state\n  createSession,      // Create new session\n  deleteSession,      // Delete a session\n  selectSession,      // Switch to a session\n  updateSession       // Update session\n} = useSessions({ sessionApi })\n```\n\n#### `useCommandAutocomplete`\n\nCommand autocomplete functionality.\n\n```tsx\nimport { useCommandAutocomplete, CommandService } from '@10play/claude-agent-sdk-ui'\n\nconst commandService = new CommandService()\n\nconst {\n  showAutocomplete,     // Should show autocomplete UI\n  filteredCommands,     // Filtered command list\n  selectedIndex,        // Currently selected index\n  selectCommand,        // Select a command\n  handleKeyDown         // Handle keyboard navigation\n} = useCommandAutocomplete({\n  commandService,\n  inputValue: messageText\n})\n```\n\n#### `useSpeechToText`\n\nSpeech recognition hook.\n\n```tsx\nimport { useSpeechToText } from '@10play/claude-agent-sdk-ui'\n\nconst {\n  transcript,         // Current transcript\n  isListening,        // Is currently listening\n  startListening,     // Start recording\n  stopListening,      // Stop recording\n  resetTranscript,    // Clear transcript\n  browserSupportsSpeechRecognition\n} = useSpeechToText()\n```\n\n---\n\n### Contexts\n\n#### `ThemeProvider` / `useTheme`\n\nTheme management.\n\n```tsx\nimport { ThemeProvider, useTheme } from '@10play/claude-agent-sdk-ui'\n\nfunction App() {\n  return (\n    <ThemeProvider defaultTheme=\"dark\">\n      <YourApp />\n    </ThemeProvider>\n  )\n}\n\nfunction ThemeToggleButton() {\n  const { theme, setTheme } = useTheme()\n  return (\n    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>\n      Toggle Theme\n    </button>\n  )\n}\n```\n\n#### `ToolResultProvider` / `useToolResultContext`\n\nTool result state management.\n\n```tsx\nimport { ToolResultProvider, useToolResultContext } from '@10play/claude-agent-sdk-ui'\n\nfunction App() {\n  return (\n    <ToolResultProvider>\n      <YourApp />\n    </ToolResultProvider>\n  )\n}\n\nfunction ToolPanel() {\n  const { toolResults, clearToolResults } = useToolResultContext()\n  // ...\n}\n```\n\n---\n\n### Utilities\n\n#### Session Export/Import\n\n```tsx\nimport { downloadSessionAsJson, parseSessionImport } from '@10play/claude-agent-sdk-ui'\n\n// Export session\ndownloadSessionAsJson(messages, 'my-chat.json')\n\n// Import session\nconst jsonString = await file.text()\nconst { messages, title } = parseSessionImport(jsonString)\n```\n\n#### Image Utilities\n\n```tsx\nimport {\n  fileToImageAttachment,\n  isValidImageFile,\n  revokeImagePreview\n} from '@10play/claude-agent-sdk-ui'\n\nconst attachment = await fileToImageAttachment(file)\nconst isValid = isValidImageFile(file)\nrevokeImagePreview(attachment) // Clean up blob URLs\n```\n\n#### Document Utilities\n\n```tsx\nimport {\n  fileToDocumentAttachment,\n  isValidDocumentFile,\n  formatFileSize\n} from '@10play/claude-agent-sdk-ui'\n\nconst doc = await fileToDocumentAttachment(file)\nconst isValid = isValidDocumentFile(file) // PDF, TXT\nconst size = formatFileSize(file.size) // \"1.5 MB\"\n```\n\n---\n\n## Component Architecture & Composition\n\nUnderstanding how components work together will help you build better applications and troubleshoot issues effectively.\n\n### Component Hierarchy\n\n```\nFullChatApp\n├── ThemeProvider\n├── ToolResultProvider\n├── SessionList (sidebar)\n│   ├── SessionItem (multiple)\n│   └── NewSessionButton\n└── ChatInterface\n    ├── EditableTitle\n    ├── WorkingDirectorySelector\n    ├── MessageList\n    │   ├── ProcessingIndicator\n    │   ├── MessageBubble (multiple)\n    │   │   ├── AssistantMessage\n    │   │   │   └── MarkdownEditor\n    │   │   │       └── StreamingText\n    │   │   ├── ToolCallResultPair\n    │   │   │   ├── ToolCallMessage\n    │   │   │   │   └── CollapsibleCodeBlock\n    │   │   │   └── ToolResultMessage\n    │   │   │       └── CollapsibleCodeBlock / JsonTreeViewer\n    │   │   ├── SystemMessage\n    │   │   └── StreamingMessage\n    │   └── ThoughtProcess (tool panel)\n    │       └── ToolControlPanel\n    │           └── TodoListViewer\n    └── MessageInputWithAttachments\n        ├── CommandAutocomplete\n        ├── ImagePreview\n        ├── DocumentPreview\n        └── SpeechToText button\n```\n\n### Data Flow\n\n1. **Message Sending Flow:**\n   ```\n   User Input → MessageInput → sendMessage() → WebSocket\n   → Backend → Claude API → WebSocket Response → useChatSession\n   → State Update → MessageList → Re-render\n   ```\n\n2. **Session Management Flow:**\n   ```\n   User Action → useSessions hook → SessionApi (REST)\n   → Backend → State Update → UI Update\n   ```\n\n3. **Tool Execution Flow:**\n   ```\n   Claude → tool_use → ToolCallMessage rendered\n   → Backend executes tool → tool_result\n   → ToolResultMessage rendered → ToolResultContext updated\n   ```\n\n### Context Providers\n\nThe library uses React Context for shared state:\n\n#### ThemeProvider\n- **Purpose:** Manages light/dark theme state\n- **Persisted:** Yes (localStorage)\n- **Usage:** Wrap your app with `<ThemeProvider>`\n- **Access:** Use `useTheme()` hook\n\n#### ToolResultProvider\n- **Purpose:** Tracks tool execution results for the control panel\n- **Persisted:** No (session-scoped)\n- **Usage:** Wrap your app with `<ToolResultProvider>`\n- **Access:** Use `useToolResultContext()` hook\n\n### Component Communication\n\nComponents communicate through:\n\n1. **Props drilling** - For direct parent-child relationships\n2. **Context API** - For cross-cutting concerns (theme, tool results)\n3. **Callbacks** - For event handling (onSessionUpdate, onMessagesChange)\n4. **WebSocket events** - For real-time streaming from backend\n\n### State Management\n\nThe library uses React hooks for state management:\n\n- **useChatSession** - Manages chat messages, streaming, and WebSocket connection\n- **useSessions** - Manages session list and CRUD operations\n- **useCommandAutocomplete** - Manages slash command autocomplete state\n- **useSpeechToText** - Manages voice input state\n\n---\n\n## Styling & Customization\n\nThe library uses Tailwind CSS with CSS variables for theming, making it easy to customize the appearance.\n\n### CSS Variables\n\nAll colors and spacing can be customized via CSS variables:\n\n```css\n:root {\n  /* Background colors */\n  --background: 0 0% 100%;\n  --foreground: 222.2 84% 4.9%;\n\n  /* Component colors */\n  --card: 0 0% 100%;\n  --card-foreground: 222.2 84% 4.9%;\n\n  --popover: 0 0% 100%;\n  --popover-foreground: 222.2 84% 4.9%;\n\n  /* Primary brand color */\n  --primary: 221.2 83.2% 53.3%;\n  --primary-foreground: 210 40% 98%;\n\n  /* Secondary colors */\n  --secondary: 210 40% 96.1%;\n  --secondary-foreground: 222.2 47.4% 11.2%;\n\n  /* Muted colors for less important content */\n  --muted: 210 40% 96.1%;\n  --muted-foreground: 215.4 16.3% 46.9%;\n\n  /* Accent colors for highlights */\n  --accent: 210 40% 96.1%;\n  --accent-foreground: 222.2 47.4% 11.2%;\n\n  /* Destructive colors for errors */\n  --destructive: 0 84.2% 60.2%;\n  --destructive-foreground: 210 40% 98%;\n\n  /* Border and input */\n  --border: 214.3 31.8% 91.4%;\n  --input: 214.3 31.8% 91.4%;\n  --ring: 221.2 83.2% 53.3%;\n\n  /* Border radius */\n  --radius: 0.5rem;\n}\n\n.dark {\n  --background: 222.2 84% 4.9%;\n  --foreground: 210 40% 98%;\n\n  /* ... dark theme colors */\n}\n```\n\n### Custom Theme Example\n\nCreate your own theme by overriding CSS variables:\n\n```css\n/* custom-theme.css */\n:root {\n  /* Brand colors */\n  --primary: 262 83% 58%;        /* Purple */\n  --primary-foreground: 0 0% 100%;\n\n  /* Custom accent */\n  --accent: 173 80% 40%;         /* Teal */\n  --accent-foreground: 0 0% 100%;\n\n  /* Rounded corners */\n  --radius: 1rem;\n}\n\n.dark {\n  --background: 240 10% 3.9%;    /* Deeper dark */\n  --primary: 263 70% 50%;        /* Adjusted purple for dark mode */\n}\n```\n\nThen import it after the main styles:\n\n```tsx\nimport '@10play/claude-agent-sdk-ui/styles.css'\nimport './custom-theme.css'\n```\n\n### Component-Specific Styling\n\nTarget specific components with Tailwind classes:\n\n```tsx\nimport { FullChatApp } from '@10play/claude-agent-sdk-ui'\n\n<div className=\"my-chat-wrapper\">\n  <FullChatApp {...props} />\n</div>\n```\n\n```css\n/* Override specific component styles */\n.my-chat-wrapper {\n  /* Message bubbles */\n  --muted: 210 40% 98%;\n\n  /* Adjust spacing */\n  --spacing-message: 1rem;\n}\n\n/* Target internal components (use with caution) */\n.my-chat-wrapper .message-bubble {\n  @apply shadow-lg;\n}\n```\n\n### Dark Mode Customization\n\nThe library automatically applies dark mode classes. You can customize dark mode separately:\n\n```css\n.dark {\n  /* Override dark mode colors */\n  --background: 220 15% 8%;      /* Custom dark background */\n  --card: 220 15% 12%;           /* Slightly lighter cards */\n  --border: 220 15% 20%;         /* Visible borders in dark mode */\n}\n```\n\n### Markdown Styling\n\nCustomize markdown rendering in messages:\n\n```css\n/* Override markdown styles */\n.markdown-editor {\n  /* Code blocks */\n  pre {\n    @apply bg-muted rounded-lg p-4;\n  }\n\n  /* Inline code */\n  code {\n    @apply bg-accent text-accent-foreground px-1 rounded;\n  }\n\n  /* Headings */\n  h1, h2, h3 {\n    @apply font-bold text-foreground;\n  }\n\n  /* Links */\n  a {\n    @apply text-primary hover:underline;\n  }\n}\n```\n\n---\n\n## Backend API Specification\n\nThe UI library expects your backend to implement the following REST API endpoints and WebSocket protocol.\n\n### REST API Endpoints\n\n#### Sessions Management\n\n**GET `/api/sessions`**\n- **Description:** List all sessions\n- **Response:**\n```typescript\n{\n  sessions: Array<{\n    id: string\n    title: string\n    created_at: string\n    updated_at: string\n    metadata?: {\n      working_directory?: string\n      model?: string\n      [key: string]: any\n    }\n  }>\n}\n```\n\n**POST `/api/sessions`**\n- **Description:** Create a new session\n- **Request Body:**\n```typescript\n{\n  title?: string\n  metadata?: {\n    working_directory?: string\n    model?: string\n    [key: string]: any\n  }\n}\n```\n- **Response:**\n```typescript\n{\n  session: {\n    id: string\n    title: string\n    created_at: string\n    updated_at: string\n    metadata?: object\n  }\n}\n```\n\n**GET `/api/sessions/:id`**\n- **Description:** Get a specific session with messages\n- **Response:**\n```typescript\n{\n  session: {\n    id: string\n    title: string\n    created_at: string\n    updated_at: string\n    metadata?: object\n  },\n  messages: Array<{\n    id: string\n    session_id: string\n    sdk_message: SDKMessage  // From @anthropic-ai/claude-agent-sdk\n    timestamp: string\n  }>\n}\n```\n\n**PUT `/api/sessions/:id`**\n- **Description:** Update session (title, metadata)\n- **Request Body:**\n```typescript\n{\n  title?: string\n  metadata?: object\n}\n```\n- **Response:**\n```typescript\n{\n  session: {\n    id: string\n    title: string\n    updated_at: string\n    metadata?: object\n  }\n}\n```\n\n**DELETE `/api/sessions/:id`**\n- **Description:** Delete a session\n- **Response:**\n```typescript\n{\n  success: boolean\n}\n```\n\n**POST `/api/sessions/:id/messages`**\n- **Description:** Load messages into a session (for import)\n- **Request Body:**\n```typescript\n{\n  title?: string\n  messages: Array<SDKMessage>\n}\n```\n- **Response:**\n```typescript\n{\n  session: {\n    id: string\n    title: string\n    updated_at: string\n  }\n}\n```\n\n#### Settings Management\n\n**GET `/api/sessions/:id/settings`**\n- **Description:** Get session settings (model, tools, budget)\n- **Response:**\n```typescript\n{\n  model: string\n  allowed_tools: string[]\n  max_budget_usd?: number\n}\n```\n\n**PUT `/api/sessions/:id/settings`**\n- **Description:** Update session settings\n- **Request Body:**\n```typescript\n{\n  model?: string\n  allowed_tools?: string[]\n  max_budget_usd?: number\n}\n```\n- **Response:**\n```typescript\n{\n  settings: {\n    model: string\n    allowed_tools: string[]\n    max_budget_usd?: number\n  }\n}\n```\n\n### WebSocket Protocol\n\nThe WebSocket connection is used for real-time message streaming.\n\n#### Connection\n\n**Endpoint:** `ws://your-backend/ws`\n\n**Query Parameters:**\n- `sessionId` - The session ID for this connection\n\n**Example:** `ws://localhost:4001/ws?sessionId=abc123`\n\n#### Client → Server Messages\n\n**Send Message:**\n```typescript\n{\n  type: 'message',\n  sessionId: string,\n  content: string | Array<ContentBlock>  // Text or multimodal content\n}\n```\n\n**Stop Stream:**\n```typescript\n{\n  type: 'stop',\n  sessionId: string\n}\n```\n\n**Update Title:**\n```typescript\n{\n  type: 'update_title',\n  sessionId: string,\n  title: string\n}\n```\n\n#### Server → Client Messages\n\nThe server should send `StreamEvent` objects from the Claude Agent SDK:\n\n```typescript\n{\n  event: SDKStreamEvent  // From @anthropic-ai/claude-agent-sdk\n}\n```\n\n**Event Types:**\n- `user_message` - User message added to history\n- `partial_assistant_message` - Streaming assistant response\n- `assistant_message` - Complete assistant message\n- `system_message` - System-level message\n- `status_message` - Status update\n- `hook_response_message` - Hook execution result\n\n**Error Messages:**\n```typescript\n{\n  error: string,\n  details?: any\n}\n```\n\n#### Example WebSocket Flow\n\n```\nClient connects: ws://localhost:4001/ws?sessionId=abc123\n\nClient → Server:\n{\n  \"type\": \"message\",\n  \"sessionId\": \"abc123\",\n  \"content\": \"Hello Claude!\"\n}\n\nServer → Client (multiple events):\n{\n  \"event\": {\n    \"type\": \"user_message\",\n    \"message\": { ... }\n  }\n}\n\n{\n  \"event\": {\n    \"type\": \"partial_assistant_message\",\n    \"message\": { \"content\": \"Hello! \" }\n  }\n}\n\n{\n  \"event\": {\n    \"type\": \"partial_assistant_message\",\n    \"message\": { \"content\": \"How can I help?\" }\n  }\n}\n\n{\n  \"event\": {\n    \"type\": \"assistant_message\",\n    \"message\": { ... complete message ... }\n  }\n}\n```\n\n### Reference Implementation\n\nFor a complete, working backend implementation, see the example backend in the repository:\n\n📁 **[`apps/example-backend/`](https://github.com/10play/claude-agent-sdk-ui/tree/main/apps/example-backend)**\n\nThe example backend includes:\n- Express.js server with all endpoints\n- WebSocket server implementation\n- Claude Agent SDK integration\n- Session persistence (file-based)\n- MCP server support\n- TypeScript with full type safety\n\n---\n\n## Performance Optimization\n\nTips and best practices for optimal performance with large chat histories and complex applications.\n\n### Message Virtualization\n\nFor chats with hundreds of messages, consider implementing virtualization:\n\n```tsx\nimport { useChatSession } from '@10play/claude-agent-sdk-ui'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nfunction VirtualizedChat({ session }) {\n  const { messages } = useChatSession({ session, websocketUrl })\n  const parentRef = useRef<HTMLDivElement>(null)\n\n  const virtualizer = useVirtualizer({\n    count: messages.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 100, // Estimated message height\n    overscan: 5\n  })\n\n  return (\n    <div ref={parentRef} className=\"h-screen overflow-auto\">\n      <div\n        style={{\n          height: `${virtualizer.getTotalSize()}px`,\n          position: 'relative'\n        }}\n      >\n        {virtualizer.getVirtualItems().map((virtualRow) => {\n          const message = messages[virtualRow.index]\n          return (\n            <div\n              key={virtualRow.key}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                transform: `translateY(${virtualRow.start}px)`\n              }}\n            >\n              <MessageBubble message={message} />\n            </div>\n          )\n        })}\n      </div>\n    </div>\n  )\n}\n```\n\n### Pagination\n\nImplement message pagination for large sessions:\n\n```tsx\nfunction PaginatedChat({ session }) {\n  const [page, setPage] = useState(0)\n  const [pageSize] = useState(50)\n\n  const { messages } = useChatSession({ session, websocketUrl })\n\n  // Show only recent messages\n  const visibleMessages = messages.slice(-pageSize * (page + 1))\n\n  return (\n    <div>\n      {messages.length > pageSize && (\n        <button onClick={() => setPage(p => p + 1)}>\n          Load Earlier Messages\n        </button>\n      )}\n      <MessageList messages={visibleMessages} />\n    </div>\n  )\n}\n```\n\n### Debounced Updates\n\nDebounce expensive operations like title updates:\n\n```tsx\nimport { useDebouncedCallback } from 'use-debounce'\n\nfunction DebouncedTitleEdit() {\n  const debouncedUpdate = useDebouncedCallback(\n    (newTitle: string) => {\n      updateTitle(newTitle)\n    },\n    1000 // Wait 1 second after user stops typing\n  )\n\n  return (\n    <input\n      onChange={(e) => debouncedUpdate(e.target.value)}\n      placeholder=\"Session title\"\n    />\n  )\n}\n```\n\n### Memoization\n\nUse React memoization for expensive computations:\n\n```tsx\nimport { useMemo } from 'react'\n\nfunction ChatStats({ messages }) {\n  const stats = useMemo(() => {\n    return {\n      totalMessages: messages.length,\n      userMessages: messages.filter(m => m.sdk_message.type === 'user').length,\n      toolCalls: messages.filter(m =>\n        m.sdk_message.type === 'assistant' &&\n        m.sdk_message.message?.content?.some(c => c.type === 'tool_use')\n      ).length\n    }\n  }, [messages])\n\n  return <div>{/* Display stats */}</div>\n}\n```\n\n### Image Optimization\n\nOptimize image attachments before sending:\n\n```tsx\nasync function optimizeImage(file: File): Promise<File> {\n  // Resize if too large\n  if (file.size > 5 * 1024 * 1024) { // 5MB\n    const img = await createImageBitmap(file)\n    const canvas = document.createElement('canvas')\n\n    // Scale down to max 1920px width\n    const scale = Math.min(1, 1920 / img.width)\n    canvas.width = img.width * scale\n    canvas.height = img.height * scale\n\n    const ctx = canvas.getContext('2d')!\n    ctx.drawImage(img, 0, 0, canvas.width, canvas.height)\n\n    // Convert to blob\n    const blob = await new Promise<Blob>((resolve) => {\n      canvas.toBlob((b) => resolve(b!), 'image/jpeg', 0.85)\n    })\n\n    return new File([blob], file.name, { type: 'image/jpeg' })\n  }\n\n  return file\n}\n```\n\n### Cleanup & Memory Management\n\nClean up blob URLs to prevent memory leaks:\n\n```tsx\nimport { useEffect } from 'react'\nimport { revokeImagePreview } from '@10play/claude-agent-sdk-ui'\n\nfunction ImageMessage({ attachment }) {\n  useEffect(() => {\n    // Cleanup when component unmounts\n    return () => {\n      if (attachment.preview) {\n        revokeImagePreview(attachment)\n      }\n    }\n  }, [attachment])\n\n  return <img src={attachment.preview} alt=\"\" />\n}\n```\n\n### WebSocket Reconnection\n\nImplement robust reconnection logic:\n\n```tsx\nfunction useRobustWebSocket(url: string) {\n  const [reconnectAttempts, setReconnectAttempts] = useState(0)\n  const maxRetries = 5\n\n  useEffect(() => {\n    let ws: WebSocket\n    let reconnectTimer: NodeJS.Timeout\n\n    const connect = () => {\n      ws = new WebSocket(url)\n\n      ws.onclose = () => {\n        if (reconnectAttempts < maxRetries) {\n          const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000)\n          reconnectTimer = setTimeout(() => {\n            setReconnectAttempts(a => a + 1)\n            connect()\n          }, delay)\n        }\n      }\n\n      ws.onopen = () => {\n        setReconnectAttempts(0) // Reset on successful connection\n      }\n    }\n\n    connect()\n\n    return () => {\n      clearTimeout(reconnectTimer)\n      ws?.close()\n    }\n  }, [url, reconnectAttempts])\n}\n```\n\n### Bundle Size Optimization\n\nIf you're not using certain features, you can reduce bundle size:\n\n```tsx\n// Instead of importing everything\nimport { FullChatApp } from '@10play/claude-agent-sdk-ui' // ~500KB\n\n// Import only what you need\nimport { ChatInterface } from 'claude-agent-sdk-ui/components/ChatInterface'\nimport { MessageInput } from 'claude-agent-sdk-ui/components/MessageInput'\n// This may reduce bundle size slightly\n\n// Note: Tree-shaking should handle this automatically with modern bundlers\n```\n\n### Best Practices Summary\n\n✅ **Do:**\n- Use virtualization for 100+ messages\n- Implement pagination for very long sessions\n- Debounce user input updates\n- Memoize expensive computations\n- Clean up blob URLs and subscriptions\n- Implement WebSocket reconnection logic\n- Optimize images before upload\n\n❌ **Don't:**\n- Render all messages unconditionally\n- Make API calls on every keystroke\n- Keep large message histories in memory\n- Forget to cleanup event listeners\n- Send unoptimized large images\n\n---\n\n## Common Patterns and Recipes\n\n### 1. Simple Text-Only Chat\n\n```tsx\nimport { FullChatApp } from '@10play/claude-agent-sdk-ui'\nimport '@10play/claude-agent-sdk-ui/styles.css'\n\nfunction App() {\n  return (\n    <FullChatApp\n      apiBaseUrl=\"http://localhost:4001/api\"\n      websocketUrl=\"ws://localhost:4001/ws\"\n      supportImages={false}\n      supportDocuments={false}\n    />\n  )\n}\n```\n\n### 2. Dark Mode by Default\n\n```tsx\n<FullChatApp\n  apiBaseUrl=\"http://localhost:4001/api\"\n  websocketUrl=\"ws://localhost:4001/ws\"\n  defaultTheme=\"dark\"\n/>\n```\n\n### 3. Custom Tool Configuration\n\n```tsx\n<FullChatApp\n  apiBaseUrl=\"http://localhost:4001/api\"\n  websocketUrl=\"ws://localhost:4001/ws\"\n  defaultSettings={{\n    model: 'claude-sonnet-4-5-20250929',\n    allowedTools: ['Read', 'Write', 'Bash'],\n    maxBudgetUsd: 1.0\n  }}\n/>\n```\n\n### 4. Building a Custom Chat Interface\n\n```tsx\nimport {\n  ChatInterface,\n  ThemeProvider,\n  ToolResultProvider,\n  useSessions,\n  createSessionApi\n} from '@10play/claude-agent-sdk-ui'\nimport '@10play/claude-agent-sdk-ui/styles.css'\n\nfunction CustomChatApp() {\n  const sessionApi = createSessionApi('http://localhost:4001/api')\n  const { currentSession, updateSession } = useSessions({ sessionApi })\n\n  return (\n    <ThemeProvider defaultTheme=\"light\">\n      <ToolResultProvider>\n        <div className=\"custom-layout\">\n          <header>My Custom Chat</header>\n          <ChatInterface\n            session={currentSession}\n            websocketUrl=\"ws://localhost:4001/ws\"\n            apiBaseUrl=\"http://localhost:4001/api\"\n            onSessionUpdate={updateSession}\n            supportImages={true}\n          />\n        </div>\n      </ToolResultProvider>\n    </ThemeProvider>\n  )\n}\n```\n\n### 5. Using Individual Components\n\n```tsx\nimport {\n  MessageInput,\n  StreamingMessage,\n  ThemeToggle,\n  useChatSession\n} from '@10play/claude-agent-sdk-ui'\nimport '@10play/claude-agent-sdk-ui/styles.css'\n\nfunction MinimalChat({ session }) {\n  const {\n    messages,\n    sendMessage,\n    isStreaming\n  } = useChatSession({\n    session,\n    websocketUrl: 'ws://localhost:4001/ws'\n  })\n\n  return (\n    <div className=\"flex flex-col h-screen bg-background\">\n      <header className=\"flex justify-between p-4 border-b\">\n        <h1>Chat</h1>\n        <ThemeToggle />\n      </header>\n\n      <div className=\"flex-1 overflow-y-auto p-4\">\n        {messages.map((msg, idx) => (\n          <StreamingMessage key={idx} event={msg} />\n        ))}\n      </div>\n\n      <MessageInput\n        onSendMessage={sendMessage}\n        disabled={isStreaming}\n      />\n    </div>\n  )\n}\n```\n\n### 6. Handling Tool Results\n\n```tsx\nimport { useToolResultContext } from '@10play/claude-agent-sdk-ui'\n\nfunction ToolResultsPanel() {\n  const { toolResults, clearToolResults } = useToolResultContext()\n\n  return (\n    <div className=\"tool-results\">\n      <h3>Tool Results ({toolResults.length})</h3>\n      <button onClick={clearToolResults}>Clear</button>\n      {toolResults.map((result, idx) => (\n        <div key={idx}>\n          <strong>{result.name}</strong>: {result.status}\n        </div>\n      ))}\n    </div>\n  )\n}\n```\n\n### 7. Custom Message Handling\n\n```tsx\nimport { useChatSession } from '@10play/claude-agent-sdk-ui'\n\nfunction ChatWithLogging({ session }) {\n  const { sendMessage, messages } = useChatSession({\n    session,\n    websocketUrl: 'ws://localhost:4001/ws',\n    onMessagesChange: (msgs) => {\n      // Custom logging or analytics\n      console.log('Messages updated:', msgs.length)\n      analytics.track('messages_changed', { count: msgs.length })\n    }\n  })\n\n  const handleSend = async (text: string) => {\n    // Pre-process message\n    const processed = text.trim().toLowerCase()\n\n    // Send to chat\n    await sendMessage(processed)\n\n    // Post-process\n    console.log('Message sent:', processed)\n  }\n\n  return <MessageInput onSendMessage={handleSend} />\n}\n```\n\n### 8. Implementing Session Persistence\n\n```tsx\nimport { downloadSessionAsJson, parseSessionImport } from '@10play/claude-agent-sdk-ui'\n\nfunction ChatWithPersistence() {\n  const handleExport = () => {\n    // Export current session\n    downloadSessionAsJson(messages, `chat-${Date.now()}.json`)\n  }\n\n  const handleImport = async (file: File) => {\n    const text = await file.text()\n    const { messages, title } = parseSessionImport(text)\n\n    // Load into session\n    await sessionApi.loadMessages({\n      title: title || 'Imported Session',\n      messages\n    })\n  }\n\n  return (\n    <>\n      <button onClick={handleExport}>Export Chat</button>\n      <input\n        type=\"file\"\n        accept=\".json\"\n        onChange={(e) => e.target.files?.[0] && handleImport(e.target.files[0])}\n      />\n    </>\n  )\n}\n```\n\n---\n\n## Troubleshooting\n\n### Common Issues\n\n#### 1. **Styles not loading**\n\n**Problem:** Components appear unstyled\n\n**Solution:** Make sure to import the CSS file:\n\n```tsx\nimport '@10play/claude-agent-sdk-ui/styles.css'\n```\n\nAdd this to your app's entry point (usually `main.tsx` or `App.tsx`).\n\n---\n\n#### 2. **WebSocket connection fails**\n\n**Problem:** `WebSocket connection failed` error\n\n**Solutions:**\n- Verify your backend server is running\n- Check the WebSocket URL format: `ws://localhost:4001/ws` (not `http://`)\n- For HTTPS sites, use `wss://` instead of `ws://`\n- Check CORS configuration on your backend\n\n```tsx\n// Development\n<FullChatApp websocketUrl=\"ws://localhost:4001/ws\" />\n\n// Production (HTTPS)\n<FullChatApp websocketUrl=\"wss://your-domain.com/ws\" />\n```\n\n---\n\n#### 3. **TypeScript errors with message types**\n\n**Problem:** Type errors when working with messages\n\n**Solution:** Import proper types from the SDK:\n\n```tsx\nimport type { StreamEvent, SDKMessage } from '@anthropic-ai/claude-agent-sdk'\n\n// Never use 'any' - always use proper types\nconst messages: StreamEvent[] = []  // ✅ Good\nconst messages: any[] = []          // ❌ Bad (violates architecture guidelines)\n```\n\n---\n\n#### 4. **Image/document attachments not working**\n\n**Problem:** Files aren't uploading or displaying\n\n**Solutions:**\n- Ensure `supportImages` and `supportDocuments` props are `true`\n- Check file size limits (browser-dependent, typically 10MB)\n- Verify file types:\n  - Images: PNG, JPEG, GIF, WebP\n  - Documents: PDF, TXT\n- Check browser console for errors\n\n```tsx\n<FullChatApp\n  supportImages={true}    // ✅ Enable images\n  supportDocuments={true} // ✅ Enable documents\n/>\n```\n\n---\n\n#### 5. **Theme not persisting**\n\n**Problem:** Theme resets on page reload\n\n**Solution:** Implement theme persistence:\n\n```tsx\nimport { ThemeProvider } from '@10play/claude-agent-sdk-ui'\n\nfunction App() {\n  const [theme, setTheme] = useState<'light' | 'dark'>(() => {\n    // Load from localStorage\n    return (localStorage.getItem('theme') as 'light' | 'dark') || 'light'\n  })\n\n  useEffect(() => {\n    // Save to localStorage\n    localStorage.setItem('theme', theme)\n  }, [theme])\n\n  return (\n    <ThemeProvider defaultTheme={theme}>\n      <FullChatApp {...props} />\n    </ThemeProvider>\n  )\n}\n```\n\n---\n\n#### 6. **Sessions not loading**\n\n**Problem:** Session list is empty or not loading\n\n**Solutions:**\n- Verify `apiBaseUrl` is correct and backend is running\n- Check network tab for failed API requests\n- Ensure backend endpoints match expected format:\n  - `GET /api/sessions` - List sessions\n  - `POST /api/sessions` - Create session\n  - `GET /api/sessions/:id` - Get session\n  - `DELETE /api/sessions/:id` - Delete session\n\n---\n\n#### 7. **Command autocomplete not showing**\n\n**Problem:** Slash commands don't trigger autocomplete\n\n**Solutions:**\n- Type `/` at the start of the message\n- Ensure you're using a component with command support:\n  - `MessageInput` ✅\n  - `MessageInputWithAttachments` ✅\n  - Custom input ❌ (unless you implement it)\n\n---\n\n#### 8. **Build errors with Vite/Webpack**\n\n**Problem:** Build fails with import errors\n\n**Solution:** Ensure peer dependencies are installed:\n\n```bash\nnpm install react react-dom lucide-react\n```\n\nFor Vite, add to `vite.config.ts`:\n\n```typescript\nexport default defineConfig({\n  optimizeDeps: {\n    include: ['@10play/claude-agent-sdk-ui']\n  }\n})\n```\n\n---\n\n#### 9. **Speech-to-text not working**\n\n**Problem:** Microphone button doesn't work\n\n**Solutions:**\n- Only works over HTTPS (or localhost)\n- Browser must support Web Speech API (Chrome, Edge, Safari)\n- User must grant microphone permissions\n- Check browser console for permission errors\n\n```tsx\nimport { useSpeechToText } from '@10play/claude-agent-sdk-ui'\n\nconst { browserSupportsSpeechRecognition } = useSpeechToText()\n\nif (!browserSupportsSpeechRecognition) {\n  console.warn('Speech recognition not supported')\n}\n```\n\n---\n\n#### 10. **Performance issues with large chat histories**\n\n**Problem:** UI becomes slow with many messages\n\n**Solutions:**\n- Implement message pagination\n- Limit rendered messages with windowing\n- Use React virtualization libraries for long lists\n\n```tsx\nimport { useChatSession } from '@10play/claude-agent-sdk-ui'\n\nfunction OptimizedChat({ session }) {\n  const { messages } = useChatSession({ session, websocketUrl })\n\n  // Only render last 100 messages\n  const recentMessages = messages.slice(-100)\n\n  return (\n    <MessageList>\n      {recentMessages.map(msg => (\n        <StreamingMessage key={msg.id} event={msg} />\n      ))}\n    </MessageList>\n  )\n}\n```\n\n---\n\n### Getting Help\n\n- **GitHub Issues**: [Report bugs or request features](https://github.com/your-username/claude-agent-sdk-ui/issues)\n- **Discussions**: [Ask questions and share ideas](https://github.com/your-username/claude-agent-sdk-ui/discussions)\n- **Documentation**: Check the main repo README for architecture guidelines\n\n---\n\n## Requirements\n\n- React 18+\n- TypeScript 5+\n- Modern browser with ES2020 support\n\n### Peer Dependencies\n\n```json\n{\n  \"react\": \">=18.0.0\",\n  \"react-dom\": \">=18.0.0\",\n  \"lucide-react\": \">=0.294.0\"\n}\n```\n\n> **Note:** The Claude Agent SDK (`@anthropic-ai/claude-agent-sdk`) is included as a direct dependency—you don't need to install it separately!\n\n---\n\n## License\n\nMIT © 10Play\n\n---\n\n## Contributing\n\nContributions are welcome! Please read the contributing guidelines in the main repository.\n\n---\n\n## Architecture Guidelines\n\nThis library follows strict architectural principles:\n\n1. **All message rendering components must be in the UI package** - Ensures consistency and reusability\n2. **Never use `any` types** - Always use proper types from `claude-agent-sdk` for type safety\n3. **Components are backend-agnostic** - UI components don't dictate backend implementation\n\nFor more details, see `CLAUDE.md` in the repository root.\n","readmeFilename":"README.md"}