diff --git a/src/components/image-editor/ImageCanvasEditorView.test.tsx b/src/components/image-editor/ImageCanvasEditorView.test.tsx
index 34da2091..7c966ef2 100644
--- a/src/components/image-editor/ImageCanvasEditorView.test.tsx
+++ b/src/components/image-editor/ImageCanvasEditorView.test.tsx
@@ -1623,6 +1623,49 @@ describe('ImageCanvasEditorView', () => {
expect(createEditorAssetMock).not.toHaveBeenCalled();
});
+ it('saves canvas layout without embedding image payloads in layer snapshots', async () => {
+ loadEditorAssetLibraryMock.mockResolvedValueOnce({
+ folders: [
+ {
+ folderId: 'project',
+ label: '项目素材',
+ sortOrder: 0,
+ collapsed: false,
+ systemDefault: true,
+ },
+ ],
+ assets: [
+ {
+ assetId: 'asset-data-heavy',
+ folderId: 'project',
+ label: '大图素材',
+ imageSrc: 'data:image/png;base64,'.concat('a'.repeat(4000)),
+ width: 1024,
+ height: 768,
+ sourceType: 'uploaded',
+ },
+ ],
+ });
+ render();
+
+ await screen.findByRole('button', { name: '添加大图素材' });
+ fireEvent.click(screen.getByRole('button', { name: '添加大图素材' }));
+
+ await waitFor(() => {
+ expect(saveEditorProjectLayoutMock).toHaveBeenCalled();
+ });
+ const layoutCalls = saveEditorProjectLayoutMock.mock.calls;
+ const lastLayout = layoutCalls.at(-1)?.[1];
+
+ expect(lastLayout.layers).toEqual(
+ expect.arrayContaining([
+ expect.not.objectContaining({
+ src: expect.stringMatching(/^data:image/u),
+ }),
+ ]),
+ );
+ });
+
it('adds an asset library image to the canvas with pointer dragging', async () => {
await renderLoadedEditor();
diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx
index 17e8059f..fce14e00 100644
--- a/src/components/image-editor/ImageCanvasEditorView.tsx
+++ b/src/components/image-editor/ImageCanvasEditorView.tsx
@@ -539,7 +539,6 @@ function serializeLayer(layer: CanvasLayer): EditorProjectLayerSnapshot {
layerId: layer.id,
resourceId: layer.resourceId,
title: layer.title,
- src: layer.src,
x: layer.x,
y: layer.y,
width: layer.width,
@@ -565,10 +564,14 @@ function serializeLayer(layer: CanvasLayer): EditorProjectLayerSnapshot {
};
}
-function hydrateLayer(snapshot: EditorProjectLayerSnapshot): CanvasLayer | null {
+function hydrateLayer(
+ snapshot: EditorProjectLayerSnapshot,
+ resourcesById: Map,
+): CanvasLayer | null {
const resourceId = typeof snapshot.resourceId === 'string' ? snapshot.resourceId : '';
const layerId = typeof snapshot.layerId === 'string' ? snapshot.layerId : '';
- const src = typeof snapshot.src === 'string' ? snapshot.src : '';
+ const snapshotSrc = typeof snapshot.src === 'string' ? snapshot.src : '';
+ const src = snapshotSrc || resourcesById.get(resourceId)?.imageSrc || '';
const title = typeof snapshot.title === 'string' ? snapshot.title : '画布图片';
if (!resourceId || !layerId || !src) {
return null;
@@ -1221,8 +1224,14 @@ export function ImageCanvasEditorView() {
setProjectTitle(nextProjectTitle);
setProjectRenameValue(nextProjectTitle);
setViewport(project.viewport);
+ const resourcesById = new Map(
+ project.resources.map((resource) => [
+ resource.resourceId,
+ { imageSrc: resource.imageSrc },
+ ]),
+ );
const hydratedLayers = project.layers
- .map(hydrateLayer)
+ .map((layer) => hydrateLayer(layer, resourcesById))
.filter((layer): layer is CanvasLayer => Boolean(layer));
layerCounterRef.current = hydratedLayers.length;
setLayers(hydratedLayers);
@@ -2099,11 +2108,19 @@ export function ImageCanvasEditorView() {
}
: currentLayer,
);
- if (options.saveLayout) {
- void saveProjectLayoutNow(nextLayers).catch(() => {});
- }
return nextLayers;
});
+ if (options.saveLayout) {
+ const latestLayers = layersRef.current.map((currentLayer) =>
+ currentLayer.id === layer.id
+ ? {
+ ...currentLayer,
+ resourceId: resource.resourceId,
+ }
+ : currentLayer,
+ );
+ void saveProjectLayoutNow(latestLayers).catch(() => {});
+ }
return resource;
})
.catch(() => {
@@ -2228,9 +2245,7 @@ export function ImageCanvasEditorView() {
);
captureCanvasHistory();
setLayers((currentLayers) => {
- const nextLayers = [...currentLayers, nextLayer];
- void saveProjectLayoutNow(nextLayers).catch(() => {});
- return nextLayers;
+ return [...currentLayers, nextLayer];
});
selectSingleLayer(nextLayer.id);
setHoveredLayerId(null);
@@ -2853,7 +2868,6 @@ export function ImageCanvasEditorView() {
}
: currentLayer,
);
- void saveProjectLayoutNow(nextLayers).catch(() => {});
return nextLayers;
});
}