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
+20 -16
View File
@@ -1,6 +1,9 @@
import { Link } from '@tanstack/react-router';
import { useLiveQuery } from 'dexie-react-hooks';
import { ChevronRight, Shield, ScrollText, CheckCheck, Skull, Handshake, VenetianMask } from 'lucide-react';
import {
Brain, Users, Swords, ScrollText, VenetianMask, Target, Map, CalendarDays, Library, Hammer, Dice5, Monitor,
ChevronRight, Shield, CheckCheck, Skull, Handshake,
} from 'lucide-react';
import type { Campaign, Character, Npc, Quest, DiceRoll } from '@/lib/schemas';
import { diceRepo } from '@/lib/db/repositories';
import { getSystem } from '@/lib/rules';
@@ -15,20 +18,21 @@ export function DashboardPage() {
return <RequireCampaign>{(c) => <Dashboard campaign={c} />}</RequireCampaign>;
}
const LINKS = [
{ to: '/assistant', label: 'Assistant', icon: '🧠' },
{ to: '/characters', label: 'Characters', icon: '🧝' },
{ to: '/combat', label: 'Combat', icon: '⚔' },
{ to: '/notes', label: 'Notes & Wiki', icon: '📜' },
{ to: '/npcs', label: 'NPCs', icon: '🎭' },
{ to: '/quests', label: 'Quests', icon: '🗺' },
{ to: '/maps', label: 'Maps', icon: '🗺️' },
{ to: '/calendar', label: 'Calendar', icon: '📅' },
{ to: '/compendium', label: 'Compendium', icon: '📚' },
{ to: '/homebrew', label: 'Homebrew', icon: '🛠️' },
{ to: '/dice', label: 'Dice', icon: '🎲' },
{ to: '/play', label: 'Player View', icon: '📺' },
] as const;
type NavIcon = React.ComponentType<{ size?: number; className?: string; 'aria-hidden'?: boolean | 'true' | 'false' }>;
const LINKS: { to: string; label: string; icon: NavIcon }[] = [
{ to: '/assistant', label: 'Assistant', icon: Brain },
{ to: '/characters', label: 'Characters', icon: Users },
{ to: '/combat', label: 'Combat', icon: Swords },
{ to: '/notes', label: 'Notes & Wiki', icon: ScrollText },
{ to: '/npcs', label: 'NPCs', icon: VenetianMask },
{ to: '/quests', label: 'Quests', icon: Target },
{ to: '/maps', label: 'Maps', icon: Map },
{ to: '/calendar', label: 'Calendar', icon: CalendarDays },
{ to: '/compendium', label: 'Compendium', icon: Library },
{ to: '/homebrew', label: 'Homebrew', icon: Hammer },
{ to: '/dice', label: 'Dice', icon: Dice5 },
{ to: '/play', label: 'Player View', icon: Monitor },
];
function Dashboard({ campaign }: { campaign: Campaign }) {
const characters = useCharacters(campaign.id);
@@ -84,7 +88,7 @@ function Dashboard({ campaign }: { campaign: Campaign }) {
to={l.to}
className="flex flex-col items-center gap-1 rounded-xl border border-line bg-panel p-4 text-center transition-colors hover:border-accent/60 hover:bg-accent-glow"
>
<span className="text-2xl" aria-hidden>{l.icon}</span>
<l.icon size={24} className="text-muted" aria-hidden />
<span className="text-sm text-ink">{l.label}</span>
</Link>
))}