feat(control): grid snap, NPC context actions, and overlay dim fix

Re-enable users-branch UI, snap session tokens to square/hex grid from the control preview, refine inactive/open-info NPC menus, block marker actions while an effect brush is active, and dim materials/NPC overlays only when they are open.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-08-06 13:48:59 +08:00
parent e53d1ea934
commit 4456eb0277
24 changed files with 563 additions and 41 deletions
@@ -25,6 +25,7 @@ function PlayerSprite({
displayScale,
gridFit,
onMove,
snapNorm,
}: {
player: AppPlayer;
nx: number;
@@ -35,6 +36,7 @@ function PlayerSprite({
displayScale: number;
gridFit: number;
onMove?: (playerId: string, nx: number, ny: number) => void;
snapNorm?: (nx: number, ny: number) => { nx: number; ny: number };
}) {
const imageUrl = usePlayerImageUrl(player.id);
const [localPos, setLocalPos] = useState<{ nx: number; ny: number } | null>(null);
@@ -105,8 +107,11 @@ function PlayerSprite({
const drag = dragRef.current;
if (drag?.pointerId !== e.pointerId) return;
const p = point(e);
drag.lastNx = Math.max(0, Math.min(1, drag.startNx + p.x - drag.pointerNx));
drag.lastNy = Math.max(0, Math.min(1, drag.startNy + p.y - drag.pointerNy));
const rawNx = Math.max(0, Math.min(1, drag.startNx + p.x - drag.pointerNx));
const rawNy = Math.max(0, Math.min(1, drag.startNy + p.y - drag.pointerNy));
const snapped = snapNorm ? snapNorm(rawNx, rawNy) : { nx: rawNx, ny: rawNy };
drag.lastNx = snapped.nx;
drag.lastNy = snapped.ny;
setLocalPos({ nx: drag.lastNx, ny: drag.lastNy });
schedule(drag.lastNx, drag.lastNy);
}
@@ -135,6 +140,7 @@ export function ScenePlayerTokensOverlay({
grid = null,
editable = false,
onMove,
snapNorm,
}: {
library: readonly AppPlayer[];
session: ScenePlayerTokensSessionState | null;
@@ -144,6 +150,7 @@ export function ScenePlayerTokensOverlay({
grid?: SceneGrid | null;
editable?: boolean;
onMove?: (playerId: string, nx: number, ny: number) => void;
snapNorm?: (nx: number, ny: number) => { nx: number; ny: number };
}) {
if (!viewport || !session?.visible) return null;
const byId = new Map(library.map((p) => [String(p.id), p]));
@@ -166,6 +173,7 @@ export function ScenePlayerTokensOverlay({
displayScale={displayScale}
gridFit={gridFit}
{...(onMove ? { onMove } : {})}
{...(snapNorm ? { snapNorm } : {})}
/>
);
})}