├── .eslintignore ├── .vitepress ├── docsTagsAlias.json ├── theme │ ├── components │ │ ├── DocFooter.vue │ │ ├── HomePage.vue │ │ ├── AppContainer.vue │ │ ├── TocList.vue │ │ └── Share.vue │ └── index.ts ├── creators.ts ├── styles │ ├── kbd.css │ ├── vars.css │ └── main.css └── config.ts ├── .markdownlintignore ├── toc.md ├── netlify.toml ├── public ├── og.png ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── mstile-70x70.png ├── apple-touch-icon.png ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── tengwar-annatar-glaemscrafu.woff2 ├── tengwar-annatar-glaemscrafu-bold.woff2 ├── browserconfig.xml ├── _redirects ├── obsidian-logo.svg ├── site.webmanifest ├── safari-pinned-tab.svg └── logo.svg ├── .gitattributes ├── 笔记 ├── 演示目录1 │ ├── 文档-带评论.md │ └── 文档-不带评论.md ├── ✍️ 文档工程 │ ├── 图表即代码(CaC)图表绘制.md │ └── API 文档系统.md └── index.md ├── .editorconfig ├── eslint.config.js ├── 视图 ├── 尚未编写完成的页面.md └── 未添加标签的页面.md ├── metadata └── index.ts ├── .gitignore ├── uno.config.ts ├── tsconfig.json ├── .github ├── FUNDING.yml └── workflows │ ├── production-deployment-to-github-pages.yaml │ ├── production-deployment.yaml │ ├── preview-docs-build.yaml │ └── preview-docs-deployment.yaml ├── scripts ├── types │ └── metadata.d.ts └── update.ts ├── .markdownlint.jsonc ├── index.md ├── LICENSE ├── .vscode └── settings.json ├── vite.config.ts ├── README.md ├── 🔌 知识库插件列表.md ├── package.json └── LICENSE-CC-BY-SA /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | -------------------------------------------------------------------------------- /.vitepress/docsTagsAlias.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | node_modules 3 | -------------------------------------------------------------------------------- /toc.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | 3 | 排序方式:`最近更新` 4 | 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 5 | -------------------------------------------------------------------------------- /public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/og.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/mstile-70x70.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/mstile-144x144.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/mstile-150x150.png -------------------------------------------------------------------------------- /public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/mstile-310x150.png -------------------------------------------------------------------------------- /public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/mstile-310x310.png -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/tengwar-annatar-glaemscrafu.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/tengwar-annatar-glaemscrafu.woff2 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-vendored=false 2 | *.md linguist-generated=false 3 | *.md linguist-documentation=false 4 | *.md linguist-detectable=true 5 | -------------------------------------------------------------------------------- /public/tengwar-annatar-glaemscrafu-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackiexiao/nolebase-template/HEAD/public/tengwar-annatar-glaemscrafu-bold.woff2 -------------------------------------------------------------------------------- /笔记/演示目录1/文档-带评论.md: -------------------------------------------------------------------------------- 1 | --- 2 | comment: true 3 | --- 4 | 5 | # 文档-带评论 6 | 7 | 设置 8 | ``` 9 | --- 10 | comment: true 11 | --- 12 | ``` 13 | 14 | 你好 nolebase! -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /笔记/演示目录1/文档-不带评论.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: 3 | - 标签内容 4 | comment: false 5 | --- 6 | 7 | # 文档-不带评论 8 | 9 | 设置 10 | ``` 11 | --- 12 | comment: false 13 | --- 14 | ``` 15 | 16 | ## 标题1 17 | 18 | ## 标题2 19 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | 3 | export default antfu({ 4 | unocss: true, 5 | ignores: [ 6 | '**/*.md', 7 | '**/*.yaml', 8 | '**/*.yml', 9 | ], 10 | }) 11 | -------------------------------------------------------------------------------- /视图/尚未编写完成的页面.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: 3 | - 视图 4 | - 软件/Obsidian/插件/Dataview 5 | - 软件/Obsidian/插件 6 | --- 7 | # 尚未编写完成的页面 8 | 9 | ```dataview 10 | TABLE 11 | file.path AS "路径" 12 | FROM "笔记" 13 | WHERE status = "尚未完成" 14 | ``` -------------------------------------------------------------------------------- /视图/未添加标签的页面.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: 3 | - 视图 4 | - 软件/Obsidian/插件 5 | - 软件/Obsidian/插件/Dataview 6 | --- 7 | # 未添加标签的页面 8 | 9 | ```dataview 10 | TABLE 11 | file.path AS "路径" 12 | FROM "笔记" 13 | WHERE length(file.tags) = 0 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #603cba 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | # Plausible.io analytics 2 | # 3 | # Proxying Plausible through Netlify | Plausible docs 4 | # https://plausible.io/docs/proxy/guides/netlify 5 | /assets/page-external-data/js/script.js https://plausible.io/js/script.js 200 6 | /api/v1/page-external-data/submit https://plausible.io/api/event 200 7 | 8 | # Short link 9 | /to/* /.netlify/functions/to/:splat 301 10 | -------------------------------------------------------------------------------- /public/obsidian-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vitepress/theme/components/DocFooter.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 17 | -------------------------------------------------------------------------------- /metadata/index.ts: -------------------------------------------------------------------------------- 1 | /** 文本 */ 2 | export const siteName = 'Nólëbase' 3 | export const siteShortName = 'Nólëbase' 4 | export const siteDescription = '记录回忆,知识和畅想的地方' 5 | 6 | /** 文档所在目录 */ 7 | export const include = ['笔记', '生活'] 8 | 9 | /** Repo */ 10 | export const githubRepoLink = 'https://github.com/nolebase/nolebase' 11 | /** Discord */ 12 | export const discordLink = 'https://discord.gg/XuNFDcDZGj' 13 | 14 | /** 无协议前缀域名 */ 15 | export const plainTargetDomain = 'nolebase.ayaka.io' 16 | /** 完整域名 */ 17 | export const targetDomain = `https://${plainTargetDomain}` 18 | -------------------------------------------------------------------------------- /笔记/✍️ 文档工程/图表即代码(CaC)图表绘制.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: 3 | - 知识领域/文档工程 4 | --- 5 | # 图表即代码(CaC)图表绘制 6 | 7 | CaC: Charts as Code 8 | 9 | ## 工具 10 | 11 | ### Mermaid 12 | 13 | 官方网站:[Mermaid](https://mermaid.js.org/) 14 | 开源 GitHub 仓库:[mermaid-js/mermaid](https://github.com/mermaid-js/mermaid) 15 | 16 | 有多种图表支持。 17 | #### Mermaid 生态 18 | 19 | - 自带编辑器:[Mermaid 实时编辑器](https://mermaid.live) 20 | 21 | ### Nomnoml 22 | 23 | 官方网站:[nomnoml](https://nomnoml.com/) 24 | 开源 GitHub 仓库:[skanaar/nomnoml](https://github.com/skanaar/nomnoml) 25 | 26 | 主要面向程序中的代码对象结构,编排,关系进行绘图。 27 | 28 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "\u732b\u97f3\u77e5\u8bc6\u5e93", 3 | "short_name": "\u732b\u97f3\u77e5\u8bc6\u5e93", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /笔记/✍️ 文档工程/API 文档系统.md: -------------------------------------------------------------------------------- 1 | --- 2 | status: 尚未完成 3 | --- 4 | # API 文档系统 5 | 6 | ## 说明 7 | 8 | 一个能够追踪 API 响应对象中的字段在「什么时候」、「什么版本」、「什么提交」里面新增或者发生变更,甚至是提供即将废弃的注解。 9 | 10 | 在开发的时候期间,Staging 过渡阶段,FAT 特性验收测试阶段,UAT 用户可接受度测试阶段是没办法很好的记录到版本号的,可能得根据提交记录和提交时间来完成,否则开发者得把 API 版本和后端版本剥离,并分别进行记录。不过在我看来,记录版本号、剥离版本号并记录的任意一种方案,甚至是记录提交这类的事情不应该是 API 接口开发者应该做的,应当在 API 定义发生变更之后由 API 文档系统自动变更版本号并且在文档中正确渲染和提示到使用用户。 11 | 12 | 可能的情况下,该系统甚至能提供一些 DevOps 联动,在必要的情况下可以允许文档系统提示该字段所处的阶段,比如能展示到 Staging 阶段或者是 Production 阶段。 13 | 14 | 可能的情况下,该系统还应该提示 beta / experiment API 注解高亮说明,并建议「为生产产品开发的开发者:尽可能避免使用正处于 beta / experiment 的 API,这些 API 可能随时发生变更,暂时不稳定,并采用其他替代方案实现」。 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .obsidian/ 2 | .Trash/ 3 | .trash/ 4 | .DS_Store 5 | 6 | # IDE related 7 | 8 | .idea 9 | 10 | # Obsidian 11 | 12 | **/.obsidian/* 13 | !**/.obsidian/workspace* 14 | !**/.obsidian/graph* 15 | !**/.obsidian/snippets/ 16 | 17 | # Temporary files 18 | 19 | .temp 20 | 21 | # Nuxt 22 | 23 | .nuxt 24 | 25 | # Node.js 26 | 27 | node_modules 28 | .npmrc 29 | 30 | # VitePress 31 | 32 | **/.vitepress/dist 33 | **/.vitepress/docsMetadata.json 34 | **/.vitepress/cache/ 35 | 36 | # Netlify 37 | 38 | .netlify/functions-serve/* 39 | 40 | # Generated files 41 | 42 | components.d.ts 43 | 44 | # Local env files 45 | 46 | *.local 47 | 48 | # Logs 49 | 50 | *.log 51 | -------------------------------------------------------------------------------- /.vitepress/theme/components/HomePage.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | -------------------------------------------------------------------------------- /uno.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss' 2 | 3 | export default defineConfig({ 4 | shortcuts: [ 5 | ['btn', 'px-4 py-1 rounded inline-flex justify-center gap-2 text-white leading-30px children:mya !no-underline cursor-pointer disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'], 6 | ], 7 | presets: [ 8 | presetUno({ 9 | dark: 'class', 10 | }), 11 | presetAttributify(), 12 | presetIcons({ 13 | prefix: 'i-', 14 | scale: 1.2, // size: 1.2 rem 15 | extraProperties: { 16 | 'display': 'inline-block', 17 | 'vertical-align': 'middle', 18 | 'min-width': '1.2rem', 19 | }, 20 | warn: true, 21 | }), 22 | ], 23 | }) 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "lib": [ 6 | "DOM", 7 | "ESNext" 8 | ], 9 | "baseUrl": ".", 10 | "module": "ESNext", 11 | "moduleResolution": "Bundler", 12 | "resolveJsonModule": true, 13 | "types": [ 14 | "vite/client" 15 | ], 16 | "allowJs": true, 17 | "strict": true, 18 | "strictNullChecks": true, 19 | "noUnusedLocals": true, 20 | "esModuleInterop": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "isolatedModules": true, 23 | "skipLibCheck": true 24 | }, 25 | "include": [ 26 | "**/*.ts", 27 | "**/*.d.ts", 28 | "**/*.tsx", 29 | "**/*.vue", 30 | ".vitepress/**/*.ts", 31 | ".vitepress/**/*.tsx", 32 | ".vitepress/**/*.vue" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [LittleSound,nekomeowww] 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: # Replace with a single Open Collective username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /scripts/types/metadata.d.ts: -------------------------------------------------------------------------------- 1 | export interface ArticleTree { 2 | index: string 3 | text: string 4 | link?: string 5 | lastUpdated?: number 6 | collapsible?: true 7 | collapsed?: true 8 | items?: ArticleTree[] 9 | category?: string 10 | } 11 | 12 | export interface Doc { 13 | relativePath: string 14 | hashes: { 15 | sha256: { 16 | content: string 17 | contentDiff?: string 18 | } 19 | } 20 | ignoreWhenGenerateTagsFromGPT?: boolean 21 | } 22 | 23 | export interface Tag { 24 | name: string 25 | alias: string[] 26 | description: string 27 | appearedInDocs: string[] 28 | count: number 29 | } 30 | 31 | export interface DocsMetadata { 32 | docs: Doc[] 33 | tags: Tag[] 34 | sidebar: ArticleTree[] 35 | } 36 | 37 | export interface DocsTagsAlias { 38 | name: string 39 | alias: string[] 40 | } 41 | -------------------------------------------------------------------------------- /.markdownlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | // Example markdownlint JSON(C) configuration with all properties set to their default value 3 | // Default state for all rules 4 | "default": true, 5 | 6 | // Path to configuration file to extend 7 | "extends": null, 8 | 9 | // MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time 10 | "MD001": false, 11 | 12 | // MD013/line-length - Line length 13 | "MD013": false, 14 | 15 | "MD024": false, 16 | 17 | // MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading 18 | "MD041": false, 19 | 20 | // MD033/no-inline-html - Inline HTML 21 | "MD033": false, 22 | 23 | // MD045/no-bare-urls - Images should have alternate text (alt text) 24 | "MD045": false, 25 | 26 | // MD010/no-hard-tabs - Hard tabs 27 | "MD010": false, 28 | 29 | // MD029/ol-prefix - Ordered list item prefix 30 | "MD029": false, 31 | 32 | // MD007/ul-indent - Unordered list indentation 33 | "MD007": false, 34 | 35 | "MD028": false 36 | } 37 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | sidebar: false 4 | 5 | title: Nólëbase 6 | titleTemplate: 记录回忆,知识和畅想的地方 7 | 8 | hero: 9 | name: g~Nj$3J2^ 10 | text: 记录回忆,知识和畅想的地方 11 | tagline: 以 Nólëbase 为名,读作 nole-base,取自意为「知识」的昆雅语 nólë 和意为「基础」的英文 base,即「知识库」 12 | image: 13 | src: /logo.svg 14 | alt: Vitest 15 | actions: 16 | - theme: brand 17 | text: 开始阅读 18 | link: /笔记/index 19 | - theme: alt 20 | text: 加入 Discord 服务器 21 | link: https://discord.gg/XuNFDcDZGj 22 | - theme: alt 23 | text: GitHub 上浏览 24 | link: https://github.com/nolebase/nolebase 25 | 26 | features: 27 | - title: 多样的主题和内容 28 | details: 本知识库和所生成的页面均由创作者们维护,涉及到生活中各方面知识和内容,也不乏我们的回忆和畅想。 29 | icon: 🌈 30 | - title: 皆为 Markdown 31 | details: 使用 Markdown 和 Markdown 拓展语法编写和记录笔记,每一个页面都是 Markdown 文件。 32 | icon: 📃 33 | - title: 由 VitePress 驱动 34 | details: 基于 Vite 的强大静态文档页面生成器,它生成了我们知识库的页面,提供了简单易用的主题和工具。 35 | icon: 🚀 36 | - title: 由 Obsidian 驱动 37 | details: 强大的知识库管理工具,支持花样繁多的插件和拓展,让知识管理变得更加简单。 38 | icon: 🗃 39 | --- 40 | 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ayaka Rizumu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.vitepress/theme/components/AppContainer.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "antfu", 4 | "Attributify", 5 | "headlessui", 6 | "iconify", 7 | "mathjax", 8 | "nolebase", 9 | "unlazy", 10 | "vitepress", 11 | "vuedraggable" 12 | ], 13 | "prettier.enable": false, 14 | "editor.formatOnSave": false, 15 | "editor.codeActionsOnSave": { 16 | "source.fixAll.eslint": "explicit", 17 | "source.organizeImports": "never" 18 | }, 19 | // Enable the ESlint flat config support 20 | "eslint.experimental.useFlatConfig": true, 21 | // The following is optional. 22 | // It's better to put under project setting `.vscode/settings.json` 23 | // to avoid conflicts with working with different eslint configs 24 | // that does not support all formats. 25 | "eslint.validate": [ 26 | "javascript", 27 | "javascriptreact", 28 | "typescript", 29 | "typescriptreact", 30 | "vue", 31 | "html", 32 | "markdown", 33 | "json", 34 | "jsonc", 35 | "yaml" 36 | ], 37 | "unocss.root": [ 38 | "." 39 | ], 40 | "typescript.tsdk": "node_modules/typescript/lib", 41 | "workbench.colorCustomizations": { 42 | "activityBar.background": "#3A2C04", 43 | "titleBar.activeBackground": "#523D05", 44 | "titleBar.activeForeground": "#FEFAED" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.vitepress/theme/components/TocList.vue: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 48 | -------------------------------------------------------------------------------- /.github/workflows/production-deployment-to-github-pages.yaml: -------------------------------------------------------------------------------- 1 | name: 构建并部署到 Github Pages 2 | 3 | on: 4 | workflow_dispatch: 5 | # push: 6 | # branches: 7 | # - 'main' 8 | 9 | 10 | env: 11 | STORE_PATH: '' 12 | 13 | concurrency: 14 | group: ${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build: 19 | name: Ubuntu 构建和推送 20 | runs-on: ubuntu-latest 21 | environment: 22 | name: 正式 Production - GitHub Pages 23 | url: https://nolebase.github.io/nolebase 24 | steps: 25 | - name: 签出代码 26 | uses: actions/checkout@v3 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: 安装 Node.js 20.x 31 | uses: actions/setup-node@v3 32 | with: 33 | node-version: 20.x 34 | 35 | - name: 安装 pnpm 36 | uses: pnpm/action-setup@v2 37 | with: 38 | run_install: false 39 | version: 8 40 | 41 | - name: 获取 pnpm store 目录 42 | shell: bash 43 | run: | 44 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 45 | 46 | - name: 配置 pnpm 缓存 47 | uses: actions/cache@v3 48 | with: 49 | path: ${{ env.STORE_PATH }} 50 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 51 | restore-keys: | 52 | ${{ runner.os }}-pnpm-store- 53 | 54 | - name: 安装依赖 55 | run: pnpm install --frozen-lockfile 56 | 57 | - name: 构建 58 | run: pnpm docs:build 59 | 60 | - name: 推送到 gh-pages 分支 61 | timeout-minutes: 10 62 | with: 63 | token: ${{ secrets.ACCESS_TOKEN }} 64 | BRANCH: gh-pages 65 | FOLDER: .vitepress/dist 66 | uses: JamesIves/github-pages-deploy-action@v4 67 | 68 | -------------------------------------------------------------------------------- /.github/workflows/production-deployment.yaml: -------------------------------------------------------------------------------- 1 | name: 构建并部署到 Netlify 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | workflow_dispatch: 8 | 9 | env: 10 | STORE_PATH: '' 11 | 12 | concurrency: 13 | group: ${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | build: 18 | name: Ubuntu 构建和推送 19 | runs-on: ubuntu-latest 20 | environment: 21 | name: 正式 Production 22 | url: https://nolebase.ayaka.io 23 | steps: 24 | - name: 签出代码 25 | uses: actions/checkout@v3 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: 安装 Node.js 20.x 30 | uses: actions/setup-node@v3 31 | with: 32 | node-version: 20.x 33 | 34 | - name: 安装 pnpm 35 | uses: pnpm/action-setup@v2 36 | with: 37 | run_install: false 38 | version: 8 39 | 40 | - name: 获取 pnpm store 目录 41 | shell: bash 42 | run: | 43 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 44 | 45 | - name: 配置 pnpm 缓存 46 | uses: actions/cache@v3 47 | with: 48 | path: ${{ env.STORE_PATH }} 49 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 50 | restore-keys: | 51 | ${{ runner.os }}-pnpm-store- 52 | 53 | - name: 安装依赖 54 | run: pnpm install --frozen-lockfile 55 | 56 | - name: 构建 57 | run: pnpm docs:build 58 | 59 | - name: 安装 Netlify CLI 60 | run: pnpm install -g netlify-cli 61 | 62 | - name: 推送到 Netlify 63 | timeout-minutes: 10 64 | run: netlify deploy --dir .vitepress/dist --prod 65 | env: 66 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 67 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 68 | 69 | -------------------------------------------------------------------------------- /.vitepress/creators.ts: -------------------------------------------------------------------------------- 1 | export interface SocialEntry { 2 | type: 'github' | 'twitter' | 'email' 3 | icon: string 4 | link: string 5 | } 6 | 7 | export interface Creator { 8 | avatar: string 9 | name: string 10 | username?: string 11 | title?: string 12 | org?: string 13 | desc?: string 14 | links?: SocialEntry[] 15 | nameAliases?: string[] 16 | emailAliases?: string[] 17 | } 18 | 19 | const getAvatarUrl = (name: string) => `https://github.com/${name}.png` 20 | 21 | export const creators: Creator[] = [ 22 | { 23 | name: '絢香猫', 24 | avatar: '', 25 | username: 'nekomeowww', 26 | title: 'Nólëbase 原始创作者', 27 | desc: '开发者,专注于基础设施维护,数据分析,后端、DevOps 开发', 28 | links: [ 29 | { type: 'github', icon: 'github', link: 'https://github.com/nekomeowww' }, 30 | { type: 'twitter', icon: 'twitter', link: 'https://twitter.com/ayakaneko' }, 31 | ], 32 | nameAliases: ['nekomeowww', '绚香猫', '絢香猫', 'Neko Ayaka', 'Ayaka Neko'], 33 | emailAliases: ['neko@ayaka.moe'], 34 | }, 35 | { 36 | name: '絢香音', 37 | avatar: '', 38 | username: 'LittleSound', 39 | title: 'Nólëbase 原始创作者', 40 | desc: '开源开发者,专注于前端,以及前端相关工具库和工具链开发', 41 | links: [ 42 | { type: 'github', icon: 'github', link: 'https://github.com/LittleSound' }, 43 | { type: 'twitter', icon: 'twitter', link: 'https://twitter.com/OikawaRizumu' }, 44 | ], 45 | nameAliases: ['LittleSound', '绚香音', '絢香音', 'Rizumu Oikawa', 'Rizumu Ayaka', 'Ayaka Rizumu', 'Rizumu'], 46 | emailAliases: ['rizumu@ayaka.moe', 'rizumu@oqo.moe'], 47 | }, 48 | ].map((c) => { 49 | c.avatar = c.avatar || getAvatarUrl(c.username) 50 | return c as Creator 51 | }) 52 | 53 | export const creatorNames = creators.map(c => c.name) 54 | export const creatorUsernames = creators.map(c => c.username || '') 55 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import Components from 'unplugin-vue-components/vite' 3 | import UnoCSS from 'unocss/vite' 4 | import Inspect from 'vite-plugin-inspect' 5 | 6 | import { GitChangelog, GitChangelogMarkdownSection } from '@nolebase/vitepress-plugin-git-changelog/vite' 7 | import { PageProperties, PagePropertiesMarkdownSection } from '@nolebase/vitepress-plugin-page-properties/vite' 8 | import { ThumbnailHashImages } from '@nolebase/vitepress-plugin-thumbnail-hash/vite' 9 | 10 | import { githubRepoLink } from './metadata' 11 | 12 | export default defineConfig(async () => { 13 | return { 14 | assetsInclude: ['**/*.mov'], 15 | optimizeDeps: { 16 | // vitepress is aliased with replacement `join(DIST_CLIENT_PATH, '/index')` 17 | // This needs to be excluded from optimization 18 | exclude: [ 19 | 'vitepress', 20 | ], 21 | }, 22 | plugins: [ 23 | Inspect(), 24 | GitChangelog({ 25 | repoURL: () => githubRepoLink, 26 | }), 27 | GitChangelogMarkdownSection({ 28 | getChangelogTitle: (): string => { 29 | return '文件历史' 30 | }, 31 | getContributorsTitle: (): string => { 32 | return '贡献者' 33 | }, 34 | excludes: [ 35 | 'toc.md', 36 | 'index.md', 37 | ], 38 | }), 39 | PageProperties(), 40 | PagePropertiesMarkdownSection({ 41 | excludes: [ 42 | 'toc.md', 43 | 'index.md', 44 | ], 45 | }), 46 | ThumbnailHashImages(), 47 | Components({ 48 | include: [/\.vue$/, /\.md$/], 49 | dirs: '.vitepress/theme/components', 50 | dts: '.vitepress/components.d.ts', 51 | }), 52 | UnoCSS(), 53 | ], 54 | ssr: { 55 | noExternal: [ 56 | '@nolebase/vitepress-plugin-enhanced-readabilities', 57 | '@nolebase/vitepress-plugin-highlight-targeted-heading', 58 | '@nolebase/vitepress-plugin-inline-link-preview', 59 | ], 60 | }, 61 | } 62 | }) 63 | -------------------------------------------------------------------------------- /.github/workflows/preview-docs-build.yaml: -------------------------------------------------------------------------------- 1 | name: 构建预览 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | env: 10 | STORE_PATH: '' 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | build: 18 | name: 构建 19 | 20 | runs-on: ubuntu-latest 21 | steps: 22 | # This is quite weird. 23 | # Eventhough this is the *intended* solution introduces in official blog post here 24 | # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/. 25 | # But still, as https://github.com/orgs/community/discussions/25220#discussioncomment-7856118 stated, 26 | # this is vulnerable since there is no source of truth about which PR in the triggered workflow. 27 | - name: 保留 PR 信息 28 | run: | 29 | echo "${{ github.event.number }}" > pr_num 30 | 31 | - name: 上传 PR 信息 32 | uses: actions/upload-artifact@v2 33 | with: 34 | name: pr-num 35 | path: ./pr_num 36 | 37 | - name: 签出代码 38 | uses: actions/checkout@v3 39 | with: 40 | fetch-depth: 0 41 | 42 | - name: 安装 Node.js 20.x 43 | uses: actions/setup-node@v3 44 | with: 45 | node-version: 20.x 46 | 47 | - name: 安装 pnpm 48 | uses: pnpm/action-setup@v2 49 | with: 50 | run_install: false 51 | version: 8 52 | 53 | - name: 获取 pnpm store 目录 54 | shell: bash 55 | run: | 56 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 57 | 58 | - name: 配置 pnpm 缓存 59 | uses: actions/cache@v3 60 | with: 61 | path: ${{ env.STORE_PATH }} 62 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 63 | restore-keys: | 64 | ${{ runner.os }}-pnpm-store- 65 | 66 | - name: 安装依赖 67 | run: pnpm install --frozen-lockfile 68 | 69 | - name: 构建 70 | run: | 71 | pnpm docs:build 72 | 73 | - name: 上传构建产物 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: docs-build 77 | path: .vitepress/dist 78 | -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nolebase-template 2 | 3 | 一个简约的博客/笔记网站模板,基于 markdown + obsidian + vitepress 4 | 5 | 本仓库从[nolebase](https://github.com/nolebase/nolebase/) 精简而来,方便做 template,用于初始化仓库 6 | 7 | 演示网站: https://nolebase-template.vercel.app 8 | 9 | 做了如下改动 10 | - 精简仓库: 删除了原始的笔记,较大的文件,思源宋体文件, `.obsidian/`文件夹, `netlify`文件夹 11 | 12 | 13 | ## 使用 14 | 需要 Nodejs / pnpm 15 | 16 | ```bash 17 | pnpm install # 安装 18 | pnpm docs:dev # dev模式,本地查看文档 19 | pnpm docs:build # 构建网站发布所需要的资源, build之后在 .vitepress/dist 下, 保证在本地能构建成功后再发布比较好 20 | ``` 21 | 22 | 需要修改的内容: 23 | - 可以修改 metadata/index.ts 配置一下自己的网站信息 24 | - 再修改一下 index.md 配置一下首页 25 | - 修改 `.vitepress/creators.ts`, 添加你的 github 地址,这样的话,在每个文章下面的贡献者那里就能够链接到你的 github 首页(否则只是一个名字,无法点击)。 26 | 27 | ## 部署 28 | ### vercel 部署 29 | vercel 部署很简单, 在 vercel 中选择项目后, 修改构建的 output directory 为 .vitepress/dist 就行了(默认是 ./dist) 30 | 31 | 如果你选择了用 vercel 部署,可以关闭 netflify 的 workflow. 32 | 33 | 在 github仓库页面 -> Actions -> netlify 对应 workflow -> 右上角3个点 -> disable workflow 34 | 35 | image 36 | 37 | ### 其他方式部署 38 | 其他部署方式见[原仓库](https://github.com/nolebase/nolebase/)的说明 39 | 40 | ## Obsidian 的设置 41 | ### 关于图片链接问题 42 | 如果你的 markdown 中的图片链接没有在当前文件所在目录下,会解析出错,无法在 vitepress 中正确渲染。如果没有这个问题,你可以跳过下面的内容 43 | 44 | 解决方法: 推荐的 Obsidian Setting => Files and links 设置如下 45 | - New link format => Relative path to file 46 | - Use `[[Wikilinks]]` => False 47 | - Default location for new attachments => In subfolder under current folder 48 | - Subfolder name => assets 49 | 50 | 这么做有几个好处 51 | - 保持兼容性的markdown: 可以让文档也能在 github 中被正确渲染(github无法解析`[[双链]]`) 52 | - 方便迁移文件和图片,你只需要把图片文件夹和markdown文件一起复制就行(如果是全部汇总在某个文件夹下,以后复制比较麻烦) 53 | 54 | 额外的 tips 55 | - 对于已有的笔记和图片链接,你可以考虑使用 obsidian 插件[obsidian-link-converter](https://github.com/ozntel/obsidian-link-converter) 来帮你做自动的转换 `[[wikilink]]` 为 relative_path 的 markdown link 56 | - 同时,我建议使用这个 [clear-unused-image](https://github.com/ozntel/oz-clear-unused-images-obsidian) 插件来帮助你清除无用的图片(但记得不要运行 clear attachment ,否则 vitepress 相关代码会被移除) 57 | 58 | ## 开启 giscus 评论功能 59 | giscus 利用了 [GitHub Discussions](https://docs.github.com/en/discussions) 实现的评论系统,让访客借助 GitHub 在你的网站上留下评论!(你的github仓库必须是公开的才能使用 giscus) 60 | 61 | 具体配置方法 62 | - 第1步,访问 [Giscus](https://giscus.app/zh-CN) 网站, 参考网站上的说明,一步步操作,最后得到一个配置代码 63 | - 第2步,在 `./vitepress/theme/index.ts` 中修改 giscus 相关配置,在该文件中搜索 `giscusTalk`, 参考说明,修改配置即可 64 | 65 | ## 其他替代方案 66 | - obsidian 官方的 publish 67 | - https://github.com/oleeskild/obsidian-digital-garden 68 | - https://github.com/ObsidianPublisher/obsidian-github-publisher 69 | - https://github.com/alangrainger/share-note 70 | 71 | -------------------------------------------------------------------------------- /🔌 知识库插件列表.md: -------------------------------------------------------------------------------- 1 | # 知识库插件列表 2 | 3 | ## 必选 4 | 5 | **⚠️ 注意:这些插件在知识库中参与了文档格式化、文件解析、Markdown 语法增强、拓展等功能,如果不安装可能会导致无法正常渲染和阅读** 6 | 7 | ### Admonition 8 | 9 | 说明:将警告块样式的内容添加到 Obsidian,样式遵循 [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/reference/admonitions/) 10 | 类型:Markdown 语法拓展 11 | 地址:[valentine195/obsidian-admonition: Adds admonition block-styled content to Obsidian.md](https://github.com/valentine195/obsidian-admonition) 12 | 13 | ### Asciinema Player 14 | 15 | 说明:它支持将 asciicast 文件嵌入到您的 Markdown 文件中的插件,在预览页面就可以看到录制的命令行记录 16 | 类型:Markdown 语法拓展,文件解析 17 | 地址:[nekomeowww/obsidian-asciinema-player: This is a Obsidian plugin which supports embedding asciicast files into Markdown files](https://github.com/nekomeowww/obsidian-asciinema-player) 18 | 19 | ### Obsidian Footnotes Plugin 20 | 21 | 说明:支持使用快捷键快速添加脚注,脚注将会自动递增,使编辑和引用外部来源可以更简单,使用 command+Shift+6 快速创建脚注 22 | 类型:Markdown 语法增强 23 | 地址:[akaalias/obsidian-footnotes: Makes creating footnotes in Obsidian more fun!](https://github.com/akaalias/obsidian-footnotes) 24 | 25 | ### Dataview 26 | 27 | 说明:支持在 Markdown 文件中使用类似 SQL 的语法查询数据然后可视化结果 28 | 类型:嵌入块增强,Markdown 语法拓展,数据查询 29 | 地址:[blacksmithgu/obsidian-dataview: A high-performance data index and query language over Markdown files, for https://obsidian.md/.](https://github.com/blacksmithgu/obsidian-dataview) 30 | 31 | ## 可选 32 | 33 | **以下插件不安装并不会影响整体使用体验** 34 | 35 | ### Obsidian Git 36 | 37 | 说明:支持在安装有 Git 命令的设备和环境上通过命令面板自动创建和拉取提交 38 | 类型:功能拓展 39 | 地址:[denolehov/obsidian-git: Backup your Obsidian.md vault with git](https://github.com/denolehov/obsidian-git) 40 | 41 | ### Auto Pair Chinese Symbol 42 | 43 | 说明:支持 `《》 【】()‘’ “” 「」` 符号输入时自动补齐 44 | 类型:功能拓展,Markdown 语法增强 45 | 地址:[renmu123/obsidian-auto-pair-chinese-symbol: 中文符号自动补齐](https://github.com/renmu123/obsidian-auto-pair-chinese-symbol) 46 | 47 | ### File Explorer Note Count 48 | 49 | 说明:支持在文件浏览标签页中展示包含的文档数 50 | 类型:功能拓展 51 | 地址:[ozntel/file-explorer-note-count: Obsidian Plugin for viewing the number of elements under each folder within the file explorer](https://github.com/ozntel/file-explorer-note-count) 52 | 53 | ### Remember Cursor Position 54 | 55 | 说明:插件会记住每个文档的光标和滚动位置。这使得在笔记、链接之间切换、返回时非常方便,从而无需和找到滚动到上次所在的位置 56 | 类型:功能拓展 57 | 地址:[dy-sh/obsidian-remember-cursor-position: Obsidian plugin. Remember cursor position for each note](https://github.com/dy-sh/obsidian-remember-cursor-position) 58 | 59 | ### Advanced Tables 60 | 61 | 说明:用于格式化、方便编辑和导航表格的插件 62 | 类型:功能拓展,Markdown 语法增强,表格 63 | 地址:[tgrosinger/advanced-tables-obsidian: Improved table navigation, formatting, and manipulation in Obsidian.md](https://github.com/tgrosinger/advanced-tables-obsidian) 64 | -------------------------------------------------------------------------------- /.vitepress/styles/kbd.css: -------------------------------------------------------------------------------- 1 | /* 键盘按键样式 */ 2 | .VPDoc kbd { 3 | padding: 4px 8px; 4 | font-size: 0.8rem; 5 | cursor: pointer; 6 | user-select: none; 7 | position: relative; 8 | bottom: 2px; 9 | font-family: -apple-system,BlinkMacSystemFont,"Segoe UI Adjusted","Segoe UI","Liberation Sans",sans-serif; 10 | font-weight: 600; 11 | } 12 | 13 | .VPDoc kbd::after { 14 | display: inline; 15 | content: ''; 16 | position: absolute; 17 | left: 0; 18 | top: 0; 19 | height: 100%; 20 | width: 100%; 21 | background: hsl(0 0% 94% / 1); 22 | box-shadow: 0px 2px 0 2px hsl(0 0% 88% / 1); 23 | border-radius: 6px; 24 | font-size: 0.8rem; 25 | cursor: pointer; 26 | user-select: none; 27 | z-index: -1; 28 | } 29 | 30 | /* 夜间模式的键盘按键样式 */ 31 | .dark .VPDoc kbd::after { 32 | background: #3c3c3c; 33 | box-shadow: 0 2px 0 2px #292929; 34 | } 35 | 36 | /** 37 | 图标可以在这里复制得到 38 | https://apple.stackexchange.com/a/123577 39 | */ 40 | 41 | /* macOS command 图标 */ 42 | .VPDoc kbd[data-macos-keyboard-key="command"]::before { 43 | content: "⌘"; 44 | vertical-align: top; 45 | margin-right: 4px; 46 | } 47 | 48 | /* macOS Option 图标 */ 49 | .VPDoc kbd[data-macos-keyboard-key="option"]::before { 50 | content: "⌥"; 51 | vertical-align: top; 52 | margin-right: 4px; 53 | } 54 | 55 | .VPDoc kbd[data-windows-keyboard-key="windows"]::before { 56 | content: "⊞"; 57 | vertical-align: top; 58 | margin-right: 4px; 59 | font-size: 1.4em; 60 | } 61 | 62 | .VPDoc kbd[data-keyboard-key="shift"]::before { 63 | content: "⇧"; 64 | vertical-align: top; 65 | margin-right: 4px; 66 | } 67 | 68 | .VPDoc kbd[data-keyboard-key="return"]::before { 69 | content: "⏎"; 70 | vertical-align: top; 71 | margin-right: 4px; 72 | } 73 | 74 | .VPDoc kbd[data-keyboard-key="control"]::before { 75 | content: "⌃"; 76 | vertical-align: top; 77 | margin-right: 4px; 78 | } 79 | 80 | .VPDoc kbd[data-keyboard-key="enter"]::before { 81 | content: "⏎"; 82 | vertical-align: top; 83 | margin-right: 4px; 84 | } 85 | 86 | .VPDoc kbd[data-keyboard-key="space"]::before { 87 | content: "␣"; 88 | vertical-align: top; 89 | margin-right: 4px; 90 | font-weight: bold; 91 | } 92 | 93 | .VPDoc kbd[data-keyboard-key="up-arrow"]::before { 94 | content: "↑"; 95 | vertical-align: top; 96 | margin-right: 4px; 97 | } 98 | 99 | .VPDoc kbd[data-keyboard-key="down-arrow"]::before { 100 | content: "↓"; 101 | vertical-align: top; 102 | margin-right: 4px; 103 | } 104 | 105 | .VPDoc kbd[data-keyboard-key="left-arrow"]::before { 106 | content: "←"; 107 | vertical-align: top; 108 | margin-right: 4px; 109 | } 110 | 111 | .VPDoc kbd[data-keyboard-key="right-arrow"]::before { 112 | content: "→"; 113 | vertical-align: top; 114 | margin-right: 4px; 115 | } 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nolebase", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "author": { 6 | "name": "Nólëbase", 7 | "email": "nolebase@ayaka.moe", 8 | "url": "https://github.com/nolebase" 9 | }, 10 | "license": "MIT", 11 | "keywords": [ 12 | "vitepress", 13 | "nolebase", 14 | "markdown", 15 | "nolebase-integration", 16 | "obsidian", 17 | "knowledge-base", 18 | "vitepress-doc" 19 | ], 20 | "scripts": { 21 | "dev": "pnpm run docs:dev", 22 | "build": "pnpm run docs:build", 23 | "serve": "pnpm run docs:serve", 24 | "docs:dev": "pnpm run update && vitepress dev", 25 | "docs:build": "pnpm run update && vitepress build", 26 | "docs:serve": "vitepress serve", 27 | "update": "tsx scripts/update.ts", 28 | "test": "echo \"Error: no test specified\" && exit 1" 29 | }, 30 | "devDependencies": { 31 | "@antfu/eslint-config": "^2.12.2", 32 | "@iconify-json/eos-icons": "^1.1.10", 33 | "@iconify-json/ic": "^1.1.17", 34 | "@iconify-json/octicon": "^1.1.53", 35 | "@iconify-json/svg-spinners": "^1.1.2", 36 | "@netlify/functions": "^1.6.0", 37 | "@nolebase/markdown-it-bi-directional-links": "2.0.0-rc11", 38 | "@nolebase/markdown-it-unlazy-img": "2.0.0-rc11", 39 | "@nolebase/vitepress-plugin-enhanced-mark": "2.0.0-rc11", 40 | "@nolebase/vitepress-plugin-enhanced-readabilities": "2.0.0-rc11", 41 | "@nolebase/vitepress-plugin-git-changelog": "2.0.0-rc11", 42 | "@nolebase/vitepress-plugin-highlight-targeted-heading": "2.0.0-rc11", 43 | "@nolebase/vitepress-plugin-inline-link-preview": "2.0.0-rc11", 44 | "@nolebase/vitepress-plugin-og-image": "2.0.0-rc11", 45 | "@nolebase/vitepress-plugin-page-properties": "2.0.0-rc11", 46 | "@nolebase/vitepress-plugin-thumbnail-hash": "2.0.0-rc11", 47 | "@types/fs-extra": "^11.0.4", 48 | "@types/lodash": "^4.17.0", 49 | "@types/markdown-it": "^12.2.3", 50 | "@types/markdown-it-footnote": "^3.0.4", 51 | "@types/node-fetch": "^2.6.11", 52 | "@unocss/eslint-config": "^0.58.9", 53 | "@unocss/reset": "^0.58.9", 54 | "@vueuse/core": "^10.9.0", 55 | "@vueuse/shared": "^10.9.0", 56 | "emoji-regex": "^10.3.0", 57 | "eslint": "^8.57.0", 58 | "fast-glob": "^3.3.2", 59 | "fs-extra": "^11.2.0", 60 | "gray-matter": "^4.0.3", 61 | "less": "^4.2.0", 62 | "lodash": "^4.17.21", 63 | "markdown-it": "^13.0.2", 64 | "markdown-it-footnote": "^3.0.3", 65 | "markdown-it-mathjax3": "^4.3.2", 66 | "node-fetch": "^3.3.2", 67 | "sharp": "^0.32.6", 68 | "simple-git": "^3.24.0", 69 | "tsx": "^4.7.2", 70 | "typescript": "^5.4.4", 71 | "unocss": "^0.58.9", 72 | "unplugin-vue-components": "^0.26.0", 73 | "uuid": "^9.0.1", 74 | "vite": "^5.2.8", 75 | "vite-plugin-inspect": "^0.8.3", 76 | "vitepress": "^1.0.2", 77 | "vitepress-plugin-comment-with-giscus": "^1.1.15", 78 | "vue": "^3.4.21" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.vitepress/theme/components/Share.vue: -------------------------------------------------------------------------------- 1 | 95 | 96 | 128 | -------------------------------------------------------------------------------- /.vitepress/styles/vars.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Colors: Palette 3 | * 4 | * The primitive colors used for accent colors. These colors are referenced 5 | * by functional colors such as "Text", "Background", or "Brand". 6 | * 7 | * Each colors have exact same color scale system with 3 levels of solid 8 | * colors with different brightness, and 1 soft color. 9 | * 10 | * - `XXX-1`: The most solid color used mainly for colored text. It must 11 | * satisfy the contrast ratio against when used on top of `XXX-soft`. 12 | * 13 | * - `XXX-2`: The color used mainly for hover state of the button. 14 | * 15 | * - `XXX-3`: The color for solid background, such as bg color of the button. 16 | * It must satisfy the contrast ratio with pure white (#ffffff) text on 17 | * top of it. 18 | * 19 | * - `XXX-soft`: The color used for subtle background such as custom container 20 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors 21 | * on top of it. 22 | * 23 | * The soft color must be semi transparent alpha channel. This is crucial 24 | * because it allows adding multiple "soft" colors on top of each other 25 | * to create a accent, such as when having inline code block inside 26 | * custom containers. 27 | * -------------------------------------------------------------------------- */ 28 | 29 | :root { 30 | --vp-c-sky-1: #3c96ca; 31 | --vp-c-sky-2: #49a1d4; 32 | --vp-c-sky-3: #49a1d4; 33 | --vp-c-sky-soft: rgba(73, 161, 212, 0.84); 34 | } 35 | 36 | .dark { 37 | --vp-c-sky-1: #49a3d7; 38 | --vp-c-sky-2: #58b1e5; 39 | --vp-c-sky-3: #58b1e5; 40 | --vp-c-sky-soft: #58b1e5; 41 | } 42 | 43 | /** 44 | * Colors: Function 45 | * 46 | * - `default`: The color used purely for subtle indication without any 47 | * special meanings attched to it such as bg color for menu hover state. 48 | * 49 | * - `brand`: Used for primary brand colors, such as link text, button with 50 | * brand theme, etc. 51 | * 52 | * - `tip`: Used to indicate useful information. The default theme uses the 53 | * brand color for this by default. 54 | * 55 | * - `warning`: Used to indicate warning to the users. Used in custom 56 | * container, badges, etc. 57 | * 58 | * - `danger`: Used to show error, or dangerous message to the users. Used 59 | * in custom container, badges, etc. 60 | * 61 | * To understand the scaling system, refer to "Colors: Palette" section. 62 | * -------------------------------------------------------------------------- */ 63 | 64 | :root { 65 | /* 主色 */ 66 | --vp-c-brand-1: var(--vp-c-sky-1); 67 | --vp-c-brand-2: var(--vp-c-sky-2); 68 | --vp-c-brand-3: var(--vp-c-sky-3); 69 | --vp-c-brand-soft: var(--vp-c-sky-soft); 70 | } 71 | 72 | /** 73 | * Component: Home 74 | * -------------------------------------------------------------------------- */ 75 | 76 | :root { 77 | --vp-home-hero-name-color: transparent; 78 | --vp-home-hero-name-background: -webkit-linear-gradient( 79 | 120deg, 80 | #8d6fc7 40%, 81 | #4fc4d8 82 | ); 83 | --vp-home-hero-image-background-image: linear-gradient( 84 | -45deg, 85 | #8d73bf90 30%, 86 | #d6c0e890 87 | ); 88 | --vp-home-hero-image-filter: blur(30px); 89 | } 90 | 91 | @media (min-width: 640px) { 92 | :root { 93 | --vp-home-hero-image-filter: blur(56px); 94 | } 95 | } 96 | 97 | @media (min-width: 960px) { 98 | :root { 99 | --vp-home-hero-image-filter: blur(72px); 100 | } 101 | } 102 | 103 | /** 104 | * Component: Badge 105 | * -------------------------------------------------------------------------- */ 106 | :root { 107 | /* 配置一下 tips 的自定义块的颜色为非默认的产品色,避免与文字不兼容导致可读性下降 */ 108 | --vp-custom-block-tip-bg: #def4f4; 109 | --vp-custom-block-tip-code-bg: #cbd9dd7d; 110 | } 111 | 112 | .dark:root { 113 | /* 配置一下在暗色模式下 tips 的自定义块的颜色为非默认的产品色,避免与文字不兼容导致可读性下降 */ 114 | --vp-custom-block-tip-bg: #02474e; 115 | --vp-custom-block-tip-code-bg: #0d1e1e9c; 116 | } 117 | 118 | :root { 119 | --vp-nolebase-highlight-targeted-heading-bg: rgba(253, 216, 95, 0.31); 120 | } 121 | -------------------------------------------------------------------------------- /.github/workflows/preview-docs-deployment.yaml: -------------------------------------------------------------------------------- 1 | name: 部署预览到 Netlify 2 | 3 | on: 4 | workflow_run: 5 | workflows: 6 | - 构建预览 7 | types: 8 | - completed 9 | workflow_dispatch: 10 | 11 | env: 12 | PR_NUM: 0 13 | STORE_PATH: '' 14 | NETLIFY_JSON_OUTPUT: '' 15 | NETLIFY_URL: '' 16 | 17 | jobs: 18 | on-success: 19 | name: 部署预览到 Netlify 20 | runs-on: ubuntu-latest 21 | environment: 22 | name: 预览 Preview 23 | url: ${{ env.NETLIFY_URL }} 24 | permissions: 25 | pull-requests: write 26 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 27 | steps: 28 | - name: 下载 PR 信息 29 | uses: dawidd6/action-download-artifact@v3 30 | with: 31 | workflow_conclusion: success 32 | run_id: ${{ github.event.workflow_run.id }} 33 | name: pr-num 34 | path: pr-num 35 | allow_forks: true 36 | 37 | - name: 获取 PR 信息 38 | id: pr-num 39 | run: | 40 | echo "PR_NUM=$(cat pr-num/pr_num)" >> $GITHUB_ENV 41 | 42 | - name: 下载构建产物 43 | uses: dawidd6/action-download-artifact@v3 44 | with: 45 | workflow_conclusion: success 46 | run_id: ${{ github.event.workflow_run.id }} 47 | name: docs-build 48 | path: docs-build 49 | allow_forks: true 50 | 51 | - name: 安装 Node.js 20.x 52 | uses: actions/setup-node@v3 53 | with: 54 | node-version: 20.x 55 | 56 | - name: 安装 pnpm 57 | uses: pnpm/action-setup@v2 58 | with: 59 | run_install: false 60 | version: 8 61 | 62 | - name: 获取 pnpm store 目录 63 | shell: bash 64 | run: | 65 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 66 | 67 | - name: 配置 pnpm 缓存 68 | uses: actions/cache@v3 69 | with: 70 | path: ${{ env.STORE_PATH }} 71 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 72 | restore-keys: | 73 | ${{ runner.os }}-pnpm-store- 74 | 75 | - name: 安装 Netlify CLI 76 | run: pnpm install -g netlify-cli 77 | 78 | - name: 推送到 Netlify 79 | run: | 80 | NETLIFY_JSON_OUTPUT=$(netlify deploy --dir docs-build --json) 81 | echo $NETLIFY_JSON_OUTPUT 82 | 83 | echo "NETLIFY_JSON_OUTPUT=$(echo $NETLIFY_JSON_OUTPUT)" >> $GITHUB_ENV 84 | echo "NETLIFY_URL=$(echo $NETLIFY_JSON_OUTPUT | jq -r .deploy_url)" >> $GITHUB_ENV 85 | env: 86 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 87 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 88 | 89 | - name: 搜索评论 ID 90 | uses: peter-evans/find-comment@v2 91 | id: fc 92 | with: 93 | issue-number: ${{ env.PR_NUM }} 94 | comment-author: 'github-actions[bot]' 95 | body-includes: 到 Netlify 96 | 97 | - name: 创建或更新评论 98 | uses: peter-evans/create-or-update-comment@v3 99 | with: 100 | comment-id: ${{ steps.fc.outputs.comment-id }} 101 | issue-number: ${{ env.PR_NUM }} 102 | body: | 103 | ## ✅ 成功部署到 Netlify 104 | 105 | | 系统 | 状态 | 预览链接 | 106 | |:---------|:------------|:----------------------------------| 107 | | Ubuntu | 成功 | ${{ env.NETLIFY_URL }} | 108 | edit-mode: replace 109 | 110 | on-failure: 111 | name: 无法部署预览到 Netlify 112 | runs-on: ubuntu-latest 113 | permissions: 114 | pull-requests: write 115 | 116 | if: ${{ github.event.workflow_run.conclusion == 'failure' }} 117 | steps: 118 | - name: 下载 PR 信息 119 | uses: dawidd6/action-download-artifact@v3 120 | with: 121 | workflow_conclusion: success 122 | run_id: ${{ github.event.workflow_run.id }} 123 | name: pr-num 124 | path: pr-num 125 | allow_forks: true 126 | 127 | - name: 获取 PR 信息 128 | id: pr-num 129 | run: | 130 | echo "PR_NUM=$(cat pr-num/pr_num)" >> $GITHUB_ENV 131 | 132 | - name: 搜索评论 ID 133 | uses: peter-evans/find-comment@v2 134 | id: fc 135 | with: 136 | issue-number: ${{ env.PR_NUM }} 137 | comment-author: 'github-actions[bot]' 138 | body-includes: 到 Netlify 139 | 140 | - name: 创建或更新评论 141 | uses: peter-evans/create-or-update-comment@v3 142 | with: 143 | comment-id: ${{ steps.fc.outputs.comment-id }} 144 | issue-number: ${{ env.PR_NUM }} 145 | body: | 146 | ## ❌ 无法部署到 Netlify 147 | 148 | | 系统 | 状态 | 预览链接 | 149 | |:---------|:------------|:----------------------------------| 150 | | Ubuntu | 失败 | 请检查工作流程运行的状态和日志。 | 151 | edit-mode: replace 152 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import type { Theme } from 'vitepress' 2 | import DefaultTheme from 'vitepress/theme' 3 | import giscusTalk from 'vitepress-plugin-comment-with-giscus'; 4 | import { useData, useRoute } from 'vitepress'; 5 | import { toRefs } from "vue"; 6 | import { h } from 'vue' 7 | 8 | import { 9 | InjectionKey as NolebaseEnhancedReadabilitiesInjectionKey, 10 | LayoutMode as NolebaseEnhancedReadabilitiesLayoutMode, 11 | NolebaseEnhancedReadabilitiesMenu, 12 | NolebaseEnhancedReadabilitiesScreenMenu, 13 | } from '@nolebase/vitepress-plugin-enhanced-readabilities/client' 14 | 15 | import { 16 | NolebaseInlineLinkPreviewPlugin, 17 | } from '@nolebase/vitepress-plugin-inline-link-preview/client' 18 | 19 | import { 20 | NolebaseHighlightTargetedHeading, 21 | } from '@nolebase/vitepress-plugin-highlight-targeted-heading/client' 22 | 23 | import { 24 | InjectionKey as NolebaseGitChangelogInjectionKey, 25 | NolebaseGitChangelogPlugin, 26 | } from '@nolebase/vitepress-plugin-git-changelog/client' 27 | 28 | import { 29 | NolebasePagePropertiesPlugin, 30 | } from '@nolebase/vitepress-plugin-page-properties/client' 31 | 32 | import { 33 | NolebaseUnlazyImg, 34 | } from '@nolebase/vitepress-plugin-thumbnail-hash/client' 35 | 36 | import { creators } from '../creators' 37 | 38 | import AppContainer from './components/AppContainer.vue' 39 | import DocFooter from './components/DocFooter.vue' 40 | import HomePage from './components/HomePage.vue' 41 | import Share from './components/Share.vue' 42 | import TocList from './components/TocList.vue' 43 | 44 | import '@nolebase/vitepress-plugin-enhanced-readabilities/client/style.css' 45 | import '@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css' 46 | import '@nolebase/vitepress-plugin-inline-link-preview/client/style.css' 47 | import '@nolebase/vitepress-plugin-git-changelog/client/style.css' 48 | import '@nolebase/vitepress-plugin-page-properties/client/style.css' 49 | import '@nolebase/vitepress-plugin-thumbnail-hash/client/style.css' 50 | import '@nolebase/vitepress-plugin-enhanced-mark/client/style.css' 51 | 52 | import 'virtual:uno.css' 53 | 54 | import '../styles/main.css' 55 | import '../styles/vars.css' 56 | 57 | import('@nolebase/vitepress-plugin-inline-link-preview/client') 58 | 59 | const ExtendedTheme: Theme = { 60 | extends: DefaultTheme, 61 | Layout: () => { 62 | return h(DefaultTheme.Layout, null, { 63 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 64 | 'doc-top': () => [ 65 | h(NolebaseHighlightTargetedHeading), 66 | ], 67 | 'doc-footer-before': () => [ 68 | h(DocFooter), 69 | ], 70 | 'nav-bar-content-after': () => [ 71 | h(NolebaseEnhancedReadabilitiesMenu), 72 | h(Share), 73 | ], 74 | 'nav-screen-content-after': () => [ 75 | h(NolebaseEnhancedReadabilitiesScreenMenu), 76 | ], 77 | }) 78 | }, 79 | enhanceApp({ app }) { 80 | /** 81 | * Have to manually import and register the essential components that needed during build globally. 82 | * 83 | * Learn more at: Warn `Hydration completed but contains mismatches.` and Custom components are not rendered · Issue #1918 · vuejs/vitepress 84 | * https://github.com/vuejs/vitepress/issues/1918 85 | */ 86 | 87 | app.component('HomePage', HomePage) 88 | app.component('DocFooter', DocFooter) 89 | app.component('Share', Share) 90 | app.component('TocList', TocList) 91 | app.component('AppContainer', AppContainer) 92 | app.component('NolebaseUnlazyImg', NolebaseUnlazyImg) 93 | 94 | app.provide(NolebaseEnhancedReadabilitiesInjectionKey, { 95 | layoutSwitch: { 96 | defaultMode: NolebaseEnhancedReadabilitiesLayoutMode.SidebarWidthAdjustableOnly, 97 | }, 98 | spotlight: { 99 | defaultToggle: true, 100 | hoverBlockColor: 'rgb(240 197 52 / 7%)', 101 | }, 102 | }) 103 | 104 | app.provide(NolebaseGitChangelogInjectionKey, { 105 | mapContributors: creators, 106 | }) 107 | 108 | app.use(NolebaseInlineLinkPreviewPlugin) 109 | app.use(NolebaseGitChangelogPlugin) 110 | app.use(NolebasePagePropertiesPlugin<{ 111 | tags: string[] 112 | progress: number 113 | }>(), { 114 | properties: { 115 | 'zh-CN': [ 116 | { 117 | key: 'tags', 118 | type: 'tags', 119 | title: '标签', 120 | }, 121 | { 122 | key: 'progress', 123 | type: 'progress', 124 | title: '完成进度', 125 | }, 126 | { 127 | key: 'wordCount', 128 | type: 'dynamic', 129 | title: '字数', 130 | options: { 131 | type: 'wordsCount', 132 | }, 133 | }, 134 | { 135 | key: 'readingTime', 136 | type: 'dynamic', 137 | title: '阅读时间', 138 | options: { 139 | type: 'readingTime', 140 | dateFnsLocaleName: 'zhCN', 141 | }, 142 | }, 143 | ], 144 | }, 145 | }) 146 | }, 147 | setup() { 148 | // Get frontmatter and route 149 | const { frontmatter } = toRefs(useData()); 150 | const route = useRoute(); 151 | 152 | // Obtain configuration from: https://giscus.app/ 153 | giscusTalk({ 154 | repo: 'Jackiexiao/nolebase-template', 155 | repoId: 'R_kgDOL5WHsg', 156 | category: 'Announcements', // default: `General` 157 | categoryId: 'DIC_kwDOL5WHss4CfTYs', 158 | mapping: 'url', // default: `pathname` 159 | inputPosition: 'top', // default: `top` 160 | lang: 'zh-CN', // default: `zh-CN` 161 | // i18n setting (Note: This configuration will override the default language set by lang) 162 | // Configured as an object with key-value pairs inside: 163 | // [your i18n configuration name]: [corresponds to the language pack name in Giscus] 164 | locales: { 165 | 'zh-Hans': 'zh-CN', 166 | 'en-US': 'en' 167 | }, 168 | homePageShowComment: false, // Whether to display the comment area on the homepage, the default is false 169 | lightTheme: 'light', // default: `light` 170 | darkTheme: 'transparent_dark', // default: `transparent_dark` 171 | // ... 172 | }, { 173 | frontmatter, route 174 | }, 175 | // Whether to activate the comment area on all pages. 176 | // The default is true, which means enabled, this parameter can be ignored; 177 | // If it is false, it means it is not enabled. 178 | // You can use `comment: true` preface to enable it separately on the page. 179 | true 180 | ); 181 | } 182 | } 183 | 184 | export default ExtendedTheme 185 | -------------------------------------------------------------------------------- /.vitepress/styles/main.css: -------------------------------------------------------------------------------- 1 | @import 'kbd.css'; 2 | 3 | html, body { 4 | -webkit-tap-highlight-color: transparent; /* 避免触屏下的点击高亮颜色 */ 5 | } 6 | 7 | /* 参考 Vitest */ 8 | /* fix height ~ 2 lines of text: 3 or more cards per row */ 9 | .VPTeamMembersItem.small .profile .data .affiliation { 10 | min-height: 3rem; 11 | } 12 | 13 | .VPTeamMembersItem.small .profile .data .desc { 14 | min-height: 3rem; 15 | } 16 | 17 | /* fix height ~ 3 lines of text: 4 cards per row */ 18 | @media (min-width: 1064px) and (max-width: 1143px) { 19 | .VPTeamMembersItem.small .profile .data .affiliation { 20 | min-height: 4rem; 21 | } 22 | 23 | .VPTeamMembersItem.small .profile .data .desc { 24 | min-height: 4rem; 25 | } 26 | } 27 | 28 | /* fix height ~ 3 lines of text: 3 cards per row */ 29 | @media (min-width: 815px) and (max-width: 875px) { 30 | .VPTeamMembersItem.small .profile .data .affiliation { 31 | min-height: 4rem; 32 | } 33 | 34 | .VPTeamMembersItem.small .profile .data .desc { 35 | min-height: 4rem; 36 | } 37 | } 38 | 39 | /* fix height ~ 3 lines of text: 2 cards per row */ 40 | @media (max-width: 612px) { 41 | .VPTeamMembersItem.small .profile .data .affiliation { 42 | min-height: 4rem; 43 | } 44 | 45 | .VPTeamMembersItem.small .profile .data .desc { 46 | min-height: 4rem; 47 | } 48 | } 49 | 50 | /* fix height: one card per row */ 51 | @media (max-width: 568px) { 52 | .VPTeamMembersItem.small .profile .data .affiliation { 53 | min-height: unset; 54 | } 55 | 56 | .VPTeamMembersItem.small .profile .data .desc { 57 | min-height: unset; 58 | } 59 | } 60 | 61 | /* 覆盖 VPTeamMembers 组件内部处理的子元素包含特定 max-width 样式致使元素宽度与正文不一致的问题 */ 62 | .vp-doc .VPTeamMembers.small.count-2 .container, 63 | .vp-doc .VPTeamMembers.small.count-3 .container { 64 | max-width: 1152px !important; 65 | } 66 | 67 | .VPTeamMembers.medium.count-2 .container { 68 | max-width: unset; 69 | } 70 | 71 | .VPTeamMembers.small.count-2 .container { 72 | max-width: unset; 73 | } 74 | 75 | /* 标题所需要的字体 */ 76 | @font-face { 77 | font-family: tengwarannatar; 78 | src: url(/tengwar-annatar-glaemscrafu.woff2) format('woff2'); 79 | font-weight: 400; 80 | font-style: normal; 81 | font-display: block; 82 | } 83 | 84 | /* 标题所需要的字体 */ 85 | @font-face { 86 | font-family: tengwarannatar-bold; 87 | src: url(/tengwar-annatar-glaemscrafu-bold.woff2) format('woff2'); 88 | font-weight: 400; 89 | font-style: normal; 90 | font-display: block; 91 | } 92 | 93 | /* 首页标题样式 */ 94 | .VPHomeHero > .container > .main > h1 { 95 | font-size: 5rem; 96 | font-family: 'tengwarannatar-bold'; 97 | } 98 | 99 | /* 首页子标题样式 */ 100 | .VPHomeHero > .container > .main > .text { 101 | padding-top: 45px; 102 | font-size: 4rem; 103 | } 104 | 105 | /* 首页介绍样式 */ 106 | .VPHomeHero > .container > .main > .tagline { 107 | padding-top: 24px; 108 | } 109 | 110 | /* 自适应样式 */ 111 | @media (max-width: 1024px) { 112 | /* 首页标题样式 */ 113 | .VPHomeHero > .container > .main > h1 { 114 | font-size: 3.5rem; 115 | } 116 | 117 | /* 首页子标题样式 */ 118 | .VPHomeHero > .container > .main > .text { 119 | padding-top: 30px; 120 | font-size: 3rem; 121 | } 122 | 123 | /* 首页介绍样式 */ 124 | .VPHomeHero > .container > .main > .tagline { 125 | padding-top: 20px; 126 | } 127 | } 128 | 129 | /* 自适应样式 */ 130 | @media (max-width: 640px) { 131 | /* 首页标题样式 */ 132 | .VPHomeHero > .container > .main > h1 { 133 | font-size: 2.5rem; 134 | } 135 | 136 | /* 首页子标题样式 */ 137 | .VPHomeHero > .container > .main > .text { 138 | padding-top: 20px; 139 | font-size: 2rem; 140 | } 141 | 142 | /* 首页介绍样式 */ 143 | .VPHomeHero > .container > .main > .tagline { 144 | padding-top: 20px; 145 | } 146 | } 147 | 148 | /* 主页下方的页脚透明度 */ 149 | .VPFooter { 150 | opacity: 0.8; 151 | } 152 | 153 | /* 正文结尾的 CC 协议链接基本样式 */ 154 | .footer-cc-link { 155 | color: var(--vp-c-text-2); 156 | transition: all 0.3s ease; 157 | text-decoration: underline; 158 | text-decoration-color: rgb(114, 114, 114); 159 | } 160 | 161 | /* 正文结尾的 CC 协议链接鼠标 hover 样式 */ 162 | .footer-cc-link:hover { 163 | color: var(--vp-c-text-1); 164 | } 165 | 166 | /* 正文结尾的 CC 协议链接暗色模式下鼠标 hover 样式 */ 167 | .dark .footer-cc-link:hover { 168 | color: var(--vp-c-text-1); 169 | } 170 | 171 | /* 调整文档页面页脚到正文结尾的间隔距离 */ 172 | .VPDoc .VPDocFooter { 173 | margin-top: 32px; 174 | } 175 | 176 | /* 脚注 */ 177 | .footnotes > .footnotes-list { 178 | margin-top: 32px; 179 | opacity: 0.9; 180 | font-size: 12px; 181 | /* 确保脚注上的返回链接符号 ↩ 不会被 body 的 Emoji 字体覆盖渲染为 ↩️ */ 182 | font-family: sans-serif; 183 | } 184 | 185 | .footnotes > .footnotes-list > .footnote-item > p { 186 | line-height: 18px; 187 | } 188 | 189 | /* 覆盖正文的 h1 标题样式 */ 190 | .vp-doc > div > h1 { 191 | margin-bottom: 16px; 192 | } 193 | 194 | .vp-doc a { 195 | text-decoration: none; 196 | transition: all 0.3s ease; 197 | } 198 | 199 | .vp-doc a:hover { 200 | text-decoration: underline; 201 | } 202 | 203 | /** 204 | Discord 链接按钮样式 205 | */ 206 | 207 | .VPHero.VPHomeHero .actions a[href="https://discord.gg/XuNFDcDZGj"] { 208 | color: white; 209 | background-color: #404eed; 210 | } 211 | 212 | .VPHero.VPHomeHero .actions a[href="https://discord.gg/XuNFDcDZGj"]:hover { 213 | color: white; 214 | background-color: #6975f2; 215 | } 216 | 217 | .VPHero.VPHomeHero .actions a[href="https://discord.gg/XuNFDcDZGj"]:active { 218 | color: white; 219 | background-color: #404eed; 220 | } 221 | 222 | /** 223 | 暗色模式下 Discord 链接按钮样式 224 | */ 225 | 226 | .dark .VPHero.VPHomeHero .actions a[href="https://discord.gg/XuNFDcDZGj"] { 227 | color: white; 228 | background-color: #444c9e; 229 | } 230 | 231 | .dark .VPHero.VPHomeHero .actions a[href="https://discord.gg/XuNFDcDZGj"]:hover { 232 | color: white; 233 | background-color: #404eed; 234 | } 235 | 236 | .dark .VPHero.VPHomeHero .actions a[href="https://discord.gg/XuNFDcDZGj"]:active { 237 | color: white; 238 | background-color: #444c9e; 239 | } 240 | 241 | /** 242 | GitHub 链接按钮样式 243 | */ 244 | 245 | .VPHero.VPHomeHero .actions a[href="https://github.com/nolebase/integrations"] { 246 | color: white; 247 | background-color: #000; 248 | } 249 | 250 | .VPHero.VPHomeHero .actions a[href="https://github.com/nolebase/integrations"]:hover { 251 | color: white; 252 | background-color: #4b4b4b; 253 | } 254 | 255 | .VPHero.VPHomeHero .actions a[href="https://github.com/nolebase/integrations"]:active { 256 | color: white; 257 | background-color: #000; 258 | } 259 | 260 | /** 261 | 暗色模式下 GitHub 链接按钮样式 262 | */ 263 | 264 | .dark .VPHero.VPHomeHero .actions a[href="https://github.com/nolebase/integrations"] { 265 | color: white; 266 | background-color: #4b4b4b; 267 | } 268 | 269 | .dark .VPHero.VPHomeHero .actions a[href="https://github.com/nolebase/integrations"]:hover { 270 | color: white; 271 | background-color: #252525; 272 | } 273 | 274 | .dark .VPHero.VPHomeHero .actions a[href="https://github.com/nolebase/integrations"]:active { 275 | color: white; 276 | background-color: #4b4b4b; 277 | } 278 | -------------------------------------------------------------------------------- /.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process' 2 | import { defineConfig } from 'vitepress' 3 | import MarkdownItFootnote from 'markdown-it-footnote' 4 | import MarkdownItMathjax3 from 'markdown-it-mathjax3' 5 | 6 | import { BiDirectionalLinks } from '@nolebase/markdown-it-bi-directional-links' 7 | import { InlineLinkPreviewElementTransform } from '@nolebase/vitepress-plugin-inline-link-preview/markdown-it' 8 | import { buildEndGenerateOpenGraphImages } from '@nolebase/vitepress-plugin-og-image/vitepress' 9 | import { UnlazyImages } from '@nolebase/markdown-it-unlazy-img' 10 | 11 | import { discordLink, githubRepoLink, siteDescription, siteName, targetDomain } from '../metadata' 12 | import { creatorNames, creatorUsernames } from './creators' 13 | import { sidebar } from './docsMetadata.json' 14 | 15 | export default defineConfig({ 16 | vue: { 17 | template: { 18 | transformAssetUrls: { 19 | video: ['src', 'poster'], 20 | source: ['src'], 21 | img: ['src'], 22 | image: ['xlink:href', 'href'], 23 | use: ['xlink:href', 'href'], 24 | NolebaseUnlazyImg: ['src'], 25 | }, 26 | }, 27 | }, 28 | lang: 'zh-CN', 29 | title: siteName, 30 | description: siteDescription, 31 | ignoreDeadLinks: true, 32 | head: [ 33 | ['meta', { 34 | name: 'theme-color', 35 | content: '#ffffff', 36 | }], 37 | [ 38 | 'link', 39 | { 40 | rel: 'apple-touch-icon', 41 | href: '/apple-touch-icon.png', 42 | sizes: '180x180', 43 | }, 44 | ], 45 | ['link', { 46 | rel: 'icon', 47 | href: '/logo.svg', 48 | type: 'image/svg+xml', 49 | }], 50 | [ 51 | 'link', 52 | { 53 | rel: 'alternate icon', 54 | href: '/favicon.ico', 55 | type: 'image/png', 56 | sizes: '16x16', 57 | }, 58 | ], 59 | ['meta', { 60 | name: 'author', 61 | content: creatorNames.join(', '), 62 | }], 63 | [ 64 | 'meta', 65 | { 66 | name: 'keywords', 67 | content: 68 | ['markdown', 'knowledge-base', '知识库', 'vitepress', 'obsidian', 'notebook', 'notes', ...creatorUsernames].join(', '), 69 | }, 70 | ], 71 | 72 | ['meta', { 73 | property: 'og:title', 74 | content: siteName, 75 | }], 76 | [ 77 | 'meta', 78 | { 79 | property: 'og:image', 80 | content: `${targetDomain}/og.png`, 81 | }, 82 | ], 83 | ['meta', { 84 | property: 'og:description', 85 | content: siteDescription, 86 | }], 87 | ['meta', { 88 | property: 'og:site_name', 89 | content: siteName, 90 | }], 91 | 92 | ['meta', { 93 | name: 'twitter:card', 94 | content: 'summary_large_image', 95 | }], 96 | ['meta', { 97 | name: 'twitter:creator', 98 | content: creatorUsernames.join(', '), 99 | }], 100 | [ 101 | 'meta', 102 | { 103 | name: 'twitter:image', 104 | content: `${targetDomain}/og.png`, 105 | }, 106 | ], 107 | 108 | [ 109 | 'link', 110 | { 111 | rel: 'mask-icon', 112 | href: '/safari-pinned-tab.svg', 113 | color: '#927baf', 114 | }, 115 | ], 116 | ['link', { 117 | rel: 'manifest', 118 | href: '/site.webmanifest', 119 | }], 120 | ['meta', { 121 | name: 'msapplication-TileColor', 122 | content: '#603cba', 123 | }], 124 | // Proxying Plausible through Netlify | Plausible docs 125 | // https://plausible.io/docs/proxy/guides/netlify 126 | ['script', { 'defer': 'true', 'data-domain': 'nolebase.ayaka.io', 'data-api': '/api/v1/page-external-data/submit', 'src': '/assets/page-external-data/js/script.js' }], 127 | ], 128 | themeConfig: { 129 | outline: { label: '页面大纲', level: 'deep' }, 130 | darkModeSwitchLabel: '切换主题', 131 | editLink: { 132 | pattern: `${githubRepoLink}/tree/main/:path`, 133 | text: '编辑本页面', 134 | }, 135 | socialLinks: [ 136 | { icon: 'github', link: githubRepoLink }, 137 | { icon: 'discord', link: discordLink }, 138 | ], 139 | footer: { 140 | message: '用 撰写', 141 | copyright: 142 | 'CC BY-SA 4.0 © 2022-PRESENT Nólëbase 的创作者们', 143 | }, 144 | search: { 145 | provider: 'local', 146 | options: { 147 | locales: { 148 | root: { 149 | translations: { 150 | button: { 151 | buttonText: '搜索文档', 152 | buttonAriaLabel: '搜索文档', 153 | }, 154 | modal: { 155 | noResultsText: '无法找到相关结果', 156 | resetButtonTitle: '清除查询条件', 157 | footer: { 158 | selectText: '选择', 159 | navigateText: '切换', 160 | }, 161 | }, 162 | }, 163 | }, 164 | }, 165 | 166 | // Add title ang tags field in frontmatter to search 167 | // You can exclude a page from search by adding search: false to the page's frontmatter. 168 | _render(src, env, md) { 169 | // without `md.render(src, env)`, the some information will be missing from the env. 170 | let html = md.render(src, env) 171 | let tagsPart = '' 172 | let headingPart = '' 173 | let contentPart = '' 174 | let fullContent = '' 175 | const sortContent = () => [headingPart, tagsPart, contentPart] as const 176 | let { frontmatter, content } = env 177 | 178 | if (!frontmatter) 179 | return html 180 | 181 | if (frontmatter.search === false) 182 | return '' 183 | 184 | contentPart = content ||= src 185 | 186 | const headingMatch = content.match(/^#{1} .*/m) 187 | const hasHeading = !!(headingMatch && headingMatch[0] && headingMatch.index !== undefined) 188 | 189 | if (hasHeading) { 190 | const headingEnd = headingMatch.index! + headingMatch[0].length 191 | headingPart = content.slice(0, headingEnd) 192 | contentPart = content.slice(headingEnd) 193 | } 194 | else if (frontmatter.title) { 195 | headingPart = `# ${frontmatter.title}` 196 | } 197 | 198 | const tags = frontmatter.tags 199 | if (tags && Array.isArray(tags) && tags.length) 200 | tagsPart = `Tags: #${tags.join(', #')}` 201 | 202 | fullContent = sortContent().filter(Boolean).join('\n\n') 203 | 204 | html = md.render(fullContent, env) 205 | 206 | return html 207 | }, 208 | }, 209 | }, 210 | nav: [ 211 | { text: '主页', link: '/' }, 212 | { text: '笔记', link: '/笔记/' }, 213 | { text: '最近更新', link: '/toc' }, 214 | ], 215 | sidebar, 216 | }, 217 | markdown: { 218 | theme: { 219 | light: 'github-light', 220 | dark: 'one-dark-pro', 221 | }, 222 | math: true, 223 | config: (md) => { 224 | md.use(MarkdownItFootnote) 225 | md.use(MarkdownItMathjax3) 226 | md.use(BiDirectionalLinks({ 227 | dir: process.cwd(), 228 | })) 229 | md.use(UnlazyImages(), { 230 | imgElementTag: 'NolebaseUnlazyImg', 231 | }) 232 | md.use(InlineLinkPreviewElementTransform, { 233 | tag: 'VPNolebaseInlineLinkPreview', 234 | }) 235 | }, 236 | }, 237 | async buildEnd(siteConfig) { 238 | await buildEndGenerateOpenGraphImages({ 239 | baseUrl: targetDomain, 240 | category: { 241 | byLevel: 2, 242 | }, 243 | })(siteConfig) 244 | }, 245 | }) 246 | -------------------------------------------------------------------------------- /scripts/update.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tsx 2 | import { fileURLToPath } from 'node:url' 3 | import { dirname, join, resolve } from 'node:path' 4 | import { createHash } from 'node:crypto' 5 | import process from 'node:process' 6 | import fs from 'fs-extra' 7 | import fg from 'fast-glob' 8 | import Git from 'simple-git' 9 | import matter from 'gray-matter' 10 | import uniq from 'lodash/uniq' 11 | import TagsAlias from '../.vitepress/docsTagsAlias.json' 12 | import type { ArticleTree, DocsMetadata, DocsTagsAlias, Tag } from './types/metadata' 13 | 14 | const dir = './' 15 | const target = '笔记/' 16 | const folderTop = true 17 | 18 | export const DIR_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') 19 | export const DIR_VITEPRESS = resolve(dirname(fileURLToPath(import.meta.url)), '../.vitepress') 20 | 21 | const git = Git(DIR_ROOT) 22 | 23 | /** 24 | * 列出所有的页面 25 | * @param dir 目录 26 | * @param options 选项 27 | * @param options.target 目标 28 | * @param options.ignore 忽略 29 | * @returns 符合 glob 的文件列表 30 | */ 31 | export async function listPages(dir: string, options: { target?: string, ignore?: string[] }) { 32 | const { 33 | target = '', 34 | ignore = [], 35 | } = options 36 | 37 | const files = await fg(`${target}**/*.md`, { 38 | onlyFiles: true, 39 | cwd: dir, 40 | ignore: [ 41 | '_*', 42 | 'dist', 43 | 'node_modules', 44 | ...ignore, 45 | ], 46 | }) 47 | 48 | files.sort() 49 | return files 50 | } 51 | 52 | /** 53 | * 添加和计算路由项 54 | * @param indexes 路由树 55 | * @param path 路径 56 | * @param upgradeIndex 是否升级 index 57 | * @returns 路由树 58 | */ 59 | async function addRouteItem(indexes: ArticleTree[], path: string, upgradeIndex = false) { 60 | const suffixIndex = path.lastIndexOf('.') 61 | const nameStartsAt = path.lastIndexOf('/') + 1 62 | const title = path.slice(nameStartsAt, suffixIndex) 63 | const item = { 64 | index: title, 65 | text: title, 66 | link: `/${path.slice(0, suffixIndex)}`, 67 | lastUpdated: +await git.raw(['log', '-1', '--format=%at', path]) * 1000, 68 | } 69 | const linkItems = item.link.split('/') 70 | linkItems.shift() 71 | 72 | target.split('/').forEach((item) => { 73 | if (item) 74 | linkItems.shift() 75 | }) 76 | 77 | if (linkItems.length === 1) 78 | return 79 | 80 | indexes = addRouteItemRecursion(indexes, item, linkItems, upgradeIndex) 81 | } 82 | 83 | /** 84 | * 递归式添加和计算路由项 85 | * @param indexes 路由树 86 | * @param item 路由项 87 | * @param path 路径 88 | * @param upgradeIndex 是否升级 index 89 | * @returns 路由树 90 | */ 91 | function addRouteItemRecursion(indexes: ArticleTree[], item: any, path: string[], upgradeIndex: boolean) { 92 | if (path.length === 1) { 93 | indexes.push(item) 94 | return indexes 95 | } 96 | else { 97 | const onePath = path.shift() 98 | if (!onePath) 99 | return indexes 100 | 101 | let obj = indexes.find(obj => obj.index === onePath) 102 | 103 | if (!obj) { 104 | // 如果没有找到,就创建一个 105 | obj = { index: onePath, text: onePath, collapsed: true, items: [] } 106 | indexes.push(obj) 107 | } 108 | else if (!obj.items) { 109 | // 如果找到了,但是没有 items,就创建对应的 items 和标记为可折叠 110 | obj.collapsed = true 111 | obj.items = [] 112 | } 113 | 114 | if (path.length === 1 && path[0] === 'index') { 115 | // 如果只有一个元素,并且是 index.md,直接写入 link 和 lastUpdated 116 | obj.link = item.link 117 | obj.lastUpdated = item.lastUpdated 118 | } 119 | else { 120 | // 否则,递归遍历 121 | obj.items = addRouteItemRecursion(obj.items ?? [], item, path, upgradeIndex) 122 | } 123 | 124 | return indexes 125 | } 126 | } 127 | 128 | /** 129 | * 处理 docsMetadata.sidebar,拼接 sidebar 路由树 130 | * @param docs 符合 glob 的文件列表 131 | * @param docsMetadata docsMetadata.json 的内容 132 | */ 133 | async function processSidebar(docs: string[], docsMetadata: DocsMetadata) { 134 | await Promise.all(docs.map(async (docPath: string) => { 135 | await addRouteItem(docsMetadata.sidebar, docPath) 136 | })) 137 | } 138 | 139 | /** 140 | * 排序传入的ArticleTree数组 141 | * @param articleTree 需要排序的ArticleTree数组 142 | * @return 排序后的结果 143 | */ 144 | function articleTreeSort(articleTree: ArticleTree[]) { 145 | articleTree.sort((itemA, itemB) => { 146 | return itemA.text.localeCompare(itemB.text) 147 | }) 148 | return articleTree 149 | } 150 | 151 | /** 152 | * 排序sidebar,返回新的sidebar数组 153 | * @param sidebar 需要排序的ArticleTree数组 154 | * @param folderTop 是否优先排序文件夹 155 | * @returns ArticleTree[] 排序好了的数组 156 | */ 157 | function sidebarSort(sidebar: ArticleTree[], folderTop: boolean = true) { 158 | let _sideBar 159 | if (folderTop) { 160 | // 分别找出直接的文件和嵌套文件夹 161 | const files = articleTreeSort(sidebar.filter((item) => { 162 | return !item.items || item.items.length === 0 163 | })) 164 | const folders = articleTreeSort(sidebar.filter((item) => { 165 | return item.items && item.items.length > 0 166 | })) 167 | // 然后在排序完成后合并为新的数组 168 | _sideBar = [...folders, ...files] 169 | } 170 | else { 171 | _sideBar = articleTreeSort(sidebar) 172 | } 173 | 174 | // 如果有子菜单就递归排序每个子菜单 175 | for (const articleTree of _sideBar) { 176 | if (articleTree.items && articleTree.items.length > 0) 177 | articleTree.items = sidebarSort(articleTree.items, folderTop) 178 | } 179 | return _sideBar 180 | } 181 | 182 | /** 183 | * 判断 srcTag 是否是 targetTag 的别名 184 | * 185 | * 判断根据下面的规则进行: 186 | * 1. srcTag === targetTag 187 | * 2. srcTag.toUpperCase() === targetTag.toUpperCase() 188 | * 189 | * @param srcTag 原始 tag 190 | * @param targetTag 目标 tag 191 | * @returns 是否是别名 192 | */ 193 | function isTagAliasOfTag(srcTag: string, targetTag: string) { 194 | return srcTag === targetTag || srcTag.toUpperCase() === targetTag.toUpperCase() 195 | } 196 | 197 | function findTagAlias(tag: string, docsMetadata: DocsMetadata, aliasMapping: DocsTagsAlias[]) { 198 | const potentialAlias: string[] = [] 199 | 200 | docsMetadata.tags.forEach((item) => { 201 | // 在已经存在在 docsMetadata.json 中的 alias 进行查找和筛选 202 | item.alias.filter((alias) => { 203 | return isTagAliasOfTag(alias, tag) // 筛选 alias 是 tag 的别名的 alias 204 | }).forEach((alias) => { 205 | potentialAlias.push(alias) // 将别名加入到 potentialAlias 中 206 | }) 207 | 208 | if (isTagAliasOfTag(item.name, tag)) { // 如果有记录的 tag.name 是当前 tag 的别名 209 | potentialAlias.push(item.name) // 那么将 tag.name 加入到 potentialAlias 中 210 | } 211 | }) 212 | 213 | // 在 docsTagsAlias.json 中进行查找和筛选 214 | for (const aliasTag of aliasMapping) { 215 | // 如果人工编撰的的 aliasTag.name 是当前 tag 的别名 216 | // 那么这意味着 aliasTag.name 和 aliasTag.alias 中的所有 alias 都是当前 tag 的别名 217 | if (isTagAliasOfTag(aliasTag.name, tag)) { 218 | // 将 aliasTag.name 和 aliasTag.alias 中的所有 alias 加入到 potentialAlias 中 219 | potentialAlias.push(aliasTag.name) 220 | potentialAlias.push(...aliasTag.alias) 221 | } 222 | 223 | aliasTag.alias.forEach((alias) => { 224 | // 如果人工编撰的的 aliasTag.alias 中的某个 alias 是当前 tag 的别名 225 | // 那么这意味着 aliasTag.name 和 aliasTag.alias 中的所有 alias 都是当前 tag 的别名 226 | if (isTagAliasOfTag(alias, tag)) { 227 | // 将 aliasTag.name 和 aliasTag.alias 中的所有 alias 加入到 potentialAlias 中 228 | potentialAlias.push(aliasTag.name) 229 | potentialAlias.push(...aliasTag.alias) 230 | } 231 | }) 232 | } 233 | 234 | return potentialAlias 235 | } 236 | 237 | async function processTags(doc: string, docsMetadata: DocsMetadata, tags: string[]) { 238 | for (const tag of tags) { 239 | docsMetadata.tags = docsMetadata.tags || [] 240 | const found = docsMetadata.tags.find((item) => { 241 | if (item.name === tag) 242 | return item 243 | return null 244 | }) 245 | 246 | // 优先查找所有的 alias 247 | const aliases = uniq(findTagAlias(tag, docsMetadata, TagsAlias)) 248 | 249 | // 对于每一个 alias,如果在 docsMetadata.tags 中找到了,那么就将当前 doc 加入到 appearedInDocs 中 250 | docsMetadata.tags.forEach((item, index) => { 251 | aliases.forEach((alias) => { 252 | if (item.name === alias && !docsMetadata.tags[index].appearedInDocs.includes(doc)) 253 | docsMetadata.tags[index].appearedInDocs.push(doc) 254 | }) 255 | }) 256 | 257 | // 如果 tag 尚未出现在 docsMetadata.tags 中,那么就创建一个新的 tag 258 | if (!found) { 259 | const tagRecord: Tag = { 260 | name: tag, 261 | alias: aliases, 262 | appearedInDocs: [], 263 | description: '', 264 | count: 1, 265 | } 266 | 267 | // 将当前 doc 加入到 appearedInDocs 中 268 | tagRecord.appearedInDocs.push(doc) 269 | // 将新创建的 tag 加入到 docsMetadata.tags 中 270 | docsMetadata.tags.push(tagRecord) 271 | continue 272 | } 273 | 274 | found.count++ 275 | if (!found.appearedInDocs.includes(doc)) 276 | found.appearedInDocs.push(doc) 277 | found.alias = uniq([...found.alias, ...aliases]) 278 | } 279 | } 280 | 281 | /** 282 | * 处理 docsMetadata.docs,计算和统计 sha256 hash 等信息 283 | * @param docs 符合 glob 的文件列表 284 | * @param docsMetadata docsMetadata.json 的内容 285 | */ 286 | async function processDocs(docs: string[], docsMetadata: DocsMetadata) { 287 | if (!docsMetadata.docs) 288 | docsMetadata.docs = [] 289 | 290 | const tagsToBeProcessed: { doc: string, tags: string[] }[] = [] 291 | 292 | docsMetadata.docs = docs.map((docPath) => { 293 | // 尝试在 docsMetadata.docs 中找到当前文件的历史 hash 记录 294 | const found = docsMetadata.docs.find((item) => { 295 | if (item.relativePath === docPath) 296 | return item 297 | return null 298 | }) 299 | 300 | // 读取源文件 301 | const content = fs.readFileSync(docPath, 'utf-8') 302 | // 解析 Markdown 文件的 frontmatter 303 | const parsedPageContent = matter(content) 304 | 305 | if (Array.isArray(parsedPageContent.data.tags)) { 306 | if (parsedPageContent.data.tags.includes(null)) 307 | console.error('null tag found in', docPath) 308 | 309 | tagsToBeProcessed.push({ doc: docPath, tags: parsedPageContent.data.tags }) 310 | } 311 | 312 | const hash = createHash('sha256') 313 | const tempSha256Hash = hash.update(parsedPageContent.content).digest('hex') // 对 Markdown 正文进行 sha256 hash 314 | 315 | // 如果没有找到,就初始化 316 | if (!found) { 317 | return { 318 | relativePath: docPath, 319 | hashes: { sha256: { content: tempSha256Hash } }, 320 | } 321 | } 322 | else { 323 | // 如果 found.hashes 不存在,就初始化 324 | if (!found.hashes) 325 | found.hashes = { sha256: { content: tempSha256Hash } } 326 | // 如果 found.hashes.sha256 不存在,就初始化 327 | if (!found.hashes.sha256) 328 | found.hashes.sha256 = { content: tempSha256Hash } 329 | // 如果历史记录的 sha256 hash 与当前的相同,就不标记 contentDiff,并且直接返回 330 | if (found.hashes.sha256.content === tempSha256Hash && !found.hashes.sha256.contentDiff) 331 | return found 332 | 333 | // 否则,标记 contentDiff 334 | found.hashes.sha256.contentDiff = tempSha256Hash 335 | return found 336 | } 337 | }) 338 | 339 | await Promise.all(tagsToBeProcessed.map(async ({ doc, tags }) => { 340 | await processTags(doc, docsMetadata, tags) 341 | })) 342 | } 343 | 344 | async function run() { 345 | let now = (new Date()).getTime() 346 | const docs = await listPages(dir, { target }) 347 | console.log('listed pages in', `${(new Date()).getTime() - now}ms`) 348 | now = (new Date()).getTime() 349 | 350 | const docsMetadata: DocsMetadata = { docs: [], sidebar: [], tags: [] } 351 | 352 | await processDocs(docs, docsMetadata) 353 | console.log('processed docs in', `${(new Date()).getTime() - now}ms`) 354 | now = (new Date()).getTime() 355 | 356 | await processSidebar(docs, docsMetadata) 357 | console.log('processed sidebar in', `${(new Date()).getTime() - now}ms`) 358 | now = (new Date()).getTime() 359 | 360 | docsMetadata.sidebar = sidebarSort(docsMetadata.sidebar, folderTop) 361 | console.log('processed sidebar sort in', `${(new Date()).getTime() - now}ms`) 362 | 363 | await fs.writeJSON(join(DIR_VITEPRESS, 'docsMetadata.json'), docsMetadata, { spaces: 2 }) 364 | } 365 | 366 | run().catch((err) => { 367 | console.error(err) 368 | process.exit(1) 369 | }) 370 | -------------------------------------------------------------------------------- /笔记/index.md: -------------------------------------------------------------------------------- 1 | # 📒 笔记 2 | 3 | ## 小音和猫猫的知识库 4 | 5 | 欢迎来到小音和猫猫的知识库 [Nólëbase](https://nolebase.ayaka.io),这里是 📒 笔记分区 6 | 7 | ## 想要自己部署和在本地启动强大的 Nólëbase 知识库,或者自己拥有一份? 8 | 9 | 很高兴你对 [Nólëbase](https://nolebase.ayaka.io) 感兴趣! 10 | 11 | ### 背景介绍 12 | 13 | 首先 [Nólëbase](https://nolebase.ayaka.io) 是完全可以在本地无网络环境的情况下使用的!你可以在跟随[如何下载到本地](#如何下载到本地)的指引下载或者克隆之后使用 [Obsidian](https://obsidian.md) 和 [Logseq](https://logseq.com/) 这样的知识库软件打开,也可以用 [Typora](https://typora.io/) 这样的 Markdown 编辑器打开进行浏览和编辑,这意味着即便你不具备任何的编程技能,也可以使用或者借鉴我们的知识库分类和组织方法。 14 | 15 | 在继续之前,也请容许我介绍一下对项目所使用的技术和架构。 16 | 17 | 和其他的由 [Hexo](https://hexo.io) 驱动和生成的博客和静态网站类似,[Nólëbase](https://nolebase.ayaka.io) 其实使用了名为 [VitePress](https://vitepress.dev) 的静态生成器来驱动和生成网站,像 [VitePress](https://vitepress.dev) 这样的静态生成器支持在 Markdown 文件中使用 [Vue](https://vuejs.org/) 组件来嵌入并增强文档的阅读和使用体验。而 [VitePress](https://vitepress.dev) 和 [Vue](https://vuejs.org/) 是 [Node.js](https://nodejs.org/en) 生态的一部分,他们都属于「前端技术」的一部分。 18 | 19 | 在运行和部署上,我们使用免费的 [Netlify](https://www.netlify.com/) 来提供网站的托管,使用 [GitHub Actions](https://github.com/features/actions)(你可以理解一个将会在每次我们将笔记和知识库内容更新到 GitHub 仓库之后会自动触发的一个自动化工作)把我们的笔记和知识库内容自动构建和部署到 [Netlify](https://www.netlify.com/) 上。 20 | 21 | ### 等等,这和 [Obsidian Publish](https://obsidian.md/publish) 有什么不同 22 | 23 | 好问题,作为 [Obsidian](https://obsidian.md) 的用户,你可能会知道他们开发团队提供了一套专门用于发布 [Obsidian](https://obsidian.md) 中的笔记的服务「[Obsidian Publish](https://obsidian.md/publish)」,如果你不知道也不要紧,可以把 [Obsidian Publish](https://obsidian.md/publish) 理解为一个可以帮助你将 [Obsidian](https://obsidian.md) 中的笔记发布到互联网上帮助你分享和提供搜索引擎优化(SEO)的服务。 24 | 25 | #### 为什么不用 [Obsidian Publish](https://obsidian.md/publish) 26 | 27 | 1. [Obsidian Publish](https://obsidian.md/publish) 需要付费才能使用,但是实际上对于静态网站而言,就像先前介绍的那样,我们可以白嫖一些像是 [GitHub Pages](https://pages.github.com/),[Netlify](https://www.netlify.com/),[Vercel](https://vercel.com/) 和 [Cloudflare Pages](https://pages.cloudflare.com/) 这样现成的基础设施帮忙托管; 28 | 2. [Obsidian Publish](https://obsidian.md/publish) 部署后的静态页面是没有 [Obsidian](https://obsidian.md) 自带的插件功能的支持的,这意味着很多作者(或者你)在本地撰写好的使用了插件支持的语法的文档在本地使用 [Obsidian](https://obsidian.md) 渲染和预览的时候是能正常工作的,然而将会在 [Obsidian Publish](https://obsidian.md/publish) 部署和托管后变得不可用。这是非常关键的一个问题,很多用户(包括我在内)都非常依赖于 [Obsidian](https://obsidian.md) 的插件生态,这样的问题现在还没有足够好的解决方案,但我们创建了名为 [Nólëbase 集成](https://github.com/nolebase/integrations) 这样的项目来尝试提供一些常见的和常用的 [Obsidian](https://obsidian.md) 插件在 [VitePress](https://vitepress.dev) 和 [Vue](https://vuejs.org/) 的环境下的替代方案,这样的替代方案虽然不能完全替代 [Obsidian](https://obsidian.md) 插件的功能,但是可以在一定程度上提供类似的功能,你也可以关注一下; 29 | 30 | #### 在什么情况下你应该使用 [Obsidian Publish](https://obsidian.md/publish) 31 | 32 | [Obsidian Publish](https://obsidian.md/publish) 也有它自己的优点: 33 | 34 | 1. 寻找适合 [VitePress](https://vitepress.dev) 的 Markdown 插件是困难和复杂的,[Obsidian Publish](https://obsidian.md/publish) 天然支持所有 [Obsidian](https://obsidian.md) 原生支持的语法和插件,这意味着你不需要额外的调研和试错工作就可以将你在本地使用 [Obsidian](https://obsidian.md) 撰写的文档直接部署到互联网上; 35 | 2. 运行和部署像是 [Nólëbase](https://nolebase.ayaka.io) 这样的静态网站对于不具备任何的代码和编程知识的作者难度大得多,但是 [Obsidian Publish](https://obsidian.md/publish) 不需要任何的代码和编程知识,也无需关心任何的技术细节,和 Notion 的分享一样,你只需要点击几下就可以完成部署; 36 | 37 | 所以如果你没有掌握必备的编程技能,或者你不想花时间和精力去学习和掌握这些技能,那么 [Obsidian Publish](https://obsidian.md/publish) 可能是一个更好的选择。 38 | 39 | ### 所以,也有别人在这样使用 [VitePress](https://vitepress.dev) 作为可部署的知识库吗? 40 | 41 | 哦,当然! 42 | 43 | 这里有一些我们在 GitHub 上发现的使用 [VitePress](https://vitepress.dev) 作为知识库的项目: 44 | 45 | - [Charles7c/charles7c.github.io:基于 VitePress 构建的个人知识库/博客。](https://github.com/Charles7c/charles7c.github.io) 46 | - [Lercel/Vision](https://github.com/Lercel/Vision) 47 | - [maomao1996/mm-notes: 茂茂物语: 各种笔记记录(想到啥写啥系列)](https://github.com/maomao1996/mm-notes) 48 | - [ATQQ/sugar-blog: ✍️📚我写博客的地方🤪🤪🤪记录随笔与学习笔记](https://github.com/ATQQ/sugar-blog) 49 | - [chodocs/chodocs: 一站式前端内容网站,包括学习路线、知识体系。](https://github.com/chodocs/chodocs) 50 | - [cangzihan/knolls-think-tank: 基于Nólëbase的个人知识库](https://github.com/cangzihan/knolls-think-tank) 51 | - [CHENJIAMIAN/Blog: Obsidian笔记库 | 我的笔记分bei享fen | 根据GitHub工作流自动构建vitepress博客 | http://chenjiamian.me/Blog/](https://github.com/CHENJIAMIAN/Blog) 52 | - [realzhengyiming/YiMingBlog: a... new blog again, hhhh](https://github.com/realzhengyiming/YiMingBlog) 53 | - [nikitavoloboev/knowledge: Everything I know](https://github.com/nikitavoloboev/knowledge?tab=readme-ov-file) 54 | - [senup/wiki: wiki](https://github.com/senup/wiki?tab=readme-ov-file) 55 | - [kkoscielniak/digital-garden: 🥦 Things I know](https://github.com/kkoscielniak/digital-garden) 56 | - [Merlin-Chest/Blog: 个人学习及知识记录整理](https://github.com/Merlin-Chest/Blog) 57 | - [selwynpolit/d9book: Drupal at your Fingertips: A developers quick reference for Drupal 9 and 10](https://github.com/selwynpolit/d9book) 58 | - [vlad196/ALTRegularGnomeWiki: открытое сообщество пользователей операционной системы ALT Regular Gnome](https://github.com/vlad196/ALTRegularGnomeWiki) 59 | - [shalotts/shalodoc](https://github.com/shalotts/shalodoc) 60 | - [vdriasworld/manual: Vdrias World! 游玩指南](https://github.com/vdriasworld/manual) 61 | - [LittleSkinCommspt/manual-ng: under dev](https://github.com/LittleSkinCommspt/manual-ng) 62 | 63 | ### 如何下载到本地 64 | 65 | 废话不多说,我们开始下载的步骤吧。 66 | 67 | 如果你没有熟练掌握诸如命令行和 [Git](https://git-scm.com/) 的使用,我们在这里建议你使用 [GitHub](https://github.com) 本身提供的 [下载源代码存档](https://docs.github.com/zh/repositories/working-with-files/using-files/downloading-source-code-archives) 功能直接从 [GitHub](https://github.com) 站点上下载打包好的压缩文件包,然后到本地解压后查看和使用。 68 | 69 | 如果你掌握了命令行和 [Git](https://git-scm.com/) 的使用,可以通过下面的命令克隆项目仓库到名为 `nolebase` 的目录中: 70 | 71 | ```shell 72 | git clone https://github.com/nolebase/nolebase 73 | ``` 74 | 75 | #### 使用的是 Windows 吗 76 | 77 | > [!WARNING] 注意 78 | > 如果你使用的是 [Git for Windows](https://gitforwindows.org/) ,那么可能会在执行上述命令时,遇到类似这样的报错: 79 | > 80 | > ```PowerShell 81 | > PS D:\> git clone https://github.com/nolebase/nolebase 82 | > ... 83 | > error: invalid path 'x: xxx.md' 84 | > fatal: unable to checkout working tree 85 | > warning: Clone succeeded, but checkout failed. 86 | > You can inspect what was checked out with 'git status' 87 | > and retry with 'git restore --source=HEAD :/' 88 | > ``` 89 | > 90 | > 这是 [Git for Windows](https://gitforwindows.org/) 的默认配置导致的[问题](https://github.com/git-for-windows/git/issues/2777)。 91 | > 92 | > 你可以在命令行窗口中输入下面的命令来解决这个问题: 93 | > ```PowerShell 94 | > git config --global core.protectNTFS false 95 | > ``` 96 | 97 | 98 | ### 如何使用、运行或者部署 99 | 100 | 完成了下载了吗?很好,恭喜你已经完成了很艰难的一步! 101 | 102 | 正如先前介绍过 103 | 104 | > [Nólëbase](https://nolebase.ayaka.io) 其实使用了名为 [VitePress](https://vitepress.dev) 的静态生成器来驱动和生成网站。 105 | > 106 | > 而 [VitePress](https://vitepress.dev) 和 [Vue](https://vuejs.org/) 是 [Node.js](https://nodejs.org/en) 生态的一部分,他们都属于「前端技术」的一部分。 107 | 108 | 因此你需要先配置一下 [Node.js](https://nodejs.org/en) 和添加和管理 [VitePress](https://vitepress.dev) 和 [Vue](https://vuejs.org/) 作为底层管理依赖的工具 [pnpm](https://pnpm.io/) 。 109 | 110 | #### 准备工作 111 | 112 | 所以你在继续下面的步骤之前,需要完成另外的两件事情: 113 | 114 | 1. 安装和配置 [Node.js](https://nodejs.org/en),要校验 Node.js 是否安装成功,可以通过打开命令行窗口然后运行 `node --version` 和 `npm --version` 来查看是否会报错; 115 | 2. 安装和配置 [pnpm](https://pnpm.io/),要校验 pnpm 是否安装成功,可以通过打开命令行窗口然后运行 `pnpm --version`。 116 | 117 | ##### 使用的是 Windows 吗 118 | 119 | > [!WARNING] 注意 120 | > 121 | > 如果你使用的是 Windows,可以选择通过 [`scoop`](https://scoop.sh/)(一款在 Windows 上面向开发者可用的包管理器)来安装这些必要的工具,这样可以避免在 Windows 上面安装和配置这些工具的时候遇到的一些问题。 122 | > 123 | > 想要快速安装 Scoop,使用 Win + Q 打开搜索,键入「Powershell」之后点击搜索结果中的蓝色方块,然后输入下面的命令: 124 | > 125 | > ```powershell 126 | > Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser 127 | > ``` 128 | > 129 | > ```powershell 130 | > Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression 131 | > ``` 132 | 133 | 使用 Win + Q 打开搜索,键入「Powershell」之后点击搜索结果中的蓝色方块,然后输入下面的命令: 134 | 135 | ```shell 136 | node --version 137 | ``` 138 | 139 | 如果你看到了类似于 `vxx.xx.xx` 的版本号(比如 `v21.1.0`),那么恭喜你,你已经成功安装了 [Node.js](https://nodejs.org/en)。 140 | 141 | 如果没有看到,那么你需要先安装 [Node.js](https://nodejs.org/en)。如果采用了上面提到的 `scoop`,可以使用下面的命令来安装 [Node.js](https://nodejs.org/en): 142 | 143 | ```shell 144 | scoop install nodejs 145 | ``` 146 | 147 | > [!WARNING] 注意 148 | > 149 | > 由于我们使用到了 `sharp` 这个依赖来生成图片,而 `sharp` 依赖需要使用到 Python,因此你也需要安装 Python。 150 | > 151 | > 如果采用了上面提到的 `scoop`,可以使用下面的命令来安装 Python: 152 | > 153 | > ```shell 154 | > scoop install python 155 | > ``` 156 | 157 | 接下来让我们来安装 [pnpm](https://pnpm.io/),使用下面的命令来安装 [pnpm](https://pnpm.io/): 158 | 159 | ```shell 160 | corepack enable 161 | ``` 162 | 163 | ```shell 164 | corepack prepare pnpm@latest --activate 165 | ``` 166 | 167 | 首次安装完成之后需要运行一下 168 | 169 | ```shell 170 | pnpm setup 171 | ``` 172 | 173 | 来配置 [pnpm](https://pnpm.io/) 所需要的目录,完成之后需要关掉当前的 Powershell 窗口,然后重新打开一个新的 Powershell 窗口。 174 | 175 | 差不多准备好啦,让我们前往 Nólëbase 知识库所在的目录吧,使用下面的命令来前往 Nólëbase 知识库所在的目录: 176 | 177 | ```shell 178 | cd 179 | ``` 180 | 181 | 先多输入一个空格,然后接下来打开文件管理器,把你解压缩完成的 Nólëbase 知识库文件夹拖拽到运行窗口中,最后按下 回车 键,就可以告诉 Powershell 前往 Nólëbase 知识库所在的目录了。 182 | 183 | ##### 使用的是 macOS 吗 184 | 185 | > [!WARNING] 注意 186 | > 187 | > 如果你使用的是 macOS,可以选择通过 [`Homebrew`](https://brew.sh/)(一款在 macOS 上面向开发者可用的包管理器)来安装这些必要的工具,这样可以避免在 macOS 上面安装和配置这些工具的时候遇到的一些问题。 188 | > 189 | > 想要快速安装 Homebrew,使用 command + 空格 打开「终端」应用,然后输入下面的命令: 190 | > 191 | > ```shell 192 | > /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 193 | > ``` 194 | 195 | 使用 command + 空格 打开「终端」应用,然后输入下面的命令: 196 | 197 | ```shell 198 | node --version 199 | ``` 200 | 201 | 如果你看到了类似于 `vxx.xx.xx` 的版本号(比如 `v21.1.0`),那么恭喜你,你已经成功安装了 [Node.js](https://nodejs.org/en)。 202 | 203 | 如果没有看到,那么你需要先安装 [Node.js](https://nodejs.org/en)。如果采用了上面提到的 `Homebrew`,可以使用下面的命令来安装 [Node.js](https://nodejs.org/en): 204 | 205 | ```shell 206 | brew install node 207 | ``` 208 | 209 | 接下来让我们来安装 [pnpm](https://pnpm.io/),使用下面的命令来安装 [pnpm](https://pnpm.io/): 210 | 211 | ```shell 212 | corepack enable 213 | ``` 214 | 215 | ```shell 216 | corepack prepare pnpm@latest --activate 217 | ``` 218 | 219 | 首次安装完成之后需要运行一下 220 | 221 | ```shell 222 | pnpm setup 223 | ``` 224 | 225 | 来配置 [pnpm](https://pnpm.io/) 所需要的目录,完成之后需要关掉当前的终端窗口,然后重新打开一个新的终端窗口。 226 | 227 | 差不多准备好啦,让我们前往 Nólëbase 知识库所在的目录吧,使用下面的命令来前往 Nólëbase 知识库所在的目录: 228 | 229 | ```shell 230 | cd 231 | ``` 232 | 233 | 先多输入一个空格,然后接下来打开访达,把你解压缩完成的 Nólëbase 知识库文件夹拖拽到终端窗口中,最后按下 return 键,就可以告诉终端前往 Nólëbase 知识库所在的目录了。 234 | 235 | ##### 使用的是 Linux 吗 236 | 237 | 你既然已经在使用 Linux 了,应该知道怎么做了吧? 238 | 239 | #### 安装依赖和运行开发服务器 240 | 241 | 在倒数第二步中,我们需要安装依赖,这样 [VitePress](https://vitepress.dev) 和 [Vue](https://vuejs.org/) 就会被下载到本地的 [Nólëbase](https://nolebase.ayaka.io) 目录/文件夹下的 `node_modules` 目录/文件夹下了: 242 | 243 | ```shell 244 | pnpm install 245 | ``` 246 | 247 | 接下来你可以直接运行下面的命令开启一个本地运行的 [Nólëbase](https://nolebase.ayaka.io) 知识库前端服务器,通常而言我们称之为「开发服务器」,用这个服务器,可以通过浏览器在本地直接访问渲染完成的页面: 248 | 249 | ```shell 250 | pnpm docs:dev 251 | ``` 252 | 253 | 就像这样 254 | 255 | ```shell 256 | $ pnpm docs:dev 257 | 258 | vitepress v1.0.0-rc.20 259 | 260 | ➜ Local: http://localhost:5173/ 261 | ➜ Network: use --host to expose 262 | ➜ press h to show help 263 | ``` 264 | 265 | 当你看到上面的字样的时候,就可以前往本地的 [http://localhost:5173](http://localhost:5173) 查看渲染完成的页面了: 266 | 267 | 在这个本地运行的 Nólëbase 知识库前端服务器启动的情况下,你针对所有 Markdown 文件、样式文件、配置文件的变更,都会实时响应到网页中。 268 | 269 | 如果刷新不及时或者更新有异常,也可以试试看使用 command + R (macOS 系统) Ctrl + R (Windows 和 Linux 系统)快捷键强制刷新。 270 | 271 | #### 构建并渲染为可部署的静态页面 272 | 273 | 想要部署页面,首先先确保你已经执行过了[安装依赖和运行开发服务器](#安装依赖和运行开发服务器) 的步骤,一般而言构建和渲染的时候可能遇到的问题都可以在运行开发服务器的时候发现,接下来你只需要一个简单的命令就能完成构建了: 274 | 275 | ```shell 276 | pnpm docs:build 277 | ``` 278 | 279 | 构建完成后,渲染出来的 HTML 和各种资源将会被存储在 `.vitepress/dist` 目录下面,你可以通过上传 `.vitepress/dist` 目录来完成 Nólëbase 知识库的部署。 280 | 281 | #### 使用静态网站托管服务部署页面 282 | 283 | Nólëbase 知识库使用 VitePress 静态生成器来驱动和生成静态页面,因此可以部署到下列已知的优质**静态网站托管服务**: 284 | 285 | - [Netlify](https://netlify.com/) 286 | - [Vercel](https://vercel.com/) 287 | - [Cloudflare Pages](https://pages.cloudflare.com/) 288 | - [AWS Amplify](https://aws.amazon.com/cn/amplify/) 289 | - [Render](https://render.com/) 290 | - [GitHub Pages](https://pages.github.com/) 291 | - [Azure Static Web](https://azure.microsoft.com/en-us/products/app-service/static) 292 | 293 | > [!NOTE] 294 | > 与自建单独的虚拟机并使用类似于 Nginx,或者对象存储(OSS)相比,使用上述提及的静态网站托管服务时,**可以省略手动部署流程**,也**无需花费时间与精力维护单独的网站服务器**。 295 | > 296 | > 让我们把精力放在写作上吧!❤️ 297 | 298 | 请参照 VitePress 官方文档的[部署 VitePress 站点](https://vitepress.dev/zh/guide/deploy)页面文档所介绍的内容,通过主流的静态网站托管服务来部署自己的 Nólëbase 知识库。 299 | -------------------------------------------------------------------------------- /LICENSE-CC-BY-SA: -------------------------------------------------------------------------------- 1 | Attribution-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-ShareAlike 4.0 International Public 58 | License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-ShareAlike 4.0 International Public License ("Public 63 | License"). To the extent this Public License may be interpreted as a 64 | contract, You are granted the Licensed Rights in consideration of Your 65 | acceptance of these terms and conditions, and the Licensor grants You 66 | such rights in consideration of benefits the Licensor receives from 67 | making the Licensed Material available under these terms and 68 | conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. Share means to provide material to the public by any means or 126 | process that requires permission under the Licensed Rights, such 127 | as reproduction, public display, public performance, distribution, 128 | dissemination, communication, or importation, and to make material 129 | available to the public including in ways that members of the 130 | public may access the material from a place and at a time 131 | individually chosen by them. 132 | 133 | l. Sui Generis Database Rights means rights other than copyright 134 | resulting from Directive 96/9/EC of the European Parliament and of 135 | the Council of 11 March 1996 on the legal protection of databases, 136 | as amended and/or succeeded, as well as other essentially 137 | equivalent rights anywhere in the world. 138 | 139 | m. You means the individual or entity exercising the Licensed Rights 140 | under this Public License. Your has a corresponding meaning. 141 | 142 | 143 | Section 2 -- Scope. 144 | 145 | a. License grant. 146 | 147 | 1. Subject to the terms and conditions of this Public License, 148 | the Licensor hereby grants You a worldwide, royalty-free, 149 | non-sublicensable, non-exclusive, irrevocable license to 150 | exercise the Licensed Rights in the Licensed Material to: 151 | 152 | a. reproduce and Share the Licensed Material, in whole or 153 | in part; and 154 | 155 | b. produce, reproduce, and Share Adapted Material. 156 | 157 | 2. Exceptions and Limitations. For the avoidance of doubt, where 158 | Exceptions and Limitations apply to Your use, this Public 159 | License does not apply, and You do not need to comply with 160 | its terms and conditions. 161 | 162 | 3. Term. The term of this Public License is specified in Section 163 | 6(a). 164 | 165 | 4. Media and formats; technical modifications allowed. The 166 | Licensor authorizes You to exercise the Licensed Rights in 167 | all media and formats whether now known or hereafter created, 168 | and to make technical modifications necessary to do so. The 169 | Licensor waives and/or agrees not to assert any right or 170 | authority to forbid You from making technical modifications 171 | necessary to exercise the Licensed Rights, including 172 | technical modifications necessary to circumvent Effective 173 | Technological Measures. For purposes of this Public License, 174 | simply making modifications authorized by this Section 2(a) 175 | (4) never produces Adapted Material. 176 | 177 | 5. Downstream recipients. 178 | 179 | a. Offer from the Licensor -- Licensed Material. Every 180 | recipient of the Licensed Material automatically 181 | receives an offer from the Licensor to exercise the 182 | Licensed Rights under the terms and conditions of this 183 | Public License. 184 | 185 | b. Additional offer from the Licensor -- Adapted Material. 186 | Every recipient of Adapted Material from You 187 | automatically receives an offer from the Licensor to 188 | exercise the Licensed Rights in the Adapted Material 189 | under the conditions of the Adapter's License You apply. 190 | 191 | c. No downstream restrictions. You may not offer or impose 192 | any additional or different terms or conditions on, or 193 | apply any Effective Technological Measures to, the 194 | Licensed Material if doing so restricts exercise of the 195 | Licensed Rights by any recipient of the Licensed 196 | Material. 197 | 198 | 6. No endorsement. Nothing in this Public License constitutes or 199 | may be construed as permission to assert or imply that You 200 | are, or that Your use of the Licensed Material is, connected 201 | with, or sponsored, endorsed, or granted official status by, 202 | the Licensor or others designated to receive attribution as 203 | provided in Section 3(a)(1)(A)(i). 204 | 205 | b. Other rights. 206 | 207 | 1. Moral rights, such as the right of integrity, are not 208 | licensed under this Public License, nor are publicity, 209 | privacy, and/or other similar personality rights; however, to 210 | the extent possible, the Licensor waives and/or agrees not to 211 | assert any such rights held by the Licensor to the limited 212 | extent necessary to allow You to exercise the Licensed 213 | Rights, but not otherwise. 214 | 215 | 2. Patent and trademark rights are not licensed under this 216 | Public License. 217 | 218 | 3. To the extent possible, the Licensor waives any right to 219 | collect royalties from You for the exercise of the Licensed 220 | Rights, whether directly or through a collecting society 221 | under any voluntary or waivable statutory or compulsory 222 | licensing scheme. In all other cases the Licensor expressly 223 | reserves any right to collect such royalties. 224 | 225 | 226 | Section 3 -- License Conditions. 227 | 228 | Your exercise of the Licensed Rights is expressly made subject to the 229 | following conditions. 230 | 231 | a. Attribution. 232 | 233 | 1. If You Share the Licensed Material (including in modified 234 | form), You must: 235 | 236 | a. retain the following if it is supplied by the Licensor 237 | with the Licensed Material: 238 | 239 | i. identification of the creator(s) of the Licensed 240 | Material and any others designated to receive 241 | attribution, in any reasonable manner requested by 242 | the Licensor (including by pseudonym if 243 | designated); 244 | 245 | ii. a copyright notice; 246 | 247 | iii. a notice that refers to this Public License; 248 | 249 | iv. a notice that refers to the disclaimer of 250 | warranties; 251 | 252 | v. a URI or hyperlink to the Licensed Material to the 253 | extent reasonably practicable; 254 | 255 | b. indicate if You modified the Licensed Material and 256 | retain an indication of any previous modifications; and 257 | 258 | c. indicate the Licensed Material is licensed under this 259 | Public License, and include the text of, or the URI or 260 | hyperlink to, this Public License. 261 | 262 | 2. You may satisfy the conditions in Section 3(a)(1) in any 263 | reasonable manner based on the medium, means, and context in 264 | which You Share the Licensed Material. For example, it may be 265 | reasonable to satisfy the conditions by providing a URI or 266 | hyperlink to a resource that includes the required 267 | information. 268 | 269 | 3. If requested by the Licensor, You must remove any of the 270 | information required by Section 3(a)(1)(A) to the extent 271 | reasonably practicable. 272 | 273 | b. ShareAlike. 274 | 275 | In addition to the conditions in Section 3(a), if You Share 276 | Adapted Material You produce, the following conditions also apply. 277 | 278 | 1. The Adapter's License You apply must be a Creative Commons 279 | license with the same License Elements, this version or 280 | later, or a BY-SA Compatible License. 281 | 282 | 2. You must include the text of, or the URI or hyperlink to, the 283 | Adapter's License You apply. You may satisfy this condition 284 | in any reasonable manner based on the medium, means, and 285 | context in which You Share Adapted Material. 286 | 287 | 3. You may not offer or impose any additional or different terms 288 | or conditions on, or apply any Effective Technological 289 | Measures to, Adapted Material that restrict exercise of the 290 | rights granted under the Adapter's License You apply. 291 | 292 | 293 | Section 4 -- Sui Generis Database Rights. 294 | 295 | Where the Licensed Rights include Sui Generis Database Rights that 296 | apply to Your use of the Licensed Material: 297 | 298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 299 | to extract, reuse, reproduce, and Share all or a substantial 300 | portion of the contents of the database; 301 | 302 | b. if You include all or a substantial portion of the database 303 | contents in a database in which You have Sui Generis Database 304 | Rights, then the database in which You have Sui Generis Database 305 | Rights (but not its individual contents) is Adapted Material, 306 | 307 | including for purposes of Section 3(b); and 308 | c. You must comply with the conditions in Section 3(a) if You Share 309 | all or a substantial portion of the contents of the database. 310 | 311 | For the avoidance of doubt, this Section 4 supplements and does not 312 | replace Your obligations under this Public License where the Licensed 313 | Rights include other Copyright and Similar Rights. 314 | 315 | 316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 317 | 318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 328 | 329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 338 | 339 | c. The disclaimer of warranties and limitation of liability provided 340 | above shall be interpreted in a manner that, to the extent 341 | possible, most closely approximates an absolute disclaimer and 342 | waiver of all liability. 343 | 344 | 345 | Section 6 -- Term and Termination. 346 | 347 | a. This Public License applies for the term of the Copyright and 348 | Similar Rights licensed here. However, if You fail to comply with 349 | this Public License, then Your rights under this Public License 350 | terminate automatically. 351 | 352 | b. Where Your right to use the Licensed Material has terminated under 353 | Section 6(a), it reinstates: 354 | 355 | 1. automatically as of the date the violation is cured, provided 356 | it is cured within 30 days of Your discovery of the 357 | violation; or 358 | 359 | 2. upon express reinstatement by the Licensor. 360 | 361 | For the avoidance of doubt, this Section 6(b) does not affect any 362 | right the Licensor may have to seek remedies for Your violations 363 | of this Public License. 364 | 365 | c. For the avoidance of doubt, the Licensor may also offer the 366 | Licensed Material under separate terms or conditions or stop 367 | distributing the Licensed Material at any time; however, doing so 368 | will not terminate this Public License. 369 | 370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 371 | License. 372 | 373 | 374 | Section 7 -- Other Terms and Conditions. 375 | 376 | a. The Licensor shall not be bound by any additional or different 377 | terms or conditions communicated by You unless expressly agreed. 378 | 379 | b. Any arrangements, understandings, or agreements regarding the 380 | Licensed Material not stated herein are separate from and 381 | independent of the terms and conditions of this Public License. 382 | 383 | 384 | Section 8 -- Interpretation. 385 | 386 | a. For the avoidance of doubt, this Public License does not, and 387 | shall not be interpreted to, reduce, limit, restrict, or impose 388 | conditions on any use of the Licensed Material that could lawfully 389 | be made without permission under this Public License. 390 | 391 | b. To the extent possible, if any provision of this Public License is 392 | deemed unenforceable, it shall be automatically reformed to the 393 | minimum extent necessary to make it enforceable. If the provision 394 | cannot be reformed, it shall be severed from this Public License 395 | without affecting the enforceability of the remaining terms and 396 | conditions. 397 | 398 | c. No term or condition of this Public License will be waived and no 399 | failure to comply consented to unless expressly agreed to by the 400 | Licensor. 401 | 402 | d. Nothing in this Public License constitutes or may be interpreted 403 | as a limitation upon, or waiver of, any privileges and immunities 404 | that apply to the Licensor or You, including from the legal 405 | processes of any jurisdiction or authority. 406 | 407 | 408 | ======================================================================= 409 | 410 | Creative Commons is not a party to its public 411 | licenses. Notwithstanding, Creative Commons may elect to apply one of 412 | its public licenses to material it publishes and in those instances 413 | will be considered the “Licensor.” The text of the Creative Commons 414 | public licenses is dedicated to the public domain under the CC0 Public 415 | Domain Dedication. Except for the limited purpose of indicating that 416 | material is shared under a Creative Commons public license or as 417 | otherwise permitted by the Creative Commons policies published at 418 | creativecommons.org/policies, Creative Commons does not authorize the 419 | use of the trademark "Creative Commons" or any other trademark or logo 420 | of Creative Commons without its prior written consent including, 421 | without limitation, in connection with any unauthorized modifications 422 | to any of its public licenses or any other arrangements, 423 | understandings, or agreements concerning use of licensed material. For 424 | the avoidance of doubt, this paragraph does not form part of the 425 | public licenses. 426 | 427 | Creative Commons may be contacted at creativecommons.org. 428 | --------------------------------------------------------------------------------