Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect, useRef } from 'react'; | |
| import { createRoot } from 'react-dom/client'; | |
| import { GoogleGenAI } from "@google/genai"; | |
| import { | |
| ChevronDown, Github, FileText, ArrowLeft, Cpu, Globe, Zap, | |
| Code2, Copy, Check, Trash2, Download, Upload, Play, Eye, | |
| Code as CodeIcon, Sun, Moon, Sparkles | |
| } from 'lucide-react'; | |
| // --- Types & Config --- | |
| declare global { | |
| interface Window { Prism: any; } | |
| } | |
| type ViewState = 'app' | 'docs' | 'editor'; | |
| type Theme = 'dark' | 'light'; | |
| const LANGUAGES = [ | |
| 'JavaScript', 'TypeScript', 'Python', 'Java', 'C++', 'C#', | |
| 'Go', 'Rust', 'Swift', 'Kotlin', 'PHP', 'Ruby', 'SQL', 'HTML', 'CSS', | |
| 'Bash', 'JSON', 'YAML', 'Markdown', 'Natural Language' | |
| ]; | |
| const LANGUAGE_MAP: Record<string, string> = { | |
| 'JavaScript': 'javascript', 'TypeScript': 'typescript', 'Python': 'python', | |
| 'Java': 'java', 'C++': 'cpp', 'C#': 'csharp', 'Go': 'go', 'Rust': 'rust', | |
| 'Swift': 'swift', 'Kotlin': 'kotlin', 'PHP': 'php', 'Ruby': 'ruby', | |
| 'SQL': 'sql', 'HTML': 'markup', 'CSS': 'css', 'Bash': 'bash', | |
| 'JSON': 'json', 'YAML': 'yaml', 'Markdown': 'markdown', 'Natural Language': 'text' | |
| }; | |
| const SHARED_CODE_STYLES: React.CSSProperties = { | |
| fontFamily: '"JetBrains Mono", monospace', | |
| fontSize: '0.875rem', | |
| lineHeight: '1.625rem', | |
| padding: '1rem', | |
| whiteSpace: 'pre', | |
| wordSpacing: 'normal', | |
| wordBreak: 'normal', | |
| tabSize: 2, | |
| MozTabSize: 2, | |
| boxSizing: 'border-box', | |
| }; | |
| // --- Logic: Language Detection --- | |
| const detectLanguage = (code: string): string => { | |
| if (!code.trim()) return 'Natural Language'; | |
| const snippets = { | |
| HTML: /<[a-z][\s\S]*>/i, | |
| JSON: /^[\s]*[\{\[]/, | |
| SQL: /\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|CREATE|DROP)\b/i, | |
| Python: /\b(def|import|if __name__ == "__main__":|print\(f?['"])\b/, | |
| JavaScript: /\b(const|let|var|function|async|await|console\.log)\b/, | |
| Rust: /\b(fn|pub|use|let mut|match|impl)\b/, | |
| Go: /\b(package|import|func|go\s|chan|select)\b/, | |
| Swift: /\b(import|let|var|func|enum|struct|extension)\b/, | |
| Bash: /#!\/bin\/(bash|sh)|sudo\s/, | |
| Markdown: /^#\s|!\[.*\]\(.*\)|\[.*\]\(.*\)/m, | |
| YAML: /^[\w-]+:\s/m, | |
| CSS: /[\.#][\w-]+\s*\{/ | |
| }; | |
| for (const [lang, regex] of Object.entries(snippets)) { | |
| if (regex.test(code)) return lang; | |
| } | |
| return 'Natural Language'; | |
| }; | |
| const parseMarkdownToHTML = (text: string) => { | |
| return text | |
| .replace(/^### (.*$)/gim, '<h3>$1</h3>') | |
| .replace(/^## (.*$)/gim, '<h2>$1</h2>') | |
| .replace(/^# (.*$)/gim, '<h1>$1</h1>') | |
| .replace(/\*\*(.*)\*\*/gim, '<b>$1</b>') | |
| .replace(/\*(.*)\*/gim, '<i>$1</i>') | |
| .replace(/!\[(.*?)\]\((.*?)\)/gim, "<img alt='$1' src='$2' />") | |
| .replace(/\[(.*?)\]\((.*?)\)/gim, "<a href='$2'>$1</a>") | |
| .replace(/`([^`]+)`/g, '<code>$1</code>') | |
| .replace(/^\s*-\s+(.*)/gim, '<li>$1</li>') | |
| .replace(/(<li>.*<\/li>)/gim, '<ul>$1</ul>') | |
| .replace(/<\/ul>\s*<ul>/gim, '') | |
| .replace(/\n$/gim, '<br />'); | |
| }; | |
| // --- Shared Components --- | |
| const LogoSVG = ({ className, theme }: { className?: string, theme: Theme }) => ( | |
| <svg className={className} width="32" height="32" viewBox="0 0 65 65" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| <path d="M32.447 0c.68 0 1.272.465 1.438 1.125a39 39 0 0 0 2 5.905q3.23 7.5 8.854 13.125q5.626 5.626 13.125 8.855a39 39 0 0 0 5.905 1.999c.66.166 1.125.758 1.125 1.438s-.464 1.273-1.125 1.439a39 39 0 0 0-5.905 1.999q-7.5 3.23-13.125 8.854q-5.625 5.627-8.854 13.125a39 39 0 0 0-2 5.906a1.485 1.485 0 0 1-1.438 1.124c-.68 0-1.272-.464-1.438-1.125a39 39 0 0 0-2-5.905q-3.228-7.5-8.854-13.125T7.03 35.885a39 39 0 0 0-5.905-2A1.485 1.485 0 0 1 0 32.448c0-.68.465-1.272 1.125-1.438a39 39 0 0 0 5.905-2q7.5-3.229 13.125-8.854C25.78 14.53 26.857 12.03 29.01 7.03a39 39 0 0 0 1.999-5.905A1.485 1.485 0 0 1 32.447 0" fill="url(#logo-grad)"/> | |
| <defs> | |
| <linearGradient id="logo-grad" x1="18.447" x2="52.153" y1="43.42" y2="15.004" gradientUnits="userSpaceOnUse"> | |
| <stop offset="0" stopColor={theme === 'dark' ? "#4893fc" : "#2563eb"}/><stop offset="1" stopColor={theme === 'dark' ? "#bd99fe" : "#9333ea"}/> | |
| </linearGradient> | |
| </defs> | |
| </svg> | |
| ); | |
| const Toast = ({ message, onClose }: { message: string; onClose: () => void }) => { | |
| useEffect(() => { const timer = setTimeout(onClose, 3000); return () => clearTimeout(timer); }, [onClose]); | |
| return ( | |
| <div className="fixed bottom-8 left-1/2 -translate-x-1/2 z-[100] animate-[slideUp_0.3s_ease-out]"> | |
| <div className="bg-neutral-900/90 border border-gray-700 text-white px-4 py-2 rounded-lg shadow-2xl backdrop-blur-md flex items-center gap-2 text-sm font-mono"> | |
| <span className="text-cyan-400">●</span> {message} | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| const LanguageSelect = ({ language, onChange, theme }: { language: string; onChange: (val: string) => void; theme: Theme }) => { | |
| const [isOpen, setIsOpen] = useState(false); | |
| const ref = useRef<HTMLDivElement>(null); | |
| useEffect(() => { | |
| const click = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setIsOpen(false); }; | |
| document.addEventListener('mousedown', click); return () => document.removeEventListener('mousedown', click); | |
| }, []); | |
| return ( | |
| <div className="relative w-full" ref={ref}> | |
| <button onClick={() => setIsOpen(!isOpen)} className={`w-full flex items-center justify-between border px-4 py-3 rounded-lg backdrop-blur-sm transition-all text-sm font-mono ${theme === 'dark' ? 'bg-black/40 border-cyan-500/30 text-cyan-400 hover:border-cyan-500/60' : 'bg-white border-gray-200 text-gray-900 hover:border-cyan-500 shadow-sm'}`}> | |
| <span>{language}</span> | |
| <ChevronDown size={16} className={`transition-transform ${isOpen ? 'rotate-180' : ''}`} /> | |
| </button> | |
| {isOpen && ( | |
| <div className={`absolute top-full left-0 right-0 mt-2 max-h-60 overflow-y-auto border rounded-lg shadow-xl z-50 custom-scrollbar ${theme === 'dark' ? 'bg-neutral-900 border-gray-800' : 'bg-white border-gray-200'}`}> | |
| {LANGUAGES.map((lang) => ( | |
| <button key={lang} onClick={() => { onChange(lang); setIsOpen(false); }} className={`w-full text-left px-4 py-2 text-sm font-mono transition-colors ${language === lang ? 'bg-cyan-500/10 text-cyan-500' : theme === 'dark' ? 'text-gray-400 hover:bg-gray-800' : 'text-gray-600 hover:bg-gray-100'}`}> | |
| {lang} | |
| </button> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| const CodeEditor = ({ code, language, onChange, readOnly = false, placeholder, theme }: { code: string; language: string; onChange?: (val: string) => void; readOnly?: boolean; placeholder?: string; theme: Theme }) => { | |
| const textareaRef = useRef<HTMLTextAreaElement>(null); | |
| const preRef = useRef<HTMLPreElement>(null); | |
| const [highlightedCode, setHighlightedCode] = useState(''); | |
| useEffect(() => { | |
| const prismLang = LANGUAGE_MAP[language] || 'text'; | |
| if (window.Prism && window.Prism.languages[prismLang]) { | |
| setHighlightedCode(window.Prism.highlight(code || '', window.Prism.languages[prismLang], prismLang)); | |
| } else { | |
| setHighlightedCode((code || '').replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")); | |
| } | |
| }, [code, language]); | |
| const syncScroll = () => { if (textareaRef.current && preRef.current) { preRef.current.scrollTop = textareaRef.current.scrollTop; preRef.current.scrollLeft = textareaRef.current.scrollLeft; } }; | |
| return ( | |
| <div className="relative w-full h-full overflow-hidden"> | |
| <pre ref={preRef} aria-hidden="true" style={SHARED_CODE_STYLES} className="absolute inset-0 m-0 pointer-events-none !bg-transparent overflow-hidden z-0"> | |
| <code className={`language-${LANGUAGE_MAP[language] || 'text'} !bg-transparent !p-0 block min-w-full`} dangerouslySetInnerHTML={{ __html: highlightedCode + '\n' }} /> | |
| </pre> | |
| <textarea ref={textareaRef} value={code} onChange={(e) => onChange && onChange(e.target.value)} onScroll={syncScroll} readOnly={readOnly} placeholder={placeholder} style={SHARED_CODE_STYLES} className="absolute inset-0 w-full h-full bg-transparent text-transparent caret-cyan-500 resize-none focus:outline-none placeholder-gray-500 overflow-auto custom-scrollbar border-none ring-0 z-10" spellCheck={false} /> | |
| </div> | |
| ); | |
| }; | |
| const EditorFooter = ({ onClear, textToCopy, colorClass, theme }: { onClear?: () => void; textToCopy: string; colorClass: string; theme: Theme }) => { | |
| const [copied, setCopied] = useState(false); | |
| const handleCopy = async () => { if (!textToCopy) return; await navigator.clipboard.writeText(textToCopy); setCopied(true); setTimeout(() => setCopied(false), 2000); }; | |
| return ( | |
| <div className={`flex items-center justify-end gap-4 px-4 py-2 border-t backdrop-blur-sm ${theme === 'dark' ? 'border-white/5 bg-black/20' : 'border-gray-100 bg-gray-50'}`}> | |
| {onClear && ( | |
| <button onClick={onClear} className={`flex items-center gap-2 text-[10px] font-mono opacity-50 hover:opacity-100 hover:text-red-500 transition-all ${colorClass}`}> | |
| <Trash2 size={12} /> CLEAR | |
| </button> | |
| )} | |
| <button onClick={handleCopy} className={`flex items-center gap-2 text-[10px] font-mono opacity-50 hover:opacity-100 transition-all ${colorClass}`}> | |
| {copied ? <Check size={12} className="text-green-500" /> : <Copy size={12} />} {copied ? 'COPIED' : 'COPY'} | |
| </button> | |
| </div> | |
| ); | |
| }; | |
| const Snippet = ({ code, filename, theme }: { code: string; filename: string; theme: Theme }) => { | |
| const [copied, setCopied] = useState(false); | |
| const handleCopy = async () => { await navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 2000); }; | |
| return ( | |
| <div className={`rounded-xl overflow-hidden border mb-4 ${theme === 'dark' ? 'bg-neutral-950 border-white/5' : 'bg-gray-50 border-gray-200 shadow-sm'}`}> | |
| <div className={`flex items-center justify-between px-4 py-3 border-b ${theme === 'dark' ? 'border-white/5 bg-white/5' : 'border-gray-200 bg-gray-100'}`}> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-2 h-2 rounded-full bg-red-500/40" /><div className="w-2 h-2 rounded-full bg-yellow-500/40" /><div className="w-2 h-2 rounded-full bg-green-500/40" /> | |
| <span className="ml-2 text-[10px] font-mono text-gray-500 uppercase tracking-widest">{filename}</span> | |
| </div> | |
| <button onClick={handleCopy} className={`text-xs font-mono transition-colors ${theme === 'dark' ? 'text-gray-400 hover:text-white' : 'text-gray-500 hover:text-black'}`}> | |
| {copied ? 'COPIED' : 'COPY'} | |
| </button> | |
| </div> | |
| <div className={`p-6 font-mono text-sm leading-relaxed overflow-x-auto custom-scrollbar ${theme === 'dark' ? 'text-gray-400 bg-black/40' : 'text-gray-700 bg-white'}`}> | |
| <pre className="whitespace-pre">{code}</pre> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| // --- View Components --- | |
| const DocsView = ({ onBack, theme }: { onBack: () => void; theme: Theme }) => { | |
| const jsCode = `// Prime Discovery Logic | |
| function findPrimes(limit) { | |
| const results = []; | |
| for (let i = 2; i <= limit; i++) { | |
| let isPrime = true; | |
| for (let j = 2; j <= Math.sqrt(i); j++) { | |
| if (i % j === 0) { isPrime = false; break; } | |
| } | |
| if (isPrime) results.push(i); | |
| } | |
| return results; | |
| } | |
| const primes = findPrimes(50); | |
| console.log("Calculated Primes:", primes);`; | |
| const htmlCode = `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Prime Discovery</title> | |
| </head> | |
| <body> | |
| <script> | |
| ${jsCode} | |
| </script> | |
| </body> | |
| </html>`; | |
| return ( | |
| <div className="relative z-30 flex-1 w-full max-w-[900px] mx-auto px-4 pb-20 animate-[fadeIn_0.5s_ease-out]"> | |
| <div className="mb-12"> | |
| <button onClick={onBack} className={`flex items-center gap-2 text-xs font-mono transition-colors group ${theme === 'dark' ? 'text-cyan-400' : 'text-cyan-600'}`}> | |
| <ArrowLeft size={16} className="group-hover:-translate-x-1 transition-transform" /> RETURN_TO_SYSTEM | |
| </button> | |
| </div> | |
| <header className={`mb-16 border-b pb-12 ${theme === 'dark' ? 'border-white/5' : 'border-gray-200'}`}> | |
| <div className={`inline-block px-3 py-1 rounded-full border text-[10px] font-mono mb-4 tracking-widest uppercase ${theme === 'dark' ? 'bg-cyan-500/10 border-cyan-500/20 text-cyan-400' : 'bg-cyan-50 border-cyan-200 text-cyan-700'}`}>Documentation V3.0</div> | |
| <h2 className={`text-4xl sm:text-5xl font-black tracking-tighter mb-6 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>Master the <span className="text-transparent bg-gradient-to-r from-cyan-400 to-purple-500 bg-clip-text">AXIS</span> Engine</h2> | |
| <p className={`font-mono text-sm leading-relaxed max-w-2xl ${theme === 'dark' ? 'text-gray-400' : 'text-gray-600'}`}>AXIS is a precision tool for code translation and multi-language logic synthesis.</p> | |
| </header> | |
| <div className="space-y-12"> | |
| <section> | |
| <div className="flex items-center gap-3 mb-8"> | |
| <div className={`w-8 h-8 rounded-lg flex items-center justify-center border ${theme === 'dark' ? 'bg-purple-500/20 border-purple-500/30 text-purple-400' : 'bg-purple-50 border-purple-200 text-purple-600'}`}><CodeIcon size={18} /></div> | |
| <h3 className={`text-xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>Standard Workflow</h3> | |
| </div> | |
| <Snippet code={jsCode} filename="prime_discovery.js" theme={theme} /> | |
| <div className="py-6 text-center"><span className={`font-mono text-[10px] uppercase tracking-widest ${theme === 'dark' ? 'text-gray-500' : 'text-gray-400'}`}>Targeting <span className="text-pink-500">HTML</span> Output:</span></div> | |
| <Snippet code={htmlCode} filename="index.html" theme={theme} /> | |
| </section> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| const RichTextEditor = ({ onBack, theme }: { onBack: () => void; theme: Theme }) => { | |
| const [code, setCode] = useState('# AXIS Workspace\n\nEdit Markdown or HTML here.\n\n## Subheading\n\n- Bullet point 1\n- Bullet point 2\n\nThis is **bold** text.'); | |
| const [mode, setMode] = useState<'edit' | 'preview' | 'split'>('split'); | |
| const [copied, setCopied] = useState(false); | |
| const getPreviewContent = () => { | |
| // Check if it's already a full HTML doc | |
| if (code.trim().toLowerCase().startsWith('<!doctype html') || code.trim().toLowerCase().startsWith('<html')) { | |
| return code; | |
| } | |
| const parsed = parseMarkdownToHTML(code); | |
| return ` | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <style> | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; | |
| color: #24292f; | |
| padding: 2.5rem; | |
| line-height: 1.6; | |
| background: #ffffff; | |
| min-height: 100vh; | |
| } | |
| h1, h2, h3 { | |
| color: #1f2328; | |
| margin-top: 24px; | |
| margin-bottom: 16px; | |
| font-weight: 600; | |
| line-height: 1.25; | |
| } | |
| h1 { border-bottom: 1px solid #d0d7de; padding-bottom: 0.3em; font-size: 2em; } | |
| h2 { border-bottom: 1px solid #d0d7de; padding-bottom: 0.3em; font-size: 1.5em; } | |
| p { margin-top: 0; margin-bottom: 16px; } | |
| code { | |
| background-color: rgba(175, 184, 193, 0.2); | |
| padding: 0.2em 0.4em; | |
| border-radius: 6px; | |
| font-family: "JetBrains Mono", monospace; | |
| font-size: 85%; | |
| } | |
| ul { padding-left: 2em; margin-bottom: 16px; } | |
| li { margin-bottom: 0.25rem; } | |
| a { color: #0969da; text-decoration: none; } | |
| a:hover { text-decoration: underline; } | |
| </style> | |
| </head> | |
| <body>${parsed}</body> | |
| </html> | |
| `; | |
| }; | |
| const handleCopy = async () => { if (!code) return; await navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 2000); }; | |
| const handleExport = () => { | |
| const blob = new Blob([code], { type: 'text/plain' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); a.href = url; a.download = 'axis_note.txt'; a.click(); | |
| }; | |
| return ( | |
| <div className="relative z-30 flex-1 w-full max-w-[1200px] mx-auto px-4 pb-10 animate-[fadeIn_0.5s_ease-out]"> | |
| <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-4 gap-4"> | |
| <button onClick={onBack} className={`flex items-center gap-2 text-[10px] font-mono font-bold tracking-[0.2em] transition-all uppercase ${theme === 'dark' ? 'text-cyan-400/60 hover:text-cyan-400' : 'text-cyan-600/60 hover:text-cyan-600'}`}> | |
| <ArrowLeft size={14} /> EXIT_WORKSPACE | |
| </button> | |
| <div className={`flex p-1 rounded-lg border w-full sm:w-auto ${theme === 'dark' ? 'bg-black/40 border-white/10' : 'bg-gray-100 border-gray-200'}`}> | |
| {['edit', 'preview', 'split'].map((m) => ( | |
| <button key={m} onClick={() => setMode(m as any)} className={`flex-1 sm:flex-none px-3 py-1 rounded text-[10px] font-mono transition-all uppercase ${mode === m ? (theme === 'dark' ? 'bg-cyan-500 text-black' : 'bg-white text-black shadow-sm') : 'text-gray-500 hover:text-gray-400'}`}> | |
| {m} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| <div className={`border rounded-2xl overflow-hidden shadow-2xl flex flex-col h-[65vh] sm:h-[75vh] ${theme === 'dark' ? 'bg-neutral-900 border-white/10' : 'bg-white border-gray-200'}`}> | |
| <div className={`flex-1 flex ${mode === 'split' ? 'flex-col lg:flex-row' : 'flex-col'} overflow-hidden`}> | |
| {(mode === 'edit' || mode === 'split') && ( | |
| <div className={`flex-1 ${mode === 'split' ? 'border-b lg:border-b-0 lg:border-r border-white/5' : ''} ${theme === 'dark' ? 'bg-black/20' : 'bg-gray-50/50'}`}> | |
| <CodeEditor code={code} language="Markdown" onChange={setCode} placeholder="Source logic..." theme={theme} /> | |
| </div> | |
| )} | |
| {(mode === 'preview' || mode === 'split') && ( | |
| <div className="flex-1 bg-white relative"> | |
| <iframe srcDoc={getPreviewContent()} title="prev" className="w-full h-full border-none" /> | |
| </div> | |
| )} | |
| </div> | |
| <div className={`px-4 py-3 border-t flex flex-wrap items-center justify-between text-[10px] font-mono uppercase gap-2 ${theme === 'dark' ? 'border-white/5 bg-black/40 text-gray-500' : 'border-gray-100 bg-gray-50 text-gray-400'}`}> | |
| <div className="flex gap-4"><button onClick={() => setCode('')} className="hover:text-red-500 transition-colors">Clear</button><span>Chars: {code.length}</span></div> | |
| <div className="flex gap-4"> | |
| <button onClick={handleCopy} className="hover:text-cyan-500 transition-colors">{copied ? 'COPIED' : 'COPY'}</button> | |
| <button onClick={handleExport} className="hover:text-cyan-500 transition-colors">Export</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| // --- Main Application --- | |
| const App = () => { | |
| const [view, setView] = useState<ViewState>('app'); | |
| const [theme, setTheme] = useState<Theme>('dark'); | |
| const [inputCode, setInputCode] = useState(''); | |
| const [outputCode, setOutputCode] = useState(''); | |
| const [outputLanguage, setOutputLanguage] = useState('Python'); | |
| const [loading, setLoading] = useState(false); | |
| const [toast, setToast] = useState(''); | |
| const inputLanguage = detectLanguage(inputCode); | |
| useEffect(() => { | |
| document.body.className = theme === 'light' ? 'light-mode' : ''; | |
| }, [theme]); | |
| const toggleTheme = () => setTheme(prev => prev === 'dark' ? 'light' : 'dark'); | |
| const handleTranslate = async () => { | |
| if (!inputCode.trim()) { setToast('Enter code to sync.'); return; } | |
| setLoading(true); setOutputCode(''); | |
| try { | |
| const response = await fetch('/api/translate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ inputCode, outputLanguage }) | |
| }); | |
| const data = await response.json(); | |
| setOutputCode(data.code || ''); | |
| } catch { | |
| setToast('Synthesis failed.'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleDispatch = () => { | |
| const extMap: Record<string, string> = { | |
| 'JavaScript': 'js', 'TypeScript': 'ts', 'Python': 'py', 'Java': 'java', 'C++': 'cpp', | |
| 'C#': 'cs', 'Go': 'go', 'Rust': 'rs', 'Swift': 'swift', 'Kotlin': 'kt', 'PHP': 'php', | |
| 'Ruby': 'rb', 'SQL': 'sql', 'HTML': 'html', 'CSS': 'css', 'Bash': 'sh', 'JSON': 'json', | |
| 'YAML': 'yaml', 'Markdown': 'md' | |
| }; | |
| const ext = extMap[outputLanguage] || 'txt'; | |
| const blob = new Blob([outputCode], { type: 'text/plain' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); a.href = url; a.download = `axis_build.${ext}`; a.click(); | |
| setToast(`File dispatched: axis_build.${ext}`); | |
| }; | |
| return ( | |
| <div className={`relative min-h-screen flex flex-col transition-colors duration-500 overflow-x-hidden ${theme === 'dark' ? 'bg-black text-white' : 'bg-gray-50 text-gray-900'}`}> | |
| {/* Background Layer */} | |
| <div className="grid-bg" /> | |
| <div className="glow-orb glow-orb-1" /> | |
| <div className="glow-orb glow-orb-2" /> | |
| <header className="px-4 py-6 sm:py-10 flex flex-col items-center justify-center relative z-20 w-full"> | |
| {/* Navigation Wrapper */} | |
| <div className="flex w-full max-w-[1400px] justify-between items-start mb-6 sm:mb-0"> | |
| {/* Left Buttons */} | |
| <div className="flex flex-col sm:flex-row gap-2"> | |
| <button onClick={() => setView(view === 'editor' ? 'app' : 'editor')} className={`p-2 rounded-lg border flex items-center justify-center gap-2 transition-all w-28 sm:w-32 ${theme === 'dark' ? 'bg-black/20 border-white/10 text-gray-400 hover:text-white' : 'bg-white border-gray-200 text-gray-600 hover:text-black shadow-sm'}`}> | |
| <Github size={18} /><span className="text-[10px] font-mono uppercase tracking-widest">Editor</span> | |
| </button> | |
| <button onClick={() => setView(view === 'docs' ? 'app' : 'docs')} className={`p-2 rounded-lg border flex items-center justify-center gap-2 transition-all w-28 sm:w-32 ${theme === 'dark' ? 'bg-black/20 border-white/10 text-gray-400 hover:text-white' : 'bg-white border-gray-200 text-gray-600 hover:text-black shadow-sm'}`}> | |
| <FileText size={18} /><span className="text-[10px] font-mono uppercase tracking-widest">Docs</span> | |
| </button> | |
| </div> | |
| {/* Right Toggle */} | |
| <button onClick={toggleTheme} className={`p-2 rounded-lg border transition-all ${theme === 'dark' ? 'bg-black/20 border-white/10 text-yellow-500' : 'bg-white border-gray-200 text-gray-600 hover:text-blue-600 shadow-sm'}`}> | |
| {theme === 'dark' ? <Sun size={20} /> : <Moon size={20} />} | |
| </button> | |
| </div> | |
| <h1 className="bg-gradient-to-r from-cyan-400 via-pink-500 to-purple-600 bg-clip-text text-5xl sm:text-7xl font-black text-transparent tracking-tighter uppercase mb-2 select-none">AXIS</h1> | |
| <div className="font-mono text-[9px] sm:text-[10px] uppercase tracking-[0.4em] opacity-40 text-center">aligned. integrated. syntax.</div> | |
| </header> | |
| {view === 'app' ? ( | |
| <main className="flex-1 w-full max-w-[1400px] mx-auto px-4 pb-20 flex flex-col lg:flex-row gap-10 z-20"> | |
| {/* Ingestion Section */} | |
| <div className="flex-1 flex flex-col space-y-4"> | |
| <div className="flex items-center justify-between"> | |
| <span className={`text-[11px] font-bold tracking-[0.2em] uppercase ${theme === 'dark' ? 'text-cyan-400' : 'text-cyan-600'}`}>Source Ingestion</span> | |
| <div className={`flex items-center gap-2 px-3 py-1 rounded-full border text-[9px] font-mono ${theme === 'dark' ? 'bg-cyan-500/10 border-cyan-500/20 text-cyan-400' : 'bg-cyan-50 border-cyan-200 text-cyan-700'}`}> | |
| <Sparkles size={10} /> {inputLanguage.toUpperCase()} | |
| </div> | |
| </div> | |
| <div className={`h-[400px] sm:h-[600px] flex flex-col rounded-2xl border-2 overflow-hidden shadow-2xl transition-all duration-300 ${theme === 'dark' ? 'border-cyan-500/20 bg-black/60 focus-within:border-cyan-500/40' : 'border-gray-200 bg-white focus-within:border-cyan-500/40'}`}> | |
| <CodeEditor code={inputCode} language={inputLanguage} onChange={setInputCode} theme={theme} placeholder="// Paste Source Logic..." /> | |
| <EditorFooter onClear={() => setInputCode('')} textToCopy={inputCode} colorClass="text-cyan-500" theme={theme} /> | |
| </div> | |
| <button onClick={handleTranslate} disabled={loading} className={`w-full py-4 sm:py-5 rounded-xl font-black uppercase tracking-[0.2em] transition-all shadow-xl text-sm sm:text-base ${loading ? 'opacity-50 cursor-wait' : 'bg-cyan-500 text-black hover:scale-[1.01] active:scale-95'}`}>{loading ? 'Synthesizing...' : 'Apply Engine'}</button> | |
| </div> | |
| {/* Output Section */} | |
| <div className="flex-1 flex flex-col space-y-4"> | |
| <span className={`text-[11px] font-bold tracking-[0.2em] uppercase ${theme === 'dark' ? 'text-pink-500' : 'text-pink-600'}`}>Neural Output</span> | |
| <LanguageSelect language={outputLanguage} onChange={setOutputLanguage} theme={theme} /> | |
| <div className={`h-[400px] sm:h-[600px] flex flex-col rounded-2xl border-2 overflow-hidden shadow-2xl transition-all duration-300 ${theme === 'dark' ? 'border-pink-500/20 bg-black/60 focus-within:border-pink-500/40' : 'border-gray-200 bg-white focus-within:border-pink-500/40'}`}> | |
| <CodeEditor code={outputCode} language={outputLanguage} readOnly theme={theme} placeholder="// Logic Stream Output..." /> | |
| <EditorFooter textToCopy={outputCode} colorClass="text-pink-500" theme={theme} /> | |
| </div> | |
| <button onClick={handleDispatch} disabled={!outputCode} className={`w-full py-4 sm:py-5 rounded-xl font-black uppercase tracking-[0.2em] border transition-all text-sm sm:text-base ${theme === 'dark' ? 'bg-neutral-900 border-white/5 text-white hover:bg-neutral-800' : 'bg-white border-gray-200 text-gray-900 hover:bg-gray-50 shadow-sm active:scale-95'}`}>Dispatch Build</button> | |
| </div> | |
| </main> | |
| ) : view === 'docs' ? <DocsView onBack={() => setView('app')} theme={theme} /> : <RichTextEditor onBack={() => setView('app')} theme={theme} />} | |
| <footer className="relative z-20 pb-12 pt-8 w-full flex flex-col items-center justify-center gap-6 px-4"> | |
| <a href="https://axis.jessejesse.com" target="_blank" rel="noreferrer" className={`font-mono text-[10px] sm:text-sm font-bold transition-opacity uppercase tracking-widest text-center ${theme === 'dark' ? 'text-white/60 hover:text-white' : 'text-black/60 hover:text-black'}`}>axis.jessejesse.com</a> | |
| <LogoSVG theme={theme} className={`mx-auto transition-all hover:scale-110 ${theme === 'dark' ? 'grayscale opacity-50 hover:grayscale-0 hover:opacity-100' : ''}`} /> | |
| </footer> | |
| {toast && <Toast message={toast} onClose={() => setToast('')} />} | |
| </div> | |
| ); | |
| }; | |
| const root = createRoot(document.getElementById('root')!); | |
| root.render(<App />); | |