现在大部分跟代码有关的编辑器和网站基本都是支持 markdown 语法的,我比较喜欢的 markdown 内容显示的网站是 vitepress 的网站,vitepress 支持的语法比较全,而且有很多个性化的渲染语法,是原生的 markdown 不具备的。
本文分享一下利用 markdown 自带的拓展类进行自定义渲染语法。
自定义行渲染规则 {#自定义行渲染规则}
行渲染规则就是指单行内容的渲染,一般是比较短的渲染,比如最常见的加粗语法: **加粗**
就是行渲染规则。
而我发现 Python的 markdown 库居然不支持"删除文本"的规则,当然也可能有什么参数我没有开启所以才没支持。不过没关系,我可以自定义这个规则。
可以看一下官方给的自定义的代码:https://python-markdown.github.io/extensions/api/#example_3
官方文档这里的例子就是在实现一个"删除文本"的规则,可以直接拿来用:
from markdown.inlinepatterns import InlineProcessor
from markdown.extensions import Extension
import xml.etree.ElementTree as etree
class DelInlineProcessor(InlineProcessor):
def handleMatch(self, m, data):
el = etree.Element('del')
el.text = m.group(1)
return el, m.start(0), m.end(0)
class` `DelExtension(Extension):`
`def` `extendMarkdown(self,` `md):`
`DEL_PATTERN` `=` `r'--(.*?)--'` `# like --del--`
`md.inlinePatterns.register(DelInlineProcessor(DEL_PATTERN,` `md),` `'del',` `175)`
`
文档里面说了,这个 InlineProcessor
类的第一个调用函数 handleMatch
的参数有两个,第一个参数 m
就是利用正则匹配到的内容,如正则 r'--(.*?)--'
匹配的内容。
将匹配的内容放到自定义的标签里面的方式是使用 etree.Element('del')
,这个就可以给匹配内容添加标签,实现一个简单的行内容渲染规则。
然后使用自定义的规则:
from utils.markdown_ext import (
DelExtension,
IconExtension
)
def` `make_markdown():`
`md` `=` `markdown.Markdown(extensions=[`
`'markdown.extensions.extra',`
`'markdown_checklist.extension',`
`CodeHiliteExtension(pygments_formatter=CustomHtmlFormatter),`
`TocExtension(slugify=slugify),`
`DelExtension(),` `# 自定义规则`
`])`
`return` `md`
`
删除文本规则 {#删除文本规则}
这是我自定义的删除文本规则(在例子里面稍微改了一下):
# utils/markdown_ext.py
class DelInlineProcessor(InlineProcessor):
def handleMatch(self, m, data):
el = etree.Element('del')
el.text = m.group(1)
return el, m.start(0), m.end(0)
class DelExtension(Extension):
"""
删除文本
匹配:~~删除~~
输出:<del>删除</del>
"""
<span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
<span class="n">DEL_PATTERN</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'~~(.*?)~~'</span> <span class="c1"># like ~~del~~</span>
<span class="n">md</span><span class="o">.</span><span class="n">inlinePatterns</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">DelInlineProcessor</span><span class="p">(</span><span class="n">DEL_PATTERN</span><span class="p">,</span> <span class="n">md</span><span class="p">),</span> <span class="s1">'del'</span><span class="p">,</span> <span class="mi">175</span><span class="p">)</span>
图标规则 {#图标规则}
为了方便在文本里面使用图标,我按照删除文本的规则定义了一个渲染图标的规则:
class IconInlineProcessor(InlineProcessor):
def handleMatch(self, m, data):
text = m.group(1)
el = etree.Element('i')
el.set('class', 'fa fa-{}'.format(text.replace('icon:', '')))
return el, m.start(0), m.end(0)
class IconExtension(Extension):
"""
渲染图标
匹配:icon:exclamation-triangle
输出:<i class="fa fa-exclamation-triangle"></i>
"""
<span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
<span class="n">DEL_PATTERN</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'(icon:[a-z-]+)'</span>
<span class="n">md</span><span class="o">.</span><span class="n">inlinePatterns</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">IconInlineProcessor</span><span class="p">(</span><span class="n">DEL_PATTERN</span><span class="p">,</span> <span class="n">md</span><span class="p">),</span> <span class="s1">'icon'</span><span class="p">,</span> <span class="mi">180</span><span class="p">)</span>
这个规则很简单,比如输入icon:home
就可以被渲染成 <i class="fa fa-home"></i>
这种图标的标签。
自定义块渲染规则 {#自定义块渲染规则}
块渲染规则是指将一个段落渲染成 html 的段落,比如代码块就是块渲染。
我这里想要实现一个块渲染效果,最后可以生成 bootstrap 里面的 alert 效果,这里可以看一下目标的效果 html 的代码:
<div class="alert alert-primary" role="alert">
A simple primary alert---check it out!
</div>
<div class="alert alert-secondary" role="alert">
A simple secondary alert---check it out!
</div>
<div class="alert alert-success" role="alert">
A simple success alert---check it out!
</div>
<div class="alert alert-danger" role="alert">
A simple danger alert---check it out!
</div>
<div class="alert alert-warning" role="alert">
A simple warning alert---check it out!
</div>
<div class="alert alert-info" role="alert">
A simple info alert---check it out!
</div>
<div class="alert alert-light" role="alert">
A simple light alert---check it out!
</div>
<div class="alert alert-dark" role="alert">
A simple dark alert---check it out!
</div>
这些都是 bootstrap 里面的效果,其实就是在 div 标签里加了一个 class 然后添加了一个固定的 role="alert"
。
我希望输入的规则是这样的:
:::info
文本内容
:::
也就是这个块需要使用 :::
开头和结尾,并且开头里面必须填写一个级别。
官方示例 {#官方示例}
先看一下官方的实例:
def test_block_processor():
class BoxBlockProcessor(BlockProcessor):
RE_FENCE_START = r'^ *!{3,} *\n' # start line, e.g., ` !!!! `
RE_FENCE_END = r'\n *!{3,}\s*$' # last non-blank line, e.g, '!!!\n \n\n'
<span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
<span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">blocks</span><span class="p">):</span>
<span class="n">original_block</span> <span class="o">=</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># Find block with ending fence</span>
<span class="k">for</span> <span class="n">block_num</span><span class="p">,</span> <span class="n">block</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">blocks</span><span class="p">):</span>
<span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
<span class="c1"># remove fence</span>
<span class="n">blocks</span><span class="p">[</span><span class="n">block_num</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
<span class="c1"># render fenced area inside a new div</span>
<span class="n">e</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">SubElement</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s1">'div'</span><span class="p">)</span>
<span class="n">e</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'style'</span><span class="p">,</span> <span class="s1">'display: inline-block; border: 1px solid red;'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">parseBlocks</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
<span class="c1"># remove used blocks</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">blocks</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">True</span> <span class="c1"># or could have had no return statement</span>
<span class="c1"># No closing marker! Restore and do nothing</span>
<span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">original_block</span>
<span class="k">return</span> <span class="kc">False</span> <span class="c1"># equivalent to our test() routine returning False</span>
<span class="k">class</span> <span class="nc">BoxExtension</span><span class="p">(</span><span class="n">Extension</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
<span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">blockprocessors</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">BoxBlockProcessor</span><span class="p">(</span><span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="p">),</span> <span class="s1">'box'</span><span class="p">,</span> <span class="mi">175</span><span class="p">)</span>
这里的关键就是怎么匹配块的开头和结尾,利用的是两个正则,然后匹配之后就可以给这个块添加 html 标签和其他属性。
类似 vitepress 的规则 {#类似-vitepress-的规则}
看下我的代码:
class AlertBlockProcessor(BlockProcessor):
RE_FENCE_START = r'^:{3}\s*primary\s*.*\n*|^:{3}\s*secondary\s*.*\n*|^:{3}\s*success\s*.*\n*|^:{3}\s*danger\s*.*\n*|^:{3}\s*warning\s*.*\n*|^:{3}\s*info\s*.*\n*'
RE_FENCE_END = r'\n*:{3}$'
<span class="n">icon_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'primary'</span><span class="p">:</span> <span class="s1">'info-circle'</span><span class="p">,</span>
<span class="s1">'secondary'</span><span class="p">:</span> <span class="s1">'info-circle'</span><span class="p">,</span>
<span class="s1">'success'</span><span class="p">:</span> <span class="s1">'info-circle'</span><span class="p">,</span>
<span class="s1">'danger'</span><span class="p">:</span> <span class="s1">'warning'</span><span class="p">,</span>
<span class="s1">'warning'</span><span class="p">:</span> <span class="s1">'warning'</span><span class="p">,</span>
<span class="s1">'info'</span><span class="p">:</span> <span class="s1">'info-circle'</span>
<span class="p">}</span>
<span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
<span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">blocks</span><span class="p">):</span>
<span class="c1"># print(blocks)</span>
<span class="n">original_block</span> <span class="o">=</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">first_blocks</span> <span class="o">=</span> <span class="n">original_block</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">first_blocks</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
<span class="n">title</span> <span class="o">=</span> <span class="n">first_blocks</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">first_blocks</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">title</span> <span class="o">=</span> <span class="s1">'Tip'</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># print(blocks[0])</span>
<span class="c1"># Find block with ending fence</span>
<span class="k">for</span> <span class="n">block_num</span><span class="p">,</span> <span class="n">block</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">blocks</span><span class="p">):</span>
<span class="c1"># print(re.search(self.RE_FENCE_END, block), block)</span>
<span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
<span class="c1"># remove fence</span>
<span class="n">blocks</span><span class="p">[</span><span class="n">block_num</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
<span class="c1"># render fenced area inside a new div</span>
<span class="n">e</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">SubElement</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s1">'div'</span><span class="p">)</span>
<span class="n">icon_elm</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'i'</span><span class="p">)</span>
<span class="n">strong_tag</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'strong'</span><span class="p">)</span>
<span class="n">title_elm</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'p'</span><span class="p">)</span>
<span class="n">class_value</span> <span class="o">=</span> <span class="s1">'alert alert-</span><span class="si">{}</span><span class="s1">'</span>
<span class="n">flag</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">'primary'</span><span class="p">,</span> <span class="s1">'secondary'</span><span class="p">,</span> <span class="s1">'success'</span><span class="p">,</span> <span class="s1">'danger'</span><span class="p">,</span> <span class="s1">'warning'</span><span class="p">,</span> <span class="s1">'info'</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">original_block</span><span class="p">:</span>
<span class="n">e</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'class'</span><span class="p">,</span> <span class="n">class_value</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">key</span><span class="p">))</span>
<span class="n">e</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'role'</span><span class="p">,</span> <span class="s1">'alert'</span><span class="p">)</span>
<span class="n">icon_elm</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'class'</span><span class="p">,</span> <span class="s1">'fa fa-</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">icon_dict</span><span class="p">[</span><span class="n">key</span><span class="p">]))</span>
<span class="n">strong_tag</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">icon_elm</span><span class="p">)</span>
<span class="n">span_elm</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'span'</span><span class="p">)</span>
<span class="n">span_elm</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">title</span>
<span class="n">strong_tag</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">span_elm</span><span class="p">)</span>
<span class="n">title_elm</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">strong_tag</span><span class="p">)</span>
<span class="n">e</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">title_elm</span><span class="p">)</span>
<span class="n">flag</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">break</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">flag</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">parseBlocks</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
<span class="c1"># remove used blocks</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">blocks</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">True</span> <span class="c1"># or could have had no return statement</span>
<span class="c1"># No closing marker! Restore and do nothing</span>
<span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">original_block</span>
<span class="k">return</span> <span class="kc">False</span> <span class="c1"># equivalent to our test() routine returning False</span>
class AlertExtension(Extension):
"""
生产Alert块
"""
<span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
<span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">blockprocessors</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">AlertBlockProcessor</span><span class="p">(</span><span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="p">),</span> <span class="s1">'alert'</span><span class="p">,</span> <span class="mi">178</span><span class="p">)</span>
这里是实现了一个跟 vitepress 的规则类似的效果。
我这里就是需要匹配 :::info 标题
这种格式,然后结尾必须是 :::
,然后根据填写的级别去追加 class 属性和 role 属性。
测试和效果 {#测试和效果}
测试自定义规则 {#测试自定义规则}
我定义了三个规则,这里直接写一个用例同时验证三个规则,代码如下:
if __name__ == '__main__':
import markdown
<span class="n">m</span> <span class="o">=</span> <span class="n">markdown</span><span class="o">.</span><span class="n">Markdown</span><span class="p">(</span><span class="n">extensions</span><span class="o">=</span><span class="p">[</span>
<span class="s1">'markdown.extensions.extra'</span><span class="p">,</span>
<span class="n">DelExtension</span><span class="p">(),</span>
<span class="n">IconExtension</span><span class="p">(),</span>
<span class="n">AlertExtension</span><span class="p">()</span>
<span class="p">])</span>
<span class="n">t</span> <span class="o">=</span> <span class="s1">'''</span>
::: warning 提示
注意,这是一个演示效果,~~我是被删除的内容~~
:::
'''
<span class="n">html</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">convert</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">html</span><span class="p">)</span>
看一下输出的 html 效果:
<div class="alert alert-warning" role="alert">
<p><strong><i class="fa fa-warning"></i><span>提示</span></strong></p>
<p>注意,这是一个演示效果,<del>我是被删除的内容</del></p>
</div>
可以看到,代码块的规则符合预期,然后标签规则也符合预期,"删除文本"也是符合预期的。
演示效果 {#演示效果}
markdown 文本如下:
::: primary
这是一个没有标题的 alert
:::
::: primary
🎉 自定义标题
这是一个没有标题的 alert
:::
::: secondary
这是一个没有标题的 alert
:::
::: success 注意
这是一个演示效果,\~\~我是被删除的内容\~\~
:::
::: danger 注意
这是一个演示效果,\~\~我是被删除的内容\~\~
:::
::: warning 注意
这是一个演示效果,\~\~我是被删除的内容\~\~
:::
::: info 注意
`这是一个演示效果,~~我是被删除的内容~~
:::
`
渲染效果:
这是一个没有标题的 alert
🎉 自定义标题
这是一个没有标题的 alert
这是一个没有标题的 alert
注意
这是一个演示效果,~~我是被删除的内容~~
注意
这是一个演示效果,~~我是被删除的内容~~
注意
这是一个演示效果,~~我是被删除的内容~~
注意
这是一个演示效果,~~我是被删除的内容~~
代码组的效果 {#代码组的效果}
我还按照 vitepress 的渲染格式实现了代码组的效果,这个效果主要依靠 bootstrap 的效果,而且实现这个效果不仅仅是依靠 markdown 拓展,还需要写 js 代码来实现。
$ npx vitepress init
$ pnpm vitepress init
$ bunx vitepress init