@scriptonita/chess-football-ui 0.1.1 → 0.2.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.cjs +351 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +354 -152
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -39,14 +39,18 @@ var PIECE_ICON = {
|
|
|
39
39
|
bishop: lucideReact.ChessBishop,
|
|
40
40
|
knight: lucideReact.ChessKnight
|
|
41
41
|
};
|
|
42
|
-
var
|
|
42
|
+
var EMBOSS_FILTER = {
|
|
43
|
+
white: "drop-shadow(0 -0.75px 0 rgba(255,255,255,0.65)) drop-shadow(0 1.25px 1px rgba(0,0,0,0.45))",
|
|
44
|
+
black: "drop-shadow(0 -0.5px 0 rgba(255,255,255,0.3)) drop-shadow(0 1.25px 1.5px rgba(0,0,0,0.8))"
|
|
45
|
+
};
|
|
46
|
+
var PieceIcon = ({ type, side }) => {
|
|
43
47
|
const Icon = PIECE_ICON[type] ?? lucideReact.ShieldQuestion;
|
|
44
48
|
const style = {
|
|
45
49
|
width: "clamp(16px, 7.4cqw, 44px)",
|
|
46
50
|
height: "clamp(16px, 7.4cqw, 44px)",
|
|
47
|
-
filter:
|
|
51
|
+
filter: EMBOSS_FILTER[side]
|
|
48
52
|
};
|
|
49
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Icon, { style, strokeWidth:
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Icon, { style, strokeWidth: 2, "aria-hidden": "true" });
|
|
50
54
|
};
|
|
51
55
|
var WHITE_PIECE_STYLE = {
|
|
52
56
|
background: "radial-gradient(circle at 30% 25%, #ffffff 0%, #f4f4f5 55%, #d4d4d8 100%)",
|
|
@@ -87,8 +91,16 @@ function GamePiece({ piece, isSelected, hasBall, onClick }) {
|
|
|
87
91
|
piece.hasMovedThisTurn && "opacity-70"
|
|
88
92
|
),
|
|
89
93
|
children: [
|
|
90
|
-
/* @__PURE__ */ jsxRuntime.jsx(PieceIcon, { type: piece.type }),
|
|
94
|
+
/* @__PURE__ */ jsxRuntime.jsx(PieceIcon, { type: piece.type, side: piece.side }),
|
|
91
95
|
hasBall && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full ring-2 ring-orange-400 pointer-events-none" }),
|
|
96
|
+
piece.hasMovedThisTurn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
97
|
+
"span",
|
|
98
|
+
{
|
|
99
|
+
"aria-hidden": "true",
|
|
100
|
+
className: "absolute top-0.5 right-0.5 w-[14px] h-[14px] rounded-full bg-fg-muted/80 flex items-center justify-center text-[9px] text-bg-primary font-bold leading-none pointer-events-none",
|
|
101
|
+
children: "\u2713"
|
|
102
|
+
}
|
|
103
|
+
),
|
|
92
104
|
isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full animate-pulse ring-2 ring-yellow-400 ring-offset-2 ring-offset-transparent" })
|
|
93
105
|
]
|
|
94
106
|
}
|
|
@@ -142,20 +154,29 @@ var Football = ({ className }) => {
|
|
|
142
154
|
}
|
|
143
155
|
);
|
|
144
156
|
};
|
|
157
|
+
var fallback = (key) => key;
|
|
158
|
+
var GameI18nContext = React.createContext(fallback);
|
|
159
|
+
function GameI18nProvider({ t, children }) {
|
|
160
|
+
return /* @__PURE__ */ jsxRuntime.jsx(GameI18nContext.Provider, { value: t, children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.MotionConfig, { reducedMotion: "user", children }) });
|
|
161
|
+
}
|
|
162
|
+
function useGameT() {
|
|
163
|
+
return React.useContext(GameI18nContext);
|
|
164
|
+
}
|
|
145
165
|
var COLS = 9;
|
|
146
166
|
var ROWS = 12;
|
|
147
167
|
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
148
|
-
function GameBoard({ userSide, showCoordinates = false, keyboardNav =
|
|
168
|
+
function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
|
|
169
|
+
const t = useGameT();
|
|
149
170
|
const {
|
|
150
171
|
boardState,
|
|
151
172
|
selectedPieceId,
|
|
152
173
|
setSelectedPieceId,
|
|
153
|
-
interactionMode,
|
|
154
174
|
setInteractionMode,
|
|
155
175
|
movePiece,
|
|
156
176
|
passBall
|
|
157
177
|
} = chunkJ2TBQPPC_cjs.useGameStore();
|
|
158
178
|
const [cursor, setCursor] = React.useState(null);
|
|
179
|
+
const [disambiguateAt, setDisambiguateAt] = React.useState(null);
|
|
159
180
|
const prevBallRef = React.useRef(boardState?.ball);
|
|
160
181
|
const prevBall = prevBallRef.current;
|
|
161
182
|
React.useEffect(() => {
|
|
@@ -164,19 +185,19 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
164
185
|
const validMoves = React.useMemo(() => {
|
|
165
186
|
if (!boardState) return [];
|
|
166
187
|
const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
167
|
-
if (sp &&
|
|
188
|
+
if (sp && !sp.hasMovedThisTurn) {
|
|
168
189
|
return chessFootballEngine.getValidMoves(sp, boardState);
|
|
169
190
|
}
|
|
170
191
|
return [];
|
|
171
|
-
}, [boardState, selectedPieceId
|
|
192
|
+
}, [boardState, selectedPieceId]);
|
|
172
193
|
const validPasses = React.useMemo(() => {
|
|
173
194
|
if (!boardState) return [];
|
|
174
195
|
const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
175
|
-
if (sp &&
|
|
196
|
+
if (sp && boardState.ball.holderId === sp.id) {
|
|
176
197
|
return chessFootballEngine.getValidPasses(sp, boardState);
|
|
177
198
|
}
|
|
178
199
|
return [];
|
|
179
|
-
}, [boardState, selectedPieceId
|
|
200
|
+
}, [boardState, selectedPieceId]);
|
|
180
201
|
if (!boardState) return null;
|
|
181
202
|
const lastMove = boardState.lastMove;
|
|
182
203
|
const ball = boardState.ball;
|
|
@@ -196,33 +217,40 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
196
217
|
);
|
|
197
218
|
pickupProportion = totalSteps > 0 ? Math.min(0.85, Math.max(0.15, stepsToBall / totalSteps)) : 0.5;
|
|
198
219
|
}
|
|
220
|
+
const ballCarrier = ball.holderId ? boardState.pieces.find((p) => p.id === ball.holderId) ?? null : null;
|
|
221
|
+
const isOffsideRisk = !!ballCarrier && ballCarrier.type !== "king" && userSide !== null && ballCarrier.side === userSide && boardState.turn === userSide && chessFootballEngine.isInEnemyArea(ballCarrier.pos, ballCarrier.side);
|
|
199
222
|
const handleSquareClick = (x, y) => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
223
|
+
const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
|
|
224
|
+
const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
|
|
225
|
+
if (isValidMove && isValidPass) {
|
|
226
|
+
setDisambiguateAt({ x, y });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (isValidMove && selectedPieceId) {
|
|
230
|
+
movePiece(selectedPieceId, { x, y });
|
|
231
|
+
setDisambiguateAt(null);
|
|
232
|
+
} else if (isValidPass) {
|
|
233
|
+
passBall({ x, y });
|
|
234
|
+
setDisambiguateAt(null);
|
|
235
|
+
} else {
|
|
236
|
+
setDisambiguateAt(null);
|
|
208
237
|
}
|
|
209
238
|
};
|
|
210
239
|
const handlePieceClick = (pieceId, x, y, e) => {
|
|
211
240
|
e.stopPropagation();
|
|
212
241
|
const pieceAt = boardState.pieces.find((p) => p.id === pieceId);
|
|
213
242
|
if (!pieceAt) return;
|
|
214
|
-
const
|
|
215
|
-
|
|
243
|
+
const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
|
|
244
|
+
const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
|
|
245
|
+
if (isValidMove || isValidPass) {
|
|
216
246
|
handleSquareClick(x, y);
|
|
217
247
|
} else if (boardState.turn === pieceAt.side && userSide === pieceAt.side) {
|
|
218
248
|
if (selectedPieceId === pieceAt.id) {
|
|
219
|
-
|
|
220
|
-
setInteractionMode(interactionMode === "move" ? "pass" : "move");
|
|
221
|
-
}
|
|
249
|
+
setSelectedPieceId(null);
|
|
222
250
|
} else {
|
|
223
251
|
setSelectedPieceId(pieceAt.id);
|
|
224
|
-
setInteractionMode("move");
|
|
225
252
|
}
|
|
253
|
+
setDisambiguateAt(null);
|
|
226
254
|
}
|
|
227
255
|
};
|
|
228
256
|
const defaultCursor = () => {
|
|
@@ -251,7 +279,6 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
251
279
|
case "ArrowUp":
|
|
252
280
|
step(0, 1);
|
|
253
281
|
break;
|
|
254
|
-
// visually up = higher rank (y grows toward the top)
|
|
255
282
|
case "ArrowDown":
|
|
256
283
|
step(0, -1);
|
|
257
284
|
break;
|
|
@@ -271,15 +298,25 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
271
298
|
}
|
|
272
299
|
case "m":
|
|
273
300
|
case "M":
|
|
274
|
-
|
|
301
|
+
if (disambiguateAt && selectedPieceId) {
|
|
302
|
+
movePiece(selectedPieceId, disambiguateAt);
|
|
303
|
+
setDisambiguateAt(null);
|
|
304
|
+
}
|
|
275
305
|
break;
|
|
276
306
|
case "p":
|
|
277
307
|
case "P":
|
|
278
|
-
|
|
308
|
+
if (disambiguateAt) {
|
|
309
|
+
passBall(disambiguateAt);
|
|
310
|
+
setDisambiguateAt(null);
|
|
311
|
+
}
|
|
279
312
|
break;
|
|
280
313
|
case "Escape":
|
|
281
|
-
|
|
282
|
-
|
|
314
|
+
if (disambiguateAt) {
|
|
315
|
+
setDisambiguateAt(null);
|
|
316
|
+
} else {
|
|
317
|
+
setSelectedPieceId(null);
|
|
318
|
+
setInteractionMode(null);
|
|
319
|
+
}
|
|
283
320
|
break;
|
|
284
321
|
}
|
|
285
322
|
};
|
|
@@ -289,9 +326,16 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
289
326
|
for (let x = 0; x < COLS; x++) {
|
|
290
327
|
const isGoalArea = x >= 2 && x <= 6 && (y >= 0 && y <= 1 || y >= 10 && y <= 11);
|
|
291
328
|
const isEven = (x + y) % 2 === 0;
|
|
292
|
-
const
|
|
329
|
+
const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
|
|
330
|
+
const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
|
|
331
|
+
const isAmbiguous = isValidMove && isValidPass;
|
|
332
|
+
const isLastMoveOrigin = lastMove?.from && lastMove.from.x === x && lastMove.from.y === y;
|
|
333
|
+
const isLastMoveDest = lastMove?.to && lastMove.to.x === x && lastMove.to.y === y;
|
|
334
|
+
const isLastMove = isLastMoveOrigin || isLastMoveDest;
|
|
335
|
+
const isEnemyGoalArea = userSide !== null && (x >= 2 && x <= 6) && (userSide === "white" ? y >= 10 && y <= 11 : y >= 0 && y <= 1);
|
|
293
336
|
const pieceAt = boardState.pieces.find((p) => p.pos.x === x && p.pos.y === y);
|
|
294
337
|
const isCursor = keyboardNav && cursor?.x === x && cursor?.y === y;
|
|
338
|
+
const isDisambiguateTarget = disambiguateAt?.x === x && disambiguateAt?.y === y;
|
|
295
339
|
squares.push(
|
|
296
340
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
297
341
|
"div",
|
|
@@ -299,16 +343,60 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
299
343
|
onClick: () => handleSquareClick(x, y),
|
|
300
344
|
className: cn(
|
|
301
345
|
"relative aspect-square w-full flex items-center justify-center cursor-pointer",
|
|
302
|
-
isEven ? "bg-
|
|
346
|
+
isEven ? "bg-field-green-1" : "bg-field-green-2",
|
|
303
347
|
isGoalArea && "bg-[#1a3a18] ring-1 ring-inset ring-emerald-500/30",
|
|
304
|
-
|
|
348
|
+
!isValidMove && !isValidPass && isLastMove && "bg-last-move-highlight/20",
|
|
349
|
+
// §8: pulsing warning ring on enemy goal area
|
|
350
|
+
isOffsideRisk && isEnemyGoalArea && !isValidMove && !isValidPass && "ring-2 ring-inset ring-warning/60",
|
|
351
|
+
isAmbiguous && "ring-[3px] ring-inset ring-yellow-400/90 bg-yellow-400/20",
|
|
352
|
+
!isAmbiguous && isValidMove && "ring-[3px] ring-inset ring-yellow-400/80 bg-yellow-400/15",
|
|
353
|
+
!isAmbiguous && isValidPass && "ring-[3px] ring-inset ring-sky-400/80 bg-sky-400/15"
|
|
305
354
|
),
|
|
306
355
|
children: [
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
356
|
+
!isAmbiguous && isValidMove && !pieceAt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-3 h-3 rounded-full bg-yellow-400/65" }),
|
|
357
|
+
!isAmbiguous && isValidPass && !pieceAt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-3 h-3 rounded-full bg-sky-400/65" }),
|
|
358
|
+
isAmbiguous && !pieceAt && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-0.5", children: [
|
|
359
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-yellow-400/80" }),
|
|
360
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-sky-400/80" })
|
|
361
|
+
] }),
|
|
362
|
+
isCursor && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 ring-2 ring-inset ring-white pointer-events-none z-20", "aria-hidden": "true" }),
|
|
363
|
+
isDisambiguateTarget && selectedPieceId && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
364
|
+
"div",
|
|
365
|
+
{
|
|
366
|
+
className: "absolute z-50 bottom-full mb-1 left-1/2 -translate-x-1/2 flex gap-1 bg-bg-secondary border border-border-subtle rounded-md p-1 shadow-xl pointer-events-auto whitespace-nowrap",
|
|
367
|
+
onClick: (e) => e.stopPropagation(),
|
|
368
|
+
children: [
|
|
369
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
370
|
+
"button",
|
|
371
|
+
{
|
|
372
|
+
className: "flex items-center gap-1 px-2 py-1 rounded text-[11px] font-semibold font-inter text-move-highlight bg-move-highlight/10 hover:bg-move-highlight/20 transition-colors",
|
|
373
|
+
onClick: () => {
|
|
374
|
+
movePiece(selectedPieceId, { x, y });
|
|
375
|
+
setDisambiguateAt(null);
|
|
376
|
+
},
|
|
377
|
+
children: [
|
|
378
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Move, { size: 11, strokeWidth: 2 }),
|
|
379
|
+
t("move")
|
|
380
|
+
]
|
|
381
|
+
}
|
|
382
|
+
),
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
384
|
+
"button",
|
|
385
|
+
{
|
|
386
|
+
className: "flex items-center gap-1 px-2 py-1 rounded text-[11px] font-semibold font-inter text-pass-highlight bg-pass-highlight/10 hover:bg-pass-highlight/20 transition-colors",
|
|
387
|
+
onClick: () => {
|
|
388
|
+
passBall({ x, y });
|
|
389
|
+
setDisambiguateAt(null);
|
|
390
|
+
},
|
|
391
|
+
children: [
|
|
392
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-3 h-3 inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(Football, {}) }),
|
|
393
|
+
t("pass")
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
)
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
)
|
|
312
400
|
]
|
|
313
401
|
},
|
|
314
402
|
`${x}-${y}`
|
|
@@ -322,7 +410,11 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
322
410
|
"div",
|
|
323
411
|
{
|
|
324
412
|
className: cn(
|
|
325
|
-
|
|
413
|
+
// §6: Mobile edge-to-edge — thin 2px margin, no border, no radius
|
|
414
|
+
"w-full mx-0.5 bg-[#1a3317] overflow-hidden",
|
|
415
|
+
// §5+§6: Desktop — centered, rounded, bordered, height-based max-width
|
|
416
|
+
"md:mx-auto md:rounded-xl md:shadow-2xl md:border-4 md:border-[#1a3317]",
|
|
417
|
+
"md:max-w-[min(700px,calc((100dvh_-_150px)*0.75))]",
|
|
326
418
|
keyboardNav && "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-green focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary"
|
|
327
419
|
),
|
|
328
420
|
...keyboardNav ? {
|
|
@@ -357,12 +449,7 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
357
449
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 pointer-events-none", children: [
|
|
358
450
|
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "sync", children: boardState.pieces.map((piece) => {
|
|
359
451
|
const isPickupCarrier = pickupCarrierId === piece.id;
|
|
360
|
-
const pieceTransition = isPickupCarrier ? { duration: PICKUP_DURATION, ease: "linear" } : {
|
|
361
|
-
type: "spring",
|
|
362
|
-
stiffness: 200,
|
|
363
|
-
damping: 25,
|
|
364
|
-
mass: 1
|
|
365
|
-
};
|
|
452
|
+
const pieceTransition = isPickupCarrier ? { duration: PICKUP_DURATION, ease: "linear" } : { type: "spring", stiffness: 200, damping: 25, mass: 1 };
|
|
366
453
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
367
454
|
framerMotion.motion.div,
|
|
368
455
|
{
|
|
@@ -406,23 +493,10 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
406
493
|
top: [`${prevY}%`, `${prevY}%`, `${prevY}%`, `${targetY}%`],
|
|
407
494
|
scale: [1, 1, 1.35, 1]
|
|
408
495
|
};
|
|
409
|
-
transition = {
|
|
410
|
-
duration: PICKUP_DURATION,
|
|
411
|
-
times: [0, pulseStart, pickupProportion, 1],
|
|
412
|
-
ease: "linear"
|
|
413
|
-
};
|
|
496
|
+
transition = { duration: PICKUP_DURATION, times: [0, pulseStart, pickupProportion, 1], ease: "linear" };
|
|
414
497
|
} else {
|
|
415
|
-
animate = {
|
|
416
|
-
|
|
417
|
-
top: `${targetY}%`,
|
|
418
|
-
scale: 1
|
|
419
|
-
};
|
|
420
|
-
transition = {
|
|
421
|
-
type: "spring",
|
|
422
|
-
stiffness: 300,
|
|
423
|
-
damping: 30,
|
|
424
|
-
mass: 0.5
|
|
425
|
-
};
|
|
498
|
+
animate = { left: `${targetX}%`, top: `${targetY}%`, scale: 1 };
|
|
499
|
+
transition = { type: "spring", stiffness: 300, damping: 30, mass: 0.5 };
|
|
426
500
|
}
|
|
427
501
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
428
502
|
framerMotion.motion.div,
|
|
@@ -442,13 +516,17 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
442
516
|
"ball"
|
|
443
517
|
);
|
|
444
518
|
})()
|
|
519
|
+
] }),
|
|
520
|
+
isOffsideRisk && /* @__PURE__ */ jsxRuntime.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: [
|
|
521
|
+
"\u26A0 ",
|
|
522
|
+
t("offsideWarning")
|
|
445
523
|
] })
|
|
446
524
|
] })
|
|
447
525
|
}
|
|
448
526
|
);
|
|
449
527
|
if (!showCoordinates) return board;
|
|
450
528
|
const ranksTopToBottom = [...chessFootballEngine.RANK_LABELS].reverse();
|
|
451
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full max-w-[540px] mx-auto", "aria-hidden": "false", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
529
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full max-w-[540px] md:max-w-none mx-auto", "aria-hidden": "false", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
452
530
|
"div",
|
|
453
531
|
{
|
|
454
532
|
className: "grid gap-1 select-none",
|
|
@@ -471,26 +549,10 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
471
549
|
) });
|
|
472
550
|
}
|
|
473
551
|
function FileRow({ files }) {
|
|
474
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-9 gap-[1px] px-[2px]", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
475
|
-
"span",
|
|
476
|
-
{
|
|
477
|
-
"aria-hidden": "true",
|
|
478
|
-
className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 uppercase tracking-[0.5px]",
|
|
479
|
-
children: f
|
|
480
|
-
},
|
|
481
|
-
f
|
|
482
|
-
)) });
|
|
552
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-9 gap-[1px] px-[2px]", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 uppercase tracking-[0.5px]", children: f }, f)) });
|
|
483
553
|
}
|
|
484
554
|
function RankColumn({ ranks }) {
|
|
485
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-rows-12 gap-[1px] py-[2px]", children: ranks.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
486
|
-
"span",
|
|
487
|
-
{
|
|
488
|
-
"aria-hidden": "true",
|
|
489
|
-
className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 tabular-nums",
|
|
490
|
-
children: r
|
|
491
|
-
},
|
|
492
|
-
r
|
|
493
|
-
)) });
|
|
555
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-rows-12 gap-[1px] py-[2px]", children: ranks.map((r) => /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 tabular-nums", children: r }, r)) });
|
|
494
556
|
}
|
|
495
557
|
function Avatar({ className, children }) {
|
|
496
558
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative w-9 h-9 rounded-full overflow-hidden shrink-0", className), children });
|
|
@@ -502,14 +564,6 @@ function AvatarImage({ src, alt, className }) {
|
|
|
502
564
|
function AvatarFallback({ className, children }) {
|
|
503
565
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("absolute inset-0 flex items-center justify-center text-xs font-semibold", className), children });
|
|
504
566
|
}
|
|
505
|
-
var fallback = (key) => key;
|
|
506
|
-
var GameI18nContext = React.createContext(fallback);
|
|
507
|
-
function GameI18nProvider({ t, children }) {
|
|
508
|
-
return /* @__PURE__ */ jsxRuntime.jsx(GameI18nContext.Provider, { value: t, children });
|
|
509
|
-
}
|
|
510
|
-
function useGameT() {
|
|
511
|
-
return React.useContext(GameI18nContext);
|
|
512
|
-
}
|
|
513
567
|
function ActionPoints({ total, remaining, size = 20, className, kingMustRelease }) {
|
|
514
568
|
const t = useGameT();
|
|
515
569
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-center gap-1.5", className), "aria-label": t("actionPointsAriaLabel", { remaining, total }), children: Array.from({ length: total }).map((_, i) => {
|
|
@@ -567,6 +621,7 @@ function Scoreboard({
|
|
|
567
621
|
const isMyTurn = turn === userSide;
|
|
568
622
|
const myKingMustRelease = isMyTurn && kingMustRelease === turn;
|
|
569
623
|
const rivalKingMustRelease = !isMyTurn && kingMustRelease === turn;
|
|
624
|
+
const maxAP = boardState.maxActionPoints ?? 5;
|
|
570
625
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-bg-secondary flex items-center justify-between px-5 py-2.5", children: [
|
|
571
626
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
572
627
|
PlayerInfo,
|
|
@@ -576,6 +631,7 @@ function Scoreboard({
|
|
|
576
631
|
avatarUrl: myAvatar,
|
|
577
632
|
isActive: isMyTurn,
|
|
578
633
|
actionPoints: isMyTurn ? actionPoints : 0,
|
|
634
|
+
maxActionPoints: maxAP,
|
|
579
635
|
kingMustRelease: myKingMustRelease,
|
|
580
636
|
align: "left"
|
|
581
637
|
}
|
|
@@ -593,13 +649,14 @@ function Scoreboard({
|
|
|
593
649
|
avatarUrl: rivalAvatar,
|
|
594
650
|
isActive: !isMyTurn,
|
|
595
651
|
actionPoints: !isMyTurn ? actionPoints : 0,
|
|
652
|
+
maxActionPoints: maxAP,
|
|
596
653
|
kingMustRelease: rivalKingMustRelease,
|
|
597
654
|
align: "right"
|
|
598
655
|
}
|
|
599
656
|
)
|
|
600
657
|
] });
|
|
601
658
|
}
|
|
602
|
-
function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, kingMustRelease, align }) {
|
|
659
|
+
function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, maxActionPoints, kingMustRelease, align }) {
|
|
603
660
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-2.5", align === "right" && "flex-row-reverse"), children: [
|
|
604
661
|
/* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: cn(
|
|
605
662
|
"w-9 h-9 border-2 transition-colors duration-300",
|
|
@@ -620,7 +677,7 @@ function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, kingMustRel
|
|
|
620
677
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
621
678
|
ActionPoints,
|
|
622
679
|
{
|
|
623
|
-
total:
|
|
680
|
+
total: maxActionPoints,
|
|
624
681
|
remaining: actionPoints,
|
|
625
682
|
size: 12,
|
|
626
683
|
kingMustRelease,
|
|
@@ -741,78 +798,36 @@ function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, on
|
|
|
741
798
|
function GameControls({ isMyTurn }) {
|
|
742
799
|
const {
|
|
743
800
|
boardState,
|
|
744
|
-
selectedPieceId,
|
|
745
|
-
interactionMode,
|
|
746
|
-
setInteractionMode,
|
|
747
801
|
endTurn
|
|
748
802
|
} = chunkJ2TBQPPC_cjs.useGameStore();
|
|
749
803
|
const t = useGameT();
|
|
750
804
|
const [showEndTurnConfirm, setShowEndTurnConfirm] = React.useState(false);
|
|
751
805
|
if (!boardState) return null;
|
|
752
806
|
const actionPoints = boardState.actionPoints;
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
807
|
+
const handleEndTurn = () => {
|
|
808
|
+
if (actionPoints >= 2) {
|
|
809
|
+
setShowEndTurnConfirm(true);
|
|
810
|
+
} else {
|
|
811
|
+
endTurn();
|
|
812
|
+
}
|
|
813
|
+
};
|
|
757
814
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-bg-secondary flex flex-col gap-3 px-5 pt-3 pb-5", children: [
|
|
758
|
-
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isMyTurn && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
759
|
-
framerMotion.motion.div,
|
|
760
|
-
{
|
|
761
|
-
initial: { opacity: 0, y: 8 },
|
|
762
|
-
animate: { opacity: 1, y: 0 },
|
|
763
|
-
exit: { opacity: 0, y: 8 },
|
|
764
|
-
className: "flex gap-2",
|
|
765
|
-
children: [
|
|
766
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
767
|
-
"button",
|
|
768
|
-
{
|
|
769
|
-
onClick: () => setInteractionMode("move"),
|
|
770
|
-
disabled: !canMove,
|
|
771
|
-
className: cn(
|
|
772
|
-
"flex-1 flex items-center justify-center gap-2 h-11 rounded-md border font-inter text-sm font-semibold",
|
|
773
|
-
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-move-highlight",
|
|
774
|
-
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
775
|
-
interactionMode === "move" ? "bg-move-highlight/10 border-move-highlight text-move-highlight" : "bg-bg-surface border-border-subtle text-move-highlight hover:bg-move-highlight/5"
|
|
776
|
-
),
|
|
777
|
-
"aria-pressed": interactionMode === "move",
|
|
778
|
-
children: [
|
|
779
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Move, { size: 16, strokeWidth: 2 }),
|
|
780
|
-
t("move")
|
|
781
|
-
]
|
|
782
|
-
}
|
|
783
|
-
),
|
|
784
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
785
|
-
"button",
|
|
786
|
-
{
|
|
787
|
-
onClick: () => setInteractionMode("pass"),
|
|
788
|
-
disabled: !canPass,
|
|
789
|
-
className: cn(
|
|
790
|
-
"flex-1 flex items-center justify-center gap-2 h-11 rounded-md border font-inter text-sm font-semibold",
|
|
791
|
-
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-pass-highlight",
|
|
792
|
-
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
793
|
-
interactionMode === "pass" ? "bg-pass-highlight/10 border-pass-highlight text-pass-highlight" : "bg-bg-surface border-border-subtle text-pass-highlight hover:bg-pass-highlight/5"
|
|
794
|
-
),
|
|
795
|
-
"aria-pressed": interactionMode === "pass",
|
|
796
|
-
children: [
|
|
797
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(Football, {}) }),
|
|
798
|
-
t("pass")
|
|
799
|
-
]
|
|
800
|
-
}
|
|
801
|
-
)
|
|
802
|
-
]
|
|
803
|
-
},
|
|
804
|
-
"action-btns"
|
|
805
|
-
) }),
|
|
806
815
|
isMyTurn && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
807
816
|
Button,
|
|
808
817
|
{
|
|
809
818
|
variant: "primary",
|
|
810
819
|
size: "default",
|
|
811
820
|
className: "w-full gap-2 tracking-[1.5px]",
|
|
812
|
-
onClick:
|
|
821
|
+
onClick: handleEndTurn,
|
|
813
822
|
children: [
|
|
814
823
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Flag, { size: 16, strokeWidth: 2 }),
|
|
815
|
-
t("endTurn")
|
|
824
|
+
t("endTurn"),
|
|
825
|
+
actionPoints >= 2 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("ml-1 font-inter text-xs font-normal opacity-70 tracking-normal"), children: [
|
|
826
|
+
"\xB7 ",
|
|
827
|
+
actionPoints,
|
|
828
|
+
" ",
|
|
829
|
+
t("actionPointsShort")
|
|
830
|
+
] })
|
|
816
831
|
]
|
|
817
832
|
}
|
|
818
833
|
),
|
|
@@ -1103,6 +1118,193 @@ function StaticGameBoard() {
|
|
|
1103
1118
|
)
|
|
1104
1119
|
] });
|
|
1105
1120
|
}
|
|
1121
|
+
function TurnBanner({ isMyTurn, waitingLabel, className }) {
|
|
1122
|
+
const t = useGameT();
|
|
1123
|
+
const { boardState } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
1124
|
+
if (!boardState) return null;
|
|
1125
|
+
const { actionPoints, maxActionPoints } = boardState;
|
|
1126
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1127
|
+
"div",
|
|
1128
|
+
{
|
|
1129
|
+
className: cn(
|
|
1130
|
+
"w-full h-9 flex items-center justify-between px-4 shrink-0 transition-colors duration-300",
|
|
1131
|
+
isMyTurn ? "bg-accent-green/15 border-b border-accent-green/30" : "bg-bg-surface border-b border-border-subtle",
|
|
1132
|
+
className
|
|
1133
|
+
),
|
|
1134
|
+
"aria-live": "polite",
|
|
1135
|
+
"aria-atomic": "true",
|
|
1136
|
+
children: [
|
|
1137
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1138
|
+
"span",
|
|
1139
|
+
{
|
|
1140
|
+
className: cn(
|
|
1141
|
+
"font-anton text-sm tracking-[1.5px] uppercase",
|
|
1142
|
+
isMyTurn ? "text-accent-green" : "text-fg-muted"
|
|
1143
|
+
),
|
|
1144
|
+
children: isMyTurn ? t("yourTurn") : waitingLabel ?? t("waitingRival")
|
|
1145
|
+
}
|
|
1146
|
+
),
|
|
1147
|
+
isMyTurn && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", "aria-label": t("actionPointsAriaLabel", { remaining: actionPoints, total: maxActionPoints }), children: [
|
|
1148
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { size: 12, className: "text-accent-green", "aria-hidden": "true" }),
|
|
1149
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-xs text-fg-secondary tabular-nums", children: [
|
|
1150
|
+
actionPoints,
|
|
1151
|
+
"/",
|
|
1152
|
+
maxActionPoints
|
|
1153
|
+
] })
|
|
1154
|
+
] })
|
|
1155
|
+
]
|
|
1156
|
+
}
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
var TOASTABLE = /* @__PURE__ */ new Set(["interception", "offside", "tackle"]);
|
|
1160
|
+
var TOAST_CONFIG = {
|
|
1161
|
+
interception: "bg-danger/20 border-danger/50 text-danger",
|
|
1162
|
+
offside: "bg-warning/20 border-warning/50 text-warning",
|
|
1163
|
+
tackle: "bg-warning/20 border-warning/50 text-warning"
|
|
1164
|
+
};
|
|
1165
|
+
function EventToast({ className }) {
|
|
1166
|
+
const t = useGameT();
|
|
1167
|
+
const { boardState } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
1168
|
+
const [toast, setToast] = React.useState(null);
|
|
1169
|
+
React.useEffect(() => {
|
|
1170
|
+
const lastMove = boardState?.lastMove;
|
|
1171
|
+
if (!lastMove) return;
|
|
1172
|
+
if (!TOASTABLE.has(lastMove.type)) return;
|
|
1173
|
+
setToast({ type: lastMove.type, key: String(lastMove.at) });
|
|
1174
|
+
}, [boardState?.lastMove?.at]);
|
|
1175
|
+
React.useEffect(() => {
|
|
1176
|
+
if (!toast) return;
|
|
1177
|
+
const timer = setTimeout(() => setToast(null), 2500);
|
|
1178
|
+
return () => clearTimeout(timer);
|
|
1179
|
+
}, [toast?.key]);
|
|
1180
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1181
|
+
"div",
|
|
1182
|
+
{
|
|
1183
|
+
role: "status",
|
|
1184
|
+
"aria-live": "polite",
|
|
1185
|
+
"aria-atomic": "true",
|
|
1186
|
+
className: cn("pointer-events-none flex justify-center", className),
|
|
1187
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: toast && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1188
|
+
framerMotion.motion.div,
|
|
1189
|
+
{
|
|
1190
|
+
initial: { opacity: 0, y: -8 },
|
|
1191
|
+
animate: { opacity: 1, y: 0 },
|
|
1192
|
+
exit: { opacity: 0, y: -8 },
|
|
1193
|
+
transition: { duration: 0.15 },
|
|
1194
|
+
className: cn(
|
|
1195
|
+
"px-4 py-2 rounded-full border font-inter text-sm font-semibold shadow-lg backdrop-blur-sm text-center",
|
|
1196
|
+
TOAST_CONFIG[toast.type]
|
|
1197
|
+
),
|
|
1198
|
+
children: t(`eventToast.${toast.type}`)
|
|
1199
|
+
},
|
|
1200
|
+
toast.key
|
|
1201
|
+
) })
|
|
1202
|
+
}
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
function MobileHistory({ className }) {
|
|
1206
|
+
const { boardState } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
1207
|
+
const [open, setOpen] = React.useState(false);
|
|
1208
|
+
const lastMove = boardState?.moveHistory?.at(-1);
|
|
1209
|
+
if (!lastMove) return null;
|
|
1210
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1211
|
+
/* @__PURE__ */ jsxRuntime.jsx(HistoryChip, { lastMove, onClick: () => setOpen(true), className }),
|
|
1212
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1213
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1214
|
+
framerMotion.motion.div,
|
|
1215
|
+
{
|
|
1216
|
+
initial: { opacity: 0 },
|
|
1217
|
+
animate: { opacity: 1 },
|
|
1218
|
+
exit: { opacity: 0 },
|
|
1219
|
+
transition: { duration: 0.2 },
|
|
1220
|
+
className: "fixed inset-0 z-40 bg-black/60 backdrop-blur-sm",
|
|
1221
|
+
onClick: () => setOpen(false),
|
|
1222
|
+
"aria-hidden": "true"
|
|
1223
|
+
},
|
|
1224
|
+
"backdrop"
|
|
1225
|
+
),
|
|
1226
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1227
|
+
framerMotion.motion.div,
|
|
1228
|
+
{
|
|
1229
|
+
initial: { y: "100%" },
|
|
1230
|
+
animate: { y: 0 },
|
|
1231
|
+
exit: { y: "100%" },
|
|
1232
|
+
transition: { type: "spring", stiffness: 300, damping: 32 },
|
|
1233
|
+
className: "fixed bottom-0 left-0 right-0 z-50 bg-bg-secondary rounded-t-2xl border-t border-border-subtle shadow-xl",
|
|
1234
|
+
role: "dialog",
|
|
1235
|
+
"aria-modal": "true",
|
|
1236
|
+
children: [
|
|
1237
|
+
/* @__PURE__ */ jsxRuntime.jsx(SheetHeader, { onClose: () => setOpen(false) }),
|
|
1238
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-6 max-h-[50dvh] overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(MoveHistory, { scrollable: false }) })
|
|
1239
|
+
]
|
|
1240
|
+
},
|
|
1241
|
+
"sheet"
|
|
1242
|
+
)
|
|
1243
|
+
] }) })
|
|
1244
|
+
] });
|
|
1245
|
+
}
|
|
1246
|
+
function SheetHeader({ onClose }) {
|
|
1247
|
+
const t = useGameT();
|
|
1248
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 pt-4 pb-3 border-b border-border-subtle", children: [
|
|
1249
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[10px] tracking-[0.5px] uppercase text-fg-muted", children: t("history.title") }),
|
|
1250
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1251
|
+
"button",
|
|
1252
|
+
{
|
|
1253
|
+
onClick: onClose,
|
|
1254
|
+
className: "w-8 h-8 flex items-center justify-center rounded-full bg-bg-surface hover:bg-bg-surface-elevated text-fg-muted hover:text-fg-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-green",
|
|
1255
|
+
"aria-label": "Close",
|
|
1256
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14 })
|
|
1257
|
+
}
|
|
1258
|
+
)
|
|
1259
|
+
] });
|
|
1260
|
+
}
|
|
1261
|
+
function HistoryChip({ lastMove, onClick, className }) {
|
|
1262
|
+
const t = useGameT();
|
|
1263
|
+
const toSq = chessFootballEngine.squareName(lastMove.to);
|
|
1264
|
+
let label;
|
|
1265
|
+
switch (lastMove.type) {
|
|
1266
|
+
case "pass":
|
|
1267
|
+
label = `\u2295 ${toSq}`;
|
|
1268
|
+
break;
|
|
1269
|
+
case "tackle":
|
|
1270
|
+
label = `\u2715 ${toSq}`;
|
|
1271
|
+
break;
|
|
1272
|
+
case "interception":
|
|
1273
|
+
label = `\u26D4 ${toSq}`;
|
|
1274
|
+
break;
|
|
1275
|
+
case "goal":
|
|
1276
|
+
label = `\u26BD GOL`;
|
|
1277
|
+
break;
|
|
1278
|
+
case "offside":
|
|
1279
|
+
label = `\u26A0 OFS`;
|
|
1280
|
+
break;
|
|
1281
|
+
default: {
|
|
1282
|
+
const pieceShort = t(`pieces.${chessFootballEngine.SHORT_KEY[lastMove.pieceType]}`);
|
|
1283
|
+
const fromSq = lastMove.from ? chessFootballEngine.squareName(lastMove.from) : "";
|
|
1284
|
+
label = fromSq ? `${pieceShort}${fromSq}\u2192${toSq}` : `${pieceShort}\u2192${toSq}`;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1288
|
+
"button",
|
|
1289
|
+
{
|
|
1290
|
+
onClick,
|
|
1291
|
+
className: cn(
|
|
1292
|
+
"flex items-center gap-1.5 px-3 py-1.5 rounded-full",
|
|
1293
|
+
"bg-bg-surface border border-border-subtle",
|
|
1294
|
+
"font-mono text-[11px] text-fg-secondary",
|
|
1295
|
+
"hover:text-fg-primary hover:bg-bg-surface-elevated active:scale-95",
|
|
1296
|
+
"transition-all duration-150",
|
|
1297
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-green",
|
|
1298
|
+
className
|
|
1299
|
+
),
|
|
1300
|
+
"aria-label": t("history.viewAll"),
|
|
1301
|
+
children: [
|
|
1302
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
|
|
1303
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-fg-muted text-[10px]", "aria-hidden": "true", children: "\u25BE" })
|
|
1304
|
+
]
|
|
1305
|
+
}
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1106
1308
|
|
|
1107
1309
|
exports.ActionPoints = ActionPoints;
|
|
1108
1310
|
exports.Avatar = Avatar;
|
|
@@ -1110,16 +1312,19 @@ exports.AvatarFallback = AvatarFallback;
|
|
|
1110
1312
|
exports.AvatarImage = AvatarImage;
|
|
1111
1313
|
exports.Button = Button;
|
|
1112
1314
|
exports.ConfirmDialog = ConfirmDialog;
|
|
1315
|
+
exports.EventToast = EventToast;
|
|
1113
1316
|
exports.Football = Football;
|
|
1114
1317
|
exports.GameBoard = GameBoard;
|
|
1115
1318
|
exports.GameControls = GameControls;
|
|
1116
1319
|
exports.GameI18nProvider = GameI18nProvider;
|
|
1117
1320
|
exports.GamePiece = GamePiece;
|
|
1118
1321
|
exports.MiniScoreboard = MiniScoreboard;
|
|
1322
|
+
exports.MobileHistory = MobileHistory;
|
|
1119
1323
|
exports.MoveHistory = MoveHistory;
|
|
1120
1324
|
exports.Scoreboard = Scoreboard;
|
|
1121
1325
|
exports.SelectedPieceDetail = SelectedPieceDetail;
|
|
1122
1326
|
exports.StaticGameBoard = StaticGameBoard;
|
|
1327
|
+
exports.TurnBanner = TurnBanner;
|
|
1123
1328
|
exports.cn = cn;
|
|
1124
1329
|
exports.useGameT = useGameT;
|
|
1125
1330
|
//# sourceMappingURL=index.cjs.map
|