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:
@@ -17,11 +17,20 @@ import type {
|
||||
Project,
|
||||
ProjectId,
|
||||
AppToken,
|
||||
AppPlayer,
|
||||
AppPlayerTeam,
|
||||
PlayerId,
|
||||
PlayerTeamId,
|
||||
PlayerImageOffset,
|
||||
PlayersUpsertProgressEvent,
|
||||
Scene,
|
||||
SceneDarknessEvent,
|
||||
SceneDarknessState,
|
||||
SceneGrid,
|
||||
SceneId,
|
||||
SceneNpcToken,
|
||||
SceneNpcTokensSessionEvent,
|
||||
SceneNpcTokensSessionState,
|
||||
SceneToken,
|
||||
SceneTokensSessionEvent,
|
||||
SceneTokensSessionState,
|
||||
@@ -182,6 +191,25 @@ export const ipcChannels = {
|
||||
dispatch: 'sceneTokensSession.dispatch',
|
||||
stateChanged: 'sceneTokensSession.stateChanged',
|
||||
},
|
||||
players: {
|
||||
list: 'players.list',
|
||||
upsert: 'players.upsert',
|
||||
delete: 'players.delete',
|
||||
setOrder: 'players.setOrder',
|
||||
upsertTeam: 'players.upsertTeam',
|
||||
deleteTeam: 'players.deleteTeam',
|
||||
setTeamsOrder: 'players.setTeamsOrder',
|
||||
assignTeam: 'players.assignTeam',
|
||||
pickImage: 'players.pickImage',
|
||||
imageUrl: 'players.imageUrl',
|
||||
upsertProgress: 'players.upsertProgress',
|
||||
stateChanged: 'players.stateChanged',
|
||||
},
|
||||
sceneNpcTokensSession: {
|
||||
getState: 'sceneNpcTokensSession.getState',
|
||||
dispatch: 'sceneNpcTokensSession.dispatch',
|
||||
stateChanged: 'sceneNpcTokensSession.stateChanged',
|
||||
},
|
||||
video: {
|
||||
getState: 'video.getState',
|
||||
dispatch: 'video.dispatch',
|
||||
@@ -254,6 +282,9 @@ export type IpcEventMap = {
|
||||
[ipcChannels.sceneView.stateChanged]: { state: SceneViewState };
|
||||
[ipcChannels.tokens.stateChanged]: { tokens: AppToken[] };
|
||||
[ipcChannels.sceneTokensSession.stateChanged]: { state: SceneTokensSessionState };
|
||||
[ipcChannels.players.stateChanged]: { players: AppPlayer[]; teams: AppPlayerTeam[] };
|
||||
[ipcChannels.players.upsertProgress]: PlayersUpsertProgressEvent;
|
||||
[ipcChannels.sceneNpcTokensSession.stateChanged]: { state: SceneNpcTokensSessionState };
|
||||
[ipcChannels.video.stateChanged]: { state: VideoPlaybackState };
|
||||
[ipcChannels.license.statusChanged]: { snapshot: LicenseSnapshot };
|
||||
[ipcChannels.windows.multiWindowStateChanged]: { open: boolean };
|
||||
@@ -368,6 +399,9 @@ export type IpcInvokeMap = {
|
||||
description?: string;
|
||||
filePath?: string;
|
||||
groupId?: NpcGroupId | null;
|
||||
ringColor?: string;
|
||||
imageOffset?: PlayerImageOffset;
|
||||
imageScale?: number;
|
||||
};
|
||||
res: { project: Project };
|
||||
};
|
||||
@@ -377,6 +411,9 @@ export type IpcInvokeMap = {
|
||||
name?: string;
|
||||
description?: string;
|
||||
groupId?: NpcGroupId | null;
|
||||
ringColor?: string;
|
||||
imageOffset?: PlayerImageOffset;
|
||||
imageScale?: number;
|
||||
};
|
||||
res: { project: Project };
|
||||
};
|
||||
@@ -708,6 +745,62 @@ export type IpcInvokeMap = {
|
||||
req: { event: SceneTokensSessionEvent };
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.players.list]: {
|
||||
req: Record<string, never>;
|
||||
res: { players: AppPlayer[]; teams: AppPlayerTeam[] };
|
||||
};
|
||||
[ipcChannels.players.upsert]: {
|
||||
req: {
|
||||
id?: PlayerId | null;
|
||||
name: string;
|
||||
filePath?: string | null;
|
||||
teamId?: PlayerTeamId | null;
|
||||
ringColor?: string;
|
||||
imageOffset?: PlayerImageOffset;
|
||||
imageScale?: number;
|
||||
};
|
||||
res: { player: AppPlayer };
|
||||
};
|
||||
[ipcChannels.players.delete]: {
|
||||
req: { id: PlayerId };
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.players.setOrder]: {
|
||||
req: { playerIds: PlayerId[] };
|
||||
res: { players: AppPlayer[] };
|
||||
};
|
||||
[ipcChannels.players.upsertTeam]: {
|
||||
req: { id?: PlayerTeamId | null; name: string; color?: string };
|
||||
res: { team: AppPlayerTeam };
|
||||
};
|
||||
[ipcChannels.players.deleteTeam]: {
|
||||
req: { id: PlayerTeamId };
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.players.setTeamsOrder]: {
|
||||
req: { teamIds: PlayerTeamId[] };
|
||||
res: { teams: AppPlayerTeam[] };
|
||||
};
|
||||
[ipcChannels.players.assignTeam]: {
|
||||
req: { playerId: PlayerId; teamId: PlayerTeamId | null };
|
||||
res: { player: AppPlayer | null };
|
||||
};
|
||||
[ipcChannels.players.pickImage]: {
|
||||
req: Record<string, never>;
|
||||
res: { canceled: true } | { canceled: false; filePath: string; previewDataUrl: string };
|
||||
};
|
||||
[ipcChannels.players.imageUrl]: {
|
||||
req: { id: PlayerId };
|
||||
res: { url: string | null };
|
||||
};
|
||||
[ipcChannels.sceneNpcTokensSession.getState]: {
|
||||
req: Record<string, never>;
|
||||
res: { state: SceneNpcTokensSessionState };
|
||||
};
|
||||
[ipcChannels.sceneNpcTokensSession.dispatch]: {
|
||||
req: { event: SceneNpcTokensSessionEvent };
|
||||
res: { ok: true };
|
||||
};
|
||||
[ipcChannels.video.getState]: {
|
||||
req: Record<string, never>;
|
||||
res: { state: VideoPlaybackState };
|
||||
@@ -749,6 +842,9 @@ export type LegacyIpcEventMap = {
|
||||
[ipcChannels.sceneView.stateChanged]: { state: SceneViewState };
|
||||
[ipcChannels.tokens.stateChanged]: { tokens: AppToken[] };
|
||||
[ipcChannels.sceneTokensSession.stateChanged]: { state: SceneTokensSessionState };
|
||||
[ipcChannels.players.stateChanged]: { players: AppPlayer[]; teams: AppPlayerTeam[] };
|
||||
[ipcChannels.players.upsertProgress]: PlayersUpsertProgressEvent;
|
||||
[ipcChannels.sceneNpcTokensSession.stateChanged]: { state: SceneNpcTokensSessionState };
|
||||
[ipcChannels.video.stateChanged]: { state: VideoPlaybackState };
|
||||
[ipcChannels.license.statusChanged]: Record<string, never>;
|
||||
};
|
||||
@@ -764,6 +860,7 @@ export type ScenePatch = {
|
||||
darkenScene?: boolean;
|
||||
traps?: SceneTrap[];
|
||||
tokens?: SceneToken[];
|
||||
npcTokens?: SceneNpcToken[];
|
||||
grid?: SceneGrid;
|
||||
settings?: Partial<Scene['settings']>;
|
||||
media?: Partial<Scene['media']>;
|
||||
|
||||
Reference in New Issue
Block a user