环境搭建的一些坑
📅 发表于 2025/05/16
🔄 更新于 2025/05/16
👁️ 次访问
📝 0 字
⏳ 0 分钟
环境搭建
#vitpress博客
#busuanzi
#图片缩放
# 安装
mkdir plmblog && cd plmblog
bun add -D vitepress
# 选择初始化在docs文件夹里
bunx vitepress init
# copy markdown文件及Index.md
# 安装组件
bun add -D vue
# 代码库标题
bun add -D vitepress-plugin-group-icons
# busuanzi
bun add -D busuanzi.pure.js
# 自动侧边栏
pnpm add -D vitepress-sidebar
# mark 渲染
bun add -D markdown-it-mark
# 数学
bun add -D markdown-it-mathjax3@4.3.2 vitepress@latest
# 图片缩放
bun add -D medium-zoom
# giscus
bun add -D vitepress-plugin-comment-with-giscus
# sass
bun add -D sass-embedded`
# container
# 在vitepress下建立文件夹
.vitepress/configs
.vitepress/theme
.vitepress/theme/components
.vitepress/theme/style
.vitepress/theme/utils
# 建立组件index.ts
.vitepress/theme/index.ts
# 打不开某些文件,可能是因为文件里有{1,2,3,4}这种内容,去掉大括号即可,得看控制台。
## 拷贝css
## 首页文字下划线
## 安装组件
Index.md
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
lang: zh-CN
navbar: true
sidebar: true
lastUpdated: false
hero:
name: plmblog
text: 学习笔记 📚
tagline: 🏠 Work for a better life.
actions:
- theme: brand
text: Agent-Tool-RL 笔记
link: /posts/llm/agent/rl/02-agent-tool
- theme: alt
text: Agent-Search-RL 笔记
link: /posts/llm/agent/rl/03-agent-search
image: /public/vitepress-logo-large.svg # 替换为你的图片路径
imageAlt: "Hero Logo"
imageStyle: "max-width: 300px; border-radius: 50%; margin-top: 20px;"
features:
- icon: 🤖
title: 主要记录LLM相关笔记
details: Agent、Tool-Use、Interaction、LLM、RL等等。
- icon: ✍️
title: 好记性不如烂笔头
details: 自己的笔记便于快速回忆,它比会消失的记忆有用。
- icon: 📚
title: 重新拾起笔记
details: 找回丢失的那些年,希望能保持成长。
---
<HomeUnderline />
HomeUnderline.vue
index.ts配置,切记有一些内容。
/* .vitepress/theme/index.ts */
import DefaultTheme from 'vitepress/theme'
import HomeUnderline from "./components/HomeUnderline.vue"
import bsz from "./components/bsz.vue"
import busuanzi from 'busuanzi.pure.js'
import { h } from 'vue'
import './style/index.css'
import { inBrowser } from 'vitepress'
export default {
extends: DefaultTheme,
enhanceApp({app, router}) {
// 注册全局组件
app.component('HomeUnderline' , HomeUnderline)
// fetch不蒜子
if (inBrowser) {
router.onAfterRouteChanged = () => {
busuanzi.fetch()
}
}
},
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-bottom': () => h(bsz), //不蒜子layout-bottom插槽,
// 'doc-before': () => h(MyDocInfo),
// 'doc-footer-before': () => h(backtotop), // 使用doc-footer-before插槽
})
},
}
bsz.vue,需要安装插件
<script setup>
</script>
<template>
<div class="busuanzi">
总访客数: <span id="busuanzi_value_site_uv" /> · 总访问量: <span id="busuanzi_value_site_pv" />
</div>
<div class="busuanzi">
PLM's Blog @ 2016 - {{ new Date().getFullYear() }}
</div>
</template>
<style>
.busuanzi {
font-size: 15px;
color: black;
text-align: center;
}
</style>
MyDocInfo.vue
import MyDocInfo from "./components/MyDocInfo.vue"
app.component('MyDocInfo', MyDocInfo)
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-bottom': () => h(bsz), //不蒜子layout-bottom插槽,
'doc-before': () => h(MyDocInfo),
// 'doc-footer-before': () => h(backtotop), // 使用doc-footer-before插槽
})
},
<script lang="ts" setup>
import { useData } from 'vitepress'
import { computed, ref, onMounted } from 'vue'
import { countWord, countTransK, formatDate } from '../utils/functions'
const { frontmatter } = useData()
const { page } = useData()
const createDate = computed(() => {
const frontmatterDate = page.value.frontmatter.date
return frontmatterDate ?
new Date(frontmatterDate) :
new Date(page.value.lastUpdated!)
})
const updateDate = computed(() => {
const frontmatterUpdate = page.value.frontmatter.update
const lastUpdated = page.value.lastUpdated
const frontmatterDate = page.value.frontmatter.date
return frontmatterUpdate ?
new Date(frontmatterUpdate) :
lastUpdated ?
new Date(lastUpdated) :
new Date(frontmatterDate)
})
const wordCount = ref(0)
const imageCount = ref(0)
const wordTime = computed(() => {
return ((wordCount.value / 275) * 60)
})
const imageTime = computed(() => {
const n = imageCount.value
if (imageCount.value <= 10) {
// 等差数列求和
return n * 13 + (n * (n - 1)) / 2
}
return 175 + (n - 10) * 3
})
// 阅读时间
const readTime = computed(() => {
return Math.ceil((wordTime.value + imageTime.value) / 60)
})
const pv = ref('')
const lastUpdated = ref('')
let timeoutPV = 0
const getPV = () => {
if (timeoutPV) clearTimeout(timeoutPV)
timeoutPV = window.setTimeout(() => {
const $PV = document.querySelector('#busuanzi_value_page_pv')
const text = $PV?.innerHTML
if ($PV && text) {
pv.value = countTransK(parseInt(text))
} else {
getPV()
}
}, 500)
}
function analyze() {
document.querySelectorAll('.meta-des').forEach(v => v.remove())
const docDomContainer = window.document.querySelector('#VPContent')
const imgs = docDomContainer?.querySelectorAll<HTMLImageElement>(
'.content-container .main img'
)
imageCount.value = imgs?.length || 0
const words = docDomContainer?.querySelector('.content-container .main')?.textContent || ''
wordCount.value = countWord(words)
getPV()
const dateOption = formatDate()
// lastUpdated.value = dateOption.format(new Date(frontmatter.value.lastUpdated || page.value.lastUpdated!)).replace(/\//g, '-')
lastUpdated.value = new Date(frontmatter.value.lastUpdated || page.value.lastUpdated!).toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
})
}
onMounted(() => {
// 初始化时执行一次
analyze()
})
</script>
<template>
<article class="post-header">
<h1 class="title">{{ frontmatter.title }}</h1>
<div class="stats-container">
<div class="stat-divider" v-if="frontmatter.categories?.length"></div>
<!-- 发表时间 -->
<div class="stat-item">
📅
发表于
<span class="stat-text">
{{ createDate.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }) }}
</span>
</div>
<!-- 更新时间 -->
<div class="stat-item">
🔄 更新于
<span class="stat-text">
{{ updateDate.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }) }}
</span>
</div>
<div class="stat-divider"></div>
<!-- 访问PV -->
<div class="stat-item">
👁️
<span class="stat-text">{{ pv }} 次访问</span>
</div>
<div class="stat-divider"></div>
<!-- 字数统计 -->
<div class="stat-item">
📝
<span class="stat-text">{{ wordCount }} 字</span>
</div>
<div class="stat-divider"></div>
<!-- 阅读时长 -->
<div class="stat-item">
⏳
<span class="stat-text">{{ readTime }} 分钟</span>
</div>
<div class="stat-divider"></div>
</div>
<!-- 标签组 -->
<div v-if="frontmatter.tags?.length" class="tag-group">
<div class="category-item" v-for="(tag, index) in frontmatter.categories" :key="index">
{{ tag }}
</div>
<div class="tag-item" v-for="(tag, index) in frontmatter.tags" :key="index">
#{{ tag }}
</div>
</div>
</article>
</template>
<style scoped>
.post-header {
margin-bottom: 0rem;
}
.title {
position: relative; /* 为下划线定位做准备 */
padding-bottom: 8px; /* 为下划线留出空间 */
background: -webkit-linear-gradient(10deg, #bd34fe 5%, #e43498 15%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 36px;
line-height: normal;
font-weight: bold;
color: var(--vp-c-text-soft);
}
/* 添加渐变下划线 */
.title::after {
content: '';
position: absolute;
left: 0%;
bottom: 0;
width: 100%;
height: 2px;
background: linear-gradient(
90deg,
rgba(189, 52, 254, 0.3),
#e43498 40%,
rgba(189, 52, 254, 0.3)
);
border-radius: 2px;
}
/* 统计信息卡片式设计 */
.stats-container {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 8px 16px;
margin: 0.3rem 0;
background: linear-gradient(135deg, rgba(189, 52, 254, 0.05), rgba(228, 52, 152, 0.05)); /* 浅色渐变背景 */
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
font-size: 0.7rem;
}
/* 统一统计项样式 */
.stat-item {
display: flex;
align-items: center;
gap: 0.3rem;
padding: 0.4rem 1rem;
background: linear-gradient(
to right,
rgba(236, 72, 153, 0.1),
rgba(129, 140, 248, 0.1)
);
/* border-left: 1px solid var(--vp-c-brand-1); */
border-radius: 6px;
transition: all 0.2s ease;
}
.stat-item:hover {
background: linear-gradient(
to right,
rgba(236, 72, 153, 0.15),
rgba(129, 140, 248, 0.15)
);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 调整分隔符样式 */
.stat-divider {
width: 1px;
background: var(--vp-c-divider);
opacity: 0.3;
border-radius: 0;
}
/* 标签设计 */
.tag-group {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-top: 0.2rem;
justify-content: flex-start;
}
.tag-item {
padding: 0.3rem 0.8rem;
background: linear-gradient(135deg, #f3e8ff, #e9d5ff); /* 更浅的紫色渐变 */
border-radius: 14px;
font-size: 0.7rem;
color: #5a5a5a;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
.tag-item:hover {
background: linear-gradient(135deg, #e9d5ff, #f3e8ff); /* 保留 hover 效果 */
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
.category-item {
padding: 0.3rem 0.8rem;
background: linear-gradient(135deg, #d1fae5, #a7f3d0); /* 更浅的绿色渐变 */
border-radius: 14px;
font-size: 0.7rem;
color: #065f46;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
.category-item:hover {
background: linear-gradient(135deg, #a7f3d0, #d1fae5); /* 保留 hover 效果 */
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* 暗色模式适配 */
.dark .tag-item {
background: linear-gradient(135deg, #d8b4fe, #c084fc); /* 暗色模式下更浅的紫色渐变 */
color: #ffffff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.dark .tag-item:hover {
background: linear-gradient(135deg, #c084fc, #d8b4fe); /* 暗色模式下的 hover 效果 */
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.dark .category-item {
background: linear-gradient(135deg, #6ee7b7, #34d399); /* 暗色模式下更浅的绿色渐变 */
color: #ffffff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.dark .category-item:hover {
background: linear-gradient(135deg, #34d399, #6ee7b7); /* 暗色模式下的 hover 效果 */
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
/* 暗色模式适配 */
.dark {
.title {
background: -webkit-linear-gradient(10deg, #e43498 5%, #bd34fe 15%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 36px;
line-height: normal;
font-weight: bold;
}
.stats-container {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.tag-item {
background: var(--vp-c-bg-soft);
}
.stat-item {
background: linear-gradient(
to right,
rgba(219, 39, 119, 0.1),
rgba(99, 102, 241, 0.1)
);
}
.stat-item:hover {
background: linear-gradient(
to right,
rgba(219, 39, 119, 0.15),
rgba(99, 102, 241, 0.15)
);
}
}
/* 移动端适配 */
@media (max-width: 768px) {
.title {
font-size: 1.8rem;
padding-left: 1.5rem;
}
.stats-container {
flex-wrap: wrap;
gap: 1rem;
padding: 1rem;
}
.stat-item {
width: 100%;
justify-content: flex-start;
}
.stat-divider {
display: none;
}
}
</style>
# 1. 安装
pnpm add -D vitepress-sidebar
# 2. copy configs/index.ts, nav.ts, sidebar.ts
/* configs/index.ts */
export * from './nav'
export * from './sidebar'
/* configs/sidebar.ts */
import type { DefaultTheme } from 'vitepress'
export const vitePressSidebarConfigs = [
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/nlp',
basePath: '/posts/olds/nlp/',
resolvePath: '/posts/olds/nlp/',
useTitleFromFileHeading: false,
rootGroupText: 'NLP',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/dl',
basePath: '/posts/olds/dl/',
resolvePath: '/posts/olds/dl/',
useTitleFromFileHeading: false,
rootGroupText: 'DL',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/rl',
basePath: '/posts/olds/rl/',
resolvePath: '/posts/olds/rl/',
useTitleFromFileHeading: false,
rootGroupText: 'RL',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/ml',
basePath: '/posts/olds/ml/',
resolvePath: '/posts/olds/ml/',
useTitleFromFileHeading: false,
rootGroupText: 'ML',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/algo',
basePath: '/posts/olds/algo/',
resolvePath: '/posts/olds/algo/',
useTitleFromFileHeading: false,
rootGroupText: 'ALGO',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/bigdata',
basePath: '/posts/olds/bigdata/',
resolvePath: '/posts/olds/bigdata/',
useTitleFromFileHeading: false,
rootGroupText: 'BIG Data',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/env',
basePath: '/posts/olds/env/',
resolvePath: '/posts/olds/env/',
useTitleFromFileHeading: false,
rootGroupText: '环境搭建',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/olds/other',
basePath: '/posts/olds/other/',
resolvePath: '/posts/olds/other/',
useTitleFromFileHeading: false,
rootGroupText: '其他',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/llm/agent/rl',
basePath: '/posts/llm/agent/rl/',
resolvePath: '/posts/llm/agent/rl/',
useTitleFromFileHeading: false,
rootGroupText: 'Agent-RL',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/llm/agent/basic',
basePath: '/posts/llm/agent/basic/',
resolvePath: '/posts/llm/agent/basic/',
useTitleFromFileHeading: false,
rootGroupText: 'Agent-基础',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/llm/rl',
basePath: '/posts/llm/rl/',
resolvePath: '/posts/llm/rl/',
useTitleFromFileHeading: false,
rootGroupText: 'LLM-RL',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/llm/basic',
basePath: '/posts/llm/basic/',
resolvePath: '/posts/llm/basic/',
useTitleFromFileHeading: false,
rootGroupText: 'LLM-basic',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/exps/env',
basePath: '/posts/exps/env/',
resolvePath: '/posts/exps/env/',
useTitleFromFileHeading: false,
rootGroupText: '环境搭建',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'posts/exps/mind',
basePath: '/posts/exps/mind/',
resolvePath: '/posts/exps/mind/',
useTitleFromFileHeading: false,
rootGroupText: '心得体会',
collapsed: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
sortMenusByFrontmatterDate: true,
sortMenusOrderByDescending: true,
},
{
documentRootPath: 'docs',
scanStartPath: 'about',
resolvePath: '/about/',
useTitleFromFrontmatter: false,
rootGroupText: '个人简介',
// useTitleFromFrontmatter: true,
}
];
/* configs/nav.ts */
import type { DefaultTheme } from 'vitepress'
export const nav: DefaultTheme.Config['nav'] = [
{ text: '首页', link: '/' },
{
text: '🐲LLM',
items: [
{
text: '🦋Basic',
items: [
{ text: '基础', link: '/posts/llm/basic' },
],
},
{
text: '🤖Agent',
items: [
{ text: '🚗概念及应用', link: '/posts/llm/agent/basic' },
{ text: '🚄Agent-RL', link: '/posts/llm/agent/rl/02-agent-tool' },
],
},
{
text: '🐼rl',
items: [
{ text: 'r1相关', link: '/posts/llm/rl' },
],
},
],
},
{
text: '📙旧文章',
items: [
{
text: '🍓NLP',
items: [
{ text: '自然语言处理', link: '/posts/olds/nlp' },
],
},
{
text: '🍑基础知识',
items: [
{ text: '深度学习', link: '/posts/olds/dl' },
{ text: '强化学习', link: '/posts/olds/rl' },
{ text: '机器学习', link: '/posts/olds/ml' },
],
},
{
text: '🍎算法',
items: [
{ text: '算法题', link: '/posts/olds/algo' },
{ text: '大数据', link: '/posts/olds/bigdata' },
],
},
{
text: '🍒其他',
items: [
{ text: '环境搭建', link: '/posts/olds/env' },
{ text: '其他', link: '/posts/olds/other' },
],
},
],
},
{
text: '经验',
items: [
{
text: '环境',
items: [
{ text: '环境搭建', link: '/posts/exps/env' },
],
},
{
text: '心得',
items: [
{ text: '心得体会', link: '/posts/exps/mind' },
],
},
],
},
{
text: "归档",
link: '/archive.md'
},
{ text: '关于我', link: '/about/me' },
]
Config.mts
import { defineConfig } from 'vitepress'
import { nav } from './configs'
import { vitePressSidebarConfigs } from './configs'
import { withSidebar } from 'vitepress-sidebar';
// https://vitepress.dev/reference/site-config
const vitePressOptions = {
title: "📚 plmblog",
description: "记录一些学习笔记。",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav,
outline: {
level: [1,4], // 确保字段值正确
label: '当前页大纲' // 确保字段拼写正确
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
]
}
}
// 自动使用侧边栏配置
export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarConfigs));
bun add -D markdown-it-mark
bun add -D markdownItContainer
config.mts,主要是渲染以及css配置(custom-block)。
import { defineConfig } from 'vitepress'
import { nav } from './configs'
import { vitePressSidebarConfigs } from './configs'
import { withSidebar } from 'vitepress-sidebar';
import markdownItMark from 'markdown-it-mark'
import markdownItContainer from 'markdown-it-container';
// 代码标题
import { groupIconMdPlugin, groupIconVitePlugin, localIconLoader} from 'vitepress-plugin-group-icons'
const vitePressOptions = {
title: "📚 plmblog",
description: "记录一些学习笔记。",
lastUpdated: true,
themeConfig: {
nav,
outline: {
level: [1,4], // 确保字段值正确
label: '当前页大纲' // 确保字段拼写正确
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/plmsmile' }
],
search: {
provider: 'local'
},
docFooter: {
prev: '上一页',
next: '下一页',
},
},
head: [
['link',{ rel: 'icon', href: '/plm.png'}],
],
sitemap: {
hostname: 'http://localhost:5173/',
},
markdown: {
image: {
lazyLoading: true
},
lineNumbers: true,
math: true,
container: {
tipLabel: '提示',
warningLabel: '警告',
dangerLabel: '危险',
infoLabel: '信息',
detailsLabel: '详细信息',
noteLabel: '笔记',
importantLabel: '重要',
cautionLabel: '小心',
todoLabel: '待办',
},
config: (md) => {
const containerTypes = ['note', 'important', 'caution', 'tip', 'warning', 'danger', 'info', 'details', 'todo'];
const containerLabels = vitePressOptions.markdown.container;
containerTypes.forEach(type => {
md.use(markdownItContainer, type, {
render: (tokens, idx) => {
const token = tokens[idx];
if (token.nesting === 1) {
const title = token.info.trim().slice(type.length).trim() || containerLabels[`${type}Label`] || type.charAt(0).toUpperCase() + type.slice(1);
return `<div class="custom-block ${type}">\n<div class="custom-block-title">${title}</div>\n`;
} else {
return `</div>\n`;
}
}
});
});
md.use((md) => {
// md.use(timeline);
md.use(groupIconMdPlugin);
md.use(markdownItMark);
});
}
},
vite: {
plugins: [
groupIconVitePlugin({
customIcon: {
python: 'https://api.iconify.design/vscode-icons:file-type-python.svg',
shell: 'https://api.iconify.design/vscode-icons:file-type-powershell-psd.svg',
bash: 'https://api.iconify.design/catppuccin:bash.svg'
},
}
) //代码组图标
],
},
}
// 自动使用侧边栏配置
export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarConfigs));
bun add -D medium-zoom
// import
// 图片缩放
// 配置css 即可,看原文
# 安装库
bun add -D vitepress-plugin-group-icons
# 配置css
# index.ts引用css
import 'virtual:group-icons.css'
# config.mts里配置
md.use(groupIconMdPlugin);
vite: {
plugins: [
groupIconVitePlugin() //代码组图标
],
},
import { groupIconMdPlugin, groupIconVitePlugin, localIconLoader} from 'vitepress-plugin-group-icons'
vite: {
plugins: [
groupIconVitePlugin({
customIcon: {
python: 'https://api.iconify.design/vscode-icons:file-type-python.svg',
shell: 'https://api.iconify.design/vscode-icons:file-type-powershell-psd.svg',
bash: 'https://api.iconify.design/catppuccin:bash.svg'
},
}
) //代码组图标
],
},
四个文件
Docmeta.ts
post.data.ts
post.ts
archive.vue
archive.md
import { defineConfig } from 'vitepress'
import { nav } from './configs'
import { vitePressSidebarConfigs } from './configs'
import { withSidebar } from 'vitepress-sidebar';
import markdownItMark from 'markdown-it-mark'
import markdownItContainer from 'markdown-it-container';
// 代码标题
import { groupIconMdPlugin, groupIconVitePlugin, localIconLoader} from 'vitepress-plugin-group-icons'
const vitePressOptions = {
title: "📚 plmblog",
description: "记录一些学习笔记。",
lastUpdated: true,
themeConfig: {
nav,
outline: {
level: [1,4], // 确保字段值正确
label: '当前页大纲' // 确保字段拼写正确
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/plmsmile' }
],
search: {
provider: 'local'
},
docFooter: {
prev: '上一页',
next: '下一页',
},
},
head: [
['link',{ rel: 'icon', href: '/plm.png'}],
],
sitemap: {
hostname: 'http://localhost:5173/',
},
markdown: {
image: {
lazyLoading: true
},
lineNumbers: true,
math: true,
container: {
tipLabel: '提示',
warningLabel: '警告',
dangerLabel: '危险',
infoLabel: '信息',
detailsLabel: '详细信息',
noteLabel: '笔记',
importantLabel: '重要',
cautionLabel: '小心',
todoLabel: '待办',
},
config: (md) => {
const containerTypes = ['note', 'important', 'caution', 'tip', 'warning', 'danger', 'info', 'details', 'todo'];
const containerLabels = vitePressOptions.markdown.container;
containerTypes.forEach(type => {
md.use(markdownItContainer, type, {
render: (tokens, idx) => {
const token = tokens[idx];
if (token.nesting === 1) {
const title = token.info.trim().slice(type.length).trim() || containerLabels[`${type}Label`] || type.charAt(0).toUpperCase() + type.slice(1);
return `<div class="custom-block ${type}">\n<div class="custom-block-title">${title}</div>\n`;
} else {
return `</div>\n`;
}
}
});
});
md.use((md) => {
// md.use(timeline);
md.use(groupIconMdPlugin);
md.use(markdownItMark);
});
}
},
vite: {
plugins: [
groupIconVitePlugin({
customIcon: {
python: 'https://api.iconify.design/vscode-icons:file-type-python.svg',
shell: 'https://api.iconify.design/vscode-icons:file-type-powershell-psd.svg',
bash: 'https://api.iconify.design/catppuccin:bash.svg'
},
}
) //代码组图标
],
},
}
// 自动使用侧边栏配置
export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarConfigs));
1、vitepress支持markdown-it-mark
需要先安装,参考 VitePress TODO,出现问题需要先声明一下。
config.mts
配置生效。import 和 use
再设定一些css。
2、typora支持高亮语法。
brew install --cask hammerspoon
-- ==高亮文字== 快捷键
hs.hotkey.bind({'cmd'}, 'h', function()
local appName = hs.application.frontmostApplication():name()
if appName ~= "Typora" then return end -- 仅在Typora生效
-- 保存原始剪贴板
local originalClip = hs.pasteboard.getContents()
-- 执行高亮操作
hs.eventtap.keyStroke({'cmd'}, 'c') -- 复制选中文本
hs.timer.usleep(1000) -- 50ms延迟
local selectedText = hs.pasteboard.getContents()
if selectedText and #selectedText > 0 then
hs.pasteboard.setContents('=='..selectedText..'==')
hs.eventtap.keyStroke({'cmd'}, 'v') -- 粘贴处理后的文本
hs.eventtap.keyStroke({}, 'left') -- 光标左移
else
hs.eventtap.keyStrokes('==') -- 插入新标记
hs.eventtap.keyStroke({}, 'left')
end
-- 恢复剪贴板(延迟0.5秒避免冲突)
hs.timer.doAfter(0.5, function()
hs.pasteboard.setContents(originalClip)
end)
end)
3、vitepress支持caution、important、note等标签语法,注意class name和custom-block css里的名称要一致,然后使用cursor调整相关css即可。
# 安装插件
bun add markdown-it-container
# config.mts 渲染
import markdownItContainer from 'markdown-it-container'; // 确保使用 import
const containerTypes = ['note', 'important', 'caution', 'tip', 'warning', 'danger', 'info', 'details', 'todo'];
const containerLabels = vitePressOptions.markdown.container; // 获取 container 配置
containerTypes.forEach(type => {
md.use(markdownItContainer, type, {
render: (tokens, idx) => {
const token = tokens[idx];
if (token.nesting === 1) {
// Opening tag with title
const title = token.info.trim().slice(type.length).trim() || containerLabels[`${type}Label`] || type.charAt(0).toUpperCase() + type.slice(1);
return `<div class="custom-block ${type}">\n<div class="custom-block-title">${title}</div>\n`;
} else {
// Closing tag
return `</div>\n`;
}
}
});
});