feat(scene): video map editor parity and help updates

Enable scene editor, overlays, effects, and darkness on video scenes; brighten GM trap markers; document snap, NPC types, materials, and control controls in RU/EN help.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-08-07 08:23:08 +08:00
parent 8d5a68c71e
commit 1fbaaa6e77
17 changed files with 443 additions and 98 deletions
+23 -33
View File
@@ -373,8 +373,7 @@ export function ControlApp() {
const currentHistoryIdx = currentGraphNodeId != null ? history.lastIndexOf(currentGraphNodeId) : -1;
const currentScene =
project && session?.currentSceneId ? project.scenes[session.currentSceneId] : undefined;
const isVideoPreviewScene = currentScene?.previewAssetType === 'video';
const isDarkenScene = Boolean(currentScene?.darkenScene) && !isVideoPreviewScene;
const isDarkenScene = Boolean(currentScene?.darkenScene);
const snapNormActive = useCallback(
(nx: number, ny: number) => {
@@ -868,7 +867,7 @@ export function ControlApp() {
useEffect(() => {
const frame = previewFrameRef.current;
if (!frame || isVideoPreviewScene) return;
if (!frame) return;
const onWheel = (e: WheelEvent) => {
e.preventDefault();
const host = previewHostRef.current;
@@ -892,7 +891,7 @@ export function ControlApp() {
};
frame.addEventListener('wheel', onWheel, { passive: false });
return () => frame.removeEventListener('wheel', onWheel);
}, [isVideoPreviewScene]);
}, []);
useEffect(() => {
return () => {
@@ -1256,11 +1255,6 @@ export function ControlApp() {
}
async function commitStroke(): Promise<void> {
if (isVideoPreviewScene) {
brushRef.current = null;
clearDraftFromPixi();
return;
}
if (!fxState) return;
const b = brushRef.current;
if (!b) return;
@@ -1608,8 +1602,7 @@ export function ControlApp() {
</Button>
</div>
<div className={styles.spacer12} />
{!isVideoPreviewScene ? (
<>
<>
<div className={styles.sectionLabel}>{t('control.effects')}</div>
<div className={styles.spacer8} />
<div className={styles.effectsStack}>
@@ -1815,7 +1808,6 @@ export function ControlApp() {
</div>
<div className={styles.spacer12} />
</>
) : null}
<div className={styles.storyWrap}>
<div className={styles.sectionLabel}>{t('control.storyLine')}</div>
<div className={styles.spacer10} />
@@ -1960,14 +1952,11 @@ export function ControlApp() {
</div>
</div>
<div className={styles.spacer10} />
{isVideoPreviewScene ? <div className={styles.videoHint}>{t('control.videoBrushHint')}</div> : null}
<div className={styles.spacer10} />
<div
ref={previewFrameRef}
className={styles.previewFrame}
title={
isVideoPreviewScene ? undefined : 'Колесо — зум; перетаскивание СКМ/ПКМ или Space+ЛКМ — пан'
}
title="Колесо — зум; перетаскивание СКМ/ПКМ или Space+ЛКМ — пан"
>
<div ref={previewHostRef} className={styles.previewHost}>
<ControlScenePreview
@@ -1977,8 +1966,7 @@ export function ControlApp() {
onContentRectChange={setPreviewContentRect}
/>
</div>
{!isVideoPreviewScene ? (
<>
<>
<SceneGridOverlay grid={currentScene?.grid} viewport={previewContentRect} />
<PixiEffectsOverlay
ref={effectsOverlayRef}
@@ -2188,17 +2176,20 @@ export function ControlApp() {
ny: snapped.ny,
});
}}
onContextMenu={
markersInteractive
? (e, placement) => {
{...(markersInteractive
? {
onContextMenu: (
e: React.MouseEvent,
placement: { id: string },
) => {
setNpcSessionCtxMenu({
x: e.clientX,
y: e.clientY,
placementId: String(placement.id),
});
}
: undefined
}
},
}
: {})}
/>
) : null}
{USERS_BRANCH_FEATURES_ENABLED && previewContentRect ? (
@@ -2280,6 +2271,13 @@ export function ControlApp() {
/>
) : null}
</>
{previewContentRect && currentScene?.darkenScene ? (
<SceneDarknessOverlay
state={sdState}
overlayAlpha={0.5}
viewport={previewContentRect}
style={{ zIndex: 30 }}
/>
) : null}
{(() => {
const project = session?.project;
@@ -2322,7 +2320,7 @@ export function ControlApp() {
const showMaterial = materialItems.length > 0;
const showNpcs = npcItems.length > 0;
const screenRect = presentationScreenRect;
const showGuide = Boolean(screenRect) && !isVideoPreviewScene;
const showGuide = Boolean(screenRect);
if (!showMaterial && !showNpcs && !showGuide) return null;
const closes = [
...(showMaterial
@@ -2357,14 +2355,6 @@ export function ControlApp() {
};
return (
<>
{previewContentRect && currentScene?.darkenScene ? (
<SceneDarknessOverlay
state={sdState}
overlayAlpha={0.5}
viewport={previewContentRect}
style={{ zIndex: 30 }}
/>
) : null}
<SceneOverlayHost
active={showMaterial || showNpcs}
viewport={screenRect}