feat: multi-material overlays, presentation guides, and release prebuilds

Align control/presentation with presentation screen rect and darkness z-order; sync window titles and session window cleanup. Pack Win/Mac/Linux with npmRebuild disabled and release-native-prep for classic-level and sharp.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-08-03 11:28:18 +08:00
parent 61446dacfc
commit 2979d06f1c
41 changed files with 1060 additions and 252 deletions
@@ -3,6 +3,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
import type { MaterialsZoomTool, NpcsZoomTool } from '../../../shared/types';
import styles from '../materials/MaterialOverlay.module.css';
import { overlayRootStyle, type SceneOverlayViewport } from './overlayViewport';
import { SceneOverlayViewContext } from './SceneOverlayViewContext';
export type SceneOverlayCloseAction = {
@@ -14,6 +15,10 @@ export type SceneOverlayCloseAction = {
type SceneOverlayHostProps = {
/** Есть ли что показывать (материал и/или NPC). */
active: boolean;
/** Область картинки сцены (contain); координаты относительно родителя. */
viewport?: SceneOverlayViewport | null;
/** Рамка видимой области (предпросмотр пульта). */
showViewportGuide?: boolean;
zoomTool?: MaterialsZoomTool | NpcsZoomTool;
onZoomAt?: (nx: number, ny: number, target: EventTarget | null) => void;
closes?: readonly SceneOverlayCloseAction[];
@@ -26,6 +31,8 @@ type SceneOverlayHostProps = {
*/
export function SceneOverlayHost({
active,
viewport = null,
showViewportGuide = false,
zoomTool = null,
onZoomAt,
closes = [],
@@ -58,7 +65,7 @@ export function SceneOverlayHost({
const ctx = useMemo(() => ({ rootRef, view }), [view]);
if (!active) return null;
if (!active && !showViewportGuide) return null;
const captureZoom = Boolean(zoomTool && onZoomAt);
const zoomCursor =
@@ -81,6 +88,7 @@ export function SceneOverlayHost({
className={[styles.root, styles.hostHitThrough, captureZoom ? styles.captureZoom : '', zoomCursor]
.filter(Boolean)
.join(' ')}
style={overlayRootStyle(viewport)}
role="presentation"
onClick={(e) => {
if (!captureZoom || !onZoomAt) return;
@@ -89,6 +97,7 @@ export function SceneOverlayHost({
onZoomAt(nx, ny, e.target);
}}
>
{showViewportGuide ? <div className={styles.viewportGuide} aria-hidden /> : null}
<div className={styles.dim} />
{children}
{closes.length > 0 ? (