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:
90
public/js/elements-panel.js
Normal file
90
public/js/elements-panel.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { registerClearHandler } from './devtools.js';
|
||||
|
||||
function renderNode(node, depth, maxExpand) {
|
||||
if (node.type === 'text') {
|
||||
const text = node.text.trim();
|
||||
if (!text) return '';
|
||||
return `<div class="el-node"><span class="el-text">${escapeHtml(text)}</span></div>`;
|
||||
}
|
||||
|
||||
const hasChildren = node.children && node.children.length > 0;
|
||||
const expanded = depth < maxExpand;
|
||||
|
||||
let attrs = '';
|
||||
if (node.attrs) {
|
||||
for (const a of node.attrs) {
|
||||
attrs += ` <span class="el-attr-name">${escapeHtml(a.name)}</span>=<span class="el-attr-value">"${escapeHtml(a.value)}"</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
let html = '<div class="el-node">';
|
||||
html += `<div class="el-node-header" data-has-children="${hasChildren}">`;
|
||||
html += `<span class="el-toggle">${hasChildren ? (expanded ? '▼' : '▶') : ' '}</span>`;
|
||||
html += `<span><<span class="el-tag">${escapeHtml(node.tag)}</span>${attrs}></span>`;
|
||||
|
||||
// Inline short text content
|
||||
if (hasChildren && node.children.length === 1 && node.children[0].type === 'text' && node.children[0].text.trim().length < 60) {
|
||||
html += `<span class="el-text">${escapeHtml(node.children[0].text.trim())}</span>`;
|
||||
html += `<span></<span class="el-tag">${escapeHtml(node.tag)}</span>></span>`;
|
||||
html += '</div></div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
if (hasChildren) {
|
||||
html += `<div class="el-children ${expanded ? 'expanded' : ''}">`;
|
||||
for (const child of node.children) {
|
||||
html += renderNode(child, depth + 1, maxExpand);
|
||||
}
|
||||
html += `<div style="padding-left:16px"></<span class="el-tag">${escapeHtml(node.tag)}</span>></div>`;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function bindToggleHandlers(container) {
|
||||
container.querySelectorAll('.el-node-header[data-has-children="true"]').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
const children = header.nextElementSibling;
|
||||
if (!children || !children.classList.contains('el-children')) return;
|
||||
const toggle = header.querySelector('.el-toggle');
|
||||
const isExpanded = children.classList.contains('expanded');
|
||||
children.classList.toggle('expanded');
|
||||
toggle.textContent = isExpanded ? '▶' : '▼';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let lastTree = null;
|
||||
|
||||
function render(tree) {
|
||||
lastTree = tree;
|
||||
const out = document.getElementById('elements-output');
|
||||
if (!tree) {
|
||||
out.innerHTML = '<div style="padding:12px;color:var(--text-dim);font-size:11px;">No elements captured. Run your code to see the DOM tree.</div>';
|
||||
return;
|
||||
}
|
||||
out.innerHTML = renderNode(tree, 0, 2);
|
||||
bindToggleHandlers(out);
|
||||
}
|
||||
|
||||
export function clearElements() {
|
||||
lastTree = null;
|
||||
document.getElementById('elements-output').innerHTML = '';
|
||||
}
|
||||
|
||||
export function initElements() {
|
||||
registerClearHandler('elements', clearElements);
|
||||
|
||||
window.addEventListener('message', (e) => {
|
||||
if (!e.data || e.data.type !== 'devtools' || e.data.tab !== 'elements') return;
|
||||
if (e.data.tree) render(e.data.tree);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user