JavaRunner / index.html
parthmax's picture
Update index.html
0659a19 verified
raw
history blame
12.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaRunner — Learn & Run Java Online</title>
<meta name="description" content="JavaRunner is a mobile-friendly online Java IDE. Write, run, save, and download Java code. Perfect for beginners like Ragini Didi.">
<meta name="keywords" content="Java IDE, Online Java compiler, JavaRunner, Learn Java, Java code editor, mobile-friendly IDE, Ragini Didi">
<meta name="author" content="Saksham Pathak">
<meta name="robots" content="index, follow">
<!-- Open Graph for SEO & social -->
<meta property="og:title" content="JavaRunner — Learn & Run Java Online">
<meta property="og:description" content="JavaRunner is a mobile-friendly online Java IDE. Write, run, save, and download Java code. Perfect for beginners like Ragini Didi.">
<meta property="og:type" content="website">
<!-- Fonts & Icons -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<!-- CodeMirror -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/clike/clike.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/edit/closebrackets.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/edit/matchbrackets.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/edit/indentation.min.js"></script>
<style>
:root {
--bg:#0b1b2b;
--glass: rgba(255,255,255,0.06);
--glass-hover: rgba(255,255,255,0.1);
--accent:#6c8cff;
--accent-light:#8be5d0;
--text:#e6eef8;
--muted:#9aa4b2;
--radius:14px;
--shadow:0 8px 30px rgba(2,6,23,0.6);
}
*{box-sizing:border-box;margin:0;padding:0;}
body{
font-family:'Inter',sans-serif;
background:linear-gradient(180deg, #071227, #0d1a2b);
color:var(--text);
display:flex;
flex-direction:column;
min-height:100vh;
padding:10px;
}
/* Header */
header{
display:flex;
justify-content:center;
align-items:center;
margin-bottom:12px;
}
.brand{
font-weight:700;
font-size:24px;
color:var(--accent);
display:flex;
align-items:center;
gap:10px;
text-shadow: 1px 1px 4px rgba(0,0,0,0.5);
}
.brand i{font-size:28px;}
/* App container */
.app{
display:flex;
flex:1;
gap:12px;
max-width:1200px;
width:100%;
}
.panel{
background: var(--glass);
backdrop-filter: blur(12px) saturate(1.1);
border-radius: var(--radius);
border:1px solid rgba(255,255,255,0.08);
padding:12px;
box-shadow: var(--shadow);
transition: transform .2s ease, box-shadow .2s ease;
}
.panel:hover{transform:translateY(-3px);box-shadow:0 16px 40px rgba(8,14,40,0.5);}
/* Sidebar */
.sidebar{
flex:0 0 260px;
display:flex;
flex-direction:column;
gap:10px;
height:calc(100vh - 60px);
}
.sidebar-header{
display:flex;
justify-content:space-between;
align-items:center;
}
.file-list{
flex:1;
overflow-y:auto;
display:flex;
flex-direction:column;
gap:6px;
}
.file-item{
display:flex;
justify-content:space-between;
align-items:center;
padding:8px;
border-radius:10px;
cursor:pointer;
background: rgba(255,255,255,0.02);
transition:0.2s;
}
.file-item:hover{background:var(--glass-hover);}
.file-item.active{background:rgba(108,140,255,0.12);color:#fff;}
.file-actions{display:flex;gap:6px;align-items:center;}
/* Editor */
.editor-area{
flex:1;
display:flex;
flex-direction:column;
gap:8px;
min-height:400px;
}
.cm-wrap{
border-radius:12px;
overflow:hidden;
border:1px solid rgba(255,255,255,0.04);
}
.CodeMirror{
height:calc(100vh - 300px);
background: rgba(2,6,23,0.7);
color:#e8f1ff;
font-size:14px;
}
/* Buttons */
.btn{
background: rgba(255,255,255,0.03);
border:1px solid rgba(255,255,255,0.08);
color:var(--muted);
padding:6px 10px;
border-radius:10px;
cursor:pointer;
display:inline-flex;
align-items:center;
gap:6px;
transition: all .15s ease;
}
.btn:hover{color:#fff;transform:translateY(-2px);background:var(--glass-hover);}
.btn-primary{
background: linear-gradient(90deg,var(--accent),var(--accent-light));
color:#04253b;
padding:10px 14px;
font-weight:700;
border:none;
}
/* Console */
.console{
margin-top:6px;
background: rgba(0,0,0,0.5);
border-radius:10px;
padding:12px;
font-family:monospace;
color:#cfe7ff;
min-height:120px;
overflow:auto;
}
/* Footer */
footer{
text-align:center;
color:var(--muted);
font-size:14px;
padding:12px 0 6px 0;
}
footer .note{
color:#c7d7ff;
font-weight:600;
}
/* Responsive */
@media(max-width:900px){
.app{flex-direction:column;}
.sidebar{width:100%;height:auto;order:2;}
.editor-area{order:1;}
.CodeMirror{height:300px;}
}
@media(max-width:480px){
body{padding:6px;}
.brand{font-size:20px;}
.btn{padding:5px 8px;font-size:13px;}
.btn-primary{padding:8px 10px;font-size:14px;}
}
</style>
</head>
<body>
<header>
<div class="brand"><i class="fa-solid fa-rocket"></i> JavaRunner</div>
</header>
<div class="app">
<!-- Sidebar -->
<div class="panel sidebar">
<div class="sidebar-header">
<button class="btn" id="newBtn"><i class="fa-solid fa-file-circle-plus"></i> New</button>
<button class="btn" id="importBtn"><i class="fa-solid fa-file-import"></i> Import</button>
</div>
<input type="text" placeholder="Search files..." id="searchFile" style="width:100%;padding:6px;border-radius:8px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);color:var(--muted);margin-bottom:6px;">
<div class="file-list" id="fileList"></div>
</div>
<!-- Editor -->
<div class="panel editor-area">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;">
<div>Active File: <span id="activeFile"></span></div>
<div style="display:flex;gap:6px;flex-wrap:wrap;">
<button class="btn-primary" id="runBtn"><i class="fa-solid fa-play"></i> Run</button>
<button class="btn" id="saveBtn"><i class="fa-solid fa-floppy-disk"></i> Save</button>
<button class="btn" id="downloadBtn"><i class="fa-solid fa-download"></i> Download</button>
<button class="btn" id="formatBtn"><i class="fa-solid fa-align-left"></i> Format</button>
<button class="btn" id="cutBtn"><i class="fa-solid fa-cut"></i></button>
<button class="btn" id="copyBtn"><i class="fa-solid fa-copy"></i></button>
<button class="btn" id="pasteBtn"><i class="fa-solid fa-clipboard"></i></button>
<button class="btn" id="deleteBtn"><i class="fa-solid fa-trash"></i></button>
</div>
</div>
<div class="cm-wrap" id="cmWrap"><div id="editor"></div></div>
<div class="console" id="console">Console output will appear here.</div>
</div>
</div>
<footer>
Made with ❤️ for <span class="note">Ragini Didi</span>
</footer>
<input type="file" id="fileInput" accept=".java" style="display:none">
<script>
// ---------- Editor setup ----------
const editor = CodeMirror(document.getElementById('editor'),{
mode:"text/x-java",
lineNumbers:true,
tabSize:4,
indentUnit:4,
autoCloseBrackets:true,
matchBrackets:true,
value:`class Main {
public static void main(String[] args) {
System.out.println("Hello Ragini Didi!");
}
}`
});
// Resize editor dynamically
function resizeEditor(){
const wrap=document.querySelector('.CodeMirror');
if(!wrap) return;
let vh=window.innerHeight;
wrap.style.height=(vh - 280)+'px';
editor.refresh();
}
window.addEventListener('resize',resizeEditor);
setTimeout(resizeEditor,200);
// ---------- Local storage & file management ----------
let files=JSON.parse(localStorage.getItem('javaRunner_files')||'{}');
let active=localStorage.getItem('javaRunner_active')||null;
const fileListEl=document.getElementById('fileList');
const activeFileEl=document.getElementById('activeFile');
const consoleEl=document.getElementById('console');
function saveToStorage(){localStorage.setItem('javaRunner_files',JSON.stringify(files));}
function setActive(name){
active=name;
localStorage.setItem('javaRunner_active',active);
activeFileEl.innerText=name||'—';
renderFiles();
if(active && files[active]) editor.setValue(files[active]);
}
function renderFiles(filter=''){
fileListEl.innerHTML='';
const names=Object.keys(files).filter(n=>n.toLowerCase().includes(filter.toLowerCase())).sort();
if(names.length===0){fileListEl.innerHTML='<div style="color:'+ 'var(--muted)' +';padding:10px;font-size:13px">No files yet.</div>';return;}
names.forEach(name=>{
const div=document.createElement('div');
div.className='file-item'+(name===active?' active':'');
div.innerHTML=`<div>${name}</div><div style="display:flex;gap:6px;">
<button class="btn" onclick="openFile('${name}')"><i class="fa-solid fa-folder-open"></i></button></div>`;
fileListEl.appendChild(div);
});
}
// File actions
function newFile(){
let name=prompt('Filename (end with .java)','Main.java');
if(!name) return;if(!name.endsWith('.java')){alert('Filename must end with .java');return;}
if(files[name]){alert('File exists');return;}
const classname=name.replace('.java','').replace(/[^A-Za-z0-9_]/g,'')||'Main';
files[name]=`class ${classname} {\n public static void main(String[] args) {\n System.out.println("Hello Ragini Didi!");\n }\n}`;
saveToStorage();
setActive(name);
}
function openFile(name){setActive(name);}
function deleteFile(name){if(confirm(`Delete file ${name}?`)){delete files[name]; if(active===name) active=null; saveToStorage(); setActive(active);}}
function saveFile(){if(!active){alert('No active file'); return;} files[active]=editor.getValue(); saveToStorage(); consoleEl.innerText='Saved successfully!';}
function downloadFile(){if(!active){alert('No active file'); return;} const blob=new Blob([editor.getValue()],{type:'text/plain'}); const a=document.createElement('a'); a.href=URL.createObjectURL(blob); a.download=active; a.click(); URL.revokeObjectURL(a.href);}
function formatCode(){const value=editor.getValue(); editor.setValue(value.split('\n').map(l=>l.trimStart()).join('\n'));}
function cutText(){editor.execCommand('cut');}
function copyText(){editor.execCommand('copy');}
function pasteText(){editor.execCommand('paste');}
// Run code (connect to backend /run API)
async function runCode(){
if(!active){alert('No active file'); return;}
consoleEl.innerText='Running...';
const code=editor.getValue();
try{
const res=await fetch('/run', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({code})});
const data=await res.json();
consoleEl.innerText=data.output||data.error||'No output';
}catch(e){consoleEl.innerText='Error running code: '+e.message;}
}
// Search files
document.getElementById('searchFile').addEventListener('input',e=>renderFiles(e.target.value));
// Button events
document.getElementById('newBtn').addEventListener('click',newFile);
document.getElementById('saveBtn').addEventListener('click',saveFile);
document.getElementById('downloadBtn').addEventListener('click',downloadFile);
document.getElementById('formatBtn').addEventListener('click',formatCode);
document.getElementById('cutBtn').addEventListener('click',cutText);
document.getElementById('copyBtn').addEventListener('click',copyText);
document.getElementById('pasteBtn').addEventListener('click',pasteText);
document.getElementById('runBtn').addEventListener('click',runCode);
// Import files
const fileInput=document.getElementById('fileInput');
document.getElementById('importBtn').addEventListener('click',()=>fileInput.click());
fileInput.addEventListener('change',e=>{
const file=e.target.files[0];
if(!file) return;
const reader=new FileReader();
reader.onload=()=>{files[file.name]=reader.result; saveToStorage(); setActive(file.name);}
reader.readAsText(file);
});
// Initialize
setActive(active);
renderFiles();
</script>
</body>
</html>