feat(tokens): app-local non-player tokens with session moves and UI polish

Add token library/placements, keep play-time moves for the session, lock presentation interactions, and fix export/import modal layout plus freeform trap label.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-27 11:04:12 +08:00
parent bdeb64e356
commit f270812219
44 changed files with 2617 additions and 341 deletions
@@ -109,6 +109,10 @@
transform: translate(-50%, -50%);
width: 520px;
max-width: calc(100vw - 32px);
max-height: calc(100vh - 32px);
overflow: auto;
box-sizing: border-box;
min-width: 0;
border-radius: var(--radius-lg);
border: 1px solid var(--stroke);
background: var(--color-surface-elevated);
@@ -116,6 +120,7 @@
padding: 16px;
display: grid;
gap: 12px;
grid-template-columns: minmax(0, 1fr);
}
.descriptionViewDialog {
+17
View File
@@ -36,6 +36,9 @@ import { useNpcsOverlayState } from '../shared/npcs/useNpcsOverlayState';
import { SceneOverlayHost } from '../shared/sceneOverlay/SceneOverlayHost';
import { useSceneViewState } from '../shared/sceneView/useSceneViewState';
import { SceneGridOverlay } from '../shared/grid/SceneGridOverlay';
import { SceneTokensOverlay } from '../shared/tokens/SceneTokensOverlay';
import { useAppTokens } from '../shared/tokens/useAppTokens';
import { useSceneTokensSession } from '../shared/tokens/useSceneTokensSession';
import { SceneTrapsOverlay } from '../shared/traps/SceneTrapsOverlay';
import { useSceneTrapsState } from '../shared/traps/useSceneTrapsState';
import { Button } from '../shared/ui/controls';
@@ -126,6 +129,8 @@ export function ControlApp() {
const [effectsSfxGainUi, setEffectsSfxGainUi] = useState(() => getEffectsSfxGain());
const [sdState, sd] = useSceneDarknessState();
const [sceneTraps, sceneTrapsApi] = useSceneTrapsState();
const appTokens = useAppTokens();
const [sceneTokensSession, sceneTokensApi] = useSceneTokensSession();
const [sceneView, sceneViewApi] = useSceneViewState();
const [sceneViewDraft, setSceneViewDraft] = useState<SceneViewCamera | null>(null);
const [materialsOverlay, materialsApi] = useMaterialsOverlayState();
@@ -1884,6 +1889,18 @@ export function ControlApp() {
clearDraftFromPixi();
}}
/>
{previewContentRect ? (
<SceneTokensOverlay
placements={currentScene?.tokens ?? []}
library={appTokens}
session={sceneTokensSession}
viewport={previewContentRect}
editable
onMove={(placementId, nx, ny) => {
void sceneTokensApi.dispatch({ kind: 'move', placementId, nx, ny });
}}
/>
) : null}
{previewContentRect ? (
<SceneTrapsOverlay
traps={currentScene?.traps ?? []}