From 809b54e04aaf37e474b5151340b704d57e5afc48 Mon Sep 17 00:00:00 2001 From: Andrew Trokhymenko Date: Wed, 27 Nov 2024 14:30:09 -0500 Subject: [PATCH] upload new files --- app/components/workbench/Workbench.client.tsx | 59 ++++++++++++++ app/lib/stores/files.ts | 4 + app/lib/stores/workbench.ts | 81 +++++++++++++++++-- 3 files changed, 139 insertions(+), 5 deletions(-) diff --git a/app/components/workbench/Workbench.client.tsx b/app/components/workbench/Workbench.client.tsx index 7e21dd0..da5b60d 100644 --- a/app/components/workbench/Workbench.client.tsx +++ b/app/components/workbench/Workbench.client.tsx @@ -57,6 +57,7 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => renderLogger.trace('Workbench'); const [isSyncing, setIsSyncing] = useState(false); + const [isUploading, setIsUploading] = useState(false); const hasPreview = useStore(computed(workbenchStore.previews, (previews) => previews.length > 0)); const showWorkbench = useStore(workbenchStore.showWorkbench); @@ -119,6 +120,60 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => } }, []); + const handleUploadFiles = useCallback(async () => { + setIsUploading(true); + + try { + // const directoryHandle = await window.showDirectoryPicker(); + + // // First upload new files + // await workbenchStore.uploadFilesFromDisk(directoryHandle); + + // // Get current files state + // const currentFiles = workbenchStore.files.get(); + + // // Create new modifications map with all files as "new" + // const newModifications = new Map(); + // Object.entries(currentFiles).forEach(([path, file]) => { + // if (file.type === 'file') { + // newModifications.set(path, file.content); + // } + // }); + + // // Update workbench state + // await workbenchStore.refreshFiles(); + // workbenchStore.resetAllFileModifications(); + + // toast.success('Files uploaded successfully'); + // } catch (error) { + // toast.error('Failed to upload files'); + // } + await handleUploadFilesFunc(); + } + + finally { + setIsUploading(false); + } + }, []); + + async function handleUploadFilesFunc() { + try { + // First clean all statuses + await workbenchStore.saveAllFiles(); + await workbenchStore.resetAllFileModifications(); + await workbenchStore.refreshFiles(); + + // Now upload new files + const directoryHandle = await window.showDirectoryPicker(); + await workbenchStore.uploadFilesFromDisk(directoryHandle); + + toast.success('Files uploaded successfully'); + } catch (error) { + console.error('Upload files error:', error); + toast.error('Failed to upload files'); + } + } + return ( chatStarted && ( {isSyncing ?
:
} {isSyncing ? 'Syncing...' : 'Sync Files'} + + {isSyncing ?
:
} + {isSyncing ? 'Uploading...' : 'Upload Files'} + { diff --git a/app/lib/stores/files.ts b/app/lib/stores/files.ts index 663ae58..b0f726e 100644 --- a/app/lib/stores/files.ts +++ b/app/lib/stores/files.ts @@ -80,6 +80,10 @@ export class FilesStore { this.#modifiedFiles.clear(); } + markFileAsNew(filePath: string) { + this.#modifiedFiles.set(filePath, ''); + } + async saveFile(filePath: string, content: string) { const webcontainer = await this.#webcontainer; diff --git a/app/lib/stores/workbench.ts b/app/lib/stores/workbench.ts index 4db14e7..6378ba7 100644 --- a/app/lib/stores/workbench.ts +++ b/app/lib/stores/workbench.ts @@ -32,6 +32,7 @@ export type WorkbenchViewType = 'code' | 'preview'; export class WorkbenchStore { #previewsStore = new PreviewsStore(webcontainer); #filesStore = new FilesStore(webcontainer); + #editorStore = new EditorStore(this.#filesStore); #terminalStore = new TerminalStore(webcontainer); @@ -43,7 +44,7 @@ export class WorkbenchStore { modifiedFiles = new Set(); artifactIdList: string[] = []; #boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined; - #globalExecutionQueue=Promise.resolve(); + #globalExecutionQueue = Promise.resolve(); constructor() { if (import.meta.hot) { import.meta.hot.data.artifacts = this.artifacts; @@ -54,7 +55,7 @@ export class WorkbenchStore { } addToExecutionQueue(callback: () => Promise) { - this.#globalExecutionQueue=this.#globalExecutionQueue.then(()=>callback()) + this.#globalExecutionQueue = this.#globalExecutionQueue.then(() => callback()) } get previews() { @@ -277,11 +278,11 @@ export class WorkbenchStore { } runAction(data: ActionCallbackData, isStreaming: boolean = false) { - if(isStreaming) { + if (isStreaming) { this._runAction(data, isStreaming) } - else{ - this.addToExecutionQueue(()=>this._runAction(data, isStreaming)) + else { + this.addToExecutionQueue(() => this._runAction(data, isStreaming)) } } async _runAction(data: ActionCallbackData, isStreaming: boolean = false) { @@ -381,6 +382,61 @@ export class WorkbenchStore { return syncedFiles; } + async uploadFilesFromDisk(sourceHandle: FileSystemDirectoryHandle) { + const loadedFiles = []; + const wc = await webcontainer; + const newFiles = {}; + + const processDirectory = async (handle: FileSystemDirectoryHandle, currentPath: string = '') => { + const entries = await Array.fromAsync(handle.values()); + + for (const entry of entries) { + const entryPath = currentPath ? `${currentPath}/${entry.name}` : entry.name; + const fullPath = `/${entryPath}`; + + if (entry.kind === 'directory') { + await wc.fs.mkdir(fullPath, { recursive: true }); + const subDirHandle = await handle.getDirectoryHandle(entry.name); + await processDirectory(subDirHandle, entryPath); + } else { + const file = await entry.getFile(); + const content = await file.text(); + + // Write to WebContainer + await wc.fs.writeFile(fullPath, content); + + // Mark file as new + this.#filesStore.markFileAsNew(fullPath); + + // Update the files store with the current content + this.files.setKey(fullPath, { type: 'file', content, isBinary: false }); + + // Collect for editor store with actual content + newFiles[fullPath] = { type: 'file', content, isBinary: false }; + loadedFiles.push(entryPath); + } + } + } + + await processDirectory(sourceHandle); + + return loadedFiles; + } + + async refreshFiles() { + // Clear old state + this.modifiedFiles = new Set(); + this.artifactIdList = []; + + // Reset stores + this.#filesStore = new FilesStore(webcontainer); + this.#editorStore = new EditorStore(this.#filesStore); + + // Update UI state + this.currentView.set('code'); + this.unsavedFiles.set(new Set()); + } + async pushToGitHub(repoName: string, githubUsername: string, ghToken: string) { try { @@ -486,6 +542,21 @@ export class WorkbenchStore { console.error('Error pushing to GitHub:', error instanceof Error ? error.message : String(error)); } } + + async markFileAsModified(filePath: string) { + const file = this.#filesStore.getFile(filePath); + if (file?.type === 'file') { + // First collect all original content + const originalContent = file.content; + console.log(`Processing ${filePath}:`, originalContent); + + // Then save modifications + await this.saveFile(filePath, originalContent); + } + } + + + } export const workbenchStore = new WorkbenchStore();