Skip to content

Commit dc60c79

Browse files
authored
release v0.2 (#4)
* fixing sorting logic * saving * refactor: changing code structure * feat: good sorting ui * feat: load actual challenge data * feat: CORE functionality implemented * feat: cleaned the directory * feat: cleaned dir * feat: landing page * Feat/sparkrush UI (#3) * made the light mode on spark rush * feat: score overlay * feat: countdown * Feat/sparkrush UI (#5) * made the light mode on spark rush * feat: score overlay * feat: countdown
1 parent 5cdb118 commit dc60c79

8 files changed

Lines changed: 409 additions & 243 deletions

File tree

src/app/globals.css

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
@import url('https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap');
2+
13
@import "tailwindcss";
24

35
:root {
4-
--background: #ffffff;
5-
--foreground: #171717;
6+
--background: #f8f9fa;
7+
--foreground: #202124;
68
--google-blue: #4285f4;
79
--google-red: #ea4335;
810
--google-yellow: #fbbc04;
@@ -16,15 +18,10 @@
1618
--font-mono: var(--font-geist-mono);
1719
}
1820

19-
@media (prefers-color-scheme: dark) {
20-
:root {
21-
--background: #0a0a0a;
22-
--foreground: #ededed;
23-
}
24-
}
25-
2621
body {
2722
background: var(--background);
2823
color: var(--foreground);
29-
font-family: Arial, Helvetica, sans-serif;
24+
font-family: 'Google Sans', sans-serif, Arial, Helvetica;
3025
}
26+
27+
/* Add Google Font import */
Lines changed: 99 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,116 @@
11
"use client";
22

3-
import React, { useEffect, useState } from "react";
3+
import React from "react";
44
import { CodeSpace } from "./components/CodeSpace";
5-
import { Challenge, CodeBlock, GameState } from "./types";
6-
import { checkIsWin, generateHtml, loadRandomChallenge } from "./challenge";
7-
import { formatTime, shuffleArray } from "./utils";
5+
import { formatTime } from "./utils";
86
import { GameOverModal } from "./components/GameOverModal";
97
import { PreviewPane } from "./components/PreviewPane";
8+
import { SparkRushProvider, useSparkRush } from "./SparkRushContext";
109

11-
export const SparkRush = () => {
12-
const [gameState, setGameState] = useState<GameState>({
13-
timeRemaining: 180,
14-
score: 0,
15-
gameActive: true,
16-
gameOver: false,
17-
});
18-
19-
const [challenge, setChallenge] = useState<Challenge>(loadRandomChallenge());
20-
const [codeBlocks, setCodeBlocks] = useState<CodeBlock[]>([]);
21-
22-
const [showSuccessFlash, setShowSuccessFlash] = useState(false);
23-
const [showTargetFlash, setShowTargetFlash] = useState(true);
24-
25-
// initiate code blocks based on target
26-
useEffect(() => {
27-
// shuffle challenge code blocks and make sure that it is not in win condition
28-
let blocks = shuffleArray(challenge.codeBlocks);
29-
while (checkIsWin(blocks, challenge.codeBlocks)) {
30-
blocks = shuffleArray(challenge.codeBlocks);
31-
}
32-
setCodeBlocks(blocks);
33-
}, [challenge]);
34-
35-
// Flash ONLY the target when a new challenge loads
36-
useEffect(() => {
37-
setShowTargetFlash(true);
38-
setTimeout(() => {
39-
setShowTargetFlash(false);
40-
}, 1000);
41-
}, [challenge]);
42-
43-
// Timer
44-
useEffect(() => {
45-
if (!gameState.gameActive) return;
46-
47-
const interval = setInterval(() => {
48-
setGameState((prev) => {
49-
const newTime = prev.timeRemaining - 1;
50-
if (newTime <= 0) {
51-
return {
52-
...prev,
53-
timeRemaining: 0,
54-
gameActive: false,
55-
gameOver: true,
56-
};
57-
}
58-
return { ...prev, timeRemaining: newTime };
59-
});
60-
}, 1000);
61-
62-
return () => clearInterval(interval);
63-
}, [gameState.gameActive]);
64-
65-
66-
67-
// Check for win condition
68-
useEffect(() => {
69-
if (codeBlocks.length === 0) return;
70-
71-
if (checkIsWin(codeBlocks, challenge.codeBlocks)) {
72-
setGameState((prev) => ({ ...prev, score: prev.score + 1 }));
73-
setShowSuccessFlash(true); // turn green
74-
75-
setTimeout(() => {
76-
const newChallenge = loadRandomChallenge();
77-
while (newChallenge.id === challenge.id) {
78-
newChallenge.id = loadRandomChallenge().id;
79-
}
80-
81-
setChallenge(newChallenge);
82-
setShowSuccessFlash(false);
83-
}, 1000);
84-
}
85-
}, [codeBlocks]);
86-
87-
const handleReset = () => {
88-
const newChallenge = loadRandomChallenge();
89-
setChallenge(newChallenge);
90-
setGameState({
91-
timeRemaining: 180,
92-
score: 0,
93-
gameActive: true,
94-
gameOver: false,
95-
});
96-
};
10+
const SparkRushGame = () => {
11+
const { gameState, challenge, showTargetFlash, showSuccessOverlay } =
12+
useSparkRush();
9713

9814
return (
99-
<>
100-
<div className="w-full h-screen bg-slate-950 font-sans overflow-hidden">
101-
{gameState.gameOver && (
102-
<GameOverModal score={gameState.score} onReset={handleReset} />
103-
)}
15+
<div
16+
className={`w-full h-screen font-sans overflow-hidden flex flex-col transition-all duration-200 ${
17+
showSuccessOverlay ? "bg-yellow-100" : "bg-white"
18+
}`}
19+
>
20+
{/* score overlay */}
21+
<div
22+
className={`absolute inset-0 bg-opacity-50 flex items-center justify-center z-50 transition-all duration-500 pointer-events-none ${
23+
showSuccessOverlay ? "visible opacity-100" : "invisible opacity-0"
24+
} flex flex-col gap-8`}
25+
>
26+
<div className="text-yellow-300 drop-shadow-sm drop-shadow-black text-9xl font-bold ">
27+
{gameState.score}
28+
</div>
29+
<div className="text-yellow-300 drop-shadow-sm drop-shadow-black text-3xl font-bold ">
30+
{`Time : ${formatTime(gameState.timeRemaining)}`}
31+
</div>
32+
</div>
33+
34+
{/* countdown overlay */}
35+
<div
36+
className={`absolute inset-0 bg-opacity-50 flex items-center justify-center z-50 transition-all duration-500 pointer-events-none ${
37+
gameState.timeRemaining <= 5 && !showSuccessOverlay? "visible opacity-100" : "invisible opacity-0"
38+
} flex flex-col gap-8`}
39+
>
40+
<div className="text-yellow-300 drop-shadow-sm drop-shadow-black text-9xl font-bold ">
41+
{gameState.timeRemaining}
42+
</div>
43+
</div>
10444

105-
<div className="grid grid-cols-2 h-full p-6 gap-6">
45+
{gameState.gameOver && <GameOverModal />}
46+
47+
{/* Header */}
48+
<header className="flex items-center justify-between p-4 border-b border-gray-200">
49+
<h1 className="text-2xl font-bold">
50+
<span style={{ color: "var(--google-blue)" }}>S</span>
51+
<span style={{ color: "var(--google-red)" }}>p</span>
52+
<span style={{ color: "var(--google-yellow)" }}>a</span>
53+
<span style={{ color: "var(--google-green)" }}>r</span>
54+
<span style={{ color: "var(--google-blue)" }}>k</span>
55+
<span style={{ color: "var(--foreground)" }}> </span>
56+
<span style={{ color: "var(--google-red)" }}>R</span>
57+
<span style={{ color: "var(--google-yellow)" }}>u</span>
58+
<span style={{ color: "var(--google-green)" }}>s</span>
59+
<span style={{ color: "var(--google-blue)" }}>h</span>
60+
</h1>
61+
<div className="flex items-center gap-6">
10662
<div
107-
className={`flex flex-col ${
108-
showSuccessFlash ? "bg-green-400/20" : "bg-slate-900/40"
109-
} w-full p-6 rounded-lg border-slate-800 border `}
63+
className={`text-2xl font-semibold transition-colors flex items-center gap-2 ${
64+
gameState.timeRemaining <= 10
65+
? "text-red-500 animate-pulse"
66+
: "text-gray-600"
67+
}`}
11068
>
111-
<div className="mb-6">
112-
<h1 className="text-4xl font-bold text-slate-200 mb-2">
113-
Spark Rush
114-
</h1>
115-
<div className="flex gap-8 items-center">
116-
<div
117-
className={`text-xl font-semibold ${
118-
gameState.timeRemaining <= 10
119-
? "text-orange-400 animate-pulse"
120-
: "text-slate-300"
121-
}`}
122-
>
123-
{formatTime(gameState.timeRemaining)}
124-
</div>
125-
<div className="text-xl font-semibold text-slate-300">
126-
{gameState.score}
127-
</div>
128-
</div>
129-
</div>
69+
<span className="text-2xl"></span>{" "}
70+
{formatTime(gameState.timeRemaining)}
71+
</div>
72+
<div className="text-2xl font-semibold text-green-500 flex items-center gap-2">
73+
<span className="text-2xl"></span> {gameState.score}
74+
</div>
75+
</div>
76+
</header>
77+
78+
{/* Main Content */}
79+
<main className="flex-1 grid grid-cols-2 gap-8 p-8 overflow-hidden">
80+
{/* Left Column */}
81+
<div className="flex flex-col gap-8">
82+
{/* Challenge Info */}
83+
<div className="flex-shrink-0 p-6 rounded-xl border-2 border-gray-200 bg-white shadow-sm">
84+
<h2
85+
className="text-3xl font-bold mb-2"
86+
style={{ color: "var(--google-blue)" }}
87+
>
88+
{challenge.title}
89+
</h2>
90+
<p className="text-gray-600 text-lg">{challenge.description}</p>
91+
</div>
13092

131-
<CodeSpace codeBlocks={codeBlocks} setCodeBlocks={setCodeBlocks} />
93+
{/* Code Space */}
94+
<div
95+
className={`flex-1 flex flex-col transition-all duration-300 rounded-xl border-2 ${"bg-gray-50 border-gray-200"}`}
96+
>
97+
<CodeSpace />
13298
</div>
99+
</div>
133100

134-
<PreviewPane
135-
codeBlocks={codeBlocks}
136-
targetHtml={generateHtml(challenge.codeBlocks)}
137-
showTargetFlash={showTargetFlash}
138-
/>
101+
{/* Right Column (Preview) */}
102+
<div className="flex-1 flex flex-col">
103+
<PreviewPane showTargetFlash={showTargetFlash} />
139104
</div>
140-
</div>
141-
</>
105+
</main>
106+
</div>
107+
);
108+
};
109+
110+
export const SparkRush = () => {
111+
return (
112+
<SparkRushProvider>
113+
<SparkRushGame />
114+
</SparkRushProvider>
142115
);
143116
};

0 commit comments

Comments
 (0)