Files
CodeNomad/src/types/attachment.ts
2025-10-28 21:36:16 +00:00

129 lines
2.7 KiB
TypeScript

export interface Attachment {
id: string
type: AttachmentType
display: string
url: string
filename: string
mediaType: string
source: AttachmentSource
}
export type AttachmentType = "file" | "text" | "symbol" | "agent"
export type AttachmentSource = FileSource | TextSource | SymbolSource | AgentSource
export interface FileSource {
type: "file"
path: string
mime: string
data?: Uint8Array
}
export interface TextSource {
type: "text"
value: string
}
export interface SymbolSource {
type: "symbol"
path: string
name: string
kind: number
range: SymbolRange
}
export interface SymbolRange {
start: Position
end: Position
}
export interface Position {
line: number
char: number
}
export interface AgentSource {
type: "agent"
name: string
}
export function createFileAttachment(
path: string,
filename: string,
mime: string = "text/plain",
data?: Uint8Array,
workspaceRoot?: string,
): Attachment {
let fileUrl = path
if (workspaceRoot && !path.startsWith("file://")) {
const absolutePath = path.startsWith("/") ? path : `${workspaceRoot}/${path}`
fileUrl = `file://${absolutePath}`
} else if (!path.startsWith("file://") && path.startsWith("/")) {
fileUrl = `file://${path}`
}
return {
id: crypto.randomUUID(),
type: "file",
display: `@${filename}`,
url: fileUrl,
filename,
mediaType: mime,
source: {
type: "file",
path: path,
mime,
data,
},
}
}
export function createTextAttachment(value: string, display: string, filename: string): Attachment {
const base64 = encodeTextAsBase64(value)
return {
id: crypto.randomUUID(),
type: "text",
display,
url: `data:text/plain;base64,${base64}`,
filename,
mediaType: "text/plain",
source: {
type: "text",
value,
},
}
}
function encodeTextAsBase64(value: string): string {
if (typeof TextEncoder !== "undefined") {
const encoder = new TextEncoder()
const bytes = encoder.encode(value)
let binary = ""
const chunkSize = 0x8000
for (let index = 0; index < bytes.length; index += chunkSize) {
const chunk = bytes.subarray(index, Math.min(index + chunkSize, bytes.length))
binary += String.fromCharCode(...chunk)
}
return btoa(binary)
}
return btoa(
encodeURIComponent(value).replace(/%([0-9A-F]{2})/g, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16))),
)
}
export function createAgentAttachment(agentName: string): Attachment {
return {
id: crypto.randomUUID(),
type: "agent",
display: `@${agentName}`,
url: "",
filename: agentName,
mediaType: "text/plain",
source: {
type: "agent",
name: agentName,
},
}
}