Skip to content

Commit 8a6ea78

Browse files
committed
feat: implement file system persistence and text operations
- Add IndexedDB + localStorage persistence for file system - Implement incremental merge: default filesystem.json + user changes - Add file upload/download functionality for text files - Complete Notepad file operations: New, Open, Save, Save As - Fix copy operation in Explorer (was incorrectly using cut) - Persist recycle bin contents across sessions - Track original paths for recycle bin restore Key changes: - src/utils/storage.ts: New persistence utilities - src/context/FileSystemContext.tsx: Integrated persistence layer - src/apps/Notepad.tsx: Full file menu implementation - src/apps/Explorer.tsx: Upload functionality + copy fix
1 parent e033486 commit 8a6ea78

File tree

4 files changed

+841
-103
lines changed

4 files changed

+841
-103
lines changed

src/apps/Explorer.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ interface ExplorerProps {
118118
}
119119

120120
const Explorer: React.FC<ExplorerProps> = ({ initialPath = [], windowId }) => {
121-
const { getFile, createFile, renameFile, deleteFile, cutFile, pasteFile, clipboard, getFileProperties, emptyRecycleBin, restoreFromRecycleBin, moveFile } = useFileSystem();
121+
const { getFile, createFile, renameFile, deleteFile, cutFile, pasteFile, clipboard, getFileProperties, emptyRecycleBin, restoreFromRecycleBin, moveFile, copyToClipboard, uploadTextFile } = useFileSystem();
122122
const api = useApp(windowId);
123123

124124
const [history, setHistory] = useState<string[][]>([initialPath]);
@@ -317,11 +317,31 @@ const Explorer: React.FC<ExplorerProps> = ({ initialPath = [], windowId }) => {
317317

318318
const handleCopy = () => {
319319
if (contextMenu.targetItem) {
320-
cutFile(currentPath, contextMenu.targetItem.key);
320+
copyToClipboard(currentPath, contextMenu.targetItem.key);
321321
closeContextMenu();
322322
}
323323
};
324324

325+
const handleUpload = () => {
326+
const input = document.createElement('input');
327+
input.type = 'file';
328+
input.accept = '.txt,.md,.json,.js,.ts,.jsx,.tsx,.css,.html,.xml,.csv';
329+
input.onchange = (e) => {
330+
const file = (e.target as HTMLInputElement).files?.[0];
331+
if (file) {
332+
const reader = new FileReader();
333+
reader.onload = (event) => {
334+
const content = event.target?.result as string;
335+
uploadTextFile(currentPath, file.name, content);
336+
closeContextMenu();
337+
};
338+
reader.readAsText(file);
339+
}
340+
};
341+
input.click();
342+
closeContextMenu();
343+
};
344+
325345
const handleCut = () => {
326346
if (contextMenu.targetItem) {
327347
cutFile(currentPath, contextMenu.targetItem.key);
@@ -387,6 +407,8 @@ const Explorer: React.FC<ExplorerProps> = ({ initialPath = [], windowId }) => {
387407
{ label: '新建文件夹', action: () => handleCreateFile('folder') },
388408
{ label: '新建文本文档', action: () => handleCreateFile('file') },
389409
{ type: 'separator' },
410+
{ label: '上传文件', action: handleUpload },
411+
{ type: 'separator' },
390412
{ label: '复制', action: handleCopy, disabled: !contextMenu.targetItem },
391413
{ label: '剪切', action: handleCut, disabled: !contextMenu.targetItem },
392414
{ label: '粘贴', action: handlePaste, disabled: !clipboard },

0 commit comments

Comments
 (0)