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
+6 -2
View File
@@ -2,7 +2,8 @@ import fs from 'node:fs/promises';
import { session } from 'electron';
import { asAssetId, asTokenId } from '../../shared/types/ids';
import { asAssetId, asPlayerId, asTokenId } from '../../shared/types/ids';
import type { PlayersStore } from '../players/playersStore';
import type { ZipProjectStore } from '../project/zipStore';
import type { TokensStore } from '../tokens/tokensStore';
@@ -75,11 +76,12 @@ async function serveFile(info: ReadInfo, request: Request): Promise<Response> {
}
/**
* Обслуживает `dnd://asset?...` и `dnd://token?...`.
* Обслуживает `dnd://asset?...`, `dnd://token?...` и `dnd://player?...`.
*/
export function registerDndAssetProtocol(
projectStore: ZipProjectStore,
tokensStore: TokensStore,
playersStore: PlayersStore,
): void {
session.defaultSession.protocol.handle('dnd', async (request) => {
const url = new URL(request.url);
@@ -92,6 +94,8 @@ export function registerDndAssetProtocol(
info = projectStore.getAssetReadInfo(asAssetId(id));
} else if (url.hostname === 'token') {
info = tokensStore.getImageReadInfo(asTokenId(id));
} else if (url.hostname === 'player') {
info = playersStore.getImageReadInfo(asPlayerId(id));
}
if (!info) {
return new Response(null, { status: 404 });