UI/UX "Living Codex" 4/n: assistant features + emoji purge

- 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>
This commit is contained in:
2026-06-08 23:29:06 +02:00
parent 235cdecb53
commit 740cf20b93
24 changed files with 1176 additions and 67 deletions
+39
View File
@@ -0,0 +1,39 @@
import { Check } from 'lucide-react';
import type { Character } from '@/lib/schemas';
import { getTurnGuide, getGenericGuide } from '@/lib/assistant/actions';
/** Collapsible "What can I do?" guide for the player in-session view. */
export function ActionGuide({ character }: { character: Character }) {
const guide = getTurnGuide(character.system, character.className?.toLowerCase() ?? '') ?? getGenericGuide(character.system);
return (
<details className="mt-3 rounded-lg border border-line bg-surface-2">
<summary className="cursor-pointer select-none px-3 py-2 text-xs text-muted hover:text-ink">
What can I do on my turn?
</summary>
<div className="border-t border-line px-3 pb-3 pt-2">
<ul className="space-y-2">
{guide.actions.map((a) => (
<li key={a.label}>
<span className="text-xs font-semibold text-ink">{a.label}: </span>
<span className="text-xs text-muted">{a.desc}</span>
</li>
))}
</ul>
{guide.upgradeAt && character.level < guide.upgradeAt.level && (
<p className="mt-2 rounded-md border border-accent/30 bg-accent/5 px-2 py-1 text-xs text-accent">
{guide.upgradeAt.note}
</p>
)}
{guide.upgradeAt && character.level >= guide.upgradeAt.level && (
<p className="mt-2 rounded-md border border-success/30 bg-success/5 px-2 py-1 text-xs text-success">
<Check size={12} className="mr-1 inline" aria-hidden /> {guide.upgradeAt.note}
</p>
)}
{guide.tip && (
<p className="mt-2 border-t border-line pt-2 text-xs italic text-muted">{guide.tip}</p>
)}
</div>
</details>
);
}