Skip to content

环境搭建的一些坑

📅 发表于 2025/05/16
🔄 更新于 2025/05/16
👁️ 次访问
📝 0 字
0 分钟
环境搭建
#vitpress博客
#busuanzi
#图片缩放

vitepress安装过程

安装命令

安装命令
shell

# 安装
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

Index.md

markdown
---
# 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

busuanzi

index.ts配置,切记有一些内容。

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,需要安装插件

vue
<script setup>
</script>

<template>
  <div class="busuanzi">
    总访客数: <span id="busuanzi_value_site_uv" /> &nbsp; · &nbsp;  总访问量: <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>

文章meta信息

  • MyDocInfo.vue
  • 在index.ts中注册和插入使用,改动到具体位置。
ts
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插槽
    })
  },
vue
<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>

插件安装

自动侧边栏

shell
# 1. 安装
pnpm add -D vitepress-sidebar

# 2. copy configs/index.ts, nav.ts, sidebar.ts
ts
/* configs/index.ts */
export * from './nav'
export * from './sidebar'
ts
/* 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,

  }
];
ts
/* 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

ts
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));

mark及container渲染

shell
bun add -D markdown-it-mark
bun add -D markdownItContainer

config.mts,主要是渲染以及css配置(custom-block)。

ts
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));

图片缩放

ts
bun add -D medium-zoom

// import
// 图片缩放

// 配置css 即可,看原文

代码组

安装说明
shell
# 安装库
bun add -D vitepress-plugin-group-icons

# 配置css

# index.ts引用css
import 'virtual:group-icons.css'

# config.mts里配置
md.use(groupIconMdPlugin);

vite: { 
    plugins: [
      groupIconVitePlugin() //代码组图标
    ],
  },

其他配置

链接前面加图标

唯知笔记

代码加图标

核心代码
ts
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

config.mts

typescript
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支持高亮语法

  • 打开设置,支持
  • mac 安装autohotkey。brew install --cask hammerspoon
  • 配置脚本,再run即可。
lua
-- ==高亮文字== 快捷键
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即可。

python
# 安装插件
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`;
            }
          }
        });
      });
总访客数:   ·   总访问量:
PLM's Blog @ 2016 - 2025