8b28a9b11a
Добавлена кисть маркера на пульте и презентации с настройками в панели эффектов; штрихи привязаны к жёлтой рамке, ластик и очистка работают с отдельным сессионным слоем. Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
import { applySceneMarkerEraserStroke } from '../../shared/sceneMarkerEraser';
|
|
import type { SceneMarkerEvent, SceneMarkerState, SceneMarkerStroke } from '../../shared/types';
|
|
|
|
function emptyState(): SceneMarkerState {
|
|
return {
|
|
revision: 1,
|
|
cacheKey: null,
|
|
strokes: [],
|
|
draft: null,
|
|
};
|
|
}
|
|
|
|
function makeStrokeId(prefix: string): string {
|
|
return `${prefix}_${String(Date.now())}_${String(Math.floor(Math.random() * 1_000_000_000))}`;
|
|
}
|
|
|
|
export class SceneMarkerStore {
|
|
private state: SceneMarkerState = emptyState();
|
|
/** Кэш штрихов маркера по ключу (graphNodeId или sceneId) на время сессии показа. */
|
|
private cache = new Map<string, SceneMarkerStroke[]>();
|
|
private currentKey: string | null = null;
|
|
|
|
getState(): SceneMarkerState {
|
|
return { ...this.state, strokes: [...this.state.strokes] };
|
|
}
|
|
|
|
/** Сброс кэша при новом запуске показа (кнопка «Запустить»). */
|
|
resetSession(): void {
|
|
this.cache.clear();
|
|
this.currentKey = null;
|
|
this.state = emptyState();
|
|
}
|
|
|
|
/** Переключение сцены: сохраняем штрихи в кэш и загружаем новую сцену. */
|
|
switchScene(cacheKey: string | null): SceneMarkerState {
|
|
if (this.currentKey !== null) {
|
|
this.cache.set(this.currentKey, [...this.state.strokes]);
|
|
}
|
|
this.currentKey = cacheKey;
|
|
const strokes = cacheKey ? [...(this.cache.get(cacheKey) ?? [])] : [];
|
|
this.state = {
|
|
revision: this.state.revision + 1,
|
|
cacheKey,
|
|
strokes,
|
|
draft: null,
|
|
};
|
|
return this.getState();
|
|
}
|
|
|
|
dispatch(event: SceneMarkerEvent): SceneMarkerState {
|
|
switch (event.kind) {
|
|
case 'draft.set':
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
draft: event.draft,
|
|
};
|
|
return this.getState();
|
|
case 'stroke.add': {
|
|
const strokes = [...this.state.strokes, event.stroke];
|
|
if (this.currentKey) {
|
|
this.cache.set(this.currentKey, strokes);
|
|
}
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
strokes,
|
|
draft: null,
|
|
};
|
|
return this.getState();
|
|
}
|
|
case 'strokes.erase': {
|
|
const strokes = applySceneMarkerEraserStroke(
|
|
this.state.strokes,
|
|
event.points,
|
|
event.radiusN,
|
|
(prefix) => makeStrokeId(prefix),
|
|
);
|
|
if (this.currentKey) {
|
|
this.cache.set(this.currentKey, strokes);
|
|
}
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
strokes,
|
|
draft: null,
|
|
};
|
|
return this.getState();
|
|
}
|
|
case 'strokes.clear':
|
|
if (this.currentKey) {
|
|
this.cache.set(this.currentKey, []);
|
|
}
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
strokes: [],
|
|
draft: null,
|
|
};
|
|
return this.getState();
|
|
default: {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
const _x: never = event;
|
|
return this.getState();
|
|
}
|
|
}
|
|
}
|
|
}
|