anycoder-efdeab1b / index.html
XXXMARK's picture
Upload folder using huggingface_hub
4825688 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CodeFlow Studio - Live Editor</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600&family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #1e1e1e;
}
::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
/* Editor Styles */
.editor-container {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
font-family: 'Fira Code', monospace;
}
.line-numbers {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3rem;
background-color: #1e1e1e;
color: #6b7280;
text-align: right;
padding: 1rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5rem;
user-select: none;
border-right: 1px solid #374151;
z-index: 10;
}
textarea.code-input {
position: absolute;
left: 3rem;
top: 0;
width: calc(100% - 3rem);
height: 100%;
background: transparent;
color: #e5e7eb;
border: none;
resize: none;
outline: none;
padding: 1rem;
font-size: 0.875rem;
line-height: 1.5rem;
white-space: pre;
overflow: auto;
z-index: 20;
caret-color: #60a5fa;
}
/* Syntax Highlighting Layer (Visual Only) */
pre.code-highlight {
position: absolute;
left: 3rem;
top: 0;
width: calc(100% - 3rem);
height: 100%;
margin: 0;
padding: 1rem;
font-size: 0.875rem;
line-height: 1.5rem;
pointer-events: none;
z-index: 15;
overflow: hidden;
white-space: pre;
}
/* Glassmorphism & Utilities */
.glass-panel {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Range Slider Styling */
input[type=range] {
-webkit-appearance: none;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: #60a5fa;
cursor: pointer;
margin-top: -6px;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: #4b5563;
border-radius: 2px;
}
</style>
</head>
<body class="bg-gray-900 text-gray-100 h-screen w-screen overflow-hidden flex flex-col font-sans selection:bg-blue-500 selection:text-white">
<!-- Header -->
<header class="h-14 bg-gray-800 border-b border-gray-700 flex items-center justify-between px-4 shrink-0 z-30 shadow-lg">
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center shadow-blue-500/20 shadow-lg">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path></svg>
</div>
<h1 class="font-bold text-lg tracking-tight">Code<span class="text-blue-400">Flow</span></h1>
<span class="text-xs bg-gray-700 px-2 py-0.5 rounded text-gray-400 border border-gray-600">v2.0</span>
</div>
<div class="flex items-center gap-4">
<!-- Built with Anycoder Link -->
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="hidden md:flex items-center gap-2 text-xs font-medium text-gray-400 hover:text-blue-400 transition-colors px-3 py-1.5 rounded-full bg-gray-800 border border-gray-700 hover:border-blue-500/50 group">
<span class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></span>
Built with anycoder
</a>
<button id="settingsBtn" class="p-2 text-gray-400 hover:text-white hover:bg-gray-700 rounded-lg transition-all duration-200 group relative">
<svg class="w-5 h-5 group-hover:rotate-90 transition-transform duration-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
</button>
</div>
</header>
<!-- Main Workspace -->
<main class="flex-1 flex overflow-hidden relative">
<!-- Left Panel: Input -->
<section class="flex-1 flex flex-col min-w-[300px] border-r border-gray-700 bg-[#1e1e1e] relative group">
<!-- Toolbar -->
<div class="h-10 bg-[#252526] flex items-center justify-between px-4 border-b border-gray-700 select-none">
<div class="flex items-center gap-2">
<span class="text-xs font-semibold text-blue-400 uppercase tracking-wider">Input.js</span>
<span class="w-2 h-2 rounded-full bg-yellow-500" title="Unsaved changes"></span>
</div>
<div class="flex gap-2">
<button onclick="copyCode()" class="text-xs flex items-center gap-1 text-gray-400 hover:text-white px-2 py-1 rounded hover:bg-gray-700 transition-colors" title="Copy to Clipboard">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
Copy
</button>
<button onclick="downloadCode()" class="text-xs flex items-center gap-1 text-gray-400 hover:text-white px-2 py-1 rounded hover:bg-gray-700 transition-colors" title="Download .txt">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
Save
</button>
</div>
</div>
<!-- Editor Area -->
<div class="relative flex-1 overflow-hidden">
<div class="editor-container">
<div class="line-numbers" id="lineNumbers">1</div>
<textarea id="codeEditor" class="code-input" spellcheck="false" placeholder="// Write your JavaScript code here...">// Welcome to CodeFlow Studio
// Try editing this code and see the results instantly!
function calculateFactorial(n) {
if (n === 0 || n === 1) {
return 1;
}
return n * calculateFactorial(n - 1);
}
const number = 5;
const result = calculateFactorial(number);
console.log(`The factorial of ${number} is: ${result}`);
// Create a simple visual element
const div = document.createElement('div');
div.style.padding = '20px';
div.style.background = 'linear-gradient(135deg, #6366f1, #a855f7)';
div.style.color = 'white';
div.style.borderRadius = '12px';
div.style.marginTop = '10px';
div.style.textAlign = 'center';
div.style.fontFamily = 'sans-serif';
div.innerHTML = '<h2>Hello World!</h2><p>This DOM node was created by your code.</p>';
document.body.appendChild(div);</textarea>
</div>
</div>
<!-- Status Bar -->
<div class="h-6 bg-[#007acc] text-white text-[10px] flex items-center px-3 justify-between select-none">
<div class="flex gap-4">
<span class="flex items-center gap-1"><svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path></svg> master*</span>
<span id="cursorPos">Ln 1, Col 1</span>
</div>
<div class="flex gap-4">
<span>UTF-8</span>
<span>JavaScript</span>
<span id="liveIndicator" class="flex items-center gap-1"><span class="w-1.5 h-1.5 bg-green-400 rounded-full animate-pulse"></span> Live</span>
</div>
</div>
</section>
<!-- Resizer (Visual only for this demo, functional in full app) -->
<div class="w-1 bg-gray-700 hover:bg-blue-500 cursor-col-resize transition-colors z-20"></div>
<!-- Right Panel: Output -->
<section class="flex-1 flex flex-col min-w-[300px] bg-white relative">
<!-- Output Toolbar -->
<div class="h-10 bg-gray-100 flex items-center justify-between px-4 border-b border-gray-200 select-none">
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg>
<span class="text-xs font-semibold text-gray-600 uppercase tracking-wider">Live Preview</span>
</div>
<button onclick="clearOutput()" class="text-xs text-red-500 hover:text-red-700 font-medium px-2 py-1 rounded hover:bg-red-50 transition-colors">
Clear Console
</button>
</div>
<!-- Output Content -->
<div class="flex-1 overflow-auto p-4 font-mono text-sm text-gray-800" id="outputContainer">
<!-- Dynamic content goes here -->
<div class="text-gray-400 italic text-xs mb-2 border-b border-gray-200 pb-2">Console Output:</div>
<div id="consoleLogs" class="space-y-1"></div>
<div id="visualOutput" class="mt-4"></div>
</div>
</section>
</main>
<!-- Settings Modal Overlay -->
<div id="settingsModal" class="fixed inset-0 z-50 hidden">
<!-- Backdrop -->
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm transition-opacity opacity-0" id="modalBackdrop"></div>
<!-- Modal Content -->
<div class="absolute inset-0 flex items-center justify-center p-4 pointer-events-none">
<div class="bg-[#1e1e1e] w-full max-w-md rounded-xl shadow-2xl border border-gray-700 transform scale-95 opacity-0 transition-all duration-300 pointer-events-auto flex flex-col max-h-[80vh]" id="modalPanel">
<!-- Modal Header -->
<div class="flex items-center justify-between p-4 border-b border-gray-700">
<h2 class="text-lg font-semibold text-white flex items-center gap-2">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
Settings
</h2>
<button id="closeSettings" class="text-gray-400 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<!-- Modal Body -->
<div class="p-6 space-y-6 overflow-y-auto custom-scrollbar">
<!-- Theme Selection -->
<div class="space-y-3">
<label class="text-sm font-medium text-gray-300">Editor Theme</label>
<div class="grid grid-cols-3 gap-3">
<button class="theme-btn p-3 rounded-lg border border-gray-600 bg-[#1e1e1e] hover:border-blue-500 focus:ring-2 ring-blue-500 transition-all active-theme" data-theme="dark">
<div class="w-full h-8 bg-gray-800 rounded mb-2 border border-gray-700"></div>
<span class="text-xs text-gray-400">Dark</span>
</button>
<button class="theme-btn p-3 rounded-lg border border-gray-600 bg-[#1e1e1e] hover:border-blue-500 focus:ring-2 ring-blue-500 transition-all" data-theme="midnight">
<div class="w-full h-8 bg-[#0f172a] rounded mb-2 border border-gray-700"></div>
<span class="text-xs text-gray-400">Midnight</span>
</button>
<button class="theme-btn p-3 rounded-lg border border-gray-600 bg-[#1e1e1e] hover:border-blue-500 focus:ring-2 ring-blue-500 transition-all" data-theme="light">
<div class="w-full h-8 bg-gray-100 rounded mb-2 border border-gray-300"></div>
<span class="text-xs text-gray-400">Light</span>
</button>
</div>
</div>
<!-- Font Size -->
<div class="space-y-3">
<div class="flex justify-between">
<label class="text-sm font-medium text-gray-300">Font Size</label>
<span id="fontSizeVal" class="text-xs text-blue-400 font-mono">14px</span>
</div>
<input type="range" min="10" max="24" value="14" id="fontSizeSlider" class="w-full">
</div>
<!-- Toggles -->
<div class="space-y-3">
<label class="text-sm font-medium text-gray-300">Behavior</label>
<label class="flex items-center justify-between p-3 rounded-lg bg-gray-800/50 border border-gray-700 cursor-pointer hover:bg-gray-800 transition-colors">
<span class="text-sm text-gray-300">Word Wrap</span>
<div class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
<input type="checkbox" name="toggle" id="wordWrapToggle" class="toggle-checkbox absolute block w-5 h-5 rounded-full bg-white border-4 appearance-none cursor-pointer checked:right-0 checked:border-blue-500"/>
<label for="wordWrapToggle" class="toggle-label block overflow-hidden h-5 rounded-full bg-gray-600 cursor-pointer"></label>
</div>
</label>
<label class="flex items-center justify-between p-3 rounded-lg bg-gray-800/50 border border-gray-700 cursor-pointer hover:bg-gray-800 transition-colors">
<span class="text-sm text-gray-300">Auto Run on Type</span>
<div class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
<input type="checkbox" name="toggle" id="autoRunToggle" checked class="toggle-checkbox absolute block w-5 h-5 rounded-full bg-white border-4 appearance-none cursor-pointer checked:right-0 checked:border-blue-500"/>
<label for="autoRunToggle" class="toggle-label block overflow-hidden h-5 rounded-full bg-gray-600 cursor-pointer"></label>
</div>
</label>
</div>
</div>
<!-- Modal Footer -->
<div class="p-4 border-t border-gray-700 flex justify-end bg-[#252526] rounded-b-xl">
<button id="saveSettings" class="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium rounded-lg transition-colors shadow-lg shadow-blue-600/20">
Done
</button>
</div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div id="toast" class="fixed bottom-6 right-6 bg-gray-800 text-white px-4 py-3 rounded-lg shadow-xl border border-gray-700 transform translate-y-20 opacity-0 transition-all duration-300 flex items-center gap-3 z-50">
<div id="toastIcon"></div>
<span id="toastMsg" class="text-sm font-medium">Notification</span>
</div>
<script>
// --- Elements ---
const editor = document.getElementById('codeEditor');
const lineNumbers = document.getElementById('lineNumbers');
const consoleLogs = document.getElementById('consoleLogs');
const visualOutput = document.getElementById('visualOutput');
const cursorPosDisplay = document.getElementById('cursorPos');
// Settings Elements
const settingsBtn = document.getElementById('settingsBtn');
const settingsModal = document.getElementById('settingsModal');
const modalBackdrop = document.getElementById('modalBackdrop');
const modalPanel = document.getElementById('modalPanel');
const closeSettings = document.getElementById('closeSettings');
const saveSettings = document.getElementById('saveSettings');
const fontSizeSlider = document.getElementById('fontSizeSlider');
const fontSizeVal = document.getElementById('fontSizeVal');
const wordWrapToggle = document.getElementById('wordWrapToggle');
const autoRunToggle = document.getElementById('autoRunToggle');
// State
let config = {
fontSize: 14,
wordWrap: false,
autoRun: true,
theme: 'dark'
};
// --- Initialization ---
document.addEventListener('DOMContentLoaded', () => {
updateLineNumbers();
runCode(); // Initial run
});
// --- Editor Logic ---
// Sync Line Numbers
const updateLineNumbers = () => {
const lines = editor.value.split('\n').length;
lineNumbers.innerHTML = Array(lines).fill(0).map((_, i) => `<div>${i + 1}</div>`).join('');
};
// Sync Scroll
editor.addEventListener('scroll', () => {
lineNumbers.scrollTop = editor.scrollTop;
});
// Input Handling
editor.addEventListener('input', () => {
updateLineNumbers();
if (config.autoRun) {
debouncedRun();
}
});
// Tab Support
editor.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const start = editor.selectionStart;
const end = editor.selectionEnd;
editor.value = editor.value.substring(0, start) + " " + editor.value.substring(end);
editor.selectionStart = editor.selectionEnd = start + 4;
}
});
// Cursor Position Update
editor.addEventListener('keyup', updateCursorPos);
editor.addEventListener('click', updateCursorPos);
function updateCursorPos() {
const val = editor.value;
const sel = editor.selectionStart;
const lines = val.substr(0, sel).split("\n");
const lineNum = lines.length;
const colNum = lines[lines.length - 1].length + 1;
cursorPosDisplay.textContent = `Ln ${lineNum}, Col ${colNum}`;
}
// --- Execution Engine ---
let debounceTimer;
const debouncedRun = () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(runCode, 800); // 800ms delay for live typing
};
function clearOutput() {
consoleLogs.innerHTML = '';
visualOutput.innerHTML = '';
}
function runCode() {
clearOutput();
const code = editor.value;
// Capture Console
const originalLog = console.log;
const originalError = console.error;
const originalWarn = console.warn;
const createLogEntry = (type, args) => {
const div = document.createElement('div');
div.className = `font-mono text-xs p-2 rounded border-l-2 ${
type === 'error' ? 'bg-red-50 border-red-500 text-red-700' :
type === 'warn' ? 'bg-yellow-50 border-yellow-500 text-yellow-700' :
'bg-gray-50 border-blue-500 text-gray-700'
}`;
// Format arguments
const text = args.map(arg => {
if (typeof arg === 'object') return JSON.stringify(arg, null, 2);
return String(arg);
}).join(' ');
div.textContent = `> ${text}`;
consoleLogs.appendChild(div);
// Auto scroll to bottom
const container = document.getElementById('outputContainer');
container.scrollTop = container.scrollHeight;
};
console.log = (...args) => {
createLogEntry('log', args);
originalLog(...args);
};
console.error = (...args) => {
createLogEntry('error', args);
originalError(...args);
};
console.warn = (...args) => {
createLogEntry('warn', args);
originalWarn(...args);
};
try {
// Execute Code safely-ish
// We wrap it in a function to allow returns (though ignored) and strict mode
const runUserCode = new Function(code);
runUserCode();
} catch (err) {
console.error(err.toString());
} finally {
// Restore console
console.log = originalLog;
console.error = originalError;
console.warn = originalWarn;
}
}
// --- Settings Modal Logic ---
function openModal() {
settingsModal.classList.remove('hidden');
// Trigger animations
setTimeout(() => {
modalBackdrop.classList.remove('opacity-0');
modalPanel.classList.remove('opacity-0', 'scale-95');
modalPanel.classList.add('scale-100');
}, 10);
}
function closeModal() {
modalBackdrop.classList.add('opacity-0');
modalPanel.classList.remove('scale-100');
modalPanel.classList.add('opacity-0', 'scale-95');
setTimeout(() => {
settingsModal.classList.add('hidden');
}, 300);
}
settingsBtn.addEventListener('click', openModal);
closeSettings.addEventListener('click', closeModal);
saveSettings.addEventListener('click', closeModal);
modalBackdrop.addEventListener('click', closeModal);
// --- Settings Interactions ---
// Font Size
fontSizeSlider.addEventListener('input', (e) => {
const size = e.target.value;
fontSizeVal.textContent = `${size}px`;
editor.style.fontSize = `${size}px`;
lineNumbers.style.fontSize = `${size}px`;
lineNumbers.style.lineHeight = `${parseInt(size) * 1.5}px`; // Approximate
editor.style.lineHeight = `${parseInt(size) * 1.5}px`;
});
// Word Wrap
wordWrapToggle.addEventListener('change', (e) => {
config.wordWrap = e.target.checked;
editor.style.whiteSpace = config.wordWrap ? 'pre-wrap' : 'pre';
});
// Auto Run
autoRunToggle.addEventListener('change', (e) => {
config.autoRun = e.target.checked;
const indicator = document.getElementById('liveIndicator');
if(config.autoRun) {
indicator.innerHTML = '<span class="w-1.5 h-1.5 bg-green-400 rounded-full animate-pulse"></span> Live';
runCode(); // Run immediately if turned on
} else {
indicator.innerHTML = '<span class="w-1.5 h-1.5 bg-gray-400 rounded-full"></span> Manual';
}
});
// Theme Switching (Simplified)
document.querySelectorAll('.theme-btn').forEach(btn => {
btn.addEventListener('click', () => {
// Remove active state from all
document.querySelectorAll('.theme-btn').forEach(b => {
b.classList.remove('ring-2', 'ring-blue-500', 'border-blue-500');
b.classList.add('border-gray-600');
});
// Add active state
btn.classList.remove('border-gray-600');
btn.classList.add('ring-2', 'ring-blue-500', 'border-blue-500');
const theme = btn.dataset.theme;
applyTheme(theme);
});
});
function applyTheme(theme) {
if (theme === 'light') {
editor.style.backgroundColor = '#ffffff';
editor.style.color = '#1f2937';
lineNumbers.style.backgroundColor = '#f3f4f6';
lineNumbers.style.color = '#9ca3af';
lineNumbers.style.borderRightColor = '#e5e7eb';
} else if (theme === 'midnight') {
editor.style.backgroundColor = '#0f172a';
editor.style.color = '#cbd5e1';
lineNumbers.style.backgroundColor = '#1e293b';
lineNumbers.style.color = '#64748b';
lineNumbers.style.borderRightColor = '#334155';
} else {
// Dark Default
editor.style.backgroundColor = '#1e1e1e';
editor.style.color = '#e5e7eb';
lineNumbers.style.backgroundColor = '#1e1e1e';
lineNumbers.style.color = '#6b7280';
lineNumbers.style.borderRightColor = '#374151';
}
}
// --- Utilities ---
function copyCode() {
navigator.clipboard.writeText(editor.value).then(() => {
showToast('Code copied to clipboard!', 'success');
}).catch(() => {
showToast('Failed to copy code.', 'error');
});
}
function downloadCode() {
const blob = new Blob([editor.value], { type: 'text/plain' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'script.txt'; // As requested .txt
a.click();
window.URL.revokeObjectURL(url);
showToast('File downloaded successfully!', 'success');
}
function showToast(message, type) {
const toast = document.getElementById('toast');
const msg = document.getElementById('toastMsg');
const icon = document.getElementById('toastIcon');
msg.textContent = message;
if (type === 'success') {
icon.innerHTML = `<svg class="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>`;
} else {
icon.innerHTML = `<svg class="w-5 h-5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>`;
}
toast.classList.remove('translate-y-20', 'opacity-0');
setTimeout(() => {
toast.classList.add('translate-y-20', 'opacity-0');
}, 3000);
}
</script>
</body>
</html>