diff --git a/ui/src/game/Game.svelte b/ui/src/game/Game.svelte index b5a7472..2e21ee4 100644 --- a/ui/src/game/Game.svelte +++ b/ui/src/game/Game.svelte @@ -10,6 +10,7 @@ import Rack from './Rack.svelte'; import { gateway } from '../lib/gateway'; import { gameSource, localSource, isLocalGameId } from '../lib/gamesource'; + import { localGuestId } from '../lib/localguest'; import { notePreviewLocal, notePreviewNetwork } from '../lib/localeval-metrics'; import { navigate } from '../lib/router.svelte'; import { app, handleError, showToast, markChatRead, seedChatUnread } from '../lib/app.svelte'; @@ -1442,9 +1443,18 @@ // seatName renders a seat's name: "you" for the viewer, the localized "searching for // opponent" placeholder for an open game's still-empty seat (no account), otherwise the // display name. + // isMe reports whether a seat belongs to the viewer — matched against the server account id OR + // the device-local guest id, the same rule the lobby uses for "my games". A device-local game + // (vs_ai / hotseat) is seated under the local guest id when created offline, and its human seat + // must still read "You" after a merge switches the active account to a durable id; without the + // local-guest arm the seat matched neither and a vs_ai game showed BOTH seats as robots. + function isMe(accountId: string | undefined): boolean { + return !!accountId && (accountId === app.session?.userId || accountId === localGuestId()); + } + function seatName(s: { accountId: string; displayName: string } | undefined): string { if (!s) return ''; - if (s.accountId === app.session?.userId) return t('common.you'); + if (isMe(s.accountId)) return t('common.you'); // An honest-AI game shows the robot opponent as 🤖, never its (pooled human-like) name. if (view?.game.vsAi) return '🤖'; if (!s.accountId) return t('game.searchingForOpponent');