浏览代码

enh: support non chrome browsers

Timothy J. Baek 7 月之前
父节点
当前提交
9f812e7022
共有 1 个文件被更改,包括 139 次插入50 次删除
  1. 139 50
      src/lib/components/workspace/Knowledge/Collection.svelte

+ 139 - 50
src/lib/components/workspace/Knowledge/Collection.svelte

@@ -129,75 +129,164 @@
 	};
 
 	const uploadDirectoryHandler = async () => {
-		try {
-			// Get directory handle through picker
-			const dirHandle = await window.showDirectoryPicker();
+		// Check if File System Access API is supported
+		const isFileSystemAccessSupported = 'showDirectoryPicker' in window;
 
-			console.log(typeof dirHandle);
-			console.log(dirHandle);
+		try {
+			if (isFileSystemAccessSupported) {
+				// Modern browsers (Chrome, Edge) implementation
+				await handleModernBrowserUpload();
+			} else {
+				// Firefox fallback
+				await handleFirefoxUpload();
+			}
+		} catch (error) {
+			handleUploadError(error);
+		}
+	};
 
-			let totalFiles = 0;
-			let uploadedFiles = 0;
+	// Helper function to check if a path contains hidden folders
+	const hasHiddenFolder = (path) => {
+		return path.split('/').some((part) => part.startsWith('.'));
+	};
 
-			// Function to update the UI with the progress
-			const updateProgress = () => {
-				const percentage = (uploadedFiles / totalFiles) * 100;
-				toast.info(`Upload Progress: ${uploadedFiles}/${totalFiles} (${percentage.toFixed(2)}%)`);
-			};
+	// Modern browsers implementation using File System Access API
+	const handleModernBrowserUpload = async () => {
+		const dirHandle = await window.showDirectoryPicker();
+		let totalFiles = 0;
+		let uploadedFiles = 0;
 
-			// Recursive function to count all files excluding hidden ones
-			async function countFiles(dirHandle) {
-				for await (const entry of dirHandle.values()) {
-					if (entry.name.startsWith('.')) continue; // Skip hidden files and directories
+		// Function to update the UI with the progress
+		const updateProgress = () => {
+			const percentage = (uploadedFiles / totalFiles) * 100;
+			toast.info(`Upload Progress: ${uploadedFiles}/${totalFiles} (${percentage.toFixed(2)}%)`);
+		};
 
-					if (entry.kind === 'file') {
-						totalFiles++;
-					} else if (entry.kind === 'directory') {
+		// Recursive function to count all files excluding hidden ones
+		async function countFiles(dirHandle) {
+			for await (const entry of dirHandle.values()) {
+				// Skip hidden files and directories
+				if (entry.name.startsWith('.')) continue;
+
+				if (entry.kind === 'file') {
+					totalFiles++;
+				} else if (entry.kind === 'directory') {
+					// Only process non-hidden directories
+					if (!entry.name.startsWith('.')) {
 						await countFiles(entry);
 					}
 				}
 			}
+		}
 
-			// Recursive function to process directories excluding hidden files
-			async function processDirectory(dirHandle, path = '') {
-				for await (const entry of dirHandle.values()) {
-					if (entry.name.startsWith('.')) continue; // Skip hidden files and directories
+		// Recursive function to process directories excluding hidden files and folders
+		async function processDirectory(dirHandle, path = '') {
+			for await (const entry of dirHandle.values()) {
+				// Skip hidden files and directories
+				if (entry.name.startsWith('.')) continue;
 
-					const entryPath = path ? `${path}/${entry.name}` : entry.name;
+				const entryPath = path ? `${path}/${entry.name}` : entry.name;
 
-					if (entry.kind === 'file') {
-						// Get file from handle
-						const file = await entry.getFile();
-						// Create a new file with the path information
-						const fileWithPath = new File([file], entryPath, { type: file.type });
+				// Skip if the path contains any hidden folders
+				if (hasHiddenFolder(entryPath)) continue;
 
-						await uploadFileHandler(fileWithPath);
-						uploadedFiles++;
-						updateProgress();
-					} else if (entry.kind === 'directory') {
-						// Recursively process subdirectories
+				if (entry.kind === 'file') {
+					const file = await entry.getFile();
+					const fileWithPath = new File([file], entryPath, { type: file.type });
+
+					await uploadFileHandler(fileWithPath);
+					uploadedFiles++;
+					updateProgress();
+				} else if (entry.kind === 'directory') {
+					// Only process non-hidden directories
+					if (!entry.name.startsWith('.')) {
 						await processDirectory(entry, entryPath);
 					}
 				}
 			}
+		}
 
-			// First count all files excluding hidden ones
-			await countFiles(dirHandle);
-			updateProgress();
+		await countFiles(dirHandle);
+		updateProgress();
 
-			// Start processing from root directory
-			if (totalFiles > 0) {
-				await processDirectory(dirHandle);
-			} else {
-				console.log('No files to upload.');
-			}
-		} catch (error) {
-			if (error.name === 'AbortError') {
-				toast.info('Directory selection was cancelled');
-			} else {
-				toast.error('Error accessing directory');
-				console.error('Directory access error:', error);
-			}
+		if (totalFiles > 0) {
+			await processDirectory(dirHandle);
+		} else {
+			console.log('No files to upload.');
+		}
+	};
+
+	// Firefox fallback implementation using traditional file input
+	const handleFirefoxUpload = async () => {
+		return new Promise((resolve, reject) => {
+			// Create hidden file input
+			const input = document.createElement('input');
+			input.type = 'file';
+			input.webkitdirectory = true;
+			input.directory = true;
+			input.multiple = true;
+			input.style.display = 'none';
+
+			// Add input to DOM temporarily
+			document.body.appendChild(input);
+
+			input.onchange = async () => {
+				try {
+					const files = Array.from(input.files)
+						// Filter out files from hidden folders
+						.filter((file) => !hasHiddenFolder(file.webkitRelativePath));
+
+					let totalFiles = files.length;
+					let uploadedFiles = 0;
+
+					// Function to update the UI with the progress
+					const updateProgress = () => {
+						const percentage = (uploadedFiles / totalFiles) * 100;
+						toast.info(
+							`Upload Progress: ${uploadedFiles}/${totalFiles} (${percentage.toFixed(2)}%)`
+						);
+					};
+
+					updateProgress();
+
+					// Process all files
+					for (const file of files) {
+						// Skip hidden files (additional check)
+						if (!file.name.startsWith('.')) {
+							const relativePath = file.webkitRelativePath || file.name;
+							const fileWithPath = new File([file], relativePath, { type: file.type });
+
+							await uploadFileHandler(fileWithPath);
+							uploadedFiles++;
+							updateProgress();
+						}
+					}
+
+					// Clean up
+					document.body.removeChild(input);
+					resolve();
+				} catch (error) {
+					reject(error);
+				}
+			};
+
+			input.onerror = (error) => {
+				document.body.removeChild(input);
+				reject(error);
+			};
+
+			// Trigger file picker
+			input.click();
+		});
+	};
+
+	// Error handler
+	const handleUploadError = (error) => {
+		if (error.name === 'AbortError') {
+			toast.info('Directory selection was cancelled');
+		} else {
+			toast.error('Error accessing directory');
+			console.error('Directory access error:', error);
 		}
 	};