refactor(theme): 🔥 split theme (#429)
部署到 Cloudflare Pages / deploy (push) Has been cancelled
Details
部署到 Cloudflare Pages / deploy (push) Has been cancelled
Details
parent
24e3253558
commit
a4f0fc78c1
|
@ -17,7 +17,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# 第一步:检出主仓库代码
|
# 第一步:检出主仓库代码
|
||||||
- name: 检出主仓库代码
|
- name: 检出主仓库代码
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.base.ref }}
|
ref: ${{ github.event.pull_request.base.ref }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
@ -30,9 +30,7 @@ jobs:
|
||||||
|
|
||||||
# 第三步:安装 pnpm
|
# 第三步:安装 pnpm
|
||||||
- name: 安装 pnpm
|
- name: 安装 pnpm
|
||||||
uses: pnpm/action-setup@v3
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
# 第四步:安装依赖
|
# 第四步:安装依赖
|
||||||
- name: 安装依赖
|
- name: 安装依赖
|
||||||
|
|
|
@ -12,4 +12,5 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ssr: { noExternal: true },
|
||||||
})
|
})
|
||||||
|
|
23
package.json
23
package.json
|
@ -2,7 +2,7 @@
|
||||||
"name": "rle-wiki",
|
"name": "rle-wiki",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"packageManager": "pnpm@9.10.0+sha512.73a29afa36a0d092ece5271de5177ecbf8318d454ecd701343131b8ebc0c1a91c487da46ab77c8e596d6acf1461e3594ced4becedf8921b074fbd8653ed7051c",
|
"packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b",
|
||||||
"description": "RLE 指北",
|
"description": "RLE 指北",
|
||||||
"license": "CC-BY-SA-4.0",
|
"license": "CC-BY-SA-4.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -10,34 +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.21.1",
|
"@antfu/eslint-config": "^2.21.1",
|
||||||
"@cloudflare/workers-types": "^4.20240620.0",
|
"@cloudflare/workers-types": "^4.20240620.0",
|
||||||
"@iconify-json/carbon": "^1.1.36",
|
"@iconify-json/carbon": "^1.2.1",
|
||||||
"@iconify-json/icon-park-outline": "^1.1.15",
|
"@iconify-json/icon-park-outline": "^1.2.0",
|
||||||
"@iconify-json/octicon": "^1.1.55",
|
"@iconify-json/octicon": "^1.2.0",
|
||||||
"@project-trans/suggestion-box": "^0.0.9",
|
"@project-trans/suggestion-box": "^0.0.9",
|
||||||
"@project-trans/vitepress-theme-project-trans": "workspace:*",
|
"@project-trans/vitepress-theme-project-trans": "^0.3.1726924363",
|
||||||
"@types/markdown-it": "^13.0.8",
|
|
||||||
"@types/markdown-it-footnote": "^3.0.4",
|
|
||||||
"@unocss/eslint-plugin": "^0.59.4",
|
"@unocss/eslint-plugin": "^0.59.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-format": "^0.1.2",
|
"eslint-plugin-format": "^0.1.2",
|
||||||
"simple-git": "^3.25.0",
|
"unocss": "^0.62.4",
|
||||||
"unbuild": "^2.0.0",
|
|
||||||
"unocss": "^0.58.9",
|
|
||||||
"unplugin-vue-components": "^0.26.0",
|
"unplugin-vue-components": "^0.26.0",
|
||||||
"vite": "^5.3.1",
|
"vite": "^5.3.1",
|
||||||
"vitepress": "^1.2.3",
|
"vitepress": "^1.2.3",
|
||||||
"vitepress-sidebar": "^1.23.2",
|
|
||||||
"vue": "^3.4.30"
|
"vue": "^3.4.30"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"hasown": "npm:@nolyfill/hasown@latest"
|
"hasown": "npm:@nolyfill/hasown@^1",
|
||||||
|
"is-core-module": "npm:@nolyfill/is-core-module@^1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
# VitePress Theme Project Trans
|
|
||||||
|
|
||||||
这是一个由 Project Trans 开发的 VitePress 主题。
|
|
||||||
|
|
||||||
## 使用方式
|
|
||||||
|
|
||||||
修改下述两个文件:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// docs/.vitepress/config.ts
|
|
||||||
import genConfig from '@project-trans/vitepress-theme-project-trans/config'
|
|
||||||
import type { SidebarOptions } from '@project-trans/vitepress-theme-project-trans/theme'
|
|
||||||
import type { ThemeContext } from '@project-trans/vitepress-theme-project-trans/utils'
|
|
||||||
import { withThemeContext } from '@project-trans/vitepress-theme-project-trans/utils'
|
|
||||||
|
|
||||||
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
|
|
||||||
export default withThemeContext(themeConfig, genConfig)
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// docs/.vitepress/theme/index.ts
|
|
||||||
// https://vitepress.dev/guide/custom-theme
|
|
||||||
import PtjsTheme from '@project-trans/vitepress-theme-project-trans/theme'
|
|
||||||
|
|
||||||
import 'uno.css'
|
|
||||||
import './style.css'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
extends: PtjsTheme,
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { defineBuildConfig } from 'unbuild'
|
|
||||||
|
|
||||||
export default defineBuildConfig({
|
|
||||||
entries: [
|
|
||||||
{
|
|
||||||
builder: 'mkdist',
|
|
||||||
input: './src',
|
|
||||||
outDir: './dist',
|
|
||||||
pattern: ['**/*.ts', '**/*.css'],
|
|
||||||
format: 'esm',
|
|
||||||
loaders: ['js'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
builder: 'mkdist',
|
|
||||||
input: './src',
|
|
||||||
outDir: './dist',
|
|
||||||
pattern: ['**/*.ts', '**/*.css'],
|
|
||||||
format: 'cjs',
|
|
||||||
loaders: ['js'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
builder: 'mkdist',
|
|
||||||
input: './src',
|
|
||||||
outDir: './dist',
|
|
||||||
pattern: ['**/*.vue'],
|
|
||||||
loaders: ['vue'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
declaration: true,
|
|
||||||
clean: true,
|
|
||||||
sourcemap: true,
|
|
||||||
rollup: { emitCJS: true },
|
|
||||||
})
|
|
|
@ -1,71 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@project-trans/vitepress-theme-project-trans",
|
|
||||||
"type": "module",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "A VitePress theme made with ❤️ by project-trans",
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/project-trans/RLE-wiki"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"./components": {
|
|
||||||
"types": "./dist/components.d.ts",
|
|
||||||
"import": "./dist/components.mjs",
|
|
||||||
"require": "./dist/components.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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",
|
|
||||||
"@nolebase/vitepress-plugin-meta": "^2.2.1",
|
|
||||||
"@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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import DefaultTheme from 'vitepress/theme'
|
|
||||||
import {
|
|
||||||
NolebaseHighlightTargetedHeading,
|
|
||||||
} from '@nolebase/vitepress-plugin-highlight-targeted-heading/client'
|
|
||||||
import {
|
|
||||||
NolebaseEnhancedReadabilitiesMenu,
|
|
||||||
NolebaseEnhancedReadabilitiesScreenMenu,
|
|
||||||
} from '@nolebase/vitepress-plugin-enhanced-readabilities/client'
|
|
||||||
|
|
||||||
import { useData } from 'vitepress'
|
|
||||||
import CopyrightInfo from './components/CopyrightInfo.vue'
|
|
||||||
import AppFooter from './components/AppFooter.vue'
|
|
||||||
import AppearanceToggle from './components/AppearanceToggle.vue'
|
|
||||||
import PageInfo from './components/PageInfo.vue'
|
|
||||||
|
|
||||||
const { Layout } = DefaultTheme
|
|
||||||
const { frontmatter } = useData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<AppearanceToggle>
|
|
||||||
<Layout>
|
|
||||||
<template #doc-before>
|
|
||||||
<NolebaseHighlightTargetedHeading />
|
|
||||||
<div class="vp-doc vp-doc-before">
|
|
||||||
<h1>{{ frontmatter.title }}</h1>
|
|
||||||
<PageInfo />
|
|
||||||
<CopyrightInfo />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #doc-after>
|
|
||||||
<AppFooter />
|
|
||||||
</template>
|
|
||||||
<template #nav-bar-content-after>
|
|
||||||
<NolebaseEnhancedReadabilitiesMenu />
|
|
||||||
</template>
|
|
||||||
<template #nav-screen-content-after>
|
|
||||||
<NolebaseEnhancedReadabilitiesScreenMenu />
|
|
||||||
</template>
|
|
||||||
</Layout>
|
|
||||||
</AppearanceToggle>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.vp-doc-before + main > div > div > h2:first-of-type {
|
|
||||||
border-top: unset;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,19 +0,0 @@
|
||||||
import AppearanceToggle from './components/AppearanceToggle.vue'
|
|
||||||
import AppFooter from './components/AppFooter.vue'
|
|
||||||
import AppSBox from './components/AppSBox.vue'
|
|
||||||
import ArticlesMenu from './components/ArticlesMenu.vue'
|
|
||||||
import CopyrightInfo from './components/CopyrightInfo.vue'
|
|
||||||
import HomeContent from './components/HomeContent.vue'
|
|
||||||
import PageInfo from './components/PageInfo.vue'
|
|
||||||
import ReadingTime from './components/ReadingTime.vue'
|
|
||||||
|
|
||||||
export {
|
|
||||||
AppFooter,
|
|
||||||
AppSBox,
|
|
||||||
AppearanceToggle,
|
|
||||||
ArticlesMenu,
|
|
||||||
CopyrightInfo,
|
|
||||||
HomeContent,
|
|
||||||
PageInfo,
|
|
||||||
ReadingTime,
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, watch, } from 'vue';
|
|
||||||
import { NolebaseGitChangelog } from '@nolebase/vitepress-plugin-git-changelog/client';
|
|
||||||
import { useRoute } from 'vitepress';
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
// 定义一个 ref 来存储动态 key
|
|
||||||
const componentKey = ref(0);
|
|
||||||
const frontmatter = ref({});
|
|
||||||
const isFrontmatterLoaded = ref(false);
|
|
||||||
|
|
||||||
// 更新 key 和 frontmatter 的函数
|
|
||||||
const updateKeyAndFrontmatter = () => {
|
|
||||||
componentKey.value += 1;
|
|
||||||
frontmatter.value = route.data?.frontmatter || {};
|
|
||||||
isFrontmatterLoaded.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听路由变化,更新 key 和 frontmatter
|
|
||||||
watch(() => route.path, () => {
|
|
||||||
isFrontmatterLoaded.value = false;
|
|
||||||
updateKeyAndFrontmatter();
|
|
||||||
}, { immediate: true }); // 在组件挂载时立即执行一次,确保第一次渲染时 key 和 frontmatter 是正确的
|
|
||||||
|
|
||||||
// 在组件挂载时更新 key 和 frontmatter
|
|
||||||
// onMounted(updateKeyAndFrontmatter);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div :key="componentKey" class="vp-doc">
|
|
||||||
<h2 id="意见反馈">
|
|
||||||
意见反馈
|
|
||||||
</h2>
|
|
||||||
<AppSBox />
|
|
||||||
<!-- 仅在 Frontmatter 加载完成且未设置 hideChangelog 时渲染 GitChangelog -->
|
|
||||||
<NolebaseGitChangelog v-if="isFrontmatterLoaded && !frontmatter.hideChangelog" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import SuggestionBox from '@project-trans/suggestion-box'
|
|
||||||
import '@project-trans/suggestion-box/dist/style.css'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<SuggestionBox
|
|
||||||
text-content-placeholder="这个页面有什么问题?或者有什么建议?请在下方留言,我们会尽快回复和处理。"
|
|
||||||
target-url="/api/v1/suggestion"
|
|
||||||
/>
|
|
||||||
</template>
|
|
|
@ -1,76 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useData } from 'vitepress'
|
|
||||||
import { nextTick, provide } from 'vue'
|
|
||||||
|
|
||||||
const { isDark } = useData()
|
|
||||||
|
|
||||||
const isSSR = typeof window === 'undefined'
|
|
||||||
|
|
||||||
function enableTransitions() {
|
|
||||||
return isSSR
|
|
||||||
? false
|
|
||||||
: 'startViewTransition' in document
|
|
||||||
&& window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
|
||||||
}
|
|
||||||
|
|
||||||
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
|
||||||
if (!enableTransitions()) {
|
|
||||||
isDark.value = !isDark.value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!document.documentElement.classList.contains('VPSwitchAppearance-ViewTransition'))
|
|
||||||
document.documentElement.classList.add('VPSwitchAppearance-ViewTransition')
|
|
||||||
|
|
||||||
const clipPath = [
|
|
||||||
`circle(0px at ${x}px ${y}px)`,
|
|
||||||
`circle(${Math.hypot(
|
|
||||||
Math.max(x, innerWidth - x),
|
|
||||||
Math.max(y, innerHeight - y),
|
|
||||||
)}px at ${x}px ${y}px)`,
|
|
||||||
]
|
|
||||||
|
|
||||||
await (document as any).startViewTransition(async () => {
|
|
||||||
isDark.value = !isDark.value
|
|
||||||
await nextTick()
|
|
||||||
}).ready
|
|
||||||
|
|
||||||
document.documentElement.animate(
|
|
||||||
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
|
|
||||||
{
|
|
||||||
duration: 500,
|
|
||||||
easing: 'ease-in',
|
|
||||||
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<slot />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
::view-transition-old(root),
|
|
||||||
::view-transition-new(root) {
|
|
||||||
animation: none;
|
|
||||||
mix-blend-mode: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
::view-transition-old(root),
|
|
||||||
.dark::view-transition-new(root) {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::view-transition-new(root),
|
|
||||||
.dark::view-transition-old(root) {
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.VPSwitchAppearance-ViewTransition .VPSwitchAppearance .check {
|
|
||||||
transition: transform 350ms 0ms !important;
|
|
||||||
}
|
|
||||||
.VPSwitchAppearance-ViewTransition.dark .VPSwitchAppearance .check {
|
|
||||||
transition: transform 350ms 500ms !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRoute } from 'vitepress'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { data } from './articlesmenu.data'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const articles = computed(() =>
|
|
||||||
data
|
|
||||||
.filter((article) => {
|
|
||||||
if (!article.url.startsWith(route.path))
|
|
||||||
return false
|
|
||||||
if (article.url === route.path)
|
|
||||||
return false
|
|
||||||
const relateUrl = article.url.replace(route.path, '')
|
|
||||||
const slashCount = relateUrl.split('/').length - 1
|
|
||||||
if (slashCount > 1)
|
|
||||||
return false
|
|
||||||
if (slashCount === 1 && !relateUrl.endsWith('/'))
|
|
||||||
return false
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
.map(article => ({ link: article.url, text: article.title })),
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<li v-for="article in articles" :key="article.link">
|
|
||||||
<a :href="article.link">{{ article.text }}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
|
@ -1,68 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useData } from 'vitepress'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import type { Node, Trie } from '../plugins/CopyrightLoader.data'
|
|
||||||
import { data } from '../plugins/CopyrightLoader.data'
|
|
||||||
|
|
||||||
function searchClosestInTrie(
|
|
||||||
that: Trie<Record<string, any>>,
|
|
||||||
path: string[],
|
|
||||||
node: Node<Record<string, any>> = that.root,
|
|
||||||
): Record<string, any> | null {
|
|
||||||
if (path.length === 0)
|
|
||||||
return node.value
|
|
||||||
|
|
||||||
if (path[0] in node.children) {
|
|
||||||
let value = searchClosestInTrie(
|
|
||||||
that,
|
|
||||||
path.slice(1),
|
|
||||||
node.children[path[0]],
|
|
||||||
)
|
|
||||||
if (value === null)
|
|
||||||
value = node.value
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return node.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const paths = useData()
|
|
||||||
.page.value.relativePath.replace('.md', '').split('/')
|
|
||||||
.filter((item: string) => item !== '')
|
|
||||||
const attrs = computed(() => searchClosestInTrie(data, paths))
|
|
||||||
const frontmatter = useData().frontmatter
|
|
||||||
|
|
||||||
const originUrlExists = computed(() => (attrs.value?.copyright?.url ?? null) != null)
|
|
||||||
const originUrl = computed(() => attrs.value?.copyright?.url ?? 'javascript:void(0)')
|
|
||||||
|
|
||||||
const license = computed(() => attrs.value?.copyright?.license ?? null)
|
|
||||||
const licenseExists = computed(() => license.value != null)
|
|
||||||
const licenseUrlExists = computed(() => (attrs.value?.copyright?.licenseUrl ?? null) != null)
|
|
||||||
const licenseUrl = computed(() => attrs.value?.copyright?.licenseUrl ?? 'javascript:void(0)')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="attrs?.copyright?.enable ?? false">
|
|
||||||
<div class="tip custom-block">
|
|
||||||
<p class="custom-block-title">
|
|
||||||
Copyright
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span>这篇文章 </span>
|
|
||||||
<a v-if="originUrlExists" :href="originUrl">{{ frontmatter.title }}</a>
|
|
||||||
<span v-else>{{ frontmatter.title }}</span>
|
|
||||||
<span> 由 </span>
|
|
||||||
<span v-for="author in attrs?.author" :key="author">{{ author }}</span>
|
|
||||||
<span> 创作</span>
|
|
||||||
<span v-if="licenseExists">
|
|
||||||
<span>,Project Trans 在 </span>
|
|
||||||
<a v-if="licenseUrlExists" :href="licenseUrl">{{ license }}</a>
|
|
||||||
<span v-else>{{ license }}</span>
|
|
||||||
<span> 许可下使用</span>
|
|
||||||
</span>
|
|
||||||
<span>。</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,27 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="vp-doc container">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
margin: auto;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1280px;
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
|
||||||
.container {
|
|
||||||
padding: 0 48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 64px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,70 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useData } from 'vitepress'
|
|
||||||
import { computed, onMounted, ref, watchEffect } from 'vue'
|
|
||||||
import ReadingTime from './ReadingTime.vue' // 导入 ReadingTime 组件
|
|
||||||
|
|
||||||
// 从 VitePress 获取页面数据
|
|
||||||
const { frontmatter, page, theme, lang } = useData()
|
|
||||||
|
|
||||||
// 计算页面的最后更新时间
|
|
||||||
const date = computed(
|
|
||||||
() => new Date(frontmatter.value.lastUpdated ?? page.value.lastUpdated),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 计算 ISO 格式的日期时间字符串
|
|
||||||
const isoDatetime = computed(() => date.value.toISOString())
|
|
||||||
|
|
||||||
// 定义一个响应式变量来存储格式化后的日期时间字符串
|
|
||||||
const datetime = ref('')
|
|
||||||
|
|
||||||
// 避免 hydration 错误,在组件挂载后执行
|
|
||||||
onMounted(() => {
|
|
||||||
watchEffect(() => {
|
|
||||||
// 使用国际化 API 格式化日期时间
|
|
||||||
datetime.value = new Intl.DateTimeFormat(
|
|
||||||
theme.value.lastUpdated?.formatOptions?.forceLocale ? lang.value : undefined,
|
|
||||||
theme.value.lastUpdated?.formatOptions ?? {
|
|
||||||
dateStyle: 'short',
|
|
||||||
timeStyle: 'short',
|
|
||||||
},
|
|
||||||
).format(date.value)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// 计算页面的作者信息
|
|
||||||
const authors = computed(() => {
|
|
||||||
let author = (frontmatter.value?.author ?? []) as string[]
|
|
||||||
if (!Array.isArray(author))
|
|
||||||
author = [author]
|
|
||||||
return author
|
|
||||||
})
|
|
||||||
|
|
||||||
// 计算显示的作者信息
|
|
||||||
const displayAuthors = computed(() => {
|
|
||||||
if (authors.value.length === 0) {
|
|
||||||
return '匿名'
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return `${authors.value.join(', ')} 等`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="mb-10 mt-4 flex flex-wrap gap-4">
|
|
||||||
<div class="inline-flex items-center gap-1">
|
|
||||||
<span class="i-octicon:person" />
|
|
||||||
<span>作者:</span>
|
|
||||||
<span>{{ displayAuthors }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline-flex items-center gap-1">
|
|
||||||
<span class="i-octicon:calendar-16" />
|
|
||||||
<span>{{ theme.lastUpdated?.text || 'Last updated' }}:</span>
|
|
||||||
<time :datetime="isoDatetime">{{ datetime }}</time>
|
|
||||||
</div>
|
|
||||||
<ClientOnly>
|
|
||||||
<ReadingTime /> <!-- 添加 ReadingTime 组件 -->
|
|
||||||
</ClientOnly>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,33 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useData } from 'vitepress'
|
|
||||||
import { ref, watch } from 'vue'
|
|
||||||
|
|
||||||
// 获取页面数据
|
|
||||||
const { frontmatter } = useData()
|
|
||||||
|
|
||||||
// 计算阅读时间的函数
|
|
||||||
function calculateReadingTime(wordCount: number) {
|
|
||||||
const wordsPerMinute = 500 // 假设中文阅读速度为每分钟500字
|
|
||||||
return Math.ceil(wordCount / wordsPerMinute) // 计算预计阅读时间
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 ref 创建响应式变量
|
|
||||||
const wordCount = ref(frontmatter.value.wordCount || 0)
|
|
||||||
const readingTime = ref(calculateReadingTime(wordCount.value))
|
|
||||||
|
|
||||||
// 监听 frontmatter 的变化
|
|
||||||
watch(() => frontmatter.value, (newFrontmatter) => {
|
|
||||||
wordCount.value = newFrontmatter.wordCount || 0
|
|
||||||
readingTime.value = calculateReadingTime(wordCount.value)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<p>字数: {{ wordCount }} 预计阅读时间: {{ readingTime }} 分钟</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* 这里可以添加样式 */
|
|
||||||
</style>
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { createContentLoader } from 'vitepress'
|
|
||||||
|
|
||||||
declare const data: { url: string, title: string }[]
|
|
||||||
|
|
||||||
export { data }
|
|
||||||
|
|
||||||
export default createContentLoader('**/*.md', {
|
|
||||||
transform: list =>
|
|
||||||
list.map(item => ({ url: item.url, title: item.frontmatter.title })),
|
|
||||||
})
|
|
|
@ -1,230 +0,0 @@
|
||||||
import fs from 'node:fs'
|
|
||||||
import path, { dirname, resolve } from 'node:path'
|
|
||||||
import process from 'node:process'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
import { GitChangelog } from '@nolebase/vitepress-plugin-git-changelog/vite'
|
|
||||||
import { transformHeadMeta } from '@nolebase/vitepress-plugin-meta'
|
|
||||||
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'
|
|
||||||
|
|
||||||
// 从文件系统读取 Markdown 文件内容
|
|
||||||
function readMarkdownFileContent(filePath: string): string {
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
return fs.readFileSync(filePath, 'utf-8')
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 统计文档的字数函数
|
|
||||||
function countWords(content: string): number {
|
|
||||||
const cleanedContent = content
|
|
||||||
.replace(/```[\s\S]*?```/g, '') // 移除代码块
|
|
||||||
.replace(/!\[.*?\]\(.*?\)/g, '') // 移除图片链接
|
|
||||||
.replace(/\[.*?\]\(.*?\)/g, '') // 移除普通链接
|
|
||||||
.replace(/<[^>]+(>|$)/g, '') // 移除 HTML 标签
|
|
||||||
.replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, '') // 移除标点符号
|
|
||||||
.replace(/\s+/g, ' ') // 将多余的空格归为一个空格
|
|
||||||
.trim() // 去除首尾空格
|
|
||||||
|
|
||||||
const chineseCharacters = cleanedContent.match(/[\u4E00-\u9FFF\uFF01-\uFFE5]/g) || []
|
|
||||||
const words = cleanedContent.split(/\s+/).filter(Boolean)
|
|
||||||
|
|
||||||
return chineseCharacters.length + words.length
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
|
||||||
function genConfig() {
|
|
||||||
const themeConfig = useThemeContext()
|
|
||||||
const { siteTitle, githubRepoLink, 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: '清除查询条件',
|
|
||||||
displayDetails: '显示详细列表',
|
|
||||||
footer: {
|
|
||||||
selectText: '选择',
|
|
||||||
navigateText: '切换',
|
|
||||||
closeText: '关闭',
|
|
||||||
// 无障碍(ARIA)标签,用于描述键盘导航操作
|
|
||||||
navigateUpKeyAriaLabel: '上箭头',
|
|
||||||
navigateDownKeyAriaLabel: '下箭头',
|
|
||||||
selectKeyAriaLabel: '回车',
|
|
||||||
closeKeyAriaLabel: '退出',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// 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: async (context) => {
|
|
||||||
let head = [...context.head]
|
|
||||||
|
|
||||||
const returnedHead = await transformHeadMeta()(head, context)
|
|
||||||
if (typeof returnedHead !== 'undefined')
|
|
||||||
head = returnedHead
|
|
||||||
|
|
||||||
return head
|
|
||||||
},
|
|
||||||
vite: {
|
|
||||||
plugins: [
|
|
||||||
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',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
transformPageData(pageData) {
|
|
||||||
// 构建 Markdown 文件路径
|
|
||||||
const markdownFile = `${pageData.relativePath}`
|
|
||||||
const filePath = path.join(process.cwd(), 'docs', markdownFile)
|
|
||||||
|
|
||||||
// 从文件系统读取文件内容
|
|
||||||
const content = readMarkdownFileContent(filePath)
|
|
||||||
|
|
||||||
// 统计字数并插入到 Frontmatter
|
|
||||||
const wordCount = countWords(content)
|
|
||||||
|
|
||||||
return {
|
|
||||||
frontmatter: {
|
|
||||||
...pageData.frontmatter,
|
|
||||||
wordCount, // 将字数写入 Frontmatter
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default genConfig
|
|
|
@ -1,4 +0,0 @@
|
||||||
:root {
|
|
||||||
--vp-font-family-base: sans-serif;
|
|
||||||
--vp-font-family-mono: monospace;
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
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,29 +0,0 @@
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
// https://vitepress.dev/guide/custom-theme
|
|
||||||
import type { Theme } from 'vitepress'
|
|
||||||
import DefaultTheme from 'vitepress/theme-without-fonts'
|
|
||||||
import { h } from 'vue'
|
|
||||||
import './custom-font.css'
|
|
||||||
|
|
||||||
import {
|
|
||||||
NolebaseEnhancedReadabilitiesPlugin,
|
|
||||||
} from '@nolebase/vitepress-plugin-enhanced-readabilities/client'
|
|
||||||
|
|
||||||
import {
|
|
||||||
NolebaseGitChangelogPlugin,
|
|
||||||
} from '@nolebase/vitepress-plugin-git-changelog/client'
|
|
||||||
import type Options from 'vitepress-sidebar'
|
|
||||||
|
|
||||||
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 Layout from './Layout.vue'
|
|
||||||
|
|
||||||
export type SidebarOptions = Options
|
|
||||||
|
|
||||||
export default {
|
|
||||||
extends: DefaultTheme,
|
|
||||||
Layout,
|
|
||||||
enhanceApp({ app }) {
|
|
||||||
app.use(NolebaseEnhancedReadabilitiesPlugin, {
|
|
||||||
spotlight: {
|
|
||||||
defaultToggle: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(NolebaseGitChangelogPlugin, {
|
|
||||||
locales: {
|
|
||||||
'zh-CN': {
|
|
||||||
lastEditedDateFnsLocaleName: 'zhCN',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
} satisfies Theme
|
|
|
@ -1,2 +0,0 @@
|
||||||
export * from './path'
|
|
||||||
export * from './themeContext'
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { normalizePath } from 'vite'
|
|
||||||
|
|
||||||
export function pathEquals(path: string, equals: string): boolean {
|
|
||||||
return normalizePath(path) === (normalizePath(equals))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pathStartsWith(path: string, startsWith: string): boolean {
|
|
||||||
return normalizePath(path).startsWith(normalizePath(startsWith))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pathEndsWith(path: string, startsWith: string): boolean {
|
|
||||||
return normalizePath(path).endsWith(normalizePath(startsWith))
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
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()!
|
|
||||||
}
|
|
6638
pnpm-lock.yaml
6638
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,2 @@
|
||||||
packages:
|
packages:
|
||||||
- functions
|
- functions
|
||||||
- packages/*
|
|
||||||
|
|
Loading…
Reference in New Issue