diff --git a/docs/llm-chat-proposal.md b/docs/llm-chat-proposal.md new file mode 100644 index 0000000..184a63b --- /dev/null +++ b/docs/llm-chat-proposal.md @@ -0,0 +1,459 @@ +# LLM Chat Interface - Technical Proposal + +## Overview + +Create a separate LLM chat interface for conversing with AI models, reusing generic scroll/layout patterns from ChatViewer while building LLM-specific features. + +## Architecture + +``` +src/components/ + LLMChatViewer.tsx # Main LLM chat component (similar to ChatViewer) + llm/ + MessageItem.tsx # LLM message renderer (user/assistant/system) + CodeBlock.tsx # Syntax-highlighted code with copy button + StreamingMessage.tsx # Real-time token streaming display + ConfigPanel.tsx # Model/temperature/system prompt settings + MessageActions.tsx # Copy, regenerate, edit buttons + shared/ # Extracted from ChatViewer + VirtualizedTimeline.tsx # Generic Virtuoso wrapper + DayMarker.tsx # Date separator component + ScrollToMessage.ts # Scroll utilities + +src/lib/ + llm/ + anthropic-client.ts # Claude API client (streaming support) + message-formatter.ts # Markdown + code block parsing + token-counter.ts # Token usage tracking + llm-parser.ts # Command parser for `llm` command + +src/types/ + llm.ts # LLM-specific types (separate from chat.ts) + +src/services/ + llm-history.ts # Persist conversations to Dexie +``` + +## Type System + +```typescript +// src/types/llm.ts + +export interface LLMMessage { + id: string; + role: "user" | "assistant" | "system"; + content: string; + timestamp: number; + tokens?: number; + model?: string; + streaming?: boolean; // Currently being streamed + error?: string; // Error message if failed +} + +export interface LLMConversation { + id: string; + title: string; // Auto-generated from first message + messages: LLMMessage[]; + systemPrompt?: string; + model: string; // "claude-3-5-sonnet-20241022", etc. + temperature?: number; + maxTokens?: number; + createdAt: number; + updatedAt: number; + totalTokens: number; // Running total +} + +export interface LLMConfig { + model: string; + systemPrompt?: string; + temperature: number; + maxTokens: number; +} + +export interface StreamChunk { + text: string; + done: boolean; + error?: string; +} +``` + +## Key Components + +### 1. LLMChatViewer (Main Component) + +Similar structure to ChatViewer, but simplified: + +```typescript +interface LLMChatViewerProps { + conversationId?: string; // Optional, creates new if not provided + customTitle?: string; +} + +function LLMChatViewer({ conversationId, customTitle }: LLMChatViewerProps) { + const [conversation, setConversation] = useState(); + const [streamingMessageId, setStreamingMessageId] = useState(); + const [isStreaming, setIsStreaming] = useState(false); + const virtuosoRef = useRef(null); + + // Load/create conversation + // Render messages with Virtuoso + // Handle send (streaming response) + // Handle regenerate, edit, copy +} +``` + +**Reuses from ChatViewer:** +- Virtuoso scroll setup (lines 549-580) +- Day marker injection logic (lines 288-312) +- Scroll-to-bottom on new messages +- Message hover effects + +**LLM-specific additions:** +- Streaming message updates +- Stop generation button +- Token counter display +- Config panel toggle + +### 2. MessageItem (LLM Messages) + +```typescript +interface MessageItemProps { + message: LLMMessage; + onRegenerate?: () => void; + onEdit?: () => void; + onCopy?: () => void; +} + +function MessageItem({ message, onRegenerate, onEdit, onCopy }: MessageItemProps) { + // Different styling for user vs assistant + // Parse markdown and code blocks + // Show tokens, timestamp + // Action buttons (copy, regenerate, edit) +} +``` + +**Styling approach:** +- User messages: Right-aligned, primary color background +- Assistant messages: Left-aligned, with robot icon +- System messages: Centered, subtle styling +- Streaming: Blinking cursor after last token + +### 3. CodeBlock (Syntax Highlighting) + +```typescript +interface CodeBlockProps { + code: string; + language: string; +} + +function CodeBlock({ code, language }: CodeBlockProps) { + // Use Prism.js or Shiki for syntax highlighting + // Copy button in top-right corner + // Language badge +} +``` + +Libraries to add: +- `react-syntax-highlighter` or `shiki` +- Use theme matching Grimoire's dark mode + +### 4. StreamingMessage (Real-time Display) + +```typescript +interface StreamingMessageProps { + messageId: string; + initialContent: string; + onChunk: (chunk: StreamChunk) => void; + onComplete: () => void; +} + +function StreamingMessage({ messageId, initialContent, onChunk, onComplete }: StreamingMessageProps) { + const [content, setContent] = useState(initialContent); + + useEffect(() => { + // Subscribe to streaming updates + // Append chunks as they arrive + // Parse markdown incrementally + // Show blinking cursor + }, [messageId]); +} +``` + +### 5. ConfigPanel (Settings Sidebar/Modal) + +```typescript +interface ConfigPanelProps { + config: LLMConfig; + onChange: (config: LLMConfig) => void; + onClear: () => void; +} + +function ConfigPanel({ config, onChange, onClear }: ConfigPanelProps) { + return ( +
+ {/* Model selector */} + + + {/* System prompt */} +