Phase 2: character portraits → token icons (local + realtime)
- character.portrait + mapToken.image (data URLs, Dexie v10, additive). - src/lib/img/resize.ts: center-crop + downscale helper (160px tokens / 256px portraits) keeping IndexedDB + wire payloads small. - Portrait uploader on the character sheet; token icon uploader in the map token modal (falls back to the linked character's portrait). Palette PC entries show the portrait thumbnail. - Tokens render the image clipped to the circle (HP ring + condition badge on top). - Realtime: generalized the image channel to many images (map bg + token/portrait icons), deduped by content and resent on reconnect. Player tokens/party carry an imageId resolved from the session image cache; party panel shows portraits. - Also: fix the encounter-picker dropdown text being clipped (drop fixed height). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ export interface CanvasToken {
|
||||
row: number;
|
||||
size: number;
|
||||
kind: 'pc' | 'npc' | 'monster' | 'object';
|
||||
/** resolved icon data URL (token image or linked character portrait) */
|
||||
image?: string | undefined;
|
||||
hp?: { current: number; max: number; temp: number } | undefined;
|
||||
conditions: { name: string; value?: number | undefined }[];
|
||||
}
|
||||
@@ -412,6 +414,10 @@ function TokenChip({ token, gridSize, vp, cols, rows, draggable, onMove, onClick
|
||||
touchAction: 'none',
|
||||
}}
|
||||
>
|
||||
{token.image && (
|
||||
<img src={token.image} alt="" draggable={false}
|
||||
className="pointer-events-none absolute inset-0 h-full w-full rounded-full object-cover" />
|
||||
)}
|
||||
{frac !== null && (
|
||||
<svg className="pointer-events-none absolute inset-0" viewBox="0 0 36 36" aria-hidden>
|
||||
<circle cx="18" cy="18" r="17" fill="none" stroke="rgba(0,0,0,0.35)" strokeWidth="2" />
|
||||
@@ -419,7 +425,7 @@ function TokenChip({ token, gridSize, vp, cols, rows, draggable, onMove, onClick
|
||||
strokeWidth="2.5" strokeDasharray={`${frac * 106.8} 106.8`} transform="rotate(-90 18 18)" strokeLinecap="round" />
|
||||
</svg>
|
||||
)}
|
||||
<span className="z-10 truncate px-0.5">{token.label}</span>
|
||||
{!token.image && <span className="z-10 truncate px-0.5">{token.label}</span>}
|
||||
{token.conditions.length > 0 && (
|
||||
<span className="absolute -right-1 -top-1 z-10 grid h-4 w-4 place-items-center rounded-full bg-danger text-[9px] text-white" title={token.conditions.map((c) => c.name).join(', ')}>
|
||||
{token.conditions.length}
|
||||
|
||||
Reference in New Issue
Block a user