📱 HTML DevTools 💻
1.3 KB ✨ 格式化 📦 压缩 📋 分段
✨ 格式化 📦 压缩 📋 分段 源码大小: 0 B
16
⚡ 操作
🔍 元素检查器
🎨 CSS 快速编辑
0
0
0
1
🖼️ 边框与轮廓 NEW
0
0
🔄 变换与过渡 NEW
1.0
0s
🎨 滤镜效果 NEW
0px
100%
100%
0%
0deg
✒️ 字体与文本
16
1.5
0
400
🌈 颜色工具
就绪
🔍 查找与替换
查找
替换
0/0
\n\n' + bodyHTML + '\n\n'; } catch (e) { return frame.srcdoc || ''; } } function syncPreviewToCode() { if (_isSyncing) return; if (syncingFromCode) return; if (isDrag || _isResizing) { clearTimeout(window._syncTimer); window._syncTimer = setTimeout(() => { syncPreviewToCode(); }, 200); return; } _isSyncing = true; syncingFromPreview = true; try { const html = getEditedHTML(); if (html && html !== code.value) { code.value = html; lastHTML = html; localStorage.setItem('html_editor_content', html); updateSizeBadge(html); updateSegIndicator(); // 不重新刷新,避免循环 } } catch (e) { console.warn('sync error:', e); } setTimeout(() => { syncingFromPreview = false; _isSyncing = false; }, 100); } // ============================================================ // 开发者工具 - 新增CSS功能函数 (保持不变) // ============================================================ function quickCSS(prop, value) { if (!activeEl && !inspectorMode) { toast('请先在预览中选择一个元素', 'error'); return; } const el = activeEl; if (!el) return; el.style[prop] = value; const valEls = { margin: $('devMarginVal'), padding: $('devPaddingVal'), borderRadius: $('devRadiusVal'), opacity: $('devOpacityVal'), fontSize: $('devFontSizeVal'), lineHeight: $('devLineHeightVal'), letterSpacing: $('devLetterSpacingVal'), fontWeight: $('devFontWeightVal') }; if (valEls[prop]) { let displayVal = value.replace('px', '').replace('%', ''); if (prop === 'opacity') displayVal = parseFloat(value).toFixed(1); if (prop === 'lineHeight') displayVal = parseFloat(value).toFixed(1); valEls[prop].textContent = displayVal; } setTimeout(() => syncPreviewToCode(), 30); } function applyBorderWidth(val) { if (!activeEl) return; activeEl.style.borderWidth = val + 'px'; $('devBorderWidthVal').textContent = val; setTimeout(() => syncPreviewToCode(), 30); } function applyBorderStyle(val) { if (!activeEl) return; activeEl.style.borderStyle = val; setTimeout(() => syncPreviewToCode(), 30); } function applyBorderColor(val) { if (!activeEl) return; activeEl.style.borderColor = val; $('devBorderColor').value = val; $('devBorderColorPick').value = val; setTimeout(() => syncPreviewToCode(), 30); } function applyOutlineWidth(val) { if (!activeEl) return; activeEl.style.outlineWidth = val + 'px'; $('devOutlineWidthVal').textContent = val; setTimeout(() => syncPreviewToCode(), 30); } function applyOutlineStyle(val) { if (!activeEl) return; activeEl.style.outlineStyle = val; setTimeout(() => syncPreviewToCode(), 30); } function updateTransform() { if (!activeEl) return; const rotate = parseFloat($('devRotate').value) || 0; const scale = parseFloat($('devScale').value) / 100 || 1; const skew = parseFloat($('devSkew').value) || 0; let transform = ''; if (rotate !== 0) transform += `rotate(${rotate}deg) `; if (scale !== 1) transform += `scale(${scale.toFixed(2)}) `; if (skew !== 0) transform += `skew(${skew}deg) `; activeEl.style.transform = transform.trim(); $('devRotateVal').textContent = rotate + '°'; $('devScaleVal').textContent = scale.toFixed(1); $('devSkewVal').textContent = skew + '°'; setTimeout(() => syncPreviewToCode(), 30); } function resetTransform() { if (!activeEl) return; activeEl.style.transform = ''; $('devRotate').value = 0; $('devRotateVal').textContent = '0°'; $('devScale').value = 100; $('devScaleVal').textContent = '1.0'; $('devSkew').value = 0; $('devSkewVal').textContent = '0°'; setTimeout(() => syncPreviewToCode(), 30); toast('变换已重置'); } function applyTransitionDur(val) { if (!activeEl) return; const dur = parseFloat(val) / 10; activeEl.style.transitionDuration = dur + 's'; $('devTransitionDurVal').textContent = dur + 's'; setTimeout(() => syncPreviewToCode(), 30); } function applyTransitionEase(val) { if (!activeEl) return; activeEl.style.transitionTimingFunction = val; setTimeout(() => syncPreviewToCode(), 30); } function updateFilter() { if (!activeEl) return; const blur = parseFloat($('devFilterBlur').value) || 0; const bright = parseFloat($('devFilterBrightness').value) || 100; const contrast = parseFloat($('devFilterContrast').value) || 100; const gray = parseFloat($('devFilterGrayscale').value) || 0; const hue = parseFloat($('devFilterHueRotate').value) || 0; let filter = ''; if (blur > 0) filter += `blur(${blur}px) `; if (bright !== 100) filter += `brightness(${bright}%) `; if (contrast !== 100) filter += `contrast(${contrast}%) `; if (gray > 0) filter += `grayscale(${gray}%) `; if (hue > 0) filter += `hue-rotate(${hue}deg) `; activeEl.style.filter = filter.trim(); $('devFilterBlurVal').textContent = blur + 'px'; $('devFilterBrightnessVal').textContent = bright + '%'; $('devFilterContrastVal').textContent = contrast + '%'; $('devFilterGrayscaleVal').textContent = gray + '%'; $('devFilterHueRotateVal').textContent = hue + 'deg'; setTimeout(() => syncPreviewToCode(), 30); } function resetFilter() { if (!activeEl) return; activeEl.style.filter = ''; $('devFilterBlur').value = 0; $('devFilterBlurVal').textContent = '0px'; $('devFilterBrightness').value = 100; $('devFilterBrightnessVal').textContent = '100%'; $('devFilterContrast').value = 100; $('devFilterContrastVal').textContent = '100%'; $('devFilterGrayscale').value = 0; $('devFilterGrayscaleVal').textContent = '0%'; $('devFilterHueRotate').value = 0; $('devFilterHueRotateVal').textContent = '0deg'; setTimeout(() => syncPreviewToCode(), 30); toast('滤镜已重置'); } function replaceText() { if (!activeEl) { toast('请先选择一个元素', 'error'); return; } const currentText = activeEl.textContent || ''; const newText = prompt('请输入新的文本内容:', currentText); if (newText !== null) { activeEl.textContent = newText; setTimeout(() => syncPreviewToCode(), 30); toast('文字已更新'); } } function replaceImage() { if (!activeEl) { toast('请先选择一个元素', 'error'); return; } if (activeEl.tagName.toLowerCase() !== 'img') { toast('选中的元素不是图片 ()', 'error'); return; } const currentSrc = activeEl.getAttribute('src') || ''; const newSrc = prompt('请输入新的图片链接 (URL):', currentSrc); if (newSrc !== null) { activeEl.setAttribute('src', newSrc); setTimeout(() => syncPreviewToCode(), 30); toast('图片链接已更新'); } } function toggleInspector() { if (!editMode) toggleEdit(); inspectorMode = !inspectorMode; const btn = $('inspectorBtn'); if (inspectorMode) { btn.textContent = '🔎 检查中...'; btn.style.background = 'var(--w)'; btn.style.color = '#fff'; if (editDoc && editDoc.body) { editDoc.body.style.cursor = 'pointer'; // 重新绑定 mouseover/mouseout 事件 if (editDoc.body._mouseoverHandler) { editDoc.body.removeEventListener('mouseover', editDoc.body._mouseoverHandler, true); } if (editDoc.body._mouseoutHandler) { editDoc.body.removeEventListener('mouseout', editDoc.body._mouseoutHandler, true); } editDoc.body._mouseoverHandler = onInspectorHover; editDoc.body._mouseoutHandler = onInspectorOut; editDoc.body.addEventListener('mouseover', editDoc.body._mouseoverHandler, true); editDoc.body.addEventListener('mouseout', editDoc.body._mouseoutHandler, true); } toast('悬停元素查看,点击选择'); } else { btn.textContent = '🔎 启用检查'; btn.style.background = ''; btn.style.color = ''; if (editDoc && editDoc.body) { editDoc.body.style.cursor = editMode ? 'crosshair' : ''; if (editDoc.body._mouseoverHandler) { editDoc.body.removeEventListener('mouseover', editDoc.body._mouseoverHandler, true); delete editDoc.body._mouseoverHandler; } if (editDoc.body._mouseoutHandler) { editDoc.body.removeEventListener('mouseout', editDoc.body._mouseoutHandler, true); delete editDoc.body._mouseoutHandler; } } if (inspectorHover) inspectorHover.classList.remove('dom-highlight'); } } function onInspectorHover(e) { if (!inspectorMode) return; if (inspectorHover) inspectorHover.classList.remove( 'dom-highlight'); const el = e.target; if (el && el !== editDoc?.body && el !== editDoc?.documentElement) { el.classList.add('dom-highlight'); inspectorHover = el; } } function onInspectorOut(e) { if (e.target.classList) e.target.classList.remove('dom-highlight'); } function handleInspectorClick(e) { e.preventDefault(); e.stopPropagation(); const el = e.target; if (el && el !== editDoc?.body && el !== editDoc?.documentElement) { select(el); toggleInspector(); } } function showElementTree() { if (!editDoc) { toast('请先切换到预览模式', 'error'); return; } const tree = buildElementTree(editDoc.body, 0); console.log('🌳 元素树:\n' + tree); toast('元素树已输出到控制台 (F12)'); } function buildElementTree(el, depth) { let tree = ' '.repeat(depth) + '<' + el.tagName.toLowerCase(); if (el.id) tree += ' id="' + el.id + '"'; if (el.className && typeof el.className === 'string') tree += ' class="' + el.className + '"'; tree += '>'; if (el.children.length === 0 && el.textContent.trim().length < 50) { tree += ' "' + el.textContent.trim() .substring(0, 40) + '"'; } tree += '\n'; for (const child of el.children) { tree += buildElementTree(child, depth + 1); } return tree; } function toggleTextBold() { if (!activeEl) return toast('请先选择元素', 'error'); activeEl.style.fontWeight = getComputedStyle(activeEl).fontWeight === '700' ? '400' : '700'; setTimeout(() => syncPreviewToCode(), 30); } function toggleTextItalic() { if (!activeEl) return toast('请先选择元素', 'error'); activeEl.style.fontStyle = getComputedStyle(activeEl).fontStyle === 'italic' ? 'normal' : 'italic'; setTimeout(() => syncPreviewToCode(), 30); } function toggleTextUnderline() { if (!activeEl) return toast('请先选择元素', 'error'); activeEl.style.textDecoration = getComputedStyle(activeEl).textDecoration.includes('underline') ? 'none' : 'underline'; setTimeout(() => syncPreviewToCode(), 30); } function addBoxShadow() { if (!activeEl) return toast('请先选择元素', 'error'); activeEl.style.boxShadow = '0 4px 16px rgba(0,0,0,0.15)'; setTimeout(() => syncPreviewToCode(), 30); toast('已添加阴影'); } function addGradient() { if (!activeEl) return toast('请先选择元素', 'error'); activeEl.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; activeEl.style.color = '#fff'; setTimeout(() => syncPreviewToCode(), 30); toast('已添加渐变背景'); } function duplicateElement() { if (!activeEl) return toast('请先选择元素', 'error'); const clone = activeEl.cloneNode( true); activeEl.parentNode.insertBefore(clone, activeEl.nextSibling); select(clone); setTimeout(() => syncPreviewToCode(), 50); toast('元素已复制'); } function deleteSelectedElement() { if (!activeEl) return toast('请先选择元素', 'error'); if (confirm('确认删除此元素?')) { const el = activeEl; deselect(); el.remove(); setTimeout(() => syncPreviewToCode(), 50); toast('已删除'); } } function wrapWithDiv() { if (!activeEl) return toast('请先选择元素', 'error'); const wrapper = editDoc.createElement( 'div'); wrapper.style.padding = '8px'; wrapper.style.border = '1px dashed #ccc'; activeEl.parentNode.insertBefore(wrapper, activeEl); wrapper.appendChild(activeEl); select(wrapper); setTimeout(() => syncPreviewToCode(), 50); toast('已包裹Div'); } function extractToComponent() { if (!activeEl) return toast('请先选择元素', 'error'); const html = activeEl.outerHTML; navigator.clipboard.writeText(html).then(() => { toast('组件HTML已复制到剪贴板 📋'); }); } function addAnimation() { if (!activeEl) return toast('请先选择元素', 'error'); const animName = 'anim_' + Date .now(); const style = editDoc.createElement('style'); style.textContent = `@keyframes ${animName}{0%{transform:scale(1)}50%{transform:scale(1.05)}100%{transform:scale(1)}}`; editDoc.head.appendChild(style); activeEl.style.animation = `${animName} 2s ease-in-out infinite`; setTimeout(() => syncPreviewToCode(), 50); toast('已添加动画效果 ✨'); } function addHoverEffect() { if (!activeEl) return toast('请先选择元素', 'error'); const id = activeEl.id || ('el_' + Date.now()); if (!activeEl.id) activeEl.id = id; const style = editDoc.createElement('style'); style.textContent = `#${id}:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(0,0,0,0.15);}`; editDoc.head.appendChild(style); setTimeout(() => syncPreviewToCode(), 50); toast('已添加悬停效果 🖱️'); } // ============================================================ // 格式化/压缩 (保持不变) // ============================================================ function formatHTML() { ensureCodeView(); let html = code.value; let formatted = '', indent = 0; html = html.replace(/>\s*<'); const tags = html.split(/(<[^>]*>)/g).filter(Boolean); for (const part of tags) { if (part.startsWith('') && ![ 'br', 'hr', 'img', 'input', 'meta', 'link' ].includes(part.match(/^<(\w+)/)?.[1] || '')) indent++; } const newVal = formatted.trim(); if (newVal !== code.value) { code.value = newVal; lastHTML = newVal; localStorage.setItem('html_editor_content', newVal); } refresh(); updateSegIndicator(); toast('代码已格式化 ✨', 'success'); } function minifyHTML() { ensureCodeView(); const newVal = code.value.replace(//g, '').replace(/>\s+<').replace(/\s{2,}/g, ' ').replace( /^\s+|\s+$/gm, ''); if (newVal !== code.value) { code.value = newVal; lastHTML = newVal; localStorage.setItem('html_editor_content', newVal); } refresh(); updateSegIndicator(); toast('代码已压缩 📦'); } function newDoc() { ensureCodeView(); if (code.value.trim() && !confirm('新建会清空当前内容,确定?')) return; const newVal = getDefault(); code.value = newVal; lastHTML = newVal; localStorage.setItem('html_editor_content', newVal); refresh(); updateSegIndicator(); toast('新建文档'); } function loadFile(inp) { ensureCodeView(); const f = inp.files[0]; if (!f) return; const ext = f.name.split('.').pop().toLowerCase(); if (ext !== 'html' && ext !== 'htm') { toast('仅支持 .html 文件', 'error'); inp.value = ''; return; } const r = new FileReader(); r.onload = function(e) { const newVal = e.target.result; code.value = newVal; lastHTML = newVal; localStorage.setItem('html_editor_content', newVal); refresh(); updateSegIndicator(); toast('文件已加载 📂'); }; r.onerror = function() { toast('读取失败', 'error') }; r.readAsText(f); } function saveFile() { try { const content = code.value; const blob = new Blob([content], { type: 'text/html;charset=utf-8' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'index.html'; link.style.display = 'none'; document.body.appendChild(link); link.click(); setTimeout(() => { document.body.removeChild(link); URL.revokeObjectURL(url); }, 100); toast('已保存 💾', 'success'); } catch (e) { try { if (navigator.share) { const file = new File([code.value], 'index.html', { type: 'text/html' }); navigator.share({ title: 'HTML 文件', files: [file] }).then(() => { toast('已分享/保存 📤', 'success'); }).catch(() => { toast('保存失败,请尝试手动复制', 'error'); }); } else { toast('保存失败,请尝试其他方式', 'error'); } } catch (e2) { toast('保存失败: ' + e2.message, 'error'); } } } function rgb2hex(rgb) { if (!rgb || rgb === 'transparent' || rgb === 'rgba(0,0,0,0)') return '#ffffff'; const m = rgb.match(/[\d.]+/g); if (!m || m.length < 3) return '#ffffff'; return '#' + [parseInt(m[0]), parseInt(m[1]), parseInt(m[2])].map(x => x.toString(16).padStart(2, '0')).join( ''); } function bindUI() { eFS.addEventListener('input', applyStyle); eColor.addEventListener('input', applyStyle); eBg.addEventListener('input', applyStyle); eX.addEventListener('input', applyPos); eY.addEventListener('input', applyPos); eW.addEventListener('input', applyPos); eH.addEventListener('input', applyPos); } // ============================================================ // 初始化 // ============================================================ function getDefault() { return ` 文档

🍋 HTML DevTools 编辑器

点击 🔧 工具 打开元素修改面板。

支持可视化编辑、CSS调整、查找替换。

卡片 1

使用工具面板修改此元素

可编辑

卡片 2

拖拽调整位置和大小

`; } function init() { code.value = getDefault(); lastHTML = code.value; _originalHTML = code.value; refresh(); updateSegIndicator(); bindUI(); // 监听frame加载完成 frame.addEventListener('load', function() { setupDoc(); if (editMode) { bindEditEvents(); } }); try { if (frame.contentDocument && frame.contentDocument.readyState === 'complete') setupDoc(); } catch (e) {} updateNavButtons(); updateFindBadge(); $('findCount').textContent = '0/0'; switchTab('code'); initPreviewMode(); const panel = $('sourceToolsPanel'); if (panel.classList.contains('open')) panel.classList.remove('open'); } init();