Add responsive preview, editor themes, template gallery, devtools, and autocomplete

- Device breakpoint toggles (mobile 375px / tablet 768px / desktop 100%)
- Editor theme selector with 6 themes (VS Dark/Light, High Contrast, Monokai, Dracula, GitHub Dark)
- Starter template gallery with 8 pre-built templates (Todo, API Fetch, CSS Animation, etc.)
- Code autocomplete with DOM/React type definitions and snippet completions
- Devtools panels: console, network, elements, performance
- Code formatter (Prettier), diff view, and linter integration
This commit is contained in:
root
2026-02-27 01:22:01 -06:00
parent b18c9c1dc8
commit 6ca8519250
19 changed files with 1755 additions and 29 deletions

View File

@@ -0,0 +1,70 @@
import { registerClearHandler } from './devtools.js';
let entries = [];
function formatSize(bytes) {
if (bytes === 0 || bytes === undefined) return '-';
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
function typeClass(initiatorType) {
switch (initiatorType) {
case 'script': return 'net-type-script';
case 'link': case 'css': return 'net-type-link';
case 'img': return 'net-type-img';
case 'fetch': return 'net-type-fetch';
case 'xmlhttprequest': return 'net-type-xmlhttprequest';
default: return 'net-type-other';
}
}
function truncateUrl(url) {
try {
const u = new URL(url);
const path = u.pathname.split('/').pop() || u.pathname;
return path + u.search;
} catch { return url; }
}
function render() {
const out = document.getElementById('network-output');
if (!entries.length) {
out.innerHTML = '<div style="padding:12px;color:var(--text-dim);font-size:11px;">No network requests captured. Run your code to see resources.</div>';
return;
}
let totalSize = 0;
let html = '<table class="network-table"><thead><tr><th>Name</th><th>Type</th><th>Size</th><th>Time</th></tr></thead><tbody>';
for (const e of entries) {
totalSize += e.transferSize || 0;
html += `<tr>
<td title="${e.name}">${truncateUrl(e.name)}</td>
<td><span class="net-type ${typeClass(e.initiatorType)}">${e.initiatorType}</span></td>
<td>${formatSize(e.transferSize)}</td>
<td>${Math.round(e.duration)}ms</td>
</tr>`;
}
html += '</tbody></table>';
html += `<div class="network-footer"><span>${entries.length} request${entries.length !== 1 ? 's' : ''}</span><span>${formatSize(totalSize)} transferred</span></div>`;
out.innerHTML = html;
}
export function clearNetwork() {
entries = [];
const out = document.getElementById('network-output');
out.innerHTML = '';
}
export function initNetwork() {
registerClearHandler('network', clearNetwork);
window.addEventListener('message', (e) => {
if (!e.data || e.data.type !== 'devtools' || e.data.tab !== 'network') return;
if (e.data.entries) {
entries.push(...e.data.entries);
render();
}
});
}