feat(players): app Players library and circular NPC tokens on scenes

Add userData players/teams, scene npcTokens with hex-inscribed sizing, session scale synced to presentation, and Playwright e2e coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-30 13:38:35 +08:00
parent a3a03eb9e3
commit 101f595bac
57 changed files with 4176 additions and 317 deletions
+12
View File
@@ -15,6 +15,8 @@ import { MaterialOverlay } from './materials/MaterialOverlay';
import { useMaterialsOverlayState } from './materials/useMaterialsOverlayState';
import { NpcsSceneOverlay } from './npcs/NpcsSceneOverlay';
import { useNpcsOverlayState } from './npcs/useNpcsOverlayState';
import { SceneNpcTokensOverlay } from './playerToken/SceneNpcTokensOverlay';
import { useSceneNpcTokensSession } from './playerToken/useSceneNpcTokensSession';
import { SceneOverlayHost } from './sceneOverlay/SceneOverlayHost';
import { useSceneViewState } from './sceneView/useSceneViewState';
import { SceneTokensOverlay } from './tokens/SceneTokensOverlay';
@@ -49,6 +51,7 @@ export function PresentationView({
const [npcsOverlay] = useNpcsOverlayState();
const appTokens = useAppTokens();
const [sceneTokensSession] = useSceneTokensSession();
const [sceneNpcTokensSession] = useSceneNpcTokensSession();
const [vp] = useVideoPlaybackState();
const videoElRef = useRef<HTMLVideoElement | null>(null);
const [contentRect, setContentRect] = React.useState<{ x: number; y: number; w: number; h: number } | null>(
@@ -177,6 +180,15 @@ export function PresentationView({
viewport={contentRect}
/>
) : null}
{scene?.previewAssetType === 'image' && contentRect ? (
<SceneNpcTokensOverlay
placements={scene.npcTokens ?? []}
library={project?.npcs ?? []}
session={sceneNpcTokensSession}
viewport={contentRect}
grid={scene.grid}
/>
) : null}
{scene?.previewAssetType === 'image' && contentRect ? (
<SceneTrapsOverlay
traps={scene.traps ?? []}