mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-18 03:13:08 +00:00
feat: Add image upload support for Spec Creation chat
Add the ability to attach images (JPEG, PNG) in the Spec Creation chat interface for Claude to analyze during app specification creation. Frontend changes: - Add ImageAttachment interface to types.ts with id, filename, mimeType, base64Data, previewUrl, and size fields - Update ChatMessage interface with optional attachments field - Update useSpecChat hook to accept and send attachments via WebSocket - Add file input, drag-drop support, and preview thumbnails to SpecCreationChat component with validation (5 MB max, JPEG/PNG only) - Update ChatMessage component to render image attachments with click-to-enlarge functionality Backend changes: - Add ImageAttachment Pydantic schema with base64 validation - Update spec_creation.py WebSocket handler to parse and validate image attachments from client messages - Update spec_chat_session.py to format multimodal content blocks for Claude API using async generator pattern Features: - Drag-and-drop or click paperclip button to attach images - Preview thumbnails with remove button before sending - File type validation (image/jpeg, image/png) - File size validation (5 MB maximum) - Images display in chat history - Click images to view full size - Cross-platform compatible (Windows, macOS, Linux) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useCallback, useRef, useEffect } from 'react'
|
||||
import type { ChatMessage, SpecChatServerMessage, SpecQuestion } from '../lib/types'
|
||||
import type { ChatMessage, ImageAttachment, SpecChatServerMessage, SpecQuestion } from '../lib/types'
|
||||
|
||||
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error'
|
||||
|
||||
@@ -21,7 +21,7 @@ interface UseSpecChatReturn {
|
||||
currentQuestions: SpecQuestion[] | null
|
||||
currentToolId: string | null
|
||||
start: () => void
|
||||
sendMessage: (content: string) => void
|
||||
sendMessage: (content: string, attachments?: ImageAttachment[]) => void
|
||||
sendAnswer: (answers: Record<string, string | string[]>) => void
|
||||
disconnect: () => void
|
||||
}
|
||||
@@ -303,19 +303,20 @@ export function useSpecChat({
|
||||
setTimeout(checkAndSend, 100)
|
||||
}, [connect])
|
||||
|
||||
const sendMessage = useCallback((content: string) => {
|
||||
const sendMessage = useCallback((content: string, attachments?: ImageAttachment[]) => {
|
||||
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
|
||||
onError?.('Not connected')
|
||||
return
|
||||
}
|
||||
|
||||
// Add user message to chat
|
||||
// Add user message to chat (with attachments for display)
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content,
|
||||
attachments,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
])
|
||||
@@ -325,8 +326,23 @@ export function useSpecChat({
|
||||
setCurrentToolId(null)
|
||||
setIsLoading(true)
|
||||
|
||||
// Build message payload
|
||||
const payload: { type: string; content: string; attachments?: Array<{ filename: string; mimeType: string; base64Data: string }> } = {
|
||||
type: 'message',
|
||||
content,
|
||||
}
|
||||
|
||||
// Add attachments if present (send base64 data, not preview URL)
|
||||
if (attachments && attachments.length > 0) {
|
||||
payload.attachments = attachments.map((a) => ({
|
||||
filename: a.filename,
|
||||
mimeType: a.mimeType,
|
||||
base64Data: a.base64Data,
|
||||
}))
|
||||
}
|
||||
|
||||
// Send to server
|
||||
wsRef.current.send(JSON.stringify({ type: 'message', content }))
|
||||
wsRef.current.send(JSON.stringify(payload))
|
||||
}, [onError])
|
||||
|
||||
const sendAnswer = useCallback((answers: Record<string, string | string[]>) => {
|
||||
|
||||
Reference in New Issue
Block a user