Files
History/src/components/NationSelect.tsx
T
kamilkrzywdaandOpenCode e2936e85bb History: AI-driven alternate-history simulator
Pax Historia-style sim where the LLM is the world engine.

- React 19 + Vite 6 + Tailwind v4 + shadcn/ui frontend
- Express single-port backend (/api + /sse + built client + SPA fallback)
- SQLite (better-sqlite3) persistence, one row per game
- MapLibre choropleth map (OpenFreeMap dark + ISO_A3 regions)
- Defensive turn loop: strip fences, balanced-{} extract, parse, clamp 0-100,
  retry-once nudge, generic 'unclear' fallback; never crashes on bad output
- 5 seed presets (wwii-1939, coldwar-1955, modern-2025, no-wwii-1940, aegis-2400)
- LLM client over native fetch w/ injectable transport (mesh-bot pattern);
  LLM_STUB=1 deterministic transport for tests/e2e
- Unit (vitest, 19) + deterministic Playwright e2e
- Multi-stage node:22-slim Docker + Traefik (history.dev.kkhost.pl:4321)

Co-Authored-By: OpenCode <opencode@users.noreply.github.com>
2026-10-10 21:20:23 +00:00

57 lines
2.0 KiB
TypeScript

import { Button } from './ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import StatBar from './StatBar';
import { cn } from '@/lib/utils';
import type { Preset } from '@shared/types';
interface Props {
preset: Preset;
loading?: boolean;
onPick: (nationId: string) => void;
onBack: () => void;
}
/** Pick which nation to lead, with a short world-context blurb above. */
export default function NationSelect({ preset, loading, onPick, onBack }: Props) {
return (
<div className="mx-auto max-w-6xl px-6 py-8">
<Button variant="ghost" size="sm" onClick={onBack} className="mb-4">
← Back to scenarios
</Button>
<h1 className="text-3xl font-bold tracking-tight">{preset.title}</h1>
<p
className="mt-3 max-w-3xl whitespace-pre-line text-sm leading-relaxed text-muted-foreground"
data-testid="world-background"
>
{preset.worldBackground}
</p>
<h2 className="mb-4 mt-8 text-lg font-semibold">Choose your nation</h2>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{preset.nations.map((n) => (
<Card key={n.id} className="flex flex-col" data-testid={`nation-card-${n.id}`}>
<CardHeader>
<CardTitle className="text-base">{n.name}</CardTitle>
<CardDescription>
{n.type} · {n.territory.length} region{n.territory.length === 1 ? '' : 's'}
</CardDescription>
</CardHeader>
<CardContent className="mt-auto space-y-1.5">
<StatBar label="Military" value={n.stats.military} />
<StatBar label="Economy" value={n.stats.economy} />
<StatBar label="Stability" value={n.stats.stability} />
<Button
className={cn('w-full mt-3')}
onClick={() => onPick(n.id)}
disabled={loading}
>
Play as {n.name}
</Button>
</CardContent>
</Card>
))}
</div>
</div>
);
}