Signed-off-by: Anna Beiyan <131662099+AnnaBeiyan@users.noreply.github.com> Co-authored-by: Anna Beiyan <131662099+AnnaBeiyan@users.noreply.github.com> Co-authored-by: Neko Ayaka <neko@ayaka.moe>pull/358/head
parent
25eaa2ba69
commit
d31461684e
|
@ -1,18 +1,18 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// Generated by unplugin-vue-components
|
// Generated by unplugin-vue-components
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AppearanceToggle: typeof import('./theme/components/AppearanceToggle.vue')['default']
|
AppearanceToggle: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/AppearanceToggle.vue')['default']
|
||||||
AppFooter: typeof import('./theme/components/AppFooter.vue')['default']
|
AppFooter: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/AppFooter.vue')['default']
|
||||||
AppSBox: typeof import('./theme/components/AppSBox.vue')['default']
|
AppSBox: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/AppSBox.vue')['default']
|
||||||
ArticlesMenu: typeof import('./theme/components/ArticlesMenu.vue')['default']
|
ArticlesMenu: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/ArticlesMenu.vue')['default']
|
||||||
CopyrightInfo: typeof import('./theme/components/CopyrightInfo.vue')['default']
|
CopyrightInfo: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/CopyrightInfo.vue')['default']
|
||||||
HomeContent: typeof import('./theme/components/HomeContent.vue')['default']
|
HomeContent: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/HomeContent.vue')['default']
|
||||||
PageInfo: typeof import('./theme/components/PageInfo.vue')['default']
|
PageInfo: typeof import('./../../packages/vitepress-theme-project-trans/dist/components/PageInfo.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,202 +1,101 @@
|
||||||
import { defineConfig } from 'vitepress'
|
import genConfig from '@project-trans/vitepress-theme-project-trans/config'
|
||||||
import nav from './nav'
|
import type { SidebarOptions } from '@project-trans/vitepress-theme-project-trans/theme'
|
||||||
import mdPangu from 'markdown-it-pangu'
|
import type { ThemeContext } from '@project-trans/vitepress-theme-project-trans/utils'
|
||||||
import katex from 'markdown-it-katex'
|
import { withThemeContext } from '@project-trans/vitepress-theme-project-trans/utils'
|
||||||
import footnote from 'markdown-it-footnote'
|
import type { DefaultTheme } from 'vitepress'
|
||||||
import { sidebar } from './sidebar'
|
|
||||||
import { rootDir, githubRepoLink } from './meta'
|
type NavConfig = DefaultTheme.Config['nav']
|
||||||
import { readFileSync, statSync } from 'node:fs'
|
|
||||||
import { join } from 'node:path'
|
const nav: NavConfig = [
|
||||||
const siteTitle = 'RLE.wiki'
|
{
|
||||||
const siteDescription = '一份 RLE 指北'
|
text: '大学指南',
|
||||||
|
link: '/campus/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '时尚护理',
|
||||||
|
link: '/fashion/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '安全防护',
|
||||||
|
link: '/personal-safety/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '其它',
|
||||||
|
link: '/others/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '贡献指南',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
text: '校园版块投稿指南',
|
||||||
|
link: '/contributor-guide/campus.md',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '其他投稿指南',
|
||||||
|
link: '/contributor-guide/other.md',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '校园版块贡献模板',
|
||||||
|
link: '/contributor-guide/CampusTemplate.md',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const baseConfig = {
|
||||||
|
useTitleFromFrontmatter: true,
|
||||||
|
useFolderTitleFromIndexFile: true,
|
||||||
|
useFolderLinkFromIndexFile: true,
|
||||||
|
excludeFilesByFrontmatterFieldName: true,
|
||||||
|
collapsed: true,
|
||||||
|
documentRootPath: '/docs',
|
||||||
|
} satisfies Partial<SidebarOptions>
|
||||||
|
|
||||||
|
const sidebarOptions = [
|
||||||
|
// 大学指南
|
||||||
|
{
|
||||||
|
...baseConfig,
|
||||||
|
scanStartPath: 'campus',
|
||||||
|
resolvePath: '/campus/',
|
||||||
|
},
|
||||||
|
// 贡献指南
|
||||||
|
{
|
||||||
|
...baseConfig,
|
||||||
|
scanStartPath: 'contributor-guide',
|
||||||
|
resolvePath: '/contributor-guide/',
|
||||||
|
},
|
||||||
|
// 时尚护理
|
||||||
|
{
|
||||||
|
...baseConfig,
|
||||||
|
scanStartPath: 'fashion',
|
||||||
|
resolvePath: '/fashion/',
|
||||||
|
},
|
||||||
|
// 安全防护
|
||||||
|
{
|
||||||
|
...baseConfig,
|
||||||
|
scanStartPath: 'personal-safety',
|
||||||
|
resolvePath: '/personal-safety/',
|
||||||
|
},
|
||||||
|
// 其它
|
||||||
|
{
|
||||||
|
...baseConfig,
|
||||||
|
scanStartPath: 'others',
|
||||||
|
resolvePath: '/others/',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const themeConfig: ThemeContext = {
|
||||||
|
siteTitle: 'RLE.wiki',
|
||||||
|
siteDescription: '一份 RLE 指北',
|
||||||
|
/** Repo */
|
||||||
|
githubRepoLink: 'https://github.com/project-trans/RLE-wiki',
|
||||||
|
/** vitepress 根目录 */
|
||||||
|
rootDir: 'docs',
|
||||||
|
/** 文档所在目录(目前似未使用此项) */
|
||||||
|
include: ['campus', 'contributor-guide', 'fashion'],
|
||||||
|
nav,
|
||||||
|
sidebarOptions,
|
||||||
|
}
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
// https://vitepress.dev/reference/site-config
|
||||||
export default defineConfig({
|
export default withThemeContext(themeConfig, genConfig)
|
||||||
lang: 'zh-CN',
|
|
||||||
title: siteTitle,
|
|
||||||
cleanUrls: true,
|
|
||||||
markdown: {
|
|
||||||
config(md) {
|
|
||||||
md.use(mdPangu);
|
|
||||||
md.use(footnote);
|
|
||||||
md.use(katex);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dir: rootDir,
|
|
||||||
head: [
|
|
||||||
['link', { rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" }],
|
|
||||||
['link', { rel: "icon", type: "image/png", sizes: "32x32", href: "/favicon-32x32.png" }],
|
|
||||||
['link', { rel: "icon", type: "image/png", sizes: "16x16", href: "/favicon-16x16.png" }],
|
|
||||||
['link', { rel: "manifest", href: "/site.webmanifest" }],
|
|
||||||
['meta', { name: "msapplication-TileColor", content: "#4c4c4c" }],
|
|
||||||
['meta', { name: "theme-color", content: "#ffffff" }],
|
|
||||||
['meta', { property: 'og:site_name', content: siteTitle }],
|
|
||||||
],
|
|
||||||
themeConfig: {
|
|
||||||
// https://vitepress.dev/reference/default-theme-config
|
|
||||||
siteTitle: false,
|
|
||||||
logo: {
|
|
||||||
src: '/logo-horizontal.svg',
|
|
||||||
alt: 'Logo: RLE.wiki',
|
|
||||||
},
|
|
||||||
nav,
|
|
||||||
sidebar,
|
|
||||||
socialLinks: [
|
|
||||||
{ icon: 'github', link: githubRepoLink }
|
|
||||||
],
|
|
||||||
editLink: {
|
|
||||||
pattern: `${githubRepoLink}/edit/main/docs/:path`,
|
|
||||||
text: '在 GitHub 上编辑此页面', // label localization
|
|
||||||
},
|
|
||||||
// label localization
|
|
||||||
outline: { label: '本页大纲', level: 'deep' },
|
|
||||||
lastUpdated: { text: '最后更新' },
|
|
||||||
darkModeSwitchLabel: '深色模式',
|
|
||||||
sidebarMenuLabel: '目录',
|
|
||||||
returnToTopLabel: '返回顶部',
|
|
||||||
docFooter: {
|
|
||||||
prev: '上一页',
|
|
||||||
next: '下一页',
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
provider: 'local',
|
|
||||||
options: {
|
|
||||||
locales: {
|
|
||||||
root: {
|
|
||||||
translations: {
|
|
||||||
button: {
|
|
||||||
buttonText: '搜索文档',
|
|
||||||
buttonAriaLabel: '搜索文档',
|
|
||||||
},
|
|
||||||
modal: {
|
|
||||||
noResultsText: '无法找到相关结果',
|
|
||||||
resetButtonTitle: '清除查询条件',
|
|
||||||
footer: {
|
|
||||||
selectText: '选择',
|
|
||||||
navigateText: '切换',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Add title field in frontmatter to search
|
|
||||||
// You can exclude a page from search by adding search: false to the page's frontmatter.
|
|
||||||
_render(src, env, md) {
|
|
||||||
if (env.frontmatter?.search === false) return ''
|
|
||||||
let html = md.render(src, env)
|
|
||||||
if (env.frontmatter?.title)
|
|
||||||
html = md.render(`# ${env.frontmatter.title}\n`) + html
|
|
||||||
return html
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
transformHead: (context) => {
|
|
||||||
const head = [...context.head] || []
|
|
||||||
|
|
||||||
const pageSourceFilePath = join(rootDir, context.pageData.filePath)
|
|
||||||
const pageSourceFileStat = statSync(join(rootDir, context.pageData.filePath))
|
|
||||||
|
|
||||||
if (pageSourceFileStat.isDirectory()) {
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{
|
|
||||||
property: 'og:title',
|
|
||||||
content: siteTitle,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{
|
|
||||||
name: 'description',
|
|
||||||
content: siteDescription,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
return head
|
|
||||||
}
|
|
||||||
|
|
||||||
let pageSourceFileContent = readFileSync(pageSourceFilePath, { encoding: 'utf-8' })
|
|
||||||
|
|
||||||
// remove all frontmatter
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/---[\s\S]*?---/, '')
|
|
||||||
|
|
||||||
// remove markdown heading markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/^(#+)\s+(.*)/gm, ' $2 ')
|
|
||||||
// remove markdown link markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\[([^\]]+)\]\([^)]+\)/gm, ' $1 ')
|
|
||||||
// remove markdown image markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\!\[([^\]]+)\]\([^)]+\)/gm, ' $1 ')
|
|
||||||
// remove markdown reference link markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\[.*]/gm, '')
|
|
||||||
// remove markdown bold markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\*\*([^*]+)\*\*/gm, ' $1 ')
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/__([^*]+)__/gm, ' $1 ')
|
|
||||||
// remove markdown italic markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\*([^*]+)\*/gm, ' $1 ')
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/_([^*]+)_/gm, ' $1 ')
|
|
||||||
// remove markdown code markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/`([^`]+)`/gm, ' $1 ')
|
|
||||||
// remove markdown code block markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/```([^`]+)```/gm, ' $1 ')
|
|
||||||
// remove markdown table header markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\|:?-+:?\|/gm, '')
|
|
||||||
// remove markdown table cell markup but keep the text content
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/\|([^|]+)\|/gm, ' $1 ')
|
|
||||||
|
|
||||||
// remove specific html tags completely
|
|
||||||
const tags = ['']
|
|
||||||
tags.forEach((tag) => {
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(new RegExp(`<${tag}[^>]*>[\\s\\S]*?<\\/${tag}>`, 'g'), '')
|
|
||||||
})
|
|
||||||
|
|
||||||
// remove specific html tags but keep the text content
|
|
||||||
const tagsToKeepContent = ['u', 'Containers', 'img', 'a']
|
|
||||||
tagsToKeepContent.forEach((tag) => {
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'g'), ' $1 ')
|
|
||||||
})
|
|
||||||
|
|
||||||
// remove all new lines (either \r, \n)
|
|
||||||
pageSourceFileContent = pageSourceFileContent.replace(/[\r|\n]/gm, '')
|
|
||||||
|
|
||||||
// calculate the first 200 characters of the page content
|
|
||||||
let pageContent = pageSourceFileContent.slice(0, 200)
|
|
||||||
// trim space
|
|
||||||
pageContent = pageContent.trim()
|
|
||||||
// if pageSourceFileContent is longer than 200 characters, add ellipsis
|
|
||||||
if (pageSourceFileContent.length > 100)
|
|
||||||
pageContent += '...'
|
|
||||||
|
|
||||||
if (context.pageData.frontmatter?.layout === 'home') {
|
|
||||||
pageContent = context.pageData.frontmatter?.hero?.tagline ?? siteDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{ name: 'description', content: pageContent },
|
|
||||||
])
|
|
||||||
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{ property: 'og:title', content: context.title },
|
|
||||||
])
|
|
||||||
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{ property: 'og:description', content: pageContent },
|
|
||||||
])
|
|
||||||
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{ property: 'og:title', content: context.title },
|
|
||||||
])
|
|
||||||
|
|
||||||
head.push([
|
|
||||||
'meta',
|
|
||||||
{ property: 'twitter:description', content: pageContent },
|
|
||||||
])
|
|
||||||
|
|
||||||
return head
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
declare module 'markdown-it-pangu' {
|
declare module 'markdown-it-pangu' {
|
||||||
import { PluginSimple } from 'markdown-it'
|
import type { PluginSimple } from 'markdown-it'
|
||||||
|
|
||||||
const pangu: PluginSimple
|
const pangu: PluginSimple
|
||||||
export default pangu
|
export default pangu
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'markdown-it-katex' {
|
declare module 'markdown-it-katex' {
|
||||||
import { PluginSimple } from 'markdown-it'
|
import type { PluginSimple } from 'markdown-it'
|
||||||
|
|
||||||
const katex: PluginSimple
|
const katex: PluginSimple
|
||||||
export default katex
|
export default katex
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
/** Repo */
|
|
||||||
export const githubRepoLink = 'https://github.com/project-trans/RLE-wiki'
|
|
||||||
/** vitepress 根目录 */
|
|
||||||
export const rootDir = 'docs'
|
|
||||||
/** 文档所在目录 */
|
|
||||||
export const include = ['campus', 'contributor-guide', 'fashion']
|
|
|
@ -1,41 +0,0 @@
|
||||||
import type { DefaultTheme } from 'vitepress'
|
|
||||||
|
|
||||||
type NavConfig = DefaultTheme.Config['nav']
|
|
||||||
|
|
||||||
const nav: NavConfig = [
|
|
||||||
{
|
|
||||||
text: '大学指南',
|
|
||||||
link: '/campus/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '时尚护理',
|
|
||||||
link: '/fashion/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '安全防护',
|
|
||||||
link: '/personal-safety/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '其它',
|
|
||||||
link: '/others/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '贡献指南',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
text: '校园版块投稿指南',
|
|
||||||
link: '/contributor-guide/campus.md',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '其他投稿指南',
|
|
||||||
link: '/contributor-guide/other.md',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '校园版块贡献模板',
|
|
||||||
link: '/contributor-guide/CampusTemplate.md',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export default nav
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { ContentData, createContentLoader, defineLoader } from "vitepress";
|
|
||||||
|
|
||||||
let contentLoader = createContentLoader('/**/*.md')
|
|
||||||
|
|
||||||
export interface Node<T> {
|
|
||||||
value: T | null;
|
|
||||||
children: { [key: string]: Node<T> };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Trie<T> {
|
|
||||||
root: Node<T>;
|
|
||||||
insert(path: string[], value: T, node: Node<T>): void;
|
|
||||||
insert(path: string[], value: T): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const data: Trie<Record<string, any>>
|
|
||||||
export { data };
|
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
export default defineLoader({
|
|
||||||
watch: contentLoader.watch,
|
|
||||||
async load(): Promise<Trie<Record<string, any>>> {
|
|
||||||
let raw: ContentData[] = await contentLoader.load()
|
|
||||||
let trie: Trie<Record<string, any>> = {
|
|
||||||
root: { value: null, children: {} },
|
|
||||||
|
|
||||||
insert(this: Trie<Record<string, any>>, path, value, node: Node<Record<string, any>> = this.root) {
|
|
||||||
if (path.length === 0) {
|
|
||||||
node.value = value
|
|
||||||
} else if (path.length === 1) {
|
|
||||||
if (!(path[0] in node.children)) {
|
|
||||||
node.children[path[0]] = { value: value, children: {} }
|
|
||||||
} else {
|
|
||||||
node.children[path[0]].value = value
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!(path[0] in node.children)) {
|
|
||||||
let new_node = { value: null, children: {} }
|
|
||||||
this.insert(path.slice(1), value, new_node)
|
|
||||||
node.children[path[0]] = new_node
|
|
||||||
} else {
|
|
||||||
this.insert(path.slice(1), value, node.children[path[0]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let v of raw) {
|
|
||||||
let frontmatter = v.frontmatter ?? null
|
|
||||||
let copyright = frontmatter?.copyright ?? null
|
|
||||||
if (copyright !== null) {
|
|
||||||
trie.insert(v.url.split('/').filter((item, _index, _array) => item !== ''), {
|
|
||||||
author: frontmatter.author ?? null,
|
|
||||||
title: frontmatter.title ?? null,
|
|
||||||
copyright: copyright
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return trie;
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,75 +0,0 @@
|
||||||
import type {
|
|
||||||
SidebarItem,
|
|
||||||
SidebarMultiItem,
|
|
||||||
} from 'vitepress-sidebar'
|
|
||||||
import { generateSidebar } from 'vitepress-sidebar'
|
|
||||||
import type Options from 'vitepress-sidebar'
|
|
||||||
|
|
||||||
export const sidebar = generate()
|
|
||||||
|
|
||||||
function generate() {
|
|
||||||
const baseConfig = {
|
|
||||||
useTitleFromFrontmatter: true,
|
|
||||||
useFolderTitleFromIndexFile: true,
|
|
||||||
useFolderLinkFromIndexFile: true,
|
|
||||||
excludeFilesByFrontmatter: true,
|
|
||||||
collapsed: true,
|
|
||||||
} satisfies Partial<Options>
|
|
||||||
|
|
||||||
const sidebar = generateSidebar([
|
|
||||||
// 大学指南
|
|
||||||
{
|
|
||||||
...baseConfig,
|
|
||||||
documentRootPath: '/docs',
|
|
||||||
scanStartPath: 'campus',
|
|
||||||
resolvePath: '/campus/',
|
|
||||||
},
|
|
||||||
// 贡献指南
|
|
||||||
{
|
|
||||||
...baseConfig,
|
|
||||||
documentRootPath: '/docs',
|
|
||||||
scanStartPath: 'contributor-guide',
|
|
||||||
resolvePath: '/contributor-guide/',
|
|
||||||
},
|
|
||||||
// 时尚护理
|
|
||||||
{
|
|
||||||
...baseConfig,
|
|
||||||
documentRootPath: '/docs',
|
|
||||||
scanStartPath: 'fashion',
|
|
||||||
resolvePath: '/fashion/',
|
|
||||||
},
|
|
||||||
// 安全防护
|
|
||||||
{
|
|
||||||
...baseConfig,
|
|
||||||
documentRootPath: '/docs',
|
|
||||||
scanStartPath: 'personal-safety',
|
|
||||||
resolvePath: '/personal-safety/',
|
|
||||||
},
|
|
||||||
// 其它
|
|
||||||
{
|
|
||||||
...baseConfig,
|
|
||||||
documentRootPath: '/docs',
|
|
||||||
scanStartPath: 'others',
|
|
||||||
resolvePath: '/others/',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
for (const key in sidebar) {
|
|
||||||
const sidebarMultiItem: SidebarMultiItem = (sidebar as any)[key]
|
|
||||||
sidebarMultiItem.items.sort(sidebarTitleSorter)
|
|
||||||
}
|
|
||||||
return sidebar
|
|
||||||
}
|
|
||||||
|
|
||||||
function sidebarTitleSorter(infoA: SidebarItem, infoB: SidebarItem): number {
|
|
||||||
const textA = infoA.text
|
|
||||||
const textB = infoB.text
|
|
||||||
if (textA === undefined || textB === undefined)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
const infoANfc = textA.normalize('NFC')
|
|
||||||
const infoBNfc = textB.normalize('NFC')
|
|
||||||
return infoANfc.localeCompare(infoBNfc, 'zh', {
|
|
||||||
numeric: true,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,63 +1,9 @@
|
||||||
// https://vitepress.dev/guide/custom-theme
|
// https://vitepress.dev/guide/custom-theme
|
||||||
import type { Theme } from 'vitepress'
|
import PtjsTheme from '@project-trans/vitepress-theme-project-trans/theme'
|
||||||
import DefaultTheme from 'vitepress/theme'
|
|
||||||
import { h } from 'vue'
|
|
||||||
|
|
||||||
import {
|
|
||||||
NolebaseEnhancedReadabilitiesMenu,
|
|
||||||
NolebaseEnhancedReadabilitiesPlugin,
|
|
||||||
NolebaseEnhancedReadabilitiesScreenMenu,
|
|
||||||
} from '@nolebase/vitepress-plugin-enhanced-readabilities/client'
|
|
||||||
|
|
||||||
import {
|
|
||||||
NolebaseHighlightTargetedHeading,
|
|
||||||
} from '@nolebase/vitepress-plugin-highlight-targeted-heading/client'
|
|
||||||
|
|
||||||
import {
|
|
||||||
NolebaseGitChangelogPlugin,
|
|
||||||
} from '@nolebase/vitepress-plugin-git-changelog/client'
|
|
||||||
import AppearanceToggle from './components/AppearanceToggle.vue'
|
|
||||||
|
|
||||||
import '@nolebase/vitepress-plugin-enhanced-readabilities/client/style.css'
|
|
||||||
import '@nolebase/vitepress-plugin-git-changelog/client/style.css'
|
|
||||||
import '@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css'
|
|
||||||
|
|
||||||
import 'uno.css'
|
import 'uno.css'
|
||||||
import AppFooter from './components/AppFooter.vue'
|
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extends: DefaultTheme,
|
extends: PtjsTheme,
|
||||||
Layout: () => {
|
}
|
||||||
return h(AppearanceToggle, null, {
|
|
||||||
default: () => h(DefaultTheme.Layout, null, {
|
|
||||||
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
|
||||||
'doc-top': () => [
|
|
||||||
h(NolebaseHighlightTargetedHeading),
|
|
||||||
],
|
|
||||||
'doc-after': () => [h(AppFooter)],
|
|
||||||
'nav-bar-content-after': () => [
|
|
||||||
h(NolebaseEnhancedReadabilitiesMenu),
|
|
||||||
],
|
|
||||||
'nav-screen-content-after': () => [
|
|
||||||
h(NolebaseEnhancedReadabilitiesScreenMenu),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
enhanceApp({ app }) {
|
|
||||||
app.use(NolebaseEnhancedReadabilitiesPlugin, {
|
|
||||||
spotlight: {
|
|
||||||
defaultToggle: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(NolebaseGitChangelogPlugin, {
|
|
||||||
locales: {
|
|
||||||
'zh-CN': {
|
|
||||||
lastEditedDateFnsLocaleName: 'zhCN',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
} satisfies Theme
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ hero:
|
||||||
---
|
---
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import HomeContent from './.vitepress/theme/components/HomeContent.vue'
|
import HomeContent from '@project-trans/vitepress-theme-project-trans/components/HomeContent.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<HomeContent>
|
<HomeContent>
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
import { resolve } from 'node:path'
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import Components from 'unplugin-vue-components/vite'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
import { GitChangelog, GitChangelogMarkdownSection } from '@nolebase/vitepress-plugin-git-changelog/vite'
|
|
||||||
import {
|
|
||||||
MarkdownSectionWrapper,
|
|
||||||
PageHeaderTemplate,
|
|
||||||
TemplateAppSBox,
|
|
||||||
TemplateCopyrightInfo,
|
|
||||||
} from './.vitepress/plugins/MarkdownSectionWrapper'
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
|
@ -22,55 +12,4 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
|
||||||
MarkdownSectionWrapper(
|
|
||||||
[PageHeaderTemplate, TemplateCopyrightInfo],
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
excludes: [],
|
|
||||||
exclude: (_, { helpers }): boolean => {
|
|
||||||
if (helpers.idEquals('index.md'))
|
|
||||||
return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GitChangelog({
|
|
||||||
repoURL: 'https://github.com/project-trans/RLE-wiki',
|
|
||||||
maxGitLogCount: 1000,
|
|
||||||
rewritePaths: {
|
|
||||||
'docs/': '',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
// GitChangelogMarkdownSection({
|
|
||||||
// sections: {
|
|
||||||
// disableChangelog: false,
|
|
||||||
// disableContributors: true,
|
|
||||||
// },
|
|
||||||
// getChangelogTitle: (): string => {
|
|
||||||
// return '文件历史'
|
|
||||||
// },
|
|
||||||
// excludes: [],
|
|
||||||
// exclude: (_, { helpers }): boolean => {
|
|
||||||
// if (helpers.idEquals('index.md'))
|
|
||||||
// return true
|
|
||||||
|
|
||||||
// return false
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
Components({
|
|
||||||
dirs: resolve(__dirname, '.vitepress/theme/components'),
|
|
||||||
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
|
||||||
dts: './.vitepress/components.d.ts',
|
|
||||||
transformer: 'vue3',
|
|
||||||
}),
|
|
||||||
UnoCSS(),
|
|
||||||
],
|
|
||||||
ssr: {
|
|
||||||
noExternal: [
|
|
||||||
'@nolebase/vitepress-plugin-enhanced-readabilities',
|
|
||||||
'@nolebase/vitepress-plugin-highlight-targeted-heading',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
37
package.json
37
package.json
|
@ -2,6 +2,7 @@
|
||||||
"name": "rle-wiki",
|
"name": "rle-wiki",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"packageManager": "pnpm@9.2.0",
|
||||||
"description": "RLE 指北",
|
"description": "RLE 指北",
|
||||||
"license": "CC-BY-SA-4.0",
|
"license": "CC-BY-SA-4.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -9,33 +10,29 @@
|
||||||
"dev:wrangler": "wrangler pages dev ./docs/.vitepress/dist/",
|
"dev:wrangler": "wrangler pages dev ./docs/.vitepress/dist/",
|
||||||
"build": "vitepress build docs",
|
"build": "vitepress build docs",
|
||||||
"preview": "vitepress preview docs",
|
"preview": "vitepress preview docs",
|
||||||
"update-package": "pnpm dlx vp-update"
|
"update-package": "pnpm dlx vp-update",
|
||||||
|
"postinstall": "pnpm --filter @project-trans/* build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^2.13.3",
|
"@antfu/eslint-config": "^2.21.1",
|
||||||
"@cloudflare/workers-types": "^4.20240405.0",
|
"@cloudflare/workers-types": "^4.20240620.0",
|
||||||
"@iconify-json/octicon": "^1.1.53",
|
"@iconify-json/octicon": "^1.1.55",
|
||||||
"@nolebase/vitepress-plugin-enhanced-readabilities": "2.0.0-rc3",
|
|
||||||
"@nolebase/vitepress-plugin-git-changelog": "2.0.0-rc3",
|
|
||||||
"@nolebase/vitepress-plugin-highlight-targeted-heading": "2.0.0-rc3",
|
|
||||||
"@project-trans/suggestion-box": "^0.0.9",
|
"@project-trans/suggestion-box": "^0.0.9",
|
||||||
"@types/markdown-it": "^13.0.7",
|
"@project-trans/vitepress-theme-project-trans": "workspace:*",
|
||||||
|
"@types/markdown-it": "^13.0.8",
|
||||||
"@types/markdown-it-footnote": "^3.0.4",
|
"@types/markdown-it-footnote": "^3.0.4",
|
||||||
"@unocss/eslint-plugin": "^0.59.0",
|
"@unocss/eslint-plugin": "^0.59.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-format": "^0.1.0",
|
"eslint-plugin-format": "^0.1.2",
|
||||||
"markdown-it-footnote": "^3.0.3",
|
"simple-git": "^3.25.0",
|
||||||
"markdown-it-katex": "^2.0.3",
|
"unbuild": "^2.0.0",
|
||||||
"markdown-it-pangu": "^1.0.2",
|
|
||||||
"md5": "^2.3.0",
|
|
||||||
"simple-git": "^3.24.0",
|
|
||||||
"unocss": "^0.58.9",
|
"unocss": "^0.58.9",
|
||||||
"unplugin-vue-components": "^0.26.0",
|
"unplugin-vue-components": "^0.26.0",
|
||||||
"vite": "^5.2.8",
|
"vite": "^5.3.1",
|
||||||
"vitepress": "1.0.0-rc.42",
|
"vitepress": "^1.2.3",
|
||||||
"vitepress-sidebar": "^1.22.0",
|
"vitepress-sidebar": "^1.23.2",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.30",
|
||||||
"wrangler": "^3.49.0"
|
"wrangler": "^3.61.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { defineBuildConfig } from 'unbuild'
|
||||||
|
|
||||||
|
export default defineBuildConfig({
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
builder: 'mkdist',
|
||||||
|
input: './src',
|
||||||
|
outDir: './dist',
|
||||||
|
pattern: ['**/*.ts'],
|
||||||
|
format: 'esm',
|
||||||
|
loaders: ['js'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
builder: 'mkdist',
|
||||||
|
input: './src',
|
||||||
|
outDir: './dist',
|
||||||
|
pattern: ['**/*.ts'],
|
||||||
|
format: 'cjs',
|
||||||
|
loaders: ['js'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
builder: 'mkdist',
|
||||||
|
input: './src',
|
||||||
|
outDir: './dist',
|
||||||
|
pattern: ['**/*.vue'],
|
||||||
|
loaders: ['vue'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
declaration: true,
|
||||||
|
clean: true,
|
||||||
|
sourcemap: true,
|
||||||
|
rollup: { emitCJS: true },
|
||||||
|
})
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"name": "@project-trans/vitepress-theme-project-trans",
|
||||||
|
"type": "module",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "RLE 指北",
|
||||||
|
"license": "MIT",
|
||||||
|
"exports": {
|
||||||
|
"./config": {
|
||||||
|
"types": "./dist/config.d.ts",
|
||||||
|
"import": "./dist/config.mjs",
|
||||||
|
"require": "./dist/config.js"
|
||||||
|
},
|
||||||
|
"./utils": {
|
||||||
|
"types": "./dist/utils/index.d.ts",
|
||||||
|
"import": "./dist/utils/index.mjs",
|
||||||
|
"require": "./dist/utils/index.js"
|
||||||
|
},
|
||||||
|
"./theme": {
|
||||||
|
"types": "./dist/theme.d.ts",
|
||||||
|
"import": "./dist/theme.mjs",
|
||||||
|
"require": "./dist/theme.js"
|
||||||
|
},
|
||||||
|
"./plugins/MarkdownSectionWrapper": {
|
||||||
|
"types": "./dist/plugins/MarkdownSectionWrapper.d.ts",
|
||||||
|
"import": "./dist/plugins/MarkdownSectionWrapper.mjs",
|
||||||
|
"require": "./dist/plugins/MarkdownSectionWrapper.js"
|
||||||
|
},
|
||||||
|
"./components/*": "./dist/components/*"
|
||||||
|
},
|
||||||
|
"main": "./dist/config.js",
|
||||||
|
"module": "./dist/config.mjs",
|
||||||
|
"types": "./dist/config.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "unbuild",
|
||||||
|
"dev": "vitepress dev",
|
||||||
|
"dev:wrangler": "wrangler pages dev ./dist/",
|
||||||
|
"preview": "vitepress preview",
|
||||||
|
"update-package": "pnpm dlx vp-update"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@antfu/eslint-config": "^2.21.1",
|
||||||
|
"@cloudflare/workers-types": "^4.20240620.0",
|
||||||
|
"@iconify-json/octicon": "^1.1.55",
|
||||||
|
"@nolebase/vitepress-plugin-enhanced-readabilities": "2.1.2",
|
||||||
|
"@nolebase/vitepress-plugin-git-changelog": "2.1.2",
|
||||||
|
"@nolebase/vitepress-plugin-highlight-targeted-heading": "2.1.2",
|
||||||
|
"@project-trans/suggestion-box": "^0.0.9",
|
||||||
|
"@types/markdown-it": "^14.1.1",
|
||||||
|
"@types/markdown-it-footnote": "^3.0.4",
|
||||||
|
"@types/node": "^20.14.8",
|
||||||
|
"@unocss/eslint-plugin": "^0.61.0",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-format": "^0.1.2",
|
||||||
|
"markdown-it-footnote": "^4.0.0",
|
||||||
|
"markdown-it-katex": "^2.0.3",
|
||||||
|
"markdown-it-pangu": "^1.0.2",
|
||||||
|
"md5": "^2.3.0",
|
||||||
|
"simple-git": "^3.25.0",
|
||||||
|
"unocss": "^0.61.0",
|
||||||
|
"unplugin-vue-components": "^0.27.0",
|
||||||
|
"vite": "^5.3.1",
|
||||||
|
"vitepress": "^1.2.3",
|
||||||
|
"vitepress-sidebar": "^1.23.2",
|
||||||
|
"vue": "^3.4.30",
|
||||||
|
"wrangler": "^3.61.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,6 @@ import { NolebaseGitChangelog } from '@nolebase/vitepress-plugin-git-changelog/c
|
||||||
意见反馈
|
意见反馈
|
||||||
</h2>
|
</h2>
|
||||||
<AppSBox />
|
<AppSBox />
|
||||||
<h2 id="文件历史">
|
|
||||||
文件历史
|
|
||||||
</h2>
|
|
||||||
<NolebaseGitChangelog />
|
<NolebaseGitChangelog />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -1,11 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import SuggestionBox from '@project-trans/suggestion-box'
|
||||||
|
import '@project-trans/suggestion-box/dist/style.css'
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SuggestionBox
|
<SuggestionBox
|
||||||
text-content-placeholder="这个页面有什么问题?或者有什么建议?请在下方留言,我们会尽快回复和处理。"
|
text-content-placeholder="这个页面有什么问题?或者有什么建议?请在下方留言,我们会尽快回复和处理。"
|
||||||
target-url="/api/v1/suggestion"
|
target-url="/api/v1/suggestion"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import SuggestionBox from '@project-trans/suggestion-box';
|
|
||||||
import '@project-trans/suggestion-box/dist/style.css'
|
|
||||||
</script>
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useData } from 'vitepress'
|
import { useData } from 'vitepress'
|
||||||
import DefaultTheme from 'vitepress/theme'
|
import { nextTick, provide } from 'vue'
|
||||||
import { nextTick, provide, onMounted } from 'vue'
|
|
||||||
|
|
||||||
const { isDark } = useData()
|
const { isDark } = useData()
|
||||||
|
|
||||||
const isSSR = typeof window === 'undefined'
|
const isSSR = typeof window === 'undefined'
|
||||||
|
|
||||||
const enableTransitions = () => isSSR ? false :
|
function enableTransitions() {
|
||||||
'startViewTransition' in document &&
|
return isSSR
|
||||||
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
? false
|
||||||
|
: 'startViewTransition' in document
|
||||||
|
&& window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
||||||
|
}
|
||||||
|
|
||||||
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||||
if (!enableTransitions()) {
|
if (!enableTransitions()) {
|
||||||
|
@ -18,16 +19,15 @@ provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!document.documentElement.classList.contains('VPSwitchAppearance-ViewTransition')) {
|
if (!document.documentElement.classList.contains('VPSwitchAppearance-ViewTransition'))
|
||||||
document.documentElement.classList.add('VPSwitchAppearance-ViewTransition')
|
document.documentElement.classList.add('VPSwitchAppearance-ViewTransition')
|
||||||
}
|
|
||||||
|
|
||||||
const clipPath = [
|
const clipPath = [
|
||||||
`circle(0px at ${x}px ${y}px)`,
|
`circle(0px at ${x}px ${y}px)`,
|
||||||
`circle(${Math.hypot(
|
`circle(${Math.hypot(
|
||||||
Math.max(x, innerWidth - x),
|
Math.max(x, innerWidth - x),
|
||||||
Math.max(y, innerHeight - y)
|
Math.max(y, innerHeight - y),
|
||||||
)}px at ${x}px ${y}px)`
|
)}px at ${x}px ${y}px)`,
|
||||||
]
|
]
|
||||||
|
|
||||||
await (document as any).startViewTransition(async () => {
|
await (document as any).startViewTransition(async () => {
|
||||||
|
@ -40,8 +40,8 @@ provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||||
{
|
{
|
||||||
duration: 500,
|
duration: 500,
|
||||||
easing: 'ease-in',
|
easing: 'ease-in',
|
||||||
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
|
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
|
@ -1,48 +1,51 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useData } from "vitepress";
|
import { useData } from 'vitepress'
|
||||||
import { Node, Trie, data } from "../../plugins/CopyrightLoader.data";
|
import type { Node, Trie } from '../plugins/CopyrightLoader.data'
|
||||||
|
import { data } from '../plugins/CopyrightLoader.data'
|
||||||
|
|
||||||
function searchClosestInTrie(
|
function searchClosestInTrie(
|
||||||
that: Trie<Record<string, any>>,
|
that: Trie<Record<string, any>>,
|
||||||
path: string[],
|
path: string[],
|
||||||
node: Node<Record<string, any>> = that.root
|
node: Node<Record<string, any>> = that.root,
|
||||||
): Record<string, any> | null {
|
): Record<string, any> | null {
|
||||||
if (path.length === 0) {
|
if (path.length === 0)
|
||||||
return node.value;
|
return node.value
|
||||||
}
|
|
||||||
if (path[0] in node.children) {
|
if (path[0] in node.children) {
|
||||||
let value = searchClosestInTrie(
|
let value = searchClosestInTrie(
|
||||||
that,
|
that,
|
||||||
path.slice(1),
|
path.slice(1),
|
||||||
node.children[path[0]]
|
node.children[path[0]],
|
||||||
);
|
)
|
||||||
if (value === null) {
|
if (value === null)
|
||||||
value = node.value;
|
value = node.value
|
||||||
}
|
|
||||||
return value;
|
return value
|
||||||
}
|
}
|
||||||
return node.value;
|
return node.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const paths = useData()
|
const paths = useData()
|
||||||
.page.value.relativePath.replace('.md', '').split('/')
|
.page.value.relativePath.replace('.md', '').split('/')
|
||||||
.filter((item: string) => item !== '');
|
.filter((item: string) => item !== '')
|
||||||
const attrs = searchClosestInTrie(data, paths);
|
const attrs = searchClosestInTrie(data, paths)
|
||||||
const frontmatter = useData().frontmatter.value;
|
const frontmatter = useData().frontmatter.value
|
||||||
|
|
||||||
const originUrlExists = (attrs?.copyright?.url ?? null) != null;
|
const originUrlExists = (attrs?.copyright?.url ?? null) != null
|
||||||
const originUrl = attrs?.copyright?.url ?? 'javascript:void(0)';
|
const originUrl = attrs?.copyright?.url ?? 'javascript:void(0)'
|
||||||
|
|
||||||
const license = attrs?.copyright?.license ?? null;
|
const license = attrs?.copyright?.license ?? null
|
||||||
const licenseExists = license != null;
|
const licenseExists = license != null
|
||||||
const licenseUrlExists = (attrs?.copyright?.licenseUrl ?? null) != null;
|
const licenseUrlExists = (attrs?.copyright?.licenseUrl ?? null) != null
|
||||||
const licenseUrl = attrs?.copyright?.licenseUrl ?? 'javascript:void(0)'
|
const licenseUrl = attrs?.copyright?.licenseUrl ?? 'javascript:void(0)'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="attrs?.copyright?.enable ?? false">
|
<div v-if="attrs?.copyright?.enable ?? false">
|
||||||
<div class="tip custom-block">
|
<div class="tip custom-block">
|
||||||
<p class="custom-block-title">Copyright</p>
|
<p class="custom-block-title">
|
||||||
|
Copyright
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span>这篇文章 </span>
|
<span>这篇文章 </span>
|
||||||
<a v-if="originUrlExists" :href="originUrl">{{ frontmatter.title }}</a>
|
<a v-if="originUrlExists" :href="originUrl">{{ frontmatter.title }}</a>
|
||||||
|
@ -59,6 +62,6 @@ const licenseUrl = attrs?.copyright?.licenseUrl ?? 'javascript:void(0)'
|
||||||
<span>。</span>
|
<span>。</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useData } from 'vitepress'
|
import { useData } from 'vitepress'
|
||||||
import { computed, onMounted, ref, watchEffect } from 'vue';
|
import { computed, onMounted, ref, watchEffect } from 'vue'
|
||||||
|
|
||||||
const { frontmatter, page, theme, lang } = useData()
|
const { frontmatter, page, theme, lang } = useData()
|
||||||
|
|
||||||
const date = computed(
|
const date = computed(
|
||||||
() => new Date(frontmatter.value.lastUpdated ?? page.value.lastUpdated)
|
() => new Date(frontmatter.value.lastUpdated ?? page.value.lastUpdated),
|
||||||
)
|
)
|
||||||
const isoDatetime = computed(() => date.value.toISOString())
|
const isoDatetime = computed(() => date.value.toISOString())
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ onMounted(() => {
|
||||||
theme.value.lastUpdated?.formatOptions?.forceLocale ? lang.value : undefined,
|
theme.value.lastUpdated?.formatOptions?.forceLocale ? lang.value : undefined,
|
||||||
theme.value.lastUpdated?.formatOptions ?? {
|
theme.value.lastUpdated?.formatOptions ?? {
|
||||||
dateStyle: 'short',
|
dateStyle: 'short',
|
||||||
timeStyle: 'short'
|
timeStyle: 'short',
|
||||||
}
|
},
|
||||||
).format(date.value)
|
).format(date.value)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -33,16 +33,15 @@ const authors = computed(() => {
|
||||||
|
|
||||||
return [...author, '匿名']
|
return [...author, '匿名']
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-wrap gap-4 mt-4 mb-10">
|
<div class="mb-10 mt-4 flex flex-wrap gap-4">
|
||||||
<div class="inline-flex items-center gap-1">
|
<div class="inline-flex items-center gap-1">
|
||||||
<span class="i-octicon:person" />
|
<span class="i-octicon:person" />
|
||||||
<span>作者:</span>
|
<span>作者:</span>
|
||||||
<span class="space-x-2">
|
<span class="space-x-2">
|
||||||
<span v-for="author of authors">
|
<span v-for="(author, index) in authors" :key="index">
|
||||||
{{ author }}
|
{{ author }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
|
@ -0,0 +1,18 @@
|
||||||
|
import AppearanceToggle from './AppearanceToggle.vue'
|
||||||
|
import AppFooter from './AppFooter.vue'
|
||||||
|
import AppSBox from './AppSBox.vue'
|
||||||
|
import ArticlesMenu from './ArticlesMenu.vue'
|
||||||
|
import CopyrightInfo from './CopyrightInfo.vue'
|
||||||
|
import HomeContent from './HomeContent.vue'
|
||||||
|
import PageInfo from './PageInfo.vue'
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
AppearanceToggle,
|
||||||
|
AppFooter,
|
||||||
|
AppSBox,
|
||||||
|
ArticlesMenu,
|
||||||
|
CopyrightInfo,
|
||||||
|
HomeContent,
|
||||||
|
PageInfo
|
||||||
|
}
|
|
@ -0,0 +1,325 @@
|
||||||
|
import { readFileSync, statSync } from 'node:fs'
|
||||||
|
import { dirname, join, resolve } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { GitChangelog } from '@nolebase/vitepress-plugin-git-changelog/vite'
|
||||||
|
import {
|
||||||
|
MarkdownSectionWrapper,
|
||||||
|
PageHeaderTemplate,
|
||||||
|
TemplateCopyrightInfo,
|
||||||
|
} from '@project-trans/vitepress-theme-project-trans/plugins/MarkdownSectionWrapper'
|
||||||
|
import footnote from 'markdown-it-footnote'
|
||||||
|
import katex from 'markdown-it-katex'
|
||||||
|
import mdPangu from 'markdown-it-pangu'
|
||||||
|
import UnoCSS from 'unocss/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { defineConfig } from 'vitepress'
|
||||||
|
import { generateSidebar } from './sidebar'
|
||||||
|
import { useThemeContext } from './utils/themeContext'
|
||||||
|
|
||||||
|
// https://vitepress.dev/reference/site-config
|
||||||
|
function genConfig() {
|
||||||
|
const themeConfig = useThemeContext()
|
||||||
|
const { siteTitle, siteDescription, githubRepoLink, rootDir, nav }
|
||||||
|
= themeConfig
|
||||||
|
return defineConfig({
|
||||||
|
lang: 'zh-CN',
|
||||||
|
title: siteTitle,
|
||||||
|
cleanUrls: true,
|
||||||
|
markdown: {
|
||||||
|
config(md) {
|
||||||
|
md.use(mdPangu)
|
||||||
|
md.use(footnote)
|
||||||
|
md.use(katex)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
head: [
|
||||||
|
[
|
||||||
|
'link',
|
||||||
|
{
|
||||||
|
rel: 'apple-touch-icon',
|
||||||
|
sizes: '180x180',
|
||||||
|
href: '/apple-touch-icon.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'link',
|
||||||
|
{
|
||||||
|
rel: 'icon',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '32x32',
|
||||||
|
href: '/favicon-32x32.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'link',
|
||||||
|
{
|
||||||
|
rel: 'icon',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '16x16',
|
||||||
|
href: '/favicon-16x16.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
['link', { rel: 'manifest', href: '/site.webmanifest' }],
|
||||||
|
['meta', { name: 'msapplication-TileColor', content: '#4c4c4c' }],
|
||||||
|
['meta', { name: 'theme-color', content: '#ffffff' }],
|
||||||
|
['meta', { property: 'og:site_name', content: siteTitle }],
|
||||||
|
],
|
||||||
|
themeConfig: {
|
||||||
|
// https://vitepress.dev/reference/default-theme-config
|
||||||
|
siteTitle: false,
|
||||||
|
logo: {
|
||||||
|
src: '/logo-horizontal.svg',
|
||||||
|
alt: 'Logo: RLE.wiki',
|
||||||
|
},
|
||||||
|
nav,
|
||||||
|
sidebar: generateSidebar(),
|
||||||
|
socialLinks: [{ icon: 'github', link: githubRepoLink }],
|
||||||
|
editLink: {
|
||||||
|
pattern: `${githubRepoLink}/edit/main/docs/:path`,
|
||||||
|
text: '在 GitHub 上编辑此页面', // label localization
|
||||||
|
},
|
||||||
|
// label localization
|
||||||
|
outline: { label: '本页大纲', level: 'deep' },
|
||||||
|
lastUpdated: { text: '最后更新' },
|
||||||
|
darkModeSwitchLabel: '深色模式',
|
||||||
|
sidebarMenuLabel: '目录',
|
||||||
|
returnToTopLabel: '返回顶部',
|
||||||
|
docFooter: {
|
||||||
|
prev: '上一页',
|
||||||
|
next: '下一页',
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
provider: 'local',
|
||||||
|
options: {
|
||||||
|
locales: {
|
||||||
|
root: {
|
||||||
|
translations: {
|
||||||
|
button: {
|
||||||
|
buttonText: '搜索文档',
|
||||||
|
buttonAriaLabel: '搜索文档',
|
||||||
|
},
|
||||||
|
modal: {
|
||||||
|
noResultsText: '无法找到相关结果',
|
||||||
|
resetButtonTitle: '清除查询条件',
|
||||||
|
footer: {
|
||||||
|
selectText: '选择',
|
||||||
|
navigateText: '切换',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Add title field in frontmatter to search
|
||||||
|
// You can exclude a page from search by adding search: false to the page's frontmatter.
|
||||||
|
_render(src, env, md) {
|
||||||
|
if (env.frontmatter?.search === false)
|
||||||
|
return ''
|
||||||
|
let html = md.render(src, env)
|
||||||
|
if (env.frontmatter?.title)
|
||||||
|
html = md.render(`# ${env.frontmatter.title}\n`) + html
|
||||||
|
return html
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transformHead: (context) => {
|
||||||
|
const head = [...context.head] || []
|
||||||
|
|
||||||
|
const pageSourceFilePath = join(rootDir, context.pageData.filePath)
|
||||||
|
const pageSourceFileStat = statSync(
|
||||||
|
join(rootDir, context.pageData.filePath),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (pageSourceFileStat.isDirectory()) {
|
||||||
|
head.push([
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'og:title',
|
||||||
|
content: siteTitle,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
head.push([
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
content: siteDescription,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return head
|
||||||
|
}
|
||||||
|
|
||||||
|
let pageSourceFileContent = readFileSync(pageSourceFilePath, {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove all frontmatter
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/---[\s\S]*?---/,
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
|
// remove markdown heading markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/^(#+)\s+(.*)/gm,
|
||||||
|
' $2 ',
|
||||||
|
)
|
||||||
|
// remove markdown link markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/\[([^\]]+)\]\([^)]+\)/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
// remove markdown image markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/\!\[([^\]]+)\]\([^)]+\)/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
// remove markdown reference link markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(/\[.*]/gm, '')
|
||||||
|
// remove markdown bold markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/\*\*([^*]+)\*\*/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/__([^*]+)__/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
// remove markdown italic markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/\*([^*]+)\*/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/_([^*]+)_/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
// remove markdown code markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/`([^`]+)`/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
// remove markdown code block markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/```([^`]+)```/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
// remove markdown table header markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(/\|:?-+:?\|/gm, '')
|
||||||
|
// remove markdown table cell markup but keep the text content
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
/\|([^|]+)\|/gm,
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
|
||||||
|
// remove specific html tags completely
|
||||||
|
const tags = ['']
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
new RegExp(`<${tag}[^>]*>[\\s\\S]*?<\\/${tag}>`, 'g'),
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove specific html tags but keep the text content
|
||||||
|
const tagsToKeepContent = ['u', 'Containers', 'img', 'a']
|
||||||
|
tagsToKeepContent.forEach((tag) => {
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(
|
||||||
|
new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'g'),
|
||||||
|
' $1 ',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove all new lines (either \r, \n)
|
||||||
|
pageSourceFileContent = pageSourceFileContent.replace(/[\r|\n]/gm, '')
|
||||||
|
|
||||||
|
// calculate the first 200 characters of the page content
|
||||||
|
let pageContent = pageSourceFileContent.slice(0, 200)
|
||||||
|
// trim space
|
||||||
|
pageContent = pageContent.trim()
|
||||||
|
// if pageSourceFileContent is longer than 200 characters, add ellipsis
|
||||||
|
if (pageSourceFileContent.length > 100)
|
||||||
|
pageContent += '...'
|
||||||
|
|
||||||
|
if (context.pageData.frontmatter?.layout === 'home') {
|
||||||
|
pageContent
|
||||||
|
= context.pageData.frontmatter?.hero?.tagline ?? siteDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
head.push(['meta', { name: 'description', content: pageContent }])
|
||||||
|
|
||||||
|
head.push(['meta', { property: 'og:title', content: context.title }])
|
||||||
|
|
||||||
|
head.push(['meta', { property: 'og:description', content: pageContent }])
|
||||||
|
|
||||||
|
head.push(['meta', { property: 'og:title', content: context.title }])
|
||||||
|
|
||||||
|
head.push([
|
||||||
|
'meta',
|
||||||
|
{ property: 'twitter:description', content: pageContent },
|
||||||
|
])
|
||||||
|
|
||||||
|
return head
|
||||||
|
},
|
||||||
|
vite: {
|
||||||
|
plugins: [
|
||||||
|
MarkdownSectionWrapper(
|
||||||
|
[PageHeaderTemplate, TemplateCopyrightInfo],
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
excludes: [],
|
||||||
|
exclude: (_, { helpers }): boolean => {
|
||||||
|
if (helpers.idEquals('index.md'))
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GitChangelog({
|
||||||
|
repoURL: githubRepoLink,
|
||||||
|
}),
|
||||||
|
// GitChangelogMarkdownSection({
|
||||||
|
// sections: {
|
||||||
|
// disableChangelog: false,
|
||||||
|
// disableContributors: true,
|
||||||
|
// },
|
||||||
|
// getChangelogTitle: (): string => {
|
||||||
|
// return '文件历史'
|
||||||
|
// },
|
||||||
|
// excludes: [],
|
||||||
|
// exclude: (_, { helpers }): boolean => {
|
||||||
|
// if (helpers.idEquals('index.md'))
|
||||||
|
// return true
|
||||||
|
|
||||||
|
// return false
|
||||||
|
// },
|
||||||
|
// }),
|
||||||
|
Components({
|
||||||
|
dirs: [
|
||||||
|
'docs/.vitepress/theme/components',
|
||||||
|
resolve(
|
||||||
|
typeof dirname(fileURLToPath(import.meta.url)) === 'string'
|
||||||
|
? dirname(fileURLToPath(import.meta.url))
|
||||||
|
: __dirname,
|
||||||
|
'./components',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
||||||
|
dts: './.vitepress/components.d.ts',
|
||||||
|
transformer: 'vue3',
|
||||||
|
}),
|
||||||
|
UnoCSS(),
|
||||||
|
],
|
||||||
|
ssr: {
|
||||||
|
noExternal: [
|
||||||
|
'@nolebase/vitepress-plugin-enhanced-readabilities',
|
||||||
|
'@nolebase/vitepress-plugin-highlight-targeted-heading',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default genConfig
|
|
@ -0,0 +1,62 @@
|
||||||
|
import type { ContentData } from 'vitepress'
|
||||||
|
import { createContentLoader, defineLoader } from 'vitepress'
|
||||||
|
|
||||||
|
const contentLoader = createContentLoader('/**/*.md')
|
||||||
|
|
||||||
|
export interface Node<T> {
|
||||||
|
value: T | null
|
||||||
|
children: { [key: string]: Node<T> }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Trie<T> {
|
||||||
|
root: Node<T>
|
||||||
|
insert: ((path: string[], value: T, node: Node<T>) => void) & ((path: string[], value: T) => void)
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const data: Trie<Record<string, any>>
|
||||||
|
export { data }
|
||||||
|
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
export default defineLoader({
|
||||||
|
watch: contentLoader.watch,
|
||||||
|
async load(): Promise<Trie<Record<string, any>>> {
|
||||||
|
const raw: ContentData[] = await contentLoader.load()
|
||||||
|
const trie: Trie<Record<string, any>> = {
|
||||||
|
root: { value: null, children: {} },
|
||||||
|
|
||||||
|
insert(this: Trie<Record<string, any>>, path, value, node: Node<Record<string, any>> = this.root) {
|
||||||
|
if (path.length === 0) {
|
||||||
|
node.value = value
|
||||||
|
}
|
||||||
|
else if (path.length === 1) {
|
||||||
|
if (!(path[0] in node.children))
|
||||||
|
node.children[path[0]] = { value, children: {} }
|
||||||
|
else
|
||||||
|
node.children[path[0]].value = value
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!(path[0] in node.children)) {
|
||||||
|
const new_node = { value: null, children: {} }
|
||||||
|
this.insert(path.slice(1), value, new_node)
|
||||||
|
node.children[path[0]] = new_node
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.insert(path.slice(1), value, node.children[path[0]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for (const v of raw) {
|
||||||
|
const frontmatter = v.frontmatter ?? null
|
||||||
|
const copyright = frontmatter?.copyright ?? null
|
||||||
|
if (copyright !== null) {
|
||||||
|
trie.insert(v.url.split('/').filter((item, _index, _array) => item !== ''), {
|
||||||
|
author: frontmatter.author ?? null,
|
||||||
|
title: frontmatter.title ?? null,
|
||||||
|
copyright,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trie
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,11 +1,12 @@
|
||||||
import { resolve, relative } from 'node:path'
|
import { dirname, relative, resolve } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
import type { Plugin } from 'vite'
|
import type { Plugin } from 'vite'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
pathEndsWith,
|
pathEndsWith,
|
||||||
pathEquals,
|
pathEquals,
|
||||||
pathStartsWith,
|
pathStartsWith,
|
||||||
} from '../path'
|
} from '../utils/path'
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
helpers: {
|
helpers: {
|
||||||
|
@ -79,7 +80,7 @@ export interface MarkdownSectionWrapperOptions {
|
||||||
exclude?: (id: string, context: Context) => boolean
|
exclude?: (id: string, context: Context) => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MarkdownSectionWrapper(headerTransformers: ((frontmatter: string|null, text: string, id: string) => string)[], footerTransformers: ((frontmatter: string|null, text: string, id: string) => string)[], options?: MarkdownSectionWrapperOptions): Plugin {
|
export function MarkdownSectionWrapper(headerTransformers: ((frontmatter: string | null, text: string, id: string) => string)[], footerTransformers: ((frontmatter: string | null, text: string, id: string) => string)[], options?: MarkdownSectionWrapperOptions): Plugin {
|
||||||
const {
|
const {
|
||||||
excludes = ['index.md'],
|
excludes = ['index.md'],
|
||||||
exclude = () => false,
|
exclude = () => false,
|
||||||
|
@ -116,18 +117,18 @@ export function MarkdownSectionWrapper(headerTransformers: ((frontmatter: string
|
||||||
if (exclude(id, { helpers: { idEndsWith, idEquals, idStartsWith, pathEndsWith, pathEquals, pathStartsWith } }))
|
if (exclude(id, { helpers: { idEndsWith, idEquals, idStartsWith, pathEndsWith, pathEquals, pathStartsWith } }))
|
||||||
return null
|
return null
|
||||||
|
|
||||||
let frontmatter = (code.match(/(^---$(\s|\S)+?^---$)/m)?.[0] ?? null)
|
const frontmatter = (code.match(/(^---$([\s\S])+?^---$)/m)?.[0] ?? null)
|
||||||
let text = code.replace(/(^---$(\s|\S)+?^---$)/m, '')
|
const text = code.replace(/(^---$([\s\S])+?^---$)/m, '')
|
||||||
|
|
||||||
let headers: string[] = headerTransformers.map(f => f(frontmatter, text, id))
|
const headers: string[] = headerTransformers.map(f => f(frontmatter, text, id))
|
||||||
let footers: string[] = footerTransformers.map(f => f(frontmatter, text, id))
|
const footers: string[] = footerTransformers.map(f => f(frontmatter, text, id))
|
||||||
|
|
||||||
return [frontmatter, ...headers, text, ...footers].join("")
|
return [frontmatter, ...headers, text, ...footers].join('')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TemplateAppSBox(_frontmatter: string|null, _text: string, _id: string): string {
|
export function TemplateAppSBox(_frontmatter: string | null, _text: string, _id: string): string {
|
||||||
return `
|
return `
|
||||||
|
|
||||||
## 意见反馈
|
## 意见反馈
|
||||||
|
@ -137,7 +138,7 @@ export function TemplateAppSBox(_frontmatter: string|null, _text: string, _id: s
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TemplateCopyrightInfo(_frontmatter: string|null, _text: string, _id: string): string {
|
export function TemplateCopyrightInfo(_frontmatter: string | null, _text: string, _id: string): string {
|
||||||
return `
|
return `
|
||||||
|
|
||||||
<CopyrightInfo />
|
<CopyrightInfo />
|
||||||
|
@ -145,21 +146,23 @@ export function TemplateCopyrightInfo(_frontmatter: string|null, _text: string,
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
const ROOT = resolve(__dirname, '../../')
|
const ROOT = resolve(__dirname, '../../')
|
||||||
|
|
||||||
export function PageHeaderTemplate(_frontmatter: string|null, _text: string, id: string): string {
|
export function PageHeaderTemplate(_frontmatter: string | null, _text: string, id: string): string {
|
||||||
if (!id.endsWith('.md'))
|
if (!id.endsWith('.md'))
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
id = relative(ROOT, id)
|
id = relative(ROOT, id)
|
||||||
|
|
||||||
if (id == 'index.md')
|
if (id === 'index.md')
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
|
||||||
# {{ $frontmatter.title }}
|
# {{ $frontmatter.title }}
|
||||||
|
|
||||||
<PageInfo />
|
<PageInfo />
|
||||||
|
|
||||||
`}
|
`
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import type {
|
||||||
|
SidebarItem,
|
||||||
|
SidebarMultiItem,
|
||||||
|
} from 'vitepress-sidebar'
|
||||||
|
import { generateSidebar as genSidebar } from 'vitepress-sidebar'
|
||||||
|
import { useThemeContext } from './utils/themeContext'
|
||||||
|
|
||||||
|
export function generateSidebar() {
|
||||||
|
const { sidebarOptions } = useThemeContext()
|
||||||
|
const sidebar = genSidebar(sidebarOptions)
|
||||||
|
for (const key in sidebar) {
|
||||||
|
const sidebarMultiItem: SidebarMultiItem = (sidebar as any)[key]
|
||||||
|
sidebarMultiItem.items.sort(sidebarTitleSorter)
|
||||||
|
}
|
||||||
|
return sidebar
|
||||||
|
}
|
||||||
|
|
||||||
|
function sidebarTitleSorter(infoA: SidebarItem, infoB: SidebarItem): number {
|
||||||
|
const textA = infoA.text
|
||||||
|
const textB = infoB.text
|
||||||
|
if (textA === undefined || textB === undefined)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
const infoANfc = textA.normalize('NFC')
|
||||||
|
const infoBNfc = textB.normalize('NFC')
|
||||||
|
return infoANfc.localeCompare(infoBNfc, 'zh', {
|
||||||
|
numeric: true,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// https://vitepress.dev/guide/custom-theme
|
||||||
|
import type { Theme } from 'vitepress'
|
||||||
|
import DefaultTheme from 'vitepress/theme'
|
||||||
|
import { h } from 'vue'
|
||||||
|
|
||||||
|
import {
|
||||||
|
NolebaseEnhancedReadabilitiesMenu,
|
||||||
|
NolebaseEnhancedReadabilitiesPlugin,
|
||||||
|
NolebaseEnhancedReadabilitiesScreenMenu,
|
||||||
|
} from '@nolebase/vitepress-plugin-enhanced-readabilities/client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
NolebaseHighlightTargetedHeading,
|
||||||
|
} from '@nolebase/vitepress-plugin-highlight-targeted-heading/client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
NolebaseGitChangelogPlugin,
|
||||||
|
} from '@nolebase/vitepress-plugin-git-changelog/client'
|
||||||
|
import type Options from 'vitepress-sidebar'
|
||||||
|
import AppearanceToggle from './components/AppearanceToggle.vue'
|
||||||
|
|
||||||
|
import '@nolebase/vitepress-plugin-enhanced-readabilities/client/style.css'
|
||||||
|
import '@nolebase/vitepress-plugin-git-changelog/client/style.css'
|
||||||
|
import '@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css'
|
||||||
|
|
||||||
|
import AppFooter from './components/AppFooter.vue'
|
||||||
|
|
||||||
|
export type SidebarOptions = Options
|
||||||
|
|
||||||
|
export default {
|
||||||
|
extends: DefaultTheme,
|
||||||
|
Layout: () => {
|
||||||
|
return h(AppearanceToggle, null, {
|
||||||
|
default: () => h(DefaultTheme.Layout, null, {
|
||||||
|
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
||||||
|
'doc-top': () => [
|
||||||
|
h(NolebaseHighlightTargetedHeading),
|
||||||
|
],
|
||||||
|
'doc-after': () => [h(AppFooter)],
|
||||||
|
'nav-bar-content-after': () => [
|
||||||
|
h(NolebaseEnhancedReadabilitiesMenu),
|
||||||
|
],
|
||||||
|
'nav-screen-content-after': () => [
|
||||||
|
h(NolebaseEnhancedReadabilitiesScreenMenu),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
enhanceApp({ app }) {
|
||||||
|
app.use(NolebaseEnhancedReadabilitiesPlugin, {
|
||||||
|
spotlight: {
|
||||||
|
defaultToggle: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(NolebaseGitChangelogPlugin, {
|
||||||
|
locales: {
|
||||||
|
'zh-CN': {
|
||||||
|
lastEditedDateFnsLocaleName: 'zhCN',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
} satisfies Theme
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './path'
|
||||||
|
export * from './themeContext'
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||||
|
import type { DefaultTheme } from 'vitepress'
|
||||||
|
import type { generateSidebar } from 'vitepress-sidebar'
|
||||||
|
import type Options from 'vitepress-sidebar'
|
||||||
|
|
||||||
|
type NavConfig = DefaultTheme.Config['nav']
|
||||||
|
|
||||||
|
export interface ThemeContext {
|
||||||
|
siteTitle: string
|
||||||
|
siteDescription: string
|
||||||
|
githubRepoLink: string
|
||||||
|
rootDir: string
|
||||||
|
include: string[]
|
||||||
|
nav: NavConfig
|
||||||
|
sidebarOptions: Options | Options[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeContext = new AsyncLocalStorage<ThemeContext>()
|
||||||
|
|
||||||
|
export function withThemeContext<T>(context: ThemeContext, fn: () => T): T {
|
||||||
|
return themeContext.run(context, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useThemeContext(): ThemeContext {
|
||||||
|
return themeContext.getStore()!
|
||||||
|
}
|
9745
pnpm-lock.yaml
9745
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,2 +1,3 @@
|
||||||
packages:
|
packages:
|
||||||
- functions
|
- functions
|
||||||
|
- packages/*
|
||||||
|
|
Loading…
Reference in New Issue