feat(materials): add campaign materials overlay for sessions

Let GMs manage and show images over the scene from the editor and control panel, with zoom tools, help docs, and full ru/en i18n.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-17 11:05:38 +08:00
parent 195d4be086
commit 61875be857
39 changed files with 2521 additions and 22 deletions
+15
View File
@@ -7,6 +7,9 @@ import { PixiEffectsOverlay } from './effects/PxiEffectsOverlay';
import { SceneDarknessOverlay } from './effects/SceneDarknessOverlay';
import { useEffectsState } from './effects/useEffectsState';
import { useSceneDarknessState } from './effects/useSceneDarknessState';
import { DEFAULT_MATERIALS_OVERLAY_LAYOUT } from '../../shared/types';
import { MaterialOverlay } from './materials/MaterialOverlay';
import { useMaterialsOverlayState } from './materials/useMaterialsOverlayState';
import styles from './PresentationView.module.css';
import { RotatedImage } from './RotatedImage';
import { useAssetUrl } from './useAssetImageUrl';
@@ -28,6 +31,7 @@ export function PresentationView({
}: PresentationViewProps) {
const [fxState] = useEffectsState();
const [sdState] = useSceneDarknessState();
const [materialsOverlay] = useMaterialsOverlayState();
const [vp] = useVideoPlaybackState();
const videoElRef = useRef<HTMLVideoElement | null>(null);
const [contentRect, setContentRect] = React.useState<{ x: number; y: number; w: number; h: number } | null>(
@@ -35,6 +39,10 @@ export function PresentationView({
);
const scene =
session?.project && session.currentSceneId ? session.project.scenes[session.currentSceneId] : undefined;
const activeMaterial =
session?.project && materialsOverlay?.activeMaterialId
? (session.project.materials ?? []).find((m) => m.id === materialsOverlay.activeMaterialId)
: undefined;
const originalUrl = useAssetUrl(scene?.previewAssetId ?? null);
const thumbUrl = useAssetUrl(scene?.previewThumbAssetId ?? null);
const [shownImageUrl, setShownImageUrl] = useState<string | null>(null);
@@ -138,6 +146,13 @@ export function PresentationView({
{showEffects && scene?.previewAssetType === 'image' && scene.darkenScene && contentRect ? (
<SceneDarknessOverlay state={sdState} overlayAlpha={1} viewport={contentRect} />
) : null}
{activeMaterial ? (
<MaterialOverlay
assetId={activeMaterial.assetId}
rotationDeg={activeMaterial.rotationDeg ?? 0}
layout={materialsOverlay?.layout ?? DEFAULT_MATERIALS_OVERLAY_LAYOUT}
/>
) : null}
{showTitle ? (
<div className={styles.titleWrap}>
<div className={compact ? styles.titleCompact : styles.titleFull}>