From e7136976438fbb66cd7a28df601934c9163598cb Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Sun, 3 May 2026 01:53:54 -0700 Subject: [PATCH] feat(v2): auto-send file as a message on attach (no draft staging) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picking a file from the composer now uploads + sends a message containing the upload directive in one shot. The previous flow inserted `[[upload:fileName|…]]` into the textarea as raw text, which made the user see the raw token as they typed and felt off. The user's existing draft text is preserved untouched — they can keep typing and hit Send when ready, and the file lands as its own message. Matches the drag-and-drop pattern in Slack / Discord / Linear: pick → goes now. Images keep the existing inline-image behavior. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/src/v2/components/V2PodChat.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/v2/components/V2PodChat.tsx b/frontend/src/v2/components/V2PodChat.tsx index dabf5973..7495c4a7 100644 --- a/frontend/src/v2/components/V2PodChat.tsx +++ b/frontend/src/v2/components/V2PodChat.tsx @@ -455,19 +455,22 @@ const V2PodChat: React.FC = ({ detail, inspectorCollapsed, onTog } }; - // Composer attach: handles both images (sends as standalone image message, - // legacy v2 behavior) and other file kinds (PDF / md / txt / csv / json, - // inserts an [[upload:fileName|originalName|size|kind]] directive into the - // draft so the user can add accompanying text and send when ready). Both - // paths POST to /api/uploads with the active podId so the file shows up in - // the inspector's Artifacts section. + // Composer attach: pick a file → upload → send a message containing just + // the [[upload:fileName|originalName|size|kind]] directive (or the raw + // image URL for image kinds, which the bubble renders inline). Drafts the + // user is composing are preserved untouched — they can keep typing and + // hit Send when ready, and the file lands as its own message. + // + // Drag-and-drop in chat clients (Slack, Discord, Linear) all match this + // shape: pick → goes-now. Inserting a directive token into the textarea + // showed raw `[[upload:…]]` text as the user typed and felt off. const handleAttachFile = async (file: File | null) => { if (!file || uploading) return; setUploading(true); setComposerError(null); try { const formData = new FormData(); - formData.append('image', file); // legacy multer field name + formData.append('image', file); // legacy multer field name; route accepts non-images formData.append('podId', pod._id); const uploaded = await api.post<{ url?: string; @@ -484,7 +487,7 @@ const V2PodChat: React.FC = ({ detail, inspectorCollapsed, onTog } if (uploaded.fileName) { const directive = `[[upload:${uploaded.fileName}|${uploaded.originalName || file.name}|${uploaded.size || file.size}|${uploaded.kind || 'file'}]]`; - setDraft((prev) => (prev ? `${prev.replace(/\s+$/, '')} ${directive}` : directive)); + await sendMessage(directive); } } catch (err) { const e = err as { response?: { data?: { msg?: string } } };