feat(npcs): show multiple character overlays in session playback

Toggle NPCs independently from the characters window, keep one shared dim/close-all, and scroll descriptions for all selected characters.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-21 13:23:19 +08:00
parent f4c0ac1438
commit 1829191410
8 changed files with 538 additions and 121 deletions
+17 -10
View File
@@ -10,6 +10,7 @@ import { useSceneDarknessState } from './effects/useSceneDarknessState';
import { DEFAULT_MATERIALS_OVERLAY_LAYOUT, DEFAULT_NPCS_OVERLAY_LAYOUT } from '../../shared/types';
import { MaterialOverlay } from './materials/MaterialOverlay';
import { useMaterialsOverlayState } from './materials/useMaterialsOverlayState';
import { NpcsSceneOverlay } from './npcs/NpcsSceneOverlay';
import { useNpcsOverlayState } from './npcs/useNpcsOverlayState';
import styles from './PresentationView.module.css';
import { RotatedImage } from './RotatedImage';
@@ -45,10 +46,21 @@ export function PresentationView({
session?.project && materialsOverlay?.activeMaterialId
? (session.project.materials ?? []).find((m) => m.id === materialsOverlay.activeMaterialId)
: undefined;
const activeNpc =
session?.project && npcsOverlay?.activeNpcId
? (session.project.npcs ?? []).find((n) => n.id === npcsOverlay.activeNpcId)
: undefined;
const project = session?.project;
const activeNpcItems =
project && (npcsOverlay?.activeNpcIds?.length ?? 0) > 0
? (npcsOverlay?.activeNpcIds ?? [])
.map((id) => {
const npc = (project.npcs ?? []).find((n) => n.id === id);
if (!npc) return null;
return {
npcId: npc.id,
assetId: npc.avatarAssetId,
layout: npcsOverlay?.layouts[id] ?? DEFAULT_NPCS_OVERLAY_LAYOUT,
};
})
.filter((x): x is NonNullable<typeof x> => x !== null)
: [];
const originalUrl = useAssetUrl(scene?.previewAssetId ?? null);
const thumbUrl = useAssetUrl(scene?.previewThumbAssetId ?? null);
const [shownImageUrl, setShownImageUrl] = useState<string | null>(null);
@@ -159,12 +171,7 @@ export function PresentationView({
layout={materialsOverlay?.layout ?? DEFAULT_MATERIALS_OVERLAY_LAYOUT}
/>
) : null}
{activeNpc ? (
<MaterialOverlay
assetId={activeNpc.avatarAssetId}
layout={npcsOverlay?.layout ?? DEFAULT_NPCS_OVERLAY_LAYOUT}
/>
) : null}
{activeNpcItems.length > 0 ? <NpcsSceneOverlay items={activeNpcItems} /> : null}
{showTitle ? (
<div className={styles.titleWrap}>
<div className={compact ? styles.titleCompact : styles.titleFull}>