首页 › 在线工具 › Markdown 预览器TOOLBOX · 在线工具Markdown 预览器左侧输入 Markdown,右侧实时预览,支持复制 HTML。✦ 文本工具 ● 本地处理 ◇ 无需 AI ○ 免费 ✦ 工具说明 左边写 Markdown,右边实时看到最终效果。下方有字数 / Token 估算,方便你把内容切到合适长度。使用方式:直接写。全部在浏览器本地运行,不会上传任何数据。Markdown 输入链接 inline code 和代码块 def hello(): print(\"Hello, world!\") ```"></textarea> </div> <div> <div class="counter-controls"> <label class="counter-label">实时预览</label> </div> <div id="md-output" class="md-output"></div> </div> </div> <div class="check-list" style="margin-top:16px;padding:14px;border:1px dashed rgba(148,197,255,0.3);border-radius:12px;"> <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;"> <div><strong style="color:#22d3ee;">字符数:</strong><span id="stat-char">0</span></div> <div><strong style="color:#22d3ee;">中文字数:</strong><span id="stat-cn">0</span></div> <div><strong style="color:#22d3ee;">英文单词数:</strong><span id="stat-en">0</span></div> <div><strong style="color:#22d3ee;">估算 Tokens(GPT 风格):</strong><span id="stat-token">0</span></div> </div> </div> <div style="margin-top:10px;"> <button type="button" class="btn btn-ghost" id="md-copy">复制 Markdown</button> <button type="button" class="btn btn-ghost" id="md-copy-html">复制 HTML</button> <button type="button" class="btn btn-ghost" id="md-clear">清空</button> </div> </div> <script> (function () { var input = document.getElementById("md-input"); var output = document.getElementById("md-output"); function escapeHtml(s) { return s.replace(/[&<>"']/g, function (c) { return ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]; }); } // 一个非常轻量的 Markdown 渲染器(仅用于在线预览) function renderMarkdown(text) { if (!text) return "<p style=\"color:#94a3b8;font-style:italic;\">在左侧输入一些内容……</p>"; var lines = text.split("\n"); var html = []; var i = 0; var inCode = false; var codeLang = ""; var codeBuf = []; function flushParagraph(buf) { if (buf.length) { html.push("<p>" + inline(buf.join(" ")) + "</p>"); } buf.length = 0; } function inline(s) { // 粗体 s = s.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"); // 斜体 s = s.replace(/\*([^\*]+?)\*/g, "<em>$1</em>"); // 行内代码 s = s.replace(/`([^`]+)`/g, "<code>$1</code>"); // 链接 s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener nofollow\">$1</a>"); return s; } var paragraphBuf = []; while (i < lines.length) { var line = lines[i]; // 代码块开始 if (line.trim().startsWith("```") && !inCode) { flushParagraph(paragraphBuf); inCode = true; codeLang = line.trim().slice(3).trim(); codeBuf = []; i++; continue; } // 代码块结束 if (line.trim() === "```" && inCode) { inCode = false; var langAttr = codeLang ? (" class=\"language-" + codeLang + "\"") : ""; html.push("<pre><code" + langAttr + ">" + escapeHtml(codeBuf.join("\n")) + "</code></pre>"); codeLang = ""; codeBuf = []; i++; continue; } // 代码块内 if (inCode) { codeBuf.push(line); i++; continue; } // 标题 if (line.match(/^#{1,6}\s/)) { flushParagraph(paragraphBuf); var level = line.match(/^(#+)\s/)[1].length; html.push("<h" + level + ">" + inline(line.replace(/^#+\s/, "")) + "</h" + level + ">"); i++; continue; } // 水平分割线 if (line.trim().match(/^---+$/) || line.trim().match(/^\*\*\*+$/)) { flushParagraph(paragraphBuf); html.push("<hr/>"); i++; continue; } // 无序列表 if (line.match(/^\s*[-*]\s/)) { flushParagraph(paragraphBuf); var list = []; while (i < lines.length && lines[i].match(/^\s*[-*]\s/)) { list.push("<li>" + inline(lines[i].replace(/^\s*[-*]\s/, "")) + "</li>"); i++; } html.push("<ul>" + list.join("") + "</ul>"); continue; } // 有序列表 if (line.match(/^\s*\d+\.\s/)) { flushParagraph(paragraphBuf); var list2 = []; while (i < lines.length && lines[i].match(/^\s*\d+\.\s/)) { list2.push("<li>" + inline(lines[i].replace(/^\s*\d+\.\s/, "")) + "</li>"); i++; } html.push("<ol>" + list2.join("") + "</ol>"); continue; } // 空行 if (line.trim() === "") { flushParagraph(paragraphBuf); i++; continue; } // 默认:段落 paragraphBuf.push(line); i++; } flushParagraph(paragraphBuf); if (inCode) { html.push("<pre><code>" + escapeHtml(codeBuf.join("\n")) + "</code></pre>"); } return html.join("\n"); } function estimateTokens(text) { if (!text) return 0; // 粗略估算:中文每个字约 1.5 tokens,英文每个词约 1 token var cn = (text.match(/[\u4e00-\u9fa5]/g) || []).length; var en = (text.match(/[a-zA-Z]+/g) || []).length; var other = Math.max(0, (text.length - cn - en) / 4); return Math.round(cn * 1.5 + en + other); } function updateStats(text) { document.getElementById("stat-char").textContent = text.length; var cnCount = (text.match(/[\u4e00-\u9fa5]/g) || []).length; document.getElementById("stat-cn").textContent = cnCount; var enWords = (text.match(/[a-zA-Z]+/g) || []).length; document.getElementById("stat-en").textContent = enWords; document.getElementById("stat-token").textContent = estimateTokens(text); } function handleInput() { var text = input.value; output.innerHTML = renderMarkdown(text); updateStats(text); } input.addEventListener("input", handleInput); // 按钮 document.getElementById("md-copy").addEventListener("click", function () { navigator.clipboard.writeText(input.value); }); document.getElementById("md-copy-html").addEventListener("click", function () { navigator.clipboard.writeText(output.innerHTML); }); document.getElementById("md-clear").addEventListener("click", function () { input.value = ""; handleInput(); }); // 首次渲染 handleInput(); })(); </script> <style> .md-output { background: rgba(15,23,42,0.45); border: 1px solid rgba(148,197,255,0.15); border-radius: 10px; padding: 20px; min-height: 320px; overflow-y: auto; line-height: 1.7; color: #cbd5e1; } .md-output h1, .md-output h2, .md-output h3 { color: #e2f7ff; font-weight: 700; margin: 0 0 10px 0; } .md-output h1 { font-size: 1.6rem; margin-top: 16px; } .md-output h2 { font-size: 1.3rem; margin-top: 14px; } .md-output h3 { font-size: 1.1rem; margin-top: 12px; } .md-output p { margin: 0 0 12px 0; } .md-output ul, .md-output ol { margin: 0 0 12px 20px; padding: 0; } .md-output li { margin-bottom: 4px; } .md-output code { background: rgba(15,23,42,0.8); padding: 2px 6px; border-radius: 4px; font-family: Consolas, monospace; font-size: 0.9em; color: #67e8f9; } .md-output pre { background: rgba(15,23,42,0.8); padding: 14px; border-radius: 8px; overflow-x: auto; border: 1px solid rgba(148,197,255,0.15); } .md-output pre code { background: transparent; padding: 0; color: #e2f7ff; } .md-output hr { border: none; border-top: 1px solid rgba(148,197,255,0.2); margin: 16px 0; } .md-output a { color: #22d3ee; } </style> 同分类工具 📝 字数统计器 🧹 文本清理器 🔤 批量替换器 🔠 大小写转换器 🌙