feat: auto sync

added auto syncing
This commit is contained in:
Dustin Loring 2025-01-17 14:49:32 -05:00
parent f0d2faae6e
commit ff111214c9
3 changed files with 74 additions and 6 deletions

View File

@ -3,7 +3,7 @@ import { saveAs } from 'file-saver';
import { motion, type HTMLMotionProps, type Variants } from 'framer-motion';
import JSZip from 'jszip';
import { computed } from 'nanostores';
import { memo, useCallback, useEffect, useState } from 'react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import { EditorPanel } from './EditorPanel';
import { GitHubPushModal } from './GitHubPushModal';
@ -66,6 +66,30 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
const files = useStore(workbenchStore.files);
const selectedView = useStore(workbenchStore.currentView);
const [showGitHubModal, setShowGitHubModal] = useState(false);
const [showGitHubPushModal, setShowGitHubPushModal] = useState(false);
const [isSyncing, setIsSyncing] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleSyncFiles = useCallback(async () => {
setIsSyncing(true);
try {
if ('showDirectoryPicker' in window) {
const directoryHandle = await window.showDirectoryPicker();
await workbenchStore.syncFiles(directoryHandle);
toast.success('Files synced successfully');
} else {
// Fallback to download as zip
await downloadZip();
toast.info('Your browser does not support the File System Access API. Files have been downloaded as a zip instead.');
}
} catch (error) {
console.error('Error syncing files:', error);
toast.error('Failed to sync files');
} finally {
setIsSyncing(false);
}
}, []);
const downloadZip = async () => {
const zip = new JSZip();
@ -157,6 +181,10 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
<div className="ml-auto" />
{selectedView === 'code' && (
<>
<PanelHeaderButton className="mr-1 text-sm" onClick={handleSyncFiles} disabled={isSyncing}>
{isSyncing ? <div className="i-ph:spinner" /> : <div className="i-ph:cloud-arrow-down" />}
{isSyncing ? 'Syncing...' : 'Sync Files'}
</PanelHeaderButton>
<PanelHeaderButton className="mr-1 text-sm" onClick={downloadZip}>
<div className="i-ph:download-bold" />
Download

View File

@ -256,15 +256,38 @@ export class WorkbenchStore {
}
async runAction(data: ActionCallbackData) {
const { messageId } = data;
const artifact = this.artifacts.get()[data.messageId];
if (!artifact) return;
await artifact.runner.runAction(data);
}
const artifact = this.#getArtifact(messageId);
async syncFiles(targetHandle: FileSystemDirectoryHandle) {
const files = this.files.get();
const syncedFiles = [];
if (!artifact) {
unreachable('Artifact not found');
for (const [filePath, dirent] of Object.entries(files)) {
if (dirent?.type === 'file' && !dirent.isBinary) {
const relativePath = filePath.replace(/^\/home\/project\//, '');
const pathSegments = relativePath.split('/');
let currentHandle = targetHandle;
for (let i = 0; i < pathSegments.length - 1; i++) {
currentHandle = await currentHandle.getDirectoryHandle(pathSegments[i], { create: true });
}
// create or get the file
const fileHandle = await currentHandle.getFileHandle(pathSegments[pathSegments.length - 1], { create: true });
// write the file content
const writable = await fileHandle.createWritable();
await writable.write(dirent.content);
await writable.close();
syncedFiles.push(relativePath);
}
}
artifact.runner.runAction(data);
return syncedFiles;
}
#getArtifact(id: string) {

17
app/types/global.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
interface Window {
showDirectoryPicker(): Promise<FileSystemDirectoryHandle>;
}
interface FileSystemDirectoryHandle {
getDirectoryHandle(name: string, options?: { create?: boolean }): Promise<FileSystemDirectoryHandle>;
getFileHandle(name: string, options?: { create?: boolean }): Promise<FileSystemFileHandle>;
}
interface FileSystemFileHandle {
createWritable(): Promise<FileSystemWritableFileStream>;
}
interface FileSystemWritableFileStream extends WritableStream {
write(data: string | BufferSource | Blob): Promise<void>;
close(): Promise<void>;
}