- Replace all emoji icons (~26 instances) with Lucide icons across the app
(DashboardPage, SessionSidebar, PlayerBoards, MyCharacterPanel,
PlayerViewPage, EncounterTracker, CampaignsPage, HandoutControl,
SessionControl, CharacterSheet, EncounterTipCard, ActionGuide)
- Expose real 5e race data in wizard (ASI/vision/traits instead of stub desc);
add vision field to loadRaces5e return type
- Surface subclass descriptions after picking a subclass in the wizard
- Add per-class tips, race synergy notes, and ability/skill guidance in wizard
- New ActionGuide component: collapsible "What can I do on my turn?" in player view
- New SessionPrepCard: AI + fallback session hook generator on assistant page
- New NpcGenCard: AI + fallback quick NPC generator with "Add to campaign" action
- Inline NPC detail generator (wand button) on each NPC card in NpcsPage
- Quest hook generator button in QuestsPage header
- Condition tooltips in player party view (useConditionGlossary)
- Pass campaign.system through session snapshot so player view is system-aware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Autosave: while signed in, a debounced full backup pushes whenever durable data
changes (useCloudAutosave watches a fingerprint of the main tables; high-churn
tables excluded). No-op signed out.
- Campaign-agnostic characters: PCs are now global — the Characters roster shows
every player character regardless of active campaign, and any PC can be added to
a fight. NPCs stay per-campaign. (charactersRepo.listAllPcs / useAllPcs; difficulty
fallback stays campaign-scoped.)
- Live "bring your own character": a joining player can push one of their own local
characters; the GM's grant inserts it into the campaign and seats them.
- Character builder: a system picker (5e / PF2e, defaults to the campaign) so creation
no longer assumes 5e; PF2e skill wording ("Trained" vs "proficient"); spell step now
explains how many to pick + that they're known spells prepared later.
230 unit + 34 e2e + 2 realtime green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Player room connection lifted out of the /play route to an app-level hook
(usePlayerConnection in RootLayout), keyed by a persisted joinIntent — so a
player stays connected while browsing their sheet, the compendium, anywhere,
and a reload reconnects automatically.
- Header "● Live: CODE · Leave" badge (PlayerSessionBadge); leaving is explicit
(leaveRoom clears intent + disconnects). /play renders the table from the store.
- Realtime e2e: player navigates away, badge persists, returns to a synced table.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- GM "📣 Handout" composer in the header (title + body + optional image) sets
uiStore.activeHandout; a clear button hides it.
- snapshot gains an optional handout {title, body, imageId}; buildSnapshot streams
the handout image over the existing image channel (id "handout").
- Player view (local + networked) shows the handout as a prominent card above the
boards. resizeToMax() keeps handout images compact.
- (Live scene/map switching already works via "Show to players".)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 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>
Players claim a seat (GM approves) and drive their own character live —
HP/slots/resources/conditions/dice broadcast to the table — via new seat
protocol messages and per-seat ownership enforced in RoomHub. GM persists
player edits (charactersRepo.update → liveQuery → rebroadcast). Offline dev:
"Share with player" encodes a claim link (src/lib/sync/playerLink.ts) → /player
imports the character locally for full-sheet editing, bundled back as an
offlineSnapshot on seat claim for GM review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Pure geometry library src/lib/map/* (grid, distance 5e/pf2e/euclid, AoE
circle/cone/line/square, polygon rasterize + point-in-polygon, brush, fog set
algebra, player projection) with unit tests.
- Schema: map tokens gain size/kind/characterId/hp/conditions/gmOnly; maps gain
drawings/gridUnit(feet)/gridType/fogVersion + point/drawing sub-schemas. Dexie v8
additive migration; mapsRepo.get + transactional mutate.
- Editor split into a shared MapCanvas renderer + MapEditor tool state machine:
fog via brush/rectangle/polygon (+ size, reveal-all/hide-all), measurement
(system-aware distance in feet), AoE templates (circle/cone/line/square preview),
drawing annotations (freehand/line/arrow/rect/circle/text, GM-only), pings, and
richer tokens (NxN size, character link with live HP ring, condition dots, edit popover).
- Player-facing projection: toPlayerProjection strips GM-only content; PlayerMapView
renders it read-only with opaque fog; uiStore.activeMapId + 'Show to players'; the
player view now shows the live battle map. enemyStatus extracted to
src/lib/combat/playerProjection.ts (reused, and for Phase 18).
9 geometry unit tests; maps e2e covers upload→token→reveal-all→player projection.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Player View (/play): read-only projector screen — party HP bars + conditions,
active-encounter initiative with current turn; enemy HP hidden behind a
Healthy/Bloodied/Down status band; Fullscreen button
- SyncAdapter seam (src/lib/sync): local-first default via Dexie liveQuery;
documents where a future networked (WebSocket/CRDT) backend plugs in
- Dashboard + routes get a Player View entry
- Robustness: encountersRepo.mutate() does transactional read-modify-write so
rapid combat mutations can't overwrite each other (old C19 race); tracker uses it
New player-view e2e. Networked multiplayer backend intentionally deferred.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>