| import React, { useState, useEffect, useRef } from "react"; |
| import { io, Socket } from "socket.io-client"; |
| import { |
| Terminal, |
| Settings, |
| Play, |
| Square, |
| Cpu, |
| Globe, |
| User, |
| Key, |
| MessageSquare, |
| Activity |
| } from "lucide-react"; |
| import { motion, AnimatePresence } from "motion/react"; |
|
|
| interface LogEntry { |
| message: string; |
| type: "info" | "success" | "error" | "chat"; |
| timestamp: string; |
| } |
|
|
| export default function App() { |
| const [socket, setSocket] = useState<Socket | null>(null); |
| const [config, setConfig] = useState(() => { |
| const saved = localStorage.getItem("ai_architect_config"); |
| if (saved) { |
| try { |
| return JSON.parse(saved); |
| } catch (e) {} |
| } |
| return { |
| host: "localhost", |
| port: "25565", |
| version: "", |
| username: "AI_Architect", |
| apiKey: "", |
| model: "gemini-3-flash-preview", |
| apiType: "google", |
| apiBaseUrl: "https://api.openai.com/v1", |
| customModel: "gpt-4o", |
| maxBlocks: 10000, |
| edition: "java", |
| auth: "offline" |
| }; |
| }); |
|
|
| useEffect(() => { |
| localStorage.setItem("ai_architect_config", JSON.stringify(config)); |
| }, [config]); |
|
|
| const [status, setStatus] = useState({ connected: false, username: "" }); |
| const [logs, setLogs] = useState<LogEntry[]>([]); |
| const [msaAuth, setMsaAuth] = useState<{code: string, uri: string} | null>(null); |
| const logEndRef = useRef<HTMLDivElement>(null); |
|
|
| useEffect(() => { |
| const newSocket = io(); |
| setSocket(newSocket); |
|
|
| newSocket.on("bot-log", (log: LogEntry) => { |
| setLogs((prev) => [...prev.slice(-100), log]); |
| }); |
|
|
| newSocket.on("bot-status", (status) => { |
| setStatus(status); |
| if (status.connected) { |
| setMsaAuth(null); |
| } |
| }); |
|
|
| newSocket.on("bot-msa-code", (data) => { |
| setMsaAuth(data); |
| }); |
|
|
| return () => { |
| newSocket.disconnect(); |
| }; |
| }, []); |
|
|
| useEffect(() => { |
| logEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| }, [logs]); |
|
|
| const handleConnect = () => { |
| if (!socket) return; |
| if (!config.apiKey) { |
| alert("Please enter a Gemini API Key"); |
| return; |
| } |
| socket.emit("connect-bot", config); |
| }; |
|
|
| const handleDisconnect = () => { |
| if (!socket) return; |
| socket.emit("disconnect-bot"); |
| }; |
|
|
| return ( |
| <div className="min-h-screen bg-[#0a0a0a] text-[#e0e0e0] font-mono p-4 md:p-8 flex flex-col gap-6"> |
| {/* Header */} |
| <header className="flex items-center justify-between border-b border-[#333] pb-4"> |
| <div className="flex items-center gap-3"> |
| <div className="p-2 bg-[#1a1a1a] border border-[#333] rounded"> |
| <Cpu className="w-6 h-6 text-[#00ff00]" /> |
| </div> |
| <div> |
| <h1 className="text-xl font-bold tracking-tighter uppercase">Minecraft AI Architect</h1> |
| <p className="text-xs text-[#666]">v1.0.0 // SPIGOT 1.21.1 INTEGRATION</p> |
| </div> |
| </div> |
| <div className="flex items-center gap-4"> |
| <div className="flex items-center gap-2 px-3 py-1 bg-[#1a1a1a] border border-[#333] rounded-full"> |
| <div className={`w-2 h-2 rounded-full ${status.connected ? "bg-[#00ff00] animate-pulse" : "bg-[#ff0000]"}`} /> |
| <span className="text-[10px] uppercase tracking-widest"> |
| {status.connected ? `ONLINE: ${status.username}` : "OFFLINE"} |
| </span> |
| </div> |
| </div> |
| </header> |
| |
| <main className="grid grid-cols-1 lg:grid-cols-12 gap-6 flex-grow"> |
| {/* Configuration Panel */} |
| <section className="lg:col-span-4 flex flex-col gap-4"> |
| <div className="bg-[#111] border border-[#333] rounded-lg p-5 flex flex-col gap-4"> |
| <div className="flex items-center gap-2 mb-2"> |
| <Settings className="w-4 h-4 text-[#666]" /> |
| <h2 className="text-xs uppercase tracking-widest font-bold">Bot Configuration</h2> |
| </div> |
| |
| <div className="space-y-4"> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Server Host</label> |
| <div className="relative"> |
| <Globe className="absolute left-3 top-1/2 -translate-y-1/2 w-3 h-3 text-[#444]" /> |
| <input |
| type="text" |
| value={config.host} |
| onChange={(e) => setConfig({...config, host: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 pl-9 text-sm focus:border-[#00ff00] outline-none transition-colors" |
| placeholder="localhost" |
| /> |
| </div> |
| </div> |
| |
| <div className="grid grid-cols-3 gap-4"> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Port</label> |
| <input |
| type="text" |
| value={config.port} |
| onChange={(e) => setConfig({...config, port: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none" |
| placeholder="25565" |
| /> |
| </div> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Edition</label> |
| <select |
| value={config.edition || "java"} |
| onChange={(e) => setConfig({...config, edition: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none appearance-none cursor-pointer" |
| > |
| <option value="java">Java Edition</option> |
| <option value="bedrock">Bedrock (via Geyser)</option> |
| </select> |
| </div> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Version</label> |
| <input |
| type="text" |
| value={config.version} |
| onChange={(e) => setConfig({...config, version: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none" |
| placeholder="Auto (e.g. 1.21.1)" |
| /> |
| </div> |
| </div> |
| |
| <div className="grid grid-cols-2 gap-4"> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Bot Name / Email</label> |
| <div className="relative"> |
| <User className="absolute left-3 top-1/2 -translate-y-1/2 w-3 h-3 text-[#444]" /> |
| <input |
| type="text" |
| value={config.username} |
| onChange={(e) => setConfig({...config, username: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 pl-9 text-sm focus:border-[#00ff00] outline-none" |
| placeholder="Username or Email" |
| /> |
| </div> |
| </div> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Authentication</label> |
| <select |
| value={config.auth || "offline"} |
| onChange={(e) => setConfig({...config, auth: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none appearance-none cursor-pointer" |
| > |
| <option value="offline">Offline (Cracked)</option> |
| <option value="microsoft">Microsoft (Premium)</option> |
| </select> |
| </div> |
| </div> |
| |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">API Key (Gemini or Custom)</label> |
| <div className="relative"> |
| <Key className="absolute left-3 top-1/2 -translate-y-1/2 w-3 h-3 text-[#444]" /> |
| <input |
| type="password" |
| value={config.apiKey} |
| onChange={(e) => setConfig({...config, apiKey: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 pl-9 text-sm focus:border-[#00ff00] outline-none transition-colors" |
| placeholder="Enter your API Key..." |
| /> |
| </div> |
| </div> |
| |
| <div className="grid grid-cols-2 gap-4"> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">API Provider</label> |
| <select |
| value={config.apiType} |
| onChange={(e) => setConfig({...config, apiType: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none appearance-none cursor-pointer" |
| > |
| <option value="google">Google Gemini</option> |
| <option value="openai">OpenAI / Custom API</option> |
| </select> |
| </div> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Max Blocks Limit</label> |
| <input |
| type="number" |
| value={config.maxBlocks} |
| onChange={(e) => setConfig({...config, maxBlocks: parseInt(e.target.value) || 1000})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none" |
| /> |
| </div> |
| </div> |
| |
| {config.apiType === 'google' ? ( |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Gemini Model</label> |
| <select |
| value={config.model} |
| onChange={(e) => setConfig({...config, model: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none appearance-none cursor-pointer" |
| > |
| <option value="gemini-pro-latest">🌌 Gemini Pro Latest (Latest Pro)</option> |
| <option value="gemini-flash-latest">🌌 Gemini Flash Latest (Latest Flash)</option> |
| <option value="gemini-3-pro-preview">🌌 Gemini 3 Pro (Gen 3 Pro)</option> |
| <option value="gemini-3-flash-preview">🌌 Gemini 3 Flash (Gen 3 Flash)</option> |
| <option value="gemini-2.5-pro">💎 Gemini 2.5 Pro (Powerful)</option> |
| <option value="gemini-2.5-flash">⚡ Gemini 2.5 Flash (Fast)</option> |
| <option value="gemini-2.5-flash-lite">🔦 Gemini 2.5 Lite (Lite)</option> |
| </select> |
| </div> |
| ) : ( |
| <div className="grid grid-cols-2 gap-4"> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">API Base URL</label> |
| <input |
| type="text" |
| value={config.apiBaseUrl} |
| onChange={(e) => setConfig({...config, apiBaseUrl: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none" |
| placeholder="https://api.openai.com/v1" |
| /> |
| </div> |
| <div className="space-y-1"> |
| <label className="text-[10px] text-[#666] uppercase">Model Name</label> |
| <input |
| type="text" |
| value={config.customModel} |
| onChange={(e) => setConfig({...config, customModel: e.target.value})} |
| className="w-full bg-[#050505] border border-[#222] rounded p-2 text-sm focus:border-[#00ff00] outline-none" |
| placeholder="gpt-4o" |
| /> |
| </div> |
| </div> |
| )} |
| </div> |
| |
| <div className="mt-4 flex gap-2"> |
| {!status.connected ? ( |
| <button |
| onClick={handleConnect} |
| className="flex-grow flex items-center justify-center gap-2 bg-[#00ff00] text-black font-bold py-3 rounded hover:bg-[#00cc00] transition-colors uppercase text-xs" |
| > |
| <Play className="w-4 h-4 fill-current" /> Initialize Bot |
| </button> |
| ) : ( |
| <button |
| onClick={handleDisconnect} |
| className="flex-grow flex items-center justify-center gap-2 bg-[#ff0000] text-white font-bold py-3 rounded hover:bg-[#cc0000] transition-colors uppercase text-xs" |
| > |
| <Square className="w-4 h-4 fill-current" /> Terminate Session |
| </button> |
| )} |
| </div> |
| |
| {msaAuth && ( |
| <div className="mt-4 bg-[#1a1a1a] border border-[#00ff00] rounded p-4 flex flex-col items-center text-center gap-2 animate-pulse"> |
| <h3 className="text-[#00ff00] font-bold uppercase text-xs tracking-widest">Microsoft Authentication Required</h3> |
| <p className="text-xs text-[#ccc]">Please sign in to your Microsoft account to continue.</p> |
| <div className="bg-[#050505] px-4 py-2 rounded border border-[#333] font-mono text-lg tracking-widest text-white my-2"> |
| {msaAuth.code} |
| </div> |
| <a |
| href={msaAuth.uri} |
| target="_blank" |
| rel="noreferrer" |
| className="text-xs text-[#00ff00] hover:underline" |
| > |
| Click here to open login page ({msaAuth.uri}) |
| </a> |
| </div> |
| )} |
| </div> |
| |
| <div className="bg-[#111] border border-[#333] rounded-lg p-5"> |
| <div className="flex items-center gap-2 mb-4"> |
| <Activity className="w-4 h-4 text-[#666]" /> |
| <h2 className="text-xs uppercase tracking-widest font-bold">Quick Commands</h2> |
| </div> |
| <div className="grid grid-cols-1 gap-2"> |
| <p className="text-[10px] text-[#666] mb-2 leading-relaxed"> |
| Use these commands in Minecraft chat to interact with the AI Architect: |
| </p> |
| <div className="bg-[#050505] border border-[#222] p-2 rounded flex flex-col gap-1"> |
| <code className="text-[#00ff00] text-xs">!build [prompt]</code> |
| <span className="text-[9px] text-[#444]">Start a new build (or use !б)</span> |
| <div className="h-px bg-[#222] my-1" /> |
| <code className="text-[#00ff00] text-xs">!next [prompt]</code> |
| <span className="text-[9px] text-[#444]">Modify previous build (or use !н)</span> |
| </div> |
| </div> |
| </div> |
| </section> |
| |
| {/* Console / Logs */} |
| <section className="lg:col-span-8 flex flex-col"> |
| <div className="bg-[#050505] border border-[#333] rounded-lg flex flex-col h-[600px]"> |
| <div className="flex items-center justify-between px-4 py-2 border-b border-[#333] bg-[#111] rounded-t-lg"> |
| <div className="flex items-center gap-2"> |
| <Terminal className="w-3 h-3 text-[#666]" /> |
| <span className="text-[10px] uppercase tracking-widest font-bold">System Terminal</span> |
| </div> |
| <div className="flex gap-1"> |
| <div className="w-2 h-2 rounded-full bg-[#333]" /> |
| <div className="w-2 h-2 rounded-full bg-[#333]" /> |
| <div className="w-2 h-2 rounded-full bg-[#333]" /> |
| </div> |
| </div> |
| |
| <div className="flex-grow overflow-y-auto p-4 space-y-2 font-mono text-xs custom-scrollbar"> |
| <AnimatePresence initial={false}> |
| {logs.length === 0 && ( |
| <div className="text-[#333] italic">Waiting for connection...</div> |
| )} |
| {logs.map((log, i) => ( |
| <motion.div |
| key={i} |
| initial={{ opacity: 0, x: -5 }} |
| animate={{ opacity: 1, x: 0 }} |
| className="flex gap-3 border-l border-[#222] pl-3 py-1" |
| > |
| <span className="text-[#444] shrink-0">[{new Date(log.timestamp).toLocaleTimeString()}]</span> |
| <span className={` |
| ${log.type === "error" ? "text-[#ff0000]" : ""} |
| ${log.type === "success" ? "text-[#00ff00]" : ""} |
| ${log.type === "chat" ? "text-[#00ffff]" : ""} |
| ${log.type === "info" ? "text-[#888]" : ""} |
| `}> |
| {log.type === "chat" && <MessageSquare className="inline w-3 h-3 mr-2 opacity-50" />} |
| {log.message} |
| </span> |
| </motion.div> |
| ))} |
| </AnimatePresence> |
| <div ref={logEndRef} /> |
| </div> |
| </div> |
| </section> |
| </main> |
| |
| <footer className="mt-auto pt-4 border-t border-[#333] flex justify-between items-center text-[10px] text-[#444] uppercase tracking-widest"> |
| <span>© 2026 AI ARCHITECT PROTOCOL</span> |
| <div className="flex gap-4"> |
| <span>LATENCY: 42MS</span> |
| <span>UPTIME: 99.9%</span> |
| </div> |
| </footer> |
| |
| <style>{` |
| .custom-scrollbar::-webkit-scrollbar { |
| width: 4px; |
| } |
| .custom-scrollbar::-webkit-scrollbar-track { |
| background: #050505; |
| } |
| .custom-scrollbar::-webkit-scrollbar-thumb { |
| background: #222; |
| border-radius: 10px; |
| } |
| .custom-scrollbar::-webkit-scrollbar-thumb:hover { |
| background: #333; |
| } |
| `}</style> |
| </div> |
| ); |
| } |