@scriptonita/chess-football-ui 0.4.2 → 0.5.0

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.d.cts CHANGED
@@ -159,7 +159,7 @@ type GameTranslator = (key: string, values?: Record<string, unknown>) => string;
159
159
  * grep -rhoE "t\('[a-zA-Z0-9_.]+'" src/components | sed -E "s/t\('//;s/'//" | sort -u
160
160
  * (the dynamic `pieces.*` / `eventToast.*` families are derived separately below).
161
161
  */
162
- declare const STATIC_I18N_KEYS: readonly ["actionPointsAriaLabel", "actionPointsShort", "endTurn", "endTurnConfirm", "endTurnConfirmDescription", "endTurnConfirmYes", "endTurnKeepPlaying", "history.empty", "history.goal", "history.interception", "history.move", "history.offside", "history.pass", "history.tackle", "history.title", "history.turn", "history.viewAll", "move", "offsideWarning", "pass", "selectedPiece.alreadyMoved", "selectedPiece.atSquare", "selectedPiece.empty", "selectedPiece.hasBall", "teamBlack", "teamWhite", "waitingRival", "yourTurn"];
162
+ declare const STATIC_I18N_KEYS: readonly ["actionPointsAriaLabel", "actionPointsShort", "endTurn", "endTurnConfirm", "endTurnConfirmDescription", "endTurnConfirmYes", "endTurnKeepPlaying", "history.empty", "history.goal", "history.interception", "history.move", "history.offside", "history.pass", "history.tackle", "history.title", "history.turn", "history.viewAll", "move", "offsideWarning", "pass", "selectedPiece.alreadyMoved", "selectedPiece.atSquare", "selectedPiece.empty", "selectedPiece.hasBall", "shortcuts.arrows", "shortcuts.buttonLabel", "shortcuts.cancel", "shortcuts.move", "shortcuts.pass", "shortcuts.select", "shortcuts.title", "teamBlack", "teamWhite", "waitingRival", "yourTurn"];
163
163
  /** `pieces.*` — one key per short and long piece label the engine exposes (move-history, selected-piece-detail). */
164
164
  type PieceI18nKey = `pieces.${PieceShortKey | PieceLongKey}`;
165
165
  /** `eventToast.*` — one key per toastable event (see `ToastEvent` in `event-toast.tsx`). */
package/dist/index.d.ts CHANGED
@@ -159,7 +159,7 @@ type GameTranslator = (key: string, values?: Record<string, unknown>) => string;
159
159
  * grep -rhoE "t\('[a-zA-Z0-9_.]+'" src/components | sed -E "s/t\('//;s/'//" | sort -u
160
160
  * (the dynamic `pieces.*` / `eventToast.*` families are derived separately below).
161
161
  */
162
- declare const STATIC_I18N_KEYS: readonly ["actionPointsAriaLabel", "actionPointsShort", "endTurn", "endTurnConfirm", "endTurnConfirmDescription", "endTurnConfirmYes", "endTurnKeepPlaying", "history.empty", "history.goal", "history.interception", "history.move", "history.offside", "history.pass", "history.tackle", "history.title", "history.turn", "history.viewAll", "move", "offsideWarning", "pass", "selectedPiece.alreadyMoved", "selectedPiece.atSquare", "selectedPiece.empty", "selectedPiece.hasBall", "teamBlack", "teamWhite", "waitingRival", "yourTurn"];
162
+ declare const STATIC_I18N_KEYS: readonly ["actionPointsAriaLabel", "actionPointsShort", "endTurn", "endTurnConfirm", "endTurnConfirmDescription", "endTurnConfirmYes", "endTurnKeepPlaying", "history.empty", "history.goal", "history.interception", "history.move", "history.offside", "history.pass", "history.tackle", "history.title", "history.turn", "history.viewAll", "move", "offsideWarning", "pass", "selectedPiece.alreadyMoved", "selectedPiece.atSquare", "selectedPiece.empty", "selectedPiece.hasBall", "shortcuts.arrows", "shortcuts.buttonLabel", "shortcuts.cancel", "shortcuts.move", "shortcuts.pass", "shortcuts.select", "shortcuts.title", "teamBlack", "teamWhite", "waitingRival", "yourTurn"];
163
163
  /** `pieces.*` — one key per short and long piece label the engine exposes (move-history, selected-piece-detail). */
164
164
  type PieceI18nKey = `pieces.${PieceShortKey | PieceLongKey}`;
165
165
  /** `eventToast.*` — one key per toastable event (see `ToastEvent` in `event-toast.tsx`). */
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { useGameStore, getInitialBoardState } from './chunk-N24LGLLO.js';
2
- import { SHORT_KEY, LONG_KEY, getValidMoves, getValidPasses, isInEnemyArea, RANK_LABELS, FILE_LABELS, squareName } from '@scriptonita/chess-football-engine';
2
+ import { SHORT_KEY, LONG_KEY, getValidMoves, getValidPasses, isInEnemyArea, getPath, RANK_LABELS, FILE_LABELS, squareName } from '@scriptonita/chess-football-engine';
3
3
  import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
- import { MotionConfig, AnimatePresence, motion } from 'framer-motion';
6
- import { Crown, Zap, Flag, ShieldQuestion, ChessKnight, ChessBishop, ChessRook, ChessQueen, ChessKing, X, Move } from 'lucide-react';
5
+ import { MotionConfig, useReducedMotion, AnimatePresence, motion } from 'framer-motion';
6
+ import { HelpCircle, Crown, Zap, Flag, ShieldQuestion, ChessKnight, ChessBishop, ChessRook, ChessQueen, ChessKing, X, Move } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import * as React from 'react';
9
9
  import { createContext, useId, useContext, useState, useRef, useEffect, useMemo } from 'react';
@@ -150,6 +150,13 @@ var STATIC_I18N_KEYS = [
150
150
  "selectedPiece.atSquare",
151
151
  "selectedPiece.empty",
152
152
  "selectedPiece.hasBall",
153
+ "shortcuts.arrows",
154
+ "shortcuts.buttonLabel",
155
+ "shortcuts.cancel",
156
+ "shortcuts.move",
157
+ "shortcuts.pass",
158
+ "shortcuts.select",
159
+ "shortcuts.title",
153
160
  "teamBlack",
154
161
  "teamWhite",
155
162
  "waitingRival",
@@ -181,6 +188,7 @@ var ROWS = 12;
181
188
  var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
182
189
  function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
183
190
  const t = useGameT();
191
+ const prefersReducedMotion = useReducedMotion();
184
192
  const {
185
193
  boardState,
186
194
  selectedPieceId,
@@ -191,11 +199,62 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
191
199
  } = useGameStore();
192
200
  const [cursor, setCursor] = useState(null);
193
201
  const [disambiguateAt, setDisambiguateAt] = useState(null);
202
+ const [shortcutsOpen, setShortcutsOpen] = useState(false);
203
+ const shortcutsRef = useRef(null);
204
+ useEffect(() => {
205
+ if (!shortcutsOpen) return;
206
+ const onPointerDown = (e) => {
207
+ if (shortcutsRef.current && !shortcutsRef.current.contains(e.target)) {
208
+ setShortcutsOpen(false);
209
+ }
210
+ };
211
+ document.addEventListener("mousedown", onPointerDown);
212
+ return () => document.removeEventListener("mousedown", onPointerDown);
213
+ }, [shortcutsOpen]);
214
+ const [invalidClickAt, setInvalidClickAt] = useState(null);
215
+ const invalidClickNonceRef = useRef(0);
216
+ const triggerInvalidClickShake = (x, y) => {
217
+ invalidClickNonceRef.current += 1;
218
+ setInvalidClickAt({ x, y, nonce: invalidClickNonceRef.current });
219
+ };
220
+ const [hoveredPassAt, setHoveredPassAt] = useState(null);
221
+ useEffect(() => {
222
+ if (!invalidClickAt) return;
223
+ const id = setTimeout(() => setInvalidClickAt(null), 100);
224
+ return () => clearTimeout(id);
225
+ }, [invalidClickAt]);
194
226
  const prevBallRef = useRef(boardState?.ball);
195
227
  const prevBall = prevBallRef.current;
196
228
  useEffect(() => {
197
229
  prevBallRef.current = boardState?.ball;
198
230
  });
231
+ const prevTurnRef = useRef(boardState?.turn);
232
+ const [replay, setReplay] = useState(null);
233
+ useEffect(() => {
234
+ const prevTurn = prevTurnRef.current;
235
+ prevTurnRef.current = boardState?.turn;
236
+ if (!boardState || prevTurn === boardState.turn) return;
237
+ if (boardState.turn !== userSide) {
238
+ setReplay(null);
239
+ return;
240
+ }
241
+ if (prefersReducedMotion || userSide === null) return;
242
+ const lastEntry = boardState.moveHistory.at(-1);
243
+ if (!lastEntry) return;
244
+ const steps = boardState.moveHistory.filter(
245
+ (m) => m.turnNumber === lastEntry.turnNumber && m.pieceSide !== userSide
246
+ );
247
+ if (steps.length > 1) setReplay({ steps, index: 0 });
248
+ }, [boardState, userSide, prefersReducedMotion]);
249
+ useEffect(() => {
250
+ if (!replay) return;
251
+ if (replay.index >= replay.steps.length - 1) {
252
+ const id2 = setTimeout(() => setReplay(null), 350);
253
+ return () => clearTimeout(id2);
254
+ }
255
+ const id = setTimeout(() => setReplay((r) => r ? { ...r, index: r.index + 1 } : null), 350);
256
+ return () => clearTimeout(id);
257
+ }, [replay]);
199
258
  const validMoves = useMemo(() => {
200
259
  if (!boardState) return [];
201
260
  const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
@@ -233,7 +292,11 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
233
292
  }
234
293
  const ballCarrier = ball.holderId ? boardState.pieces.find((p) => p.id === ball.holderId) ?? null : null;
235
294
  const isOffsideRisk = !!ballCarrier && ballCarrier.type !== "king" && userSide !== null && ballCarrier.side === userSide && boardState.turn === userSide && isInEnemyArea(ballCarrier.pos, ballCarrier.side);
295
+ const passCarrier = boardState.pieces.find((p) => p.id === selectedPieceId && boardState.ball.holderId === p.id) ?? null;
296
+ const trajectoryPath = hoveredPassAt && passCarrier ? passCarrier.type === "knight" ? [hoveredPassAt] : getPath(passCarrier.pos, hoveredPassAt) : null;
297
+ const trajectoryIntercepted = !!(trajectoryPath && passCarrier && trajectoryPath.some((sq) => boardState.pieces.some((p) => p.pos.x === sq.x && p.pos.y === sq.y && p.side !== passCarrier.side)));
236
298
  const handleSquareClick = (x, y) => {
299
+ if (replay) return;
237
300
  setCursor(null);
238
301
  const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
239
302
  const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
@@ -244,15 +307,21 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
244
307
  if (isValidMove && selectedPieceId) {
245
308
  movePiece(selectedPieceId, { x, y });
246
309
  setDisambiguateAt(null);
310
+ setInvalidClickAt(null);
247
311
  } else if (isValidPass) {
248
312
  passBall({ x, y });
249
313
  setDisambiguateAt(null);
314
+ setInvalidClickAt(null);
250
315
  } else {
316
+ if (selectedPieceId) {
317
+ triggerInvalidClickShake(x, y);
318
+ }
251
319
  setDisambiguateAt(null);
252
320
  }
253
321
  };
254
322
  const handlePieceClick = (pieceId, x, y, e) => {
255
323
  e.stopPropagation();
324
+ if (replay) return;
256
325
  const pieceAt = boardState.pieces.find((p) => p.id === pieceId);
257
326
  if (!pieceAt) return;
258
327
  const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
@@ -266,6 +335,10 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
266
335
  setSelectedPieceId(pieceAt.id);
267
336
  }
268
337
  setDisambiguateAt(null);
338
+ setInvalidClickAt(null);
339
+ } else if (selectedPieceId) {
340
+ triggerInvalidClickShake(x, y);
341
+ setDisambiguateAt(null);
269
342
  }
270
343
  };
271
344
  const defaultCursor = () => {
@@ -282,7 +355,7 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
282
355
  }
283
356
  };
284
357
  const handleKeyDown = (e) => {
285
- if (!keyboardNav) return;
358
+ if (!keyboardNav || replay) return;
286
359
  const step = (dx, dy) => {
287
360
  e.preventDefault();
288
361
  setCursor((c) => {
@@ -326,7 +399,9 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
326
399
  }
327
400
  break;
328
401
  case "Escape":
329
- if (disambiguateAt) {
402
+ if (shortcutsOpen) {
403
+ setShortcutsOpen(false);
404
+ } else if (disambiguateAt) {
330
405
  setDisambiguateAt(null);
331
406
  } else {
332
407
  setSelectedPieceId(null);
@@ -344,8 +419,9 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
344
419
  const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
345
420
  const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
346
421
  const isAmbiguous = isValidMove && isValidPass;
347
- const isLastMoveOrigin = lastMove?.from && lastMove.from.x === x && lastMove.from.y === y;
348
- const isLastMoveDest = lastMove?.to && lastMove.to.x === x && lastMove.to.y === y;
422
+ const displayedLastMove = replay ? replay.steps[replay.index] : lastMove;
423
+ const isLastMoveOrigin = displayedLastMove?.from && displayedLastMove.from.x === x && displayedLastMove.from.y === y;
424
+ const isLastMoveDest = displayedLastMove?.to && displayedLastMove.to.x === x && displayedLastMove.to.y === y;
349
425
  const isLastMove = isLastMoveOrigin || isLastMoveDest;
350
426
  const isEnemyGoalArea = userSide !== null && (x >= 2 && x <= 6) && (userSide === "white" ? y >= 10 && y <= 11 : y >= 0 && y <= 1);
351
427
  const pieceAt = boardState.pieces.find((p) => p.pos.x === x && p.pos.y === y);
@@ -356,6 +432,10 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
356
432
  "div",
357
433
  {
358
434
  onClick: () => handleSquareClick(x, y),
435
+ onMouseEnter: () => {
436
+ if (isValidPass) setHoveredPassAt({ x, y });
437
+ },
438
+ onMouseLeave: () => setHoveredPassAt(null),
359
439
  className: cn(
360
440
  "relative aspect-square w-full flex items-center justify-center cursor-pointer",
361
441
  isEven ? "bg-field-green-1" : "bg-field-green-2",
@@ -463,7 +543,21 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
463
543
  /* @__PURE__ */ jsx("circle", { cx: "4.5", cy: "1.5", r: "0.1", fill: "white", fillOpacity: "0.5" }),
464
544
  /* @__PURE__ */ jsx("circle", { cx: "4.5", cy: "10.5", r: "0.1", fill: "white", fillOpacity: "0.5" }),
465
545
  /* @__PURE__ */ jsx("path", { d: "M 3.086,2 A 1.5,1.5 0 0 0 5.914,2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
466
- /* @__PURE__ */ jsx("path", { d: "M 3.086,10 A 1.5,1.5 0 0 1 5.914,10", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" })
546
+ /* @__PURE__ */ jsx("path", { d: "M 3.086,10 A 1.5,1.5 0 0 1 5.914,10", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
547
+ hoveredPassAt && passCarrier && /* @__PURE__ */ jsx(
548
+ "line",
549
+ {
550
+ "data-testid": "pass-trajectory-line",
551
+ x1: passCarrier.pos.x + 0.5,
552
+ y1: ROWS - 1 - passCarrier.pos.y + 0.5,
553
+ x2: hoveredPassAt.x + 0.5,
554
+ y2: ROWS - 1 - hoveredPassAt.y + 0.5,
555
+ stroke: trajectoryIntercepted ? "var(--danger)" : "var(--pass-highlight)",
556
+ strokeWidth: "0.08",
557
+ strokeDasharray: "0.15 0.1",
558
+ strokeLinecap: "round"
559
+ }
560
+ )
467
561
  ]
468
562
  }
469
563
  ),
@@ -536,12 +630,92 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
536
630
  },
537
631
  "ball"
538
632
  );
539
- })()
633
+ })(),
634
+ invalidClickAt && /* @__PURE__ */ jsx(
635
+ motion.div,
636
+ {
637
+ "data-testid": "invalid-action-shake",
638
+ "aria-hidden": "true",
639
+ initial: false,
640
+ animate: prefersReducedMotion ? { opacity: [0.9, 0] } : { x: [0, -3, 3, -3, 3, 0] },
641
+ transition: { duration: 0.08 },
642
+ style: {
643
+ position: "absolute",
644
+ left: `${invalidClickAt.x / COLS * 100}%`,
645
+ top: `${(ROWS - 1 - invalidClickAt.y) / ROWS * 100}%`,
646
+ width: `${100 / COLS}%`,
647
+ height: `${100 / ROWS}%`
648
+ },
649
+ className: "ring-2 ring-inset ring-danger/70 rounded-sm"
650
+ },
651
+ invalidClickAt.nonce
652
+ )
540
653
  ] }),
541
654
  isOffsideRisk && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 z-30 flex items-center gap-1.5 px-3 py-1.5 bg-warning/20 border border-warning/50 rounded-full font-inter text-xs font-semibold text-warning pointer-events-none whitespace-nowrap backdrop-blur-sm", children: [
542
655
  "\u26A0 ",
543
656
  t("offsideWarning")
544
- ] })
657
+ ] }),
658
+ keyboardNav && /* @__PURE__ */ jsxs(
659
+ "div",
660
+ {
661
+ ref: shortcutsRef,
662
+ className: "hidden md:block absolute top-2 right-2 z-40",
663
+ onKeyDown: (e) => e.stopPropagation(),
664
+ children: [
665
+ /* @__PURE__ */ jsx(
666
+ "button",
667
+ {
668
+ type: "button",
669
+ onClick: (e) => {
670
+ e.stopPropagation();
671
+ setShortcutsOpen((o) => !o);
672
+ },
673
+ "aria-label": t("shortcuts.buttonLabel"),
674
+ "aria-expanded": shortcutsOpen,
675
+ className: "w-11 h-11 flex items-center justify-center rounded-full bg-bg-secondary/80 border border-border-subtle text-fg-muted hover:text-fg-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-green backdrop-blur-sm",
676
+ children: /* @__PURE__ */ jsx(HelpCircle, { size: 16, strokeWidth: 2, "aria-hidden": "true" })
677
+ }
678
+ ),
679
+ shortcutsOpen && /* @__PURE__ */ jsxs(
680
+ "div",
681
+ {
682
+ className: "absolute top-full right-0 mt-1 z-50 w-max max-w-[220px] bg-bg-secondary border border-border-subtle rounded-md shadow-xl p-2.5 pointer-events-auto",
683
+ onClick: (e) => e.stopPropagation(),
684
+ children: [
685
+ /* @__PURE__ */ jsx("p", { className: "font-inter text-[11px] font-semibold text-fg-primary mb-1.5", children: t("shortcuts.title") }),
686
+ /* @__PURE__ */ jsxs("ul", { className: "font-mono text-[10px] text-fg-secondary leading-relaxed", children: [
687
+ /* @__PURE__ */ jsxs("li", { children: [
688
+ /* @__PURE__ */ jsx("kbd", { className: "text-fg-primary", children: "\u2191\u2193\u2190\u2192" }),
689
+ " ",
690
+ t("shortcuts.arrows")
691
+ ] }),
692
+ /* @__PURE__ */ jsxs("li", { children: [
693
+ /* @__PURE__ */ jsx("kbd", { className: "text-fg-primary", children: "Enter" }),
694
+ " ",
695
+ t("shortcuts.select")
696
+ ] }),
697
+ /* @__PURE__ */ jsxs("li", { children: [
698
+ /* @__PURE__ */ jsx("kbd", { className: "text-fg-primary", children: "M" }),
699
+ " ",
700
+ t("shortcuts.move")
701
+ ] }),
702
+ /* @__PURE__ */ jsxs("li", { children: [
703
+ /* @__PURE__ */ jsx("kbd", { className: "text-fg-primary", children: "P" }),
704
+ " ",
705
+ t("shortcuts.pass")
706
+ ] }),
707
+ /* @__PURE__ */ jsxs("li", { children: [
708
+ /* @__PURE__ */ jsx("kbd", { className: "text-fg-primary", children: "Esc" }),
709
+ " ",
710
+ t("shortcuts.cancel")
711
+ ] })
712
+ ] })
713
+ ]
714
+ }
715
+ )
716
+ ]
717
+ }
718
+ )
545
719
  ] })
546
720
  }
547
721
  );
@@ -587,31 +761,50 @@ function AvatarFallback({ className, children }) {
587
761
  }
588
762
  function ActionPoints({ total, remaining, size = 20, className, kingMustRelease }) {
589
763
  const t = useGameT();
764
+ const prevRemainingRef = useRef(remaining);
765
+ const [spentRange, setSpentRange] = useState(null);
766
+ useEffect(() => {
767
+ const prev = prevRemainingRef.current;
768
+ prevRemainingRef.current = remaining;
769
+ if (remaining < prev) {
770
+ setSpentRange([remaining, prev - 1]);
771
+ const id = setTimeout(() => setSpentRange(null), 300);
772
+ return () => clearTimeout(id);
773
+ }
774
+ setSpentRange(null);
775
+ }, [remaining]);
590
776
  return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-1.5", className), "aria-label": t("actionPointsAriaLabel", { remaining, total }), children: Array.from({ length: total }).map((_, i) => {
591
777
  const isActive = i < remaining;
592
778
  const showCrown = kingMustRelease && i === total - 1;
593
- if (showCrown) {
594
- return /* @__PURE__ */ jsx(
595
- Crown,
596
- {
597
- size,
598
- strokeWidth: 2,
599
- className: "text-yellow-400 fill-yellow-400 transition-colors duration-150",
600
- "aria-hidden": "true"
601
- },
602
- i
603
- );
604
- }
779
+ const justSpent = !!spentRange && i >= spentRange[0] && i <= spentRange[1];
605
780
  return /* @__PURE__ */ jsx(
606
- Zap,
781
+ motion.span,
607
782
  {
608
- size,
609
- strokeWidth: 2,
610
- className: cn(
611
- "transition-colors duration-150",
612
- isActive ? "text-accent-green fill-accent-green" : "text-fg-muted fill-none"
613
- ),
614
- "aria-hidden": "true"
783
+ "data-testid": `ap-pip-${i}`,
784
+ "data-spent": justSpent || void 0,
785
+ className: "inline-flex",
786
+ animate: justSpent ? { scale: [1.5, 1] } : void 0,
787
+ transition: { duration: 0.3 },
788
+ children: showCrown ? /* @__PURE__ */ jsx(
789
+ Crown,
790
+ {
791
+ size,
792
+ strokeWidth: 2,
793
+ className: "text-yellow-400 fill-yellow-400 transition-colors duration-150",
794
+ "aria-hidden": "true"
795
+ }
796
+ ) : /* @__PURE__ */ jsx(
797
+ Zap,
798
+ {
799
+ size,
800
+ strokeWidth: 2,
801
+ className: cn(
802
+ "transition-colors duration-150",
803
+ isActive ? "text-accent-green fill-accent-green" : "text-fg-muted fill-none"
804
+ ),
805
+ "aria-hidden": "true"
806
+ }
807
+ )
615
808
  },
616
809
  i
617
810
  );