npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-markdown-mini

v1.0.0-beta.3

Published

- 一个基于Vue的Markdown渲染组件,支持实时预览、主题切换和自定义扩展,适用于快速构建内容丰富的Web应用。

Downloads

33

Readme

vue-markdown-mini

  • 一个基于Vue的Markdown渲染组件,支持实时预览、主题切换和自定义扩展,适用于快速构建内容丰富的Web应用。

install

  • npm install vue-markdown-mini mermaid --save-dev

参数

| 参数名 | 类型 | 默认值 | 说明 | | -------------- | --------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | content | string | '' | MarkDown文本 | | theme | Theme | 'light' | MarkDown卡片主题 | | mdOptions | object | {} | 设置 markdown 对字符串的处理方式, 可参考markdown-it | | mdPlugins | MdPlugin[] | [] | 设置 markdown-it 插件 | | customXssRules | CustomXssRule[] | [] | 自定义 xss 对某种 tag 的过滤方式,每条规则需要指定 tag, 并给出需要加入白名单的属性数组 | | enableThink | boolean | false | 是否开启<think>标签识别 | | thinkOptions | ThinkOptions | -- | <think>标签配置,自定义样式等 | | typing | boolean | false | 开启打字机效果 | | typingOptions | TypingOptions | { step: 2, interval: 50, style: 'normal' } | 打字机效果配置 | | renderMermaid | boolean | false | 开启mermaid渲染图表 |

代码块插槽

| 插槽名 | 返回值 | 说明 | | ----------- | -------------------------------------------------- | ---------------------------- | | codeActions | { codeBlockData: CodeBlockData } | 代码块头部右侧自定义操作区域 | | codeHeader | { codeBlockData: CodeBlockData } | 自定义代码块头部区域 | | codeContent | { codeBlockData: CodeBlockData } | 自定义代码块内容区域 |

事件

| 事件名 | 返回值 | 说明 | | -------------- | ------ | -------------------------------------- | | after-mdt-init | Object | markdown-it实例mdt,在初始化完成后返回 | | typingStart | -- | 打字效果开始回调 | | typing | -- | 打字中每一步回调 | | typingEnd | -- | 打字结束回调 |

类型定义

CustomXssRule

interface CustomXssRule {
  key: string
  value: string[] | null
}

Mdplugin

interface Mdplugin {
  plugin: any
  opts?: Object
}

Theme

type Theme = 'light' | 'dark'

CodeBlockData

interface CodeBlockData {
  code: string
  language: string
}

ThinkOptions

interface ThinkOptions {
  customClass: string
}

TypingOptions

interface TypingOptions {
  step: number | [number, number] // 可指定两个数字之间step
  interval: number
  style: 'normal' | 'cursor' | 'gradient' | 'color'
}

使用方法

  • 引入组件及样式
  • import VueMarkdownMini from 'vue-markdown-mini'
  • import 'vue-markdown-mini/scss/index.scss'
  • app.component('VueMarkdownMini',VueMarkdownMini)

基本用法

基本用法只需传入 content 即可

:::demo

<template>
  <VueMarkdownMini :content="content" :theme="theme"></VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
let themeService
const theme = ref('light')
const content = ref(`
# 快速排序(Quick Sort)

### 介绍
**快速排序(Quick Sort)**:是一种高效的排序算法,它采用分治法(Divide and Conquer)的思想。它的基本思路是:

1. 选择一个基准值(pivot)
2. 将数组分成两部分:小于基准值的部分和大于等于基准值的部分
3. 递归地对这两部分进行排序

### 代码实现

1. 以下是快速排序的实现方法
\`\`\`ts
function quickSort(arr) {
  function quickSort(arr) {
  if (arr.length < 2) {
    return arr;
  }

  const pivot = arr[0];
  const left = [];
  const right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)];
}

// 使用示例
const arr = [3, 6, 8, 10, 1, 2, 1];
console.log(quickSort(arr)); // 输出排序后的数组
}
\`\`\`
`)

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>

:::

打字机效果

支持配置打字机动效果,当前内置不同效果样式可配置,支持配置动效速度与打字间隔,也适用流式数据返回场景。

:::demo

<template>
  <div class="btn-container">
    <d-button variant="solid" @click="generateAnswer">重新执行</d-button>
  </div>
  <div>
    <span class="demo-title">默认效果</span>
    <McBubble :variant="'bordered'">
      <VueMarkdownMini :content="content" :theme="theme" :typing="true"></VueMarkdownMini>
    </McBubble>
    <span class="demo-title">打字机并配置打字速度</span>
    <McBubble :variant="'bordered'">
      <VueMarkdownMini
        :content="content"
        :theme="theme"
        :typing="true"
        :typingOptions="typingOptions1"
      ></VueMarkdownMini>
    </McBubble>
    <span class="demo-title">渐变打字</span>
    <McBubble :variant="'bordered'">
      <VueMarkdownMini
        :content="content"
        :theme="theme"
        :typing="true"
        :typingOptions="typingOptions2"
      ></VueMarkdownMini>
    </McBubble>
    <span class="demo-title">彩色打字</span>
    <McBubble :variant="'bordered'">
      <VueMarkdownMini
        :content="content"
        :theme="theme"
        :typing="true"
        :typingOptions="typingOptions3"
      ></VueMarkdownMini>
    </McBubble>
    <span class="demo-title">流式返回</span>
    <McBubble :variant="'bordered'">
      <VueMarkdownMini
        :content="content1"
        :theme="theme"
        :typing="true"
        :typingOptions="typingOptions4"
        @typingEnd="typingEnd"
      ></VueMarkdownMini>
    </McBubble>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const MOCK_CONTENT = `**我了解到了你的需求**,*会进行<span style="color:red">打字机效果输出</span>,如果你需要重新执行打字机动效*,可点击重新执行按钮。`
let themeService
const content = ref(MOCK_CONTENT)
const theme = ref('light')

const typingOptions1 = {
  step: [1, 5],
  interval: 200,
  style: 'cursor',
}

const typingOptions2 = {
  interval: 200,
  style: 'gradient',
}

const typingOptions3 = {
  interval: 200,
  style: 'color',
}

const typingOptions4 = {
  interval: 200,
  step: 1,
}

const content1 = ref('')
let interval
let stramEnd = false

const streamContent = () => {
  clearInterval(interval)
  let currentIndex = 0
  interval = setInterval(() => {
    currentIndex += Math.ceil(Math.random() * 10)
    content1.value = MOCK_CONTENT.slice(0, currentIndex)
    if (currentIndex > MOCK_CONTENT.length) {
      stramEnd = true
      clearInterval(interval)
    }
  }, 100)
}

const generateAnswer = () => {
  content.value = ''
  content1.value = ''
  setTimeout(() => {
    content.value = MOCK_CONTENT
    streamContent()
  })
}

const typingEnd = () => {
  if (stramEnd) {
    console.log('流式与打字机效果完成')
  }
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  streamContent()
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>

<style lang="scss" scoped>
.btn-container {
  display: flex;
  margin-bottom: 12px;
}
.demo-title {
  margin: 20px 0px 8px 8px;
  display: inline-block;
}
</style>

:::

think标签支持

支持自定义的 think 标签,用于包裹特定内容并渲染为自定义样式的块级元素。适合用于强调思考过程或特殊内容展示

:::demo

<template>
  <div class="btn-container">
    <d-button variant="solid" @click="generateAnswer">{{
      isLoading ? '停止' : '重新生成'
    }}</d-button>
  </div>
  <div>
    <template v-for="(msg, idx) in messages" :key="idx">
      <McBubble
        v-if="msg.from === 'user'"
        :content="msg.content"
        :align="'right'"
        :avatarConfig="msg.avatarConfig"
      ></McBubble>
      <McBubble
        v-else
        :loading="msg.loading ?? false"
        :avatarConfig="msg.avatarConfig"
        :variant="'bordered'"
      >
        <div class="think-toggle-btn" @click="toggleThink(idx)">
          <i class="icon-point"></i>
          <span>{{ thinkBtnText }}</span>
          <i :class="btnIcon"></i>
        </div>
        <VueMarkdownMini
          :enableThink="true"
          :content="msg.content"
          :theme="theme"
        ></VueMarkdownMini>
      </McBubble>
    </template>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
let themeService
const theme = ref('light')
const messages = ref([])
const aiModelAvatar = {
  imgSrc: 'https://matechat.gitcode.com/logo.svg',
  width: 32,
  height: 32,
}
const isLoading = ref(false)
let interval = null
const thinkBtnText = ref('已深度思考 (用时16秒)')
const btnIcon = ref('icon-chevron-up-2')
const mockAnswer = `
<think>
嗯,用户让我帮他写一个快速排序。我得先回想一下快速排序的原理。快速排序是分治算法的一种,基本步骤是选一个基准元素,然后把数组分成两部分,一部分比基准小,另一部分比基准大,然后递归地对这两部分排序。

首先,我得确定基准的选择。通常有三种方法:选第一个元素、选中间元素或者随机选。选中间可能比较好,避免最坏情况的时间复杂度。比如数组已经有序的时候,选第一个会导致O(n²)的时间复杂度,但选中间元素可能更平衡。

然后,分区操作怎么做呢?分区的目标是把比基准小的放左边,大的放右边。可以用双指针的方法,左边找比基准大的,右边找比基准小的,然后交换,直到指针相遇。这时候再把基准放到正确的位置,返回基准的索引。

接下来是递归调用。对基准左边的子数组和右边的子数组分别进行快速排序,直到子数组的长度小于等于1,这时候数组就已经有序了。

现在要考虑用户的需求。用户可能对编程不太熟悉,所以代码需要简洁易懂,同时要有注释说明。或者用户可能需要一个高效的实现,不过快速排序的平均时间复杂度已经是O(n log n)了,应该没问题。

可能的边缘情况是数组为空或只有一个元素,这时候直接返回。所以函数里要先处理这种情况。

然后,测试一下这个函数是否正确。比如输入一个无序数组,比如[3,6,8,10,1,2,1],看看排序后的结果是否是[1,1,2,3,6,8,10]。或者测试已经有序的数组,比如[1,2,3,4,5],确保不会出现栈溢出或者错误的结果。

还有,是否需要处理重复元素?快速排序本身是能处理重复的,所以应该没问题。

再考虑语言的问题,用户没有说明用什么语言,但根据用户的名字或者上下文,可能想要Python的实现?或者可能其他语言。不过根据常见情况,可能先给出Python版本。

所以,综合这些思考,可以写出一个Python的快速排序函数。基准选中间元素,分区使用双指针,递归处理左右子数组。然后加上注释,解释每一步的作用。

最后,给出示例用法,让用户知道如何调用这个函数,并验证结果是否正确。
</think>
    
以下是使用 Python 实现的快速排序算法,包含详细注释:

\`\`\`python
def quick_sort(arr):
    """实现快速排序的递归函数"""
    # 基线条件:当数组长度小于等于1时,直接返回
    if len(arr) <= 1:
        return arr
    
    # 选择中间元素作为基准值(可有效避免最坏情况)
    pivot = arr[len(arr) // 2]
    
    # 将数组分成三部分:小于基准、等于基准、大于基准
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    
    # 递归排序左右子数组,合并结果
    return quick_sort(left) + middle + quick_sort(right)

# 示例用法
if __name__ == "__main__":
    example_arr = [3,6,8,10,1,2,1]
    sorted_arr = quick_sort(example_arr)
    print("原数组:", example_arr)
    print("排序后:", sorted_arr)
\`\`\`
`
const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

const toggleThink = (idx) => {
  if (isLoading.value) {
    return
  }
  const targetNode = document.querySelectorAll('.mc-bubble-content-container')[idx]
  if (targetNode) {
    const thinkBlock = targetNode.querySelector('.mc-think-block')
    if (thinkBlock) {
      const currentDisplay = getComputedStyle(thinkBlock).display
      thinkBlock.style.display = currentDisplay === 'none' ? 'block' : 'none'
      btnIcon.value = currentDisplay === 'none' ? 'icon-chevron-up-2' : 'icon-chevron-down-2'
    }
  }
}

const generateAnswer = () => {
  if (isLoading.value) {
    isLoading.value = false
    clearInterval(interval)
  } else {
    isLoading.value = true
    messages.value = [
      {
        from: 'ai-model',
        avatarConfig: { ...aiModelAvatar },
        content: '',
        loading: false,
      },
    ]
    thinkBtnText.value = '思考中...'
    let currentIndex = 0
    let totalTime = 0
    interval = setInterval(() => {
      if (currentIndex < mockAnswer.length) {
        messages.value[messages.value.length - 1].content = mockAnswer.slice(0, currentIndex)
        currentIndex += 10
        totalTime += 100
        if (
          messages.value[messages.value.length - 1].content.indexOf('</think>') > -1 &&
          thinkBtnText.value === '思考中...'
        ) {
          thinkBtnText.value = `已深度思考 (用时${totalTime / 1000}秒)`
        }
      } else {
        isLoading.value = false
        clearInterval(interval)
        messages.value[messages.value.length - 1].loading = false
      }
    }, 100)
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }

  messages.value.push({
    from: 'ai-model',
    avatarConfig: { ...aiModelAvatar },
    content: mockAnswer,
  })
})
</script>

:::

主题切换

组件提供了浅色与深色两种主题,默认使用浅色主题,可以通过 theme 属性来切换主题

:::demo

<template>
  <div class="btn-container">
    <d-button variant="solid" @click="changeTheme">切换主题</d-button>
  </div>
  <div class="theme-container" :class="themeClass">
    <VueMarkdownMini :content="content" :theme="theme"></VueMarkdownMini>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeMount } from 'vue'
const theme = ref('light')
const themeClass = ref('light-background')
let themeService
const content = ref(`
# 快速排序(Quick Sort)

### 介绍
**快速排序(Quick Sort)**:是一种高效的排序算法,它采用分治法(Divide and Conquer)的思想。它的基本思路是:

1. 选择一个基准值(pivot)
2. 将数组分成两部分:小于基准值的部分和大于等于基准值的部分
3. 递归地对这两部分进行排序

### 代码实现

1. 以下是快速排序的实现方法
\`\`\`ts
function quickSort(arr) {
  function quickSort(arr) {
  if (arr.length < 2) {
    return arr;
  }

  const pivot = arr[0];
  const left = [];
  const right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)];
}

// 使用示例
const arr = [3, 6, 8, 10, 1, 2, 1];
console.log(quickSort(arr)); // 输出排序后的数组
}
\`\`\`
`)

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
    themeClass.value =
      themeService.currentTheme.id === 'infinity-theme' ? 'light-background' : 'dark-background'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>
<style scoped lang="scss">
.btn-container {
  display: flex;
  justify-content: end;
}
.theme-container {
  margin-top: 8px;
  padding: 12px;
  border-radius: 8px;
}
.light-background {
  background-color: #f6f6f8;
}

.dark-background {
  background-color: #1a1a1c;
}
</style>

:::

数学公式

通过配置md-plugins katex插件,进行数学公式渲染(DEMO未实际渲染,实际使用时解开代码中注释即可按预期渲染)。 :::demo

<template>
  <VueMarkdownMini :content="content" :theme="theme" :mdPlugins="mdPlugins"></VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
//import { katex } from '@mdit/plugin-katex'; // 请首先安装@mdit/plugin-katex依赖
let themeService
const theme = ref('light')
//const mdPlugins = ref([{
//  plugin: katex,
//  opt: {}
//}])
const content = ref(
  `
$E = mc^2$
$\\sqrt{3x-1}+(1+x)^2$
`,
)

const handleAction = (codeBlockData) => {
  console.log(codeBlockData)
}

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>
<style>
/* @import 'katex/dist/katex.min.css';  请首先安装 katex 依赖 */
</style>

PlantUML 渲染

通过配置md-plugins plantuml插件,进行plantuml图渲染。 :::demo

<template>
  <VueMarkdownMini :content="content" :theme="theme" :mdPlugins="mdPlugins"></VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import PlantUml from 'markdown-it-plantuml' // 请首先安装markdown-it-plantuml依赖
let themeService
const theme = ref('light')
const mdPlugins = ref([
  {
    plugin: PlantUml,
    opts: {
      server: 'https://www.plantuml.com/plantuml',
    }, // 自定义server可参考plantuml官方文档进行搭建
  },
])
const content = ref(`
@startuml
Alice -> "Bob()" : Hello
"Bob()" -> "This is very long" as Long
' You can also declare:
' "Bob()" -> Long as "This is very long"
Long --> "Bob()" : ok
@enduml`)

const handleAction = (codeBlockData) => {
  console.log(codeBlockData)
}

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>

:::

emoji渲染

通过配置markdown-it-emoji插件,进行emoji表情渲染。 :::demo

<template>
  <VueMarkdownMini :content="content" :theme="theme" :mdPlugins="mdPlugins"></VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { full as emoji } from 'markdown-it-emoji' // 请首先安装markdown-it-emoji依赖
let themeService
const theme = ref('light')
const mdPlugins = ref([
  {
    plugin: emoji,
  },
])
const content = ref(`
:joy: :thumbsup: :laughing: :blush: :dog:
`)

const handleAction = (codeBlockData) => {
  console.log(codeBlockData)
}

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>

:::

自定义代码块操作区

我们提供了 actions 插槽,支持你自定义代码块操作区

:::demo

<template>
  <VueMarkdownMini :content="content" :theme="theme">
    <template #actions="{ codeBlockData }">
      <div class="btn-group">
        <d-button variant="solid" @click="handleAction(codeBlockData)">自定义按钮</d-button>
      </div>
    </template>
  </VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
let themeService
const theme = ref('light')
const content = ref(`以下是快速排序的实现方法:

\`\`\`ts
function quickSort(arr) {
  // 快速排序
}
\`\`\`
`)

const handleAction = (codeBlockData) => {
  console.log(codeBlockData)
}

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>

:::

自定义代码块头部

我们提供了 header 插槽,支持你自定义代码块头部区域

:::demo

<template>
  <VueMarkdownMini :content="content" :theme="theme">
    <template #header="{ codeBlockData }">
      <div class="header-container">
        <div class="header-left">
          <img src="https://matechat.gitcode.com/logo.svg" alt="logo" />
          <span>MateChat</span>
        </div>
        <div class="header-right">
          <i class="icon-publish-new"></i>
        </div>
      </div>
    </template>
  </VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
let themeService
const theme = ref('light')
const content = ref(`以下是快速排序的实现方法:

\`\`\`ts
function quickSort(arr) {
  // 快速排序
}
\`\`\`
`)

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>

::: ### 自定义代码块内容区 我们提供了 `content` 插槽,支持你自定义代码块内容区 :::demo ```vue
<template>
  <VueMarkdownMini :content="content" :theme="theme">
    <template #content="{ codeBlockData }">
      <div class="content-container" v-html="transfer(codeBlockData)"></div>
    </template>
  </VueMarkdownMini>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import markdownIt from 'markdown-it'
import hljs from 'highlight.js'
let themeService
const theme = ref('light')
const content = ref(`以下是快速排序的实现方法:
\`\`\`ts
function quickSort(arr) {
  if (arr.length < 2) {
    return arr;
  }

  const pivot = arr[0];
  const left = [];
  const right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)];
}

// 使用示例
const arr = [3, 6, 8, 10, 1, 2, 1];
console.log(quickSort(arr)); // 输出排序后的数组
\`\`\`
`)

const mdt = markdownIt({
  breaks: true,
  linkify: true,
  html: true,
  highlight: (str, lang) => {
    if (lang && hljs.getLanguage(lang)) {
      try {
        const preCode = hljs.highlight(lang, str, true).value
        const lines = preCode.split(/\n/).slice(0, -1)
        let html = lines
          .map((item, index) => {
            return (
              '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item + '</li>'
            )
          })
          .join('')
        html = '<ol>' + html + '</ol>'
        return '<pre class="hljs"><code>' + html + '</code></pre>'
      } catch (__) {}
    }

    const preCode = mdt.utils.escapeHtml(str)
    const lines = preCode.split(/\n/).slice(0, -1)
    let html = lines
      .map((item, index) => {
        return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item + '</li>'
      })
      .join('')
    html = '<ol>' + html + '</ol>'
    return '<pre class="hljs"><code>' + html + '</code></pre>'
  },
})

const htmlStr = mdt.render(content.value)

const transfer = (codeBlockData) => {
  const { code, language } = codeBlockData
  const codeBlockStr = '\`\`\`' + language + '\n' + code + '\`\`\`'
  return mdt.render(codeBlockStr)
}

const changeTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
  themeClass.value =
    themeClass.value === 'light-background' ? 'dark-background' : 'light-background'
}

const themeChange = () => {
  if (themeService) {
    theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark'
  }
}

onMounted(() => {
  if (typeof window !== 'undefined') {
    themeService = window['devuiThemeService']
  }
  themeChange()
  if (themeService && themeService.eventBus) {
    themeService.eventBus.add('themeChanged', themeChange)
  }
})
</script>