feat: scene traps, material legends, and scene editor window

Add trap overlays with session state, material legend editor/panel, and a dedicated scene editor window wired through IPC and project persistence.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-24 16:29:43 +08:00
parent d3b1c4660d
commit 02d73ddf81
39 changed files with 2563 additions and 36 deletions
@@ -14,6 +14,7 @@ function emptyState(): MaterialsOverlayState {
revision: 1,
activeMaterialId: null,
layout: { ...DEFAULT_MATERIALS_OVERLAY_LAYOUT },
legendLayout: { ...DEFAULT_MATERIALS_OVERLAY_LAYOUT, cx: 0.82, cy: 0.5, scale: 0.85 },
zoomTool: null,
};
}
@@ -25,6 +26,16 @@ function initialLayout(rotationDeg?: number): MaterialsOverlayLayout {
});
}
function initialLegendLayout(): MaterialsOverlayLayout {
return clampMaterialsLayout({
...DEFAULT_MATERIALS_OVERLAY_LAYOUT,
cx: 0.82,
cy: 0.5,
scale: 0.85,
rotationDeg: 0,
});
}
export class MaterialsOverlayStore {
private state: MaterialsOverlayState = emptyState();
@@ -40,6 +51,7 @@ export class MaterialsOverlayStore {
revision: this.state.revision + 1,
activeMaterialId: null,
layout: { ...DEFAULT_MATERIALS_OVERLAY_LAYOUT },
legendLayout: initialLegendLayout(),
zoomTool: null,
};
return this.state;
@@ -54,6 +66,7 @@ export class MaterialsOverlayStore {
revision: this.state.revision + 1,
activeMaterialId: event.materialId,
layout: initialLayout(event.rotationDeg),
legendLayout: initialLegendLayout(),
zoomTool: this.state.zoomTool,
};
return this.state;
@@ -65,6 +78,7 @@ export class MaterialsOverlayStore {
revision: this.state.revision + 1,
activeMaterialId: event.materialId,
layout: initialLayout(event.rotationDeg),
legendLayout: initialLegendLayout(),
zoomTool: this.state.zoomTool,
};
return this.state;
@@ -81,6 +95,19 @@ export class MaterialsOverlayStore {
};
return this.state;
}
case 'legendLayout.set': {
if (this.state.activeMaterialId === null) return this.state;
this.state = {
...this.state,
revision: this.state.revision + 1,
legendLayout: clampMaterialsLayout({
...this.state.legendLayout,
...event.layout,
rotationDeg: 0,
}),
};
return this.state;
}
case 'zoomTool.set': {
const tool: MaterialsZoomTool = event.tool;
this.state = {