diff --git a/app/main/index.ts b/app/main/index.ts index 6c0218d..ace002d 100644 --- a/app/main/index.ts +++ b/app/main/index.ts @@ -8,6 +8,7 @@ import { installStdoutEpipeGuards } from './safeConsole'; installStdoutEpipeGuards(); import { openDialogFilterLabel } from '../shared/appBranding'; +import { USERS_BRANCH_FEATURES_ENABLED } from '../shared/features/usersBranchFeatures'; import { ipcChannels, type ScenePreviewImportEvent, type SessionState } from '../shared/ipc/contracts'; import { PROJECT_ZIP_OPEN_DIALOG_FILTER, @@ -402,8 +403,10 @@ async function main() { const licenseService = new LicenseService(app.getPath('userData')); tokensStore = new TokensStore(app.getPath('userData')); await tokensStore.ensureLoaded(); - playersStore = new PlayersStore(app.getPath('userData')); - await playersStore.ensureLoaded(); + if (USERS_BRANCH_FEATURES_ENABLED) { + playersStore = new PlayersStore(app.getPath('userData')); + await playersStore.ensureLoaded(); + } setLicenseAssert(() => { licenseService.assertForIpc(); }); @@ -429,9 +432,11 @@ async function main() { sceneTokensSessionStore.reset(); sceneNpcTokensSessionStore.reset(); scenePlayerTokensSessionStore.reset(); - const playerIds = Array.isArray(req?.playerIds) ? req.playerIds.map(String).filter(Boolean) : []; - if (playerIds.length > 0) { - scenePlayerTokensSessionStore.dispatch({ kind: 'setSelection', playerIds }); + if (USERS_BRANCH_FEATURES_ENABLED) { + const playerIds = Array.isArray(req?.playerIds) ? req.playerIds.map(String).filter(Boolean) : []; + if (playerIds.length > 0) { + scenePlayerTokensSessionStore.dispatch({ kind: 'setSelection', playerIds }); + } } effectsStore.dispatch({ kind: 'tool.set', tool: effectsDefaultTool() }); openMultiWindow(); @@ -1357,13 +1362,19 @@ async function main() { }); registerHandler(ipcChannels.players.list, async () => { - await playersStore!.ensureLoaded(); - return { players: playersStore!.listPlayers(), teams: playersStore!.listTeams() }; + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { players: [], teams: [] }; + } + await playersStore.ensureLoaded(); + return { players: playersStore.listPlayers(), teams: playersStore.listTeams() }; }); registerHandler( ipcChannels.players.upsert, async ({ id, name, filePath, teamId, ringColor, imageOffset, imageScale }) => { - const player = await playersStore!.upsert( + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + throw new Error('Players library is disabled in this build'); + } + const player = await playersStore.upsert( { name, ...(id !== undefined ? { id } : {}), @@ -1380,17 +1391,26 @@ async function main() { }, ); registerHandler(ipcChannels.players.delete, async ({ id }) => { - await playersStore!.delete(id); + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { ok: true }; + } + await playersStore.delete(id); emitPlayersState(); return { ok: true }; }); registerHandler(ipcChannels.players.setOrder, async ({ playerIds }) => { - const players = await playersStore!.setPlayersOrder(playerIds); + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { players: [] }; + } + const players = await playersStore.setPlayersOrder(playerIds); emitPlayersState(); return { players }; }); registerHandler(ipcChannels.players.upsertTeam, async ({ id, name, color }) => { - const team = await playersStore!.upsertTeam({ + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + throw new Error('Players library is disabled in this build'); + } + const team = await playersStore.upsertTeam({ name, ...(id !== undefined ? { id } : {}), ...(color !== undefined ? { color } : {}), @@ -1399,21 +1419,33 @@ async function main() { return { team }; }); registerHandler(ipcChannels.players.deleteTeam, async ({ id }) => { - await playersStore!.deleteTeam(id); + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { ok: true }; + } + await playersStore.deleteTeam(id); emitPlayersState(); return { ok: true }; }); registerHandler(ipcChannels.players.setTeamsOrder, async ({ teamIds }) => { - const teams = await playersStore!.setTeamsOrder(teamIds); + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { teams: [] }; + } + const teams = await playersStore.setTeamsOrder(teamIds); emitPlayersState(); return { teams }; }); registerHandler(ipcChannels.players.assignTeam, async ({ playerId, teamId }) => { - const player = await playersStore!.assignPlayerTeam(playerId, teamId); + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + throw new Error('Players library is disabled in this build'); + } + const player = await playersStore.assignPlayerTeam(playerId, teamId); emitPlayersState(); return { player }; }); registerHandler(ipcChannels.players.pickImage, async () => { + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { canceled: true as const }; + } const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'], filters: [ @@ -1432,7 +1464,10 @@ async function main() { return { canceled: false as const, filePath, previewDataUrl }; }); registerHandler(ipcChannels.players.imageUrl, ({ id }) => { - return { url: playersStore!.getImageUrl(id) }; + if (!USERS_BRANCH_FEATURES_ENABLED || !playersStore) { + return { url: null }; + } + return { url: playersStore.getImageUrl(id) }; }); registerHandler(ipcChannels.sceneNpcTokensSession.getState, () => { return { state: sceneNpcTokensSessionStore.getState() }; diff --git a/app/main/protocol/dndAssetProtocol.ts b/app/main/protocol/dndAssetProtocol.ts index 1c50aa0..919002e 100644 --- a/app/main/protocol/dndAssetProtocol.ts +++ b/app/main/protocol/dndAssetProtocol.ts @@ -81,7 +81,7 @@ async function serveFile(info: ReadInfo, request: Request): Promise { export function registerDndAssetProtocol( projectStore: ZipProjectStore, tokensStore: TokensStore, - playersStore: PlayersStore, + playersStore: PlayersStore | null, ): void { session.defaultSession.protocol.handle('dnd', async (request) => { const url = new URL(request.url); @@ -95,7 +95,7 @@ export function registerDndAssetProtocol( } else if (url.hostname === 'token') { info = tokensStore.getImageReadInfo(asTokenId(id)); } else if (url.hostname === 'player') { - info = playersStore.getImageReadInfo(asPlayerId(id)); + info = playersStore?.getImageReadInfo(asPlayerId(id)) ?? null; } if (!info) { return new Response(null, { status: 404 }); diff --git a/app/renderer/control/ControlApp.tsx b/app/renderer/control/ControlApp.tsx index e4ed7ea..53d7f37 100644 --- a/app/renderer/control/ControlApp.tsx +++ b/app/renderer/control/ControlApp.tsx @@ -5,6 +5,7 @@ import { pickEraseTargetId } from '../../shared/effectEraserHitTest'; import { fitAspectRect } from '../../shared/geometry/fitAspectRect'; import { ipcChannels } from '../../shared/ipc/contracts'; import type { SessionState } from '../../shared/ipc/contracts'; +import { USERS_BRANCH_FEATURES_ENABLED } from '../../shared/features/usersBranchFeatures'; import { isNodeInMainStoryline, isNodeInSideStoryline, @@ -1778,6 +1779,8 @@ export function ControlApp() {
{t('control.screenPreview')}
+ {USERS_BRANCH_FEATURES_ENABLED ? ( + <>
{appVersionText ? ( @@ -868,6 +871,7 @@ export function EditorApp() {
{state.project ? ( <> + {USERS_BRANCH_FEATURES_ENABLED ? (
+ ) : ( + + )} {runDisabled ? (
+ {USERS_BRANCH_FEATURES_ENABLED ? (