vue-markdown-mini
v1.0.0-beta.3
Published
- 一个基于Vue的Markdown渲染组件,支持实时预览、主题切换和自定义扩展,适用于快速构建内容丰富的Web应用。
Downloads
33
Maintainers
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>