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:
@@ -8,6 +8,8 @@ import type {
|
||||
Project,
|
||||
ProjectId,
|
||||
Scene,
|
||||
SceneDarknessEvent,
|
||||
SceneDarknessState,
|
||||
SceneId,
|
||||
VideoPlaybackEvent,
|
||||
VideoPlaybackState,
|
||||
@@ -27,6 +29,7 @@ export const ipcChannels = {
|
||||
list: 'project.list',
|
||||
create: 'project.create',
|
||||
open: 'project.open',
|
||||
close: 'project.close',
|
||||
saveNow: 'project.saveNow',
|
||||
get: 'project.get',
|
||||
updateScene: 'project.updateScene',
|
||||
@@ -68,6 +71,11 @@ export const ipcChannels = {
|
||||
dispatch: 'effects.dispatch',
|
||||
stateChanged: 'effects.stateChanged',
|
||||
},
|
||||
sceneDarkness: {
|
||||
getState: 'sceneDarkness.getState',
|
||||
dispatch: 'sceneDarkness.dispatch',
|
||||
stateChanged: 'sceneDarkness.stateChanged',
|
||||
},
|
||||
video: {
|
||||
getState: 'video.getState',
|
||||
dispatch: 'video.dispatch',
|
||||
@@ -117,6 +125,7 @@ export type UpdaterProgressEvent = {
|
||||
export type IpcEventMap = {
|
||||
[ipcChannels.session.stateChanged]: { state: SessionState };
|
||||
[ipcChannels.effects.stateChanged]: { state: EffectsState };
|
||||
[ipcChannels.sceneDarkness.stateChanged]: { state: SceneDarknessState };
|
||||
[ipcChannels.video.stateChanged]: { state: VideoPlaybackState };
|
||||
[ipcChannels.license.statusChanged]: { snapshot: LicenseSnapshot };
|
||||
[ipcChannels.windows.multiWindowStateChanged]: { open: boolean };
|
||||
@@ -154,6 +163,10 @@ export type IpcInvokeMap = {
|
||||
req: { projectId: ProjectId };
|
||||
res: { project: Project };
|
||||
};
|
||||
[ipcChannels.project.close]: {
|
||||
req: Record<string, never>;
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.project.get]: {
|
||||
req: Record<string, never>;
|
||||
res: { project: Project | null };
|
||||
@@ -270,6 +283,14 @@ export type IpcInvokeMap = {
|
||||
req: { event: EffectsEvent };
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.sceneDarkness.getState]: {
|
||||
req: Record<string, never>;
|
||||
res: { state: SceneDarknessState };
|
||||
};
|
||||
[ipcChannels.sceneDarkness.dispatch]: {
|
||||
req: { event: SceneDarknessEvent };
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.video.getState]: {
|
||||
req: Record<string, never>;
|
||||
res: { state: VideoPlaybackState };
|
||||
@@ -304,6 +325,7 @@ export type SessionState = {
|
||||
export type LegacyIpcEventMap = {
|
||||
[ipcChannels.session.stateChanged]: { state: SessionState };
|
||||
[ipcChannels.effects.stateChanged]: { state: EffectsState };
|
||||
[ipcChannels.sceneDarkness.stateChanged]: { state: SceneDarknessState };
|
||||
[ipcChannels.video.stateChanged]: { state: VideoPlaybackState };
|
||||
[ipcChannels.license.statusChanged]: Record<string, never>;
|
||||
};
|
||||
@@ -316,6 +338,7 @@ export type ScenePatch = {
|
||||
previewThumbAssetId?: AssetId | null;
|
||||
previewVideoAutostart?: boolean;
|
||||
previewRotationDeg?: 0 | 90 | 180 | 270;
|
||||
darkenScene?: boolean;
|
||||
settings?: Partial<Scene['settings']>;
|
||||
media?: Partial<Scene['media']>;
|
||||
layout?: Partial<Scene['layout']>;
|
||||
|
||||
@@ -90,6 +90,8 @@ export type Scene = {
|
||||
previewVideoAutostart: boolean;
|
||||
/** Поворот превью в градусах (0/90/180/270). */
|
||||
previewRotationDeg: 0 | 90 | 180 | 270;
|
||||
/** В режиме показа: сцена начинается полностью затемнённой; мастер «раскрывает» кистью. */
|
||||
darkenScene: boolean;
|
||||
media: SceneMediaRefs;
|
||||
settings: SceneSettings;
|
||||
connections: SceneId[];
|
||||
|
||||
@@ -8,6 +8,7 @@ export type EffectToolType =
|
||||
| 'sunbeam'
|
||||
| 'poisonCloud'
|
||||
| 'freeze'
|
||||
| 'exploreBrush'
|
||||
| 'eraser';
|
||||
|
||||
export type EffectInstanceType =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './domain';
|
||||
export * from './effects';
|
||||
export * from './ids';
|
||||
export * from './sceneDarkness';
|
||||
export * from './videoPlayback';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { NPoint } from './effects';
|
||||
|
||||
export type SceneDarknessRevealStroke = {
|
||||
id: string;
|
||||
seed: number;
|
||||
createdAtMs: number;
|
||||
points: NPoint[];
|
||||
radiusN: number;
|
||||
};
|
||||
|
||||
export type SceneDarknessState = {
|
||||
revision: number;
|
||||
/** Текущая сцена помечена «Затемнить сцену» (только для изображений). */
|
||||
enabled: boolean;
|
||||
cacheKey: string | null;
|
||||
strokes: SceneDarknessRevealStroke[];
|
||||
draft: { points: NPoint[]; radiusN: number } | null;
|
||||
};
|
||||
|
||||
export type SceneDarknessEvent =
|
||||
| { kind: 'draft.set'; draft: { points: NPoint[]; radiusN: number } | null }
|
||||
| { kind: 'stroke.add'; stroke: SceneDarknessRevealStroke };
|
||||
Reference in New Issue
Block a user