// Clone by Akshat Sen of:
// "Python Environment [LATEST]" by Aaqid Masoodi
// https://ancientbrain.com/world.php?world=8775112567
// Please leave this clone trail here.
document.write(`
<style>
:root{
/* Light mode variables */
--bg-gradient: linear-gradient(135deg, #fafafa, #f0f0f5);
--panel:#ffffff;
--muted:#64748b;
--accent:#2563eb;
--glass: rgba(0,0,0,0.04);
--radius:12px;
--shadow: 0 8px 24px rgba(16,24,40,0.06);
--success:#16a34a;
--danger:#fb7185;
--terminal-bg: #020617; /* console stays dark */
--text:#0f1724;
}
/* Dark theme overrides */
.dark {
--bg-gradient: linear-gradient(135deg, #0a0a0f, #1b1b27);
--panel:#0b1220;
--muted:#94a3b8;
--accent:#60a5fa;
--glass: rgba(255,255,255,0.04);
--shadow: 0 6px 18px rgba(2,6,23,0.6);
--success:#4ade80;
--danger:#fb7185;
--terminal-bg: #020617; /* console stays dark */
--text:#e6eef8;
}
*{box-sizing:border-box}
html,body{height:100%;margin:0;background:var(--bg-gradient);font-family:Inter,ui-sans-serif,system-ui,Segoe UI,Roboto,'Helvetica Neue',Arial;color:var(--text);}
.app {
display:grid;
grid-template-rows:64px 1fr;
height:100vh;
gap:12px;
padding:12px;
}
/* Top bar */
.topbar{
display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--panel);border-radius:var(--radius);box-shadow:var(--shadow);
}
.brand{font-weight:800;color:var(--accent);margin-right:6px;font-size:18px}
.top-actions{margin-left:auto;display:flex;gap:8px;align-items:center}
.btn{
background:var(--glass);border:1px solid rgba(0,0,0,0.04);padding:8px 12px;border-radius:10px;color:var(--muted);cursor:pointer;font-weight:600;
}
.btn:hover{filter:brightness(0.98)}
.icon{opacity:0.9;margin-right:6px}
.small{padding:6px 8px;font-size:14px;border-radius:8px}
.toggle{display:flex;align-items:center;gap:6px;padding:6px 8px;background:transparent;border-radius:8px}
/* Main area split */
.main {
display:flex;
gap:12px;
height:calc(100vh - 110px);
}
/* File explorer */
.explorer{
width:220px; min-width:160px; max-width:520px; background:var(--panel); border-radius:12px;padding:10px; box-shadow:var(--shadow); display:flex;flex-direction:column;
}
.explorer h3{margin:6px 0 8px 0;color:var(--muted);font-size:13px}
.file-list{flex:1;overflow:auto;padding-right:4px}
.file-item{padding:8px;border-radius:8px;display:flex;justify-content:space-between;align-items:center;gap:8px;color:var(--muted);cursor:default}
.file-item:hover{background:var(--glass)}
.file-item.active{background:linear-gradient(90deg, rgba(37,99,235,0.08), transparent);color:var(--accent);font-weight:700}
.file-actions{display:flex;gap:6px;align-items:center}
.small-input{width:100%;padding:8px;border-radius:8px;border:1px solid rgba(0,0,0,0.04);background:transparent;color:var(--muted);}
/* Editor + console panel */
.workspace{flex:1;display:flex;flex-direction:column;gap:12px}
.top-editor{display:flex;gap:12px;align-items:center}
.tabs{display:flex;gap:6px;align-items:center;overflow:auto;padding-bottom:4px}
.tab{padding:6px 10px;border-radius:8px;background:var(--glass);color:var(--muted);cursor:pointer;display:flex;align-items:center;gap:8px}
.tab.active{background:linear-gradient(90deg, rgba(37,99,235,0.08), transparent);color:var(--accent);font-weight:700}
.tab .close{background:transparent;border:0;color:var(--muted);cursor:pointer;padding:2px 6px;border-radius:6px}
.tab .close:hover{background:rgba(0,0,0,0.03)}
/* editor area and resizable console */
.editor-area{flex:1;display:flex;gap:12px;align-items:stretch}
.code-panel{flex:1;background:var(--panel);padding:12px;border-radius:12px;box-shadow:var(--shadow);display:flex;flex-direction:column}
textarea.code{
flex:1;background:transparent;border:0;outline:none;color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,monospace;font-size:14px;
resize:none;padding:8px;border-radius:8px;
}
.right-panel{width:420px;display:flex;flex-direction:column;gap:8px}
.console-card{background:var(--terminal-bg);border-radius:10px;padding:12px;color:var(--success);font-family:ui-monospace,monospace;flex:1;overflow:auto;white-space:pre-wrap}
.console-controls{display:flex;gap:8px;align-items:center}
.console-line{padding:4px 6px;border-radius:6px;display:flex;gap:8px;align-items:center}
.console-line.err{color:var(--danger)}
.console-line.input{color:#ffd166}
.copy{opacity:0.6;font-size:13px;cursor:pointer}
.divider{width:6px;background:transparent;cursor:col-resize}
/* explorer divider (resizable) */
.explorer-divider{width:6px;background:transparent;cursor:col-resize;}
/* prompt modal */
#customPromptOverlay{position:fixed;inset:0;display:none;align-items:center;justify-content:center;z-index:99999}
#customPromptBox{background:var(--panel);padding:18px;border-radius:12px;box-shadow:var(--shadow);min-width:320px;color:var(--muted)}
#customPromptBox input{width:100%;padding:10px;border-radius:8px;border:1px solid rgba(0,0,0,0.04);margin-top:8px;background:transparent;color:var(--muted)}
.footer-note{font-size:12px;color:var(--muted);margin-top:6px}
/* small helpers */
.kbd{background:rgba(0,0,0,0.03);padding:4px 8px;border-radius:6px;font-weight:700;color:var(--muted);font-size:12px}
</style>
<div class="app">
<div class="topbar">
<div>
<div class="brand">Ancient Brain Python</div>
<div style="font-size:12px;color:var(--muted)">Designed & Developed by Aaqid</div>
</div>
<div class="top-actions">
<button id="runBtn" class="btn small">โถ Run <span class="kbd" style="margin-left:8px">Ctrl/Cmd+Enter</span></button>
<button id="saveBtn" class="btn small">๐พ Save</button>
<button id="newBtn" class="btn small">๏ผ New</button>
<button id="clearConsoleBtn" class="btn small">๐งน Clear</button>
<div class="toggle btn small" id="autoClearToggle" title="Auto-clear console on run">Auto-clear: <span id="autoClearState" style="margin-left:8px;color:var(--accent)">ON</span></div>
<button id="themeToggle" class="btn small">๐ Theme</button>
</div>
</div>
<div class="main">
<div id="explorer" class="explorer">
<h3>Files</h3>
<div style="display:flex;gap:8px;margin-bottom:8px">
<input id="newFileName" class="small-input" placeholder="new_file.py" />
<button id="createFileBtn" class="btn small">Create</button>
</div>
<div class="file-list" id="fileList"></div>
<div class="footer-note">Files saved in your browser (localStorage).</div>
</div>
<div id="explorerDivider" class="explorer-divider" title="Drag to resize file explorer"></div>
<div class="workspace">
<div class="top-editor">
<div class="tabs" id="tabs"></div>
</div>
<div class="editor-area">
<div class="code-panel">
<textarea id="code" class="code" spellcheck="false"></textarea>
<div style="display:flex;gap:8px;margin-top:8px">
<div class="kbd">Ctrl/Cmd+Enter</div>
<div style="flex:1"></div>
<div id="status" style="color:var(--muted)">Ready</div>
</div>
</div>
<div class="divider" id="divider" title="Drag to resize console"></div>
<div class="right-panel">
<div class="console-card" id="console"></div>
<div class="console-controls">
<button id="copyConsole" class="btn small">Copy</button>
<button id="downloadConsole" class="btn small">Download</button>
<div style="flex:1"></div>
<div style="color:var(--muted);font-size:13px">Console</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Prompt Modal -->
<div id="customPromptOverlay">
<div id="customPromptBox">
<div id="customPromptMessage">Input:</div>
<input id="customPromptInput" />
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:12px">
<button class="btn small" id="customPromptCancel">Cancel</button>
<button class="btn small" id="customPromptOK">OK</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/pyodide/v0.29.0/full/pyodide.js"></script>
<script>
/* ---------------------------
Utilities & state
--------------------------- */
const STORAGE_KEY = 'ancient_brain_python_v1';
let state = { files: {}, order: [], active: null, theme: 'light', autoClear: true };
const consoleEl = document.getElementById('console');
const codeEl = document.getElementById('code');
const fileListEl = document.getElementById('fileList');
const tabsEl = document.getElementById('tabs');
const statusEl = document.getElementById('status');
const autoClearStateEl = document.getElementById('autoClearState');
const explorerEl = document.getElementById('explorer');
function saveState(){ localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); }
function loadState(){
const raw = localStorage.getItem(STORAGE_KEY);
if(raw) state = JSON.parse(raw);
if(!state.files || Object.keys(state.files).length===0){
// seed with starter file
state = {
files: { 'main.py': '# Created by Ancient Brain Python\\nprint("Hello from Ancient Brain Python!")\\nname = input("Enter your name: ")\\nprint(f"Welcome, {name}")' },
order: ['main.py'],
active: 'main.py',
theme: 'light',
autoClear: true
};
saveState();
}
}
loadState();
applyTheme();
/* ---------------------------
UI rendering
--------------------------- */
function renderFileList(){
fileListEl.innerHTML = '';
state.order.forEach(fname => {
const div = document.createElement('div');
div.className = 'file-item' + (fname===state.active ? ' active':'' );
// clicking the name opens the file
const nameSpan = document.createElement('div');
nameSpan.textContent = fname;
nameSpan.style.flex = '1';
nameSpan.onclick = ()=> openFile(fname);
// actions: rename, delete
const actions = document.createElement('div');
actions.className = 'file-actions';
const renameBtn = document.createElement('button');
renameBtn.className = 'btn small';
renameBtn.textContent = 'Rename';
renameBtn.onclick = (e)=>{ e.stopPropagation(); renameFilePrompt(fname); };
const delBtn = document.createElement('button');
delBtn.className = 'btn small';
delBtn.textContent = 'Delete';
delBtn.onclick = (e)=>{ e.stopPropagation(); deleteFilePrompt(fname); };
actions.appendChild(renameBtn);
actions.appendChild(delBtn);
div.appendChild(nameSpan);
div.appendChild(actions);
fileListEl.appendChild(div);
});
}
function renderTabs(){
tabsEl.innerHTML = '';
state.order.forEach(fname => {
const t = document.createElement('div');
t.className = 'tab' + (fname===state.active ? ' active':'');
t.onclick = ()=>{ openFile(fname); };
const span = document.createElement('span');
span.textContent = fname;
const close = document.createElement('button');
close.className = 'close';
close.textContent = 'โ';
close.onclick = (e)=>{ e.stopPropagation(); closeTab(fname); };
t.appendChild(span);
t.appendChild(close);
tabsEl.appendChild(t);
});
}
function openFile(fname){
if(!fname) return;
state.active = fname;
codeEl.value = state.files[fname] || '';
renderTabs(); renderFileList();
saveState();
}
function newFile(name){
if(!name || !name.trim()) name = 'untitled.py';
if(state.files[name]){ // unique
let i=1; while(state.files[\`\${name.replace(/\\.py$/,'')}_\${i}.py\`]) i++;
name = \`\${name.replace(/\\.py$/,'')}_\${i}.py\`;
}
state.files[name] = '# new file\\n';
state.order.unshift(name);
openFile(name);
saveState();
}
function deleteFile(name){
delete state.files[name];
state.order = state.order.filter(f=>f!==name);
if(state.active===name) state.active = state.order[0] || null;
if(state.active){
codeEl.value = state.files[state.active] || '';
} else {
codeEl.value = '# No file open.\\n# Create a new file to start coding!';
}
renderTabs(); renderFileList(); saveState();
}
function deleteFilePrompt(name){
showPrompt('Type DELETE to confirm deletion of ' + name).then(v=>{
if(v === 'DELETE') {
deleteFile(name);
} else {
writeConsole('Delete cancelled.');
}
});
}
function renameFile(oldName,newName){
if(!newName || !newName.trim()) return;
if(state.files[newName] && newName !== oldName){
showPrompt('Name exists. Type a different name:').then(v=>{ if(v) renameFile(oldName,v); });
return;
}
state.files[newName] = state.files[oldName];
delete state.files[oldName];
const idx = state.order.indexOf(oldName);
if(idx>=0) state.order[idx] = newName;
if(state.active===oldName) state.active = newName;
renderTabs(); renderFileList(); saveState();
}
function renameFilePrompt(oldName){
showPrompt('Rename ' + oldName + ' to:').then(v=>{
if(!v) return;
// ensure extension .py
let nm = v.trim();
if(!/\\.py$/.test(nm)) nm = nm + '.py';
renameFile(oldName, nm);
});
}
function closeTab(name){
const idx = state.order.indexOf(name);
if(idx>=0) state.order.splice(idx,1);
if(state.active===name) state.active = state.order[0] || null;
if(state.active){
codeEl.value = state.files[state.active] || '';
} else {
codeEl.value = '# No file open.\\n# Create a new file to start coding!';
}
renderTabs(); renderFileList(); saveState();
}
renderFileList(); renderTabs(); if(state.active) openFile(state.active);
/* create/delete buttons */
document.getElementById('createFileBtn').onclick = ()=>{
const name = document.getElementById('newFileName').value.trim() || 'untitled.py';
newFile(name);
document.getElementById('newFileName').value='';
};
fileListEl.addEventListener('click', (e)=>{
// clicking name handled inline; actions have their own handlers
});
/* editor autosave */
codeEl.addEventListener('input', ()=>{
if(state.active){ state.files[state.active] = codeEl.value; saveState(); }
statusEl.textContent = 'Editing...';
clearTimeout(codeEl._idleTimer);
codeEl._idleTimer = setTimeout(()=> statusEl.textContent = 'Ready', 800);
});
/* buttons */
document.getElementById('saveBtn').onclick = ()=>{ if(state.active){ state.files[state.active]=codeEl.value; saveState(); statusEl.textContent='Saved'; setTimeout(()=>statusEl.textContent='Ready',900);} };
document.getElementById('newBtn').onclick = ()=> newFile('new_file.py');
document.getElementById('clearConsoleBtn').onclick = ()=> { consoleEl.innerHTML=''; };
/* theme toggle */
document.getElementById('themeToggle').onclick = ()=>{
state.theme = state.theme==='dark' ? 'light' : 'dark';
applyTheme(); saveState();
};
function applyTheme(){ if(state.theme==='light') document.documentElement.classList.remove('dark'); else document.documentElement.classList.add('dark'); }
/* auto-clear toggle */
document.getElementById('autoClearToggle').onclick = ()=>{
state.autoClear = !state.autoClear;
autoClearStateEl.textContent = state.autoClear ? 'ON' : 'OFF';
saveState();
};
autoClearStateEl.textContent = state.autoClear ? 'ON' : 'OFF';
/* copy/download console */
document.getElementById('copyConsole').onclick = ()=>{
navigator.clipboard.writeText(consoleEl.textContent||'').then(()=>{ const b=document.getElementById('copyConsole'); b.textContent='Copied'; setTimeout(()=>b.textContent='Copy',900); }).catch(()=>{});
};
document.getElementById('downloadConsole').onclick = ()=>{
const blob = new Blob([consoleEl.textContent],'text/plain');
const a = document.createElement('a'); a.href=URL.createObjectURL(blob); a.download='console.txt'; a.click();
};
/* explorer drag to resize */
const explorerDivider = document.getElementById('explorerDivider');
let explorerDragging = false;
explorerDivider.addEventListener('mousedown', e=>{ explorerDragging=true; document.body.style.userSelect='none'; });
window.addEventListener('mouseup', ()=>{ explorerDragging=false; document.body.style.userSelect='auto'; });
window.addEventListener('mousemove', e=>{
if(!explorerDragging) return;
const main = document.querySelector('.main');
const rect = main.getBoundingClientRect();
let x = e.clientX - rect.left;
x = Math.max(160, Math.min(rect.width - 320, x));
explorerEl.style.width = x + 'px';
});
/* divider drag to resize console */
const divider = document.getElementById('divider');
let dragging = false;
divider.addEventListener('mousedown', e=>{ dragging=true; document.body.style.userSelect='none'; });
window.addEventListener('mouseup', ()=>{ dragging=false; document.body.style.userSelect='auto'; });
window.addEventListener('mousemove', e=>{
if(!dragging) return;
const main = document.querySelector('.main');
const rect = main.getBoundingClientRect();
let x = e.clientX - rect.left;
x = Math.max(320, Math.min(rect.width - 320, x));
const right = document.querySelector('.right-panel');
right.style.width = (rect.width - x - 12) + 'px';
});
/* keyboard shortcuts */
window.addEventListener('keydown', (e)=>{
const S = (e.ctrlKey||e.metaKey) && e.key.toLowerCase()==='s';
const EnterRun = (e.ctrlKey||e.metaKey) && e.key==='Enter';
const New = (e.ctrlKey||e.metaKey) && e.key.toLowerCase()==='n';
if(S){ e.preventDefault(); document.getElementById('saveBtn').click(); }
if(EnterRun){ e.preventDefault(); document.getElementById('runBtn').click(); }
if(New){ e.preventDefault(); newFile('new_file.py'); }
});
/* ---------------------------
Console helpers (used by Python)
--------------------------- */
function writeConsole(s, type='out'){
if(typeof s !== 'string') s = String(s);
const lines = s.split(/\\n/);
lines.forEach(line=>{
if(line==='') return;
const div = document.createElement('div');
div.className = 'console-line ' + (type==='err'?'err': type==='in'?'input':'');
div.textContent = line;
div.onclick = ()=>{ navigator.clipboard.writeText(line); div.style.opacity=0.6; setTimeout(()=>div.style.opacity=1,400); };
consoleEl.appendChild(div);
});
consoleEl.scrollTop = consoleEl.scrollHeight;
}
/* ---------------------------
Custom Prompt (modal) - reused for rename/confirm
--------------------------- */
let customPromptResolve = null;
function showPrompt(message){
const overlay = document.getElementById('customPromptOverlay');
document.getElementById('customPromptMessage').textContent = message || '';
document.getElementById('customPromptInput').value = '';
overlay.style.display = 'flex';
document.getElementById('customPromptInput').focus();
return new Promise(resolve => customPromptResolve = resolve);
}
function hidePrompt(){ document.getElementById('customPromptOverlay').style.display='none'; }
document.getElementById('customPromptOK').onclick = ()=>{
const v = document.getElementById('customPromptInput').value;
hidePrompt(); if(customPromptResolve) customPromptResolve(v);
};
document.getElementById('customPromptCancel').onclick = ()=>{
hidePrompt(); if(customPromptResolve) customPromptResolve(null);
};
window.customPrompt = showPrompt;
/* ---------------------------
Pyodide integration
--------------------------- */
let pyodide = null;
let pyReady = false;
let pyodideReadyPromise = loadPyodide().then(async p=>{
pyodide = p;
// Hook Python stdout/stderr -> JS writeConsole
await pyodide.runPythonAsync(\`
import sys
from js import writeConsole as _writeConsole
class JSWriter:
def write(self, s):
if s is None:
return
if str(s).strip() == "":
return
_writeConsole(str(s), 'out')
def flush(self):
pass
sys.stdout = sys.stderr = JSWriter()
\`);
// Hook Python input() to call JS customPrompt and await
await pyodide.runPythonAsync(\`
import builtins
from js import customPrompt as _js_prompt
import asyncio
async def _async_input(msg=''):
result = await _js_prompt(msg)
return result
def _input(msg=''):
# run async prompt in a synchronous context
return asyncio.get_event_loop().run_until_complete(_async_input(msg))
builtins.input = _input
\`);
pyReady = true;
writeConsole('Pyodide ready โ Python environment initialized.');
return pyodide;
});
/* ---------------------------
Run button logic
--------------------------- */
document.getElementById('runBtn').onclick = async ()=>{
if(state.autoClear) consoleEl.innerHTML='';
statusEl.textContent = 'Running...';
const code = codeEl.value;
if(state.active){ state.files[state.active] = code; saveState(); }
try{
await pyodideReadyPromise;
await pyodide.runPythonAsync(code);
statusEl.textContent = 'Done';
}catch(err){
writeConsole(String(err), 'err');
statusEl.textContent = 'Error';
}
setTimeout(()=>statusEl.textContent='Ready', 800);
};
/* expose writeConsole to pyodide (for runPythonAsync's js import) */
window.writeConsole = writeConsole;
/* initialize editor content */
if(state.active) codeEl.value = state.files[state.active];
else codeEl.value = '# No file open.\\n# Create a new file to start coding!';
/* ensure auto-clear state UI */
autoClearStateEl.textContent = state.autoClear ? 'ON':'OFF';
/* quick load: if no files, seed main.py */
if(!state.active){
const fn = Object.keys(state.files)[0];
if(fn){ state.active = fn; openFile(fn); }
}
</script>
`);