markdown-it 渲染出来的代码块如下面左图一样,光秃秃的一块,也没有任何交互功能,现在我们来美化一下,添加一个 copy 代码的按钮,将代码块的语言也一并显示出来。


开发插件
要实现上面的功能,光用 CSS 来做是不够的,我们需要借助 markdown-it 的插件机制,用插件实现上面的功能。
- 我们的插件用 TypeScript 开发,所以先安装 markdown-it 的类型文件,获取类型说明
1
| pnpm add @types/markdown-it -D
|
- 添加一个插件模板
1 2 3 4 5 6
| import type MarkdownIt from "markdown-it";
export default (md: MarkdownIt) => { };
|
- 实现功能(由于插件比较简单,这里不多做解释,直接看注释就行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| import type MarkdownIt from "markdown-it"; import type Renderer from "markdown-it/lib/renderer";
import ClipboardJS from "clipboard"; import { escape } from "lodash-es";
const clipboard = new ClipboardJS(".markdown-it-code-copy");
const copyInnerHTML = ` <svg aria-hidden="true" focusable="false" role="img" class="octicon octicon-copy" viewBox="0 0 16 16" width="12" height="12" fill="currentColor" style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg> <span>Copy</span> `;
const copiedInnerHTML = ` <svg aria-hidden="true" focusable="false" role="img" class="octicon octicon-check" viewBox="0 0 16 16" width="12" height="12" fill="currentColor" style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg> <span>Copied!</span> `;
clipboard.on("success", (e) => { const trigger = e.trigger; e.clearSelection();
trigger.innerHTML = copiedInnerHTML; setTimeout(() => { trigger.innerHTML = copyInnerHTML; }, 3000); });
const getCodeLangFragment = (htmlString: string) => { const regex = /<code class="hljs (language-([a-z]+))">/; const match = htmlString.match(regex); return match?.[2] || ""; };
const renderCode = (renderer: Renderer.RenderRule): Renderer.RenderRule => { return (...args) => { const [tokens, idx] = args; const content = escape(tokens[idx].content); const origRendered = renderer.apply(this, args);
if (content.length === 0) return origRendered;
const lang = getCodeLangFragment(origRendered);
return ` <div class="code-enhance"> <div class="code-enhance-header"> <span>${lang}</span> <span class="markdown-it-code-copy code-enhance-copy" data-clipboard-text="${content}"> ${copyInnerHTML} </span> </div> <div class="code-enhance-content"> ${origRendered} </div> </div> `; }; };
export default (md: MarkdownIt) => { if (md.renderer.rules.code_block != null) { md.renderer.rules.code_block = renderCode(md.renderer.rules.code_block); }
if (md.renderer.rules.fence != null) { md.renderer.rules.fence = renderCode(md.renderer.rules.fence); } };
|
添加样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| .code-enhance { display: flex; flex-direction: column; border-radius: 7px; overflow: hidden; margin-top: 10px;
&-header { background-color: #e2e6ea; height: 32px; box-sizing: border-box; padding: 0 16px; font-size: 12px; display: flex; justify-content: space-between; align-items: center; gap: 8px; }
&-content { background-color: #fff; padding: 1em;
pre { margin: 0; }
code { padding: 0 !important; } }
&-copy { font-size: 12px; display: inline-flex; align-items: center; cursor: pointer; gap: 6px; line-height: 1.5; } }
|
安装插件
1 2 3 4 5
| import markdownIt from "markdown-it";
import markdownItCodeCopy from "./markdown-it-code-copy";
const md = markdownIt().use(markdownItCodeCopy);
|