Skip to content

Commit cd673d0

Browse files
committed
style: UI change
1 parent 6ade7e8 commit cd673d0

File tree

6 files changed

+224
-118
lines changed

6 files changed

+224
-118
lines changed

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function App() {
5555
<ResizablePanel defaultSize={65}>
5656
<Editor />
5757
</ResizablePanel>
58-
<ResizableHandle withHandle />
58+
<ResizableHandle className="border-[1px]" withHandle />
5959
<ResizablePanel defaultSize={35}>
6060
<Terminal />
6161
</ResizablePanel>

src/components/editor/stats.tsx

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,65 @@
11
import { useMemo } from "react";
22
import { useStore } from "@/store/useStore";
33

4+
import { FileTextIcon, TypeIcon, HashIcon } from "lucide-react";
5+
46
interface CodeStats {
57
lines: number;
68
words: number;
79
characters: number;
810
}
911

10-
export default function Stats() {
12+
const Stats = () => {
1113
const { code } = useStore();
1214

13-
const stats: CodeStats = useMemo(() => {
14-
const lines = code.split("\n").length;
15-
const words = code.trim().split(/\s+/).length;
16-
const characters = code.length;
17-
18-
return { lines, words, characters };
19-
}, [code]);
15+
const stats: CodeStats = useMemo(
16+
() => ({
17+
lines: code.split("\n").length,
18+
words: code.trim().split(/\s+/).length,
19+
characters: code.length
20+
}),
21+
[code]
22+
);
2023

2124
return (
22-
<section className="pointer-events-none absolute bottom-[22px] left-0 right-0 z-[99] hidden items-center justify-center md:flex">
23-
<div className="rounded bg-secondary px-2 font-mono text-sm text-foreground">
24-
<span>
25-
{stats.lines} Lines, {stats.words} Words, {stats.characters}{" "}
26-
Characters
27-
</span>
25+
<div className="fixed bottom-1 left-1/2 z-50 hidden -translate-x-1/2 transform rounded-lg border border-accent md:block">
26+
<div className="tex flex items-center space-x-2 bg-secondary bg-opacity-80 px-4 py-2 text-foreground shadow-lg backdrop-blur-sm">
27+
<StatItem
28+
icon={<FileTextIcon size={16} />}
29+
value={stats.lines}
30+
label="Lines"
31+
/>
32+
<StatItem
33+
icon={<TypeIcon size={16} />}
34+
value={stats.words}
35+
label="Words"
36+
/>
37+
<StatItem
38+
icon={<HashIcon size={16} />}
39+
value={stats.characters}
40+
label="Chars"
41+
/>
2842
</div>
29-
</section>
43+
</div>
3044
);
31-
}
45+
};
46+
47+
const StatItem = ({
48+
icon,
49+
value,
50+
label
51+
}: {
52+
icon: React.ReactNode;
53+
value: number;
54+
label: string;
55+
}) => (
56+
<div className="flex items-center gap-1 space-x-1">
57+
<div className="flex items-center space-x-1">
58+
{icon}
59+
<span className="font-medium">{value}</span>
60+
</div>
61+
<span className="text-xs text-foreground/55">{label}</span>
62+
</div>
63+
);
64+
65+
export default Stats;

src/components/editor/terminal.tsx

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default function Terminal() {
77
const { output, error, setOutput, isPyodideLoading, runCode, pipInstall } =
88
useStore();
99
const [terminalCode, setTerminalCode] = useState<string>("");
10-
const outputRef = useRef<HTMLPreElement>(null);
10+
const outputRef = useRef<HTMLDivElement>(null);
1111

1212
const scrollToBottom = useCallback(() => {
1313
if (outputRef.current) {
@@ -26,7 +26,7 @@ export default function Terminal() {
2626
const code = formData.get("terminalCode") as string;
2727
if (code.trim()) {
2828
if (code.includes("pip install")) {
29-
pipInstall(code);
29+
await pipInstall(code);
3030
} else {
3131
setOutput(code);
3232
await runCode(code);
@@ -39,36 +39,37 @@ export default function Terminal() {
3939

4040
if (isPyodideLoading) {
4141
return (
42-
<div className="p-4">
43-
<Loader text="Downloading Python" />
42+
<div className="flex h-full items-center justify-center bg-gray-900">
43+
<Loader text="Initializing Python Environment" />
4444
</div>
4545
);
4646
}
4747

4848
return (
49-
<pre
49+
<div
5050
ref={outputRef}
51-
className="h-full w-full overflow-x-auto bg-background px-4 py-1"
51+
className="h-full w-full overflow-y-auto bg-gray-900 px-4 py-3 font-mono text-sm text-gray-300"
5252
>
53-
<code
54-
className={`w-full font-mono text-sm ${error ? "text-red-500" : "text-foreground"}`}
55-
>
56-
{error || output}
57-
<br />
58-
<form className="flex items-center gap-1" onSubmit={handleSubmit}>
59-
<p>&gt;&gt;</p>
60-
<input
61-
value={terminalCode}
62-
name="terminalCode"
63-
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
64-
setTerminalCode(e.target.value)
65-
}
66-
className="w-full border-none bg-transparent text-foreground outline-none"
67-
autoComplete="off"
68-
placeholder="..."
69-
/>
70-
</form>
71-
</code>
72-
</pre>
53+
{(error || output) && (
54+
<div
55+
className={`whitespace-pre-wrap ${error ? "text-red-400" : "text-gray-300"}`}
56+
>
57+
{error || output}
58+
</div>
59+
)}
60+
<form className="flex items-center gap-2" onSubmit={handleSubmit}>
61+
<span className="text-green-400">&gt;&gt;&gt;</span>
62+
<input
63+
value={terminalCode}
64+
name="terminalCode"
65+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
66+
setTerminalCode(e.target.value)
67+
}
68+
className="w-full bg-transparent text-gray-300 outline-none"
69+
autoComplete="off"
70+
placeholder="..."
71+
/>
72+
</form>
73+
</div>
7374
);
7475
}

src/components/nav-buttons.tsx

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback } from "react";
1+
import { useCallback, memo } from "react";
22
import { useStore } from "@/store/useStore";
33

44
import { Button } from "./ui/button";
@@ -12,7 +12,7 @@ import {
1212
LoaderCircleIcon
1313
} from "lucide-react";
1414

15-
export default function ButtonsNav() {
15+
const ButtonsNav = () => {
1616
const {
1717
setDirection,
1818
direction,
@@ -32,48 +32,71 @@ export default function ButtonsNav() {
3232
setError(null);
3333
}, [clearOutput, setError]);
3434

35-
const handleRunCode = useCallback(async () => {
36-
await runCode(code);
35+
const handleRunCode = useCallback(() => {
36+
runCode(code);
3737
}, [runCode, code]);
3838

3939
return (
40-
<section className="flex justify-between gap-2 bg-background p-2">
41-
<div className="flex grow items-center justify-center gap-1">
42-
<Button
43-
disabled={isCodeExecuting}
44-
title="Execute the code"
45-
onClick={handleRunCode}
46-
variant="secondary"
47-
>
48-
{isCodeExecuting ? (
49-
<LoaderCircleIcon className="h-5 w-5 animate-spin" />
50-
) : (
51-
<PlayIcon className="h-5 w-5" />
52-
)}
53-
<span className={`ml-2 ${isCodeExecuting ? "opacity-80" : ""}`}>
54-
Run
55-
</span>
56-
</Button>
57-
<Button
58-
disabled={isCodeExecuting}
59-
title="Clear the terminal"
60-
onClick={handleTerminalClear}
61-
variant="secondary"
62-
>
63-
<TrashIcon className="h-5 w-5" />
64-
<span className="ml-2 hidden md:inline">Clear Terminal</span>
65-
</Button>
66-
<Button
67-
title="Change direction"
68-
variant="secondary"
69-
onClick={handleDirectionChange}
70-
>
71-
<ReplaceIcon className="h-5 w-5" />
72-
<span className="ml-2 hidden md:inline">Direction</span>
73-
</Button>
74-
<Settings />
75-
<ModeToggle />
40+
<nav className="mb-2 w-full rounded-b-lg bg-gray-900 px-4 py-2">
41+
<div className="flex flex-wrap items-center justify-center gap-1 sm:justify-between md:gap-2">
42+
<div className="flex flex-wrap items-center gap-1 md:gap-2">
43+
<NavButton
44+
onClick={handleRunCode}
45+
disabled={isCodeExecuting}
46+
icon={
47+
isCodeExecuting ? (
48+
<LoaderCircleIcon className="animate-spin" />
49+
) : (
50+
<PlayIcon />
51+
)
52+
}
53+
label="Run"
54+
/>
55+
<NavButton
56+
onClick={handleTerminalClear}
57+
disabled={isCodeExecuting}
58+
icon={<TrashIcon />}
59+
label="Clear Terminal"
60+
/>
61+
<NavButton
62+
onClick={handleDirectionChange}
63+
icon={<ReplaceIcon />}
64+
label="Toggle Direction"
65+
/>
66+
</div>
67+
<div className="flex items-center gap-1 md:gap-2">
68+
<Settings />
69+
<ModeToggle />
70+
</div>
7671
</div>
77-
</section>
72+
</nav>
7873
);
79-
}
74+
};
75+
76+
const NavButton = memo(
77+
({
78+
onClick,
79+
disabled,
80+
icon,
81+
label
82+
}: {
83+
onClick: () => void;
84+
disabled?: boolean;
85+
icon: React.ReactNode;
86+
label: string;
87+
}) => (
88+
<Button
89+
onClick={onClick}
90+
disabled={disabled}
91+
variant="secondary"
92+
className="text-foreground"
93+
>
94+
{icon}
95+
<span className="ml-2 hidden sm:inline">{label}</span>
96+
</Button>
97+
)
98+
);
99+
100+
NavButton.displayName = "NavButton";
101+
102+
export default memo(ButtonsNav);

0 commit comments

Comments
 (0)