a8907d6cc0
Make classic text chapters easier to read on phones with responsive styles, collapsed sidebar groups, and less intrusive install prompts on doc pages. Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
2.9 KiB
TypeScript
113 lines
2.9 KiB
TypeScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
|
|
const EXCLUDE_DIRS = new Set([
|
|
'node_modules',
|
|
'.vitepress',
|
|
'server',
|
|
'assets',
|
|
'images',
|
|
'.git',
|
|
'金瓶梅',
|
|
'黄帝内经',
|
|
'健康学习到150岁 - 人体系统调优不完全指南',
|
|
'梅花',
|
|
])
|
|
|
|
type SidebarItem = {
|
|
text: string
|
|
link?: string
|
|
collapsed?: boolean
|
|
items?: SidebarItem[]
|
|
}
|
|
|
|
function naturalCompare(a: string, b: string) {
|
|
return a.localeCompare(b, 'zh-CN', { numeric: true, sensitivity: 'base' })
|
|
}
|
|
|
|
function titleFromFilename(name: string) {
|
|
const base = name.replace(/\.md$/i, '')
|
|
if (/^readme$/i.test(base)) return '导读'
|
|
return base
|
|
}
|
|
|
|
function toLink(relativePath: string) {
|
|
const normalized = relativePath.replace(/\\/g, '/').replace(/\.md$/i, '')
|
|
if (normalized.endsWith('/README')) {
|
|
return `/${normalized.slice(0, -'/README'.length)}/`
|
|
}
|
|
return `/${normalized}`
|
|
}
|
|
|
|
function collectItems(dir: string, relativeDir: string): SidebarItem[] {
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
|
const subdirs = entries.filter((entry) => entry.isDirectory())
|
|
|
|
const items: SidebarItem[] = files
|
|
.sort((a, b) => naturalCompare(a.name, b.name))
|
|
.map((file) => ({
|
|
text: titleFromFilename(file.name),
|
|
link: toLink(path.join(relativeDir, file.name)),
|
|
}))
|
|
|
|
for (const subdir of subdirs.sort((a, b) => naturalCompare(a.name, b.name))) {
|
|
const subRelative = path.join(relativeDir, subdir.name)
|
|
const subItems = collectItems(path.join(dir, subdir.name), subRelative)
|
|
if (subItems.length === 0) continue
|
|
items.push({
|
|
text: subdir.name,
|
|
collapsed: true,
|
|
items: subItems,
|
|
})
|
|
}
|
|
|
|
return items
|
|
}
|
|
|
|
export function generateSidebar(): Record<string, SidebarItem[]> {
|
|
const groups: SidebarItem[] = []
|
|
|
|
const rootFiles = fs
|
|
.readdirSync(root, { withFileTypes: true })
|
|
.filter(
|
|
(entry) =>
|
|
entry.isFile() &&
|
|
entry.name.endsWith('.md') &&
|
|
entry.name !== 'README.md' &&
|
|
entry.name !== 'index.md',
|
|
)
|
|
.sort((a, b) => naturalCompare(a.name, b.name))
|
|
|
|
if (rootFiles.length > 0) {
|
|
groups.push({
|
|
text: '典籍',
|
|
collapsed: true,
|
|
items: rootFiles.map((file) => ({
|
|
text: titleFromFilename(file.name),
|
|
link: toLink(file.name),
|
|
})),
|
|
})
|
|
}
|
|
|
|
const dirs = fs
|
|
.readdirSync(root, { withFileTypes: true })
|
|
.filter((entry) => entry.isDirectory() && !EXCLUDE_DIRS.has(entry.name))
|
|
.sort((a, b) => naturalCompare(a.name, b.name))
|
|
|
|
for (const dir of dirs) {
|
|
const items = collectItems(path.join(root, dir.name), dir.name)
|
|
if (items.length === 0) continue
|
|
groups.push({
|
|
text: dir.name,
|
|
collapsed: true,
|
|
items,
|
|
})
|
|
}
|
|
|
|
return { '/': groups }
|
|
}
|