{"version":3,"file":"utils.cjs","names":[],"sources":["../../src/attachments/utils.ts"],"sourcesContent":["import type { AttachmentModality } from \"./types\";\nimport type { InputContentSource } from \"../types/message\";\n\nconst DEFAULT_MAX_SIZE = 20 * 1024 * 1024; // 20MB\n\n/**\n * Derive the attachment modality from a MIME type string.\n */\nexport function getModalityFromMimeType(mimeType: string): AttachmentModality {\n  if (mimeType.startsWith(\"image/\")) return \"image\";\n  if (mimeType.startsWith(\"audio/\")) return \"audio\";\n  if (mimeType.startsWith(\"video/\")) return \"video\";\n  return \"document\";\n}\n\n/**\n * Format a byte count as a human-readable file size string.\n */\nexport function formatFileSize(bytes: number): string {\n  if (bytes < 1024) return `${bytes} B`;\n  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Check if a file exceeds the maximum allowed size.\n */\nexport function exceedsMaxSize(\n  file: File,\n  maxSize: number = DEFAULT_MAX_SIZE,\n): boolean {\n  return file.size > maxSize;\n}\n\n/**\n * Read a File as a base64 string (without the data URL prefix).\n */\nexport function readFileAsBase64(file: File): Promise<string> {\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n    reader.onload = (e) => {\n      const result = e.target?.result as string;\n      const base64 = result?.split(\",\")[1];\n      if (base64) {\n        resolve(base64);\n      } else {\n        reject(new Error(\"Failed to read file as base64\"));\n      }\n    };\n    reader.onerror = reject;\n    reader.readAsDataURL(file);\n  });\n}\n\n/**\n * Generate a thumbnail data URL from a video file by capturing a frame near the start (at 0.1s).\n * Returns undefined if thumbnail generation fails or if called outside a browser environment.\n */\nexport function generateVideoThumbnail(\n  file: File,\n): Promise<string | undefined> {\n  if (typeof document === \"undefined\") {\n    return Promise.resolve(undefined);\n  }\n  return new Promise((resolve) => {\n    let resolved = false;\n    const video = document.createElement(\"video\");\n    const canvas = document.createElement(\"canvas\");\n    const url = URL.createObjectURL(file);\n\n    const cleanup = (result: string | undefined) => {\n      if (resolved) return;\n      resolved = true;\n      URL.revokeObjectURL(url);\n      resolve(result);\n    };\n\n    const timeout = setTimeout(() => {\n      console.warn(\n        `[CopilotKit] generateVideoThumbnail: timed out for file \"${file.name}\"`,\n      );\n      cleanup(undefined);\n    }, 10000);\n\n    video.preload = \"metadata\";\n    video.muted = true;\n    video.playsInline = true;\n\n    video.onloadeddata = () => {\n      video.currentTime = 0.1;\n    };\n\n    video.onseeked = () => {\n      clearTimeout(timeout);\n      canvas.width = video.videoWidth;\n      canvas.height = video.videoHeight;\n      const ctx = canvas.getContext(\"2d\");\n      if (ctx) {\n        ctx.drawImage(video, 0, 0);\n        const thumbnail = canvas.toDataURL(\"image/jpeg\", 0.7);\n        cleanup(thumbnail);\n      } else {\n        console.warn(\n          \"[CopilotKit] generateVideoThumbnail: could not get 2d canvas context\",\n        );\n        cleanup(undefined);\n      }\n    };\n\n    video.onerror = () => {\n      clearTimeout(timeout);\n      console.warn(\n        `[CopilotKit] generateVideoThumbnail: video element error for file \"${file.name}\"`,\n      );\n      cleanup(undefined);\n    };\n\n    video.src = url;\n  });\n}\n\n/**\n * Check if a file's MIME type matches an accept filter string.\n * Handles file extensions (e.g. \".pdf\"), MIME wildcards (\"image/*\"), and comma-separated lists.\n */\nexport function matchesAcceptFilter(file: File, accept: string): boolean {\n  if (!accept || accept === \"*/*\") return true;\n\n  const filters = accept.split(\",\").map((f) => f.trim());\n  return filters.some((filter) => {\n    if (filter.startsWith(\".\")) {\n      return (file.name ?? \"\").toLowerCase().endsWith(filter.toLowerCase());\n    }\n    if (filter.endsWith(\"/*\")) {\n      const prefix = filter.slice(0, -2);\n      return file.type.startsWith(prefix + \"/\");\n    }\n    return file.type === filter;\n  });\n}\n\n/**\n * Convert an InputContentSource to a usable URL string.\n * For data sources, returns a base64 data URL; for URL sources, returns the URL directly.\n */\nexport function getSourceUrl(source: InputContentSource): string {\n  if (source.type === \"url\") {\n    return source.value;\n  }\n  return `data:${source.mimeType};base64,${source.value}`;\n}\n\n/**\n * Return a short human-readable label for a document MIME type (e.g. \"PDF\", \"DOC\").\n */\nexport function getDocumentIcon(mimeType: string): string {\n  if (mimeType.includes(\"pdf\")) return \"PDF\";\n  if (mimeType.includes(\"sheet\") || mimeType.includes(\"excel\")) return \"XLS\";\n  if (mimeType.includes(\"presentation\") || mimeType.includes(\"powerpoint\"))\n    return \"PPT\";\n  if (mimeType.includes(\"word\") || mimeType.includes(\"document\")) return \"DOC\";\n  if (mimeType.includes(\"text/\")) return \"TXT\";\n  return \"FILE\";\n}\n"],"mappings":"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"}