@jonelhatwell/arcade-games 1.0.1 → 1.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 CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ MemoryGame: () => memorygame_default,
23
24
  SpaceDodger: () => spacedodger_default
24
25
  });
25
26
  module.exports = __toCommonJS(index_exports);
@@ -89,23 +90,34 @@ function Button({
89
90
 
90
91
  // src/components/games/spacedodger/index.tsx
91
92
  var import_jsx_runtime2 = require("react/jsx-runtime");
93
+ var config = {
94
+ canvasSize: {
95
+ width: 450,
96
+ height: 450
97
+ },
98
+ player: { x: 200, y: 350, width: 25, height: 50 }
99
+ };
92
100
  var SpaceDodger = () => {
93
101
  const canvasRef = (0, import_react.useRef)(null);
102
+ const scoreRef = (0, import_react.useRef)(0);
103
+ const lastBatchScoreRef = (0, import_react.useRef)(0);
94
104
  const [score, setScore] = (0, import_react.useState)(0);
95
105
  const [gameOver, setGameOver] = (0, import_react.useState)(false);
96
106
  const gameStateRef = (0, import_react.useRef)({
97
- player: { x: 200, y: 350, width: 30, height: 30 },
107
+ player: config.player,
98
108
  enemies: [],
99
109
  frame: 0,
100
110
  keys: {}
101
111
  });
102
112
  const resetGame = () => {
103
113
  gameStateRef.current = {
104
- player: { x: 200, y: 350, width: 30, height: 30 },
114
+ player: config.player,
105
115
  enemies: [],
106
116
  frame: 0,
107
117
  keys: {}
108
118
  };
119
+ scoreRef.current = 0;
120
+ lastBatchScoreRef.current = 0;
109
121
  setScore(0);
110
122
  setGameOver(false);
111
123
  };
@@ -123,60 +135,293 @@ var SpaceDodger = () => {
123
135
  };
124
136
  window.addEventListener("keydown", handleKeyDown);
125
137
  window.addEventListener("keyup", handleKeyUp);
138
+ const randomRadius = () => 10 + Math.random() * 15;
139
+ const getEnemySpeed = () => {
140
+ const base = 3;
141
+ const increase = Math.floor(scoreRef.current / 20) * 0.5;
142
+ return base + increase;
143
+ };
126
144
  let animationId;
127
145
  const gameLoop = () => {
128
146
  if (gameOver) return;
129
147
  state.frame++;
130
148
  ctx.fillStyle = "#000";
131
- ctx.fillRect(0, 0, 400, 400);
149
+ ctx.fillRect(0, 0, config.canvasSize.width, config.canvasSize.height);
132
150
  if (state.keys["ArrowLeft"] && state.player.x > 0) state.player.x -= 5;
133
- if (state.keys["ArrowRight"] && state.player.x < 370) state.player.x += 5;
151
+ if (state.keys["ArrowRight"] && state.player.x < config.canvasSize.width - state.player.width) state.player.x += 5;
134
152
  if (state.keys["ArrowUp"] && state.player.y > 0) state.player.y -= 5;
135
- if (state.keys["ArrowDown"] && state.player.y < 370) state.player.y += 5;
153
+ if (state.keys["ArrowDown"] && state.player.y < config.canvasSize.height - state.player.height) state.player.y += 5;
136
154
  ctx.fillStyle = "#0ea5e9";
137
- ctx.fillRect(state.player.x, state.player.y, state.player.width, state.player.height);
155
+ ctx.fillRect(
156
+ state.player.x,
157
+ state.player.y,
158
+ state.player.width,
159
+ state.player.height
160
+ );
138
161
  if (state.frame % 40 === 0) {
139
- state.enemies.push({ x: Math.random() * 370, y: -20, width: 30, height: 30 });
162
+ const radius = randomRadius();
163
+ state.enemies.push({
164
+ x: radius + Math.random() * (config.canvasSize.width - radius * 2),
165
+ y: -radius,
166
+ radius
167
+ });
168
+ }
169
+ if (scoreRef.current > 0 && scoreRef.current % 20 === 0 && lastBatchScoreRef.current !== scoreRef.current) {
170
+ lastBatchScoreRef.current = scoreRef.current;
171
+ const batchCount = 2 + Math.floor(Math.random() * 3);
172
+ for (let i = 0; i < batchCount; i++) {
173
+ const radius = randomRadius();
174
+ state.enemies.push({
175
+ x: radius + Math.random() * (config.canvasSize.width - radius * 2),
176
+ y: -Math.random() * 100,
177
+ radius
178
+ });
179
+ }
140
180
  }
141
181
  state.enemies = state.enemies.filter((enemy) => {
142
- enemy.y += 3;
182
+ enemy.y += getEnemySpeed();
143
183
  ctx.fillStyle = "#ef4444";
144
- ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
145
- if (enemy.x < state.player.x + state.player.width && enemy.x + enemy.width > state.player.x && enemy.y < state.player.y + state.player.height && enemy.y + enemy.height > state.player.y) {
184
+ ctx.beginPath();
185
+ ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI * 2);
186
+ ctx.fill();
187
+ const closestX = Math.max(
188
+ state.player.x,
189
+ Math.min(enemy.x, state.player.x + state.player.width)
190
+ );
191
+ const closestY = Math.max(
192
+ state.player.y,
193
+ Math.min(enemy.y, state.player.y + state.player.height)
194
+ );
195
+ const dx = enemy.x - closestX;
196
+ const dy = enemy.y - closestY;
197
+ if (dx * dx + dy * dy < enemy.radius * enemy.radius) {
146
198
  setGameOver(true);
147
199
  return false;
148
200
  }
149
- if (enemy.y > 400) {
150
- setScore((s) => s + 1);
201
+ if (enemy.y - enemy.radius > config.canvasSize.height) {
202
+ setScore((s) => {
203
+ scoreRef.current = s + 1;
204
+ return s + 1;
205
+ });
151
206
  return false;
152
207
  }
153
208
  return true;
154
209
  });
155
210
  animationId = requestAnimationFrame(gameLoop);
156
211
  };
157
- if (!gameOver) gameLoop();
212
+ gameLoop();
158
213
  return () => {
159
214
  cancelAnimationFrame(animationId);
160
215
  window.removeEventListener("keydown", handleKeyDown);
161
216
  window.removeEventListener("keyup", handleKeyUp);
162
217
  };
163
218
  }, [gameOver]);
164
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4 p-4", children: [
219
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative flex flex-col items-center gap-4 p-4", children: [
165
220
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-xl font-bold", children: [
166
221
  "Score: ",
167
222
  score
168
223
  ] }),
169
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("canvas", { ref: canvasRef, width: "400", height: "400", className: "border-2 border-gray-300" }),
170
- gameOver && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center", children: [
171
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-red-500 mb-2", children: "Game Over!" }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
225
+ "canvas",
226
+ {
227
+ ref: canvasRef,
228
+ width: config.canvasSize.width,
229
+ height: config.canvasSize.height,
230
+ className: "border-2 border-gray-300"
231
+ }
232
+ ),
233
+ gameOver && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/60", children: [
234
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-red-500 mb-3", children: "Game Over" }),
172
235
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { onClick: resetGame, children: "Play Again" })
173
236
  ] }),
174
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-gray-600", children: "Use arrow keys to move" })
237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-sm text-foreground", children: "Use arrow keys to move" })
175
238
  ] });
176
239
  };
177
240
  var spacedodger_default = SpaceDodger;
241
+
242
+ // src/components/games/memorygame/index.tsx
243
+ var import_react2 = require("react");
244
+ var import_jsx_runtime3 = require("react/jsx-runtime");
245
+ var config2 = {
246
+ canvasSize: { width: 450, height: 450 },
247
+ baseHideTime: 3e3,
248
+ numberRadius: 30
249
+ };
250
+ var MemoryGame = () => {
251
+ const canvasRef = (0, import_react2.useRef)(null);
252
+ const [level, setLevel] = (0, import_react2.useState)(1);
253
+ const [gameOver, setGameOver] = (0, import_react2.useState)(false);
254
+ const [message, setMessage] = (0, import_react2.useState)("");
255
+ const [showOverlay, setShowOverlay] = (0, import_react2.useState)({
256
+ visible: false,
257
+ text: ""
258
+ });
259
+ const gameStateRef = (0, import_react2.useRef)({
260
+ numbers: [],
261
+ sequence: [],
262
+ currentIndex: 0,
263
+ level: 1,
264
+ showNumbers: true,
265
+ phase: "memorize",
266
+ revealed: /* @__PURE__ */ new Set()
267
+ });
268
+ const getNumberCount = (lvl) => Math.min(4 + lvl, 9);
269
+ const getHideTime = (lvl) => Math.max(500, config2.baseHideTime - Math.floor((lvl - 1) / 1) * 500);
270
+ const generateNumbers = (lvl) => {
271
+ const count = getNumberCount(lvl);
272
+ const numbers = [];
273
+ const sequence = [];
274
+ const padding = config2.numberRadius * 2.5;
275
+ for (let i = 0; i < count; i++) {
276
+ let x, y, overlap;
277
+ let attempts = 0;
278
+ do {
279
+ overlap = false;
280
+ x = padding + Math.random() * (config2.canvasSize.width - padding * 2);
281
+ y = padding + Math.random() * (config2.canvasSize.height - padding * 2);
282
+ for (const num of numbers) {
283
+ const dx = x - num.x;
284
+ const dy = y - num.y;
285
+ if (Math.sqrt(dx * dx + dy * dy) < config2.numberRadius * 2.5) {
286
+ overlap = true;
287
+ break;
288
+ }
289
+ }
290
+ attempts++;
291
+ } while (overlap && attempts < 100);
292
+ numbers.push({ x, y, value: i + 1, radius: config2.numberRadius });
293
+ sequence.push(i + 1);
294
+ }
295
+ return { numbers, sequence };
296
+ };
297
+ const startLevel = (lvl) => {
298
+ const { numbers, sequence } = generateNumbers(lvl);
299
+ gameStateRef.current = {
300
+ numbers,
301
+ sequence,
302
+ currentIndex: 0,
303
+ level: lvl,
304
+ showNumbers: true,
305
+ phase: "memorize",
306
+ revealed: /* @__PURE__ */ new Set()
307
+ };
308
+ setMessage("Memorize the sequence!");
309
+ setTimeout(() => {
310
+ gameStateRef.current.showNumbers = false;
311
+ gameStateRef.current.phase = "recall";
312
+ setMessage("Click the numbers in order!");
313
+ }, getHideTime(lvl));
314
+ };
315
+ const resetGame = () => {
316
+ setLevel(1);
317
+ setGameOver(false);
318
+ setShowOverlay({ visible: false, text: "" });
319
+ startLevel(1);
320
+ };
321
+ const handleCanvasClick = (e) => {
322
+ if (gameOver || gameStateRef.current.phase !== "recall") return;
323
+ const canvas = canvasRef.current;
324
+ if (!canvas) return;
325
+ const rect = canvas.getBoundingClientRect();
326
+ const x = e.clientX - rect.left;
327
+ const y = e.clientY - rect.top;
328
+ const state = gameStateRef.current;
329
+ for (const num of state.numbers) {
330
+ const distance = Math.hypot(x - num.x, y - num.y);
331
+ if (distance <= num.radius) {
332
+ const expected = state.sequence[state.currentIndex];
333
+ if (num.value === expected) {
334
+ state.revealed.add(num.value);
335
+ state.currentIndex++;
336
+ if (state.currentIndex === state.sequence.length) {
337
+ const nextLevel = level + 1;
338
+ setLevel(nextLevel);
339
+ state.phase = "transition";
340
+ setShowOverlay({ visible: true, text: `Get Ready! Level ${nextLevel}` });
341
+ setTimeout(() => {
342
+ setShowOverlay({ visible: false, text: "" });
343
+ startLevel(nextLevel);
344
+ }, 1500);
345
+ }
346
+ } else {
347
+ setGameOver(true);
348
+ setMessage(`Wrong! Clicked ${num.value}, expected ${expected}`);
349
+ }
350
+ break;
351
+ }
352
+ }
353
+ };
354
+ (0, import_react2.useEffect)(() => startLevel(1), []);
355
+ (0, import_react2.useEffect)(() => {
356
+ const canvas = canvasRef.current;
357
+ if (!canvas) return;
358
+ const ctx = canvas.getContext("2d");
359
+ if (!ctx) return;
360
+ let animationId;
361
+ const draw = () => {
362
+ const state = gameStateRef.current;
363
+ ctx.fillStyle = "#000";
364
+ ctx.fillRect(0, 0, config2.canvasSize.width, config2.canvasSize.height);
365
+ state.numbers.forEach((num, idx) => {
366
+ const isNext = state.phase === "recall" && level === 1 && idx === state.currentIndex;
367
+ ctx.fillStyle = isNext ? "#22c55e" : "#3b82f6";
368
+ ctx.beginPath();
369
+ ctx.arc(num.x, num.y, num.radius, 0, Math.PI * 2);
370
+ ctx.fill();
371
+ if (state.showNumbers || state.phase === "transition" || state.revealed.has(num.value)) {
372
+ ctx.fillStyle = "#fff";
373
+ ctx.font = "bold 24px sans-serif";
374
+ ctx.textAlign = "center";
375
+ ctx.textBaseline = "middle";
376
+ ctx.fillText(num.value.toString(), num.x, num.y);
377
+ }
378
+ });
379
+ if (!gameOver) animationId = requestAnimationFrame(draw);
380
+ };
381
+ draw();
382
+ return () => cancelAnimationFrame(animationId);
383
+ }, [gameOver, level]);
384
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative flex flex-col items-center gap-4 p-4", children: [
385
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-8 text-xl font-bold", children: [
386
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
387
+ "Level: ",
388
+ level
389
+ ] }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
391
+ "Numbers: ",
392
+ getNumberCount(level)
393
+ ] })
394
+ ] }),
395
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-lg font-semibold text-blue-400 h-6", children: message }),
396
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
397
+ "canvas",
398
+ {
399
+ ref: canvasRef,
400
+ width: config2.canvasSize.width,
401
+ height: config2.canvasSize.height,
402
+ className: "border-2 border-gray-300 cursor-pointer",
403
+ onClick: handleCanvasClick
404
+ }
405
+ ),
406
+ gameOver && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/60", children: [
407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xl font-bold text-red-500 mb-2", children: "Game Over" }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "text-lg text-white mb-4", children: [
409
+ "Reached Level ",
410
+ level
411
+ ] }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { onClick: resetGame, children: "Play Again" })
413
+ ] }),
414
+ showOverlay.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/60", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-2xl font-bold text-yellow-400", children: showOverlay.text }) }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-sm text-center text-foreground max-w-md", children: [
416
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: "Memorize the numbers, then click them in sequence (1, 2, 3...)" }),
417
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-1 text-xs text-gray-400", children: "Green circle = next number to click (level 1 only)" })
418
+ ] })
419
+ ] });
420
+ };
421
+ var memorygame_default = MemoryGame;
178
422
  // Annotate the CommonJS export names for ESM import in node:
179
423
  0 && (module.exports = {
424
+ MemoryGame,
180
425
  SpaceDodger
181
426
  });
182
427
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/games/spacedodger/index.tsx","../src/components/ui/button.tsx","../src/lib/utils.ts"],"sourcesContent":["import \"./index.css\";\r\nexport { default as SpaceDodger } from \"./components/games/spacedodger\";","\r\nimport React, { useState, useEffect, useRef } from 'react';\r\nimport { Button } from '@/components/ui/button';\r\n\r\n\r\ninterface Position {\r\n x: number;\r\n y: number;\r\n}\r\n\r\n\r\ninterface Enemy extends Position {\r\n width: number;\r\n height: number;\r\n}\r\n\r\ninterface Player extends Position {\r\n width: number;\r\n height: number;\r\n}\r\n\r\n\r\ninterface SpaceDodgerState {\r\n player: Player;\r\n enemies: Enemy[];\r\n frame: number;\r\n keys: Record<string, boolean>;\r\n}\r\n\r\nconst SpaceDodger: React.FC = () => {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n const [score, setScore] = useState<number>(0);\r\n const [gameOver, setGameOver] = useState<boolean>(false);\r\n const gameStateRef = useRef<SpaceDodgerState>({\r\n player: { x: 200, y: 350, width: 30, height: 30 },\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n });\r\n\r\n const resetGame = (): void => {\r\n gameStateRef.current = {\r\n player: { x: 200, y: 350, width: 30, height: 30 },\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n };\r\n setScore(0);\r\n setGameOver(false);\r\n };\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n const state = gameStateRef.current;\r\n\r\n const handleKeyDown = (e: KeyboardEvent): void => { \r\n state.keys[e.key] = true; \r\n };\r\n const handleKeyUp = (e: KeyboardEvent): void => { \r\n state.keys[e.key] = false; \r\n };\r\n window.addEventListener('keydown', handleKeyDown);\r\n window.addEventListener('keyup', handleKeyUp);\r\n\r\n let animationId: number;\r\n const gameLoop = (): void => {\r\n if (gameOver) return;\r\n \r\n state.frame++;\r\n ctx.fillStyle = '#000';\r\n ctx.fillRect(0, 0, 400, 400);\r\n\r\n if (state.keys['ArrowLeft'] && state.player.x > 0) state.player.x -= 5;\r\n if (state.keys['ArrowRight'] && state.player.x < 370) state.player.x += 5;\r\n if (state.keys['ArrowUp'] && state.player.y > 0) state.player.y -= 5;\r\n if (state.keys['ArrowDown'] && state.player.y < 370) state.player.y += 5;\r\n\r\n ctx.fillStyle = '#0ea5e9';\r\n ctx.fillRect(state.player.x, state.player.y, state.player.width, state.player.height);\r\n\r\n if (state.frame % 40 === 0) {\r\n state.enemies.push({ x: Math.random() * 370, y: -20, width: 30, height: 30 });\r\n }\r\n\r\n state.enemies = state.enemies.filter(enemy => {\r\n enemy.y += 3;\r\n ctx.fillStyle = '#ef4444';\r\n ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);\r\n\r\n if (enemy.x < state.player.x + state.player.width &&\r\n enemy.x + enemy.width > state.player.x &&\r\n enemy.y < state.player.y + state.player.height &&\r\n enemy.y + enemy.height > state.player.y) {\r\n setGameOver(true);\r\n return false;\r\n }\r\n\r\n if (enemy.y > 400) {\r\n setScore(s => s + 1);\r\n return false;\r\n }\r\n return true;\r\n });\r\n\r\n animationId = requestAnimationFrame(gameLoop);\r\n };\r\n \r\n if (!gameOver) gameLoop();\r\n\r\n return () => {\r\n cancelAnimationFrame(animationId);\r\n window.removeEventListener('keydown', handleKeyDown);\r\n window.removeEventListener('keyup', handleKeyUp);\r\n };\r\n }, [gameOver]);\r\n\r\n return (\r\n <div className=\"flex flex-col items-center gap-4 p-4\">\r\n <div className=\"text-xl font-bold\">Score: {score}</div>\r\n <canvas ref={canvasRef} width=\"400\" height=\"400\" className=\"border-2 border-gray-300\" />\r\n {gameOver && (\r\n <div className=\"text-center\">\r\n <p className=\"text-xl font-bold text-red-500 mb-2\">Game Over!</p>\r\n <Button onClick={resetGame}>Play Again</Button>\r\n </div>\r\n )}\r\n <p className=\"text-sm text-gray-600\">Use arrow keys to move</p>\r\n </div>\r\n );\r\n};\r\n\r\nexport default SpaceDodger;","import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","import { clsx, type ClassValue } from \"clsx\"\r\nimport { twMerge } from \"tailwind-merge\"\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}"],"mappings":"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","names":["import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/games/spacedodger/index.tsx","../src/components/ui/button.tsx","../src/lib/utils.ts","../src/components/games/memorygame/index.tsx"],"sourcesContent":["import \"./index.css\";\r\nexport { default as SpaceDodger } from \"./components/games/spacedodger\";\r\nexport { default as MemoryGame } from \"./components/games/memorygame\";","import React, { useEffect, useRef, useState } from 'react';\r\nimport { Button } from '@/components/ui/button';\r\n\r\ninterface Position {\r\n x: number;\r\n y: number;\r\n}\r\n\r\ninterface Enemy extends Position {\r\n radius: number;\r\n}\r\n\r\ninterface Player extends Position {\r\n width: number;\r\n height: number;\r\n}\r\n\r\ninterface SpaceDodgerState {\r\n player: Player;\r\n enemies: Enemy[];\r\n frame: number;\r\n keys: Record<string, boolean>;\r\n}\r\n\r\n\r\nconst config = {\r\n canvasSize : { \r\n width: 450, \r\n height: 450 \r\n },\r\n player: { x: 200, y: 350, width: 25, height: 50 }\r\n}\r\n\r\nconst SpaceDodger: React.FC = () => {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n const scoreRef = useRef(0);\r\n const lastBatchScoreRef = useRef(0);\r\n\r\n const [score, setScore] = useState(0);\r\n const [gameOver, setGameOver] = useState(false);\r\n\r\n const gameStateRef = useRef<SpaceDodgerState>({\r\n player: config.player,\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n });\r\n\r\n const resetGame = () => {\r\n gameStateRef.current = {\r\n player: config.player,\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n };\r\n scoreRef.current = 0;\r\n lastBatchScoreRef.current = 0;\r\n setScore(0);\r\n setGameOver(false);\r\n };\r\n\r\n\r\n \r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n const state = gameStateRef.current;\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n state.keys[e.key] = true;\r\n };\r\n const handleKeyUp = (e: KeyboardEvent) => {\r\n state.keys[e.key] = false;\r\n };\r\n\r\n window.addEventListener('keydown', handleKeyDown);\r\n window.addEventListener('keyup', handleKeyUp);\r\n\r\n const randomRadius = () => 10 + Math.random() * 15;\r\n\r\n const getEnemySpeed = () => {\r\n const base = 3;\r\n const increase = Math.floor(scoreRef.current / 20) * 0.5;\r\n return base + increase;\r\n };\r\n\r\n let animationId: number;\r\n\r\n const gameLoop = () => {\r\n if (gameOver) return;\r\n\r\n state.frame++;\r\n\r\n ctx.fillStyle = '#000';\r\n ctx.fillRect(0, 0, config.canvasSize.width, config.canvasSize.height);\r\n\r\n // Player movement\r\n if (state.keys['ArrowLeft'] && state.player.x > 0) state.player.x -= 5;\r\n if (state.keys['ArrowRight'] && state.player.x < config.canvasSize.width - state.player.width) state.player.x += 5;\r\n if (state.keys['ArrowUp'] && state.player.y > 0) state.player.y -= 5;\r\n if (state.keys['ArrowDown'] && state.player.y < config.canvasSize.height - state.player.height) state.player.y += 5;\r\n\r\n // Draw player\r\n ctx.fillStyle = '#0ea5e9';\r\n ctx.fillRect(\r\n state.player.x,\r\n state.player.y,\r\n state.player.width,\r\n state.player.height\r\n );\r\n\r\n // Normal enemy spawn\r\n if (state.frame % 40 === 0) {\r\n const radius = randomRadius();\r\n state.enemies.push({\r\n x: radius + Math.random() * (config.canvasSize.width - radius * 2),\r\n y: -radius,\r\n radius,\r\n });\r\n }\r\n\r\n // Extra batch every 20 score\r\n if (\r\n scoreRef.current > 0 &&\r\n scoreRef.current % 20 === 0 &&\r\n lastBatchScoreRef.current !== scoreRef.current\r\n ) {\r\n lastBatchScoreRef.current = scoreRef.current;\r\n const batchCount = 2 + Math.floor(Math.random() * 3);\r\n\r\n for (let i = 0; i < batchCount; i++) {\r\n const radius = randomRadius();\r\n state.enemies.push({\r\n x: radius + Math.random() * (config.canvasSize.width - radius * 2),\r\n y: -Math.random() * 100,\r\n radius\r\n });\r\n }\r\n }\r\n\r\n // Enemies update\r\n state.enemies = state.enemies.filter(enemy => {\r\n enemy.y += getEnemySpeed();\r\n\r\n // Draw enemy (circle)\r\n ctx.fillStyle = '#ef4444';\r\n ctx.beginPath();\r\n ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI * 2);\r\n ctx.fill();\r\n\r\n // Circle vs rectangle collision\r\n const closestX = Math.max(\r\n state.player.x,\r\n Math.min(enemy.x, state.player.x + state.player.width)\r\n );\r\n const closestY = Math.max(\r\n state.player.y,\r\n Math.min(enemy.y, state.player.y + state.player.height)\r\n );\r\n\r\n const dx = enemy.x - closestX;\r\n const dy = enemy.y - closestY;\r\n\r\n if (dx * dx + dy * dy < enemy.radius * enemy.radius) {\r\n setGameOver(true);\r\n return false;\r\n }\r\n\r\n // Passed screen\r\n if (enemy.y - enemy.radius > config.canvasSize.height) {\r\n setScore(s => {\r\n scoreRef.current = s + 1;\r\n return s + 1;\r\n });\r\n return false;\r\n }\r\n\r\n return true;\r\n });\r\n\r\n animationId = requestAnimationFrame(gameLoop);\r\n };\r\n\r\n gameLoop();\r\n\r\n return () => {\r\n cancelAnimationFrame(animationId);\r\n window.removeEventListener('keydown', handleKeyDown);\r\n window.removeEventListener('keyup', handleKeyUp);\r\n };\r\n }, [gameOver]);\r\n\r\n return (\r\n <div className=\"relative flex flex-col items-center gap-4 p-4\">\r\n <div className=\"text-xl font-bold\">Score: {score}</div>\r\n <canvas\r\n ref={canvasRef}\r\n width={config.canvasSize.width}\r\n height={config.canvasSize.height}\r\n className=\"border-2 border-gray-300\"\r\n />\r\n\r\n {gameOver && (\r\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-black/60\">\r\n <p className=\"text-xl font-bold text-red-500 mb-3\">Game Over</p>\r\n <Button onClick={resetGame}>Play Again</Button>\r\n </div>\r\n )}\r\n\r\n <div className=\"text-sm text-foreground\">Use arrow keys to move</div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default SpaceDodger;","import * as React from \"react\"\r\nimport { Slot } from \"@radix-ui/react-slot\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nconst buttonVariants = cva(\r\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n destructive:\r\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\r\n outline:\r\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\r\n secondary:\r\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n ghost:\r\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\r\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\r\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\r\n icon: \"size-9\",\r\n \"icon-sm\": \"size-8\",\r\n \"icon-lg\": \"size-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n }\r\n)\r\n\r\nfunction Button({\r\n className,\r\n variant = \"default\",\r\n size = \"default\",\r\n asChild = false,\r\n ...props\r\n}: React.ComponentProps<\"button\"> &\r\n VariantProps<typeof buttonVariants> & {\r\n asChild?: boolean\r\n }) {\r\n const Comp = asChild ? Slot : \"button\"\r\n\r\n return (\r\n <Comp\r\n data-slot=\"button\"\r\n data-variant={variant}\r\n data-size={size}\r\n className={cn(buttonVariants({ variant, size, className }))}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport { Button, buttonVariants }\r\n","import { clsx, type ClassValue } from \"clsx\"\r\nimport { twMerge } from \"tailwind-merge\"\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}","import React, { useEffect, useRef, useState } from 'react';\r\nimport { Button } from '@/components/ui/button';\r\n\r\ninterface NumberItem {\r\n x: number;\r\n y: number;\r\n value: number;\r\n radius: number;\r\n}\r\n\r\ntype GamePhase = 'memorize' | 'recall' | 'transition';\r\n\r\ninterface GameState {\r\n numbers: NumberItem[];\r\n sequence: number[];\r\n currentIndex: number;\r\n level: number;\r\n showNumbers: boolean;\r\n phase: GamePhase;\r\n revealed: Set<number>;\r\n}\r\n\r\nconst config = {\r\n canvasSize: { width: 450, height: 450 },\r\n baseHideTime: 3000,\r\n numberRadius: 30,\r\n};\r\n\r\nconst MemoryGame: React.FC = () => {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n const [level, setLevel] = useState(1);\r\n const [gameOver, setGameOver] = useState(false);\r\n const [message, setMessage] = useState('');\r\n const [showOverlay, setShowOverlay] = useState<{ visible: boolean; text: string }>({\r\n visible: false,\r\n text: '',\r\n });\r\n const gameStateRef = useRef<GameState>({\r\n numbers: [],\r\n sequence: [],\r\n currentIndex: 0,\r\n level: 1,\r\n showNumbers: true,\r\n phase: 'memorize',\r\n revealed: new Set(),\r\n });\r\n\r\n // Max numbers = 9\r\n const getNumberCount = (lvl: number) => Math.min(4 + lvl, 9);\r\n\r\n // Hide time decreases as level increases, min 500ms\r\n const getHideTime = (lvl: number) =>\r\n Math.max(500, config.baseHideTime - Math.floor((lvl - 1) / 1) * 500);\r\n\r\n const generateNumbers = (lvl: number) => {\r\n const count = getNumberCount(lvl);\r\n const numbers: NumberItem[] = [];\r\n const sequence: number[] = [];\r\n const padding = config.numberRadius * 2.5;\r\n\r\n for (let i = 0; i < count; i++) {\r\n let x: number, y: number, overlap: boolean;\r\n let attempts = 0;\r\n\r\n do {\r\n overlap = false;\r\n x = padding + Math.random() * (config.canvasSize.width - padding * 2);\r\n y = padding + Math.random() * (config.canvasSize.height - padding * 2);\r\n\r\n for (const num of numbers) {\r\n const dx = x - num.x;\r\n const dy = y - num.y;\r\n if (Math.sqrt(dx * dx + dy * dy) < config.numberRadius * 2.5) {\r\n overlap = true;\r\n break;\r\n }\r\n }\r\n attempts++;\r\n } while (overlap && attempts < 100);\r\n\r\n numbers.push({ x, y, value: i + 1, radius: config.numberRadius });\r\n sequence.push(i + 1);\r\n }\r\n\r\n return { numbers, sequence };\r\n };\r\n\r\n const startLevel = (lvl: number) => {\r\n const { numbers, sequence } = generateNumbers(lvl);\r\n gameStateRef.current = {\r\n numbers,\r\n sequence,\r\n currentIndex: 0,\r\n level: lvl,\r\n showNumbers: true,\r\n phase: 'memorize',\r\n revealed: new Set(),\r\n };\r\n setMessage('Memorize the sequence!');\r\n\r\n setTimeout(() => {\r\n gameStateRef.current.showNumbers = false;\r\n gameStateRef.current.phase = 'recall';\r\n setMessage('Click the numbers in order!');\r\n }, getHideTime(lvl));\r\n };\r\n\r\n const resetGame = () => {\r\n setLevel(1);\r\n setGameOver(false);\r\n setShowOverlay({ visible: false, text: '' });\r\n startLevel(1);\r\n };\r\n\r\n const handleCanvasClick = (e: React.MouseEvent<HTMLCanvasElement>) => {\r\n if (gameOver || gameStateRef.current.phase !== 'recall') return;\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const rect = canvas.getBoundingClientRect();\r\n const x = e.clientX - rect.left;\r\n const y = e.clientY - rect.top;\r\n const state = gameStateRef.current;\r\n\r\n for (const num of state.numbers) {\r\n const distance = Math.hypot(x - num.x, y - num.y);\r\n if (distance <= num.radius) {\r\n const expected = state.sequence[state.currentIndex];\r\n if (num.value === expected) {\r\n state.revealed.add(num.value);\r\n state.currentIndex++;\r\n\r\n if (state.currentIndex === state.sequence.length) {\r\n const nextLevel = level + 1;\r\n setLevel(nextLevel);\r\n state.phase = 'transition';\r\n setShowOverlay({ visible: true, text: `Get Ready! Level ${nextLevel}` });\r\n\r\n setTimeout(() => {\r\n setShowOverlay({ visible: false, text: '' });\r\n startLevel(nextLevel);\r\n }, 1500);\r\n }\r\n } else {\r\n setGameOver(true);\r\n setMessage(`Wrong! Clicked ${num.value}, expected ${expected}`);\r\n }\r\n break;\r\n }\r\n }\r\n };\r\n\r\n useEffect(() => startLevel(1), []);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n let animationId: number;\r\n\r\n const draw = () => {\r\n const state = gameStateRef.current;\r\n ctx.fillStyle = '#000';\r\n ctx.fillRect(0, 0, config.canvasSize.width, config.canvasSize.height);\r\n\r\n state.numbers.forEach((num, idx) => {\r\n // Show green hint only in level 1\r\n const isNext = state.phase === 'recall' && level === 1 && idx === state.currentIndex;\r\n ctx.fillStyle = isNext ? '#22c55e' : '#3b82f6';\r\n ctx.beginPath();\r\n ctx.arc(num.x, num.y, num.radius, 0, Math.PI * 2);\r\n ctx.fill();\r\n\r\n // Reveal numbers if memorizing, transitioning, or already correctly guessed\r\n if (state.showNumbers || state.phase === 'transition' || state.revealed.has(num.value)) {\r\n ctx.fillStyle = '#fff';\r\n ctx.font = 'bold 24px sans-serif';\r\n ctx.textAlign = 'center';\r\n ctx.textBaseline = 'middle';\r\n ctx.fillText(num.value.toString(), num.x, num.y);\r\n }\r\n });\r\n\r\n if (!gameOver) animationId = requestAnimationFrame(draw);\r\n };\r\n\r\n draw();\r\n return () => cancelAnimationFrame(animationId);\r\n }, [gameOver, level]);\r\n\r\n return (\r\n <div className=\"relative flex flex-col items-center gap-4 p-4\">\r\n <div className=\"flex gap-8 text-xl font-bold\">\r\n <div>Level: {level}</div>\r\n <div>Numbers: {getNumberCount(level)}</div>\r\n </div>\r\n <div className=\"text-lg font-semibold text-blue-400 h-6\">{message}</div>\r\n <canvas\r\n ref={canvasRef}\r\n width={config.canvasSize.width}\r\n height={config.canvasSize.height}\r\n className=\"border-2 border-gray-300 cursor-pointer\"\r\n onClick={handleCanvasClick}\r\n />\r\n {gameOver && (\r\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-black/60\">\r\n <p className=\"text-xl font-bold text-red-500 mb-2\">Game Over</p>\r\n <p className=\"text-lg text-white mb-4\">Reached Level {level}</p>\r\n <Button onClick={resetGame}>Play Again</Button>\r\n </div>\r\n )}\r\n\r\n {showOverlay.visible && (\r\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-black/60\">\r\n <p className=\"text-2xl font-bold text-yellow-400\">{showOverlay.text}</p>\r\n </div>\r\n )}\r\n\r\n <div className=\"text-sm text-center text-foreground max-w-md\">\r\n <div>Memorize the numbers, then click them in sequence (1, 2, 3...)</div>\r\n <div className=\"mt-1 text-xs text-gray-400\">Green circle = next number to click (level 1 only)</div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default MemoryGame;\r\n"],"mappings":"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","names":["import_jsx_runtime","import_react","import_jsx_runtime","config"]}
package/dist/index.css CHANGED
@@ -22,14 +22,24 @@
22
22
  "Courier New",
23
23
  monospace;
24
24
  --color-red-500: oklch(63.7% 0.237 25.331);
25
+ --color-yellow-400: oklch(85.2% 0.199 91.936);
26
+ --color-blue-400: oklch(70.7% 0.165 254.624);
25
27
  --color-gray-300: oklch(87.2% 0.01 258.338);
26
- --color-gray-600: oklch(44.6% 0.03 256.802);
28
+ --color-gray-400: oklch(70.7% 0.022 261.325);
29
+ --color-black: #000;
27
30
  --color-white: #fff;
28
31
  --spacing: 0.25rem;
32
+ --container-md: 28rem;
33
+ --text-xs: 0.75rem;
34
+ --text-xs--line-height: calc(1 / 0.75);
29
35
  --text-sm: 0.875rem;
30
36
  --text-sm--line-height: calc(1.25 / 0.875);
37
+ --text-lg: 1.125rem;
38
+ --text-lg--line-height: calc(1.75 / 1.125);
31
39
  --text-xl: 1.25rem;
32
40
  --text-xl--line-height: calc(1.75 / 1.25);
41
+ --text-2xl: 1.5rem;
42
+ --text-2xl--line-height: calc(2 / 1.5);
33
43
  --font-weight-medium: 500;
34
44
  --font-weight-semibold: 600;
35
45
  --font-weight-bold: 700;
@@ -233,6 +243,18 @@
233
243
  container-type: inline-size;
234
244
  container-name: card-header;
235
245
  }
246
+ .visible {
247
+ visibility: visible;
248
+ }
249
+ .absolute {
250
+ position: absolute;
251
+ }
252
+ .relative {
253
+ position: relative;
254
+ }
255
+ .inset-0 {
256
+ inset: calc(var(--spacing) * 0);
257
+ }
236
258
  .col-start-2 {
237
259
  grid-column-start: 2;
238
260
  }
@@ -242,9 +264,18 @@
242
264
  .row-start-1 {
243
265
  grid-row-start: 1;
244
266
  }
267
+ .mt-1 {
268
+ margin-top: calc(var(--spacing) * 1);
269
+ }
245
270
  .mb-2 {
246
271
  margin-bottom: calc(var(--spacing) * 2);
247
272
  }
273
+ .mb-3 {
274
+ margin-bottom: calc(var(--spacing) * 3);
275
+ }
276
+ .mb-4 {
277
+ margin-bottom: calc(var(--spacing) * 4);
278
+ }
248
279
  .flex {
249
280
  display: flex;
250
281
  }
@@ -266,6 +297,9 @@
266
297
  width: calc(var(--spacing) * 10);
267
298
  height: calc(var(--spacing) * 10);
268
299
  }
300
+ .h-6 {
301
+ height: calc(var(--spacing) * 6);
302
+ }
269
303
  .h-8 {
270
304
  height: calc(var(--spacing) * 8);
271
305
  }
@@ -275,9 +309,15 @@
275
309
  .h-10 {
276
310
  height: calc(var(--spacing) * 10);
277
311
  }
312
+ .max-w-md {
313
+ max-width: var(--container-md);
314
+ }
278
315
  .shrink-0 {
279
316
  flex-shrink: 0;
280
317
  }
318
+ .cursor-pointer {
319
+ cursor: pointer;
320
+ }
281
321
  .auto-rows-min {
282
322
  grid-auto-rows: min-content;
283
323
  }
@@ -308,6 +348,9 @@
308
348
  .gap-6 {
309
349
  gap: calc(var(--spacing) * 6);
310
350
  }
351
+ .gap-8 {
352
+ gap: calc(var(--spacing) * 8);
353
+ }
311
354
  .self-start {
312
355
  align-self: flex-start;
313
356
  }
@@ -334,6 +377,12 @@
334
377
  .bg-background {
335
378
  background-color: var(--background);
336
379
  }
380
+ .bg-black\/60 {
381
+ background-color: color-mix(in srgb, #000 60%, transparent);
382
+ @supports (color: color-mix(in lab, red, red)) {
383
+ background-color: color-mix(in oklab, var(--color-black) 60%, transparent);
384
+ }
385
+ }
337
386
  .bg-card {
338
387
  background-color: var(--card);
339
388
  }
@@ -367,6 +416,14 @@
367
416
  .text-center {
368
417
  text-align: center;
369
418
  }
419
+ .text-2xl {
420
+ font-size: var(--text-2xl);
421
+ line-height: var(--tw-leading, var(--text-2xl--line-height));
422
+ }
423
+ .text-lg {
424
+ font-size: var(--text-lg);
425
+ line-height: var(--tw-leading, var(--text-lg--line-height));
426
+ }
370
427
  .text-sm {
371
428
  font-size: var(--text-sm);
372
429
  line-height: var(--tw-leading, var(--text-sm--line-height));
@@ -375,6 +432,10 @@
375
432
  font-size: var(--text-xl);
376
433
  line-height: var(--tw-leading, var(--text-xl--line-height));
377
434
  }
435
+ .text-xs {
436
+ font-size: var(--text-xs);
437
+ line-height: var(--tw-leading, var(--text-xs--line-height));
438
+ }
378
439
  .leading-none {
379
440
  --tw-leading: 1;
380
441
  line-height: 1;
@@ -394,11 +455,17 @@
394
455
  .whitespace-nowrap {
395
456
  white-space: nowrap;
396
457
  }
458
+ .text-blue-400 {
459
+ color: var(--color-blue-400);
460
+ }
397
461
  .text-card-foreground {
398
462
  color: var(--card-foreground);
399
463
  }
400
- .text-gray-600 {
401
- color: var(--color-gray-600);
464
+ .text-foreground {
465
+ color: var(--foreground);
466
+ }
467
+ .text-gray-400 {
468
+ color: var(--color-gray-400);
402
469
  }
403
470
  .text-muted-foreground {
404
471
  color: var(--muted-foreground);
@@ -418,6 +485,9 @@
418
485
  .text-white {
419
486
  color: var(--color-white);
420
487
  }
488
+ .text-yellow-400 {
489
+ color: var(--color-yellow-400);
490
+ }
421
491
  .underline-offset-4 {
422
492
  text-underline-offset: 4px;
423
493
  }
@@ -443,6 +513,34 @@
443
513
  outline-style: var(--tw-outline-style);
444
514
  outline-width: 1px;
445
515
  }
516
+ .transition {
517
+ transition-property:
518
+ color,
519
+ background-color,
520
+ border-color,
521
+ outline-color,
522
+ text-decoration-color,
523
+ fill,
524
+ stroke,
525
+ --tw-gradient-from,
526
+ --tw-gradient-via,
527
+ --tw-gradient-to,
528
+ opacity,
529
+ box-shadow,
530
+ transform,
531
+ translate,
532
+ scale,
533
+ rotate,
534
+ filter,
535
+ -webkit-backdrop-filter,
536
+ backdrop-filter,
537
+ display,
538
+ content-visibility,
539
+ overlay,
540
+ pointer-events;
541
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
542
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
543
+ }
446
544
  .transition-all {
447
545
  transition-property: all;
448
546
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));