mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-17 22:27:19 +00:00
460 lines
16 KiB
TypeScript
460 lines
16 KiB
TypeScript
import { useState } from "react";
|
||
import { useLocation } from "wouter";
|
||
import {
|
||
Code,
|
||
Play,
|
||
Save,
|
||
FileCode,
|
||
ArrowLeft,
|
||
CheckCircle,
|
||
XCircle,
|
||
Loader2,
|
||
Copy,
|
||
Zap,
|
||
} from "lucide-react";
|
||
import { haptics } from "@/lib/haptics";
|
||
|
||
const EXAMPLE_CODE = `reality HelloWorld {
|
||
platforms: all
|
||
}
|
||
|
||
journey Greet(name) {
|
||
platform: all
|
||
notify "Hello, " + name + "!"
|
||
}
|
||
|
||
journey Main() {
|
||
platform: all
|
||
Greet("World")
|
||
}`;
|
||
|
||
const PASSPORT_EXAMPLE = `import { Passport } from "@aethex.os/core"
|
||
|
||
reality AuthSystem {
|
||
platforms: [web, roblox]
|
||
}
|
||
|
||
journey Login(username) {
|
||
platform: all
|
||
|
||
let passport = Passport(username)
|
||
|
||
when passport.verify() {
|
||
sync passport across [web, roblox]
|
||
notify "Welcome, " + username
|
||
reveal passport
|
||
}
|
||
}`;
|
||
|
||
export default function MobileAethexStudio() {
|
||
const [, navigate] = useLocation();
|
||
const [code, setCode] = useState(EXAMPLE_CODE);
|
||
const [compiledOutput, setCompiledOutput] = useState("");
|
||
const [target, setTarget] = useState("javascript");
|
||
const [isCompiling, setIsCompiling] = useState(false);
|
||
const [compileStatus, setCompileStatus] = useState<"idle" | "success" | "error">("idle");
|
||
const [errorMessage, setErrorMessage] = useState("");
|
||
const [activeTab, setActiveTab] = useState<"editor" | "output" | "publish">("editor");
|
||
|
||
// Publishing fields
|
||
const [appName, setAppName] = useState("");
|
||
const [appDescription, setAppDescription] = useState("");
|
||
const [isSaving, setIsSaving] = useState(false);
|
||
|
||
const handleCompile = async () => {
|
||
haptics.medium();
|
||
setIsCompiling(true);
|
||
setCompileStatus("idle");
|
||
setErrorMessage("");
|
||
|
||
try {
|
||
const response = await fetch("/api/aethex/compile", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ code, target }),
|
||
credentials: "include",
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
setCompiledOutput(data.output);
|
||
setCompileStatus("success");
|
||
haptics.success();
|
||
} else {
|
||
setErrorMessage(data.details || data.error || "Compilation failed");
|
||
setCompileStatus("error");
|
||
haptics.error();
|
||
}
|
||
} catch (error) {
|
||
console.error("Compilation error:", error);
|
||
setErrorMessage("Failed to connect to compiler");
|
||
setCompileStatus("error");
|
||
haptics.error();
|
||
} finally {
|
||
setIsCompiling(false);
|
||
}
|
||
};
|
||
|
||
const handleSaveApp = async () => {
|
||
if (!appName.trim()) {
|
||
alert("Please enter an app name");
|
||
return;
|
||
}
|
||
|
||
haptics.medium();
|
||
setIsSaving(true);
|
||
|
||
try {
|
||
const response = await fetch("/api/aethex/apps", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
name: appName,
|
||
description: appDescription,
|
||
source_code: code,
|
||
category: "utility",
|
||
is_public: true,
|
||
targets: [target],
|
||
tags: ["mobile-created"],
|
||
}),
|
||
credentials: "include",
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
haptics.success();
|
||
alert("App published! Check the App Store.");
|
||
setAppName("");
|
||
setAppDescription("");
|
||
} else {
|
||
haptics.error();
|
||
alert(data.error || "Failed to publish app");
|
||
}
|
||
} catch (error) {
|
||
console.error("Save error:", error);
|
||
haptics.error();
|
||
alert("Failed to publish app");
|
||
} finally {
|
||
setIsSaving(false);
|
||
}
|
||
};
|
||
|
||
const handleRunCode = () => {
|
||
if (!compiledOutput) {
|
||
alert("Please compile the code first");
|
||
return;
|
||
}
|
||
|
||
haptics.light();
|
||
|
||
try {
|
||
const sandbox = {
|
||
console: {
|
||
log: (...args: any[]) => {
|
||
const output = args.map((a) => String(a)).join(" ");
|
||
alert(`Output: ${output}`);
|
||
},
|
||
},
|
||
};
|
||
new Function("console", compiledOutput)(sandbox.console);
|
||
haptics.success();
|
||
} catch (error) {
|
||
console.error("Runtime error:", error);
|
||
alert(`Runtime error: ${error}`);
|
||
haptics.error();
|
||
}
|
||
};
|
||
|
||
const loadExample = (example: string) => {
|
||
haptics.light();
|
||
if (example === "hello") {
|
||
setCode(EXAMPLE_CODE);
|
||
} else if (example === "passport") {
|
||
setCode(PASSPORT_EXAMPLE);
|
||
}
|
||
setCompiledOutput("");
|
||
setCompileStatus("idle");
|
||
};
|
||
|
||
const copyToClipboard = (text: string) => {
|
||
navigator.clipboard.writeText(text);
|
||
haptics.success();
|
||
alert("Copied to clipboard!");
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen bg-black text-white pb-20">
|
||
{/* Animated background */}
|
||
<div className="fixed inset-0 opacity-20 pointer-events-none">
|
||
<div className="absolute inset-0 bg-gradient-to-br from-purple-600/10 via-transparent to-pink-600/10" />
|
||
<div className="absolute top-0 left-1/4 w-96 h-96 bg-purple-500/5 rounded-full blur-3xl" />
|
||
<div className="absolute bottom-0 right-1/4 w-96 h-96 bg-pink-500/5 rounded-full blur-3xl" />
|
||
</div>
|
||
|
||
{/* Header */}
|
||
<div className="sticky top-0 z-50 bg-black/90 backdrop-blur-xl border-b border-purple-500/20">
|
||
<div className="flex items-center justify-between px-4 py-4 safe-area-inset-top">
|
||
<button
|
||
onClick={() => {
|
||
haptics.light();
|
||
navigate("/");
|
||
}}
|
||
className="p-2 rounded-lg bg-purple-500/10 border border-purple-500/30 active:scale-95 transition-transform"
|
||
>
|
||
<ArrowLeft className="w-5 h-5 text-purple-300" />
|
||
</button>
|
||
|
||
<div className="flex-1 text-center">
|
||
<div className="flex items-center justify-center gap-2">
|
||
<Code className="w-5 h-5 text-purple-400" />
|
||
<h1 className="text-lg font-bold text-white uppercase tracking-wider">AeThex Studio</h1>
|
||
</div>
|
||
{compileStatus === "success" && (
|
||
<p className="text-xs text-green-400 font-mono flex items-center justify-center gap-1 mt-1">
|
||
<CheckCircle className="w-3 h-3" /> Compiled
|
||
</p>
|
||
)}
|
||
{compileStatus === "error" && (
|
||
<p className="text-xs text-red-400 font-mono flex items-center justify-center gap-1 mt-1">
|
||
<XCircle className="w-3 h-3" /> Error
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
<div className="w-10" />
|
||
</div>
|
||
|
||
{/* Tab Navigation */}
|
||
<div className="flex border-t border-purple-500/20">
|
||
<button
|
||
onClick={() => {
|
||
haptics.light();
|
||
setActiveTab("editor");
|
||
}}
|
||
className={`flex-1 py-3 text-sm font-semibold uppercase tracking-wider transition-colors ${
|
||
activeTab === "editor"
|
||
? "text-purple-300 bg-purple-500/10 border-b-2 border-purple-400"
|
||
: "text-gray-400"
|
||
}`}
|
||
>
|
||
<FileCode className="w-4 h-4 mx-auto mb-1" />
|
||
Editor
|
||
</button>
|
||
<button
|
||
onClick={() => {
|
||
haptics.light();
|
||
setActiveTab("output");
|
||
}}
|
||
className={`flex-1 py-3 text-sm font-semibold uppercase tracking-wider transition-colors ${
|
||
activeTab === "output"
|
||
? "text-purple-300 bg-purple-500/10 border-b-2 border-purple-400"
|
||
: "text-gray-400"
|
||
}`}
|
||
>
|
||
<Code className="w-4 h-4 mx-auto mb-1" />
|
||
Output
|
||
</button>
|
||
<button
|
||
onClick={() => {
|
||
haptics.light();
|
||
setActiveTab("publish");
|
||
}}
|
||
className={`flex-1 py-3 text-sm font-semibold uppercase tracking-wider transition-colors ${
|
||
activeTab === "publish"
|
||
? "text-purple-300 bg-purple-500/10 border-b-2 border-purple-400"
|
||
: "text-gray-400"
|
||
}`}
|
||
>
|
||
<Zap className="w-4 h-4 mx-auto mb-1" />
|
||
Publish
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="relative p-4 space-y-4">
|
||
{activeTab === "editor" && (
|
||
<>
|
||
{/* Target Selection */}
|
||
<div className="bg-gradient-to-r from-purple-900/30 to-pink-900/30 border border-purple-500/30 rounded-xl p-4">
|
||
<label className="block text-xs text-purple-300 font-mono uppercase mb-2">
|
||
Compile Target
|
||
</label>
|
||
<select
|
||
value={target}
|
||
onChange={(e) => setTarget(e.target.value)}
|
||
className="w-full bg-black/50 border border-purple-500/30 rounded-lg px-4 py-3 text-white font-mono"
|
||
>
|
||
<option value="javascript">JavaScript (Web)</option>
|
||
<option value="roblox">Lua (Roblox)</option>
|
||
<option value="uefn">Verse (UEFN)</option>
|
||
<option value="unity">C# (Unity)</option>
|
||
</select>
|
||
</div>
|
||
|
||
{/* Examples */}
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => loadExample("hello")}
|
||
className="flex-1 py-2 px-3 text-xs font-semibold bg-purple-500/10 border border-purple-500/30 rounded-lg text-purple-300 active:scale-95 transition-transform"
|
||
>
|
||
Hello World
|
||
</button>
|
||
<button
|
||
onClick={() => loadExample("passport")}
|
||
className="flex-1 py-2 px-3 text-xs font-semibold bg-purple-500/10 border border-purple-500/30 rounded-lg text-purple-300 active:scale-95 transition-transform"
|
||
>
|
||
Passport Auth
|
||
</button>
|
||
</div>
|
||
|
||
{/* Code Editor */}
|
||
<div className="bg-gradient-to-br from-gray-900 to-gray-800 border border-purple-500/30 rounded-xl p-4">
|
||
<div className="flex items-center justify-between mb-3">
|
||
<label className="text-xs text-purple-300 font-mono uppercase">Source Code</label>
|
||
<button
|
||
onClick={() => copyToClipboard(code)}
|
||
className="p-2 rounded-lg bg-purple-500/10 border border-purple-500/30 active:scale-95 transition-transform"
|
||
>
|
||
<Copy className="w-4 h-4 text-purple-300" />
|
||
</button>
|
||
</div>
|
||
<textarea
|
||
value={code}
|
||
onChange={(e) => setCode(e.target.value)}
|
||
className="w-full h-96 bg-black/50 border border-purple-500/20 rounded-lg p-3 text-sm font-mono text-white resize-none focus:outline-none focus:border-purple-400"
|
||
placeholder="Enter AeThex code..."
|
||
spellCheck={false}
|
||
/>
|
||
</div>
|
||
|
||
{/* Compile Button */}
|
||
<button
|
||
onClick={handleCompile}
|
||
disabled={isCompiling}
|
||
className="w-full bg-gradient-to-r from-purple-600 to-pink-600 text-white font-bold py-4 rounded-xl active:scale-98 transition-transform disabled:opacity-50 flex items-center justify-center gap-2"
|
||
>
|
||
{isCompiling ? (
|
||
<>
|
||
<Loader2 className="w-5 h-5 animate-spin" />
|
||
Compiling...
|
||
</>
|
||
) : (
|
||
<>
|
||
<Zap className="w-5 h-5" />
|
||
Compile Code
|
||
</>
|
||
)}
|
||
</button>
|
||
</>
|
||
)}
|
||
|
||
{activeTab === "output" && (
|
||
<>
|
||
{/* Compiled Output */}
|
||
<div className="bg-gradient-to-br from-gray-900 to-gray-800 border border-purple-500/30 rounded-xl p-4">
|
||
<div className="flex items-center justify-between mb-3">
|
||
<label className="text-xs text-purple-300 font-mono uppercase">Compiled {target}</label>
|
||
{compiledOutput && (
|
||
<button
|
||
onClick={() => copyToClipboard(compiledOutput)}
|
||
className="p-2 rounded-lg bg-purple-500/10 border border-purple-500/30 active:scale-95 transition-transform"
|
||
>
|
||
<Copy className="w-4 h-4 text-purple-300" />
|
||
</button>
|
||
)}
|
||
</div>
|
||
<pre className="w-full h-96 bg-black/50 border border-purple-500/20 rounded-lg p-3 text-xs font-mono text-green-400 overflow-auto">
|
||
{compiledOutput || "No output yet. Compile your code first."}
|
||
</pre>
|
||
</div>
|
||
|
||
{/* Error Message */}
|
||
{errorMessage && (
|
||
<div className="bg-red-500/10 border border-red-500/30 rounded-xl p-4">
|
||
<div className="flex items-start gap-3">
|
||
<XCircle className="w-5 h-5 text-red-400 flex-shrink-0 mt-0.5" />
|
||
<div className="flex-1">
|
||
<p className="text-sm font-semibold text-red-400 mb-1">Compilation Error</p>
|
||
<p className="text-xs text-red-300 font-mono">{errorMessage}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Run Button */}
|
||
{compiledOutput && (
|
||
<button
|
||
onClick={handleRunCode}
|
||
className="w-full bg-gradient-to-r from-green-600 to-emerald-600 text-white font-bold py-4 rounded-xl active:scale-98 transition-transform flex items-center justify-center gap-2"
|
||
>
|
||
<Play className="w-5 h-5" />
|
||
Run Code
|
||
</button>
|
||
)}
|
||
</>
|
||
)}
|
||
|
||
{activeTab === "publish" && (
|
||
<>
|
||
{/* Publish Form */}
|
||
<div className="space-y-4">
|
||
<div className="bg-gradient-to-r from-purple-900/30 to-pink-900/30 border border-purple-500/30 rounded-xl p-4">
|
||
<label className="block text-xs text-purple-300 font-mono uppercase mb-2">
|
||
App Name
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={appName}
|
||
onChange={(e) => setAppName(e.target.value)}
|
||
placeholder="My Awesome Game"
|
||
className="w-full bg-black/50 border border-purple-500/30 rounded-lg px-4 py-3 text-white focus:outline-none focus:border-purple-400"
|
||
/>
|
||
</div>
|
||
|
||
<div className="bg-gradient-to-r from-purple-900/30 to-pink-900/30 border border-purple-500/30 rounded-xl p-4">
|
||
<label className="block text-xs text-purple-300 font-mono uppercase mb-2">
|
||
Description
|
||
</label>
|
||
<textarea
|
||
value={appDescription}
|
||
onChange={(e) => setAppDescription(e.target.value)}
|
||
placeholder="Describe what your app does..."
|
||
rows={4}
|
||
className="w-full bg-black/50 border border-purple-500/30 rounded-lg px-4 py-3 text-white resize-none focus:outline-none focus:border-purple-400"
|
||
/>
|
||
</div>
|
||
|
||
<div className="bg-purple-500/10 border border-purple-500/30 rounded-xl p-4">
|
||
<p className="text-xs text-purple-300 font-mono">
|
||
ℹ️ Your app will be compiled and published to the App Store for others to install and use.
|
||
Make sure your code is tested!
|
||
</p>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleSaveApp}
|
||
disabled={isSaving || !appName.trim()}
|
||
className="w-full bg-gradient-to-r from-pink-600 to-purple-600 text-white font-bold py-4 rounded-xl active:scale-98 transition-transform disabled:opacity-50 flex items-center justify-center gap-2"
|
||
>
|
||
{isSaving ? (
|
||
<>
|
||
<Loader2 className="w-5 h-5 animate-spin" />
|
||
Publishing...
|
||
</>
|
||
) : (
|
||
<>
|
||
<Save className="w-5 h-5" />
|
||
Publish to App Store
|
||
</>
|
||
)}
|
||
</button>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|