diff --git a/src/components/chat/ChatView.tsx b/src/components/chat/ChatView.tsx index 80c5c1d..4e0e56b 100644 --- a/src/components/chat/ChatView.tsx +++ b/src/components/chat/ChatView.tsx @@ -147,7 +147,7 @@ export function ChatView({ sessionId, initialMessages = [], modelName, initialMo // Build display content: embed file metadata as HTML comment for MessageItem to parse let displayContent = content; if (files && files.length > 0) { - const fileMeta = files.map(f => ({ id: f.id, name: f.name, type: f.type, size: f.size })); + const fileMeta = files.map(f => ({ id: f.id, name: f.name, type: f.type, size: f.size, data: f.data })); displayContent = `${content}`; } diff --git a/src/components/chat/MessageInput.tsx b/src/components/chat/MessageInput.tsx index b81abc2..1d618ed 100644 --- a/src/components/chat/MessageInput.tsx +++ b/src/components/chat/MessageInput.tsx @@ -770,6 +770,9 @@ export function MessageInput({ /> + {/* Attach file button */} + + {/* Folder picker button */} setFolderPickerOpen(true)} @@ -781,9 +784,6 @@ export function MessageInput({ - {/* Attach file button */} - - {/* Mode selector */}
0) { - // Allowed image types for Claude multimodal: JPEG, PNG, GIF, WebP; max 5MB each - const allowedImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; - const maxImageSize = 5 * 1024 * 1024; - const imageFiles = files.filter(f => - isImageFile(f.type) && allowedImageTypes.includes(f.type) && f.size <= maxImageSize - ); - const nonImageFiles = files.filter(f => !isImageFile(f.type)); - - // Save non-image files to disk so Claude's Read tool can access them - let fileReferences = ''; - if (nonImageFiles.length > 0) { - const workDir = workingDirectory || process.cwd(); - const savedPaths = saveUploadedFiles(nonImageFiles, workDir); - fileReferences = savedPaths - .map((p, i) => `[User attached file: ${p} (${nonImageFiles[i].name})]`) - .join('\n') + '\n\n'; - } - - // For image files, construct multimodal content blocks - if (imageFiles.length > 0) { - const contentBlocks: unknown[] = []; - - // Add image content blocks - for (const img of imageFiles) { - // Supported: image/jpeg, image/png, image/gif, image/webp - const mediaType = img.type as 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp'; - contentBlocks.push({ - type: 'image', - source: { - type: 'base64', - media_type: mediaType, - data: img.data, - }, - }); - } - - // Add text block with optional file references prepended - contentBlocks.push({ - type: 'text', - text: fileReferences + prompt, - }); - - finalPrompt = { content: contentBlocks }; - } else { - // No images, just prepend file references to the text prompt - finalPrompt = fileReferences + prompt; - } + const workDir = workingDirectory || process.cwd(); + const savedPaths = saveUploadedFiles(files, workDir); + const fileReferences = savedPaths + .map((p, i) => { + const f = files[i]; + if (isImageFile(f.type)) { + return `[User attached image: ${p} (${f.name})]`; + } + return `[User attached file: ${p} (${f.name})]`; + }) + .join('\n'); + finalPrompt = `${fileReferences}\n\nPlease read the attached file(s) above using your Read tool, then respond to the user's message:\n\n${prompt}`; } const conversation = query({ - prompt: finalPrompt as string, + prompt: finalPrompt, options: queryOptions, });