1import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png';
  2import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png';
  3import { FileTypeCategory } from '$lib/enums';
  4import { modelsStore } from '$lib/stores/models.svelte';
  5import { settingsStore } from '$lib/stores/settings.svelte';
  6import { toast } from 'svelte-sonner';
  7import { getFileTypeCategory } from '$lib/utils';
  8import { convertPDFToText } from './pdf-processing';
  9
 10/**
 11 * Read a file as a data URL (base64 encoded)
 12 * @param file - The file to read
 13 * @returns Promise resolving to the data URL string
 14 */
 15function readFileAsDataURL(file: File): Promise<string> {
 16	return new Promise((resolve, reject) => {
 17		const reader = new FileReader();
 18		reader.onload = () => resolve(reader.result as string);
 19		reader.onerror = () => reject(reader.error);
 20		reader.readAsDataURL(file);
 21	});
 22}
 23
 24/**
 25 * Read a file as UTF-8 text
 26 * @param file - The file to read
 27 * @returns Promise resolving to the text content
 28 */
 29function readFileAsUTF8(file: File): Promise<string> {
 30	return new Promise((resolve, reject) => {
 31		const reader = new FileReader();
 32		reader.onload = () => resolve(reader.result as string);
 33		reader.onerror = () => reject(reader.error);
 34		reader.readAsText(file);
 35	});
 36}
 37
 38/**
 39 * Process uploaded files into ChatUploadedFile format with previews and content
 40 *
 41 * This function processes various file types and generates appropriate previews:
 42 * - Images: Base64 data URLs with format normalization (SVG/WebP → PNG)
 43 * - Text files: UTF-8 content extraction
 44 * - PDFs: Metadata only (processed later in conversion pipeline)
 45 * - Audio: Base64 data URLs for preview
 46 *
 47 * @param files - Array of File objects to process
 48 * @returns Promise resolving to array of ChatUploadedFile objects
 49 */
 50export async function processFilesToChatUploaded(
 51	files: File[],
 52	activeModelId?: string
 53): Promise<ChatUploadedFile[]> {
 54	const results: ChatUploadedFile[] = [];
 55
 56	for (const file of files) {
 57		const id = Date.now().toString() + Math.random().toString(36).substr(2, 9);
 58		const base: ChatUploadedFile = {
 59			id,
 60			name: file.name,
 61			size: file.size,
 62			type: file.type,
 63			file
 64		};
 65
 66		try {
 67			if (getFileTypeCategory(file.type) === FileTypeCategory.IMAGE) {
 68				let preview = await readFileAsDataURL(file);
 69
 70				// Normalize SVG and WebP to PNG in previews
 71				if (isSvgMimeType(file.type)) {
 72					try {
 73						preview = await svgBase64UrlToPngDataURL(preview);
 74					} catch (err) {
 75						console.error('Failed to convert SVG to PNG:', err);
 76					}
 77				} else if (isWebpMimeType(file.type)) {
 78					try {
 79						preview = await webpBase64UrlToPngDataURL(preview);
 80					} catch (err) {
 81						console.error('Failed to convert WebP to PNG:', err);
 82					}
 83				}
 84
 85				results.push({ ...base, preview });
 86			} else if (getFileTypeCategory(file.type) === FileTypeCategory.PDF) {
 87				// Extract text content from PDF for preview
 88				try {
 89					const textContent = await convertPDFToText(file);
 90					results.push({ ...base, textContent });
 91				} catch (err) {
 92					console.warn('Failed to extract text from PDF, adding without content:', err);
 93					results.push(base);
 94				}
 95
 96				// Show suggestion toast if vision model is available but PDF as image is disabled
 97				const hasVisionSupport = activeModelId
 98					? modelsStore.modelSupportsVision(activeModelId)
 99					: false;
100				const currentConfig = settingsStore.config;
101				if (hasVisionSupport && !currentConfig.pdfAsImage) {
102					toast.info(`You can enable parsing PDF as images with vision models.`, {
103						duration: 8000,
104						action: {
105							label: 'Enable PDF as Images',
106							onClick: () => {
107								settingsStore.updateConfig('pdfAsImage', true);
108								toast.success('PDF parsing as images enabled!', {
109									duration: 3000
110								});
111							}
112						}
113					});
114				}
115			} else if (getFileTypeCategory(file.type) === FileTypeCategory.AUDIO) {
116				// Generate preview URL for audio files
117				const preview = await readFileAsDataURL(file);
118				results.push({ ...base, preview });
119			} else {
120				// Fallback: treat unknown files as text
121				try {
122					const textContent = await readFileAsUTF8(file);
123					results.push({ ...base, textContent });
124				} catch (err) {
125					console.warn('Failed to read file as text, adding without content:', err);
126					results.push(base);
127				}
128			}
129		} catch (error) {
130			console.error('Error processing file', file.name, error);
131			results.push(base);
132		}
133	}
134
135	return results;
136}