anycoder-71db9dcc / index.html
HI7RAI's picture
Upload folder using huggingface_hub
dbe0094 verified
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live ScriptForge IDE</title>
<!-- Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Modern Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;700&family=Fira+Code:wght@300;400;500;600&display=swap" rel="stylesheet">
<!-- CodeMirror -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/dracula.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/hint/show-hint.min.css">
<style>
:root {
--bg: #08090d;
--panel: #0d0f14;
--panel-alt: #12151c;
--accent: #22d3ee;
--accent-secondary: #f472b6;
--accent-tertiary: #a78bfa;
--text: #e2e8f0;
--text-muted: #64748b;
--border: rgba(34, 211, 238, 0.15);
--border-strong: rgba(34, 211, 238, 0.3);
--success: #34d399;
--warning: #fbbf24;
--error: #f87171;
--glass: rgba(13, 15, 20, 0.9);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Space Grotesk', sans-serif;
background: var(--bg);
color: var(--text);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Background Pattern */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(ellipse at 20% 20%, rgba(34, 211, 238, 0.08) 0%, transparent 50%),
radial-gradient(ellipse at 80% 80%, rgba(167, 139, 250, 0.06) 0%, transparent 50%),
radial-gradient(ellipse at 50% 50%, rgba(244, 114, 182, 0.04) 0%, transparent 60%);
pointer-events: none;
z-index: 0;
}
/* --- Header --- */
header {
background: linear-gradient(180deg, rgba(13, 15, 20, 0.98), rgba(13, 15, 20, 0.95));
padding: 12px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border);
backdrop-filter: blur(20px);
z-index: 100;
position: relative;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
}
.brand-icon {
width: 36px;
height: 36px;
background: linear-gradient(135deg, var(--accent), var(--accent-tertiary));
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
color: #000;
box-shadow: 0 4px 15px rgba(34, 211, 238, 0.3);
animation: iconFloat 3s ease-in-out infinite;
}
@keyframes iconFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-3px); }
}
.brand h1 {
font-size: 1.1rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.brand h1 span {
background: linear-gradient(135deg, var(--accent), var(--accent-tertiary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.header-center {
display: flex;
gap: 8px;
align-items: center;
}
.url-input-wrapper {
position: relative;
display: flex;
align-items: center;
}
input[type="text"],
input[type="number"] {
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--border);
color: var(--text);
padding: 10px 14px;
border-radius: 8px;
font-family: 'Fira Code', monospace;
font-size: 0.85rem;
outline: none;
transition: all 0.25s ease;
}
input[type="text"]:focus,
input[type="number"]:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15), inset 0 0 20px rgba(34, 211, 238, 0.05);
}
input::placeholder {
color: var(--text-muted);
}
/* Buttons */
button {
background: linear-gradient(135deg, rgba(34, 211, 238, 0.15), rgba(34, 211, 238, 0.05));
color: var(--accent);
border: 1px solid var(--border-strong);
padding: 10px 18px;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.25s ease;
font-size: 0.8rem;
font-family: 'Space Grotesk', sans-serif;
display: inline-flex;
align-items: center;
gap: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
button:hover {
background: linear-gradient(135deg, var(--accent), var(--accent-tertiary));
color: #000;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(34, 211, 238, 0.25);
border-color: transparent;
}
button:active {
transform: translateY(0);
}
button.primary {
background: linear-gradient(135deg, var(--accent), #06b6d4);
color: #000;
border: none;
font-weight: 700;
}
button.primary:hover {
box-shadow: 0 8px 30px rgba(34, 211, 238, 0.4);
}
button.danger {
background: rgba(248, 113, 113, 0.1);
color: var(--error);
border-color: rgba(248, 113, 113, 0.3);
}
button.danger:hover {
background: var(--error);
color: #000;
border-color: transparent;
}
button.small {
padding: 6px 12px;
font-size: 0.7rem;
}
button.icon-only {
padding: 10px;
min-width: 40px;
justify-content: center;
}
.header-right {
display: flex;
align-items: center;
gap: 12px;
}
.status-badge {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.75rem;
color: var(--text-muted);
padding: 6px 12px;
background: rgba(0, 0, 0, 0.3);
border-radius: 20px;
border: 1px solid var(--border);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--success);
animation: statusPulse 2s ease-in-out infinite;
}
.status-dot.editing {
background: var(--warning);
}
@keyframes statusPulse {
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.4); }
50% { opacity: 0.7; box-shadow: 0 0 0 4px rgba(52, 211, 153, 0); }
}
/* --- Main Layout --- */
main {
display: flex;
flex: 1;
height: calc(100vh - 60px);
position: relative;
z-index: 1;
}
/* Resizer */
.resizer {
width: 5px;
background: var(--border);
cursor: col-resize;
transition: background 0.2s;
position: relative;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
.resizer:hover,
.resizer.dragging {
background: var(--accent);
}
.resizer::after {
content: '';
width: 2px;
height: 30px;
background: var(--text-muted);
border-radius: 2px;
opacity: 0.5;
}
/* Editor Side */
.editor-container {
width: 50%;
display: flex;
flex-direction: column;
background: var(--panel);
min-width: 300px;
}
.panel-header {
padding: 12px 16px;
background: var(--panel-alt);
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 1.5px;
color: var(--text-muted);
font-weight: 600;
}
.panel-title i {
color: var(--accent);
}
.panel-actions {
display: flex;
gap: 6px;
}
/* Injection Panel */
.injection-panel {
padding: 12px 16px;
background: linear-gradient(90deg, rgba(34, 211, 238, 0.05), rgba(167, 139, 250, 0.05));
border-bottom: 1px solid var(--border);
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.injection-panel .label {
font-size: 0.75rem;
color: var(--text-muted);
font-family: 'Fira Code', monospace;
text-transform: uppercase;
letter-spacing: 1px;
}
.injection-group {
display: flex;
align-items: center;
gap: 6px;
}
.injection-panel input[type="number"] {
width: 70px;
padding: 8px 10px;
text-align: center;
}
/* CodeMirror Customization */
.CodeMirror {
flex: 1;
height: auto !important;
font-family: 'Fira Code', monospace;
font-size: 13px;
line-height: 1.6;
background: var(--panel) !important;
}
.CodeMirror-gutters {
background: rgba(0, 0, 0, 0.3) !important;
border-right: 1px solid var(--border) !important;
}
.CodeMirror-linenumber {
color: var(--text-muted) !important;
}
.injected-line {
background: rgba(34, 211, 238, 0.1) !important;
animation: injectFlash 1s ease-out;
}
@keyframes injectFlash {
0% { background: rgba(34, 211, 238, 0.3) !important; }
100% { background: rgba(34, 211, 238, 0.1) !important; }
}
/* Preview Side */
.preview-container {
width: 50%;
position: relative;
background: #000;
display: flex;
flex-direction: column;
min-width: 300px;
}
.preview-header {
padding: 12px 16px;
background: var(--panel-alt);
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.device-selector {
display: flex;
gap: 4px;
background: rgba(0, 0, 0, 0.3);
padding: 4px;
border-radius: 6px;
}
.device-btn {
padding: 6px 10px;
font-size: 0.7rem;
background: transparent;
border: none;
color: var(--text-muted);
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
}
.device-btn:hover,
.device-btn.active {
background: rgba(34, 211, 238, 0.15);
color: var(--accent);
}
.preview-frame-wrapper {
flex: 1;
position: relative;
display: flex;
align-items: center;
justify-content: center;
background: repeating-conic-gradient(#1a1a1a 0% 25%, #111 0% 50%) 50% / 20px 20px;
overflow: hidden;
}
.preview-frame-wrapper.responsive {
padding: 20px;
}
.preview-frame-wrapper.responsive iframe {
max-width: 100%;
max-height: 100%;
border-radius: 8px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
iframe {
width: 100%;
height: 100%;
border: none;
background: #fff;
}
/* Overlay Tools */
.overlay-tools {
position: absolute;
bottom: 20px;
right: 20px;
display: flex;
gap: 8px;
background: var(--glass);
padding: 10px;
border-radius: 10px;
backdrop-filter: blur(15px);
border: 1px solid var(--border);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.overlay-tools button {
padding: 8px 14px;
font-size: 0.7rem;
}
/* Console Panel */
.console-panel {
background: var(--panel);
border-top: 1px solid var(--border);
max-height: 200px;
overflow: hidden;
display: flex;
flex-direction: column;
transition: max-height 0.3s ease;
}
.console-panel.collapsed {
max-height: 36px;
}
.console-header {
padding: 8px 16px;
background: var(--panel-alt);
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
user-select: none;
}
.console-title {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 8px;
}
.console-count {
background: var(--accent);
color: #000;
padding: 2px 6px;
border-radius: 10px;
font-size: 0.65rem;
font-weight: 700;
}
.console-output {
flex: 1;
overflow-y: auto;
padding: 10px;
font-family: 'Fira Code', monospace;
font-size: 0.8rem;
}
.console-line {
padding: 4px 8px;
border-radius: 4px;
margin-bottom: 4px;
display: flex;
align-items: flex-start;
gap: 8px;
}
.console-line.log {
background: rgba(255, 255, 255, 0.03);
color: var(--text);
}
.console-line.error {
background: rgba(248, 113, 113, 0.1);
color: var(--error);
border-left: 2px solid var(--error);
}
.console-line.warn {
background: rgba(251, 191, 36, 0.1);
color: var(--warning);
border-left: 2px solid var(--warning);
}
.console-line .timestamp {
color: var(--text-muted);
font-size: 0.7rem;
min-width: 60px;
}
/* Notification Toast */
.notification {
position: fixed;
bottom: 24px;
left: 24px;
background: var(--glass);
border: 1px solid var(--border);
padding: 14px 20px;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
backdrop-filter: blur(20px);
transform: translateX(-150%);
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
z-index: 1000;
display: flex;
align-items: center;
gap: 12px;
font-size: 0.85rem;
max-width: 350px;
}
.notification.show {
transform: translateX(0);
}
.notification-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
}
.notification.success .notification-icon {
background: rgba(52, 211, 153, 0.2);
color: var(--success);
}
.notification.error .notification-icon {
background: rgba(248, 113, 113, 0.2);
color: var(--error);
}
.notification.warning .notification-icon {
background: rgba(251, 191, 36, 0.2);
color: var(--warning);
}
.notification.info .notification-icon {
background: rgba(34, 211, 238, 0.2);
color: var(--accent);
}
/* Templates Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(5px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.modal-overlay.show {
opacity: 1;
visibility: visible;
}
.modal {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 16px;
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow: hidden;
transform: scale(0.9) translateY(20px);
transition: transform 0.3s ease;
}
.modal-overlay.show .modal {
transform: scale(1) translateY(0);
}
.modal-header {
padding: 20px 24px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
font-size: 1.1rem;
font-weight: 600;
}
.modal-body {
padding: 20px;
overflow-y: auto;
max-height: calc(80vh - 140px);
}
.template-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 12px;
}
.template-card {
background: var(--panel-alt);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
cursor: pointer;
transition: all 0.25s ease;
}
.template-card:hover {
border-color: var(--accent);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(34, 211, 238, 0.15);
}
.template-card h3 {
font-size: 0.9rem;
margin-bottom: 6px;
color: var(--text);
}
.template-card p {
font-size: 0.75rem;
color: var(--text-muted);
}
/* Keyboard Shortcuts Hint */
.shortcuts-hint {
position: fixed;
bottom: 24px;
right: 24px;
font-size: 0.7rem;
color: var(--text-muted);
background: var(--glass);
padding: 8px 12px;
border-radius: 6px;
border: 1px solid var(--border);
opacity: 0.6;
transition: opacity 0.2s;
}
.shortcuts-hint:hover {
opacity: 1;
}
.shortcuts-hint kbd {
background: rgba(0, 0, 0, 0.3);
padding: 2px 6px;
border-radius: 4px;
margin: 0 2px;
font-family: 'Fira Code', monospace;
}
/* Attribution Link */
.attribution-link {
font-size: 0.75rem;
color: var(--text-muted);
text-decoration: none;
padding: 4px 10px;
border-radius: 4px;
transition: color 0.2s;
border: 1px solid transparent;
}
.attribution-link:hover {
color: var(--accent);
border-color: var(--border);
}
/* Responsive */
@media (max-width: 1024px) {
.header-center {
display: none;
}
main {
flex-direction: column;
}
.editor-container,
.preview-container {
width: 100% !important;
height: 50%;
min-width: unset;
}
.resizer {
display: none;
}
.shortcuts-hint {
display: none;
}
}
@media (max-width: 600px) {
header {
padding: 10px 12px;
}
.brand h1 {
font-size: 0.9rem;
}
.injection-panel {
padding: 10px;
gap: 8px;
}
.injection-panel input[type="number"] {
width: 55px;
padding: 6px 8px;
}
.overlay-tools {
bottom: 10px;
right: 10px;
padding: 8px;
}
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--border-strong);
}
</style>
</head>
<body>
<header>
<div class="brand">
<div class="brand-icon">
<i class="fa-solid fa-bolt"></i>
</div>
<h1><span>ScriptForge</span> IDE</h1>
</div>
<div class="header-center">
<div class="url-input-wrapper">
<input type="text" id="targetUrl" placeholder="URL zum Mergen eingeben..." style="width: 280px;">
</div>
<button id="fetchBtn" class="primary">
<i class="fa-solid fa-code-merge"></i> Merge
</button>
</div>
<div class="header-right">
<button id="templatesBtn" class="small" title="Templates">
<i class="fa-solid fa-folder-open"></i>
</button>
<button id="refreshBtn" title="Aktualisieren">
<i class="fa-solid fa-play"></i> Run
</button>
<div class="status-badge">
<div class="status-dot" id="statusDot"></div>
<span id="statusText">Bereit</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="attribution-link">
Built with anycoder
</a>
</div>
</header>
<main>
<!-- Editor Side -->
<div class="editor-container" id="editorPanel">
<div class="panel-header">
<div class="panel-title">
<i class="fa-solid fa-code"></i>
Editor
</div>
<div class="panel-actions">
<button class="small icon-only" id="formatBtn" title="Formatieren">
<i class="fa-solid fa-align-left"></i>
</button>
<button class="small icon-only" id="clearBtn" title="Löschen">
<i class="fa-solid fa-trash"></i>
</button>
</div>
</div>
<div class="injection-panel">
<span class="label">Injection Position:</span>
<div class="injection-group">
<input type="number" id="injectLine" value="10" min="1">
<span class="label">Line</span>
</div>
<button id="injectBtn" class="small">
<i class="fa-solid fa-code-branch"></i> Inject
</button>
</div>
<div id="codeEditor" style="flex: 1; overflow: hidden;"></div>
<div class="console-panel" id="consolePanel">
<div class="console-header" id="consoleToggle">
<div class="console-title">
<i class="fa-solid fa-terminal"></i>
Console
<span class="console-count" id="logCount">0</span>
</div>
<i class="fa-solid fa-chevron-down"></i>
</div>
<div class="console-output" id="consoleOutput">
<!-- Logs go here -->
</div>
</div>
</div>
<!-- Resizer -->
<div class="resizer" id="resizer"></div>
<!-- Preview Side -->
<div class="preview-container" id="previewPanel">
<div class="preview-header">
<div class="panel-title">
<i class="fa-solid fa-desktop"></i>
Preview
</div>
<div class="device-selector">
<button class="device-btn active" data-device="desktop"><i class="fa-solid fa-desktop"></i></button>
<button class="device-btn" data-device="tablet"><i class="fa-solid fa-tablet-screen-button"></i></button>
<button class="device-btn" data-device="mobile"><i class="fa-solid fa-mobile-screen-button"></i></button>
</div>
</div>
<div class="preview-frame-wrapper responsive" id="previewWrapper">
<iframe id="previewFrame" sandbox="allow-scripts allow-same-origin allow-modals"></iframe>
</div>
<div class="overlay-tools">
<button id="downloadBtn" title="Download HTML">
<i class="fa-solid fa-download"></i>
</button>
<button id="shareBtn" title="Share">
<i class="fa-solid fa-share-nodes"></i>
</button>
</div>
</div>
</main>
<!-- Templates Modal -->
<div class="modal-overlay" id="templatesModal">
<div class="modal">
<div class="modal-header">
<h2><i class="fa-solid fa-folder-tree"></i> Templates wählen</h2>
<button class="icon-only" id="closeModalBtn" style="background: transparent; border: none; color: var(--text-muted);">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<div class="modal-body">
<div class="template-grid">
<div class="template-card" data-template="hello">
<h3>Hello World</h3>
<p>Basis Script für Konsolenausgabe.</p>
</div>
<div class="template-card" data-template="dom-manip">
<h3>DOM Manipulation</h3>
<p>Elemente ändern und Styling hinzufügen.</p>
</div>
<div class="template-card" data-template="api-fetch">
<h3>API Fetch</h3>
<p>Daten von einer externen API laden.</p>
</div>
<div class="template-card" data-template="style-inject">
<h3>CSS Injection</h3>
<p>Neues Stylesheet in die Seite injizieren.</p>
</div>
</div>
</div>
</div>
</div>
<!-- Notification Toast -->
<div class="notification" id="notification">
<div class="notification-icon">
<i class="fa-solid fa-check"></i>
</div>
<span class="notification-text">Action completed successfully!</span>
</div>
<div class="shortcuts-hint">
<kbd>Ctrl</kbd> + <kbd>Enter</kbd> Run &nbsp;|&nbsp; <kbd>Ctrl</kbd> + <kbd>S</kbd> Save
</div>
<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/xml/xml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/javascript/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/css/css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/htmlmixed/htmlmixed.min.js"></script>
<script>
// --- Initialization ---
const editor = CodeMirror(document.getElementById('codeEditor'), {
mode: 'htmlmixed',
theme: 'dracula',
lineNumbers: true,
lineWrapping: true,
autoCloseTags: true,
autoCloseBrackets: true,
value: `<!-- ScriptForge Initialisiert -->\n<script>\n console.log("ScriptForge bereit!");\n document.body.style.background = '#f0f0f0';\n<\/script>\n\n<div style="padding: 20px; font-family: sans-serif;">\n <h1>Hallo Welt!</h1>\n <p>Dies ist eine Vorschau deines injizierten Codes.</p>\n <button onclick="alert('Klick registriert!')">Test Button</button>\n</div>`
});
// --- State Management ---
const state = {
logs: [],
isConsoleOpen: true
};
// --- DOM Elements ---
const resizer = document.getElementById('resizer');
const editorPanel = document.getElementById('editorPanel');
const previewPanel = document.getElementById('previewPanel');
const previewFrame = document.getElementById('previewFrame');
const consolePanel = document.getElementById('consolePanel');
const consoleOutput = document.getElementById('consoleOutput');
const logCountEl = document.getElementById('logCount');
const notification = document.getElementById('notification');
// --- Resize Logic ---
let isResizing = false;
resizer.addEventListener('mousedown', (e) => {
isResizing = true;
resizer.classList.add('dragging');
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
});
document.addEventListener('mousemove', (e) => {
if (!isResizing) return;
const containerWidth = document.querySelector('main').offsetWidth;
const newWidth = (e.clientX / containerWidth) * 100;
if (newWidth > 20 && newWidth < 80) {
editorPanel.style.width = `${newWidth}%`;
previewPanel.style.width = `${100 - newWidth}%`;
}
});
document.addEventListener('mouseup', () => {
isResizing = false;
resizer.classList.remove('dragging');
document.body.style.cursor = 'default';
document.body.style.userSelect = 'auto';
});
// --- Console Logic ---
function addLog(type, message) {
const timestamp = new Date().toLocaleTimeString();
const logLine = document.createElement('div');
logLine.className = `console-line ${type}`;
logLine.innerHTML = `<span class="timestamp">${timestamp}</span> <span>${message}</span>`;
consoleOutput.appendChild(logLine);
consoleOutput.scrollTop = consoleOutput.scrollHeight;
state.logs.push({ type, message, timestamp });
logCountEl.textContent = state.logs.length;
// Update status
const statusDot = document.getElementById('statusDot');
const statusText = document.getElementById('statusText');
if(type === 'error') {
statusDot.style.background = 'var(--error)';
statusText.textContent = 'Fehler';
} else {
statusDot.style.background = 'var(--success)';
statusText.textContent = 'Aktiv';
}
}
// Intercept console.log in iframe
function updatePreview() {
const code = editor.getValue();
const doc = previewFrame.contentDocument || previewFrame.contentWindow.document;
// Create a proxy for console
const consoleProxy = `
<script>
(function() {
const originalLog = console.log;
const originalError = console.error;
const originalWarn = console.warn;
function sendToParent(type, args) {
const message = Array.from(args).map(arg =>
typeof arg === 'object' ? JSON.stringify(arg) : String(arg