feat(editor): show scene preview immediately and optimize in background
Return control after copying the original asset, then run image optimization and thumbnail generation in the background with IPC progress updates. Block duplicate preview uploads and scene creation while requests are in flight. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+40
-6
@@ -1,7 +1,6 @@
|
||||
import { app, BrowserWindow, dialog, Menu, protocol } from 'electron';
|
||||
|
||||
import { ipcChannels, type SessionState } from '../shared/ipc/contracts';
|
||||
import type { Project } from '../shared/types';
|
||||
import { ipcChannels, type ScenePreviewImportEvent, type SessionState } from '../shared/ipc/contracts';
|
||||
import {
|
||||
PROJECT_ZIP_OPEN_DIALOG_FILTER,
|
||||
PROJECT_ZIP_SAVE_DIALOG_FILTER,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
projectZipFileNameFromBase,
|
||||
stripProjectZipExtension,
|
||||
} from '../shared/project/projectZipExtension';
|
||||
import type { Project } from '../shared/types';
|
||||
|
||||
import { EffectsStore } from './effects/effectsStore';
|
||||
import { SceneDarknessStore } from './effects/sceneDarknessStore';
|
||||
@@ -53,6 +53,12 @@ function emitZipProgress(evt: {
|
||||
}
|
||||
}
|
||||
|
||||
function emitScenePreviewImportProgress(evt: ScenePreviewImportEvent): void {
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send(ipcChannels.project.scenePreviewImportProgress, evt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Отключение GPU ломает скорость вторичных окон (презентация/пульт — WebGL). По умолчанию не трогаем.
|
||||
* При чёрном экране в упакованной сборке: `DND_DISABLE_GPU=1`.
|
||||
@@ -374,7 +380,7 @@ async function main() {
|
||||
registerHandler(ipcChannels.project.updateScene, async ({ sceneId, patch }) => {
|
||||
const next = await projectStore.updateScene(sceneId, patch);
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project && project.currentSceneId === sceneId && patch.darkenScene !== undefined) {
|
||||
if (project?.currentSceneId === sceneId && patch.darkenScene !== undefined) {
|
||||
syncSceneDarknessForProject(project);
|
||||
emitSceneDarknessState();
|
||||
}
|
||||
@@ -442,11 +448,39 @@ async function main() {
|
||||
if (canceled || !filePaths[0]) {
|
||||
const project = projectStore.getOpenProject();
|
||||
if (!project) throw new Error('No open project');
|
||||
return { project };
|
||||
return { project, assetId: null, background: false };
|
||||
}
|
||||
const project = await projectStore.importScenePreviewMedia(sceneId, filePaths[0]);
|
||||
const result = await projectStore.importScenePreviewMedia(sceneId, filePaths[0]);
|
||||
emitSessionState();
|
||||
return { project };
|
||||
emitScenePreviewImportProgress({
|
||||
sceneId,
|
||||
assetId: result.assetId,
|
||||
phase: 'queued',
|
||||
project: result.project,
|
||||
});
|
||||
void (async () => {
|
||||
try {
|
||||
emitScenePreviewImportProgress({ sceneId, assetId: result.assetId, phase: 'optimizing' });
|
||||
const finalized = await projectStore.finalizeScenePreviewImport(sceneId, result.assetId);
|
||||
if (finalized.changed) {
|
||||
emitSessionState();
|
||||
emitScenePreviewImportProgress({
|
||||
sceneId,
|
||||
assetId: result.assetId,
|
||||
phase: 'done',
|
||||
project: finalized.project,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
emitScenePreviewImportProgress({
|
||||
sceneId,
|
||||
assetId: result.assetId,
|
||||
phase: 'error',
|
||||
message: e instanceof Error ? e.message : String(e),
|
||||
});
|
||||
}
|
||||
})();
|
||||
return result;
|
||||
});
|
||||
registerHandler(ipcChannels.project.clearScenePreview, async ({ sceneId }) => {
|
||||
const project = await projectStore.clearScenePreview(sceneId);
|
||||
|
||||
Reference in New Issue
Block a user