Skip to content

Commit 4a67b78

Browse files
committed
added basic ui for game and py
1 parent 16f8e34 commit 4a67b78

5 files changed

Lines changed: 131 additions & 17 deletions

File tree

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
</head>
99
<body>
1010
<div id="root"></div>
11+
<script src="https://cdn.jsdelivr.net/pyodide/v0.27.0/full/pyodide.js"></script>
1112
<script type="module" src="/src/main.tsx"></script>
1213
</body>
1314
</html>

src/components/LevelComponent.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, { useState, useEffect, useRef } from 'react'
2+
3+
export default function LevelComponent() {
4+
const [code, setCode] = useState("Enter code here!")
5+
const [output, setOutput] = useState("")
6+
const [pyodideReady, setPyodideReady] = useState(false)
7+
const pyodideRef = useRef<any>(null)
8+
9+
// Load Pyodide on mount
10+
useEffect(() => {
11+
const loadPyodide = async () => {
12+
// @ts-ignore
13+
pyodideRef.current = await window.loadPyodide()
14+
setPyodideReady(true)
15+
}
16+
loadPyodide()
17+
}, [])
18+
19+
const runCode = async () => {
20+
if (!pyodideRef.current) return
21+
22+
// Capture stdout
23+
await pyodideRef.current.runPythonAsync(`
24+
import sys
25+
import io
26+
sys.stdout = io.StringIO()
27+
`)
28+
29+
try {
30+
await pyodideRef.current.runPythonAsync(code)
31+
const result = await pyodideRef.current.runPythonAsync(`sys.stdout.getvalue()`)
32+
setOutput(result)
33+
} catch (err: any) {
34+
setOutput(err.message)
35+
}
36+
}
37+
38+
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
39+
if (e.key === "Tab") {
40+
e.preventDefault()
41+
const el = e.target as HTMLTextAreaElement
42+
const { selectionStart, selectionEnd } = el
43+
const newValue = code.slice(0, selectionStart) + " " + code.slice(selectionEnd)
44+
setCode(newValue)
45+
requestAnimationFrame(() => {
46+
el.selectionStart = el.selectionEnd = selectionStart + 4
47+
})
48+
}
49+
}
50+
51+
return (
52+
<div className="w-screen h-screen grid grid-cols-2 font-mono bg-gray-100">
53+
54+
{/* Left panel */}
55+
<div className="flex flex-col gap-3 p-4">
56+
<div className="flex gap-3">
57+
<div className="flex-1 bg-green-950 border-2 border-white rounded-2xl p-3 text-white">
58+
<div className="italic font-bold mb-2">PYTHON CODE</div>
59+
<pre className="text-sm leading-relaxed m-0">
60+
{`nums = [1,2,3]
61+
sum = 0
62+
for i in nums:
63+
sum += i
64+
print(sum)`}
65+
</pre>
66+
</div>
67+
<div className="flex-1 bg-green-950 border-[3px] border-white rounded-2xl p-3 text-white">
68+
<div className="italic font-bold mb-2">ALIEN CODE</div>
69+
<pre className="text-sm leading-relaxed m-0">
70+
{`plz make nums [1,2,3] ty
71+
plz make sums 0 ty
72+
plz i go through nums ty
73+
plz add i to sum ty
74+
plz print sum ty`}
75+
</pre>
76+
</div>
77+
</div>
78+
<div className="bg-green-950 rounded-lg px-5 py-4 text-white text-sm">
79+
GOAL: IN <strong className="font-mono">ALIEN CODE</strong>, print the square of all numbers.
80+
</div>
81+
82+
{/* Output */}
83+
<div className="bg-zinc-900 rounded-lg p-4 text-green-400 text-sm font-mono h-32 overflow-auto">
84+
<div className="text-zinc-500 mb-1">Output:</div>
85+
<pre>{output}</pre>
86+
</div>
87+
88+
</div>
89+
90+
{/* Right panel */}
91+
<div className="flex flex-col gap-3 m-4">
92+
93+
{/* Editor */}
94+
<div className="flex-1 bg-zinc-700 rounded-lg overflow-hidden">
95+
<textarea
96+
value={code}
97+
onChange={(e) => setCode(e.target.value)}
98+
onKeyDown={handleKeyDown}
99+
spellCheck={false}
100+
className="w-full h-full bg-transparent text-white text-sm font-mono p-4 resize-none outline-none"
101+
/>
102+
</div>
103+
104+
{/* Run button */}
105+
<button
106+
onClick={runCode}
107+
disabled={!pyodideReady}
108+
className="bg-green-700 hover:bg-green-600 disabled:bg-zinc-500 text-white font-bold py-2 rounded-lg"
109+
>
110+
{pyodideReady ? "Run" : "Loading Python..."}
111+
</button>
112+
113+
</div>
114+
115+
</div>
116+
)
117+
}

src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "tailwindcss";

src/pages/home/Home.tsx

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
import Button from "../../components/button/Button";
22
import styles from "./Home.module.css";
33
import {useState} from "react";
4-
4+
import LevelComponent from "../../components/LevelComponent";
55
// The home page of the application.
66

77

8-
// function Home() {
9-
10-
// let [x, setX] = useState<number>(0);
11-
// function print() {
12-
// setX(a => a+1);
13-
// console.log(`PRINTED: ${x}`);
14-
// }
8+
function Home() {
159

16-
// return (
17-
// <div>
18-
// <header className="page"></header>
19-
// <canvas id=""></canvas>
20-
// </div>
21-
// );
22-
// }
10+
return (
11+
<div>
12+
<LevelComponent />
13+
</div>
14+
15+
);
16+
}
2317

2418
export default Home;

vite.config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { defineConfig } from 'vite'
1+
import { defineConfig, type Plugin } from 'vite'
22
import react from '@vitejs/plugin-react'
3+
import tailwindcss from '@tailwindcss/vite'
34

45
// https://vite.dev/config/
56
export default defineConfig({
6-
plugins: [react()],
7+
plugins: [react(), tailwindcss() as unknown as Plugin],
78
base: process.env.GITHUB_REPOSITORY
89
? `/${process.env.GITHUB_REPOSITORY.split('/')[1]}/`
910
: '/',

0 commit comments

Comments
 (0)