Add file attachments with @ mentions and drag & drop support

- Create attachment type system with file, text, symbol, and agent sources
- Implement file picker with SDK integration (find.files, file.status)
- Add @ detection in prompt input to trigger file search
- Create attachment chips UI for managing attached files
- Add attachment state management per session
- Update message submission to include attachments
- Implement drag & drop support for adding files
- Show git-modified files first in file picker with +/- indicators
- Filter files as user types after @
- Clear attachments after successful message send
This commit is contained in:
Shantur Rathore
2025-10-23 22:46:29 +01:00
parent 9dfb3cd612
commit bdd9837538
7 changed files with 536 additions and 18 deletions

47
src/stores/attachments.ts Normal file
View File

@@ -0,0 +1,47 @@
import { createSignal } from "solid-js"
import type { Attachment } from "../types/attachment"
const [attachments, setAttachments] = createSignal<Map<string, Attachment[]>>(new Map())
function getSessionKey(instanceId: string, sessionId: string): string {
return `${instanceId}:${sessionId}`
}
function getAttachments(instanceId: string, sessionId: string): Attachment[] {
const key = getSessionKey(instanceId, sessionId)
return attachments().get(key) || []
}
function addAttachment(instanceId: string, sessionId: string, attachment: Attachment) {
const key = getSessionKey(instanceId, sessionId)
setAttachments((prev) => {
const next = new Map(prev)
const existing = next.get(key) || []
next.set(key, [...existing, attachment])
return next
})
}
function removeAttachment(instanceId: string, sessionId: string, attachmentId: string) {
const key = getSessionKey(instanceId, sessionId)
setAttachments((prev) => {
const next = new Map(prev)
const existing = next.get(key) || []
next.set(
key,
existing.filter((a) => a.id !== attachmentId),
)
return next
})
}
function clearAttachments(instanceId: string, sessionId: string) {
const key = getSessionKey(instanceId, sessionId)
setAttachments((prev) => {
const next = new Map(prev)
next.delete(key)
return next
})
}
export { getAttachments, addAttachment, removeAttachment, clearAttachments }

View File

@@ -626,7 +626,7 @@ async function sendMessage(
instanceId: string,
sessionId: string,
prompt: string,
attachments: string[] = [],
attachments: any[] = [],
): Promise<void> {
const instance = instances().get(instanceId)
if (!instance || !instance.client) {
@@ -639,13 +639,33 @@ async function sendMessage(
throw new Error("Session not found")
}
const parts: any[] = [
{
type: "text" as const,
text: prompt,
},
]
if (attachments.length > 0) {
for (const att of attachments) {
const source = att.source
if (source.type === "file") {
parts.push({
type: "file" as const,
path: source.path,
mime: source.mime,
})
} else if (source.type === "text") {
parts.push({
type: "text" as const,
text: source.value,
})
}
}
}
const requestBody = {
parts: [
{
type: "text" as const,
text: prompt,
},
],
parts,
...(session.agent && { agent: session.agent }),
...(session.model.providerId &&
session.model.modelId && {