feat: add file upload and preview support (images and documents)
- Support image (JPEG/PNG/GIF/WebP, max 5MB) and document uploads - Images sent as multimodal content blocks to Claude SDK - Documents saved to temp dir for Claude's Read tool - Emerald capsule badges in input for attached files - Drag-and-drop, paste, and file picker support via PromptInput - Chat display: image thumbnails with lightbox, file cards - Lightbox uses Radix Dialog with prev/next navigation - File metadata embedded in message content as HTML comments - Compatible with existing slash command badge system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ac1f8cea3
commit
164f730da1
11 files changed
+562
-22
No files matched your search
@@ -1,15 +1,15 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import { streamClaude } from '@/lib/claude-client';
|
||||
import { addMessage, getSession, updateSessionTitle, updateSdkSessionId, getSetting } from '@/lib/db';
|
||||
import type { SendMessageRequest, SSEEvent, TokenUsage, MessageContentBlock } from '@/types';
|
||||
import type { SendMessageRequest, SSEEvent, TokenUsage, MessageContentBlock, FileAttachment } from '@/types';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body: SendMessageRequest = await request.json();
|
||||
const { session_id, content, model, mode } = body;
|
||||
const body: SendMessageRequest & { files?: FileAttachment[] } = await request.json();
|
||||
const { session_id, content, model, mode, files } = body;
|
||||
|
||||
if (!session_id || !content) {
|
||||
return new Response(JSON.stringify({ error: 'session_id and content are required' }), {
|
||||
@@ -63,6 +63,17 @@ export async function POST(request: NextRequest) {
|
||||
abortController.abort();
|
||||
});
|
||||
|
||||
// Convert file attachments to the format expected by streamClaude
|
||||
const fileAttachments: FileAttachment[] | undefined = files && files.length > 0
|
||||
? files.map((f, i) => ({
|
||||
id: f.id || `file-${Date.now()}-${i}`,
|
||||
name: f.name,
|
||||
type: f.type,
|
||||
size: f.size,
|
||||
data: f.data,
|
||||
}))
|
||||
: undefined;
|
||||
|
||||
// Stream Claude response, using SDK session ID for resume if available
|
||||
const stream = streamClaude({
|
||||
prompt: content,
|
||||
@@ -73,6 +84,7 @@ export async function POST(request: NextRequest) {
|
||||
workingDirectory: session.working_directory || undefined,
|
||||
abortController,
|
||||
permissionMode,
|
||||
files: fileAttachments,
|
||||
});
|
||||
|
||||
// Tee the stream: one for client, one for collecting the response
|
||||
|
||||
Reference in new issue
Block a user