UI overhaul "Living Codex" E: per-screen layouts
Ported all 9 screens to the prototype layout (visual only; all data wiring, text, aria-labels, and testids preserved) via parallel agents + central verification: - Dashboard: editorial hero, party roster w/ HP meters + avatars, quests, threats, rolls. - Campaigns: cover-art deck cards + gilt rule + footer. - Character sheet + roster: hero header, StatCoin abilities, prof rows; grimoire cards. - Combat: glowing initiative rows (PC gilt / foe ember), condition badges, log. - Dice: die-pool glyphs, adv/dis segmented toggle, crit/fumble stage, history. - Compendium: Spectral stat blocks (ember headers, ability row). - Settings: grouped icon-tile cards. Live Session: room-code card, seat grid, player board. - Battle Map: carded list + tooled editor chrome (canvas untouched). Regression fixes after the ports: dice die-buttons aria-label `d4` not `Roll 1d4` (was colliding with the primary Roll button); map "Open player view" link uses an ExternalLink icon (test updated, ↗ glyph removed); seat-claim card gets a stable data-testid="seat-option" (redesign moved rounded-lg→rounded-xl). 223 unit + 34 e2e + 2 realtime green; tsc + eslint + build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { UserPlus } from 'lucide-react';
|
||||
import type { Snapshot } from '@/lib/sync/messages';
|
||||
import type { Character } from '@/lib/schemas';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { EmptyState } from '@/components/ui/Page';
|
||||
import { Avatar, Badge } from '@/components/ui/Codex';
|
||||
|
||||
/**
|
||||
* Shown to a joined player before they control a character: pick "you" from the
|
||||
@@ -18,17 +20,21 @@ export function SeatClaimScreen({ snapshot, localChars, pending, onClaim }: {
|
||||
return <EmptyState title="No characters yet" hint="The GM hasn't added any player characters to this campaign." />;
|
||||
}
|
||||
return (
|
||||
<section className="mb-6">
|
||||
<h2 className="mb-2 text-sm font-semibold uppercase tracking-wide text-muted">Which character is yours?</h2>
|
||||
<p className="mb-3 text-sm text-muted">Pick your character to manage its HP, spells, and rolls live. The GM approves the request.</p>
|
||||
<section className="paper-grain mb-6 rounded-xl border border-line bg-panel p-4">
|
||||
<div className="font-display text-sm italic text-accent">Claim your seat</div>
|
||||
<h2 className="font-display text-xl font-semibold text-ink">Which character is yours?</h2>
|
||||
<p className="mt-1 text-sm text-muted">Pick your character to manage its HP, spells, and rolls live. The GM approves the request.</p>
|
||||
<hr className="gilt-rule my-3" />
|
||||
<div className="grid gap-2 sm:grid-cols-2">
|
||||
{snapshot.party.map((c) => {
|
||||
const hasOffline = localChars.has(c.id);
|
||||
return (
|
||||
<div key={c.id} className="flex items-center gap-3 rounded-lg border border-line bg-panel p-3">
|
||||
<div className="flex-1">
|
||||
<div className="font-display text-lg font-semibold text-ink">{c.name}</div>
|
||||
<div className="text-xs text-muted">Lv {c.level} · AC {c.ac} · {c.hp.current}/{c.hp.max} HP{hasOffline ? ' · offline edits ready' : ''}</div>
|
||||
<div key={c.id} data-testid="seat-option" className="flex items-center gap-3 rounded-xl border border-line bg-surface-2 p-3">
|
||||
<Avatar name={c.name} size={40} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate font-display text-lg font-semibold text-ink">{c.name}</div>
|
||||
<div className="font-mono text-xs text-muted">Lv {c.level} · AC {c.ac} · {c.hp.current}/{c.hp.max} HP</div>
|
||||
{hasOffline && <Badge tone="arcane" className="mt-1"><UserPlus size={11} aria-hidden /> offline edits ready</Badge>}
|
||||
</div>
|
||||
<Button size="sm" variant="primary" disabled={pending} onClick={() => onClaim(c.id)}>This is me</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user