commit 1ba3362b6dacc3919df437b6d36296d2f275d0a0
parent 157862ba6396dcabaab53168d303e400f24786f3
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date: Tue, 27 Jan 2026 21:17:48 +0530
Add initial HTML structure for Emojic Studio Pro
Diffstat:
| A | index.html | | | 376 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 376 insertions(+), 0 deletions(-)
diff --git a/index.html b/index.html
@@ -0,0 +1,376 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Emojic Studio Pro</title>
+ <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
+ <script src="https://cdn.tailwindcss.com"></script>
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
+ <style>
+ body { font-family: 'Inter', sans-serif; background-color: #1e1e1e; color: #d4d4d4; overflow: hidden; }
+ .code-font { font-family: 'JetBrains Mono', monospace; }
+
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
+ ::-webkit-scrollbar-track { background: #1e1e1e; }
+ ::-webkit-scrollbar-thumb { background: #424242; border: 2px solid #1e1e1e; border-radius: 5px; }
+ ::-webkit-scrollbar-thumb:hover { background: #4f4f4f; }
+
+ .glass { background: #252526; border: 1px solid #333; }
+ .ide-panel { background-color: #1e1e1e; border-right: 1px solid #333; }
+ .ide-tab { background-color: #2d2d2d; color: #9cdcfe; border-top: 2px solid #007acc; }
+ .ide-tab-inactive { background-color: #1e1e1e; color: #888; }
+
+ .modal-overlay { background-color: rgba(0, 0, 0, 0.7); backdrop-filter: blur(2px); }
+ .cursor::after { content: 'โ'; animation: blink 1s step-end infinite; color: #d4d4d4; }
+ @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
+ </style>
+</head>
+<body>
+ <div id="root"></div>
+
+ <script type="text/babel">
+ const { useState, useRef, useEffect } = React;
+
+ const DEFAULT_EMOJIC = `/* Emojic Source */
+#include<stdio.h>
+
+#define ๐ int
+#define ๐ main
+#define ๐ (
+#define ๐คฃ )
+#define โค๏ธ printf
+#define ๐ ;
+#define ๐ return
+#define ๐ scanf
+#define ๐ "
+#define ๐ {
+#define ๐ }
+
+๐ ๐๐๐คฃ๐
+ ๐ num๐
+ โค๏ธ๐๐Enter: ๐๐คฃ๐
+ ๐๐๐%d๐, &num๐คฃ๐
+ โค๏ธ๐๐Out: %d๐, num๐คฃ๐
+ ๐ 0๐
+๐`;
+
+ const DEFAULT_C = `#include <stdio.h>
+
+int main() {
+ int x = 10;
+ printf("Hello World %d", x);
+ return 0;
+}`;
+
+ const WS_URL = "wss://compiler.amit.is-a.dev";
+
+ // --- FIX: Dynamic API Base URL ---
+ const getApiBase = () => {
+ const hostname = window.location.hostname;
+ // If running on localhost, prefer relative path if proxy set up, or explicit localhost for Flask
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
+ // If opened as file, use prod
+ if (window.location.protocol === 'file:') return 'https://emojic.vercel.app/api';
+ return '/api';
+ }
+ return '/api';
+ };
+
+ const API_BASE = getApiBase();
+
+ const EMOJI_POOL = [
+ "๐", "๐ฝ", "๐ฉ", "๐คก", "๐น", "๐บ", "๐ป", "๐ค", "๐พ", "๐", "๐บ", "๐", "๐", "๐", "๐ฅ", "๐ฆ", "๐จ", "๐ซ", "๐ฆ", "๐ด", "๐ฆ", "๐ฆ", "๐", "๐", "๐ฆ", "๐ฆ", "๐ฆ", "๐ฆ
", "๐ฆ", "๐ฆ", "๐", "๐", "๐", "๐ท", "๐ธ", "๐ข", "๐ฆ", "๐ฆ", "๐ฆ", "๐", "๐ฆ", "๐ฆ", "๐ฆ", "๐ก", "๐ ", "๐", "๐ฌ", "๐ณ", "๐", "๐", "๐
", "๐", "๐ฆ", "๐ฆ", "๐ฆง", "๐", "๐ฆ", "๐ฆ", "๐ช", "๐ซ", "๐ฆ", "๐ฆ", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฆ", "๐", "๐ฆ", "๐", "๐ฉ", "๐ฆฎ", "๐โ๐ฆบ", "๐", "๐โโฌ", "๐", "๐"
+ ];
+
+ function App() {
+ const [activeFile, setActiveFile] = useState('main.emojic');
+ const [editorContent, setEditorContent] = useState(DEFAULT_EMOJIC);
+ const [outputContent, setOutputContent] = useState('');
+ const [isTerminalOpen, setIsTerminalOpen] = useState(true);
+ const [consoleOutput, setConsoleOutput] = useState([{ type: 'system', text: 'Emojic IDE v1.1.0 Ready.' }]);
+ const [showMapper, setShowMapper] = useState(false);
+ const [detectedTokens, setDetectedTokens] = useState([]);
+ const [tokenMap, setTokenMap] = useState({});
+
+ // Connection States
+ const [wsStatus, setWsStatus] = useState('disconnected');
+ const [backendStatus, setBackendStatus] = useState('checking'); // checking, online, offline
+
+ const [loading, setLoading] = useState(false);
+ const [userInput, setUserInput] = useState('');
+ const wsRef = useRef(null);
+ const terminalEndRef = useRef(null);
+
+ // --- Health Check on Mount ---
+ useEffect(() => {
+ const checkBackend = async () => {
+ try {
+ // Ping the home route of the API
+ const res = await fetch(API_BASE === '/api' ? '/api' : `${API_BASE}`);
+ // Even a 404 from Flask means it's running, but we want 200
+ if (res.ok || res.status === 200) {
+ setBackendStatus('online');
+ setConsoleOutput(p => [...p, { type: 'system', text: 'Connected to Backend API.' }]);
+ } else {
+ throw new Error("Status " + res.status);
+ }
+ } catch (e) {
+ setBackendStatus('offline');
+ setConsoleOutput(p => [...p, { type: 'error', text: `Backend API Unreachable (${API_BASE}). Check Vercel logs.` }]);
+ }
+ };
+ checkBackend();
+ }, []);
+
+ const switchTab = (file) => {
+ setActiveFile(file);
+ if (file === 'main.emojic' && editorContent === DEFAULT_C) setEditorContent(DEFAULT_EMOJIC);
+ else if (file === 'source.c' && editorContent === DEFAULT_EMOJIC) setEditorContent(DEFAULT_C);
+ };
+
+ const handleAnalyze = async () => {
+ setLoading(true);
+ try {
+ const response = await fetch(`${API_BASE}/analyze`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ code: editorContent })
+ });
+
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
+
+ const data = await response.json();
+ if (data.tokens) {
+ setDetectedTokens(data.tokens);
+ const initialMap = {};
+ data.tokens.forEach(t => initialMap[t] = '');
+ setTokenMap(initialMap);
+ setShowMapper(true);
+ }
+ } catch (e) {
+ alert(`Analysis Failed: ${e.message}. \n\nCheck if the Backend is running.`);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleRandomizeMap = () => {
+ const newMap = { ...tokenMap };
+ const usedEmojis = new Set();
+ detectedTokens.forEach(token => {
+ let pick;
+ let attempts = 0;
+ do { pick = EMOJI_POOL[Math.floor(Math.random() * EMOJI_POOL.length)]; attempts++; }
+ while (usedEmojis.has(pick) && attempts < 50);
+ usedEmojis.add(pick);
+ newMap[token] = pick;
+ });
+ setTokenMap(newMap);
+ };
+
+ const handleMapChange = (token, emoji) => setTokenMap(prev => ({ ...prev, [token]: emoji }));
+
+ const submitTransformation = async () => {
+ setShowMapper(false);
+ setLoading(true);
+ try {
+ const response = await fetch(`${API_BASE}/transform`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ code: editorContent, mapping: tokenMap })
+ });
+ const data = await response.json();
+ if (data.emojic_code) {
+ setOutputContent(data.emojic_code);
+ setActiveFile('main.emojic');
+ }
+ } catch (e) { alert("Transform Failed"); } finally { setLoading(false); }
+ };
+
+ const handleCompile = async () => {
+ setLoading(true);
+ try {
+ const response = await fetch(`${API_BASE}/compile`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ code: editorContent })
+ });
+ const data = await response.json();
+ if (data.c_code) setOutputContent(data.c_code);
+ } catch (e) { alert("Compile Failed"); } finally { setLoading(false); }
+ };
+
+ const handleRun = () => {
+ let codeToRun = '';
+ if (activeFile === 'source.c') {
+ codeToRun = editorContent;
+ } else {
+ if (!outputContent) {
+ setConsoleOutput(p => [...p, { type: 'error', text: 'Please compile Emojic code first!' }]);
+ return;
+ }
+ codeToRun = outputContent;
+ }
+
+ setIsTerminalOpen(true);
+ setConsoleOutput([]);
+ setWsStatus('connecting');
+
+ if (wsRef.current) wsRef.current.close();
+ const socket = new WebSocket(WS_URL);
+ wsRef.current = socket;
+
+ socket.onopen = () => {
+ setWsStatus('connected');
+ setConsoleOutput(p => [...p, { type: 'system', text: 'Connected to Cloud Compiler...' }]);
+ socket.send(JSON.stringify({ type: 'run', language: 'c', code: codeToRun }));
+ };
+
+ socket.onmessage = (e) => {
+ const data = JSON.parse(e.data);
+ if (data.type === 'stdout') setConsoleOutput(p => [...p, { type: 'stdout', text: data.data }]);
+ else if (data.type === 'status') setConsoleOutput(p => [...p, { type: 'system', text: `[${data.msg}]` }]);
+ else if (data.type === 'error') setConsoleOutput(p => [...p, { type: 'error', text: data.msg }]);
+ };
+
+ socket.onclose = () => {
+ setWsStatus('disconnected');
+ setConsoleOutput(p => [...p, { type: 'system', text: 'Process finished.' }]);
+ };
+ };
+
+ useEffect(() => { terminalEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [consoleOutput]);
+
+ return (
+ <div className="h-screen w-screen flex flex-col bg-[#1e1e1e] text-[#d4d4d4]">
+ <div className="h-12 bg-[#333333] flex items-center justify-between px-4 border-b border-[#252526]">
+ <div className="flex items-center gap-3">
+ <span className="text-xl">๐คฏ</span>
+ <span className="font-bold text-sm tracking-wide text-gray-200">EMOJIC STUDIO</span>
+ </div>
+ <div className="flex gap-2">
+ {activeFile === 'source.c' && (
+ <button onClick={handleAnalyze} className="bg-[#007acc] hover:bg-[#0062a3] text-white px-3 py-1 text-xs rounded-sm flex items-center gap-2">
+ <span>โ๏ธ</span> Configure Emojis
+ </button>
+ )}
+ {activeFile === 'main.emojic' && (
+ <button onClick={handleCompile} className="bg-[#007acc] hover:bg-[#0062a3] text-white px-3 py-1 text-xs rounded-sm flex items-center gap-2">
+ <span>โก</span> Compile to C
+ </button>
+ )}
+ <button onClick={handleRun} className="bg-[#4caf50] hover:bg-[#3d8b40] text-white px-3 py-1 text-xs rounded-sm flex items-center gap-2">
+ <span>โถ</span> Run
+ </button>
+ </div>
+ </div>
+
+ <div className="flex-grow flex overflow-hidden">
+ <div className="w-48 bg-[#252526] flex flex-col border-r border-[#1e1e1e]">
+ <div className="p-2 text-xs font-bold text-gray-400 uppercase tracking-wider">Explorer</div>
+ <div onClick={() => switchTab('main.emojic')} className={`px-4 py-1 text-sm cursor-pointer hover:bg-[#2a2d2e] flex items-center gap-2 ${activeFile === 'main.emojic' ? 'bg-[#37373d] text-white' : 'text-gray-400'}`}><span>๐</span> main.emojic</div>
+ <div onClick={() => switchTab('source.c')} className={`px-4 py-1 text-sm cursor-pointer hover:bg-[#2a2d2e] flex items-center gap-2 ${activeFile === 'source.c' ? 'bg-[#37373d] text-white' : 'text-gray-400'}`}><span>๐</span> source.c</div>
+ <div className="mt-4 p-2 text-xs font-bold text-gray-400 uppercase tracking-wider">Output</div>
+ <div className="px-4 py-1 text-xs text-gray-500 italic">{outputContent ? 'Generated C Ready' : 'No Output'}</div>
+ </div>
+
+ <div className="flex-grow flex flex-col relative">
+ <div className="flex bg-[#252526] h-9">
+ <div className={`px-4 flex items-center text-sm cursor-pointer border-r border-[#1e1e1e] ${activeFile === 'main.emojic' ? 'ide-tab' : 'ide-tab-inactive'}`} onClick={() => switchTab('main.emojic')}>main.emojic</div>
+ <div className={`px-4 flex items-center text-sm cursor-pointer border-r border-[#1e1e1e] ${activeFile === 'source.c' ? 'ide-tab' : 'ide-tab-inactive'}`} onClick={() => switchTab('source.c')}>source.c</div>
+ <div className={`px-4 flex items-center text-sm cursor-default border-r border-[#1e1e1e] bg-[#1e1e1e] text-gray-500 italic`}>{activeFile === 'source.c' ? '(Transform Mode)' : '(Compile Mode)'}</div>
+ </div>
+ <div className="flex-grow flex">
+ <div className="w-12 bg-[#1e1e1e] text-[#858585] text-right pr-2 pt-4 text-xs select-none code-font leading-6">{Array.from({length: 20}).map((_, i) => <div key={i}>{i+1}</div>)}</div>
+ <textarea value={editorContent} onChange={(e) => setEditorContent(e.target.value)} className="flex-grow bg-[#1e1e1e] text-[#d4d4d4] p-4 pt-4 outline-none resize-none code-font text-sm leading-6 whitespace-pre" spellCheck="false"></textarea>
+ {outputContent && (
+ <div className="w-1/2 border-l border-[#333] flex flex-col bg-[#1e1e1e]">
+ <div className="bg-[#252526] text-xs px-4 py-1 text-gray-400 uppercase">{activeFile === 'main.emojic' ? 'Generated C' : 'Generated Emojic'}</div>
+ <textarea readOnly value={outputContent} className="flex-grow bg-[#1e1e1e] text-[#9cdcfe] p-4 outline-none resize-none code-font text-sm leading-6"></textarea>
+ </div>
+ )}
+ </div>
+ {isTerminalOpen && (
+ <div className="h-48 bg-[#1e1e1e] border-t border-[#333] flex flex-col">
+ <div className="flex justify-between items-center px-4 py-1 bg-[#252526] border-b border-[#333]">
+ <div className="flex gap-4 text-xs uppercase font-bold text-gray-400">
+ <span className="text-white border-b-2 border-[#007acc] pb-1">Terminal</span>
+ <span className="cursor-pointer hover:text-white">Output</span>
+ <span className="cursor-pointer hover:text-white">Debug Console</span>
+ </div>
+ <button onClick={() => setIsTerminalOpen(false)} className="text-gray-400 hover:text-white">โ</button>
+ </div>
+ <div className="flex-grow p-2 overflow-y-auto code-font text-sm" onClick={() => document.getElementById('term-input')?.focus()}>
+ {consoleOutput.map((line, i) => (
+ <div key={i}>
+ {line.type === 'system' && <span className="text-blue-400 opacity-50">{line.text}</span>}
+ {line.type === 'error' && <span className="text-red-400">{line.text}</span>}
+ {line.type === 'stdout' && <span className="text-gray-100">{line.text}</span>}
+ </div>
+ ))}
+ <div className="flex items-center text-green-500">
+ <span>โ</span>
+ <input id="term-input" className="bg-transparent border-none outline-none ml-2 text-white w-full cursor" value={userInput} onChange={(e) => setUserInput(e.target.value)} onKeyDown={(e) => { if(e.key === 'Enter' && wsRef.current) { wsRef.current.send(JSON.stringify({type: 'input', data: userInput + '\n'})); setConsoleOutput(p => [...p, {type: 'stdout', text: userInput + '\n'}]); setUserInput(''); } }} />
+ </div>
+ <div ref={terminalEndRef} />
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className="h-6 bg-[#007acc] flex justify-between items-center px-3 text-xs text-white select-none">
+ <div className="flex gap-4">
+ <span>main</span>
+ <span>{wsStatus === 'connected' ? 'Connected' : 'Disconnected'}</span>
+ </div>
+ <div className="flex gap-4">
+ {/* Backend Status Indicator */}
+ <span className={
+ backendStatus === 'online' ? 'text-green-300 font-bold' :
+ backendStatus === 'offline' ? 'text-red-300 font-bold' : 'text-yellow-300'
+ }>
+ API: {backendStatus.toUpperCase()}
+ </span>
+ <span>UTF-8</span>
+ <span>C</span>
+ </div>
+ </div>
+
+ {showMapper && (
+ <div className="fixed inset-0 modal-overlay flex items-center justify-center z-50">
+ <div className="bg-[#252526] w-[600px] max-h-[80vh] flex flex-col shadow-2xl border border-[#454545] text-gray-200 rounded-lg overflow-hidden">
+ <div className="p-4 border-b border-[#454545] flex justify-between items-center bg-[#2d2d2d]">
+ <h2 className="font-bold text-lg">Configure Emojis</h2>
+ <div className="space-x-2">
+ <button onClick={handleRandomizeMap} className="text-xs bg-purple-600 hover:bg-purple-500 px-3 py-1 rounded text-white">๐ฒ Randomize</button>
+ <button onClick={() => setShowMapper(false)} className="text-xs hover:text-white text-gray-400">Cancel</button>
+ </div>
+ </div>
+ <div className="p-4 overflow-y-auto grid grid-cols-2 gap-4 bg-[#1e1e1e]">
+ {detectedTokens.map(token => (
+ <div key={token} className="flex items-center gap-2 bg-[#252526] p-2 rounded border border-[#333]">
+ <span className="code-font text-blue-400 w-1/2 text-right truncate" title={token}>{token}</span>
+ <span className="text-gray-500">โ</span>
+ <input type="text" value={tokenMap[token] || ''} onChange={(e) => handleMapChange(token, e.target.value)} className="bg-[#1e1e1e] border border-[#444] rounded px-2 py-1 w-16 text-center text-xl focus:border-blue-500 outline-none" placeholder="?" />
+ </div>
+ ))}
+ </div>
+ <div className="p-4 border-t border-[#454545] flex justify-end bg-[#2d2d2d]">
+ <button onClick={submitTransformation} className="bg-[#007acc] hover:bg-[#0062a3] text-white px-6 py-2 rounded font-bold shadow-lg">Apply & Transform</button>
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+ }
+
+ const root = ReactDOM.createRoot(document.getElementById('root'));
+ root.render(<App />);
+ </script>
+</body>
+</html>