docs: add llms support

This commit is contained in:
pengzhanbo 2025-06-14 13:28:05 +08:00
parent ebd316a415
commit fefe83b0bc
6 changed files with 220 additions and 2 deletions

View File

@ -3,7 +3,9 @@ import fs from 'node:fs'
import path from 'node:path'
import { viteBundler } from '@vuepress/bundler-vite'
import { addViteOptimizeDepsInclude, addViteSsrExternal } from '@vuepress/helper'
import { llmsPlugin } from '@vuepress/plugin-llms'
import { defineUserConfig } from 'vuepress'
import { tocGetter } from './llmstxtTOC.js'
import { theme } from './theme.js'
const pnpmWorkspace = fs.readFileSync(path.resolve(__dirname, '../../pnpm-workspace.yaml'), 'utf-8')
@ -45,6 +47,16 @@ export default defineUserConfig({
'~/composables': path.resolve(__dirname, './themes/composables'),
},
plugins: [
llmsPlugin({
llmsTxtTemplateGetter: {
description: '一个简约易用的,功能丰富的 vuepress 文档&博客 主题',
details: '',
toc: tocGetter,
},
}),
],
bundler: viteBundler(),
shouldPrefetch: false,

View File

@ -0,0 +1,107 @@
import type { LLMPage, LLMState } from '@vuepress/plugin-llms'
import type { ThemeSidebarItem } from 'vuepress-theme-plume'
import { generateTOCLink as rawGenerateTOCLink } from '@vuepress/plugin-llms'
import { ensureEndingSlash, ensureLeadingSlash } from 'vuepress/shared'
import { zhNotes } from './notes/zh/index.js'
const noteNames = {
'/guide/': '指南',
'/config/': '配置',
'/tools/': '工具',
}
function normalizePath(prefix: string, path = ''): string {
if (path.startsWith('/'))
return path
return `${ensureEndingSlash(prefix)}${path}`
}
export function tocGetter(llmPages: LLMPage[], llmState: LLMState): string {
let tableOfContent = ''
const usagePages: LLMPage[] = []
// Blog
tableOfContent += `### 博客\n\n`
const blogList: string[] = []
llmPages.forEach((page) => {
if (page.path.startsWith('/article/') || page.path.startsWith('/blog/')) {
usagePages.push(page)
blogList.push(rawGenerateTOCLink(page, llmState))
}
})
tableOfContent += `${blogList.filter(Boolean).join('')}\n`
const generateTOCLink = (path: string): string => {
const filepath = path.endsWith('/') ? `${path}README.md` : path.endsWith('.md') ? path : `${path || 'README'}.md`
const link = path.endsWith('/') ? `${path}index.html` : `${path}.html`
const page = llmPages.find((item) => {
return ensureLeadingSlash(item.filePathRelative || '') === filepath || link === item.path
})
if (page) {
usagePages.push(page)
return rawGenerateTOCLink(page, llmState)
}
return ''
}
const processAutoSidebar = (prefix: string): string[] => {
const list: string[] = []
llmPages.forEach((page) => {
if (page.path.startsWith(prefix)) {
usagePages.push(page)
list.push(generateTOCLink(page.path))
}
})
return list.filter(Boolean)
}
const processSidebar = (items: (string | ThemeSidebarItem)[], prefix: string): string[] => {
const result: string[] = []
items.forEach((item) => {
if (typeof item === 'string') {
result.push(generateTOCLink(normalizePath(prefix, item)))
}
else {
if (item.link) {
result.push(generateTOCLink(normalizePath(prefix, item.link)))
}
if (item.items === 'auto') {
result.push(...processAutoSidebar(normalizePath(prefix, item.prefix)))
}
else if (item.items?.length) {
result.push(...processSidebar(item.items, normalizePath(prefix, item.prefix)))
}
}
})
return result
}
// Notes
zhNotes.notes.forEach(({ dir, link, sidebar = [] }) => {
tableOfContent += `### ${noteNames[link]}\n\n`
if (sidebar === 'auto') {
tableOfContent += `${processAutoSidebar(link).join('')}\n`
}
else if (sidebar.length) {
const index = llmPages.findIndex(page => page.path === link)
if (index !== -1) {
usagePages.push(llmPages[index])
tableOfContent += rawGenerateTOCLink(llmPages[index], llmState)
}
tableOfContent += `${processSidebar(sidebar, normalizePath('/notes/', dir)).join('')}\n`
}
})
// Others
const unUsagePages = llmPages.filter(page => !usagePages.includes(page))
if (unUsagePages.length) {
tableOfContent += '### Others\n\n'
tableOfContent += unUsagePages
.map(page => rawGenerateTOCLink(page, llmState))
.join('')
}
return tableOfContent
}

View File

@ -15,6 +15,7 @@
"@iconify/json": "catalog:peer",
"@simonwep/pickr": "catalog:dev",
"@vuepress/bundler-vite": "catalog:vuepress",
"@vuepress/plugin-llms": "catalog:vuepress",
"@vuepress/shiki-twoslash": "catalog:vuepress",
"chart.js": "catalog:prod",
"echarts": "catalog:prod",

View File

@ -225,8 +225,13 @@ function fetchImageSize(src: string): Promise<ImgSize> {
catch {}
}
const { width, height } = imageSize(Buffer.concat(chunks))
resolve({ width: width!, height: height! })
try {
const { width, height } = imageSize(Buffer.concat(chunks))
resolve({ width: width!, height: height! })
}
catch {
resolve({ width: 0, height: 0 })
}
})
.on('error', () => resolve({ width: 0, height: 0 }))
})

92
pnpm-lock.yaml generated
View File

@ -321,6 +321,9 @@ catalogs:
'@vuepress/plugin-git':
specifier: 2.0.0-rc.108
version: 2.0.0-rc.108
'@vuepress/plugin-llms':
specifier: 2.0.0-rc.108
version: 2.0.0-rc.108
'@vuepress/plugin-markdown-hint':
specifier: 2.0.0-rc.108
version: 2.0.0-rc.108
@ -515,6 +518,9 @@ importers:
'@vuepress/bundler-vite':
specifier: catalog:vuepress
version: 2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0)
'@vuepress/plugin-llms':
specifier: catalog:vuepress
version: 2.0.0-rc.108(typescript@5.8.3)(vuepress@2.0.0-rc.23(@vuepress/bundler-vite@2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0))(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))
'@vuepress/shiki-twoslash':
specifier: catalog:vuepress
version: 2.0.0-rc.108(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))(vuepress@2.0.0-rc.23(@vuepress/bundler-vite@2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0))(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))
@ -2843,6 +2849,11 @@ packages:
peerDependencies:
vuepress: 2.0.0-rc.23
'@vuepress/plugin-llms@2.0.0-rc.108':
resolution: {integrity: sha512-DB337N7Qm0XbYQVYSNnIPBI3olkogdJrieRJbiCPCdAUQ1RqExq3wakapI5PjEzuNAvmh18a2Ex596XkzmA2OQ==}
peerDependencies:
vuepress: 2.0.0-rc.23
'@vuepress/plugin-markdown-hint@2.0.0-rc.108':
resolution: {integrity: sha512-JWTStLHudeiYd4aZQ9uRjm23IX8x9/LlSdZqX9iYW+igsSqOgNJfOuGX7W1HpEgPZdwXRwbjdHHa9Ak5Z7JDmw==}
peerDependencies:
@ -3205,6 +3216,15 @@ packages:
engines: {node: '>=18'}
hasBin: true
byte-size@9.0.1:
resolution: {integrity: sha512-YLe9x3rabBrcI0cueCdLS2l5ONUKywcRpTs02B8KP9/Cimhj7o3ZccGrPnRvcbyHMbb7W79/3MUJl7iGgTXKEw==}
engines: {node: '>=12.17'}
peerDependencies:
'@75lb/nature': latest
peerDependenciesMeta:
'@75lb/nature':
optional: true
c12@3.0.3:
resolution: {integrity: sha512-uC3MacKBb0Z15o5QWCHvHWj5Zv34pGQj9P+iXKSpTuSGFS0KKhUWf4t9AJ+gWjYOdmWCPEGpEzm8sS0iqbpo1w==}
peerDependencies:
@ -5354,6 +5374,10 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
millify@6.1.0:
resolution: {integrity: sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA==}
hasBin: true
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@ -5900,6 +5924,15 @@ packages:
rehype-stringify@10.0.1:
resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==}
remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
remark@15.0.1:
resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@ -6551,6 +6584,9 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
tokenx@1.0.1:
resolution: {integrity: sha512-MhOngUHRuVE0CHP4cNEZ/XpdXETFL65nJpEvoTW+VYPuXsT/MTeNj+UNnekNsnxecmj2DEvUYPebqz+CsPTUSg==}
toml-eslint-parser@0.10.0:
resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -6687,6 +6723,9 @@ packages:
unist-util-position@5.0.0:
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
unist-util-remove@4.0.0:
resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==}
unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
@ -9217,6 +9256,21 @@ snapshots:
transitivePeerDependencies:
- typescript
'@vuepress/plugin-llms@2.0.0-rc.108(typescript@5.8.3)(vuepress@2.0.0-rc.23(@vuepress/bundler-vite@2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0))(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))':
dependencies:
'@vuepress/helper': 2.0.0-rc.108(typescript@5.8.3)(vuepress@2.0.0-rc.23(@vuepress/bundler-vite@2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0))(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))
byte-size: 9.0.1
gray-matter: 4.0.3
millify: 6.1.0
remark: 15.0.1
tokenx: 1.0.1
unist-util-remove: 4.0.0
vuepress: 2.0.0-rc.23(@vuepress/bundler-vite@2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0))(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))
transitivePeerDependencies:
- '@75lb/nature'
- supports-color
- typescript
'@vuepress/plugin-markdown-hint@2.0.0-rc.108(markdown-it@14.1.0)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))(vuepress@2.0.0-rc.23(@vuepress/bundler-vite@2.0.0-rc.23(@types/node@22.15.30)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.89.1)(sass@1.89.1)(stylus@0.64.0)(typescript@5.8.3)(yaml@2.8.0))(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))':
dependencies:
'@mdit/plugin-alert': 0.21.0(markdown-it@14.1.0)
@ -9631,6 +9685,8 @@ snapshots:
transitivePeerDependencies:
- magicast
byte-size@9.0.1: {}
c12@3.0.3(magicast@0.3.5):
dependencies:
chokidar: 4.0.3
@ -12207,6 +12263,10 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
millify@6.1.0:
dependencies:
yargs: 17.7.2
mime-db@1.52.0: {}
mime-types@2.1.35:
@ -12713,6 +12773,30 @@ snapshots:
hast-util-to-html: 9.0.5
unified: 11.0.5
remark-parse@11.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-from-markdown: 2.0.2
micromark-util-types: 2.0.2
unified: 11.0.5
transitivePeerDependencies:
- supports-color
remark-stringify@11.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-to-markdown: 2.1.2
unified: 11.0.5
remark@15.0.1:
dependencies:
'@types/mdast': 4.0.4
remark-parse: 11.0.0
remark-stringify: 11.0.0
unified: 11.0.5
transitivePeerDependencies:
- supports-color
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
@ -13376,6 +13460,8 @@ snapshots:
dependencies:
is-number: 7.0.0
tokenx@1.0.1: {}
toml-eslint-parser@0.10.0:
dependencies:
eslint-visitor-keys: 3.4.3
@ -13500,6 +13586,12 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
unist-util-remove@4.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
unist-util-stringify-position@4.0.0:
dependencies:
'@types/unist': 3.0.3

View File

@ -126,6 +126,7 @@ catalogs:
'@vuepress/plugin-copy-code': 2.0.0-rc.108
'@vuepress/plugin-docsearch': 2.0.0-rc.108
'@vuepress/plugin-git': 2.0.0-rc.108
'@vuepress/plugin-llms': 2.0.0-rc.108
'@vuepress/plugin-markdown-hint': 2.0.0-rc.108
'@vuepress/plugin-markdown-image': 2.0.0-rc.108
'@vuepress/plugin-markdown-include': 2.0.0-rc.108