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
@@ -22,6 +22,8 @@ type MaterialOverlayProps = {
onZoomAt?: (nx: number, ny: number) => void;
/** Без собственного root/dim — внутри `SceneOverlayHost`. */
embedded?: boolean;
/** Маркеры легенды (норм. координаты картинки). */
legendMarkers?: readonly { id: string; number: number; nx: number; ny: number }[];
};
function RotateIcon() {
@@ -99,6 +101,7 @@ export function MaterialOverlay({
onLayoutChange,
onZoomAt,
embedded = false,
legendMarkers,
}: MaterialOverlayProps) {
const url = useAssetUrl(assetId);
const host = useSceneOverlayView();
@@ -381,16 +384,23 @@ export function MaterialOverlay({
src={url}
alt=""
draggable={false}
style={{
width: w,
height: h,
transform: 'translate(-50%, -50%)',
}}
onLoad={(e) => {
const img = e.currentTarget;
setNatural({ w: img.naturalWidth || 1, h: img.naturalHeight || 1 });
}}
/>
{(legendMarkers ?? []).map((m) => (
<div
key={m.id}
className={styles.legendMarker}
style={{
left: `${String(m.nx * 100)}%`,
top: `${String(m.ny * 100)}%`,
}}
>
{m.number}
</div>
))}
{editable && !zoomTool
? (['nw', 'ne', 'sw', 'se'] as const).map((corner) => (
<button