diff --git a/apps/web/src/lib/attachments.ts b/apps/web/src/lib/attachments.ts index af06c5c..264448f 100644 --- a/apps/web/src/lib/attachments.ts +++ b/apps/web/src/lib/attachments.ts @@ -56,19 +56,26 @@ export function inferLanguage(filename: string): string | null { export function flattenToMessage(attachments: Attachment[], text: string): string { if (attachments.length === 0) return text; - const blocks = attachments.map(a => { - // Pasted text is raw context, not code from a file — insert it verbatim with - // no ``` fence or provenance header. The chip only exists to keep the textarea - // tidy while composing; on send it should be exactly what the user pasted. + // Pasted text is raw context, not code from a file — insert it verbatim with no + // ``` fence or provenance header. It trails the typed text with a leading space + // so a leading slash command / prompt stays first and the paste reads as its + // continuation. File/line chips stay fenced provenance blocks, appended after. + const pasteBlocks: string[] = []; + const fencedBlocks: string[] = []; + for (const a of attachments) { if (a.kind === 'paste') { - return a.content; + pasteBlocks.push(a.content); + continue; } const fence = '```' + (a.language ?? ''); const header = a.kind === 'lines' ? `// from: ${a.filename}:${a.range?.[0] ?? '?'}-${a.range?.[1] ?? '?'}` : `// from: ${a.filename}`; - return `${fence}\n${header}\n${a.content}\n\`\`\``; - }); - return [...blocks, text].filter(Boolean).join('\n\n'); + fencedBlocks.push(`${fence}\n${header}\n${a.content}\n\`\`\``); + } + // Typed text + pasted content on the same logical line (space-joined), then + // any fenced file blocks as separate paragraphs. + const lead = [text, ...pasteBlocks].filter(Boolean).join(' '); + return [lead, ...fencedBlocks].filter(Boolean).join('\n\n'); }