diff --git a/FILESYSTEM_SVELTE_MIGRATION.md b/FILESYSTEM_SVELTE_MIGRATION.md new file mode 100644 index 0000000..b23fbaa --- /dev/null +++ b/FILESYSTEM_SVELTE_MIGRATION.md @@ -0,0 +1,221 @@ +# FileSystem Svelte Migration - Complete + +## ✅ Changes Made + +### 1. Updated Icons ([app/src/lib/components/icons/index.ts](app/src/lib/components/icons/index.ts:41-42)) +Added: +```typescript +export { default as UploadIcon } from '~icons/mdi/upload' +export { default as DownloadIcon } from '~icons/mdi/download' +``` + +### 2. Complete Rewrite of FileSystem.svelte + +**Old Implementation:** +- Used HTTP REST API (`/api/files`, `/api/config/`, etc.) +- Relied on recursive `Folder.svelte` and `File.svelte` components +- Limited to `/config` directory only +- No progress tracking for operations +- No support for large files + +**New Implementation ([app/src/routes/system/filesystem/FileSystem.svelte](app/src/routes/system/filesystem/FileSystem.svelte)):** +- ✅ Uses WebSocket chunked transfer system +- ✅ Flat directory view with navigation +- ✅ Works with entire filesystem (not just `/config`) +- ✅ Real-time progress bars for uploads/downloads +- ✅ Supports files of any size (1KB chunks) +- ✅ File size display with formatted bytes +- ✅ Download files to browser +- ✅ Upload files from browser +- ✅ Create/delete files and directories +- ✅ Edit file contents in-browser +- ✅ Error handling with user feedback + +## 📋 Key Features + +### Directory Navigation +- Current path display with breadcrumb +- "Up" button to navigate to parent directory +- Click directories to navigate into them +- Supports full filesystem tree (not limited to `/config`) + +### File Operations +- **Upload**: Click "Upload File" button, select file, see progress bar +- **Download**: Click download icon next to file, automatic browser download +- **Edit**: Click file to view, click "Edit" to modify, save changes +- **Delete**: Delete files or directories (with confirmation) +- **Create**: Create new files or directories via dialogs + +### Progress Tracking +- Upload progress: Shows percentage and bytes transferred +- Download progress: Shows percentage and bytes transferred +- Visual progress bars during transfers + +### UI Improvements +- File sizes displayed in human-readable format (B, KB, MB, GB) +- Selected file highlighted in bold +- Hover actions for download/delete on each file +- Empty directory message +- Loading spinners for async operations +- Error alerts for failed operations + +## 🔄 Migration from Old System + +### What Changed + +**Before:** +```typescript +// Old HTTP API calls +await api.get('/api/files') +await api.get(`/api/config/${name}`) +await api.post('/api/files/edit', { file, content }) +await api.post('/api/files/delete', { file }) +await api.post('/api/files/mkdir', { path }) +``` + +**After:** +```typescript +// New WebSocket chunked transfer +await fileSystemClient.listDirectory(path) +await fileSystemClient.downloadFile(path) +await fileSystemClient.uploadFile(path, data) +await fileSystemClient.deleteFile(path) +await fileSystemClient.createDirectory(path) +``` + +### Breaking Changes + +1. **Directory Type**: Old code used `Directory` from `$lib/types/models`. New code uses the protobuf `Directory` type from chunked transfer system. + +2. **File Structure**: Old system returned nested object structure. New system returns flat arrays of files and directories. + +3. **API Endpoints**: Old HTTP endpoints (`/api/files/*`) are no longer used. All operations go through WebSocket. + +## 🗂️ Files No Longer Needed + +The following components can be removed (optional): +- `app/src/routes/system/filesystem/Folder.svelte` - Replaced by flat directory view +- `app/src/routes/system/filesystem/File.svelte` - Replaced by inline file items + +**Note:** Keep `NewFolderDialog.svelte` and `NewFileDialog.svelte` as they're still used. + +## 🧪 Testing the New System + +### Test Checklist + +1. **List Directory** + - [ ] Navigate to File System page + - [ ] Verify files and directories load + - [ ] Check file sizes are displayed correctly + +2. **Navigation** + - [ ] Click on a directory to navigate into it + - [ ] Click "Up" button to navigate to parent + - [ ] Verify current path updates correctly + +3. **Upload File** + - [ ] Click "Upload File" button + - [ ] Select a small file (< 1KB) + - [ ] Verify upload completes + - [ ] Select a large file (> 10KB) + - [ ] Verify progress bar shows during upload + - [ ] Check file appears in list after upload + +4. **Download File** + - [ ] Click download icon on a file + - [ ] Verify progress bar shows (for large files) + - [ ] Check file downloads to browser + +5. **Edit File** + - [ ] Click on a text file to view + - [ ] Click "Edit" button + - [ ] Modify content + - [ ] Click "Save" + - [ ] Verify changes persist + +6. **Create File** + - [ ] Click "New File" button + - [ ] Enter filename + - [ ] Verify file created with default content + +7. **Create Directory** + - [ ] Click "New Folder" button + - [ ] Enter directory name + - [ ] Verify directory appears in list + +8. **Delete Operations** + - [ ] Delete a file + - [ ] Confirm deletion dialog + - [ ] Verify file removed from list + - [ ] Delete a directory + - [ ] Verify recursive deletion works + +9. **Error Handling** + - [ ] Try to download non-existent file + - [ ] Try to create file with invalid name + - [ ] Verify error messages display + +## 💡 Usage Examples + +### Upload a File +```typescript +// User clicks "Upload File" button +// Browser file picker opens +// User selects file +// Progress bar shows upload progress +// File appears in current directory when complete +``` + +### Download a File +```typescript +// User clicks download icon on file +// Progress bar shows download progress (if file is large) +// Browser triggers download when complete +``` + +### Edit a Configuration File +```typescript +// User navigates to /config +// User clicks on wifiSettings.json +// File content displays +// User clicks "Edit" +// User modifies JSON +// User clicks "Save" +// File updated on ESP32 +``` + +## 🔧 Configuration + +The FileSystem component uses the chunked transfer system with these defaults: + +- **Chunk Size**: 1024 bytes (defined in `chunkedTransfer.ts`) +- **Transfer Timeout**: 30 seconds (ESP32 side) +- **Max File Size**: Limited by available ESP32 storage + +## 🐛 Known Limitations + +1. **Binary Files**: File viewer assumes UTF-8 text. Binary files may not display correctly but can still be downloaded. + +2. **Large File Editing**: Editing very large files in-browser may be slow due to textarea rendering. + +3. **Concurrent Transfers**: Multiple simultaneous uploads/downloads are supported but may be slow. + +## 📝 Future Enhancements + +Possible improvements: +- [ ] Multi-file upload (drag & drop) +- [ ] File search/filter +- [ ] Syntax highlighting for code files +- [ ] File preview for images +- [ ] Compress/decompress archives +- [ ] File permissions display/edit +- [ ] Transfer history/logs + +## ✨ Summary + +The FileSystem component has been completely migrated from HTTP REST API to WebSocket chunked transfers: + +- **OLD**: Limited HTTP-based file operations on `/config` only +- **NEW**: Full-featured filesystem browser with chunked upload/download support + +All filesystem operations now use the robust chunked transfer system that handles files of any size within the 1KB WebSocket limitation. diff --git a/app/src/lib/components/icons/index.ts b/app/src/lib/components/icons/index.ts index d8b3a2c..95c6948 100644 --- a/app/src/lib/components/icons/index.ts +++ b/app/src/lib/components/icons/index.ts @@ -38,6 +38,8 @@ export { default as FolderOpenOutline } from '~icons/mdi/folder-open-outline' export { default as TrashIcon } from '~icons/mdi/trash' export { default as RotateCcw } from '~icons/mdi/rotate-left' export { default as RotateCw } from '~icons/mdi/rotate-right' +export { default as UploadIcon } from '~icons/mdi/upload' +export { default as DownloadIcon } from '~icons/mdi/download' export { default as Down } from '~icons/tabler/chevron-down' export { default as Cancel } from '~icons/tabler/x' diff --git a/app/src/routes/system/filesystem/FileSystem.svelte b/app/src/routes/system/filesystem/FileSystem.svelte index 5c58515..eae8054 100644 --- a/app/src/routes/system/filesystem/FileSystem.svelte +++ b/app/src/routes/system/filesystem/FileSystem.svelte @@ -1,182 +1,421 @@ - - - -
- File System -
- - -
+ +
+ File System +
+ + + +
- + + + +{#if error} +
+ {error} +
+{/if} + +{#if uploadProgress} +
+
+ Uploading... + {uploadProgress.percentage.toFixed(1)}% ({formatBytes(uploadProgress.bytesTransferred)} / {formatBytes(uploadProgress.totalBytes)}) +
+ +
+{/if} + +{#if downloadProgress} +
+
+ Downloading... + {downloadProgress.percentage.toFixed(1)}% ({formatBytes(downloadProgress.bytesTransferred)} / {formatBytes(downloadProgress.totalBytes)}) +
+ +
+{/if}
- -
- {#await getFiles()} - - {:then files} - - {/await} -
+ +
+ +
+ {currentPath} + {#if currentPath !== '/'} + + {/if} +
- -
- {#if filename} -
-

{filename}

-
- {#if isEditing} - - - {:else} - - - {/if} -
-
+ {#if loading} + + {:else} + + {#each directories as dir (dir.name)} +
+ + +
+ {/each} - {#await getContent(filename)} - - {:then} - {#if isEditing} - - {:else} -
{content}
- {/if} - {/await} - {:else} -
Select a file to view its contents
- {/if} -
+ + {#each files as file (file.name)} +
+ +
+ + +
+
+ {/each} + + {#if files.length === 0 && directories.length === 0} +
+ Directory is empty +
+ {/if} + {/if} +
+ + +
+ {#if selectedFile} +
+

{selectedFile}

+
+ {#if isEditing} + + + {:else} + + + + {/if} +
+
+ + {#if fileLoading} + + {:else if isEditing} + + {:else} +
{fileContent}
+ {/if} + {:else} +
+ Select a file to view its contents +
+ {/if} +
-