Skip to content

Commit 11a2927

Browse files
committed
feat: add responsive layout
1 parent c7ee6e1 commit 11a2927

File tree

1 file changed

+58
-57
lines changed

1 file changed

+58
-57
lines changed

src/pages/Index.tsx

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import VoltLabIcon from "../../jscpg.svg";
1717

1818
const BinaryBackground = () => {
19-
const [binaryStrings, setBinaryStrings] = useState<string[]>([]);
19+
const [binaryStrings, setBinaryStrings] = useState([]);
2020

2121
useEffect(() => {
2222
const generateBinaryString = () => {
@@ -45,7 +45,7 @@ const BinaryBackground = () => {
4545
{binaryStrings.map((str, i) => (
4646
<div
4747
key={i}
48-
className="font-mono text-sm whitespace-nowrap animate-fade-in"
48+
className="font-mono text-xs sm:text-sm whitespace-nowrap animate-fade-in"
4949
style={{
5050
transform: `translateY(${i * 24}px)`,
5151
color: "currentColor",
@@ -59,17 +59,28 @@ const BinaryBackground = () => {
5959
};
6060

6161
const Index = () => {
62-
const [code, setCode] = useState<string>(() => {
62+
const [code, setCode] = useState(() => {
6363
if (typeof window !== "undefined") {
6464
return localStorage.getItem("editorCode") || "";
6565
}
6666
return "";
6767
});
68-
const [output, setOutput] = useState<string>("");
69-
const [error, setError] = useState<string | null>(null);
70-
const [isFullScreen, setIsFullScreen] = useState<boolean>(false);
68+
const [output, setOutput] = useState("");
69+
const [error, setError] = useState(null);
70+
const [isFullScreen, setIsFullScreen] = useState(false);
71+
const [isVerticalLayout, setIsVerticalLayout] = useState(false);
7172

72-
const handleEditorChange = (value: string | undefined) => {
73+
useEffect(() => {
74+
const handleResize = () => {
75+
setIsVerticalLayout(window.innerWidth < 768);
76+
};
77+
78+
handleResize();
79+
window.addEventListener('resize', handleResize);
80+
return () => window.removeEventListener('resize', handleResize);
81+
}, []);
82+
83+
const handleEditorChange = (value) => {
7384
if (value !== undefined) {
7485
setCode(value);
7586
localStorage.setItem("editorCode", value);
@@ -82,10 +93,10 @@ const Index = () => {
8293
setOutput("");
8394

8495
let outputBuffer = "";
85-
const timers: { [key: string]: number } = {};
96+
const timers = {};
8697

8798
const secureConsole = {
88-
log: (...args: any[]) => {
99+
log: (...args) => {
89100
outputBuffer +=
90101
args
91102
.map((arg) =>
@@ -94,33 +105,23 @@ const Index = () => {
94105
.join(" ") + "\n";
95106
setOutput(outputBuffer);
96107
},
97-
error: (...args: any[]) => {
108+
error: (...args) => {
98109
outputBuffer += "Error: " + args.join(" ") + "\n";
99110
setOutput(outputBuffer);
100111
},
101-
time: (label: string = "default") => {
112+
time: (label = "default") => {
102113
if (timers[label]) {
103114
secureConsole.error(
104-
`Timer '${label}' already exists. Use a different label or call console.timeEnd('${label}') before starting a new timer with the same label.`
115+
`Timer '${label}' already exists.`
105116
);
106117
return;
107118
}
108-
if (typeof performance !== "undefined" && performance.now) {
109-
timers[label] = performance.now();
110-
} else {
111-
timers[label] = Date.now();
112-
}
119+
timers[label] = performance.now();
113120
},
114-
timeEnd: (label: string = "default") => {
121+
timeEnd: (label = "default") => {
115122
if (timers[label]) {
116-
let duration: number;
117-
if (typeof performance !== "undefined" && performance.now) {
118-
duration = performance.now() - timers[label];
119-
duration = parseFloat(duration.toFixed(3));
120-
} else {
121-
duration = Date.now() - timers[label];
122-
}
123-
outputBuffer += `${label}: ${duration}ms\n`;
123+
const duration = performance.now() - timers[label];
124+
outputBuffer += `${label}: ${parseFloat(duration.toFixed(3))}ms\n`;
124125
setOutput(outputBuffer);
125126
delete timers[label];
126127
} else {
@@ -154,7 +155,7 @@ const Index = () => {
154155
};
155156

156157
useEffect(() => {
157-
const handleEsc = (event: KeyboardEvent) => {
158+
const handleEsc = (event) => {
158159
if (event.key === 'Escape') {
159160
setIsFullScreen(false);
160161
}
@@ -170,19 +171,19 @@ const Index = () => {
170171
isFullScreen ? 'fixed inset-0 z-50 bg-background' : ''
171172
}`}>
172173
<BinaryBackground />
173-
<div className={`container mx-auto py-6 px-4 relative ${
174+
<div className={`container mx-auto py-4 sm:py-6 px-2 sm:px-4 relative ${
174175
isFullScreen ? 'h-full p-0' : ''
175176
}`}>
176177
<TooltipProvider>
177178
<Tooltip>
178179
<TooltipTrigger asChild>
179180
<button
180181
onClick={goToGitHub}
181-
className={`absolute top-8 right-4 border p-2 rounded-full ${
182+
className={`absolute top-4 sm:top-8 bg-white right-2 sm:right-4 border p-2 rounded-full ${
182183
isFullScreen ? 'hidden' : ''
183184
}`}
184185
>
185-
<Github className="text-black" />
186+
<Github className="w-4 h-4 sm:w-5 sm:h-5 text-black" />
186187
</button>
187188
</TooltipTrigger>
188189
<TooltipContent>
@@ -191,74 +192,74 @@ const Index = () => {
191192
</Tooltip>
192193
</TooltipProvider>
193194

194-
<div className={`flex justify-center items-center gap-4 mb-8 ${
195+
<div className={`flex justify-center items-center gap-2 sm:gap-4 mb-4 sm:mb-8 ${
195196
isFullScreen ? 'hidden' : ''
196197
}`}>
197198
<img
198199
src={VoltLabIcon}
199200
alt="Logo"
200-
className="w-12 h-12 transition-transform hover:scale-110"
201+
className="w-8 h-8 sm:w-12 sm:h-12 transition-transform hover:scale-110"
201202
/>
202203
<h1
203-
className="text-4xl font-bold tracking-tighter"
204+
className="text-2xl sm:text-4xl font-bold tracking-tighter"
204205
style={{ fontFamily: "'Space Mono', monospace" }}
205206
>
206207
Volt Lab
207208
</h1>
208209
</div>
209210

210211
<ResizablePanelGroup
211-
direction="horizontal"
212+
direction={isVerticalLayout ? "vertical" : "horizontal"}
212213
className={`${
213214
isFullScreen
214215
? 'fixed inset-0 rounded-none border-none'
215-
: 'min-h-[80vh] rounded-xl border shadow-lg'
216+
: 'min-h-[70vh] sm:min-h-[80vh] rounded-lg sm:rounded-xl border shadow-lg'
216217
} bg-background/95 backdrop-blur-sm`}
217218
>
218219
<ResizablePanel defaultSize={50}>
219220
<div className="h-full flex flex-col">
220-
<div className="flex justify-between items-center p-4 border-b bg-muted/30">
221-
<span className="text-sm font-medium">JavaScript Editor</span>
222-
<div className="flex gap-2">
221+
<div className="flex justify-between items-center p-2 sm:p-4 border-b bg-muted/30">
222+
<span className="text-xs sm:text-sm font-medium">JavaScript Editor</span>
223+
<div className="flex gap-1 sm:gap-2">
223224
<TooltipProvider>
224225
<Tooltip>
225226
<TooltipTrigger asChild>
226227
<button
227228
onClick={toggleFullScreen}
228-
className="p-2 hover:bg-muted rounded-md transition-colors"
229+
className="p-1 sm:p-2 hover:bg-muted rounded-md transition-colors"
229230
>
230231
{isFullScreen ? (
231-
<Minimize className="h-5 w-5" />
232+
<Minimize className="h-4 w-4 sm:h-5 sm:w-5" />
232233
) : (
233-
<Expand className="h-5 w-5" />
234+
<Expand className="h-4 w-4 sm:h-5 sm:w-5" />
234235
)}
235236
</button>
236237
</TooltipTrigger>
237-
<TooltipContent side="bottom" >
238+
<TooltipContent side="bottom">
238239
<p>{isFullScreen ? 'Exit Fullscreen' : 'Fullscreen'}</p>
239240
</TooltipContent>
240241
</Tooltip>
241242
</TooltipProvider>
242243
<button
243244
onClick={compileAndExecute}
244-
className="flex items-center justify-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors duration-200 shadow-sm"
245+
className="flex items-center justify-center gap-1 sm:gap-2 px-2 sm:px-4 py-1 sm:py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors duration-200 shadow-sm text-xs sm:text-sm"
245246
>
246-
<Play size={18} />
247+
<Play className="w-4 h-4 sm:w-5 sm:h-5" />
247248
Run
248249
</button>
249250
</div>
250251
</div>
251-
<div className="flex-1 p-4">
252+
<div className="flex-1 p-2 sm:p-4">
252253
<Editor
253254
height="100%"
254255
defaultLanguage="javascript"
255256
theme="vs-dark"
256257
value={code}
257258
onChange={handleEditorChange}
258259
options={{
259-
minimap: { enabled: true },
260-
fontSize: 14,
261-
padding: { top: 24, bottom: 24 },
260+
minimap: { enabled: window.innerWidth > 768 },
261+
fontSize: window.innerWidth < 640 ? 12 : 14,
262+
padding: { top: 16, bottom: 16 },
262263
wordWrap: "on",
263264
automaticLayout: true,
264265
scrollBeyondLastLine: false,
@@ -275,26 +276,26 @@ const Index = () => {
275276
<ResizableHandle withHandle />
276277

277278
<ResizablePanel defaultSize={50}>
278-
<div className="h-full flex flex-col p-6">
279-
<div className="flex items-center mb-4">
280-
<div className="flex items-center gap-2">
281-
<div className="w-2 h-2 rounded-full bg-green-500"></div>
282-
<h2 className="text-lg font-semibold flex items-center justify-center gap-1">
279+
<div className="h-full flex flex-col p-3 sm:p-6">
280+
<div className="flex items-center mb-2 sm:mb-4">
281+
<div className="flex items-center gap-1 sm:gap-2">
282+
<div className="w-1.5 h-1.5 sm:w-2 sm:h-2 rounded-full bg-green-500"></div>
283+
<h2 className="text-sm sm:text-lg font-semibold flex items-center justify-center gap-1">
283284
Console Output
284285
</h2>
285286
</div>
286287
<button
287-
className="ml-auto text-sm text-muted-foreground hover:text-foreground"
288+
className="ml-auto text-xs sm:text-sm text-muted-foreground hover:text-foreground"
288289
onClick={clearOutput}
289290
>
290291
Clear
291292
</button>
292293
</div>
293-
<div className="flex-1 font-mono bg-black p-6 rounded overflow-auto border shadow-inner">
294+
<div className="flex-1 font-mono bg-black p-3 sm:p-6 rounded overflow-auto border shadow-inner">
294295
{error ? (
295-
<div className="text-destructive">{error}</div>
296+
<div className="text-destructive text-xs sm:text-sm">{error}</div>
296297
) : (
297-
<pre className="whitespace-pre-wrap text-green-500">
298+
<pre className="whitespace-pre-wrap text-green-500 text-xs sm:text-sm">
298299
{output}
299300
</pre>
300301
)}

0 commit comments

Comments
 (0)