upload new files

This commit is contained in:
Andrew Trokhymenko 2024-11-27 14:30:09 -05:00
parent 074161024d
commit 809b54e04a
3 changed files with 139 additions and 5 deletions

View File

@ -57,6 +57,7 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
renderLogger.trace('Workbench'); renderLogger.trace('Workbench');
const [isSyncing, setIsSyncing] = useState(false); const [isSyncing, setIsSyncing] = useState(false);
const [isUploading, setIsUploading] = useState(false);
const hasPreview = useStore(computed(workbenchStore.previews, (previews) => previews.length > 0)); const hasPreview = useStore(computed(workbenchStore.previews, (previews) => previews.length > 0));
const showWorkbench = useStore(workbenchStore.showWorkbench); 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 ( return (
chatStarted && ( chatStarted && (
<motion.div <motion.div
@ -158,6 +213,10 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
{isSyncing ? <div className="i-ph:spinner" /> : <div className="i-ph:cloud-arrow-down" />} {isSyncing ? <div className="i-ph:spinner" /> : <div className="i-ph:cloud-arrow-down" />}
{isSyncing ? 'Syncing...' : 'Sync Files'} {isSyncing ? 'Syncing...' : 'Sync Files'}
</PanelHeaderButton> </PanelHeaderButton>
<PanelHeaderButton className="mr-1 text-sm" onClick={handleUploadFiles} disabled={isSyncing}>
{isSyncing ? <div className="i-ph:spinner" /> : <div className="i-ph:cloud-arrow-up" />}
{isSyncing ? 'Uploading...' : 'Upload Files'}
</PanelHeaderButton>
<PanelHeaderButton <PanelHeaderButton
className="mr-1 text-sm" className="mr-1 text-sm"
onClick={() => { onClick={() => {

View File

@ -80,6 +80,10 @@ export class FilesStore {
this.#modifiedFiles.clear(); this.#modifiedFiles.clear();
} }
markFileAsNew(filePath: string) {
this.#modifiedFiles.set(filePath, '');
}
async saveFile(filePath: string, content: string) { async saveFile(filePath: string, content: string) {
const webcontainer = await this.#webcontainer; const webcontainer = await this.#webcontainer;

View File

@ -32,6 +32,7 @@ export type WorkbenchViewType = 'code' | 'preview';
export class WorkbenchStore { export class WorkbenchStore {
#previewsStore = new PreviewsStore(webcontainer); #previewsStore = new PreviewsStore(webcontainer);
#filesStore = new FilesStore(webcontainer); #filesStore = new FilesStore(webcontainer);
#editorStore = new EditorStore(this.#filesStore); #editorStore = new EditorStore(this.#filesStore);
#terminalStore = new TerminalStore(webcontainer); #terminalStore = new TerminalStore(webcontainer);
@ -43,7 +44,7 @@ export class WorkbenchStore {
modifiedFiles = new Set<string>(); modifiedFiles = new Set<string>();
artifactIdList: string[] = []; artifactIdList: string[] = [];
#boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined; #boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined;
#globalExecutionQueue=Promise.resolve(); #globalExecutionQueue = Promise.resolve();
constructor() { constructor() {
if (import.meta.hot) { if (import.meta.hot) {
import.meta.hot.data.artifacts = this.artifacts; import.meta.hot.data.artifacts = this.artifacts;
@ -54,7 +55,7 @@ export class WorkbenchStore {
} }
addToExecutionQueue(callback: () => Promise<void>) { addToExecutionQueue(callback: () => Promise<void>) {
this.#globalExecutionQueue=this.#globalExecutionQueue.then(()=>callback()) this.#globalExecutionQueue = this.#globalExecutionQueue.then(() => callback())
} }
get previews() { get previews() {
@ -277,11 +278,11 @@ export class WorkbenchStore {
} }
runAction(data: ActionCallbackData, isStreaming: boolean = false) { runAction(data: ActionCallbackData, isStreaming: boolean = false) {
if(isStreaming) { if (isStreaming) {
this._runAction(data, isStreaming) this._runAction(data, isStreaming)
} }
else{ else {
this.addToExecutionQueue(()=>this._runAction(data, isStreaming)) this.addToExecutionQueue(() => this._runAction(data, isStreaming))
} }
} }
async _runAction(data: ActionCallbackData, isStreaming: boolean = false) { async _runAction(data: ActionCallbackData, isStreaming: boolean = false) {
@ -381,6 +382,61 @@ export class WorkbenchStore {
return syncedFiles; 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<string>();
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<string>());
}
async pushToGitHub(repoName: string, githubUsername: string, ghToken: string) { async pushToGitHub(repoName: string, githubUsername: string, ghToken: string) {
try { try {
@ -486,6 +542,21 @@ export class WorkbenchStore {
console.error('Error pushing to GitHub:', error instanceof Error ? error.message : String(error)); 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(); export const workbenchStore = new WorkbenchStore();