@stargazeryan/poker-ui 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -96,8 +96,8 @@ var SMALL_RANK = {
96
96
  sm: "text-xl"
97
97
  };
98
98
  var SMALL_SUIT = {
99
- xs: "text-base",
100
- sm: "text-xl"
99
+ xs: "text-lg",
100
+ sm: "text-2xl"
101
101
  };
102
102
  var BACK_EMBLEM_SIZE = {
103
103
  xs: 14,
@@ -143,9 +143,9 @@ function PlayingCard({ card, size = "md", className }) {
143
143
  const suitClass = getSuitClass(suit);
144
144
  const large = size === "md" || size === "lg";
145
145
  if (!large) {
146
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn(SIZE_CLASSES[size], CARD_FACE, className), style: { aspectRatio: "10/14" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("absolute inset-0 flex flex-col items-center justify-center leading-none", suitClass), children: [
147
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(SMALL_RANK[size], "font-bold tracking-tight"), children: rank }),
148
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(SMALL_SUIT[size], "mt-[1px]"), children: suitSymbol })
146
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn(SIZE_CLASSES[size], CARD_FACE, className), style: { aspectRatio: "10/14" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("absolute inset-0 flex flex-col leading-none", suitClass), children: [
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(SMALL_RANK[size], "font-bold tracking-tight"), children: rank }) }),
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: SMALL_SUIT[size], children: suitSymbol }) })
149
149
  ] }) });
150
150
  }
151
151
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn(SIZE_CLASSES[size], CARD_FACE, className), style: { aspectRatio: "10/14" }, children: [
@@ -449,20 +449,28 @@ function CardTable({
449
449
  if (content === null) return null;
450
450
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute", style: { left: `${x}px`, top: `${y}px` }, children: content }, index);
451
451
  }) }),
452
- confetti && winnerSeats.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pointer-events-none absolute inset-0 z-30 overflow-hidden", "aria-hidden": "true", children: Array.from({ length: 16 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
453
- "span",
454
- {
455
- className: "absolute top-[34%] animate-confetti rounded-[2px]",
456
- style: {
457
- left: `${6 + i * 5.6}%`,
458
- width: 6,
459
- height: 11,
460
- backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
461
- animationDelay: `${i % 8 * 90}ms`
462
- }
463
- },
464
- i
465
- )) }),
452
+ confetti && winnerSeats.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pointer-events-none absolute inset-0 z-30 overflow-hidden", "aria-hidden": "true", children: Array.from({ length: 44 }).map((_, i) => {
453
+ const w = 5 + i % 4 * 2;
454
+ const h = 9 + i % 3 * 4;
455
+ const dur = 1.7 + i % 5 * 0.28;
456
+ const delay = i % 12 * 0.14;
457
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
458
+ "span",
459
+ {
460
+ className: "absolute top-[26%] animate-confetti rounded-[2px]",
461
+ style: {
462
+ left: `${i * 37 % 96}%`,
463
+ width: w,
464
+ height: h,
465
+ backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
466
+ animationDuration: `${dur}s`,
467
+ animationDelay: `${delay}s`,
468
+ animationIterationCount: "infinite"
469
+ }
470
+ },
471
+ i
472
+ );
473
+ }) }),
466
474
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute rounded-[50%] border border-white/10 pointer-events-none", style: rimInset, "aria-hidden": "true" })
467
475
  ]
468
476
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cn.ts","../src/format.ts","../src/seat.ts","../src/components/PlayingCard.tsx","../src/components/Seat.tsx","../src/components/Pot.tsx","../src/components/CardTable.tsx"],"sourcesContent":["export { cn } from \"./cn\";\nexport { BB, formatBB } from \"./format\";\nexport { nextSeatIndex, type NextSeatOptions } from \"./seat\";\n\nexport {\n PlayingCard,\n CardBack,\n HoleCards,\n CommunityCards,\n parseCard,\n getSuitSymbol,\n type PlayingCardProps,\n type CardBackProps,\n type HoleCardsProps,\n type CommunityCardsProps,\n type CardSize,\n} from \"./components/PlayingCard\";\n\nexport { Seat, type SeatProps } from \"./components/Seat\";\nexport { Pot, type PotProps } from \"./components/Pot\";\nexport { CardTable, type CardTableProps } from \"./components/CardTable\";\n\n// Back-compat alias (the table is now game-agnostic).\nexport { CardTable as PokerTable, type CardTableProps as PokerTableProps } from \"./components/CardTable\";\n","import { twMerge } from \"tailwind-merge\";\n\n/** Join class names and resolve conflicting Tailwind utilities (last wins). */\nexport function cn(...parts: Array<string | false | null | undefined>): string {\n return twMerge(parts.filter(Boolean).join(\" \"));\n}\n","/** Big Blind value used for stack display. */\nexport const BB = 20;\n\n/** Format a chip amount as a Big-Blind string, e.g. 1234 -> \"61.7 BB\". */\nexport function formatBB(amount: number): string {\n const bb = amount / BB;\n return `${Number.isInteger(bb) ? bb : bb.toFixed(1)} BB`;\n}\n","/**\n * Seat-ring helpers. Pure functions — they contain no game-specific rules.\n */\n\nexport interface NextSeatOptions {\n /** +1 for clockwise, -1 for counter-clockwise. Default +1. */\n dir?: 1 | -1;\n /** Return true to skip a seat (e.g. folded / empty / out). */\n skip?: (index: number) => boolean;\n}\n\n/**\n * Return the index of the next seat on the ring after `current`, skipping any\n * seat matched by `opts.skip`. Wraps around. Returns `current` if every other\n * seat is skipped.\n */\nexport function nextSeatIndex(current: number, seatCount: number, opts: NextSeatOptions = {}): number {\n const n = Math.max(1, Math.floor(seatCount));\n const dir = opts.dir ?? 1;\n const skip = opts.skip;\n const start = ((Math.floor(current) % n) + n) % n;\n\n let i = start;\n for (let step = 0; step < n - 1; step++) {\n i = (i + dir + n) % n;\n if (!skip || !skip(i)) return i;\n }\n return start;\n}\n","import { cn } from \"../cn\";\n\nexport type CardSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nconst SIZE_CLASSES: Record<CardSize, string> = {\n xs: \"w-8 h-11\",\n sm: \"w-10 h-14\",\n md: \"w-16 h-22\",\n lg: \"w-24 h-32\",\n};\n\nconst CORNER_RANK: Record<CardSize, string> = {\n xs: \"text-[11px]\",\n sm: \"text-sm\",\n md: \"text-lg\",\n lg: \"text-2xl\",\n};\nconst CORNER_SUIT: Record<CardSize, string> = {\n xs: \"text-[9px]\",\n sm: \"text-xs\",\n md: \"text-sm\",\n lg: \"text-base\",\n};\nconst CENTER_SUIT: Record<CardSize, string> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n md: \"text-3xl\",\n lg: \"text-5xl\",\n};\n// Small cards (xs/sm) show a single, larger index centered in the card.\nconst SMALL_RANK: Partial<Record<CardSize, string>> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n};\nconst SMALL_SUIT: Partial<Record<CardSize, string>> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n};\nconst BACK_EMBLEM_SIZE: Record<CardSize, number> = {\n xs: 14,\n sm: 18,\n md: 28,\n lg: 42,\n};\n\n/** Parse a compact card code like \"As\", \"10h\", \"kd\" into rank + suit. */\nexport function parseCard(card: string): { rank: string; suit: string } {\n if (!card || card.length < 2) return { rank: \"\", suit: \"\" };\n return { rank: card.slice(0, -1), suit: card.slice(-1) };\n}\n\nexport function getSuitSymbol(suit: string): string {\n switch (suit) {\n case \"s\": return \"♠\";\n case \"h\": return \"♥\";\n case \"d\": return \"♦\";\n case \"c\": return \"♣\";\n default: return suit;\n }\n}\n\nfunction getSuitClass(suit: string): string {\n switch (suit) {\n case \"s\":\n case \"c\":\n return \"text-neutral-900\";\n case \"h\":\n case \"d\":\n return \"text-red-600\";\n default:\n return \"\";\n }\n}\n\nexport interface PlayingCardProps {\n /** Compact card code (\"As\", \"10h\") or null to render a face-down card back. */\n card: string | null;\n /** xs / sm show a single, larger, centered index; md / lg show corner + center. */\n size?: CardSize;\n className?: string;\n}\n\nconst CARD_FACE = \"relative bg-gradient-to-br from-white via-white to-neutral-100 rounded-md border border-neutral-300/90 overflow-hidden shadow-[0_2px_6px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.9)]\";\n\n/**\n * A single playing card. Size is driven by the `size` prop (never viewport\n * breakpoints) so cards scale predictably inside any container. Small sizes\n * (xs / sm) show only a larger upright index, centered in the card.\n */\nexport function PlayingCard({ card, size = \"md\", className }: PlayingCardProps) {\n if (!card) return <CardBack size={size} className={className} />;\n\n const { rank, suit } = parseCard(card);\n const suitSymbol = getSuitSymbol(suit);\n const suitClass = getSuitClass(suit);\n const large = size === \"md\" || size === \"lg\";\n\n // Small cards: single upright rank + suit, centered, larger.\n if (!large) {\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute inset-0 flex flex-col items-center justify-center leading-none\", suitClass)}>\n <span className={cn(SMALL_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(SMALL_SUIT[size], \"mt-[1px]\")}>{suitSymbol}</span>\n </div>\n </div>\n );\n }\n\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute top-[3px] left-[5px] flex flex-col items-center leading-none\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute bottom-[3px] right-[5px] flex flex-col items-center leading-none rotate-180\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute inset-0 flex items-center justify-center opacity-90\", suitClass)}>\n <span className={CENTER_SUIT[size]}>{suitSymbol}</span>\n </div>\n </div>\n );\n}\n\nexport interface CardBackProps {\n size?: CardSize;\n className?: string;\n}\n\nfunction CardBackEmblem({ size }: { size: CardSize }) {\n const s = BACK_EMBLEM_SIZE[size];\n return (\n <svg width={s} height={s} viewBox=\"0 0 40 40\" className=\"drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]\" aria-hidden=\"true\">\n <g fill=\"#f0c04a\" opacity=\"0.92\">\n <ellipse cx=\"20\" cy=\"11\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"20\" cy=\"29\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"11\" cy=\"20\" rx=\"7\" ry=\"4\" />\n <ellipse cx=\"29\" cy=\"20\" rx=\"7\" ry=\"4\" />\n </g>\n <circle cx=\"20\" cy=\"20\" r=\"3.2\" fill=\"#5a0c18\" />\n </svg>\n );\n}\n\n/** The face-down card back (dark red + gold rosette). */\nexport function CardBack({ size = \"md\", className }: CardBackProps) {\n return (\n <div\n className={cn(\n SIZE_CLASSES[size],\n \"relative rounded-md p-[3px] overflow-hidden\",\n \"bg-gradient-to-br from-[#7a1020] via-[#5a0c18] to-[#3f0810]\",\n \"border-2 border-[#c9a24b]/70 shadow-[0_2px_6px_rgba(0,0,0,0.45)]\",\n className,\n )}\n style={{ aspectRatio: \"10/14\" }}\n >\n <div className=\"h-full w-full rounded-[4px] border border-white/20 flex items-center justify-center bg-[repeating-linear-gradient(45deg,rgba(255,255,255,0.09)_0_6px,transparent_6px_12px),repeating-linear-gradient(-45deg,rgba(255,255,255,0.06)_0_6px,transparent_6px_12px)]\">\n <CardBackEmblem size={size} />\n </div>\n </div>\n );\n}\n\nexport interface HoleCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate the cards dealing in, one after another. Default true. */\n deal?: boolean;\n}\n\n/** A row of a player's hole cards. */\nexport function HoleCards({ cards, size = \"lg\", className, deal = true }: HoleCardsProps) {\n return (\n <div className={cn(\"flex gap-2\", className)}>\n {cards.map((card, i) => (\n <div key={i} className={cn(deal && \"animate-deal-in\")} style={deal ? { animationDelay: `${i * 100}ms` } : undefined}>\n <PlayingCard card={card} size={size} />\n </div>\n ))}\n </div>\n );\n}\n\nexport interface CommunityCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate each card with a 3D flip. Default true. */\n animated?: boolean;\n /** Reserve a fixed number of slots so the row width never changes. */\n slots?: number;\n}\n\n/** A row of community cards, each flipping in with a staggered delay. */\nexport function CommunityCards({ cards, size = \"md\", className, animated = true, slots }: CommunityCardsProps) {\n const count = slots ?? cards.length;\n const list = Array.from({ length: count }, (_, i) => cards[i] ?? null);\n return (\n <div className={cn(\"flex gap-1.5\", animated && \"[perspective:800px]\", className)}>\n {list.map((card, i) => (\n <div key={i} className={SIZE_CLASSES[size]} style={{ aspectRatio: \"10/14\" }}>\n {card ? (\n <div\n className={cn(\"h-full w-full\", animated && \"animate-card-flip\")}\n style={animated ? { animationDelay: `${i * 70}ms` } : undefined}\n >\n <PlayingCard card={card} size={size} />\n </div>\n ) : null}\n </div>\n ))}\n </div>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface SeatProps {\n /** Turn/active highlight (generic). Animated by default. */\n isActive?: boolean;\n /** Winner highlight (generic). Animated by default. */\n isWinner?: boolean;\n /** Dimmed, e.g. folded or out (generic). */\n isDimmed?: boolean;\n /** Enable the turn/winner glow animations. Default true. */\n animated?: boolean;\n /** Render a default 👑 in the top-left corner when `isWinner`. Default false. */\n winnerCrown?: boolean;\n /** Optional click handler (e.g. \"sit here\"). */\n onClick?: () => void;\n\n /** Slot: top row inside the frame — name / badges. */\n header?: ReactNode;\n /** Slot: center content — 0..N cards, avatar, anything. */\n children?: ReactNode;\n /** Slot: bottom row inside the frame — stack / status. */\n footer?: ReactNode;\n /** Slot: floating element at the frame's top-right (legacy alias of topRight). */\n badge?: ReactNode;\n /** Slot: overlay centered over the frame. */\n overlay?: ReactNode;\n\n /** Slot: floating content above the frame, centered (e.g. an action bubble). */\n above?: ReactNode;\n /** Slot: content below the frame, centered (e.g. countdown / bet pill). */\n below?: ReactNode;\n /** Slot: floating content at the frame's top-left corner (e.g. a crown). */\n topLeft?: ReactNode;\n /** Slot: floating content at the frame's top-right corner (e.g. a kick button). */\n topRight?: ReactNode;\n /** Slot: floating content centered above the top edge (e.g. D/SB/BB badges). */\n topCenter?: ReactNode;\n\n width?: number;\n height?: number;\n className?: string;\n /** Extra classes for the inner frame, to override visuals. */\n frameClassName?: string;\n /** Inline styles for the inner frame. */\n frameStyle?: CSSProperties;\n}\n\n/**\n * A neutral seat: a frame with generic visual states and named slots. Put\n * whatever you want into `header` / `children` / `footer`, and use\n * `above` / `below` / `topLeft` / `topRight` / `topCenter` / `overlay` for\n * floating bits. It knows nothing about any particular game.\n */\nexport function Seat({\n isActive = false,\n isWinner = false,\n isDimmed = false,\n animated = true,\n winnerCrown = false,\n onClick,\n header,\n children,\n footer,\n badge,\n overlay,\n above,\n below,\n topLeft,\n topRight,\n topCenter,\n width = 160,\n height = 176,\n className,\n frameClassName,\n frameStyle,\n}: SeatProps) {\n const activeClass = animated\n ? \"border-amber-400 animate-turn-glow\"\n : \"border-amber-400 shadow-[0_0_18px_rgba(243,156,18,0.5)]\";\n const winnerClass = animated\n ? \"border-amber-400 animate-winner-glow\"\n : \"border-amber-400 shadow-[0_0_0_3px_rgba(243,156,18,0.9)]\";\n\n const effTopLeft =\n topLeft !== undefined\n ? topLeft\n : isWinner && winnerCrown\n ? <span className=\"text-3xl drop-shadow-[0_2px_4px_rgba(0,0,0,0.6)] animate-bounce\">👑</span>\n : undefined;\n\n return (\n <div className={cn(\"relative flex flex-col items-center\", className)} style={{ width }}>\n {topCenter !== undefined && (\n <div className=\"absolute -top-2 left-1/2 z-20 flex -translate-x-1/2 gap-1\">{topCenter}</div>\n )}\n {effTopLeft !== undefined && <div className=\"absolute -top-3 -left-2 z-40\">{effTopLeft}</div>}\n {topRight !== undefined && <div className=\"absolute -top-2 -right-2 z-40\">{topRight}</div>}\n {above !== undefined && (\n <div className=\"pointer-events-none absolute -top-12 left-1/2 z-30 -translate-x-1/2\">{above}</div>\n )}\n\n <div\n role={onClick ? \"button\" : undefined}\n tabIndex={onClick ? 0 : undefined}\n onClick={onClick}\n onKeyDown={\n onClick\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") onClick();\n }\n : undefined\n }\n style={{ width, height, ...frameStyle }}\n className={cn(\n \"relative flex flex-col items-center justify-between gap-1 rounded-2xl border bg-neutral-900/80 p-2 text-neutral-100 transition\",\n \"border-neutral-700\",\n isActive && activeClass,\n isWinner && winnerClass,\n isDimmed && \"opacity-50\",\n onClick && \"cursor-pointer hover:border-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-400\",\n frameClassName,\n )}\n >\n {header !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between gap-1 text-[11px]\">{header}</div>\n )}\n <div className=\"flex min-h-0 flex-1 items-center justify-center\">{children}</div>\n {footer !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between text-[11px] text-neutral-300\">{footer}</div>\n )}\n\n {overlay !== undefined && (\n <div className=\"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 text-center\">{overlay}</div>\n )}\n </div>\n\n {badge !== undefined && <div className=\"absolute top-0 right-0 z-40\">{badge}</div>}\n {below !== undefined && <div className=\"mt-1 flex flex-col items-center gap-1\">{below}</div>}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../cn\";\nimport { formatBB } from \"../format\";\n\nexport interface PotProps {\n amount: number;\n /** Optional label, e.g. \"Main\" / \"Side\". */\n label?: string;\n /** Formatter for the amount; defaults to Big-Blind formatting. */\n format?: (amount: number) => string;\n /** Animate the amount counting up when it changes. Default true. */\n animate?: boolean;\n className?: string;\n}\n\n/** Smoothly count a number toward its target (presentational only). */\nfunction CountUp({ value, format }: { value: number; format: (n: number) => string }) {\n const [shown, setShown] = useState(value);\n const fromRef = useRef(value);\n\n useEffect(() => {\n const from = fromRef.current;\n const to = value;\n if (from === to) return;\n let raf = 0;\n const start = performance.now();\n const dur = 500;\n const tick = (now: number) => {\n const t = Math.min(1, (now - start) / dur);\n const eased = 1 - Math.pow(1 - t, 3);\n setShown(Math.round(from + (to - from) * eased));\n if (t < 1) raf = requestAnimationFrame(tick);\n else fromRef.current = to;\n };\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [value]);\n\n return <>{format(shown)}</>;\n}\n\n/** A small, standalone pot pill. Optional — the table does not require it. */\nexport function Pot({ amount, label, format = formatBB, animate = true, className }: PotProps) {\n return (\n <div className={cn(\"flex items-center gap-2 font-mono text-sm text-amber-400\", className)}>\n {label && <span className=\"text-neutral-400\">{label}</span>}\n <span className=\"whitespace-nowrap rounded-full border border-amber-400/50 bg-neutral-900/80 px-3 py-1 shadow-inner\">\n {animate ? <CountUp value={amount} format={format} /> : format(amount)}\n </span>\n </div>\n );\n}\n","import { useRef, useLayoutEffect, useState, useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface CardTableProps {\n /** Number of seats (2–10). Default 6. */\n seatCount?: number;\n /** Optional per-seat content. Length should equal `seatCount`. */\n seats?: (ReactNode | null)[];\n /** Alternative to `seats`: render a seat by index. */\n renderSeat?: (index: number, isYou: boolean) => ReactNode;\n /** Which seat belongs to the viewer; rotates that seat to the bottom. */\n yourSeatIndex?: number | null;\n /** Optional content in the middle of the table (cards, pot, anything). */\n center?: ReactNode;\n /** Ambient spotlight + felt shimmer. Default true. */\n ambient?: boolean;\n /** Highlight the table edge (e.g. when it is your turn). Default false. */\n yourTurn?: boolean;\n /** Winning seat indices; when non-empty, confetti falls. */\n winnerSeats?: number[];\n /** Show confetti for winners. Default true. */\n confetti?: boolean;\n /** Seat frame size used for layout. */\n seatWidth?: number;\n seatHeight?: number;\n className?: string;\n}\n\nconst DESIGN_W = 1200;\nconst DESIGN_H = 900;\nconst MARGIN_X = 164;\nconst MARGIN_Y = 196;\nconst TABLE_RX = DESIGN_W / 2 - MARGIN_X;\nconst TABLE_RY = DESIGN_H / 2 - MARGIN_Y;\nconst FELT_GAP = 11;\nconst BET_SCALE = 0.72;\nconst BET_RX = TABLE_RX * BET_SCALE;\nconst BET_RY = TABLE_RY * BET_SCALE;\nconst CONFETTI_COLORS = [\"#f39c12\", \"#e74c3c\", \"#2ecc71\", \"#3498db\", \"#f1c40f\", \"#9b59b6\"];\n\ninterface SeatLayout {\n x: number;\n y: number;\n index: number;\n isYou: boolean;\n}\n\nfunction getEllipsePoint(cx: number, cy: number, rx: number, ry: number, angleDeg: number) {\n const rad = (angleDeg * Math.PI) / 180;\n return { x: cx + rx * Math.cos(rad), y: cy + ry * Math.sin(rad) };\n}\n\nfunction calculateSeatLayouts(\n seatCount: number,\n yourSeatIndex: number | null,\n seatWidth: number,\n seatHeight: number,\n): SeatLayout[] {\n const cx = DESIGN_W / 2;\n const cy = DESIGN_H / 2;\n const ringX = TABLE_RX - FELT_GAP + seatWidth / 2 + 6;\n const ringY = TABLE_RY - FELT_GAP + seatHeight / 2 + 6;\n\n const step = 360 / seatCount;\n const angleOf = (i: number) => -90 + i * step;\n const rotationOffset = yourSeatIndex !== null ? 90 - angleOf(yourSeatIndex) : 0;\n\n const layouts: SeatLayout[] = [];\n for (let i = 0; i < seatCount; i++) {\n const pt = getEllipsePoint(cx, cy, ringX, ringY, angleOf(i) + rotationOffset);\n layouts.push({\n x: pt.x - seatWidth / 2,\n y: pt.y - seatHeight / 2,\n index: i,\n isYou: i === yourSeatIndex,\n });\n }\n return layouts;\n}\n\n/**\n * A responsive, game-agnostic card table. It scales a fixed 1200x900 design\n * onto its container and arranges `seatCount` seats around the felt. Purely\n * presentational: supply seat content and an optional `center` slot.\n */\nexport function CardTable({\n seatCount = 6,\n seats,\n renderSeat,\n yourSeatIndex = null,\n center,\n ambient = true,\n yourTurn = false,\n winnerSeats = [],\n confetti = true,\n seatWidth = 160,\n seatHeight = 176,\n className,\n}: CardTableProps) {\n const count = Math.max(2, Math.min(10, Math.floor(seatCount)));\n const outerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n\n const layouts = useMemo(\n () => calculateSeatLayouts(count, yourSeatIndex, seatWidth, seatHeight),\n [count, yourSeatIndex, seatWidth, seatHeight],\n );\n\n useLayoutEffect(() => {\n const el = outerRef.current;\n if (!el) return;\n const compute = () => {\n const w = el.clientWidth;\n const h = el.clientHeight;\n if (!w || !h) return;\n setScale(Math.min(w / DESIGN_W, h / DESIGN_H));\n };\n compute();\n const ro = new ResizeObserver(compute);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const tableFrameInset = { inset: `${MARGIN_Y}px ${MARGIN_X}px` };\n const feltInset = { inset: `${MARGIN_Y + FELT_GAP}px ${MARGIN_X + FELT_GAP}px` };\n const rimInset = { inset: `${MARGIN_Y + 6}px ${MARGIN_X + 6}px` };\n const ringInset = { inset: `${MARGIN_Y + 2}px ${MARGIN_X + 2}px` };\n const betLineInset = {\n inset: `${MARGIN_Y + (TABLE_RY - BET_RY)}px ${MARGIN_X + (TABLE_RX - BET_RX)}px`,\n };\n\n return (\n <div\n ref={outerRef}\n className={cn(\"relative flex h-full w-full min-h-0 items-center justify-center overflow-hidden\", className)}\n >\n <div style={{ width: DESIGN_W * scale, height: DESIGN_H * scale }}>\n <div\n className=\"relative\"\n style={{ width: DESIGN_W, height: DESIGN_H, transform: `scale(${scale})`, transformOrigin: \"top left\" }}\n >\n <div\n className=\"absolute -inset-6 rounded-[50%] bg-[radial-gradient(closest-side,rgba(240,192,74,0.16),transparent_72%)] blur-3xl\"\n aria-hidden=\"true\"\n />\n\n {/* wooden rail */}\n <div\n className=\"absolute rounded-[50%] bg-gradient-to-b from-[#a5744a] via-[#6b3f22] to-[#38200f] shadow-[0_26px_70px_-12px_rgba(0,0,0,0.85),inset_0_3px_0_rgba(255,255,255,0.20),inset_0_-6px_16px_rgba(0,0,0,0.5)]\"\n style={tableFrameInset}\n aria-hidden=\"true\"\n />\n\n {/* felt */}\n <div\n className=\"absolute rounded-[50%] bg-[radial-gradient(ellipse_at_50%_32%,#23804b,#0f4d2e_55%,#093b22)] shadow-[inset_0_0_90px_rgba(0,0,0,0.55),inset_0_3px_10px_rgba(255,255,255,0.06)]\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n\n {/* ambient: breathing spotlight + felt shimmer */}\n {ambient && (\n <>\n <div\n className=\"absolute animate-spotlight rounded-[50%] bg-[radial-gradient(closest-side,rgba(255,214,120,0.14),transparent_70%)] blur-2xl\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n <div className=\"absolute overflow-hidden rounded-[50%]\" style={feltInset} aria-hidden=\"true\">\n <div className=\"absolute inset-0 animate-felt-shimmer bg-[linear-gradient(115deg,transparent_42%,rgba(255,255,255,0.06)_50%,transparent_58%)] bg-[length:220%_100%]\" />\n </div>\n </>\n )}\n\n {/* betting line */}\n <div\n className=\"absolute rounded-[50%] border-2 border-white/10 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.25)]\"\n style={betLineInset}\n aria-hidden=\"true\"\n />\n\n {/* center suit watermark */}\n <div className=\"absolute flex items-center justify-center pointer-events-none\" style={feltInset} aria-hidden=\"true\">\n <span className=\"text-white/[0.05] text-[110px] font-black tracking-[0.2em] select-none leading-none\">\n ♠♥♦♣\n </span>\n </div>\n\n {/* your-turn edge glow */}\n {yourTurn && (\n <div\n className=\"pointer-events-none absolute z-10 animate-turn-glow rounded-[50%]\"\n style={ringInset}\n aria-hidden=\"true\"\n />\n )}\n\n {/* center slot (optional) */}\n {center !== undefined && (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n {center}\n </div>\n )}\n\n {/* seats */}\n <div className=\"absolute inset-0 z-20\">\n {layouts.map(({ x, y, index, isYou }) => {\n const content = seats ? (seats[index] ?? null) : renderSeat ? renderSeat(index, isYou) : null;\n if (content === null) return null;\n return (\n <div key={index} className=\"absolute\" style={{ left: `${x}px`, top: `${y}px` }}>\n {content}\n </div>\n );\n })}\n </div>\n\n {/* winner confetti */}\n {confetti && winnerSeats.length > 0 && (\n <div className=\"pointer-events-none absolute inset-0 z-30 overflow-hidden\" aria-hidden=\"true\">\n {Array.from({ length: 16 }).map((_, i) => (\n <span\n key={i}\n className=\"absolute top-[34%] animate-confetti rounded-[2px]\"\n style={{\n left: `${6 + i * 5.6}%`,\n width: 6,\n height: 11,\n backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],\n animationDelay: `${(i % 8) * 90}ms`,\n }}\n />\n ))}\n </div>\n )}\n\n {/* outer rim highlight */}\n <div className=\"absolute rounded-[50%] border border-white/10 pointer-events-none\" style={rimInset} aria-hidden=\"true\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cn.ts","../src/format.ts","../src/seat.ts","../src/components/PlayingCard.tsx","../src/components/Seat.tsx","../src/components/Pot.tsx","../src/components/CardTable.tsx"],"sourcesContent":["export { cn } from \"./cn\";\nexport { BB, formatBB } from \"./format\";\nexport { nextSeatIndex, type NextSeatOptions } from \"./seat\";\n\nexport {\n PlayingCard,\n CardBack,\n HoleCards,\n CommunityCards,\n parseCard,\n getSuitSymbol,\n type PlayingCardProps,\n type CardBackProps,\n type HoleCardsProps,\n type CommunityCardsProps,\n type CardSize,\n} from \"./components/PlayingCard\";\n\nexport { Seat, type SeatProps } from \"./components/Seat\";\nexport { Pot, type PotProps } from \"./components/Pot\";\nexport { CardTable, type CardTableProps } from \"./components/CardTable\";\n\n// Back-compat alias (the table is now game-agnostic).\nexport { CardTable as PokerTable, type CardTableProps as PokerTableProps } from \"./components/CardTable\";\n","import { twMerge } from \"tailwind-merge\";\n\n/** Join class names and resolve conflicting Tailwind utilities (last wins). */\nexport function cn(...parts: Array<string | false | null | undefined>): string {\n return twMerge(parts.filter(Boolean).join(\" \"));\n}\n","/** Big Blind value used for stack display. */\nexport const BB = 20;\n\n/** Format a chip amount as a Big-Blind string, e.g. 1234 -> \"61.7 BB\". */\nexport function formatBB(amount: number): string {\n const bb = amount / BB;\n return `${Number.isInteger(bb) ? bb : bb.toFixed(1)} BB`;\n}\n","/**\n * Seat-ring helpers. Pure functions — they contain no game-specific rules.\n */\n\nexport interface NextSeatOptions {\n /** +1 for clockwise, -1 for counter-clockwise. Default +1. */\n dir?: 1 | -1;\n /** Return true to skip a seat (e.g. folded / empty / out). */\n skip?: (index: number) => boolean;\n}\n\n/**\n * Return the index of the next seat on the ring after `current`, skipping any\n * seat matched by `opts.skip`. Wraps around. Returns `current` if every other\n * seat is skipped.\n */\nexport function nextSeatIndex(current: number, seatCount: number, opts: NextSeatOptions = {}): number {\n const n = Math.max(1, Math.floor(seatCount));\n const dir = opts.dir ?? 1;\n const skip = opts.skip;\n const start = ((Math.floor(current) % n) + n) % n;\n\n let i = start;\n for (let step = 0; step < n - 1; step++) {\n i = (i + dir + n) % n;\n if (!skip || !skip(i)) return i;\n }\n return start;\n}\n","import { cn } from \"../cn\";\n\nexport type CardSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nconst SIZE_CLASSES: Record<CardSize, string> = {\n xs: \"w-8 h-11\",\n sm: \"w-10 h-14\",\n md: \"w-16 h-22\",\n lg: \"w-24 h-32\",\n};\n\nconst CORNER_RANK: Record<CardSize, string> = {\n xs: \"text-[11px]\",\n sm: \"text-sm\",\n md: \"text-lg\",\n lg: \"text-2xl\",\n};\nconst CORNER_SUIT: Record<CardSize, string> = {\n xs: \"text-[9px]\",\n sm: \"text-xs\",\n md: \"text-sm\",\n lg: \"text-base\",\n};\nconst CENTER_SUIT: Record<CardSize, string> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n md: \"text-3xl\",\n lg: \"text-5xl\",\n};\n// Small cards (xs/sm) show a single, larger index centered in the card.\nconst SMALL_RANK: Partial<Record<CardSize, string>> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n};\nconst SMALL_SUIT: Partial<Record<CardSize, string>> = {\n xs: \"text-lg\",\n sm: \"text-2xl\",\n};\nconst BACK_EMBLEM_SIZE: Record<CardSize, number> = {\n xs: 14,\n sm: 18,\n md: 28,\n lg: 42,\n};\n\n/** Parse a compact card code like \"As\", \"10h\", \"kd\" into rank + suit. */\nexport function parseCard(card: string): { rank: string; suit: string } {\n if (!card || card.length < 2) return { rank: \"\", suit: \"\" };\n return { rank: card.slice(0, -1), suit: card.slice(-1) };\n}\n\nexport function getSuitSymbol(suit: string): string {\n switch (suit) {\n case \"s\": return \"♠\";\n case \"h\": return \"♥\";\n case \"d\": return \"♦\";\n case \"c\": return \"♣\";\n default: return suit;\n }\n}\n\nfunction getSuitClass(suit: string): string {\n switch (suit) {\n case \"s\":\n case \"c\":\n return \"text-neutral-900\";\n case \"h\":\n case \"d\":\n return \"text-red-600\";\n default:\n return \"\";\n }\n}\n\nexport interface PlayingCardProps {\n /** Compact card code (\"As\", \"10h\") or null to render a face-down card back. */\n card: string | null;\n /** xs / sm show a single, larger, centered index; md / lg show corner + center. */\n size?: CardSize;\n className?: string;\n}\n\nconst CARD_FACE = \"relative bg-gradient-to-br from-white via-white to-neutral-100 rounded-md border border-neutral-300/90 overflow-hidden shadow-[0_2px_6px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.9)]\";\n\n/**\n * A single playing card. Size is driven by the `size` prop (never viewport\n * breakpoints) so cards scale predictably inside any container. Small sizes\n * (xs / sm) show only a larger upright index, centered in the card.\n */\nexport function PlayingCard({ card, size = \"md\", className }: PlayingCardProps) {\n if (!card) return <CardBack size={size} className={className} />;\n\n const { rank, suit } = parseCard(card);\n const suitSymbol = getSuitSymbol(suit);\n const suitClass = getSuitClass(suit);\n const large = size === \"md\" || size === \"lg\";\n\n // Small cards: rank centered in the top half, suit centered in the bottom half.\n if (!large) {\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute inset-0 flex flex-col leading-none\", suitClass)}>\n <div className=\"flex flex-1 items-center justify-center\">\n <span className={cn(SMALL_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n </div>\n <div className=\"flex flex-1 items-center justify-center\">\n <span className={SMALL_SUIT[size]}>{suitSymbol}</span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute top-[3px] left-[5px] flex flex-col items-center leading-none\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute bottom-[3px] right-[5px] flex flex-col items-center leading-none rotate-180\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute inset-0 flex items-center justify-center opacity-90\", suitClass)}>\n <span className={CENTER_SUIT[size]}>{suitSymbol}</span>\n </div>\n </div>\n );\n}\n\nexport interface CardBackProps {\n size?: CardSize;\n className?: string;\n}\n\nfunction CardBackEmblem({ size }: { size: CardSize }) {\n const s = BACK_EMBLEM_SIZE[size];\n return (\n <svg width={s} height={s} viewBox=\"0 0 40 40\" className=\"drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]\" aria-hidden=\"true\">\n <g fill=\"#f0c04a\" opacity=\"0.92\">\n <ellipse cx=\"20\" cy=\"11\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"20\" cy=\"29\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"11\" cy=\"20\" rx=\"7\" ry=\"4\" />\n <ellipse cx=\"29\" cy=\"20\" rx=\"7\" ry=\"4\" />\n </g>\n <circle cx=\"20\" cy=\"20\" r=\"3.2\" fill=\"#5a0c18\" />\n </svg>\n );\n}\n\n/** The face-down card back (dark red + gold rosette). */\nexport function CardBack({ size = \"md\", className }: CardBackProps) {\n return (\n <div\n className={cn(\n SIZE_CLASSES[size],\n \"relative rounded-md p-[3px] overflow-hidden\",\n \"bg-gradient-to-br from-[#7a1020] via-[#5a0c18] to-[#3f0810]\",\n \"border-2 border-[#c9a24b]/70 shadow-[0_2px_6px_rgba(0,0,0,0.45)]\",\n className,\n )}\n style={{ aspectRatio: \"10/14\" }}\n >\n <div className=\"h-full w-full rounded-[4px] border border-white/20 flex items-center justify-center bg-[repeating-linear-gradient(45deg,rgba(255,255,255,0.09)_0_6px,transparent_6px_12px),repeating-linear-gradient(-45deg,rgba(255,255,255,0.06)_0_6px,transparent_6px_12px)]\">\n <CardBackEmblem size={size} />\n </div>\n </div>\n );\n}\n\nexport interface HoleCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate the cards dealing in, one after another. Default true. */\n deal?: boolean;\n}\n\n/** A row of a player's hole cards. */\nexport function HoleCards({ cards, size = \"lg\", className, deal = true }: HoleCardsProps) {\n return (\n <div className={cn(\"flex gap-2\", className)}>\n {cards.map((card, i) => (\n <div key={i} className={cn(deal && \"animate-deal-in\")} style={deal ? { animationDelay: `${i * 100}ms` } : undefined}>\n <PlayingCard card={card} size={size} />\n </div>\n ))}\n </div>\n );\n}\n\nexport interface CommunityCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate each card with a 3D flip. Default true. */\n animated?: boolean;\n /** Reserve a fixed number of slots so the row width never changes. */\n slots?: number;\n}\n\n/** A row of community cards, each flipping in with a staggered delay. */\nexport function CommunityCards({ cards, size = \"md\", className, animated = true, slots }: CommunityCardsProps) {\n const count = slots ?? cards.length;\n const list = Array.from({ length: count }, (_, i) => cards[i] ?? null);\n return (\n <div className={cn(\"flex gap-1.5\", animated && \"[perspective:800px]\", className)}>\n {list.map((card, i) => (\n <div key={i} className={SIZE_CLASSES[size]} style={{ aspectRatio: \"10/14\" }}>\n {card ? (\n <div\n className={cn(\"h-full w-full\", animated && \"animate-card-flip\")}\n style={animated ? { animationDelay: `${i * 70}ms` } : undefined}\n >\n <PlayingCard card={card} size={size} />\n </div>\n ) : null}\n </div>\n ))}\n </div>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface SeatProps {\n /** Turn/active highlight (generic). Animated by default. */\n isActive?: boolean;\n /** Winner highlight (generic). Animated by default. */\n isWinner?: boolean;\n /** Dimmed, e.g. folded or out (generic). */\n isDimmed?: boolean;\n /** Enable the turn/winner glow animations. Default true. */\n animated?: boolean;\n /** Render a default 👑 in the top-left corner when `isWinner`. Default false. */\n winnerCrown?: boolean;\n /** Optional click handler (e.g. \"sit here\"). */\n onClick?: () => void;\n\n /** Slot: top row inside the frame — name / badges. */\n header?: ReactNode;\n /** Slot: center content — 0..N cards, avatar, anything. */\n children?: ReactNode;\n /** Slot: bottom row inside the frame — stack / status. */\n footer?: ReactNode;\n /** Slot: floating element at the frame's top-right (legacy alias of topRight). */\n badge?: ReactNode;\n /** Slot: overlay centered over the frame. */\n overlay?: ReactNode;\n\n /** Slot: floating content above the frame, centered (e.g. an action bubble). */\n above?: ReactNode;\n /** Slot: content below the frame, centered (e.g. countdown / bet pill). */\n below?: ReactNode;\n /** Slot: floating content at the frame's top-left corner (e.g. a crown). */\n topLeft?: ReactNode;\n /** Slot: floating content at the frame's top-right corner (e.g. a kick button). */\n topRight?: ReactNode;\n /** Slot: floating content centered above the top edge (e.g. D/SB/BB badges). */\n topCenter?: ReactNode;\n\n width?: number;\n height?: number;\n className?: string;\n /** Extra classes for the inner frame, to override visuals. */\n frameClassName?: string;\n /** Inline styles for the inner frame. */\n frameStyle?: CSSProperties;\n}\n\n/**\n * A neutral seat: a frame with generic visual states and named slots. Put\n * whatever you want into `header` / `children` / `footer`, and use\n * `above` / `below` / `topLeft` / `topRight` / `topCenter` / `overlay` for\n * floating bits. It knows nothing about any particular game.\n */\nexport function Seat({\n isActive = false,\n isWinner = false,\n isDimmed = false,\n animated = true,\n winnerCrown = false,\n onClick,\n header,\n children,\n footer,\n badge,\n overlay,\n above,\n below,\n topLeft,\n topRight,\n topCenter,\n width = 160,\n height = 176,\n className,\n frameClassName,\n frameStyle,\n}: SeatProps) {\n const activeClass = animated\n ? \"border-amber-400 animate-turn-glow\"\n : \"border-amber-400 shadow-[0_0_18px_rgba(243,156,18,0.5)]\";\n const winnerClass = animated\n ? \"border-amber-400 animate-winner-glow\"\n : \"border-amber-400 shadow-[0_0_0_3px_rgba(243,156,18,0.9)]\";\n\n const effTopLeft =\n topLeft !== undefined\n ? topLeft\n : isWinner && winnerCrown\n ? <span className=\"text-3xl drop-shadow-[0_2px_4px_rgba(0,0,0,0.6)] animate-bounce\">👑</span>\n : undefined;\n\n return (\n <div className={cn(\"relative flex flex-col items-center\", className)} style={{ width }}>\n {topCenter !== undefined && (\n <div className=\"absolute -top-2 left-1/2 z-20 flex -translate-x-1/2 gap-1\">{topCenter}</div>\n )}\n {effTopLeft !== undefined && <div className=\"absolute -top-3 -left-2 z-40\">{effTopLeft}</div>}\n {topRight !== undefined && <div className=\"absolute -top-2 -right-2 z-40\">{topRight}</div>}\n {above !== undefined && (\n <div className=\"pointer-events-none absolute -top-12 left-1/2 z-30 -translate-x-1/2\">{above}</div>\n )}\n\n <div\n role={onClick ? \"button\" : undefined}\n tabIndex={onClick ? 0 : undefined}\n onClick={onClick}\n onKeyDown={\n onClick\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") onClick();\n }\n : undefined\n }\n style={{ width, height, ...frameStyle }}\n className={cn(\n \"relative flex flex-col items-center justify-between gap-1 rounded-2xl border bg-neutral-900/80 p-2 text-neutral-100 transition\",\n \"border-neutral-700\",\n isActive && activeClass,\n isWinner && winnerClass,\n isDimmed && \"opacity-50\",\n onClick && \"cursor-pointer hover:border-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-400\",\n frameClassName,\n )}\n >\n {header !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between gap-1 text-[11px]\">{header}</div>\n )}\n <div className=\"flex min-h-0 flex-1 items-center justify-center\">{children}</div>\n {footer !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between text-[11px] text-neutral-300\">{footer}</div>\n )}\n\n {overlay !== undefined && (\n <div className=\"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 text-center\">{overlay}</div>\n )}\n </div>\n\n {badge !== undefined && <div className=\"absolute top-0 right-0 z-40\">{badge}</div>}\n {below !== undefined && <div className=\"mt-1 flex flex-col items-center gap-1\">{below}</div>}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../cn\";\nimport { formatBB } from \"../format\";\n\nexport interface PotProps {\n amount: number;\n /** Optional label, e.g. \"Main\" / \"Side\". */\n label?: string;\n /** Formatter for the amount; defaults to Big-Blind formatting. */\n format?: (amount: number) => string;\n /** Animate the amount counting up when it changes. Default true. */\n animate?: boolean;\n className?: string;\n}\n\n/** Smoothly count a number toward its target (presentational only). */\nfunction CountUp({ value, format }: { value: number; format: (n: number) => string }) {\n const [shown, setShown] = useState(value);\n const fromRef = useRef(value);\n\n useEffect(() => {\n const from = fromRef.current;\n const to = value;\n if (from === to) return;\n let raf = 0;\n const start = performance.now();\n const dur = 500;\n const tick = (now: number) => {\n const t = Math.min(1, (now - start) / dur);\n const eased = 1 - Math.pow(1 - t, 3);\n setShown(Math.round(from + (to - from) * eased));\n if (t < 1) raf = requestAnimationFrame(tick);\n else fromRef.current = to;\n };\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [value]);\n\n return <>{format(shown)}</>;\n}\n\n/** A small, standalone pot pill. Optional — the table does not require it. */\nexport function Pot({ amount, label, format = formatBB, animate = true, className }: PotProps) {\n return (\n <div className={cn(\"flex items-center gap-2 font-mono text-sm text-amber-400\", className)}>\n {label && <span className=\"text-neutral-400\">{label}</span>}\n <span className=\"whitespace-nowrap rounded-full border border-amber-400/50 bg-neutral-900/80 px-3 py-1 shadow-inner\">\n {animate ? <CountUp value={amount} format={format} /> : format(amount)}\n </span>\n </div>\n );\n}\n","import { useRef, useLayoutEffect, useState, useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface CardTableProps {\n /** Number of seats (2–10). Default 6. */\n seatCount?: number;\n /** Optional per-seat content. Length should equal `seatCount`. */\n seats?: (ReactNode | null)[];\n /** Alternative to `seats`: render a seat by index. */\n renderSeat?: (index: number, isYou: boolean) => ReactNode;\n /** Which seat belongs to the viewer; rotates that seat to the bottom. */\n yourSeatIndex?: number | null;\n /** Optional content in the middle of the table (cards, pot, anything). */\n center?: ReactNode;\n /** Ambient spotlight + felt shimmer. Default true. */\n ambient?: boolean;\n /** Highlight the table edge (e.g. when it is your turn). Default false. */\n yourTurn?: boolean;\n /** Winning seat indices; when non-empty, confetti falls. */\n winnerSeats?: number[];\n /** Show confetti for winners. Default true. */\n confetti?: boolean;\n /** Seat frame size used for layout. */\n seatWidth?: number;\n seatHeight?: number;\n className?: string;\n}\n\nconst DESIGN_W = 1200;\nconst DESIGN_H = 900;\nconst MARGIN_X = 164;\nconst MARGIN_Y = 196;\nconst TABLE_RX = DESIGN_W / 2 - MARGIN_X;\nconst TABLE_RY = DESIGN_H / 2 - MARGIN_Y;\nconst FELT_GAP = 11;\nconst BET_SCALE = 0.72;\nconst BET_RX = TABLE_RX * BET_SCALE;\nconst BET_RY = TABLE_RY * BET_SCALE;\nconst CONFETTI_COLORS = [\"#f39c12\", \"#e74c3c\", \"#2ecc71\", \"#3498db\", \"#f1c40f\", \"#9b59b6\"];\n\ninterface SeatLayout {\n x: number;\n y: number;\n index: number;\n isYou: boolean;\n}\n\nfunction getEllipsePoint(cx: number, cy: number, rx: number, ry: number, angleDeg: number) {\n const rad = (angleDeg * Math.PI) / 180;\n return { x: cx + rx * Math.cos(rad), y: cy + ry * Math.sin(rad) };\n}\n\nfunction calculateSeatLayouts(\n seatCount: number,\n yourSeatIndex: number | null,\n seatWidth: number,\n seatHeight: number,\n): SeatLayout[] {\n const cx = DESIGN_W / 2;\n const cy = DESIGN_H / 2;\n const ringX = TABLE_RX - FELT_GAP + seatWidth / 2 + 6;\n const ringY = TABLE_RY - FELT_GAP + seatHeight / 2 + 6;\n\n const step = 360 / seatCount;\n const angleOf = (i: number) => -90 + i * step;\n const rotationOffset = yourSeatIndex !== null ? 90 - angleOf(yourSeatIndex) : 0;\n\n const layouts: SeatLayout[] = [];\n for (let i = 0; i < seatCount; i++) {\n const pt = getEllipsePoint(cx, cy, ringX, ringY, angleOf(i) + rotationOffset);\n layouts.push({\n x: pt.x - seatWidth / 2,\n y: pt.y - seatHeight / 2,\n index: i,\n isYou: i === yourSeatIndex,\n });\n }\n return layouts;\n}\n\n/**\n * A responsive, game-agnostic card table. It scales a fixed 1200x900 design\n * onto its container and arranges `seatCount` seats around the felt. Purely\n * presentational: supply seat content and an optional `center` slot.\n */\nexport function CardTable({\n seatCount = 6,\n seats,\n renderSeat,\n yourSeatIndex = null,\n center,\n ambient = true,\n yourTurn = false,\n winnerSeats = [],\n confetti = true,\n seatWidth = 160,\n seatHeight = 176,\n className,\n}: CardTableProps) {\n const count = Math.max(2, Math.min(10, Math.floor(seatCount)));\n const outerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n\n const layouts = useMemo(\n () => calculateSeatLayouts(count, yourSeatIndex, seatWidth, seatHeight),\n [count, yourSeatIndex, seatWidth, seatHeight],\n );\n\n useLayoutEffect(() => {\n const el = outerRef.current;\n if (!el) return;\n const compute = () => {\n const w = el.clientWidth;\n const h = el.clientHeight;\n if (!w || !h) return;\n setScale(Math.min(w / DESIGN_W, h / DESIGN_H));\n };\n compute();\n const ro = new ResizeObserver(compute);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const tableFrameInset = { inset: `${MARGIN_Y}px ${MARGIN_X}px` };\n const feltInset = { inset: `${MARGIN_Y + FELT_GAP}px ${MARGIN_X + FELT_GAP}px` };\n const rimInset = { inset: `${MARGIN_Y + 6}px ${MARGIN_X + 6}px` };\n const ringInset = { inset: `${MARGIN_Y + 2}px ${MARGIN_X + 2}px` };\n const betLineInset = {\n inset: `${MARGIN_Y + (TABLE_RY - BET_RY)}px ${MARGIN_X + (TABLE_RX - BET_RX)}px`,\n };\n\n return (\n <div\n ref={outerRef}\n className={cn(\"relative flex h-full w-full min-h-0 items-center justify-center overflow-hidden\", className)}\n >\n <div style={{ width: DESIGN_W * scale, height: DESIGN_H * scale }}>\n <div\n className=\"relative\"\n style={{ width: DESIGN_W, height: DESIGN_H, transform: `scale(${scale})`, transformOrigin: \"top left\" }}\n >\n <div\n className=\"absolute -inset-6 rounded-[50%] bg-[radial-gradient(closest-side,rgba(240,192,74,0.16),transparent_72%)] blur-3xl\"\n aria-hidden=\"true\"\n />\n\n {/* wooden rail */}\n <div\n className=\"absolute rounded-[50%] bg-gradient-to-b from-[#a5744a] via-[#6b3f22] to-[#38200f] shadow-[0_26px_70px_-12px_rgba(0,0,0,0.85),inset_0_3px_0_rgba(255,255,255,0.20),inset_0_-6px_16px_rgba(0,0,0,0.5)]\"\n style={tableFrameInset}\n aria-hidden=\"true\"\n />\n\n {/* felt */}\n <div\n className=\"absolute rounded-[50%] bg-[radial-gradient(ellipse_at_50%_32%,#23804b,#0f4d2e_55%,#093b22)] shadow-[inset_0_0_90px_rgba(0,0,0,0.55),inset_0_3px_10px_rgba(255,255,255,0.06)]\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n\n {/* ambient: breathing spotlight + felt shimmer */}\n {ambient && (\n <>\n <div\n className=\"absolute animate-spotlight rounded-[50%] bg-[radial-gradient(closest-side,rgba(255,214,120,0.14),transparent_70%)] blur-2xl\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n <div className=\"absolute overflow-hidden rounded-[50%]\" style={feltInset} aria-hidden=\"true\">\n <div className=\"absolute inset-0 animate-felt-shimmer bg-[linear-gradient(115deg,transparent_42%,rgba(255,255,255,0.06)_50%,transparent_58%)] bg-[length:220%_100%]\" />\n </div>\n </>\n )}\n\n {/* betting line */}\n <div\n className=\"absolute rounded-[50%] border-2 border-white/10 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.25)]\"\n style={betLineInset}\n aria-hidden=\"true\"\n />\n\n {/* center suit watermark */}\n <div className=\"absolute flex items-center justify-center pointer-events-none\" style={feltInset} aria-hidden=\"true\">\n <span className=\"text-white/[0.05] text-[110px] font-black tracking-[0.2em] select-none leading-none\">\n ♠♥♦♣\n </span>\n </div>\n\n {/* your-turn edge glow */}\n {yourTurn && (\n <div\n className=\"pointer-events-none absolute z-10 animate-turn-glow rounded-[50%]\"\n style={ringInset}\n aria-hidden=\"true\"\n />\n )}\n\n {/* center slot (optional) */}\n {center !== undefined && (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n {center}\n </div>\n )}\n\n {/* seats */}\n <div className=\"absolute inset-0 z-20\">\n {layouts.map(({ x, y, index, isYou }) => {\n const content = seats ? (seats[index] ?? null) : renderSeat ? renderSeat(index, isYou) : null;\n if (content === null) return null;\n return (\n <div key={index} className=\"absolute\" style={{ left: `${x}px`, top: `${y}px` }}>\n {content}\n </div>\n );\n })}\n </div>\n\n {/* winner confetti */}\n {confetti && winnerSeats.length > 0 && (\n <div className=\"pointer-events-none absolute inset-0 z-30 overflow-hidden\" aria-hidden=\"true\">\n {Array.from({ length: 44 }).map((_, i) => {\n const w = 5 + (i % 4) * 2;\n const h = 9 + (i % 3) * 4;\n const dur = 1.7 + (i % 5) * 0.28;\n const delay = (i % 12) * 0.14;\n return (\n <span\n key={i}\n className=\"absolute top-[26%] animate-confetti rounded-[2px]\"\n style={{\n left: `${(i * 37) % 96}%`,\n width: w,\n height: h,\n backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],\n animationDuration: `${dur}s`,\n animationDelay: `${delay}s`,\n animationIterationCount: \"infinite\",\n }}\n />\n );\n })}\n </div>\n )}\n\n {/* outer rim highlight */}\n <div className=\"absolute rounded-[50%] border border-white/10 pointer-events-none\" style={rimInset} aria-hidden=\"true\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.js CHANGED
@@ -58,8 +58,8 @@ var SMALL_RANK = {
58
58
  sm: "text-xl"
59
59
  };
60
60
  var SMALL_SUIT = {
61
- xs: "text-base",
62
- sm: "text-xl"
61
+ xs: "text-lg",
62
+ sm: "text-2xl"
63
63
  };
64
64
  var BACK_EMBLEM_SIZE = {
65
65
  xs: 14,
@@ -105,9 +105,9 @@ function PlayingCard({ card, size = "md", className }) {
105
105
  const suitClass = getSuitClass(suit);
106
106
  const large = size === "md" || size === "lg";
107
107
  if (!large) {
108
- return /* @__PURE__ */ jsx("div", { className: cn(SIZE_CLASSES[size], CARD_FACE, className), style: { aspectRatio: "10/14" }, children: /* @__PURE__ */ jsxs("div", { className: cn("absolute inset-0 flex flex-col items-center justify-center leading-none", suitClass), children: [
109
- /* @__PURE__ */ jsx("span", { className: cn(SMALL_RANK[size], "font-bold tracking-tight"), children: rank }),
110
- /* @__PURE__ */ jsx("span", { className: cn(SMALL_SUIT[size], "mt-[1px]"), children: suitSymbol })
108
+ return /* @__PURE__ */ jsx("div", { className: cn(SIZE_CLASSES[size], CARD_FACE, className), style: { aspectRatio: "10/14" }, children: /* @__PURE__ */ jsxs("div", { className: cn("absolute inset-0 flex flex-col leading-none", suitClass), children: [
109
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: cn(SMALL_RANK[size], "font-bold tracking-tight"), children: rank }) }),
110
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: SMALL_SUIT[size], children: suitSymbol }) })
111
111
  ] }) });
112
112
  }
113
113
  return /* @__PURE__ */ jsxs("div", { className: cn(SIZE_CLASSES[size], CARD_FACE, className), style: { aspectRatio: "10/14" }, children: [
@@ -411,20 +411,28 @@ function CardTable({
411
411
  if (content === null) return null;
412
412
  return /* @__PURE__ */ jsx4("div", { className: "absolute", style: { left: `${x}px`, top: `${y}px` }, children: content }, index);
413
413
  }) }),
414
- confetti && winnerSeats.length > 0 && /* @__PURE__ */ jsx4("div", { className: "pointer-events-none absolute inset-0 z-30 overflow-hidden", "aria-hidden": "true", children: Array.from({ length: 16 }).map((_, i) => /* @__PURE__ */ jsx4(
415
- "span",
416
- {
417
- className: "absolute top-[34%] animate-confetti rounded-[2px]",
418
- style: {
419
- left: `${6 + i * 5.6}%`,
420
- width: 6,
421
- height: 11,
422
- backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
423
- animationDelay: `${i % 8 * 90}ms`
424
- }
425
- },
426
- i
427
- )) }),
414
+ confetti && winnerSeats.length > 0 && /* @__PURE__ */ jsx4("div", { className: "pointer-events-none absolute inset-0 z-30 overflow-hidden", "aria-hidden": "true", children: Array.from({ length: 44 }).map((_, i) => {
415
+ const w = 5 + i % 4 * 2;
416
+ const h = 9 + i % 3 * 4;
417
+ const dur = 1.7 + i % 5 * 0.28;
418
+ const delay = i % 12 * 0.14;
419
+ return /* @__PURE__ */ jsx4(
420
+ "span",
421
+ {
422
+ className: "absolute top-[26%] animate-confetti rounded-[2px]",
423
+ style: {
424
+ left: `${i * 37 % 96}%`,
425
+ width: w,
426
+ height: h,
427
+ backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
428
+ animationDuration: `${dur}s`,
429
+ animationDelay: `${delay}s`,
430
+ animationIterationCount: "infinite"
431
+ }
432
+ },
433
+ i
434
+ );
435
+ }) }),
428
436
  /* @__PURE__ */ jsx4("div", { className: "absolute rounded-[50%] border border-white/10 pointer-events-none", style: rimInset, "aria-hidden": "true" })
429
437
  ]
430
438
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cn.ts","../src/format.ts","../src/seat.ts","../src/components/PlayingCard.tsx","../src/components/Seat.tsx","../src/components/Pot.tsx","../src/components/CardTable.tsx"],"sourcesContent":["import { twMerge } from \"tailwind-merge\";\n\n/** Join class names and resolve conflicting Tailwind utilities (last wins). */\nexport function cn(...parts: Array<string | false | null | undefined>): string {\n return twMerge(parts.filter(Boolean).join(\" \"));\n}\n","/** Big Blind value used for stack display. */\nexport const BB = 20;\n\n/** Format a chip amount as a Big-Blind string, e.g. 1234 -> \"61.7 BB\". */\nexport function formatBB(amount: number): string {\n const bb = amount / BB;\n return `${Number.isInteger(bb) ? bb : bb.toFixed(1)} BB`;\n}\n","/**\n * Seat-ring helpers. Pure functions — they contain no game-specific rules.\n */\n\nexport interface NextSeatOptions {\n /** +1 for clockwise, -1 for counter-clockwise. Default +1. */\n dir?: 1 | -1;\n /** Return true to skip a seat (e.g. folded / empty / out). */\n skip?: (index: number) => boolean;\n}\n\n/**\n * Return the index of the next seat on the ring after `current`, skipping any\n * seat matched by `opts.skip`. Wraps around. Returns `current` if every other\n * seat is skipped.\n */\nexport function nextSeatIndex(current: number, seatCount: number, opts: NextSeatOptions = {}): number {\n const n = Math.max(1, Math.floor(seatCount));\n const dir = opts.dir ?? 1;\n const skip = opts.skip;\n const start = ((Math.floor(current) % n) + n) % n;\n\n let i = start;\n for (let step = 0; step < n - 1; step++) {\n i = (i + dir + n) % n;\n if (!skip || !skip(i)) return i;\n }\n return start;\n}\n","import { cn } from \"../cn\";\n\nexport type CardSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nconst SIZE_CLASSES: Record<CardSize, string> = {\n xs: \"w-8 h-11\",\n sm: \"w-10 h-14\",\n md: \"w-16 h-22\",\n lg: \"w-24 h-32\",\n};\n\nconst CORNER_RANK: Record<CardSize, string> = {\n xs: \"text-[11px]\",\n sm: \"text-sm\",\n md: \"text-lg\",\n lg: \"text-2xl\",\n};\nconst CORNER_SUIT: Record<CardSize, string> = {\n xs: \"text-[9px]\",\n sm: \"text-xs\",\n md: \"text-sm\",\n lg: \"text-base\",\n};\nconst CENTER_SUIT: Record<CardSize, string> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n md: \"text-3xl\",\n lg: \"text-5xl\",\n};\n// Small cards (xs/sm) show a single, larger index centered in the card.\nconst SMALL_RANK: Partial<Record<CardSize, string>> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n};\nconst SMALL_SUIT: Partial<Record<CardSize, string>> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n};\nconst BACK_EMBLEM_SIZE: Record<CardSize, number> = {\n xs: 14,\n sm: 18,\n md: 28,\n lg: 42,\n};\n\n/** Parse a compact card code like \"As\", \"10h\", \"kd\" into rank + suit. */\nexport function parseCard(card: string): { rank: string; suit: string } {\n if (!card || card.length < 2) return { rank: \"\", suit: \"\" };\n return { rank: card.slice(0, -1), suit: card.slice(-1) };\n}\n\nexport function getSuitSymbol(suit: string): string {\n switch (suit) {\n case \"s\": return \"♠\";\n case \"h\": return \"♥\";\n case \"d\": return \"♦\";\n case \"c\": return \"♣\";\n default: return suit;\n }\n}\n\nfunction getSuitClass(suit: string): string {\n switch (suit) {\n case \"s\":\n case \"c\":\n return \"text-neutral-900\";\n case \"h\":\n case \"d\":\n return \"text-red-600\";\n default:\n return \"\";\n }\n}\n\nexport interface PlayingCardProps {\n /** Compact card code (\"As\", \"10h\") or null to render a face-down card back. */\n card: string | null;\n /** xs / sm show a single, larger, centered index; md / lg show corner + center. */\n size?: CardSize;\n className?: string;\n}\n\nconst CARD_FACE = \"relative bg-gradient-to-br from-white via-white to-neutral-100 rounded-md border border-neutral-300/90 overflow-hidden shadow-[0_2px_6px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.9)]\";\n\n/**\n * A single playing card. Size is driven by the `size` prop (never viewport\n * breakpoints) so cards scale predictably inside any container. Small sizes\n * (xs / sm) show only a larger upright index, centered in the card.\n */\nexport function PlayingCard({ card, size = \"md\", className }: PlayingCardProps) {\n if (!card) return <CardBack size={size} className={className} />;\n\n const { rank, suit } = parseCard(card);\n const suitSymbol = getSuitSymbol(suit);\n const suitClass = getSuitClass(suit);\n const large = size === \"md\" || size === \"lg\";\n\n // Small cards: single upright rank + suit, centered, larger.\n if (!large) {\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute inset-0 flex flex-col items-center justify-center leading-none\", suitClass)}>\n <span className={cn(SMALL_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(SMALL_SUIT[size], \"mt-[1px]\")}>{suitSymbol}</span>\n </div>\n </div>\n );\n }\n\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute top-[3px] left-[5px] flex flex-col items-center leading-none\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute bottom-[3px] right-[5px] flex flex-col items-center leading-none rotate-180\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute inset-0 flex items-center justify-center opacity-90\", suitClass)}>\n <span className={CENTER_SUIT[size]}>{suitSymbol}</span>\n </div>\n </div>\n );\n}\n\nexport interface CardBackProps {\n size?: CardSize;\n className?: string;\n}\n\nfunction CardBackEmblem({ size }: { size: CardSize }) {\n const s = BACK_EMBLEM_SIZE[size];\n return (\n <svg width={s} height={s} viewBox=\"0 0 40 40\" className=\"drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]\" aria-hidden=\"true\">\n <g fill=\"#f0c04a\" opacity=\"0.92\">\n <ellipse cx=\"20\" cy=\"11\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"20\" cy=\"29\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"11\" cy=\"20\" rx=\"7\" ry=\"4\" />\n <ellipse cx=\"29\" cy=\"20\" rx=\"7\" ry=\"4\" />\n </g>\n <circle cx=\"20\" cy=\"20\" r=\"3.2\" fill=\"#5a0c18\" />\n </svg>\n );\n}\n\n/** The face-down card back (dark red + gold rosette). */\nexport function CardBack({ size = \"md\", className }: CardBackProps) {\n return (\n <div\n className={cn(\n SIZE_CLASSES[size],\n \"relative rounded-md p-[3px] overflow-hidden\",\n \"bg-gradient-to-br from-[#7a1020] via-[#5a0c18] to-[#3f0810]\",\n \"border-2 border-[#c9a24b]/70 shadow-[0_2px_6px_rgba(0,0,0,0.45)]\",\n className,\n )}\n style={{ aspectRatio: \"10/14\" }}\n >\n <div className=\"h-full w-full rounded-[4px] border border-white/20 flex items-center justify-center bg-[repeating-linear-gradient(45deg,rgba(255,255,255,0.09)_0_6px,transparent_6px_12px),repeating-linear-gradient(-45deg,rgba(255,255,255,0.06)_0_6px,transparent_6px_12px)]\">\n <CardBackEmblem size={size} />\n </div>\n </div>\n );\n}\n\nexport interface HoleCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate the cards dealing in, one after another. Default true. */\n deal?: boolean;\n}\n\n/** A row of a player's hole cards. */\nexport function HoleCards({ cards, size = \"lg\", className, deal = true }: HoleCardsProps) {\n return (\n <div className={cn(\"flex gap-2\", className)}>\n {cards.map((card, i) => (\n <div key={i} className={cn(deal && \"animate-deal-in\")} style={deal ? { animationDelay: `${i * 100}ms` } : undefined}>\n <PlayingCard card={card} size={size} />\n </div>\n ))}\n </div>\n );\n}\n\nexport interface CommunityCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate each card with a 3D flip. Default true. */\n animated?: boolean;\n /** Reserve a fixed number of slots so the row width never changes. */\n slots?: number;\n}\n\n/** A row of community cards, each flipping in with a staggered delay. */\nexport function CommunityCards({ cards, size = \"md\", className, animated = true, slots }: CommunityCardsProps) {\n const count = slots ?? cards.length;\n const list = Array.from({ length: count }, (_, i) => cards[i] ?? null);\n return (\n <div className={cn(\"flex gap-1.5\", animated && \"[perspective:800px]\", className)}>\n {list.map((card, i) => (\n <div key={i} className={SIZE_CLASSES[size]} style={{ aspectRatio: \"10/14\" }}>\n {card ? (\n <div\n className={cn(\"h-full w-full\", animated && \"animate-card-flip\")}\n style={animated ? { animationDelay: `${i * 70}ms` } : undefined}\n >\n <PlayingCard card={card} size={size} />\n </div>\n ) : null}\n </div>\n ))}\n </div>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface SeatProps {\n /** Turn/active highlight (generic). Animated by default. */\n isActive?: boolean;\n /** Winner highlight (generic). Animated by default. */\n isWinner?: boolean;\n /** Dimmed, e.g. folded or out (generic). */\n isDimmed?: boolean;\n /** Enable the turn/winner glow animations. Default true. */\n animated?: boolean;\n /** Render a default 👑 in the top-left corner when `isWinner`. Default false. */\n winnerCrown?: boolean;\n /** Optional click handler (e.g. \"sit here\"). */\n onClick?: () => void;\n\n /** Slot: top row inside the frame — name / badges. */\n header?: ReactNode;\n /** Slot: center content — 0..N cards, avatar, anything. */\n children?: ReactNode;\n /** Slot: bottom row inside the frame — stack / status. */\n footer?: ReactNode;\n /** Slot: floating element at the frame's top-right (legacy alias of topRight). */\n badge?: ReactNode;\n /** Slot: overlay centered over the frame. */\n overlay?: ReactNode;\n\n /** Slot: floating content above the frame, centered (e.g. an action bubble). */\n above?: ReactNode;\n /** Slot: content below the frame, centered (e.g. countdown / bet pill). */\n below?: ReactNode;\n /** Slot: floating content at the frame's top-left corner (e.g. a crown). */\n topLeft?: ReactNode;\n /** Slot: floating content at the frame's top-right corner (e.g. a kick button). */\n topRight?: ReactNode;\n /** Slot: floating content centered above the top edge (e.g. D/SB/BB badges). */\n topCenter?: ReactNode;\n\n width?: number;\n height?: number;\n className?: string;\n /** Extra classes for the inner frame, to override visuals. */\n frameClassName?: string;\n /** Inline styles for the inner frame. */\n frameStyle?: CSSProperties;\n}\n\n/**\n * A neutral seat: a frame with generic visual states and named slots. Put\n * whatever you want into `header` / `children` / `footer`, and use\n * `above` / `below` / `topLeft` / `topRight` / `topCenter` / `overlay` for\n * floating bits. It knows nothing about any particular game.\n */\nexport function Seat({\n isActive = false,\n isWinner = false,\n isDimmed = false,\n animated = true,\n winnerCrown = false,\n onClick,\n header,\n children,\n footer,\n badge,\n overlay,\n above,\n below,\n topLeft,\n topRight,\n topCenter,\n width = 160,\n height = 176,\n className,\n frameClassName,\n frameStyle,\n}: SeatProps) {\n const activeClass = animated\n ? \"border-amber-400 animate-turn-glow\"\n : \"border-amber-400 shadow-[0_0_18px_rgba(243,156,18,0.5)]\";\n const winnerClass = animated\n ? \"border-amber-400 animate-winner-glow\"\n : \"border-amber-400 shadow-[0_0_0_3px_rgba(243,156,18,0.9)]\";\n\n const effTopLeft =\n topLeft !== undefined\n ? topLeft\n : isWinner && winnerCrown\n ? <span className=\"text-3xl drop-shadow-[0_2px_4px_rgba(0,0,0,0.6)] animate-bounce\">👑</span>\n : undefined;\n\n return (\n <div className={cn(\"relative flex flex-col items-center\", className)} style={{ width }}>\n {topCenter !== undefined && (\n <div className=\"absolute -top-2 left-1/2 z-20 flex -translate-x-1/2 gap-1\">{topCenter}</div>\n )}\n {effTopLeft !== undefined && <div className=\"absolute -top-3 -left-2 z-40\">{effTopLeft}</div>}\n {topRight !== undefined && <div className=\"absolute -top-2 -right-2 z-40\">{topRight}</div>}\n {above !== undefined && (\n <div className=\"pointer-events-none absolute -top-12 left-1/2 z-30 -translate-x-1/2\">{above}</div>\n )}\n\n <div\n role={onClick ? \"button\" : undefined}\n tabIndex={onClick ? 0 : undefined}\n onClick={onClick}\n onKeyDown={\n onClick\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") onClick();\n }\n : undefined\n }\n style={{ width, height, ...frameStyle }}\n className={cn(\n \"relative flex flex-col items-center justify-between gap-1 rounded-2xl border bg-neutral-900/80 p-2 text-neutral-100 transition\",\n \"border-neutral-700\",\n isActive && activeClass,\n isWinner && winnerClass,\n isDimmed && \"opacity-50\",\n onClick && \"cursor-pointer hover:border-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-400\",\n frameClassName,\n )}\n >\n {header !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between gap-1 text-[11px]\">{header}</div>\n )}\n <div className=\"flex min-h-0 flex-1 items-center justify-center\">{children}</div>\n {footer !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between text-[11px] text-neutral-300\">{footer}</div>\n )}\n\n {overlay !== undefined && (\n <div className=\"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 text-center\">{overlay}</div>\n )}\n </div>\n\n {badge !== undefined && <div className=\"absolute top-0 right-0 z-40\">{badge}</div>}\n {below !== undefined && <div className=\"mt-1 flex flex-col items-center gap-1\">{below}</div>}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../cn\";\nimport { formatBB } from \"../format\";\n\nexport interface PotProps {\n amount: number;\n /** Optional label, e.g. \"Main\" / \"Side\". */\n label?: string;\n /** Formatter for the amount; defaults to Big-Blind formatting. */\n format?: (amount: number) => string;\n /** Animate the amount counting up when it changes. Default true. */\n animate?: boolean;\n className?: string;\n}\n\n/** Smoothly count a number toward its target (presentational only). */\nfunction CountUp({ value, format }: { value: number; format: (n: number) => string }) {\n const [shown, setShown] = useState(value);\n const fromRef = useRef(value);\n\n useEffect(() => {\n const from = fromRef.current;\n const to = value;\n if (from === to) return;\n let raf = 0;\n const start = performance.now();\n const dur = 500;\n const tick = (now: number) => {\n const t = Math.min(1, (now - start) / dur);\n const eased = 1 - Math.pow(1 - t, 3);\n setShown(Math.round(from + (to - from) * eased));\n if (t < 1) raf = requestAnimationFrame(tick);\n else fromRef.current = to;\n };\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [value]);\n\n return <>{format(shown)}</>;\n}\n\n/** A small, standalone pot pill. Optional — the table does not require it. */\nexport function Pot({ amount, label, format = formatBB, animate = true, className }: PotProps) {\n return (\n <div className={cn(\"flex items-center gap-2 font-mono text-sm text-amber-400\", className)}>\n {label && <span className=\"text-neutral-400\">{label}</span>}\n <span className=\"whitespace-nowrap rounded-full border border-amber-400/50 bg-neutral-900/80 px-3 py-1 shadow-inner\">\n {animate ? <CountUp value={amount} format={format} /> : format(amount)}\n </span>\n </div>\n );\n}\n","import { useRef, useLayoutEffect, useState, useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface CardTableProps {\n /** Number of seats (2–10). Default 6. */\n seatCount?: number;\n /** Optional per-seat content. Length should equal `seatCount`. */\n seats?: (ReactNode | null)[];\n /** Alternative to `seats`: render a seat by index. */\n renderSeat?: (index: number, isYou: boolean) => ReactNode;\n /** Which seat belongs to the viewer; rotates that seat to the bottom. */\n yourSeatIndex?: number | null;\n /** Optional content in the middle of the table (cards, pot, anything). */\n center?: ReactNode;\n /** Ambient spotlight + felt shimmer. Default true. */\n ambient?: boolean;\n /** Highlight the table edge (e.g. when it is your turn). Default false. */\n yourTurn?: boolean;\n /** Winning seat indices; when non-empty, confetti falls. */\n winnerSeats?: number[];\n /** Show confetti for winners. Default true. */\n confetti?: boolean;\n /** Seat frame size used for layout. */\n seatWidth?: number;\n seatHeight?: number;\n className?: string;\n}\n\nconst DESIGN_W = 1200;\nconst DESIGN_H = 900;\nconst MARGIN_X = 164;\nconst MARGIN_Y = 196;\nconst TABLE_RX = DESIGN_W / 2 - MARGIN_X;\nconst TABLE_RY = DESIGN_H / 2 - MARGIN_Y;\nconst FELT_GAP = 11;\nconst BET_SCALE = 0.72;\nconst BET_RX = TABLE_RX * BET_SCALE;\nconst BET_RY = TABLE_RY * BET_SCALE;\nconst CONFETTI_COLORS = [\"#f39c12\", \"#e74c3c\", \"#2ecc71\", \"#3498db\", \"#f1c40f\", \"#9b59b6\"];\n\ninterface SeatLayout {\n x: number;\n y: number;\n index: number;\n isYou: boolean;\n}\n\nfunction getEllipsePoint(cx: number, cy: number, rx: number, ry: number, angleDeg: number) {\n const rad = (angleDeg * Math.PI) / 180;\n return { x: cx + rx * Math.cos(rad), y: cy + ry * Math.sin(rad) };\n}\n\nfunction calculateSeatLayouts(\n seatCount: number,\n yourSeatIndex: number | null,\n seatWidth: number,\n seatHeight: number,\n): SeatLayout[] {\n const cx = DESIGN_W / 2;\n const cy = DESIGN_H / 2;\n const ringX = TABLE_RX - FELT_GAP + seatWidth / 2 + 6;\n const ringY = TABLE_RY - FELT_GAP + seatHeight / 2 + 6;\n\n const step = 360 / seatCount;\n const angleOf = (i: number) => -90 + i * step;\n const rotationOffset = yourSeatIndex !== null ? 90 - angleOf(yourSeatIndex) : 0;\n\n const layouts: SeatLayout[] = [];\n for (let i = 0; i < seatCount; i++) {\n const pt = getEllipsePoint(cx, cy, ringX, ringY, angleOf(i) + rotationOffset);\n layouts.push({\n x: pt.x - seatWidth / 2,\n y: pt.y - seatHeight / 2,\n index: i,\n isYou: i === yourSeatIndex,\n });\n }\n return layouts;\n}\n\n/**\n * A responsive, game-agnostic card table. It scales a fixed 1200x900 design\n * onto its container and arranges `seatCount` seats around the felt. Purely\n * presentational: supply seat content and an optional `center` slot.\n */\nexport function CardTable({\n seatCount = 6,\n seats,\n renderSeat,\n yourSeatIndex = null,\n center,\n ambient = true,\n yourTurn = false,\n winnerSeats = [],\n confetti = true,\n seatWidth = 160,\n seatHeight = 176,\n className,\n}: CardTableProps) {\n const count = Math.max(2, Math.min(10, Math.floor(seatCount)));\n const outerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n\n const layouts = useMemo(\n () => calculateSeatLayouts(count, yourSeatIndex, seatWidth, seatHeight),\n [count, yourSeatIndex, seatWidth, seatHeight],\n );\n\n useLayoutEffect(() => {\n const el = outerRef.current;\n if (!el) return;\n const compute = () => {\n const w = el.clientWidth;\n const h = el.clientHeight;\n if (!w || !h) return;\n setScale(Math.min(w / DESIGN_W, h / DESIGN_H));\n };\n compute();\n const ro = new ResizeObserver(compute);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const tableFrameInset = { inset: `${MARGIN_Y}px ${MARGIN_X}px` };\n const feltInset = { inset: `${MARGIN_Y + FELT_GAP}px ${MARGIN_X + FELT_GAP}px` };\n const rimInset = { inset: `${MARGIN_Y + 6}px ${MARGIN_X + 6}px` };\n const ringInset = { inset: `${MARGIN_Y + 2}px ${MARGIN_X + 2}px` };\n const betLineInset = {\n inset: `${MARGIN_Y + (TABLE_RY - BET_RY)}px ${MARGIN_X + (TABLE_RX - BET_RX)}px`,\n };\n\n return (\n <div\n ref={outerRef}\n className={cn(\"relative flex h-full w-full min-h-0 items-center justify-center overflow-hidden\", className)}\n >\n <div style={{ width: DESIGN_W * scale, height: DESIGN_H * scale }}>\n <div\n className=\"relative\"\n style={{ width: DESIGN_W, height: DESIGN_H, transform: `scale(${scale})`, transformOrigin: \"top left\" }}\n >\n <div\n className=\"absolute -inset-6 rounded-[50%] bg-[radial-gradient(closest-side,rgba(240,192,74,0.16),transparent_72%)] blur-3xl\"\n aria-hidden=\"true\"\n />\n\n {/* wooden rail */}\n <div\n className=\"absolute rounded-[50%] bg-gradient-to-b from-[#a5744a] via-[#6b3f22] to-[#38200f] shadow-[0_26px_70px_-12px_rgba(0,0,0,0.85),inset_0_3px_0_rgba(255,255,255,0.20),inset_0_-6px_16px_rgba(0,0,0,0.5)]\"\n style={tableFrameInset}\n aria-hidden=\"true\"\n />\n\n {/* felt */}\n <div\n className=\"absolute rounded-[50%] bg-[radial-gradient(ellipse_at_50%_32%,#23804b,#0f4d2e_55%,#093b22)] shadow-[inset_0_0_90px_rgba(0,0,0,0.55),inset_0_3px_10px_rgba(255,255,255,0.06)]\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n\n {/* ambient: breathing spotlight + felt shimmer */}\n {ambient && (\n <>\n <div\n className=\"absolute animate-spotlight rounded-[50%] bg-[radial-gradient(closest-side,rgba(255,214,120,0.14),transparent_70%)] blur-2xl\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n <div className=\"absolute overflow-hidden rounded-[50%]\" style={feltInset} aria-hidden=\"true\">\n <div className=\"absolute inset-0 animate-felt-shimmer bg-[linear-gradient(115deg,transparent_42%,rgba(255,255,255,0.06)_50%,transparent_58%)] bg-[length:220%_100%]\" />\n </div>\n </>\n )}\n\n {/* betting line */}\n <div\n className=\"absolute rounded-[50%] border-2 border-white/10 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.25)]\"\n style={betLineInset}\n aria-hidden=\"true\"\n />\n\n {/* center suit watermark */}\n <div className=\"absolute flex items-center justify-center pointer-events-none\" style={feltInset} aria-hidden=\"true\">\n <span className=\"text-white/[0.05] text-[110px] font-black tracking-[0.2em] select-none leading-none\">\n ♠♥♦♣\n </span>\n </div>\n\n {/* your-turn edge glow */}\n {yourTurn && (\n <div\n className=\"pointer-events-none absolute z-10 animate-turn-glow rounded-[50%]\"\n style={ringInset}\n aria-hidden=\"true\"\n />\n )}\n\n {/* center slot (optional) */}\n {center !== undefined && (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n {center}\n </div>\n )}\n\n {/* seats */}\n <div className=\"absolute inset-0 z-20\">\n {layouts.map(({ x, y, index, isYou }) => {\n const content = seats ? (seats[index] ?? null) : renderSeat ? renderSeat(index, isYou) : null;\n if (content === null) return null;\n return (\n <div key={index} className=\"absolute\" style={{ left: `${x}px`, top: `${y}px` }}>\n {content}\n </div>\n );\n })}\n </div>\n\n {/* winner confetti */}\n {confetti && winnerSeats.length > 0 && (\n <div className=\"pointer-events-none absolute inset-0 z-30 overflow-hidden\" aria-hidden=\"true\">\n {Array.from({ length: 16 }).map((_, i) => (\n <span\n key={i}\n className=\"absolute top-[34%] animate-confetti rounded-[2px]\"\n style={{\n left: `${6 + i * 5.6}%`,\n width: 6,\n height: 11,\n backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],\n animationDelay: `${(i % 8) * 90}ms`,\n }}\n />\n ))}\n </div>\n )}\n\n {/* outer rim highlight */}\n <div className=\"absolute rounded-[50%] border border-white/10 pointer-events-none\" style={rimInset} aria-hidden=\"true\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","useRef","useState","Fragment","jsx","jsxs","useRef","useState"]}
1
+ {"version":3,"sources":["../src/cn.ts","../src/format.ts","../src/seat.ts","../src/components/PlayingCard.tsx","../src/components/Seat.tsx","../src/components/Pot.tsx","../src/components/CardTable.tsx"],"sourcesContent":["import { twMerge } from \"tailwind-merge\";\n\n/** Join class names and resolve conflicting Tailwind utilities (last wins). */\nexport function cn(...parts: Array<string | false | null | undefined>): string {\n return twMerge(parts.filter(Boolean).join(\" \"));\n}\n","/** Big Blind value used for stack display. */\nexport const BB = 20;\n\n/** Format a chip amount as a Big-Blind string, e.g. 1234 -> \"61.7 BB\". */\nexport function formatBB(amount: number): string {\n const bb = amount / BB;\n return `${Number.isInteger(bb) ? bb : bb.toFixed(1)} BB`;\n}\n","/**\n * Seat-ring helpers. Pure functions — they contain no game-specific rules.\n */\n\nexport interface NextSeatOptions {\n /** +1 for clockwise, -1 for counter-clockwise. Default +1. */\n dir?: 1 | -1;\n /** Return true to skip a seat (e.g. folded / empty / out). */\n skip?: (index: number) => boolean;\n}\n\n/**\n * Return the index of the next seat on the ring after `current`, skipping any\n * seat matched by `opts.skip`. Wraps around. Returns `current` if every other\n * seat is skipped.\n */\nexport function nextSeatIndex(current: number, seatCount: number, opts: NextSeatOptions = {}): number {\n const n = Math.max(1, Math.floor(seatCount));\n const dir = opts.dir ?? 1;\n const skip = opts.skip;\n const start = ((Math.floor(current) % n) + n) % n;\n\n let i = start;\n for (let step = 0; step < n - 1; step++) {\n i = (i + dir + n) % n;\n if (!skip || !skip(i)) return i;\n }\n return start;\n}\n","import { cn } from \"../cn\";\n\nexport type CardSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nconst SIZE_CLASSES: Record<CardSize, string> = {\n xs: \"w-8 h-11\",\n sm: \"w-10 h-14\",\n md: \"w-16 h-22\",\n lg: \"w-24 h-32\",\n};\n\nconst CORNER_RANK: Record<CardSize, string> = {\n xs: \"text-[11px]\",\n sm: \"text-sm\",\n md: \"text-lg\",\n lg: \"text-2xl\",\n};\nconst CORNER_SUIT: Record<CardSize, string> = {\n xs: \"text-[9px]\",\n sm: \"text-xs\",\n md: \"text-sm\",\n lg: \"text-base\",\n};\nconst CENTER_SUIT: Record<CardSize, string> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n md: \"text-3xl\",\n lg: \"text-5xl\",\n};\n// Small cards (xs/sm) show a single, larger index centered in the card.\nconst SMALL_RANK: Partial<Record<CardSize, string>> = {\n xs: \"text-base\",\n sm: \"text-xl\",\n};\nconst SMALL_SUIT: Partial<Record<CardSize, string>> = {\n xs: \"text-lg\",\n sm: \"text-2xl\",\n};\nconst BACK_EMBLEM_SIZE: Record<CardSize, number> = {\n xs: 14,\n sm: 18,\n md: 28,\n lg: 42,\n};\n\n/** Parse a compact card code like \"As\", \"10h\", \"kd\" into rank + suit. */\nexport function parseCard(card: string): { rank: string; suit: string } {\n if (!card || card.length < 2) return { rank: \"\", suit: \"\" };\n return { rank: card.slice(0, -1), suit: card.slice(-1) };\n}\n\nexport function getSuitSymbol(suit: string): string {\n switch (suit) {\n case \"s\": return \"♠\";\n case \"h\": return \"♥\";\n case \"d\": return \"♦\";\n case \"c\": return \"♣\";\n default: return suit;\n }\n}\n\nfunction getSuitClass(suit: string): string {\n switch (suit) {\n case \"s\":\n case \"c\":\n return \"text-neutral-900\";\n case \"h\":\n case \"d\":\n return \"text-red-600\";\n default:\n return \"\";\n }\n}\n\nexport interface PlayingCardProps {\n /** Compact card code (\"As\", \"10h\") or null to render a face-down card back. */\n card: string | null;\n /** xs / sm show a single, larger, centered index; md / lg show corner + center. */\n size?: CardSize;\n className?: string;\n}\n\nconst CARD_FACE = \"relative bg-gradient-to-br from-white via-white to-neutral-100 rounded-md border border-neutral-300/90 overflow-hidden shadow-[0_2px_6px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.9)]\";\n\n/**\n * A single playing card. Size is driven by the `size` prop (never viewport\n * breakpoints) so cards scale predictably inside any container. Small sizes\n * (xs / sm) show only a larger upright index, centered in the card.\n */\nexport function PlayingCard({ card, size = \"md\", className }: PlayingCardProps) {\n if (!card) return <CardBack size={size} className={className} />;\n\n const { rank, suit } = parseCard(card);\n const suitSymbol = getSuitSymbol(suit);\n const suitClass = getSuitClass(suit);\n const large = size === \"md\" || size === \"lg\";\n\n // Small cards: rank centered in the top half, suit centered in the bottom half.\n if (!large) {\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute inset-0 flex flex-col leading-none\", suitClass)}>\n <div className=\"flex flex-1 items-center justify-center\">\n <span className={cn(SMALL_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n </div>\n <div className=\"flex flex-1 items-center justify-center\">\n <span className={SMALL_SUIT[size]}>{suitSymbol}</span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className={cn(SIZE_CLASSES[size], CARD_FACE, className)} style={{ aspectRatio: \"10/14\" }}>\n <div className={cn(\"absolute top-[3px] left-[5px] flex flex-col items-center leading-none\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute bottom-[3px] right-[5px] flex flex-col items-center leading-none rotate-180\", suitClass)}>\n <span className={cn(CORNER_RANK[size], \"font-bold tracking-tight\")}>{rank}</span>\n <span className={cn(CORNER_SUIT[size], \"-mt-[1px]\")}>{suitSymbol}</span>\n </div>\n <div className={cn(\"absolute inset-0 flex items-center justify-center opacity-90\", suitClass)}>\n <span className={CENTER_SUIT[size]}>{suitSymbol}</span>\n </div>\n </div>\n );\n}\n\nexport interface CardBackProps {\n size?: CardSize;\n className?: string;\n}\n\nfunction CardBackEmblem({ size }: { size: CardSize }) {\n const s = BACK_EMBLEM_SIZE[size];\n return (\n <svg width={s} height={s} viewBox=\"0 0 40 40\" className=\"drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]\" aria-hidden=\"true\">\n <g fill=\"#f0c04a\" opacity=\"0.92\">\n <ellipse cx=\"20\" cy=\"11\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"20\" cy=\"29\" rx=\"4\" ry=\"7\" />\n <ellipse cx=\"11\" cy=\"20\" rx=\"7\" ry=\"4\" />\n <ellipse cx=\"29\" cy=\"20\" rx=\"7\" ry=\"4\" />\n </g>\n <circle cx=\"20\" cy=\"20\" r=\"3.2\" fill=\"#5a0c18\" />\n </svg>\n );\n}\n\n/** The face-down card back (dark red + gold rosette). */\nexport function CardBack({ size = \"md\", className }: CardBackProps) {\n return (\n <div\n className={cn(\n SIZE_CLASSES[size],\n \"relative rounded-md p-[3px] overflow-hidden\",\n \"bg-gradient-to-br from-[#7a1020] via-[#5a0c18] to-[#3f0810]\",\n \"border-2 border-[#c9a24b]/70 shadow-[0_2px_6px_rgba(0,0,0,0.45)]\",\n className,\n )}\n style={{ aspectRatio: \"10/14\" }}\n >\n <div className=\"h-full w-full rounded-[4px] border border-white/20 flex items-center justify-center bg-[repeating-linear-gradient(45deg,rgba(255,255,255,0.09)_0_6px,transparent_6px_12px),repeating-linear-gradient(-45deg,rgba(255,255,255,0.06)_0_6px,transparent_6px_12px)]\">\n <CardBackEmblem size={size} />\n </div>\n </div>\n );\n}\n\nexport interface HoleCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate the cards dealing in, one after another. Default true. */\n deal?: boolean;\n}\n\n/** A row of a player's hole cards. */\nexport function HoleCards({ cards, size = \"lg\", className, deal = true }: HoleCardsProps) {\n return (\n <div className={cn(\"flex gap-2\", className)}>\n {cards.map((card, i) => (\n <div key={i} className={cn(deal && \"animate-deal-in\")} style={deal ? { animationDelay: `${i * 100}ms` } : undefined}>\n <PlayingCard card={card} size={size} />\n </div>\n ))}\n </div>\n );\n}\n\nexport interface CommunityCardsProps {\n cards: (string | null)[];\n size?: CardSize;\n className?: string;\n /** Animate each card with a 3D flip. Default true. */\n animated?: boolean;\n /** Reserve a fixed number of slots so the row width never changes. */\n slots?: number;\n}\n\n/** A row of community cards, each flipping in with a staggered delay. */\nexport function CommunityCards({ cards, size = \"md\", className, animated = true, slots }: CommunityCardsProps) {\n const count = slots ?? cards.length;\n const list = Array.from({ length: count }, (_, i) => cards[i] ?? null);\n return (\n <div className={cn(\"flex gap-1.5\", animated && \"[perspective:800px]\", className)}>\n {list.map((card, i) => (\n <div key={i} className={SIZE_CLASSES[size]} style={{ aspectRatio: \"10/14\" }}>\n {card ? (\n <div\n className={cn(\"h-full w-full\", animated && \"animate-card-flip\")}\n style={animated ? { animationDelay: `${i * 70}ms` } : undefined}\n >\n <PlayingCard card={card} size={size} />\n </div>\n ) : null}\n </div>\n ))}\n </div>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface SeatProps {\n /** Turn/active highlight (generic). Animated by default. */\n isActive?: boolean;\n /** Winner highlight (generic). Animated by default. */\n isWinner?: boolean;\n /** Dimmed, e.g. folded or out (generic). */\n isDimmed?: boolean;\n /** Enable the turn/winner glow animations. Default true. */\n animated?: boolean;\n /** Render a default 👑 in the top-left corner when `isWinner`. Default false. */\n winnerCrown?: boolean;\n /** Optional click handler (e.g. \"sit here\"). */\n onClick?: () => void;\n\n /** Slot: top row inside the frame — name / badges. */\n header?: ReactNode;\n /** Slot: center content — 0..N cards, avatar, anything. */\n children?: ReactNode;\n /** Slot: bottom row inside the frame — stack / status. */\n footer?: ReactNode;\n /** Slot: floating element at the frame's top-right (legacy alias of topRight). */\n badge?: ReactNode;\n /** Slot: overlay centered over the frame. */\n overlay?: ReactNode;\n\n /** Slot: floating content above the frame, centered (e.g. an action bubble). */\n above?: ReactNode;\n /** Slot: content below the frame, centered (e.g. countdown / bet pill). */\n below?: ReactNode;\n /** Slot: floating content at the frame's top-left corner (e.g. a crown). */\n topLeft?: ReactNode;\n /** Slot: floating content at the frame's top-right corner (e.g. a kick button). */\n topRight?: ReactNode;\n /** Slot: floating content centered above the top edge (e.g. D/SB/BB badges). */\n topCenter?: ReactNode;\n\n width?: number;\n height?: number;\n className?: string;\n /** Extra classes for the inner frame, to override visuals. */\n frameClassName?: string;\n /** Inline styles for the inner frame. */\n frameStyle?: CSSProperties;\n}\n\n/**\n * A neutral seat: a frame with generic visual states and named slots. Put\n * whatever you want into `header` / `children` / `footer`, and use\n * `above` / `below` / `topLeft` / `topRight` / `topCenter` / `overlay` for\n * floating bits. It knows nothing about any particular game.\n */\nexport function Seat({\n isActive = false,\n isWinner = false,\n isDimmed = false,\n animated = true,\n winnerCrown = false,\n onClick,\n header,\n children,\n footer,\n badge,\n overlay,\n above,\n below,\n topLeft,\n topRight,\n topCenter,\n width = 160,\n height = 176,\n className,\n frameClassName,\n frameStyle,\n}: SeatProps) {\n const activeClass = animated\n ? \"border-amber-400 animate-turn-glow\"\n : \"border-amber-400 shadow-[0_0_18px_rgba(243,156,18,0.5)]\";\n const winnerClass = animated\n ? \"border-amber-400 animate-winner-glow\"\n : \"border-amber-400 shadow-[0_0_0_3px_rgba(243,156,18,0.9)]\";\n\n const effTopLeft =\n topLeft !== undefined\n ? topLeft\n : isWinner && winnerCrown\n ? <span className=\"text-3xl drop-shadow-[0_2px_4px_rgba(0,0,0,0.6)] animate-bounce\">👑</span>\n : undefined;\n\n return (\n <div className={cn(\"relative flex flex-col items-center\", className)} style={{ width }}>\n {topCenter !== undefined && (\n <div className=\"absolute -top-2 left-1/2 z-20 flex -translate-x-1/2 gap-1\">{topCenter}</div>\n )}\n {effTopLeft !== undefined && <div className=\"absolute -top-3 -left-2 z-40\">{effTopLeft}</div>}\n {topRight !== undefined && <div className=\"absolute -top-2 -right-2 z-40\">{topRight}</div>}\n {above !== undefined && (\n <div className=\"pointer-events-none absolute -top-12 left-1/2 z-30 -translate-x-1/2\">{above}</div>\n )}\n\n <div\n role={onClick ? \"button\" : undefined}\n tabIndex={onClick ? 0 : undefined}\n onClick={onClick}\n onKeyDown={\n onClick\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") onClick();\n }\n : undefined\n }\n style={{ width, height, ...frameStyle }}\n className={cn(\n \"relative flex flex-col items-center justify-between gap-1 rounded-2xl border bg-neutral-900/80 p-2 text-neutral-100 transition\",\n \"border-neutral-700\",\n isActive && activeClass,\n isWinner && winnerClass,\n isDimmed && \"opacity-50\",\n onClick && \"cursor-pointer hover:border-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-400\",\n frameClassName,\n )}\n >\n {header !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between gap-1 text-[11px]\">{header}</div>\n )}\n <div className=\"flex min-h-0 flex-1 items-center justify-center\">{children}</div>\n {footer !== undefined && (\n <div className=\"flex w-full shrink-0 items-center justify-between text-[11px] text-neutral-300\">{footer}</div>\n )}\n\n {overlay !== undefined && (\n <div className=\"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 text-center\">{overlay}</div>\n )}\n </div>\n\n {badge !== undefined && <div className=\"absolute top-0 right-0 z-40\">{badge}</div>}\n {below !== undefined && <div className=\"mt-1 flex flex-col items-center gap-1\">{below}</div>}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../cn\";\nimport { formatBB } from \"../format\";\n\nexport interface PotProps {\n amount: number;\n /** Optional label, e.g. \"Main\" / \"Side\". */\n label?: string;\n /** Formatter for the amount; defaults to Big-Blind formatting. */\n format?: (amount: number) => string;\n /** Animate the amount counting up when it changes. Default true. */\n animate?: boolean;\n className?: string;\n}\n\n/** Smoothly count a number toward its target (presentational only). */\nfunction CountUp({ value, format }: { value: number; format: (n: number) => string }) {\n const [shown, setShown] = useState(value);\n const fromRef = useRef(value);\n\n useEffect(() => {\n const from = fromRef.current;\n const to = value;\n if (from === to) return;\n let raf = 0;\n const start = performance.now();\n const dur = 500;\n const tick = (now: number) => {\n const t = Math.min(1, (now - start) / dur);\n const eased = 1 - Math.pow(1 - t, 3);\n setShown(Math.round(from + (to - from) * eased));\n if (t < 1) raf = requestAnimationFrame(tick);\n else fromRef.current = to;\n };\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [value]);\n\n return <>{format(shown)}</>;\n}\n\n/** A small, standalone pot pill. Optional — the table does not require it. */\nexport function Pot({ amount, label, format = formatBB, animate = true, className }: PotProps) {\n return (\n <div className={cn(\"flex items-center gap-2 font-mono text-sm text-amber-400\", className)}>\n {label && <span className=\"text-neutral-400\">{label}</span>}\n <span className=\"whitespace-nowrap rounded-full border border-amber-400/50 bg-neutral-900/80 px-3 py-1 shadow-inner\">\n {animate ? <CountUp value={amount} format={format} /> : format(amount)}\n </span>\n </div>\n );\n}\n","import { useRef, useLayoutEffect, useState, useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface CardTableProps {\n /** Number of seats (2–10). Default 6. */\n seatCount?: number;\n /** Optional per-seat content. Length should equal `seatCount`. */\n seats?: (ReactNode | null)[];\n /** Alternative to `seats`: render a seat by index. */\n renderSeat?: (index: number, isYou: boolean) => ReactNode;\n /** Which seat belongs to the viewer; rotates that seat to the bottom. */\n yourSeatIndex?: number | null;\n /** Optional content in the middle of the table (cards, pot, anything). */\n center?: ReactNode;\n /** Ambient spotlight + felt shimmer. Default true. */\n ambient?: boolean;\n /** Highlight the table edge (e.g. when it is your turn). Default false. */\n yourTurn?: boolean;\n /** Winning seat indices; when non-empty, confetti falls. */\n winnerSeats?: number[];\n /** Show confetti for winners. Default true. */\n confetti?: boolean;\n /** Seat frame size used for layout. */\n seatWidth?: number;\n seatHeight?: number;\n className?: string;\n}\n\nconst DESIGN_W = 1200;\nconst DESIGN_H = 900;\nconst MARGIN_X = 164;\nconst MARGIN_Y = 196;\nconst TABLE_RX = DESIGN_W / 2 - MARGIN_X;\nconst TABLE_RY = DESIGN_H / 2 - MARGIN_Y;\nconst FELT_GAP = 11;\nconst BET_SCALE = 0.72;\nconst BET_RX = TABLE_RX * BET_SCALE;\nconst BET_RY = TABLE_RY * BET_SCALE;\nconst CONFETTI_COLORS = [\"#f39c12\", \"#e74c3c\", \"#2ecc71\", \"#3498db\", \"#f1c40f\", \"#9b59b6\"];\n\ninterface SeatLayout {\n x: number;\n y: number;\n index: number;\n isYou: boolean;\n}\n\nfunction getEllipsePoint(cx: number, cy: number, rx: number, ry: number, angleDeg: number) {\n const rad = (angleDeg * Math.PI) / 180;\n return { x: cx + rx * Math.cos(rad), y: cy + ry * Math.sin(rad) };\n}\n\nfunction calculateSeatLayouts(\n seatCount: number,\n yourSeatIndex: number | null,\n seatWidth: number,\n seatHeight: number,\n): SeatLayout[] {\n const cx = DESIGN_W / 2;\n const cy = DESIGN_H / 2;\n const ringX = TABLE_RX - FELT_GAP + seatWidth / 2 + 6;\n const ringY = TABLE_RY - FELT_GAP + seatHeight / 2 + 6;\n\n const step = 360 / seatCount;\n const angleOf = (i: number) => -90 + i * step;\n const rotationOffset = yourSeatIndex !== null ? 90 - angleOf(yourSeatIndex) : 0;\n\n const layouts: SeatLayout[] = [];\n for (let i = 0; i < seatCount; i++) {\n const pt = getEllipsePoint(cx, cy, ringX, ringY, angleOf(i) + rotationOffset);\n layouts.push({\n x: pt.x - seatWidth / 2,\n y: pt.y - seatHeight / 2,\n index: i,\n isYou: i === yourSeatIndex,\n });\n }\n return layouts;\n}\n\n/**\n * A responsive, game-agnostic card table. It scales a fixed 1200x900 design\n * onto its container and arranges `seatCount` seats around the felt. Purely\n * presentational: supply seat content and an optional `center` slot.\n */\nexport function CardTable({\n seatCount = 6,\n seats,\n renderSeat,\n yourSeatIndex = null,\n center,\n ambient = true,\n yourTurn = false,\n winnerSeats = [],\n confetti = true,\n seatWidth = 160,\n seatHeight = 176,\n className,\n}: CardTableProps) {\n const count = Math.max(2, Math.min(10, Math.floor(seatCount)));\n const outerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n\n const layouts = useMemo(\n () => calculateSeatLayouts(count, yourSeatIndex, seatWidth, seatHeight),\n [count, yourSeatIndex, seatWidth, seatHeight],\n );\n\n useLayoutEffect(() => {\n const el = outerRef.current;\n if (!el) return;\n const compute = () => {\n const w = el.clientWidth;\n const h = el.clientHeight;\n if (!w || !h) return;\n setScale(Math.min(w / DESIGN_W, h / DESIGN_H));\n };\n compute();\n const ro = new ResizeObserver(compute);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const tableFrameInset = { inset: `${MARGIN_Y}px ${MARGIN_X}px` };\n const feltInset = { inset: `${MARGIN_Y + FELT_GAP}px ${MARGIN_X + FELT_GAP}px` };\n const rimInset = { inset: `${MARGIN_Y + 6}px ${MARGIN_X + 6}px` };\n const ringInset = { inset: `${MARGIN_Y + 2}px ${MARGIN_X + 2}px` };\n const betLineInset = {\n inset: `${MARGIN_Y + (TABLE_RY - BET_RY)}px ${MARGIN_X + (TABLE_RX - BET_RX)}px`,\n };\n\n return (\n <div\n ref={outerRef}\n className={cn(\"relative flex h-full w-full min-h-0 items-center justify-center overflow-hidden\", className)}\n >\n <div style={{ width: DESIGN_W * scale, height: DESIGN_H * scale }}>\n <div\n className=\"relative\"\n style={{ width: DESIGN_W, height: DESIGN_H, transform: `scale(${scale})`, transformOrigin: \"top left\" }}\n >\n <div\n className=\"absolute -inset-6 rounded-[50%] bg-[radial-gradient(closest-side,rgba(240,192,74,0.16),transparent_72%)] blur-3xl\"\n aria-hidden=\"true\"\n />\n\n {/* wooden rail */}\n <div\n className=\"absolute rounded-[50%] bg-gradient-to-b from-[#a5744a] via-[#6b3f22] to-[#38200f] shadow-[0_26px_70px_-12px_rgba(0,0,0,0.85),inset_0_3px_0_rgba(255,255,255,0.20),inset_0_-6px_16px_rgba(0,0,0,0.5)]\"\n style={tableFrameInset}\n aria-hidden=\"true\"\n />\n\n {/* felt */}\n <div\n className=\"absolute rounded-[50%] bg-[radial-gradient(ellipse_at_50%_32%,#23804b,#0f4d2e_55%,#093b22)] shadow-[inset_0_0_90px_rgba(0,0,0,0.55),inset_0_3px_10px_rgba(255,255,255,0.06)]\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n\n {/* ambient: breathing spotlight + felt shimmer */}\n {ambient && (\n <>\n <div\n className=\"absolute animate-spotlight rounded-[50%] bg-[radial-gradient(closest-side,rgba(255,214,120,0.14),transparent_70%)] blur-2xl\"\n style={feltInset}\n aria-hidden=\"true\"\n />\n <div className=\"absolute overflow-hidden rounded-[50%]\" style={feltInset} aria-hidden=\"true\">\n <div className=\"absolute inset-0 animate-felt-shimmer bg-[linear-gradient(115deg,transparent_42%,rgba(255,255,255,0.06)_50%,transparent_58%)] bg-[length:220%_100%]\" />\n </div>\n </>\n )}\n\n {/* betting line */}\n <div\n className=\"absolute rounded-[50%] border-2 border-white/10 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.25)]\"\n style={betLineInset}\n aria-hidden=\"true\"\n />\n\n {/* center suit watermark */}\n <div className=\"absolute flex items-center justify-center pointer-events-none\" style={feltInset} aria-hidden=\"true\">\n <span className=\"text-white/[0.05] text-[110px] font-black tracking-[0.2em] select-none leading-none\">\n ♠♥♦♣\n </span>\n </div>\n\n {/* your-turn edge glow */}\n {yourTurn && (\n <div\n className=\"pointer-events-none absolute z-10 animate-turn-glow rounded-[50%]\"\n style={ringInset}\n aria-hidden=\"true\"\n />\n )}\n\n {/* center slot (optional) */}\n {center !== undefined && (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n {center}\n </div>\n )}\n\n {/* seats */}\n <div className=\"absolute inset-0 z-20\">\n {layouts.map(({ x, y, index, isYou }) => {\n const content = seats ? (seats[index] ?? null) : renderSeat ? renderSeat(index, isYou) : null;\n if (content === null) return null;\n return (\n <div key={index} className=\"absolute\" style={{ left: `${x}px`, top: `${y}px` }}>\n {content}\n </div>\n );\n })}\n </div>\n\n {/* winner confetti */}\n {confetti && winnerSeats.length > 0 && (\n <div className=\"pointer-events-none absolute inset-0 z-30 overflow-hidden\" aria-hidden=\"true\">\n {Array.from({ length: 44 }).map((_, i) => {\n const w = 5 + (i % 4) * 2;\n const h = 9 + (i % 3) * 4;\n const dur = 1.7 + (i % 5) * 0.28;\n const delay = (i % 12) * 0.14;\n return (\n <span\n key={i}\n className=\"absolute top-[26%] animate-confetti rounded-[2px]\"\n style={{\n left: `${(i * 37) % 96}%`,\n width: w,\n height: h,\n backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],\n animationDuration: `${dur}s`,\n animationDelay: `${delay}s`,\n animationIterationCount: \"infinite\",\n }}\n />\n );\n })}\n </div>\n )}\n\n {/* outer rim highlight */}\n <div className=\"absolute rounded-[50%] border border-white/10 pointer-events-none\" style={rimInset} aria-hidden=\"true\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","useRef","useState","Fragment","jsx","jsxs","useRef","useState"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stargazeryan/poker-ui",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Themable React + Tailwind CSS v4 components for poker tables and playing cards.",
5
5
  "license": "MIT",
6
6
  "author": "Ryan",
package/src/styles.css CHANGED
@@ -129,7 +129,7 @@
129
129
  }
130
130
  100% {
131
131
  opacity: 0;
132
- transform: translateY(150px) rotate(560deg);
132
+ transform: translateY(280px) rotate(560deg);
133
133
  }
134
134
  }
135
135