โ˜ฐ
๐Ÿ› 
DevToolsDesk
Markdown & HTML Editor

Markdown Editor & Live HTML/CSS Preview

Write Markdown or full HTML documents with CSS and see a live preview. Includes autosave, exports, templates, and local helper tools for cleaning and converting content.

Start typing in the editor โ€” the preview updates automatically. Toggle between Auto / Markdown / Raw HTML when needed.

Editor & Live Preview

Markdown / HTML Input
Preview (Rendered HTML + CSS + JS)
AI Tools (Local)
Frameworks in preview:
Rendering Mode:
โ€ข Live preview runs in a sandboxed iframe (HTML + CSS + JS). โ€ข Press Ctrl + Space to trigger tag suggestions (e.g. type <ht โ†’ suggestion <html>). โ€ข Use Raw HTML mode when pasting full documents with <style> / <script>. โ€ข Drag the middle handle to resize editor and preview panes. โ€ข Open the Templates panel to quickly insert starter layouts. โ€ข Use the console below the preview to see console.log output and errors.
`; function isLikelyHtml(src){ return /|]|]|]|]|]|]/i.test(src.trim()); } function setRenderMode(mode){ renderMode = mode; if (mdEditor) { renderMarkdown(mdEditor.getValue()); } } function renderMarkdown(src){ clearTimeout(renderTimer); renderTimer = setTimeout(() => { const frame = document.getElementById("mdPreviewFrame"); if (!frame) return; const treatAsHtml = renderMode === "html" || (renderMode === "auto" && isLikelyHtml(src)); let htmlOutput; if (treatAsHtml) { htmlOutput = src || ""; } else { const bodyHtml = (window.marked && typeof window.marked.parse === "function") ? window.marked.parse(src || "") : (src || ""); const styles = getComputedStyle(document.body); const bg = styles.getPropertyValue("--bg") || "#f4f5f9"; const text = styles.getPropertyValue("--text") || "#1e293b"; htmlOutput = ` ${buildFrameworkLinks()}
${bodyHtml} `; } if (!treatAsHtml) { frame.srcdoc = htmlOutput; } else { // for full HTML docs, user controls head/body; still sandboxed frame.srcdoc = src || ""; } setTimeout(() => executeScripts(frame), 120); }, 200); // smart debounce } function buildFrameworkLinks() { let libs = ""; if (enabledLibraries.bootstrap) { libs += ` `; } if (enabledLibraries.tailwind) { libs += ` `; } return libs; } function executeScripts(frame) { const doc = frame.contentDocument; if (!doc) return; const consolePanel = document.getElementById("consoleOutput"); if (consolePanel) { consolePanel.innerHTML = ""; } const win = frame.contentWindow; if (!win) return; // hook console if (consolePanel) { const originalConsole = win.console || {}; const methods = ["log","info","warn","error"]; win.console = Object.assign({}, originalConsole); methods.forEach(method => { win.console[method] = (...args) => { const line = document.createElement("div"); line.textContent = `[${method}] ` + args.map(String).join(" "); if (method === "error") line.style.color = "#fecaca"; else if (method === "warn") line.style.color = "#fde68a"; consolePanel.appendChild(line); consolePanel.scrollTop = consolePanel.scrollHeight; if (typeof originalConsole[method] === "function") { originalConsole[method].apply(originalConsole, args); } }; }); } // error overlay win.onerror = function(msg, url, line, col, err) { showErrorOverlay(frame, msg + " (" + line + ":" + col + ")"); return false; }; const scripts = Array.from(doc.querySelectorAll("script")); scripts.forEach(oldScript => { const newScript = doc.createElement("script"); for (const attr of oldScript.attributes) { newScript.setAttribute(attr.name, attr.value); } newScript.textContent = oldScript.textContent; oldScript.parentNode.replaceChild(newScript, oldScript); }); } function showErrorOverlay(frame, message) { try { const doc = frame.contentDocument; if (!doc) return; const overlay = doc.getElementById("errorOverlay"); if (!overlay) return; overlay.textContent = message; overlay.style.display = "block"; setTimeout(() => { overlay.style.display = "none"; }, 5000); } catch(e){} } function initEditor(){ const textarea = document.getElementById("mdInput"); if (!textarea || !window.CodeMirror) return; const saved = localStorage.getItem("dt_markdown_editor_content"); textarea.value = saved || initialSample; mdEditor = CodeMirror.fromTextArea(textarea, { mode: "markdown", lineNumbers: true, lineWrapping: true, extraKeys: { "Ctrl-Space": "autocomplete" } }); mdEditor.on("change", function(cm){ const value = cm.getValue(); localStorage.setItem("dt_markdown_editor_content", value); renderMarkdown(value); }); mdEditor.on("inputRead", function(cm, change){ if (!change.text || !change.text.length) return; const ch = change.text.join(""); if (!ch) return; if (/[a-zA-Z<]/.test(ch) && CodeMirror.hint && CodeMirror.hint.markdownHtmlTags) { cm.showHint({ hint: CodeMirror.hint.markdownHtmlTags, completeSingle: false }); } }); function htmlTagCompletions(cm){ const cur = cm.getCursor(); const token = cm.getTokenAt(cur); const start = token.start; const end = cur.ch; const word = token.string.slice(0, end - start); if (!word || word[0] !== "<") return; const tags = [ "", "", "", "
", "", "
", "