Skip to content

Commit ebc7297

Browse files
committed
Merge branch 'main' into restart-sandbox-in-toolbar
2 parents eaaa718 + 0fb06c7 commit ebc7297

File tree

4 files changed

+83
-65
lines changed

4 files changed

+83
-65
lines changed

apps/web/client/src/app/project/[id]/_components/left-panel/image-tab/folder/index.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import { useEditorEngine } from '@/components/store/editor';
24
import { DefaultSettings } from '@onlook/constants';
35
import { type FolderNode } from '@onlook/models';
@@ -206,14 +208,41 @@ const Folder = observer(() => {
206208
</button>
207209
)}
208210
</div>
209-
<Button
210-
variant="default"
211-
size="icon"
212-
className="p-2 w-fit h-fit text-foreground-primary border-border-primary hover:border-border-onlook bg-background-secondary hover:bg-background-onlook border"
213-
onClick={() => handleCreateFolder(currentFolder)}
214-
>
215-
<Icons.DirectoryPlus className="h-4 w-4" />
216-
</Button>
211+
<Tooltip>
212+
<TooltipTrigger asChild>
213+
<Button
214+
variant={'default'}
215+
size={'icon'}
216+
className="p-2 w-fit h-fit text-foreground-primary border-border-primary hover:border-border-onlook bg-background-secondary hover:bg-background-onlook border"
217+
onClick={() => handleCreateFolder(currentFolder)}
218+
disabled={isAnyOperationLoading}
219+
>
220+
<Icons.DirectoryPlus className="h-4 w-4" />
221+
</Button>
222+
</TooltipTrigger>
223+
<TooltipPortal>
224+
<TooltipContent>
225+
<p>Create a folder</p>
226+
</TooltipContent>
227+
</TooltipPortal>
228+
</Tooltip>
229+
<Tooltip>
230+
<TooltipTrigger asChild>
231+
<Button
232+
variant={'default'}
233+
size={'icon'}
234+
className="p-2 w-fit h-fit text-foreground-primary border-border-primary hover:border-border-onlook bg-background-secondary hover:bg-background-onlook border"
235+
onClick={() => editorEngine.image.scanImages()}
236+
>
237+
<Icons.Reload className="w-4 h-4" />
238+
</Button>
239+
</TooltipTrigger>
240+
<TooltipPortal>
241+
<TooltipContent>
242+
<p>Refresh Images</p>
243+
</TooltipContent>
244+
</TooltipPortal>
245+
</Tooltip>
217246
<Tooltip>
218247
<TooltipTrigger asChild>
219248
<Button
@@ -223,7 +252,7 @@ const Folder = observer(() => {
223252
onClick={uploadOperations.handleClickAddButton}
224253
disabled={isAnyOperationLoading}
225254
>
226-
<Icons.Plus />
255+
<Icons.Plus className="w-4 h-4" />
227256
</Button>
228257
</TooltipTrigger>
229258
<TooltipPortal>

apps/web/client/src/app/project/[id]/_components/left-panel/image-tab/providers/folder-provider.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,20 +388,19 @@ export const FolderProvider = observer(({ children }: FolderProviderProps) => {
388388

389389
// Get all folder paths
390390
const getImagesInFolder = useCallback((folder: FolderNode) => {
391-
return editorEngine.activeSandbox.files.filter(image => {
391+
// Use the image manager's scanned image paths instead of sandbox files
392+
return editorEngine.image.imagePaths.filter(image => {
392393
if (image.startsWith(folder.fullPath)) {
393394
// Check if this is a direct child (not in a subdirectory)
394395
const relativePath = image.slice(folder.fullPath.length);
395396
// Remove leading slash if present
396397
const cleanRelativePath = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath;
397398
// Only include if it's a direct child (no additional path separators)
398-
if (!cleanRelativePath.includes('/')) {
399-
return isImageFile(image);
400-
}
399+
return !cleanRelativePath.includes('/') && cleanRelativePath.length > 0;
401400
}
402401
return false;
403402
});
404-
}, [editorEngine.activeSandbox.files]);
403+
}, [editorEngine.image.imagePaths]);
405404

406405
// Check if any operation is loading
407406
const isOperating =

apps/web/client/src/components/store/editor/image/index.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DefaultSettings } from '@onlook/constants';
2-
import type { ActionTarget, ImageContentData, InsertImageAction } from '@onlook/models';
2+
import { LeftPanelTabValue, type ActionTarget, type ImageContentData, type InsertImageAction } from '@onlook/models';
33
import { convertToBase64, generateNewFolderPath, getBaseName, getMimeType, isImageFile, stripImageFolderPrefix } from '@onlook/utility';
44
import { makeAutoObservable, reaction } from 'mobx';
55
import type { EditorEngine } from '../engine';
@@ -18,10 +18,18 @@ export class ImageManager {
1818

1919
init() {
2020
this.indexingReactionDisposer = reaction(
21-
() => this.editorEngine.activeSandbox.isIndexing,
22-
async (isIndexingFiles) => {
23-
if (!isIndexingFiles) {
24-
await this.scanImages();
21+
() => {
22+
return {
23+
isIndexing: this.editorEngine.activeSandbox.isIndexing,
24+
isIndexed: this.editorEngine.activeSandbox.isIndexed,
25+
};
26+
},
27+
(sandboxStatus) => {
28+
if (this.editorEngine.state.leftPanelTab !== LeftPanelTabValue.IMAGES) {
29+
return;
30+
}
31+
if (sandboxStatus.isIndexed && !sandboxStatus.isIndexing) {
32+
this.scanImages();
2533
}
2634
},
2735
);
@@ -183,16 +191,13 @@ export class ImageManager {
183191
}
184192

185193
async scanImages() {
186-
if (this._isScanning) {
187-
return;
188-
}
189-
190-
this._isScanning = true;
191-
192194
try {
193-
const files = await this.editorEngine.activeSandbox.listFilesRecursively(
194-
DefaultSettings.IMAGE_FOLDER,
195-
);
195+
if (this._isScanning) {
196+
return;
197+
}
198+
this._isScanning = true;
199+
200+
const files = this.editorEngine.activeSandbox.files;
196201
if (!files) {
197202
console.error('No files found in image folder');
198203
return;
@@ -201,7 +206,7 @@ export class ImageManager {
201206
this._imagePaths = [];
202207
return;
203208
}
204-
this._imagePaths = files.filter((file: string) => isImageFile(file));
209+
this._imagePaths = files.filter((file: string) => file.startsWith(DefaultSettings.IMAGE_FOLDER) && isImageFile(file));
205210
} catch (error) {
206211
console.error('Error scanning images:', error);
207212
this._imagePaths = [];

apps/web/client/src/components/store/editor/sandbox/index.ts

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -549,42 +549,20 @@ export class SandboxManager {
549549
async handleFileChangedEvent(normalizedPath: string) {
550550
const cachedFile = this.fileSync.readCache(normalizedPath);
551551

552-
if (isImageFile(normalizedPath)) {
553-
if (!cachedFile || cachedFile.content === null) {
554-
// If the file was not cached, we need to write an empty file
555-
this.fileSync.writeEmptyFile(normalizedPath, 'binary');
556-
} else {
557-
// If the file was already cached, we need to read the remote file and update the cache
558-
const remoteFile = await this.readRemoteFile(normalizedPath);
559-
if (!remoteFile || remoteFile.content === null) {
560-
console.error(`File content for ${normalizedPath} not found in remote`);
561-
return;
562-
}
563-
this.fileSync.updateCache(remoteFile);
564-
}
565-
} else {
566-
// If the file is not an image, we need to read the remote file and update the cache
567-
const remoteFile = await this.readRemoteFile(normalizedPath);
568-
if (!remoteFile || remoteFile.content === null) {
569-
console.error(`File content for ${normalizedPath} not found in remote`);
570-
return;
571-
}
572-
if (remoteFile.type === 'text') {
573-
// If the file is a text file, we need to process it for mapping
574-
this.fileSync.updateCache({
575-
type: 'text',
576-
path: normalizedPath,
577-
content: remoteFile.content,
578-
});
579-
if (remoteFile.content !== cachedFile?.content) {
580-
await this.processFileForMapping(remoteFile);
581-
}
582-
} else {
583-
this.fileSync.updateCache({
584-
type: 'binary',
585-
path: normalizedPath,
586-
content: remoteFile.content,
587-
});
552+
// Always read the remote file and update the cache, regardless of file type
553+
const remoteFile = await this.readRemoteFile(normalizedPath);
554+
if (!remoteFile) {
555+
console.error(`File content for ${normalizedPath} not found in remote`);
556+
return;
557+
}
558+
559+
// Always update the cache with the fresh remote file content
560+
this.fileSync.updateCache(remoteFile);
561+
562+
// For text files, also process for mapping if content has changed
563+
if (remoteFile.type === 'text' && this.isJsxFile(normalizedPath)) {
564+
if (remoteFile.content !== cachedFile?.content) {
565+
await this.processFileForMapping(remoteFile);
588566
}
589567
}
590568
}
@@ -678,6 +656,12 @@ export class SandboxManager {
678656
},
679657
});
680658

659+
// Read and cache the copied file
660+
const copiedFile = await this.readRemoteFile(normalizedTargetPath);
661+
if (copiedFile) {
662+
this.fileSync.updateCache(copiedFile);
663+
}
664+
681665
return true;
682666
} catch (error) {
683667
console.error(`Error copying ${path} to ${targetPath}:`, error);
@@ -744,6 +728,7 @@ export class SandboxManager {
744728
},
745729
});
746730

731+
// Note: Cache update handled by file watcher rename event
747732
return true;
748733
} catch (error) {
749734
console.error(`Error renaming file ${oldPath} to ${newPath}:`, error);

0 commit comments

Comments
 (0)