feat(scenes): add scene darkness reveal and fix project reopen crash
Add darkenScene with Opening brush in presentation, persist reveal strokes per scene during a session, and close projects properly on return to home. Harden dnd asset streaming to avoid Windows main-process crashes when reopening projects. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { app, BrowserWindow, dialog, Menu, protocol } from 'electron';
|
||||
|
||||
import { ipcChannels, type SessionState } from '../shared/ipc/contracts';
|
||||
import type { Project } from '../shared/types';
|
||||
import {
|
||||
PROJECT_ZIP_OPEN_DIALOG_FILTER,
|
||||
PROJECT_ZIP_SAVE_DIALOG_FILTER,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
} from '../shared/project/projectZipExtension';
|
||||
|
||||
import { EffectsStore } from './effects/effectsStore';
|
||||
import { SceneDarknessStore } from './effects/sceneDarknessStore';
|
||||
import { installIpcRouter, registerHandler, setLicenseAssert } from './ipc/router';
|
||||
import { LicenseService } from './license/licenseService';
|
||||
import { ZipProjectStore } from './project/zipStore';
|
||||
@@ -117,6 +119,7 @@ function installAppMenuForSession(): void {
|
||||
}
|
||||
|
||||
const effectsStore = new EffectsStore();
|
||||
const sceneDarknessStore = new SceneDarknessStore();
|
||||
const videoStore = new VideoPlaybackStore();
|
||||
|
||||
function emitEffectsState(): void {
|
||||
@@ -126,6 +129,20 @@ function emitEffectsState(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function emitSceneDarknessState(): void {
|
||||
const state = sceneDarknessStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send(ipcChannels.sceneDarkness.stateChanged, { state });
|
||||
}
|
||||
}
|
||||
|
||||
function syncSceneDarknessForProject(project: Project): void {
|
||||
const cacheKey = project.currentGraphNodeId ?? project.currentSceneId ?? null;
|
||||
const scene = project.currentSceneId ? project.scenes[project.currentSceneId] : undefined;
|
||||
const enabled = Boolean(scene?.darkenScene) && scene?.previewAssetType === 'image';
|
||||
sceneDarknessStore.switchScene(cacheKey, enabled);
|
||||
}
|
||||
|
||||
function emitVideoState(): void {
|
||||
const state = videoStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
@@ -267,7 +284,11 @@ async function main() {
|
||||
registerHandler(ipcChannels.license.clearToken, () => licenseService.clearToken());
|
||||
registerHandler(ipcChannels.license.acceptEula, ({ version }) => licenseService.acceptEula(version));
|
||||
registerHandler(ipcChannels.windows.openMultiWindow, () => {
|
||||
sceneDarknessStore.resetSession();
|
||||
openMultiWindow();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) syncSceneDarknessForProject(project);
|
||||
emitSceneDarknessState();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.closeMultiWindow, () => {
|
||||
@@ -303,6 +324,15 @@ async function main() {
|
||||
emitSessionState();
|
||||
return { project };
|
||||
});
|
||||
registerHandler(ipcChannels.project.close, async () => {
|
||||
await projectStore.closeOpenProject();
|
||||
effectsStore.clear();
|
||||
sceneDarknessStore.resetSession();
|
||||
emitEffectsState();
|
||||
emitSceneDarknessState();
|
||||
emitSessionState();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.project.get, () => {
|
||||
return { project: projectStore.getOpenProject() };
|
||||
});
|
||||
@@ -313,7 +343,10 @@ async function main() {
|
||||
registerHandler(ipcChannels.project.setCurrentScene, async ({ sceneId }) => {
|
||||
await projectStore.updateProject((p) => ({ ...p, currentSceneId: sceneId, currentGraphNodeId: null }));
|
||||
effectsStore.clear();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) syncSceneDarknessForProject(project);
|
||||
emitEffectsState();
|
||||
emitSceneDarknessState();
|
||||
emitSessionState();
|
||||
return { currentSceneId: projectStore.getOpenProject()?.currentSceneId ?? null };
|
||||
});
|
||||
@@ -327,7 +360,10 @@ async function main() {
|
||||
currentSceneId: gn ? gn.sceneId : null,
|
||||
}));
|
||||
effectsStore.clear();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) syncSceneDarknessForProject(project);
|
||||
emitEffectsState();
|
||||
emitSceneDarknessState();
|
||||
emitSessionState();
|
||||
const p = projectStore.getOpenProject();
|
||||
return {
|
||||
@@ -337,6 +373,11 @@ 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) {
|
||||
syncSceneDarknessForProject(project);
|
||||
emitSceneDarknessState();
|
||||
}
|
||||
emitSessionState();
|
||||
return { scene: next };
|
||||
});
|
||||
@@ -524,6 +565,15 @@ async function main() {
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
registerHandler(ipcChannels.sceneDarkness.getState, () => {
|
||||
return { state: sceneDarknessStore.getState() };
|
||||
});
|
||||
registerHandler(ipcChannels.sceneDarkness.dispatch, ({ event }) => {
|
||||
sceneDarknessStore.dispatch(event);
|
||||
emitSceneDarknessState();
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
registerHandler(ipcChannels.video.getState, () => {
|
||||
return { state: videoStore.getState() };
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user