Compare commits

..

No commits in common. "main" and "v1.0.0-beta.1" have entirely different histories.

1352 changed files with 18481 additions and 168044 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
node_modules/
.temp/
lib/
dist/
!.vuepress/
!.*.js

68
.eslintrc.js Normal file
View File

@ -0,0 +1,68 @@
module.exports = {
root: true,
extends: 'vuepress',
globals: {
__VUEPRESS_VERSION__: 'readonly',
__VUEPRESS_DEV__: 'readonly',
__VUEPRESS_SSR__: 'readonly',
__VUE_HMR_RUNTIME__: 'readonly',
__VUE_OPTIONS_API__: 'readonly',
__VUE_PROD_DEVTOOLS__: 'readonly',
},
overrides: [
{
files: ['*.ts', '*.vue'],
extends: 'vuepress-typescript',
parserOptions: {
project: ['./tsconfig.json'],
},
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-var-requires': 'off',
'vue/component-tags-order': [
'error',
{
order: ['script', 'template', 'style'],
},
],
'vue/multi-word-component-names': 'off',
},
},
{
files: ['*.vue'],
globals: {
defineEmits: 'readonly',
defineProps: 'readonly',
},
rules: {
// disable for setup script
'@typescript-eslint/no-unused-vars': 'off',
},
},
{
files: ['clientAppEnhance.ts'],
rules: {
'vue/match-component-file-name': 'off',
},
},
{
files: ['**/__tests__/**/*.ts'],
env: {
jest: true,
},
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
'vue/one-component-per-file': 'off',
'import/no-extraneous-dependencies': 'off',
},
},
{
files: ['docs/**'],
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
],
}

3
.gitattributes vendored
View File

@ -4,10 +4,7 @@
*.png binary
*.jpg binary
*.jpeg binary
*.webp binary
*.ico binary
*.gif binary
*.tff binary
*.woff binary
*.woff2 binary
*.pdf binary

15
.github/FUNDING.yml vendored
View File

@ -1,15 +0,0 @@
# These are supported funding model platforms
# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# polar: # Replace with a single Polar username
# buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
- https://theme-plume.vuejs.press/sponsor/

View File

@ -1,73 +0,0 @@
name: Bug report
description: Create a report to help us improve
title: '[Bug] '
labels:
- bug
assignees: pengzhanbo
body:
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: |-
Make SURE that you follow these statements.
options:
- label: I am using **LTS** version of Node.js.
required: true
- label: I am using the **latest v2 version** of VuePress and VuePress official plugins.
required: true
- label: I am using the **latest version** of vuepress-theme-plume and all plugins in this repo.
required: true
- label: I followed the docs and I double checked my configuration.
required: true
- type: input
id: package
attributes:
label: Package name
description: Which package are you reporting
value: vuepress-theme-plume
placeholder: package name
validations:
required: true
- type: checkboxes
id: operating-systems
attributes:
label: Which operating system are you using?
description: You may select more than one. Dont select anything if it's not about the environment.
options:
- label: macOS
- label: Windows
- label: Linux
- type: markdown
attributes:
value: |
## Describe the bug
> If applicable, add screenshots and log to help explain your problem.
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: input
id: reproduction
attributes:
label: Minimal reproduction
description: |-
If you are not reporting something obvious, a minimal reproduction repo and related log is required. you can fork [stackblitz.com](https://stackblitz.com/edit/vuepress-theme-plume-playground) to create a minimal reproduction.
placeholder: reproduction repo url
- type: textarea
id: additional-context
attributes:
label: Additional context
description: If you are not reporting something obvious, related log is required.
placeholder: Add any other context about the problem here. Especially the issue occurs in certain OS, browser or configuration.

View File

@ -1,73 +0,0 @@
name: 问题报告
description: 创建一份问题报告以帮助我们改进
title: '[Bug] '
labels:
- bug
assignees: pengzhanbo
body:
- type: checkboxes
id: checklist
attributes:
label: 检查清单
description: |-
确保你遵循以下陈述。
options:
- label: 我正在使用**LTS**版本的Node.js。
required: true
- label: 我正在使用 **最新 v2 版本** 的 VuePress 和 VuePress 官方插件。
required: true
- label: 我正在使用此仓库中的 **最新版本** 的 vuepress-theme-plume 及其所有插件。
required: true
- label: 我按照文档操作,并仔细检查了我的配置。
required: true
- type: input
id: package
attributes:
label: 包名
description: 您要报告哪个包
value: vuepress-theme-plume
placeholder: package name
validations:
required: true
- type: checkboxes
id: operating-systems
attributes:
label: 你正在使用哪个操作系统?
description: 您可以选择多个选项。如果与环境无关,请不要选择任何选项。
options:
- label: macOS
- label: Windows
- label: Linux
- type: markdown
attributes:
value: |
## 描述该错误
> 如有需要,请添加截图和日志以帮助解释您的问题。
- type: textarea
id: description
attributes:
label: 描述该错误
description: 一个清晰简洁的错误描述。
validations:
required: true
- type: input
id: reproduction
attributes:
label: 最小复现
description: |-
若非报告显而易见的问题,需提供最小化复现仓库及相关日志。你可通过 fork [stackblitz.com](https://stackblitz.com/edit/vuepress-theme-plume-playground) 来创建最小化复现环境。
placeholder: 复现项目 url
- type: textarea
id: additional-context
attributes:
label: 附加上下文
description: 如果你报告的问题不明显,需要提供相关日志。
placeholder: 在此添加有关问题的其他上下文信息。特别是问题在特定操作系统、浏览器或配置下出现。

View File

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Questions about theme and plugins
url: https://github.com/pengzhanbo/vuepress-theme-plume/discussions/new
about: Please ask questions in Discussion.

View File

@ -1,33 +0,0 @@
name: Feature request
description: Suggest an idea for this project
title: '[Feature Request]'
labels:
- enhancement
assignees: pengzhanbo
body:
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Make sure that you follow these statements.
options:
- label: I searched existing issues and no one else requests similar feature.
required: true
- label: I think that 25%+ users are positive towards this feature.
required: true
- type: textarea
id: feature
attributes:
label: Describe the feature
description: What's it for and why you want
placeholder: A clear and concise description of what the feature is, and what it will enhance or solve. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
placeholder: Add any other context or screenshots about the feature request here.

View File

@ -1,33 +0,0 @@
name: 新功能建议
description: 为该项目提出一个想法
title: '[Feature Request]'
labels:
- enhancement
assignees: pengzhanbo
body:
- type: checkboxes
id: checklist
attributes:
label: 检查清单
description: 确保遵循以下陈述。
options:
- label: 我搜索了现有问题,没有其他人请求类似功能。
required: true
- label: 我认为25%以上的用户对此功能持积极态度。
required: true
- type: textarea
id: feature
attributes:
label: 描述该功能
description: 它有什么用,你为什么想要
placeholder: 功能清晰简洁的描述,以及它将增强或解决的问题。例如,我总是感到困难,当 [...]
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: 附加上下文
placeholder: 在此添加有关功能请求的任何其他上下文或截图。

View File

@ -1,52 +0,0 @@
name: Deploy Docs
on:
push:
branches:
- main
paths:
# 以下文件发生变化时触发部署,这些文件与版本无关,因此可以自动更新
- docs/demos.md
- docs/sponsor.md
- CONTRIBUTING.md
- CONTRIBUTING.en-US.md
workflow_dispatch:
workflow_call:
permissions:
contents: write
jobs:
deploy-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Build Packages
run: pnpm build:package
- name: Docs build
env:
NODE_OPTIONS: --max_old_space_size=8192
run: pnpm docs:build
- name: Deploy docs
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: docs/.vuepress/dist
single-commit: true

View File

@ -1,45 +0,0 @@
name: Deploy Example Layout Slots
on:
push:
tags:
- v*
workflow_dispatch:
permissions:
contents: write
jobs:
deploy-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Build Packages
run: pnpm build:package
- name: Docs build
env:
NODE_OPTIONS: --max_old_space_size=8192
run: cd examples/layout-slots && pnpm docs:build
- name: Deploy docs
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages-layout-slots
folder: examples/layout-slots/docs/.vuepress/dist
single-commit: true

View File

@ -1,37 +0,0 @@
name: Linter
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_call:
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Linter
run: |
pnpm run lint:check
pnpm run lint:css

View File

@ -1,55 +0,0 @@
name: Release
on:
push:
tags:
- v*
permissions:
contents: write
id-token: write
jobs:
lint:
uses: ./.github/workflows/lint.yaml
test:
uses: ./.github/workflows/test.yaml
release:
if: github.repository == 'pengzhanbo/vuepress-theme-plume'
needs: [test, lint]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
cache: pnpm
- name: Install deps
run: pnpm install
- name: Update npm
run: npm i -g npm@latest
- name: Build And Publish
id: publish
run: |
pnpm build
pnpm release:publish --no-git-checks
pnpm release:sync
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
deploy:
uses: ./.github/workflows/docs-deploy.yaml

View File

@ -1,43 +0,0 @@
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_call:
permissions:
contents: read
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Unit Test
env:
NODE_OPTIONS: --max_old_space_size=8192
run: pnpm run test
- name: Upload coverage
if: github.ref == 'refs/heads/main'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}

17
.gitignore vendored
View File

@ -1,20 +1,13 @@
**/node_modules/
node_modules
**/.vuepress/.cache
**/.vuepress/.temp
**/.vuepress/dist
docs/.vuepress/.cache
docs/.vuepress/.temp
docs/.vuepress/dist
lib/
dist/
lib
.DS_Store
*.log
*.tsbuildinfo
.mind
coverage/
.idea
.claude/
!.claude/skills/

View File

@ -1 +0,0 @@
npx commitlint --edit $1

View File

@ -1 +0,0 @@
npx lint-staged

View File

@ -1,31 +0,0 @@
{
"default": true,
"heading-style": {
"style": "atx"
},
"ul-style": {
"style": "dash"
},
"no-duplicate-heading": false,
"first-line-h1": false,
"first-line-heading": false,
"no-blanks-blockquote": false,
"line-length": {
"line_length": 140,
"tables": false,
"code_blocks": false
},
"no-inline-html": false,
"hr-style": {
"style": "---"
},
"fenced-code-language": false,
"code-block-style": false,
"emphasis-style": false,
"no-hard-tabs": {
"spaces_per_tab": 2,
"ignore_code_languages": ["xml"]
},
"link-image-reference-definitions": false,
"no-bare-urls": false
}

View File

@ -1,4 +0,0 @@
**/node_modules/**
**/*.snippet.md
CHANGELOG.*.md
CHANGELOG.md

View File

@ -1,7 +0,0 @@
node_modules
.cache
.temp
lib
dist

View File

@ -1,8 +0,0 @@
{
"recommendations": [
"vue.volar",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"DavidAnson.vscode-markdownlint"
]
}

29
.vscode/launch.json vendored
View File

@ -1,29 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "dev",
"request": "launch",
"type": "node-terminal",
"command": "pnpm dev"
},
{
"name": "build",
"request": "launch",
"type": "node-terminal",
"command": "pnpm build"
},
{
"name": "docs:dev",
"type": "node-terminal",
"request": "launch",
"command": "pnpm docs:dev"
},
{
"name": "docs:build",
"type": "node-terminal",
"request": "launch",
"command": "pnpm docs:build"
}
]
}

79
.vscode/settings.json vendored
View File

@ -8,85 +8,22 @@
"[markdown]": {
"files.trimTrailingWhitespace": false
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"css.validate": false,
"scss.validate": false,
"less.validate": false,
"typescript.tsdk": "node_modules/typescript/lib",
"stylelint.packageManager": "pnpm",
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "format/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
"editor.formatOnSave": false,
"prettier.enable": false,
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "explicit",
"source.fixAll.eslint": "explicit",
"source.fixAll.markdownlint": "explicit",
"source.organizeImports": "never"
},
"editor.formatOnPaste": false,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
],
"stylelint.validate": [
"css",
"scss",
"postcss",
"vue"
],
"cSpell.words": [
"acfun",
"bilibili",
"bumpp",
"caniuse",
"colours",
"commitlint",
"composables",
"Docsearch",
"esbuild",
"fontawesome",
"frontmatter",
"gsap",
"iarna",
"iconfont",
"iconify",
"katex",
"leancloud",
"nprogress",
"pnpm",
"portfinder",
"qrcode",
"shiki",
"shikiji",
"shikijs",
"taze",
"Tongji",
"tsbuildinfo",
"tsdown",
"twoslash",
"vite",
"vuepress",
"vueuse"
"caniuse",
"composables",
"Docsearch",
"nprogress",
"tsbuildinfo",
"vite",
"vuepress",
"vueuse"
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

111
CLAUDE.md
View File

@ -1,111 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
vuepress-theme-plume is a VuePress 2 theme monorepo for building blogs, documentation, and knowledge bases.
It includes a main theme, several plugins, a CLI tool, and example implementations.
## Commands
```bash
# Install dependencies
pnpm install
# Build all packages (required after clone, outputs to lib/)
pnpm build
# Development - runs theme + docs dev servers concurrently
pnpm dev
# Lint (eslint + stylelint)
pnpm lint
pnpm lint:fix # auto-fix
# Run tests (vitest)
pnpm test
# Run a single test file
pnpm test src/path/to/file.spec.ts
# Run tests related to changed files (for pre-commit)
cross-env TZ=Etc/UTC vitest related --run
# Build docs only
pnpm docs:build
# Serve docs locally
pnpm docs:serve
# Release workflow
pnpm release # runs lint + build + version bump + changelog + git commit
```
## Monorepo Structure
```txt
├── theme/ # Main VuePress theme (vuepress-theme-plume)
├── plugins/ # VuePress plugins
│ ├── plugin-search/ # Full-text fuzzy search
│ ├── plugin-md-power/ # Markdown enhancements
│ └── plugin-fonts/ # Special character font support
├── cli/ # CLI tool (create project scaffolding)
├── docs/ # Documentation site
└── examples/ # Example implementations
├── pure-blog/
└── layout-slots/
```
## Theme Architecture
The theme is organized into three layers:
- **`src/node/`** - Build-time code (runs during `vuepress build/dev`)
- `prepare/` - Content preparation (frontmatter parsing, collection resolution)
- `plugins/` - VuePress plugin registration
- `config/` - Theme configuration handling
- `autoFrontmatter/` - Automatic frontmatter generation
- **`src/client/`** - Client-side code (runs in browser)
- `components/` - Vue components
- `composables/` - Vue composables (outline, search, etc.)
- `styles/` - CSS/SCSS styles
- `features/` - Feature-specific components and logic
- **`src/shared/`** - Shared code (used by both node and client)
- `frontmatter/` - Frontmatter schemas and utilities
- `locale/` - i18n translations
- `options.ts` - Theme options types
- `features/` - Feature flags and shared feature logic
## Build Output
Each package uses [tsdown](https://tsdown.dev/) to compile TypeScript. Build output goes to `lib/`:
- `lib/node/` - Node-side exports
- `lib/client/` - Client-side exports
- `lib/shared/` - Shared exports
The `lib/` directory is gitignored and must be built with `pnpm build`.
## Testing
Tests use Vitest with coverage enabled. Test files are located at `**/__test__/**/*.spec.ts` and are excluded from coverage reports. Run tests with timezone fixed to UTC to ensure consistent results.
## Key Dependencies
- **VuePress**: v2.0.0-rc.28 with @vuepress/bundler-vite
- **Vue**: ^3.5.30
- **Shiki**: ^4.x for syntax highlighting
- **VueUse**: ^14.x for composables
- **markdown-it**: ^14.x for Markdown processing
## Development Notes
- Node.js 20.19.0+ required
- pnpm catalogs are used for dependency management (`dev`, `peer`, `prod`, `vuepress`)
- The theme depends on `vuepress-plugin-md-power` and `@vuepress-plume/plugin-search` as workspace dependencies
- Some peer dependencies are optional (e.g., artplayer, dashjs, three.js)
- Plugins (`plugins/*`) do not have dev commands — changes require `pnpm build` to take effect
- The `lib/` directory is gitignored and must be rebuilt after `pnpm install`

View File

@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
q942450674@outlook.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -1,76 +0,0 @@
# Contribution Guide
## Overview
The project repository uses [pnpm workspaces](https://pnpm.io/zh/workspaces) to implement
a [Monorepo](https://en.wikipedia.org/wiki/Monorepo), which stores multiple interrelated independent Packages.
- The theme is developed and maintained in the `theme` directory.
- Plugins are developed and maintained in the `plugins` directory.
- Documentation is developed and maintained in the `docs` directory.
In the `plugins` directory:
- `plugin-search`: Provides full-text fuzzy search functionality for the theme.
- `plugin-md-power`: Provides enhanced markdown features.
- `plugin-fonts`: Provides special character font support
## Development Configuration
Development requirements:
- [Node.js](http://nodejs.org/) version 20.19.0+
- [pnpm](https://pnpm.io/zh/) version 9+
Clone the repository and install dependencies:
```sh
pnpm install
```
Before starting the development service for the first time, build the source code:
```sh
pnpm build
```
### Main Tools
- [TypeScript](https://www.typescriptlang.org/) as the development language.
- [ESLint](https://eslint.org/) for code checking and formatting.
- [StyleLint](https://stylelint.io/) for code checking and formatting.
### Scripts
#### `pnpm build`
The `build` command uses `tsc` to compile the source code into `.js` files in the `lib` directory.
It also copies resources that do not need to be compiled to the corresponding `lib` directory.
After cloning the repository, you need to run this command first to ensure that the project code
can run smoothly, as the compiled output directory is excluded from the repository by `.gitignore`.
#### `pnpm dev`
The `dev` command starts two services locally. One runs the `tsup:watch & copy:watch` for
the `theme` directory, and the other runs the `vuepress` development service for the example `docs` directory.
By default, all plugins under the `plugins` directory do not have a `dev` command.
Therefore, changes to the `plugins` directory may require running the `pnpm build` command to rebuild.
Some changes to the `plugins/**/node` directory require re-running `pnpm dev` to take effect.
#### `pnpm lint`
The `lint` command uses ESLint to check all source files.
When `lint` reports errors, you can manually modify the source code to fix the ESLint errors, or run `pnpm lint:fix` to automatically fix them.
#### `pnpm test`
The `test` command uses Vitest to run all tests.
### IDE Support
It is recommended to use `vs code` for development. This repository is configured with
the recommended `vs code` extensions for developing this theme. When you import this repository,
`vs code` may recommend that you install some extensions.

View File

@ -1,74 +0,0 @@
# 贡献指南
## 概览
项目仓库借助于 [pnpm 工作空间](https://pnpm.io/zh/workspaces) 来实现
[Monorepo](https://en.wikipedia.org/wiki/Monorepo) ,存放了多个互相关联的独立 Package 。
- 主题于 `theme` 目录中进行开发维护。
- 插件于 `plugins` 目录中进行开发维护。
- 文档于 `docs` 目录中进行开发维护。
`plugins` 目录中:
- `plugin-search`: 为主题提供 全文模糊搜索 功能
- `plugin-md-power`: 提供 markdown 增强功能
- `plugin-fonts`: 提供特殊字符字体支持
## 开发配置
开发要求:
- [Node.js](http://nodejs.org/) version 20.19.0+
- [pnpm](https://pnpm.io/zh/) version 9+
克隆代码仓库,并安装依赖:
```sh
pnpm install
```
在首次启动开发服务前,先构建源代码:
```sh
pnpm build
```
### 主要工具
- [TypeScript](https://www.typescriptlang.org/) 作为开发语言
- [ESLint](https://eslint.org/) 用于代码检查和格式化
- [StyleLint](https://stylelint.io/) 用于代码检查和格式化
### 脚本
#### `pnpm build`
`build` 命令使用 `tsc` 将源代码编译成 `lib` 目录下的 `.js` 文件。
同时复制 不需要编译的资源到对应的`lib` 目录下。
你在克隆代码仓库后,需要先执行该命令来确保项目代码可以顺利运行,因为编译后的输出目录被 `.gitignore` 排除在仓库以外了。
#### `pnpm dev`
`dev` 命令会在本地开启两个服务,一个是运行 主题 `theme` 目录的 `tsup:watch & copy:watch`,
一个是运行 示例 `docs` 目录的 `vuepress` 开发服务。
`plugins` 目录下的所有插件,默认都没有 `dev` 命令,因此,你对 `plugins` 下的改动,可能需要执行 `pnpm build` 命令
进行重新构建,部分对 `plugins/**/node` 目录下的改动,需要重新执行 `pnpm dev` 才能生效。
#### `pnpm lint`
`lint` 命令使用 ESLint 来检查所有源文件。
`lint` 给出了错误时,你可以手动修改源码以修复 eslint 的报错。
也可以执行 `pnpm lint:fix` 来自动修复。
#### `pnpm test`
`test` 命令使用 Vitest 来运行所有测试。
### IDE 支持
推荐使用 `vs code` 进行开发。本仓库配置了开发本主题时,推荐的 `vs code` 扩展,
当你导入本仓库时,`vs code` 可能会推荐你安装一些扩展。

21
LICENSE
View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (C) 2021 - PRESENT by pengzhanbo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,12 +0,0 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ---------------- | ------------------ |
| >= 1.0.0-rc.190 | :white_check_mark: |
| < 1.0.0-rc.190 | :x: |
## Reporting a Vulnerability
Please [open an issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new?assignees=pengzhanbo&title=%5BSecurity%5D).

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (C) 2021 - PRESENT by pengzhanbo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,14 +0,0 @@
# create-vuepress-theme-plume
The cli for create vuepress-theme-plume's project
## Usage
```sh
# npm
npm init vuepress-theme-plume@latest
# pnpm
pnpm create vuepress-theme-plume@latest
# yarn
yarn create vuepress-theme-plume@latest
```

View File

@ -1,2 +0,0 @@
#!/usr/bin/env node
import '../lib/index.js'

View File

@ -1,52 +0,0 @@
{
"name": "create-vuepress-theme-plume",
"type": "module",
"version": "1.0.0-rc.196",
"description": "The cli for create vuepress-theme-plume's project",
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
"license": "MIT",
"homepage": "https://theme-plume.vuejs.press/",
"repository": {
"type": "git",
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git",
"directory": "cli"
},
"bugs": {
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
},
"keywords": [
"VuePress",
"theme",
"plume",
"cli"
],
"bin": "./bin/index.js",
"files": [
"bin",
"lib",
"templates"
],
"scripts": {
"build": "tsdown --config-loader unrun"
},
"dependencies": {
"@clack/prompts": "catalog:prod",
"@pengzhanbo/utils": "catalog:prod",
"cac": "catalog:prod",
"handlebars": "catalog:prod",
"nano-spawn": "catalog:prod",
"os-locale": "catalog:prod",
"picocolors": "catalog:prod",
"sort-package-json": "catalog:prod"
},
"plume-deps": {
"vuepress": "2.0.0-rc.28",
"vue": "^3.5.32",
"http-server": "^14.1.1",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public",
"provenance": true
}
}

View File

@ -1,89 +0,0 @@
import type { Bundler, Langs, Options } from './types.js'
/**
* Language options for VuePress configuration
*
* VuePress
*/
export const languageOptions: Options<Langs> = [
{ label: 'English', value: 'en-US' },
{ label: '简体中文', value: 'zh-CN' },
]
/**
* Bundler options for VuePress build tool
*
* VuePress
*/
export const bundlerOptions: Options<Bundler> = [
{ label: 'Vite', value: 'vite' },
{ label: 'Webpack', value: 'webpack' },
]
/**
* Operation mode for VuePress CLI
*
* VuePress CLI
* @readonly
* @enum {number}
*/
export enum Mode {
/**
* Initialize existing directory
*
*
*/
init,
/**
* Create new project
*
*
*/
create,
}
/**
* Deployment type for VuePress site
*
* VuePress
* @readonly
* @enum {string}
*/
export enum DeployType {
/**
* GitHub Pages deployment
*
* GitHub Pages
*/
github = 'github',
/**
* Vercel deployment
*
* Vercel
*/
vercel = 'vercel',
/**
* Netlify deployment
*
* Netlify
*/
netlify = 'netlify',
/**
* Custom deployment
*
*
*/
custom = 'custom',
}
/**
* Deployment options for hosting platforms
*
*
*/
export const deployOptions: Options<DeployType> = [
{ label: 'Custom', value: DeployType.custom },
{ label: 'GitHub Pages', value: DeployType.github },
{ label: 'Vercel', value: DeployType.vercel },
{ label: 'Netlify', value: DeployType.netlify },
]

View File

@ -1,165 +0,0 @@
import type { File, ResolvedData } from './types.js'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import spawn from 'nano-spawn'
import { DeployType, Mode } from './constants.js'
import { createPackageJson } from './packageJson.js'
import { createRender } from './render.js'
import { getTemplate, readFiles, readJsonFile, writeFiles } from './utils/index.js'
/**
* Generate VuePress project files
*
* VuePress
*
* @param mode - Operation mode (init or create) /
* @param data - Resolved configuration data /
* @param cwd - Current working directory /
*/
export async function generate(
mode: Mode,
data: ResolvedData,
cwd: string = process.cwd(),
): Promise<void> {
let userPkg: Record<string, any> = {}
if (mode === Mode.init) {
const pkgPath = path.join(cwd, 'package.json')
if (fs.existsSync(pkgPath)) {
userPkg = (await readJsonFile(pkgPath)) || {}
}
}
const fileList: File[] = [
// add package.json
await createPackageJson(mode, userPkg, data),
// add docs files
...await createDocsFiles(data),
// add vuepress and theme-plume configs
...updateFileListTarget(await readFiles(getTemplate('.vuepress')), `${data.docsDir}/.vuepress`),
]
// add repo root files
if (mode === Mode.create) {
fileList.push(...await readFiles(getTemplate('common')))
if (data.packageManager === 'pnpm') {
fileList.push({
filepath: '.npmrc',
content: 'shamefully-hoist=true\nshell-emulator=true',
})
}
if (data.packageManager === 'yarn') {
const { output } = await spawn('yarn', ['--version'])
if (output.startsWith('2')) {
fileList.push({
filepath: '.yarnrc.yml',
content: 'nodeLinker: \'node-modules\'\n',
})
}
}
}
// rewrite git files begin ==================================
if (data.git) {
const gitFiles = await readFiles(getTemplate('git'))
if (mode === Mode.init) {
const gitignorePath = path.join(cwd, '.gitignore')
const docs = data.docsDir
if (fs.existsSync(gitignorePath)) {
const content = await fs.promises.readFile(gitignorePath, 'utf-8')
fileList.push({
filepath: '.gitignore',
content: `${content}\n${docs}/.vuepress/.cache\n${docs}/.vuepress/.temp\n${docs}/.vuepress/dist\n`,
})
fileList.push(...gitFiles.filter(({ filepath }) => filepath !== '.gitignore'))
}
else {
fileList.push(...gitFiles)
}
}
else {
fileList.push(...gitFiles)
}
}
// rewrite git files end ====================================
if (data.packageManager === 'yarn') {
fileList.push({
filepath: '.yarnrc.yml',
content: 'nodeLinker: \'node-modules\'\n',
})
}
if (data.deploy !== DeployType.custom) {
fileList.push(...await readFiles(getTemplate(`deploy/${data.deploy}`)))
}
const render = createRender(data)
const renderedFiles = fileList.map((file) => {
if (file.filepath.endsWith('.handlebars'))
file.content = render(file.content)
return file
})
const ext = data.useTs ? '' : userPkg.type !== 'module' ? '.mjs' : '.js'
const REG_EXT = /\.ts$/
const output = mode === Mode.create ? path.join(cwd, data.root) : cwd
await writeFiles(renderedFiles, output, (filepath) => {
if (filepath.endsWith('.d.ts'))
return filepath
if (ext)
return filepath.replace(REG_EXT, ext)
return filepath
})
}
/**
* Create documentation files based on configuration
*
*
*
* @param data - Resolved configuration data /
* @returns Array of file objects /
*/
async function createDocsFiles(data: ResolvedData): Promise<File[]> {
const fileList: File[] = []
if (data.multiLanguage) {
const enDocs = await readFiles(getTemplate('docs/en'))
const zhDocs = await readFiles(getTemplate('docs/zh'))
if (data.defaultLanguage === 'en-US') {
fileList.push(...enDocs)
fileList.push(...updateFileListTarget(zhDocs, 'zh'))
}
else {
fileList.push(...zhDocs)
fileList.push(...updateFileListTarget(enDocs, 'en'))
}
}
else {
if (data.defaultLanguage === 'en-US')
fileList.push(...await readFiles(getTemplate('docs/en')))
else
fileList.push(...await readFiles(getTemplate('docs/zh')))
}
return updateFileListTarget(fileList, data.docsDir)
}
/**
* Update file list target path
*
*
*
* @param fileList - Array of files /
* @param target - Target directory path /
* @returns Updated file array /
*/
function updateFileListTarget(fileList: File[], target: string): File[] {
return fileList.map(({ filepath, content }) => ({
filepath: path.join(target, filepath),
content,
}))
}

View File

@ -1,32 +0,0 @@
/**
* VuePress Theme Plume CLI Entry Point
*
* VuePress Theme Plume CLI
*
* This module provides command-line interface for creating and initializing
* VuePress projects with vuepress-theme-plume.
*
* VuePress
*
* @module cli
*/
import cac from 'cac'
import { version } from '../package.json'
import { Mode } from './constants.js'
import { run } from './run.js'
const cli = cac('create-vuepress-theme-plume')
cli
.command('[root]', 'create a new vuepress-theme-plume project / 创建新的 vuepress-theme-plume 项目')
.action((root: string) => run(Mode.create, root))
cli
.command('init [root]', 'Initial vuepress-theme-plume in the existing project / 在现有项目中初始化 vuepress-theme-plume')
.action((root: string) => run(Mode.init, root))
cli.help()
cli.version(version)
cli.parse()

View File

@ -1,30 +0,0 @@
import type { Locale } from '../types.js'
/**
* English locale configuration for CLI prompts and messages.
*
* CLI
*/
export const en: Locale = {
'question.root': 'Where would you want to initialize VuePress?',
'question.site.name': 'Site Name:',
'question.site.description': 'Site Description:',
'question.bundler': 'Select a bundler',
'question.multiLanguage': 'Do you want to use multiple languages?',
'question.defaultLanguage': 'Select the default language of the site',
'question.useTs': 'Use TypeScript?',
'question.injectNpmScripts': 'Inject npm scripts?',
'question.deploy': 'Deploy type:',
'question.git': 'Initialize a git repository?',
'question.installDeps': 'Install dependencies?',
'spinner.start': '🚀 Creating...',
'spinner.stop': '🎉 Create success!',
'spinner.git': '📄 Initializing git repository...',
'spinner.install': '📦 Installing dependencies...',
'spinner.command': '🔨 Execute the following command to start:',
'hint.cancel': 'Operation cancelled.',
'hint.root': 'The path cannot be an absolute path, and cannot contain the parent path.',
'hint.root.illegal': 'Project names cannot contain special characters.',
}

View File

@ -1,17 +0,0 @@
import type { Langs, Locale } from '../types.js'
import { en } from './en.js'
import { zh } from './zh.js'
/**
* Locale configurations for different languages.
*
*
*
* Maps language codes to their respective locale strings.
*
*
*/
export const locales: Record<Langs, Locale> = {
'zh-CN': zh,
'en-US': en,
}

View File

@ -1,30 +0,0 @@
import type { Locale } from '../types.js'
/**
* Chinese (Simplified) locale configuration for CLI prompts and messages.
*
* CLI
*/
export const zh: Locale = {
'question.root': '您想在哪里初始化 VuePress',
'question.site.name': '站点名称:',
'question.site.description': '站点描述信息:',
'question.bundler': '请选择打包工具',
'question.multiLanguage': '是否使用多语言?',
'question.defaultLanguage': '请选择站点默认语言',
'question.useTs': '是否使用 TypeScript',
'question.injectNpmScripts': '是否注入 npm 脚本?',
'question.deploy': '部署方式:',
'question.git': '是否初始化 git 仓库?',
'question.installDeps': '是否安装依赖?',
'spinner.start': '🚀 正在创建...',
'spinner.stop': '🎉 创建成功!',
'spinner.git': '📄 初始化 git 仓库...',
'spinner.install': '📦 安装依赖...',
'spinner.command': '🔨 执行以下命令即可启动:',
'hint.cancel': '操作已取消。',
'hint.root': '文件路径不能是绝对路径,不能包含父路径。',
'hint.root.illegal': '文件夹不能包含特殊字符。',
}

View File

@ -1,148 +0,0 @@
import type { File, ResolvedData } from './types.js'
import { attemptAsync, kebabCase } from '@pengzhanbo/utils'
import spawn from 'nano-spawn'
import _sortPackageJson from 'sort-package-json'
import { Mode } from './constants.js'
import { readJsonFile, resolve } from './utils/index.js'
/**
* Sort package.json fields in a consistent order.
*
* package.json
*
* @param json - Package.json object to sort / package.json
* @returns Sorted package.json object / package.json
*/
function sortPackageJson(json: Record<any, any>) {
return _sortPackageJson(json, {
sortOrder: ['name', 'type', 'version', 'private', 'description', 'packageManager', 'author', 'license', 'scripts', 'devDependencies', 'dependencies', 'pnpm'],
})
}
/**
* Create package.json file for VuePress project
*
* VuePress package.json
*
* @param mode - Operation mode (init or create) /
* @param pkg - Existing package.json data / package.json
* @param data - Resolved configuration data /
* @param data.packageManager - Package manager to use / 使
* @param data.siteName - Site name /
* @param data.siteDescription - Site description /
* @param data.docsDir - Documentation directory path /
* @param data.bundler - Bundler to use / 使
* @param data.injectNpmScripts - Whether to inject npm scripts / npm
*
* @returns File object with package.json content / package.json
*/
export async function createPackageJson(
mode: Mode,
pkg: Record<string, any>,
{
packageManager,
docsDir,
siteName,
siteDescription,
bundler,
injectNpmScripts,
}: ResolvedData,
): Promise<File> {
if (mode === Mode.create) {
pkg.name = kebabCase(siteName)
pkg.type = 'module'
pkg.version = '1.0.0'
pkg.description = siteDescription
if (packageManager !== 'npm') {
let [, version] = await attemptAsync(getPackageManagerVersion, packageManager)
if (version) {
if (packageManager === 'yarn' && version.startsWith('1')) {
version = '4.10.3'
}
pkg.packageManager = `${packageManager}@${version}`
// pnpm@10 should add `onlyBuiltDependencies`
if (packageManager === 'pnpm' && version.startsWith('10')) {
pkg.pnpm = {
onlyBuiltDependencies: ['@parcel/watcher', 'esbuild'],
}
}
}
}
const [, userInfo] = await attemptAsync(getUserInfo)
if (userInfo) {
pkg.author = userInfo.username + (userInfo.email ? ` <${userInfo.email}>` : '')
}
pkg.license = 'MIT'
pkg.engines = { node: '^20.19.0 || >=22.0.0' }
}
if (injectNpmScripts) {
pkg.scripts ??= {}
pkg.scripts = {
...pkg.scripts,
'docs:dev': `vuepress dev ${docsDir}`,
'docs:dev-clean': `vuepress dev ${docsDir} --clean-cache --clean-temp`,
'docs:build': `vuepress build ${docsDir} --clean-cache --clean-temp`,
'docs:preview': `http-server ${docsDir}/.vuepress/dist`,
}
if (mode === Mode.create) {
pkg.scripts['vp-update'] = `${packageManager === 'npm' ? 'npx' : `${packageManager} dlx`} vp-update`
}
}
pkg.devDependencies ??= {}
const hasDep = (dep: string) => pkg.devDependencies?.[dep] || pkg.dependencies?.[dep]
const context = (await readJsonFile(resolve('package.json')))!
const meta = context['plume-deps']
pkg.devDependencies[`@vuepress/bundler-${bundler}`] = `${meta.vuepress}`
pkg.devDependencies.vuepress = `${meta.vuepress}`
pkg.devDependencies['vuepress-theme-plume'] = `${context.version}`
const deps: string[] = ['http-server']
if (!hasDep('vue'))
deps.push('vue')
deps.push('typescript')
for (const dep of deps)
pkg.devDependencies[dep] = meta[dep]
return {
filepath: 'package.json',
content: JSON.stringify(sortPackageJson(pkg), null, 2),
}
}
/**
* Get user information from git global configuration.
*
* git
*
* @returns User information object with username and email /
* @throws Error if git command fails / git
*/
async function getUserInfo() {
const { output: username } = await spawn('git', ['config', '--global', 'user.name'])
const { output: email } = await spawn('git', ['config', '--global', 'user.email'])
return { username, email }
}
/**
* Get the version of a package manager.
*
*
*
* @param pkg - Package manager name (npm, yarn, pnpm) /
* @returns Version string of the package manager /
* @throws Error if package manager command fails /
*/
async function getPackageManagerVersion(pkg: string) {
const { output } = await spawn(pkg, ['--version'])
return output
}

View File

@ -1,155 +0,0 @@
import type { Bundler, Langs, PromptResult } from './types.js'
import { createRequire } from 'node:module'
import process from 'node:process'
import { cancel, confirm, group, select, text } from '@clack/prompts'
import osLocale from 'os-locale'
import { bundlerOptions, deployOptions, DeployType, languageOptions, Mode } from './constants.js'
import { setLang, t } from './translate.js'
const require = createRequire(process.cwd())
const REG_DIR_CHAR = /[<>:"\\|?*[\]]/
/**
* Prompt user for project configuration
*
*
*
* @param mode - Operation mode (init or create) /
* @param root - Optional root directory path /
* @returns Resolved prompt result /
*/
export async function prompt(mode: Mode, root?: string): Promise<PromptResult> {
let hasTs = false
if (mode === Mode.init) {
try {
hasTs = !!require.resolve('typescript')
}
catch {}
}
const result: PromptResult = await group({
displayLang: async () => {
const locale = osLocale()
if (locale === 'zh-CN' || locale === 'zh-Hans') {
setLang('zh-CN')
return 'zh-CN'
}
if (locale === 'en-US') {
setLang('en-US')
return 'en-US'
}
const lang = await select<Langs>({
message: 'Select a language to display / 选择显示语言',
options: languageOptions,
})
if (typeof lang === 'string')
setLang(lang)
return lang
},
root: async () => {
if (root)
return root
const DEFAULT_ROOT = mode === Mode.init ? './docs' : './my-project'
return await text({
message: t('question.root'),
placeholder: DEFAULT_ROOT,
validate(value) {
// not absolute path or parent path
if (value?.startsWith('/') || value?.startsWith('..'))
return t('hint.root')
// not contains illegal characters
if (value && REG_DIR_CHAR.test(value))
return t('hint.root.illegal')
return undefined
},
defaultValue: DEFAULT_ROOT,
})
},
siteName: () => text({
message: t('question.site.name'),
placeholder: 'My Vuepress Site',
defaultValue: 'My Vuepress Site',
}),
siteDescription: () => text({
message: t('question.site.description'),
}),
multiLanguage: () => confirm({
message: t('question.multiLanguage'),
initialValue: false,
}),
defaultLanguage: () => select<Langs>({
message: t('question.defaultLanguage'),
options: languageOptions,
}),
useTs: async () => {
if (mode === Mode.init)
return hasTs
if (hasTs)
return true
return await confirm({
message: t('question.useTs'),
initialValue: true,
})
},
injectNpmScripts: async () => {
if (mode === Mode.create)
return true
return await confirm({
message: t('question.injectNpmScripts'),
initialValue: true,
})
},
bundler: () => select<Bundler>({
message: t('question.bundler'),
options: bundlerOptions,
}),
deploy: async () => {
if (mode === Mode.init) {
return DeployType.custom
}
return await select<DeployType>({
message: t('question.deploy'),
options: deployOptions,
initialValue: DeployType.custom,
})
},
git: async () => {
if (mode === Mode.init)
return false
return confirm({
message: t('question.git'),
initialValue: true,
})
},
install: () => confirm({
message: t('question.installDeps'),
initialValue: true,
}),
}, {
onCancel: () => {
cancel(t('hint.cancel'))
process.exit(0)
},
})
return result
}

View File

@ -1,57 +0,0 @@
import type { ResolvedData } from './types.js'
import { kebabCase } from '@pengzhanbo/utils'
import handlebars from 'handlebars'
/**
* Extended resolved data with additional rendering information
*
*
*/
export interface RenderData extends ResolvedData {
/** Project name in kebab-case / 项目名称kebab-case 格式) */
name: string
/** Site name / 网站名称 */
siteName: string
/** Locale configuration array / 语言配置数组 */
locales: { path: string, lang: string, isEn: boolean, prefix: string }[]
/** Whether default language is English / 默认语言是否为英语 */
isEN: boolean
}
handlebars.registerHelper('removeLeadingSlash', (path: string) => path.replace(/^\//, ''))
handlebars.registerHelper('equal', (a: string, b: string) => a === b)
/**
* Create render function with Handlebars template engine
*
* 使 Handlebars
*
* @param result - Resolved configuration data /
* @returns Render function that processes Handlebars templates / Handlebars
*/
export function createRender(result: ResolvedData) {
const data: RenderData = {
...result,
name: kebabCase(result.siteName),
isEN: result.defaultLanguage === 'en-US',
locales: result.defaultLanguage === 'en-US'
? [
{ path: '/', lang: 'en-US', isEn: true, prefix: 'en' },
{ path: '/zh/', lang: 'zh-CN', isEn: false, prefix: 'zh' },
]
: [
{ path: '/', lang: 'zh-CN', isEn: false, prefix: 'zh' },
{ path: '/en/', lang: 'en-US', isEn: true, prefix: 'en' },
],
}
return function render(source: string): string {
try {
const template = handlebars.compile(source)
return template(data)
}
catch (e) {
console.error(e)
return source
}
}
}

View File

@ -1,97 +0,0 @@
import type { PromptResult, ResolvedData } from './types.js'
import path from 'node:path'
import process from 'node:process'
import { intro, outro, spinner } from '@clack/prompts'
import { sleep } from '@pengzhanbo/utils'
import spawn from 'nano-spawn'
import colors from 'picocolors'
import { Mode } from './constants.js'
import { generate } from './generate.js'
import { prompt } from './prompt.js'
import { t } from './translate.js'
import { getPackageManager } from './utils/index.js'
/**
* Run the CLI workflow for VuePress project initialization or creation
*
* VuePress CLI
*
* @param mode - Operation mode (init or create) /
* @param root - Root directory path /
*/
export async function run(mode: Mode, root?: string): Promise<void> {
intro(colors.cyan('Welcome to VuePress and vuepress-theme-plume !'))
const result = await prompt(mode, root)
const data = resolveData(result, mode)
const progress = spinner()
progress.start(t('spinner.start'))
try {
await generate(mode, data)
}
catch (e) {
console.error(`${colors.red('generate files error: ')}\n`, e)
process.exit(1)
}
// Delay for some time, I/O may not be completed yet,
// executing subsequent tasks at this point may cause issues.
await sleep(200)
const cwd = path.join(process.cwd(), data.root)
if (data.git) {
progress.message(t('spinner.git'))
try {
await spawn('git', ['init'], { cwd })
}
catch (e) {
console.error(`${colors.red('git init error: ')}\n`, e)
process.exit(1)
}
}
const pm = data.packageManager
if (data.install) {
progress.message(t('spinner.install'))
try {
await spawn(pm, ['install'], { cwd })
}
catch (e) {
console.error(`${colors.red('install dependencies error: ')}\n`, e)
process.exit(1)
}
}
const cdCommand = mode === Mode.create ? colors.green(`cd ${data.root}`) : ''
const runCommand = colors.green(`${pm} run docs:dev`)
const installCommand = colors.green(`${pm} install`)
progress.stop(t('spinner.stop'))
if (mode === Mode.create) {
outro(`${t('spinner.command')}
${cdCommand}
${data.install ? '' : `${installCommand} && `}${runCommand}`)
}
}
/**
* Resolve prompt result into final configuration data.
*
*
*
* @param result - Prompt result from user input /
* @param mode - Operation mode (init or create) /
* @returns Resolved configuration data /
*/
function resolveData(result: PromptResult, mode: Mode): ResolvedData {
return {
...result,
packageManager: getPackageManager(),
docsDir: mode === Mode.create ? 'docs' : result.root.replace(/^\.\//, '').replace(/\/$/, ''),
siteDescription: result.siteDescription || '',
}
}

View File

@ -1,47 +0,0 @@
import type { Langs, Locale } from './types.js'
import { locales } from './locales/index.js'
interface Translate {
setLang: (lang: Langs) => void
t: (key: keyof Locale) => string
}
/**
* Create a translate instance with specified language
*
*
*
* @param lang - Language code /
* @returns Translate interface /
*/
function createTranslate(lang?: Langs): Translate {
let current: Langs = lang || 'en-US'
return {
setLang: (lang) => {
current = lang
},
t: key => locales[current][key],
}
}
const translate = createTranslate()
/**
* Get translated string by key
*
*
*
* @param key - Locale key /
* @returns Translated string /
*/
export const t: Translate['t'] = translate.t
/**
* Set current language
*
*
*
* @param lang - Language code to set /
*/
export const setLang: Translate['setLang'] = translate.setLang

View File

@ -1,276 +0,0 @@
import type { DeployType } from './constants.js'
/**
* Supported language codes for VuePress site
*
* VuePress
*/
export type Langs = 'zh-CN' | 'en-US'
/**
* Locale configuration for CLI prompts and messages
*
* CLI
*/
export interface Locale {
/**
* Question: Project root directory name
*
*
*/
'question.root': string
/**
* Question: Site name
*
*
*/
'question.site.name': string
/**
* Question: Site description
*
*
*/
'question.site.description': string
/**
* Question: Enable multi-language support
*
*
*/
'question.multiLanguage': string
/**
* Question: Default language
*
*
*/
'question.defaultLanguage': string
/**
* Question: Build tool bundler
*
*
*/
'question.bundler': string
/**
* Question: Use TypeScript
*
* 使 TypeScript
*/
'question.useTs': string
/**
* Question: Inject npm scripts
*
* npm
*/
'question.injectNpmScripts': string
/**
* Question: Initialize git repository
*
* git
*/
'question.git': string
/**
* Question: Deployment type
*
*
*/
'question.deploy': string
/**
* Question: Install dependencies
*
*
*/
'question.installDeps': string
/**
* Spinner: Start message
*
*
*/
'spinner.start': string
/**
* Spinner: Stop message
*
*
*/
'spinner.stop': string
/**
* Spinner: Git init message
*
* Git
*/
'spinner.git': string
/**
* Spinner: Install message
*
*
*/
'spinner.install': string
/**
* Spinner: Command hint message
*
*
*/
'spinner.command': string
/**
* Hint: Cancel operation
*
*
*/
'hint.cancel': string
/**
* Hint: Root directory
*
*
*/
'hint.root': string
/**
* Hint: Illegal root directory name
*
*
*/
'hint.root.illegal': string
}
/**
* Package manager types
*
*
*/
export type PackageManager = 'npm' | 'yarn' | 'pnpm'
/**
* Build tool bundler types
*
*
*/
export type Bundler = 'vite' | 'webpack'
/**
* Generic options type for CLI prompts
*
* CLI
*
* @template Value - The value type for options
* @template Label - The label type for options
*/
export type Options<Value = string, Label = string> = { label: Label, value: Value }[]
/**
* File structure for generated project
*
*
*/
export interface File {
/**
* File path relative to project root
*
*
*/
filepath: string
/**
* File content
*
*
*/
content: string
}
/**
* Result from CLI prompts
*
* CLI
*/
export interface PromptResult {
/**
* CLI display language
*
* CLI
*/
displayLang: string
/**
* Project root directory name
*
*
*/
root: string
/**
* Site name
*
*
*/
siteName: string
/**
* Site description
*
*
*/
siteDescription: string
/**
* Build tool bundler
*
*
*/
bundler: Bundler
/**
* Enable multi-language support
*
*
*/
multiLanguage: boolean
/**
* Default language
*
*
*/
defaultLanguage: Langs
/**
* Use TypeScript
*
* 使 TypeScript
*/
useTs: boolean
/**
* Inject npm scripts
*
* npm
*/
injectNpmScripts: boolean
/**
* Deployment type
*
*
*/
deploy: DeployType
/**
* Initialize git repository
*
* git
*/
git: boolean
/**
* Install dependencies
*
*
*/
install: boolean
}
/**
* Resolved data after processing prompts
*
*
*/
export interface ResolvedData extends PromptResult {
/**
* Selected package manager
*
*
*/
packageManager: PackageManager
/**
* Documentation directory name
*
*
*/
docsDir: string
}

View File

@ -1,68 +0,0 @@
import type { File } from '../types.js'
import fs from 'node:fs/promises'
import path from 'node:path'
/**
* Read all files from a directory recursively
*
*
*
* @param root - Root directory path to read from /
* @returns Array of file objects /
*/
export async function readFiles(root: string): Promise<File[]> {
const filepaths = await fs.readdir(root, { recursive: true })
const files: File[] = []
for (const file of filepaths) {
const filepath = path.join(root, file)
if ((await fs.stat(filepath)).isFile()) {
files.push({
filepath: file,
content: await fs.readFile(filepath, 'utf-8'),
})
}
}
return files
}
/**
* Write files to target directory
*
*
*
* @param files - Array of file objects to write /
* @param target - Target directory path /
* @param rewrite - Optional function to rewrite file paths /
*/
export async function writeFiles(
files: File[],
target: string,
rewrite?: (path: string) => string,
): Promise<void> {
for (const { filepath, content } of files) {
let root = path.join(target, filepath).replace(/\.handlebars$/, '')
if (rewrite)
root = rewrite(root)
await fs.mkdir(path.dirname(root), { recursive: true })
await fs.writeFile(root, content)
}
}
/**
* Read and parse JSON file
*
* JSON
*
* @param filepath - Path to JSON file / JSON
* @returns Parsed JSON object or null if parsing fails / JSON null
*/
export async function readJsonFile<T extends Record<string, any> = Record<string, any>>(filepath: string): Promise<T | null> {
try {
const content = await fs.readFile(filepath, 'utf-8')
return JSON.parse(content)
}
catch {
return null
}
}

View File

@ -1,20 +0,0 @@
import type { PackageManager } from '../types.js'
import process from 'node:process'
/**
* Detect the current package manager from environment variables.
*
* 使
*
* @returns The detected package manager name /
* @example
* // When using pnpm
* const pm = getPackageManager() // returns 'pnpm'
*
* // When using npm
* const pm = getPackageManager() // returns 'npm'
*/
export function getPackageManager(): PackageManager {
const name = process.env?.npm_config_user_agent || 'npm'
return name.split('/')[0] as PackageManager
}

View File

@ -1,27 +0,0 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
export const __dirname: string = path.dirname(fileURLToPath(import.meta.url))
/**
* Resolve path relative to the project root
*
*
*
* @param args - Path segments to resolve /
* @returns Resolved absolute path /
*/
export const resolve = (...args: string[]): string => path.resolve(__dirname, '../', ...args)
/**
* Get template directory path
*
*
*
* @param dir - Subdirectory name within templates / templates
* @returns Resolved template directory path /
*/
export const getTemplate = (dir: string): string => resolve('templates', dir)
export * from './fs.js'
export * from './getPackageManager.js'

View File

@ -1,22 +0,0 @@
import { defineClientConfig } from 'vuepress/client'
// import RepoCard from 'vuepress-theme-plume/features/RepoCard.vue'
// import NpmBadge from 'vuepress-theme-plume/features/NpmBadge.vue'
// import NpmBadgeGroup from 'vuepress-theme-plume/features/NpmBadgeGroup.vue'
// import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
// import CustomComponent from './theme/components/Custom.vue'
// import './theme/styles/custom.css'
export default defineClientConfig({
enhance({ app }) {
// built-in components
// app.component('RepoCard', RepoCard)
// app.component('NpmBadge', NpmBadge)
// app.component('NpmBadgeGroup', NpmBadgeGroup)
// app.component('Swiper', Swiper) // you should install `swiper`
// your custom components
// app.component('CustomComponent', CustomComponent)
},
})

View File

@ -1,124 +0,0 @@
/**
* @see https://theme-plume.vuejs.press/guide/collection/ 查看文档了解配置详情。
*
* Collections 配置文件,它在 `.vuepress/plume.config.{{#if useTs}}ts{{else}}js{{/if}}` 中被导入。
*
* 请注意,你应该先在这里配置好 Collections然后再启动 vuepress主题会在启动 vuepress 时,
* 读取这里配置的 Collections然后在与 Collection 相关的 Markdown 文件中,自动生成 permalink。
*
* collection 的 type 为 `post` 时,表示为 文档列表类型(即没有侧边导航栏,有文档列表页)
* 可用于实现如 博客、专栏 等以文章列表聚合形式的文档集合 (内容相对碎片化的)
*
* collection 的 type 为 `doc` 时,表示为文档类型(即有侧边导航栏)
* 可用于实现如 笔记、知识库、文档等以侧边导航栏形式的文档集合 (内容强关联、成体系的)
* 如果发现 侧边栏没有显示,那么请检查你的配置是否正确,以及 Markdown 文件中的 permalink
* 是否是以对应的 Collection 配置的 link 的前缀开头。 是否展示侧边栏是根据 页面链接 的前缀 与 `collection.link`
* 的前缀是否匹配来决定。
*/
/**
* 在受支持的 IDE 中会智能提示配置项。
*
* - `defineCollections` 是用于定义 collection 集合的帮助函数
* - `defineCollection` 是用于定义单个 collection 配置的帮助函数
*
* 通过 `defineCollection` 定义的 collection 配置,应该填入 `defineCollections` 中
*/
import { defineCollection, defineCollections } from 'vuepress-theme-plume'
{{#if multiLanguage}}
{{#each locales}}
/* =================== locale: {{ lang }} ======================= */
const {{ prefix }}Blog = defineCollection({
// post 类型,这里用于实现 博客功能
type: 'post',
// 文档集合所在目录,相对于 `docs{{ path }}`
dir: 'blog',
// 文档标题,它将用于在页面的面包屑导航中显示
title: 'Blog',
// 文章列表页的链接,如果 `linkPrefix` 未定义,它也将作为 相关的文章的 permalink 的前缀
link: '/blog/',
// linkPrefix: '/article/', // 相关文章的链接前缀
// postList: true, // 是否启用文章列表页
// tags: true, // 是否启用标签页
// archives: true, // 是否启用归档页
// categories: true, // 是否启用分类页
// postCover: 'right', // 文章封面位置
// pagination: 15, // 每页显示文章数量
})
const {{ prefix }}DemoDoc = defineCollection({
// doc 类型,该类型带有侧边栏
type: 'doc',
// 文档集合所在目录,相对于 `docs{{ path }}`
dir: 'demo',
// `dir` 所指向的目录中的所有 markdown 文件,其 permalink 需要以 `linkPrefix` 配置作为前缀
// 如果 前缀不一致,则无法生成侧边栏。
// 所以请确保 markdown 文件的 permalink 都以 `{{ path }}` + `linkPrefix` 开头
linkPrefix: '/demo',
// 文档标题,它将用于在页面的面包屑导航中显示
title: 'Demo',
// 手动配置侧边栏结构
sidebar: ['', 'foo', 'bar'],
// 根据文件结构自动生成侧边栏
// sidebar: 'auto',
})
/**
* 导出所有的 collections
* ({{ prefix }}Blog 为博客示例,如果不需要博客功能,请删除)
* ({{ prefix }}DemoDoc 为参考示例,如果不需要它,请删除)
*/
export const {{ prefix }}Collections = defineCollections([
{{ prefix }}Blog,
{{ prefix }}DemoDoc,
])
{{/each}}
{{else}}
const blog = defineCollection({
// post 类型,这里用于实现 博客功能
type: 'post',
// 文档集合所在目录,相对于 `docs{{ path }}`
dir: 'blog',
// 文档标题,它将用于在页面的面包屑导航中显示
title: 'Blog',
// 文章列表页的链接,如果 `linkPrefix` 未定义,它也将作为 相关的文章的 permalink 的前缀
link: '/blog/',
// linkPrefix: '/article/', // 相关文章的链接前缀
// postList: true, // 是否启用文章列表页
// tags: true, // 是否启用标签页
// archives: true, // 是否启用归档页
// categories: true, // 是否启用分类页
// postCover: 'right', // 文章封面位置
// pagination: 15, // 每页显示文章数量
})
const demoDoc = defineCollection({
// doc 类型,该类型带有侧边栏
type: 'doc',
// 文档集合所在目录,相对于 `docs{{ path }}`
dir: 'demo',
// `dir` 所指向的目录中的所有 markdown 文件,其 permalink 需要以 `linkPrefix` 配置作为前缀
// 如果 前缀不一致,则无法生成侧边栏。
// 所以请确保 markdown 文件的 permalink 都以 `linkPrefix` 开头
linkPrefix: '/demo',
// 文档标题,它将用于在页面的面包屑导航中显示
title: 'Demo',
// 手动配置侧边栏结构
sidebar: ['', 'foo', 'bar'],
// 根据文件结构自动生成侧边栏
// sidebar: 'auto',
})
/**
* 导出所有的 collections
* (blog 为博客示例,如果不需要博客功能,请删除)
* (demoDoc 为参考示例,如果不需要它,请删除)
*/
export default defineCollections([
blog,
demoDoc,
])
{{/if}}

View File

@ -1,194 +0,0 @@
/**
* 查看以下文档了解主题配置
* - @see https://theme-plume.vuejs.press/config/intro/ 配置说明
* - @see https://theme-plume.vuejs.press/config/theme/ 主题配置项
*
* 请注意,对此文件的修改都会重启 vuepress 服务。
* 部分配置项的更新没有必要重启 vuepress 服务,建议请在 `.vuepress/config.{{#if useTs}}ts{{else}}js{{/if}}` 文件中配置
*
* 特别的,请不要在两个配置文件中重复配置相同的项,当前文件的配置项会被覆盖
*/
import { {{ bundler }}Bundler } from '@vuepress/bundler-{{ bundler }}'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
base: '/',
lang: '{{ defaultLanguage }}',
{{#if multiLanguage}}
locales: {
{{#each locales}}
'{{ path }}': {
title: '{{ ../siteName }}',
lang: '{{ lang }}',
description: '{{ ../siteDescription }}',
},
{{/each}}
},
{{else}}
title: '{{ siteName }}',
description: '{{ siteDescription }}',
{{/if}}
head: [
// 配置站点图标
['link', { rel: 'icon', type: 'image/png', href: 'https://theme-plume.vuejs.press/favicon-32x32.png' }],
],
bundler: {{ bundler }}Bundler(),
shouldPrefetch: false, // 站点较大,页面数量较多时,不建议启用
theme: plumeTheme({
/* 添加您的部署域名, 有助于 SEO, 生成 sitemap */
// hostname: 'https://your_site_url',
/* 文档仓库配置,用于 editLink */
// docsRepo: '',
// docsDir: '{{ docsDir }}',
// docsBranch: '',
/* 页内信息 */
// editLink: true,
// lastUpdated: true,
// contributors: true,
// changelog: false,
/**
* 编译缓存,加快编译速度
* @see https://theme-plume.vuejs.press/config/theme/#cache
*/
cache: 'filesystem',
/**
* 为 markdown 文件自动添加 frontmatter 配置
* @see https://theme-plume.vuejs.press/config/theme/#autofrontmatter
*/
// autoFrontmatter: {
// permalink: true, // 是否生成永久链接
// createTime: true, // 是否生成创建时间
// title: true, // 是否生成标题
// },
/* 本地搜索, 默认启用 */
search: { provider: 'local' },
/**
* Algolia DocSearch
* 启用此搜索需要将 本地搜索 search 设置为 false
* @see https://theme-plume.vuejs.press/config/plugins/search/#algolia-docsearch
*/
// search: {
// provider: 'algolia',
// appId: '',
// apiKey: '',
// indices: [''],
// },
/**
* Shiki 代码高亮
* @see https://theme-plume.vuejs.press/config/plugins/code-highlight/
*/
// codeHighlighter: {
// twoslash: true, // 启用 twoslash
// whitespace: true, // 启用 空格/Tab 高亮
// lineNumbers: true, // 启用行号
// },
/* 文章字数统计、阅读时间,设置为 false 则禁用 */
// readingTime: true,
/**
* markdown
* @see https://theme-plume.vuejs.press/config/markdown/
*/
// markdown: {
// abbr: true, // 启用 abbr 语法 *[label]: content
// annotation: true, // 启用 annotation 语法 [+label]: content
// pdf: true, // 启用 PDF 嵌入 @[pdf](/xxx.pdf)
// caniuse: true, // 启用 caniuse 语法 @[caniuse](feature_name)
// plot: true, // 启用隐秘文本语法 !!xxxx!!
// bilibili: true, // 启用嵌入 bilibili视频 语法 @[bilibili](bid)
// youtube: true, // 启用嵌入 youtube视频 语法 @[youtube](video_id)
// artPlayer: true, // 启用嵌入 artPlayer 本地视频 语法 @[artPlayer](url)
// audioReader: true, // 启用嵌入音频朗读功能 语法 @[audioReader](url)
// icon: { provider: 'iconify' }, // 启用内置图标语法 ::icon-name::
// table: true, // 启用表格增强容器语法 ::: table
// codepen: true, // 启用嵌入 codepen 语法 @[codepen](user/slash)
// replit: true, // 启用嵌入 replit 语法 @[replit](user/repl-name)
// codeSandbox: true, // 启用嵌入 codeSandbox 语法 @[codeSandbox](id)
// jsfiddle: true, // 启用嵌入 jsfiddle 语法 @[jsfiddle](user/id)
// npmTo: true, // 启用 npm-to 容器 ::: npm-to
// demo: true, // 启用 demo 容器 ::: demo
// collapse: true, // 启用折叠容器 ::: collapse
// repl: { // 启用 代码演示容器
// go: true, // ::: go-repl
// rust: true, // ::: rust-repl
// kotlin: true, // ::: kotlin-repl
// python: true, // ::: python-repl
// },
// math: { // 启用数学公式
// type: 'katex',
// },
// chartjs: true, // 启用 chart.js
// echarts: true, // 启用 ECharts
// mermaid: true, // 启用 mermaid
// flowchart: true, // 启用 flowchart
// image: {
// figure: true, // 启用 figure
// lazyload: true, // 启用图片懒加载
// mark: true, // 启用图片标记
// size: true, // 启用图片大小
// },
// include: true, // 在 Markdown 文件中导入其他 markdown 文件内容
// imageSize: 'local', // 启用 自动填充 图片宽高属性,避免页面抖动
// },
/**
* 水印
* @see https://theme-plume.vuejs.press/guide/features/watermark/
*/
// watermark: true,
/**
* 评论 comments
* @see https://theme-plume.vuejs.press/guide/features/comments/
*/
// comment: {
// provider: '', // "Artalk" | "Giscus" | "Twikoo" | "Waline"
// comment: true,
// repo: '',
// repoId: '',
// category: '',
// categoryId: '',
// mapping: 'pathname',
// reactionsEnabled: true,
// inputPosition: 'top',
// },
/**
* 资源链接替换
* @see https://theme-plume.vuejs.press/guide/features/replace-assets/
*/
// replaceAssets: 'https://cdn.example.com',
/**
* 加密功能
* @see https://theme-plume.vuejs.press/guide/features/encryption/
*/
// encrypt: {},
/**
* 启用 llmstxt 插件,用于为大语言模型提供更友好的内容
* @see https://theme-plume.vuejs.press/guide/features/llmstxt/
*/
// llmstxt: {
{{#if multiLanguage}}
// locale: '/', // 默认仅为主语言生成 llms 友好内容
// locale: 'all', // 为所有语言生成 llms 友好内容
{{else}}
// locale: '/', // 默认仅为主语言生成 llms 友好内容
{{/if}}
// }
}),
})

View File

@ -1,34 +0,0 @@
/**
* @see https://theme-plume.vuejs.press/config/navigation/ 查看文档了解配置详情
*
* Navbar 配置文件,它在 `.vuepress/plume.config.{{#if useTs}}ts{{else}}js{{/if}}` 中被导入。
*/
import { defineNavbarConfig } from 'vuepress-theme-plume'
{{#if multiLanguage}}
{{#each locales}}
export const {{prefix}}Navbar = defineNavbarConfig([
{ text: '{{#if isEn}}Home{{else}}首页{{/if}}', link: '{{ path }}' },
{ text: '{{#if isEn}}Blog{{else}}博客{{/if}}', link: '{{ path }}blog/' },
{ text: '{{#if isEn}}Tags{{else}}标签{{/if}}', link: '{{ path }}blog/tags/' },
{ text: '{{#if isEn}}Archives{{else}}归档{{/if}}', link: '{{ path }}blog/archives/' },
{
text: '{{#if isEn}}Notes{{else}}笔记{{/if}}',
items: [{ text: '{{#if isEn}}Demo{{else}}示例{{/if}}', link: '{{ path }}demo/README.md' }]
},
])
{{/each}}
{{else}}
export default defineNavbarConfig([
{ text: '{{#if isEn}}Home{{else}}首页{{/if}}', link: '/' },
{ text: '{{#if isEn}}Blog{{else}}博客{{/if}}', link: '/blog/' },
{ text: '{{#if isEn}}Tags{{else}}标签{{/if}}', link: '/blog/tags/' },
{ text: '{{#if isEn}}Archives{{else}}归档{{/if}}', link: '/blog/archives/' },
{
text: '{{#if isEn}}Notes{{else}}笔记{{/if}}',
items: [{ text: '{{#if isEn}}Demo{{else}}示例{{/if}}', link: '/demo/README.md' }]
},
])
{{/if}}

View File

@ -1,121 +0,0 @@
/**
* 查看以下文档了解主题配置
* - @see https://theme-plume.vuejs.press/config/intro/ 配置说明
* - @see https://theme-plume.vuejs.press/config/theme/ 主题配置项
*
* 请注意,对此文件的修改不会重启 vuepress 服务,而是通过热更新的方式生效
* 但同时部分配置项不支持热更新,请查看文档说明
* 对于不支持热更新的配置项,请在 `.vuepress/config.{{#if useTs}}ts{{else}}js{{/if}}` 文件中配置
*
* 特别的,请不要在两个配置文件中重复配置相同的项,当前文件的配置项会覆盖 `.vuepress/config.{{#if useTs}}ts{{else}}js{{/if}}` 文件中的配置
*/
import { defineThemeConfig } from 'vuepress-theme-plume'
{{#if multiLanguage}}
import { enCollections, zhCollections } from './collections'
import { enNavbar, zhNavbar } from './navbar'
{{else}}
import navbar from './navbar'
import collections from './collections'
{{/if}}
/**
* @see https://theme-plume.vuejs.press/config/theme/
*/
export default defineThemeConfig({
logo: 'https://theme-plume.vuejs.press/plume.png',
appearance: true, // 配置 深色模式
social: [
{ icon: 'github', link: '/' },
],
// navbarSocialInclude: ['github'], // 允许显示在导航栏的 social 社交链接
// aside: true, // 页内侧边栏, 默认显示在右侧
// outline: [2, 3], // 页内大纲, 默认显示 h2, h3
/**
* 文章版权信息
* @see https://theme-plume.vuejs.press/guide/features/copyright/
*/
// copyright: true,
// prevPage: true, // 是否启用上一页链接
// nextPage: true, // 是否启用下一页链接
// createTime: true, // 是否显示文章创建时间
/* 站点页脚 */
// footer: {
// message: 'Power by <a target="_blank" href="https://v2.vuepress.vuejs.org/">VuePress</a> & <a target="_blank" href="https://theme-plume.vuejs.press">vuepress-theme-plume</a>',
// copyright: '',
// },
{{#unless multiLanguage}}
/**
* @see https://theme-plume.vuejs.press/config/theme/#profile
*/
profile: {
avatar: 'https://theme-plume.vuejs.press/plume.png',
name: '{{ siteName }}',
description: '{{ siteDescription }}',
// circle: true,
// location: '',
// organization: '',
},
navbar,
collections,
/**
* 公告板
* @see https://theme-plume.vuejs.press/guide/features/bulletin/
*/
// bulletin: {
// layout: 'top-right',
// contentType: 'markdown',
// title: '公告板标题',
// content: '公告板内容',
// },
{{/unless}}
/* 过渡动画 @see https://theme-plume.vuejs.press/config/theme/#transition */
// transition: {
// page: true, // 启用 页面间跳转过渡动画
// postList: true, // 启用 博客文章列表过渡动画
// appearance: 'fade', // 启用 深色模式切换过渡动画, 或配置过渡动画类型
// },
{{#if multiLanguage}}
locales: {
{{#each locales}}
'{{ path }}': {
/**
* @see https://theme-plume.vuejs.press/config/theme/#profile
*/
profile: {
avatar: 'https://theme-plume.vuejs.press/plume.png',
name: '{{ ../siteName }}',
description: '{{ ../siteDescription }}',
// circle: true,
// location: '',
// organization: '',
},
navbar: {{ prefix }}Navbar,
collections: {{ prefix }}Collections,
/**
* 公告板
* @see https://theme-plume.vuejs.press/guide/features/bulletin/
*/
// bulletin: {
// layout: 'top-right',
// contentType: 'markdown',
// title: '',
// content: '',
// },
},
{{/each}}
},
{{/if}}
})

View File

@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 72 72">
<path fill="#5086a1" d="M42.334 49.147a29.945 29.945 0 0 1-19.338-8.151c-8.014-7.365-8.378-18.076-8.533-22.649l-.022-.627a2.904 2.904 0 0 1 3.457-2.951c17.005 3.355 21.695 16.324 22.056 17.4a49.543 49.543 0 0 1 3.574 15.922a1 1 0 0 1-.967 1.052c-.029.001-.106.004-.227.004" />
<path fill="#8cccd5" d="M44.436 55.316c-11.646 0-17.376-6.974-17.653-7.354a1 1 0 0 1 .262-1.424a11.103 11.103 0 0 1 12.774-1.574c-1.465-9.078 1.877-13.568 2.031-13.77a.998.998 0 0 1 .75-.39a.97.97 0 0 1 .78.325c8.944 9.771 8.793 16.532 7.908 19.691c-.034.14-1.062 4.092-4.772 4.406c-.711.062-1.405.09-2.08.09" />
<g fill="none" stroke="#333" stroke-linecap="round" stroke-linejoin="round" stroke-width="1">
<path d="M55.184 57.69S34.96 45.877 23.097 24.206m22.131 30.096c-11.93.46-17.628-6.88-17.628-6.88" />
<path d="M40.528 42.483c-.56-7.195 2.116-10.679 2.116-10.679c8.834 9.654 8.406 16.162 7.681 18.747m-13.311-3.129a30.15 30.15 0 0 1-13.341-7.162c-8.072-7.419-8.067-18.241-8.232-22.577a1.903 1.903 0 0 1 2.264-1.932C34.694 19.103 39.02 32.528 39.02 32.528" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
const message = ref('Hello World!')
</script>
<template>
<div class="my-custom-content">
{{ message }}
</div>
</template>

View File

@ -1,6 +0,0 @@
declare module '*.vue' {
import type { ComponentOptions } from 'vue'
const comp: ComponentOptions
export default comp
}

View File

@ -1,50 +0,0 @@
:root {
/** 主题颜色 */
/*
--vp-c-brand-1: #5086a1;
--vp-c-brand-2: #6aa1b7;
--vp-c-brand-3: #8cccd5;
--vp-c-brand-soft: rgba(131, 208, 218, 0.314);
*/
/** 背景颜色 */
/*
--vp-c-bg: #fff;
--vp-c-bg-alt: #f6f6f7;
--vp-c-bg-elv: #fff;
--vp-c-bg-soft: #f6f6f7;
*/
/** 文本颜色 */
/*
--vp-c-text-1: rgba(60, 60, 67);
--vp-c-text-2: rgba(60, 60, 67, 0.78);
--vp-c-text-3: rgba(60, 60, 67, 0.56);
*/
}
/** 深色模式 */
[data-theme="dark"] {
/*
--vp-c-brand-1: #8cccd5;
--vp-c-brand-2: #6aa1b7;
--vp-c-brand-3: #5086a1;
--vp-c-brand-soft: rgba(131, 208, 218, 0.314);
*/
/*
--vp-c-bg: #1b1b1f;
--vp-c-bg-alt: #161618;
--vp-c-bg-elv: #202127;
--vp-c-bg-soft: #202127;
*/
/*
--vp-c-text-1: rgba(255, 255, 245, 0.86);
--vp-c-text-2: rgba(235, 235, 245, 0.6);
--vp-c-text-3: rgba(235, 235, 245, 0.38);
*/
}

View File

@ -1,74 +0,0 @@
# {{ name }}
The Site is generated using [vuepress](https://vuepress.vuejs.org/) and [vuepress-theme-plume](https://github.com/pengzhanbo/vuepress-theme-plume)
## Install
```sh
{{#if (equal packageManager "pnpm")}}
pnpm i
{{else if (equal packageManager "yarn")}}
yarn
{{else}}
npm i
{{/if}}
```
## Usage
{{#if (equal packageManager "pnpm")}}
```sh
# start dev server
pnpm docs:dev
# build for production
pnpm docs:build
# preview production build in local
pnpm docs:preview
# update vuepress and theme
pnpm vp-update
```
{{else if (equal packageManager "yarn")}}
```sh
# start dev server
yarn docs:dev
# build for production
yarn docs:build
# preview production build in local
yarn docs:preview
# update vuepress and theme
yarn vp-update
```
{{else}}
```sh
# start dev server
npm run docs:dev
# build for production
npm run docs:build
# preview production build in local
npm run docs:preview
# update vuepress and theme
npm run vp-update
```
{{/if}}
{{#if (equal deploy "github")}}
## Deploy to GitHub Pages
The plume theme has been created with GitHub Actions: `.github/workflows/docs-deploy.yml`. You also need to make the following settings in the GitHub repository:
- [ ] `settings > Actions > General`, Scroll to the bottom of the page, under `Workflow permissions`, check `Read and write permissions`, and click the save button.
- [ ] `settings > Pages`, In `Build and deployment`, select `Deploy from a branch` for `Source`, choose `gh-pages` for `Branch`, and click the save button.
(The `gh-pages` branch may not exist upon first creation. You can complete the above setup first, push the code to the main branch, wait for `github actions` to finish, and then proceed with the setup.)
- [ ] Modify the `base` option in `docs/.vuepress/config.ts`:
- If you are planning to deploy to `https://<USERNAME>.github.io/`, you can skip this step as `base` defaults to `"/"`.
- If you are planning to deploy to `https://<USERNAME>.github.io/<REPO>/`, meaning your repository URL is `https://github.com/<USERNAME>/<REPO>`, set `base` to `"/<REPO>/"`.
To customize a domain name, please refer to [Github Pages](https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages)
{{/if}}
## Documents
- [vuepress](https://vuepress.vuejs.org/)
- [vuepress-theme-plume](https://theme-plume.vuejs.press/)

View File

@ -1,74 +0,0 @@
# {{ name }}
网站使用 [vuepress](https://vuepress.vuejs.org/) 和 [vuepress-theme-plume](https://github.com/pengzhanbo/vuepress-theme-plume) 构建生成。
## Install
```sh
{{#if (equal packageManager "pnpm")}}
pnpm i
{{else if (equal packageManager "yarn")}}
yarn
{{else}}
npm i
{{/if}}
```
## Usage
{{#if (equal packageManager "pnpm")}}
```sh
# 启动开发服务
pnpm docs:dev
# 构建生产包
pnpm docs:build
# 本地预览生产服务
pnpm docs:preview
# 更新 vuepress 和主题
pnpm vp-update
```
{{else if (equal packageManager "yarn")}}
```sh
# 启动开发服务
yarn docs:dev
# 构建生产包
yarn docs:build
# 本地预览生产服务
yarn docs:preview
# update vuepress and theme
yarn vp-update
```
{{else}}
```sh
# 启动开发服务
npm run docs:dev
# 构建生产包
npm run docs:build
# 本地预览生产服务
npm run docs:preview
# 更新 vuepress 和主题
npm run vp-update
```
{{/if}}
{{#if (equal deploy "github")}}
## 部署到 GitHub Pages
主题已创建 github actions: `.github/workflows/docs-deploy.yml`,你还需要在 github 仓库中进行以下设置:
- [ ] `settings > Actions > General`,拉到页面底部,在 `Workflow permissions` 下,勾选 `Read and write permissions`,并点击保存按钮
- [ ] `settings > Pages`, 在 `Build and deployment` 中,`Source` 选择 `Deploy from a branch`, `Branch` 选择 `gh-pages`,并点击保存按钮
(首次创建可能没有 `gh-pages`分支,你可以先完成上面的设置后,推送一次代码到主分支,等待 `github actions` 完成后再进行设置)
- [ ] 修改 `docs/.vuepress/config.ts` 中的 `base` 选项:
- 如果你准备发布到 `https://<USERNAME>.github.io/` ,你可以省略这一步,因为 `base` 默认就是 `"/"` 。
- 如果你准备发布到 `https://<USERNAME>.github.io/<REPO>/` ,也就是说你的仓库地址是 `https://github.com/<USERNAME>/<REPO>` ,则将 `base` 设置为 `"/<REPO>/"`。
如需要自定义域名,请查看 [Github Pages 文档](https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages)
{{/if}}
## 文档
- [vuepress](https://vuepress.vuejs.org/)
- [vuepress-theme-plume](https://theme-plume.vuejs.press/)

View File

@ -1,84 +0,0 @@
name: deploy
on:
# 每当 push 到 main 分支时触发部署
# Deployment is triggered whenever a push is made to the main branch.
push:
branches: [main]
# 手动触发部署
# Manually trigger deployment
workflow_dispatch:
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# “最近更新时间” 等 git 日志相关信息,需要拉取全部提交记录
# "Last updated time" and other git log-related information require fetching all commit records.
fetch-depth: 0
{{#if (equal packageManager "pnpm")}}
- name: Setup pnpm
uses: pnpm/action-setup@v4
{{/if}}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# 选择要使用的 node 版本
node-version: 22
{{#if (equal packageManager "npm")}}
# 安装依赖
# Install dependencies
- name: Install Dependencies
run: npm ci
{{/if}}
{{#if (equal packageManager "pnpm")}}
# 安装依赖
# Install dependencies
- name: Install Dependencies
run: pnpm install --frozen-lockfile
{{/if}}
{{#if (equal packageManager "yarn")}}
- name: Run install
uses: borales/actions-yarn@v4
with:
cmd: install
{{/if}}
# 运行构建脚本
# Run the build script
{{#unless (equal packageManager "yarn")}}
- name: Build VuePress site
env:
NODE_OPTIONS: --max_old_space_size=8192
run: {{packageManager}} run docs:build
{{/unless}}
{{#if (equal packageManager "yarn")}}
- name: Build VuePress site
uses: borales/actions-yarn@v4
env:
NODE_OPTIONS: --max_old_space_size=8192
with:
cmd: docs:build
{{/if}}
# 查看 workflow 的文档来获取更多信息
# @see https://github.com/crazy-max/ghaction-github-pages
- name: Deploy to GitHub Pages
uses: crazy-max/ghaction-github-pages@v4
with:
# 部署到 gh-pages 分支
target_branch: gh-pages
# 部署目录为 VuePress 的默认输出目录
build_dir: {{docsDir}}/.vuepress/dist
env:
# @see https://docs.github.com/cn/actions/reference/authentication-in-a-workflow#about-the-github_token-secret
GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }}

View File

@ -1,9 +0,0 @@
# prevent Netlify npm install
[build]
publish = "{{ docsDir }}/.vuepress/dist"
command = "{{#if (equal packageManager 'yarn')}}yarn && yarn{{else}}{{packageManager}} run{{/if}} docs:build"
[build.environment]
NODE_VERSION = "22"
NPM_FLAGS = "--version"

View File

@ -1,6 +0,0 @@
{
"framework": null,
"buildCommand": "{{#if (equal packageManager 'yarn')}}yarn{{else}}{{packageManager}} run{{/if}} docs:build",
"installCommand": "{{#if (equal packageManager 'yarn')}}yarn{{else}}{{packageManager}} install{{/if}}",
"outputDirectory": "{{ docsDir }}/.vuepress/dist"
}

View File

@ -1,23 +0,0 @@
---
pageLayout: home
externalLinkIcon: false
config:
-
type: hero
full: true
forceDark: true
effect: lightning
hero:
name: Theme Plume
tagline: VuePress Next Theme
text: A simple, feature-rich, document & blog
actions:
-
theme: brand
text: Blog
link: {{#if (equal defaultLanguage 'en-US')}}/{{else}}/en/{{/if}}blog/
-
theme: alt
text: Github →
link: https://github.com/pengzhanbo/vuepress-theme-plume
---

View File

@ -1,8 +0,0 @@
---
title: Custom Component
tags:
- preview
- component
---
<CustomComponent />

View File

@ -1,322 +0,0 @@
---
title: Markdown
tags:
- markdown
---
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
Bold: **Bold text**
Italic: _Italic text_
~~Deleted text~~
Content ==Highlight==
Mathematical expression: $-(2^{n-1})$ ~ $2^{n-1} -1$
$\frac {\partial^r} {\partial \omega^r} \left(\frac {y^{\omega}} {\omega}\right)
= \left(\frac {y^{\omega}} {\omega}\right) \left\{(\log y)^r + \sum_{i=1}^r \frac {(-1)^ Ir \cdots (r-i+1) (\log y)^{ri}} {\omega^i} \right\}$
19^th^
H~2~O
::: center
content center
:::
::: right
content right
:::
- Unordered List 1
- Unordered List 2
- Unordered List 3
1. Ordered List 1
2. Ordered List 2
3. Ordered List 3
- [ ] Task List 1
- [ ] Task List 2
- [x] Task List 3
- [x] Task List 4
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |
> quote content
>
> quote content
[links](/)
[outside links](https://github.com/pengzhanbo)
**Badge**
- <Badge type="info" text="info badge" />
- <Badge type="tip" text="tip badge" />
- <Badge type="warning" text="warning badge" />
- <Badge type="danger" text="danger badge" />
**icons**
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />
**demo wrapper**
::: window title="Demo" height="200px"
<style scoped>
.open-door {
display: flex;
gap: 20px;
padding: 20px;
}
.open-door .main {
background: #ccc;
}
</style>
<div class="open-door">
<div class="main">main</div>
<div class="aside">aside</div>
</div>
:::
**code block**
```js whitespace
const a = 1
const b = 2
const c = a + b
// [!code word:obj]
const obj = {
toLong: {
deep: {
deep: {
deep: {
value: 'this is to long text. this is to long text. this is to long text. this is to long text.', // [!code highlight]
}
}
}
}
}
```
**code groups**
::: code-tabs
@tab tab1
```js
const a = 1
const b = 2
const c = a + b
```
@tab tab2
```ts
const a: number = 1
const b: number = 2
const c: number = a + b
```
:::
**code highlight**
```ts
function foo() {
const a = 1 // [!code highlight]
console.log(a)
const b = 2 // [!code ++]
const c = 3 // [!code --]
console.log(a + b + c) // [!code error]
console.log(a + b) // [!code warning]
}
```
**code focus**
```ts
function foo() {
const a = 1 // [!code focus]
}
```
::: note
note content [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: info
content [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: tip
content [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: warning
content [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: caution
content [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: important
content [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
**GFM alert**
> [!note]
> note
> [!info]
> info
> [!tip]
> tip
> [!warning]
> warning
> [!caution]
> caution
> [!important]
> important
**code demo:**
:::: demo title="Demo" desc="A normal demo"
::: code-tabs
@tab HTML
```html
<div id="app">
<h3>vuepress-theme-plume</h3>
</div>
```
@tab Javascript
```js
const a = 'So Awesome!'
const app = document.querySelector('#app')
app.appendChild(window.document.createElement('small')).textContent = a
```
@tab CSS
```css
#app {
font-size: 2em;
text-align: center;
}
```
:::
::::
**tab card**
::: tabs
@tab title 1
content block
@tab title 2
content block
:::
:::: warning
::: tabs
@tab title 1
content block
@tab title 2
content block
:::
::::
**footnote**
footnote 1 link[^first]。
footnote 2 link[^second]。
inline footnote ^[^first] definition。
Repeated footnote definition[^second]。
[^first]: footnote **you can contain special mark**
also can contain paragraph
[^second]: footnote content.

View File

@ -1,6 +0,0 @@
---
title: Demo
---
- [bar](./bar.md)
- [foo](./foo.md)

View File

@ -1,5 +0,0 @@
---
title: bar
---
[foo](./foo.md)

View File

@ -1,5 +0,0 @@
---
title: foo
---
[bar](./bar.md)

View File

@ -1,23 +0,0 @@
---
pageLayout: home
externalLinkIcon: false
config:
-
type: hero
full: true
forceDark: true
effect: lightning
hero:
name: Theme Plume
tagline: VuePress Next Theme
text: 一个简约的,功能丰富的 vuepress 文档&博客 主题
actions:
-
theme: brand
text: 博客
link: {{#if (equal defaultLanguage 'zh-CN')}}/{{else}}/zh/{{/if}}blog/
-
theme: alt
text: Github →
link: https://github.com/pengzhanbo/vuepress-theme-plume
---

View File

@ -1,8 +0,0 @@
---
title: 自定义组件
tags:
- 预览
- 组件
---
<CustomComponent />

View File

@ -1,346 +0,0 @@
---
title: Markdown
tags:
- markdown
---
## 标题H2
### 标题H3
#### 标题H4
##### 标题H5
###### 标题H6
## 标题2 Badge <Badge type="tip" text="Badge" />
### 标题3 Badge <Badge type="warning" text="Badge" />
#### 标题4 Badge <Badge type="danger" text="Badge" />
正文内容。
`@property` CSS at-rule是 [CSS Houdini API](https://developer.mozilla.org/zh-CN/docs/Web/Guide/Houdini)
的一部分,它允许开发者显式地定义他们的 [CSS 自定义属性](https://developer.mozilla.org/zh-CN/docs/Web/CSS/--*),
允许进行属性类型检查、设定默认值以及定义该自定义属性是否可以被继承。
`@property` 的出现,极大的增强了 CSS 的能力。
加粗:**加粗文字**
斜体: _斜体文字_
~~删除文字~~
内容 ==标记==
数学表达式: $-(2^{n-1})$ ~ $2^{n-1} -1$
$\frac {\partial^r} {\partial \omega^r} \left(\frac {y^{\omega}} {\omega}\right)
= \left(\frac {y^{\omega}} {\omega}\right) \left\{(\log y)^r + \sum_{i=1}^r \frac {(-1)^ Ir \cdots (r-i+1) (\log y)^{ri}} {\omega^i} \right\}$
19^th^
H~2~O
::: center
内容居中
:::
::: right
内容右对齐
:::
- 无序列表1
- 无序列表2
- 无序列表3
1. 有序列表1
2. 有序列表2
3. 有序列表3
- [ ] 任务列表1
- [ ] 任务列表2
- [x] 任务列表3
- [x] 任务列表4
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |
> 引用内容
>
> 引用内容
[链接](/)
[外部链接](https://github.com/pengzhanbo)
![plume](/plume.svg)
**Badge**
- <Badge type="info" text="info badge" />
- <Badge type="tip" text="tip badge" />
- <Badge type="warning" text="warning badge" />
- <Badge type="danger" text="danger badge" />
**图标:**
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />
**示例容器:**
::: window title="示例" height="200px"
<style scoped>
.open-door {
display: flex;
gap: 20px;
padding: 20px;
}
.open-door .main {
background: #ccc;
}
</style>
<div class="open-door">
<div class="main">main</div>
<div class="aside">aside</div>
</div>
:::
**代码:**
```js whitespace
const a = 1
const b = 2
const c = a + b
// [!code word:obj]
const obj = {
toLong: {
deep: {
deep: {
deep: {
value: 'this is to long text. this is to long text. this is to long text. this is to long text.', // [!code highlight]
}
}
}
}
}
```
**代码分组:**
::: code-tabs
@tab tab1
```js
const a = 1
const b = 2
const c = a + b
```
@tab tab2
```ts
const a: number = 1
const b: number = 2
const c: number = a + b
```
:::
**代码块高亮:**
```ts
function foo() {
const a = 1 // [!code highlight]
console.log(a)
const b = 2 // [!code ++]
const c = 3 // [!code --]
console.log(a + b + c) // [!code error]
console.log(a + b) // [!code warning]
}
```
**代码块聚焦:**
```ts
function foo() {
const a = 1 // [!code focus]
}
```
::: tip 仅标题
:::
::: note 注释
注释内容 [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: info 信息
信息内容 [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: tip 提示
提示内容 [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: warning 警告
警告内容 [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: caution 错误
错误内容 [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: important 重要
重要内容 [link](https://github.com/pengzhanbo) `inline code`
```js
const a = 1
const b = 2
const c = a + b
```
:::
::: details 详细标题
这里是内容。
:::
**GFM alert**
> [!note]
> note
> [!info]
> info
> [!tip]
> tip
> [!warning]
> warning
> [!caution]
> caution
> [!important]
> important
**代码演示:**
:::: demo title="常规示例" desc="一个常规示例"
::: code-tabs
@tab HTML
```html
<div id="app">
<h3>vuepress-theme-plume</h3>
</div>
```
@tab Javascript
```js
const a = 'So Awesome!'
const app = document.querySelector('#app')
app.appendChild(window.document.createElement('small')).textContent = a
```
@tab CSS
```css
#app {
font-size: 2em;
text-align: center;
}
```
:::
::::
**选项卡:**
::: tabs
@tab 标题1
内容区块
@tab 标题2
内容区块
:::
:::: warning
::: tabs
@tab 标题1
内容区块
@tab 标题2
内容区块
:::
::::
**脚注:**
脚注 1 链接[^first]。
脚注 2 链接[^second]。
行内的脚注^[行内脚注文本] 定义。
重复的页脚定义[^second]。
[^first]: 脚注 **可以包含特殊标记**
也可以由多个段落组成
[^second]: 脚注文字。

View File

@ -1,6 +0,0 @@
---
title: Demo
---
- [bar](./bar.md)
- [foo](./foo.md)

View File

@ -1,5 +0,0 @@
---
title: bar
---
[foo](./foo.md)

View File

@ -1,5 +0,0 @@
---
title: foo
---
[bar](./bar.md)

View File

@ -1,13 +0,0 @@
* text eol=lf
*.txt text eol=crlf
*.png binary
*.jpg binary
*.jpeg binary
*.ico binary
*.gif binary
*.webp binary
*.tff binary
*.woff binary
*.woff2 binary
*.pdf binary

View File

@ -1,8 +0,0 @@
**/node_modules
{{ docsDir }}/.vuepress/.cache
{{ docsDir }}/.vuepress/.temp
{{ docsDir }}/.vuepress/dist
.DS_Store
*.log

View File

@ -1,10 +0,0 @@
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: ['src/index.ts'],
outDir: 'lib',
dts: true,
format: 'esm',
sourcemap: false,
fixedExtension: false,
})

View File

@ -1,15 +0,0 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const packages = fs.readdirSync(path.resolve(__dirname, 'plugins'))
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-enum': [2, 'always', ['docs', 'theme', 'cli', ...packages]],
'footer-max-line-length': [0],
},
}

View File

@ -1,9 +0,0 @@
::: center
**QQ 交流群:** [792882761](https://qm.qq.com/q/FbPPoOIscE)
![QQ qr_code](/images/qq_qrcode.png){width="618" height="616" style="width: 200px"}
您在使用过程中遇到任何问题,欢迎通过 [issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new/choose) 反馈。也欢迎加入我们的 QQ 交流群一起讨论。
:::

View File

@ -1,32 +0,0 @@
import type { ClientConfig } from 'vuepress/client'
import { defineMermaidConfig } from '@vuepress/plugin-markdown-chart/client'
import { defineAsyncComponent, h } from 'vue'
import { Layout } from 'vuepress-theme-plume/client'
import VPPostItem from 'vuepress-theme-plume/components/Posts/VPPostItem.vue'
import PageContextMenu from 'vuepress-theme-plume/features/PageContextMenu.vue'
import { defineClientConfig } from 'vuepress/client'
import AsideNav from '~/components/AsideNav.vue'
import { setupThemeColors } from '~/composables/theme-colors.js'
defineMermaidConfig({
class: {
hideEmptyMembersBox: true,
},
look: 'handDrawn',
})
export default defineClientConfig({
enhance({ app }) {
app.component('VPPostItem', VPPostItem)
app.component('TintPlate', defineAsyncComponent(() => import('vuepress-theme-plume/components/background/TintPlate.vue')))
},
setup() {
setupThemeColors()
},
layouts: {
Layout: h(Layout, null, {
'aside-outline-after': () => h(AsideNav),
'doc-title-after': () => h(PageContextMenu),
}),
},
}) as ClientConfig

View File

@ -1,13 +0,0 @@
import { defineCollections, type ThemeCollections } from 'vuepress-theme-plume'
import { themeConfig } from './theme-config.js'
import { themeGuide } from './theme-guide.js'
import { tools } from './tools.js'
export const enCollections: ThemeCollections = defineCollections([
// 博客
{ type: 'post', dir: '/blog/', link: '/blog/', title: 'Blog' },
// 文档
themeGuide,
themeConfig,
tools,
])

View File

@ -1,53 +0,0 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const themeConfig: ThemeCollectionItem = defineCollection({
type: 'doc',
title: 'Config',
dir: 'config',
linkPrefix: '/config/',
sidebar: [
{
text: 'Configuration',
collapsed: false,
items: [
'intro',
'theme',
'locales',
'navbar',
'sidebar',
'collections',
'markdown',
],
},
{
text: 'Page Configuration',
prefix: 'frontmatter',
collapsed: false,
items: [
'basic',
'home',
'post',
'friend',
],
},
{
text: 'Built-in Plugins',
prefix: 'plugins',
collapsed: false,
items: [
'',
'shiki',
'search',
'reading-time',
'llms',
'markdown-enhance',
'markdown-power',
'markdown-image',
'markdown-math',
'markdown-include',
'watermark',
],
},
],
})

View File

@ -1,202 +0,0 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const themeGuide: ThemeCollectionItem = defineCollection({
type: 'doc',
dir: 'guide',
title: 'Guide',
linkPrefix: '/guide/',
sidebar: [
{
text: 'Quick Start',
collapsed: false,
icon: 'carbon:idea',
prefix: 'quick-start',
items: [
'intro',
'usage',
'project-structure',
{
text: 'Collection',
link: 'collection',
items: ['collection-post', 'collection-doc'],
},
'sidebar',
'write',
'auto-frontmatter',
'locales',
'deployment',
'optimize-build',
],
},
{
text: 'Write',
icon: 'fluent-mdl2:edit-create',
collapsed: false,
items: [
{
text: 'markdown',
icon: 'material-symbols:markdown-outline',
prefix: 'markdown',
collapsed: true,
items: [
'basic',
'extensions',
'attrs',
'emoji',
'math',
'table',
'icons',
'mark',
'plot',
'abbr',
'annotation',
'container',
'github-alerts',
'card',
'steps',
'file-tree',
'code-tree',
'field',
'tabs',
'qrcode',
'timeline',
'demo-wrapper',
'flex',
'collapse',
'npm-to',
'caniuse',
'chat',
'include',
'env',
'obsidian',
],
},
{
text: 'code block',
prefix: 'code',
icon: 'ph:code-bold',
collapsed: true,
items: [
'intro',
'features',
'copy-code',
'code-tabs',
'import',
'twoslash',
],
},
{
text: 'code repl',
prefix: 'repl',
icon: 'carbon:demo',
collapsed: true,
items: [
'frontend',
'rust',
'golang',
'kotlin',
'python',
'codepen',
'jsFiddle',
'codeSandbox',
'replit',
],
},
{
text: 'charts',
icon: 'mdi:chart-line',
prefix: 'chart',
collapsed: true,
items: [
'chart',
'echarts',
'mermaid',
'flowchart',
'markmap',
'plantuml',
],
},
{
text: 'resource embedded',
icon: 'dashicons:embed-video',
prefix: 'embed',
collapsed: true,
items: [
'pdf',
'bilibili',
'acfun',
'youtube',
'artplayer',
'audioReader',
],
},
],
},
{
text: 'Features',
icon: 'lucide:box',
collapsed: false,
prefix: 'features',
items: [
'icon',
'search',
'image-preview',
'comments',
'bulletin',
'encryption',
'contributors',
'changelog',
'copyright',
'watermark',
'friend-links',
'replace-assets',
'seo',
'sitemap',
'llmstxt',
],
},
{
text: 'Component',
prefix: 'components',
icon: 'uiw:component',
collapsed: false,
items: [
'badge',
'icon',
'plot',
'card',
'link-card',
'image-card',
'card-grid',
'card-masonry',
'home-box',
'repo-card',
'npm-badge',
'swiper',
],
},
{
text: 'Customization',
icon: 'material-symbols:dashboard-customize-outline-rounded',
collapsed: false,
prefix: 'custom',
items: [
{ text: 'Custom Homepage', link: 'home', items: ['home-hero-effect'] },
'style',
'slots',
'component-overrides',
],
},
{
text: 'API',
icon: 'mdi:api',
prefix: 'api',
collapsed: false,
items: [
'client',
'node',
],
},
],
})

View File

@ -1,20 +0,0 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const tools: ThemeCollectionItem = defineCollection({
type: 'doc',
dir: 'tools',
title: 'Theme Tools',
linkPrefix: '/tools/',
sidebar: [
{
text: 'Tools',
icon: 'tabler:tools',
items: [
'custom-theme',
'home-hero-tint-plate',
'caniuse',
],
},
],
})

View File

@ -1,2 +0,0 @@
export * from './en/index.js'
export * from './zh/index.js'

View File

@ -1,13 +0,0 @@
import { defineCollections, type ThemeCollections } from 'vuepress-theme-plume'
import { themeConfig } from './theme-config.js'
import { themeGuide } from './theme-guide.js'
import { tools } from './tools.js'
export const zhCollections: ThemeCollections = defineCollections([
// 博客
{ type: 'post', dir: '/blog/', link: '/blog/', title: '博客' },
// 文档
themeGuide,
themeConfig,
tools,
])

View File

@ -1,53 +0,0 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const themeConfig: ThemeCollectionItem = defineCollection({
type: 'doc',
title: '配置',
dir: 'config',
linkPrefix: '/config/',
sidebar: [
{
text: '配置',
collapsed: false,
items: [
'intro',
'theme',
'locales',
'navbar',
'sidebar',
'collections',
'markdown',
],
},
{
text: '页面配置',
prefix: 'frontmatter',
collapsed: false,
items: [
'basic',
'home',
'post',
'friend',
],
},
{
text: '内置插件',
prefix: 'plugins',
collapsed: false,
items: [
'',
'shiki',
'search',
'reading-time',
'llms',
'markdown-enhance',
'markdown-power',
'markdown-image',
'markdown-math',
'markdown-include',
'watermark',
],
},
],
})

View File

@ -1,202 +0,0 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const themeGuide: ThemeCollectionItem = defineCollection({
type: 'doc',
dir: 'guide',
title: '指南',
linkPrefix: '/guide/',
sidebar: [
{
text: '从这里开始',
collapsed: false,
icon: 'carbon:idea',
prefix: 'quick-start',
items: [
'intro',
'usage',
'project-structure',
{
text: '集合',
link: 'collection',
items: ['collection-post', 'collection-doc'],
},
'sidebar',
'write',
'auto-frontmatter',
'locales',
'deployment',
'optimize-build',
],
},
{
text: '写作',
icon: 'fluent-mdl2:edit-create',
collapsed: false,
items: [
{
text: 'markdown',
icon: 'material-symbols:markdown-outline',
prefix: 'markdown',
collapsed: true,
items: [
'basic',
'extensions',
'attrs',
'emoji',
'math',
'table',
'icons',
'mark',
'plot',
'abbr',
'annotation',
'container',
'github-alerts',
'card',
'steps',
'file-tree',
'code-tree',
'field',
'tabs',
'qrcode',
'timeline',
'demo-wrapper',
'flex',
'collapse',
'npm-to',
'caniuse',
'chat',
'include',
'env',
'obsidian',
],
},
{
text: '代码块',
prefix: 'code',
icon: 'ph:code-bold',
collapsed: true,
items: [
'intro',
'features',
'copy-code',
'code-tabs',
'import',
'twoslash',
],
},
{
text: '代码演示',
prefix: 'repl',
icon: 'carbon:demo',
collapsed: true,
items: [
'frontend',
'rust',
'golang',
'kotlin',
'python',
'codepen',
'jsFiddle',
'codeSandbox',
'replit',
],
},
{
text: '图表',
icon: 'mdi:chart-line',
prefix: 'chart',
collapsed: true,
items: [
'chart',
'echarts',
'mermaid',
'flowchart',
'markmap',
'plantuml',
],
},
{
text: '资源嵌入',
icon: 'dashicons:embed-video',
prefix: 'embed',
collapsed: true,
items: [
'pdf',
'bilibili',
'acfun',
'youtube',
'artplayer',
'audioReader',
],
},
],
},
{
text: '功能',
icon: 'lucide:box',
collapsed: false,
prefix: 'features',
items: [
'icon',
'search',
'image-preview',
'comments',
'bulletin',
'encryption',
'contributors',
'changelog',
'copyright',
'watermark',
'friend-links',
'replace-assets',
'seo',
'sitemap',
'llmstxt',
],
},
{
text: '组件',
prefix: 'components',
icon: 'uiw:component',
collapsed: false,
items: [
'badge',
'icon',
'plot',
'card',
'link-card',
'image-card',
'card-grid',
'card-masonry',
'home-box',
'repo-card',
'npm-badge',
'swiper',
],
},
{
text: '自定义',
icon: 'material-symbols:dashboard-customize-outline-rounded',
collapsed: false,
prefix: 'custom',
items: [
{ text: '自定义首页', link: 'home', items: ['home-hero-effect'] },
'style',
'slots',
'component-overrides',
],
},
{
text: 'API',
icon: 'mdi:api',
prefix: 'api',
collapsed: false,
items: [
'client',
'node',
],
},
],
})

View File

@ -1,20 +0,0 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const tools: ThemeCollectionItem = defineCollection({
type: 'doc',
dir: 'tools',
title: '工具',
linkPrefix: '/tools/',
sidebar: [
{
text: '工具',
icon: 'tabler:tools',
items: [
'custom-theme',
'home-hero-tint-plate',
'caniuse',
],
},
],
})

View File

@ -1,52 +1,91 @@
import type { UserConfig } from 'vuepress'
import fs from 'node:fs'
import path from 'node:path'
import { viteBundler } from '@vuepress/bundler-vite'
import { addViteOptimizeDepsInclude, addViteSsrExternal } from '@vuepress/helper'
import { defineUserConfig } from 'vuepress'
import { theme } from './theme.js'
import * as path from 'path'
import type { PlumeThemeOptions } from '@vuepress-plume/vuepress-theme-plume'
import { defineUserConfig } from '@vuepress/cli'
const pnpmWorkspace = fs.readFileSync(path.resolve(__dirname, '../../pnpm-workspace.yaml'), 'utf-8')
const vuepress = pnpmWorkspace.match(/vuepress:\s(2.+)/)?.[1] || ''
export default defineUserConfig({
base: '/',
lang: 'zh-CN',
source: path.resolve(__dirname, '../'),
public: path.resolve(__dirname, 'public'),
locales: {
'/': { title: 'Plume 主题', lang: 'zh-CN' },
'/en/': { title: 'Plume Theme', lang: 'en-US' },
export default defineUserConfig<PlumeThemeOptions>({
lang: 'zh',
title: 'Plume Theme',
description: '',
public: path.resolve(__dirname, '../public'),
theme: '@vuepress-plume/vuepress-theme-plume',
themeConfig: {
logo: 'https://pengzhanbo.cn/g.gif',
avatar: {
url: 'https://via.placeholder.com/300?text=Profile+Photo',
name: 'Plume Theme',
description: 'The Theme for Vuepress 2.0',
},
social: {
email: 'volodymyr@foxmail.com',
github: 'pengzhanbo',
QQ: '942450674',
weiBo: 'https://weibo.com',
zhiHu: 'https://zhihu.com',
facebook: 'https://baidu.com',
twitter: 'https://baidu.com',
linkedin: 'https://baidu.com',
},
notes: {
notes: [
{
link: 'typescript',
dir: 'typescript',
text: 'Typescript',
sidebar: [],
},
],
},
darkMode: true,
navbar: [
{ text: '首页', link: '/' },
{
text: '分类',
link: '/category/',
},
{
text: '标签',
link: '/tag/',
},
{
text: '笔记',
children: [
// {
// text: '技术',
// children: [{ text: '《typescript学习笔记》', link: '/' }],
// },
// {
// text: '技术',
// children: [{ text: '《typescript学习笔记》', link: '/' }],
// },
{
text: 'typescript',
link: '/note/typescript/',
},
{
text: '标签',
link: '/tag/',
},
],
},
],
footer: {
copyright: 'Copyright © 2022-present pengzhanbo',
},
themePlugins: {
caniuse: {
mode: 'embed',
},
search: {
// hotKeys: ['s', '/'],
// maxSuggestions: 5,
// isSearchable: (page) => page.path !== '/',
// getExtraFields: () => [],
locales: {
'/': {
placeholder: '搜索',
},
},
},
},
},
head: [
['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }],
['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }],
['meta', { name: 'google-site-verification', content: 'AaTP7bapCAcoO9ZGE67ilpy99GL6tYqtD30tRHjO9Ps' }],
],
pagePatterns: ['**/*.md', '!**/*.snippet.md', '!.vuepress', '!node_modules', '!docs/guide/repl/demo/*'],
extendsBundlerOptions(bundlerOptions, app) {
addViteOptimizeDepsInclude(bundlerOptions, app, '@simonwep/pickr')
addViteSsrExternal(bundlerOptions, app, '@simonwep/pickr')
},
define: {
__VUEPRESS_VERSION__: vuepress,
// debug hydration mismatch
// __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'true',
},
alias: {
'~/theme': path.resolve(__dirname, './themes'),
'~/components': path.resolve(__dirname, './themes/components'),
'~/composables': path.resolve(__dirname, './themes/composables'),
},
bundler: viteBundler(),
shouldPrefetch: false,
theme,
}) as UserConfig
})

View File

@ -1,103 +0,0 @@
import type { ThemeNavItem } from 'vuepress-theme-plume'
import { defineNavbarConfig } from 'vuepress-theme-plume'
import { version } from '../../package.json'
export const zhNavbar: ThemeNavItem[] = defineNavbarConfig([
{
text: '指南',
icon: 'icon-park-outline:guide-board',
link: '/guide/quick-start/intro.md',
activeMatch: '^/guide/',
},
{
text: '配置',
icon: 'icon-park-outline:setting-two',
link: '/config/intro.md',
activeMatch: '^/config/',
},
{
text: '博客',
link: '/blog/',
icon: 'material-symbols:article-outline',
activeMatch: '^/(blog|article)/',
},
{
text: '案例',
link: '/demos/',
icon: 'map:wind-surfing',
},
{
text: '更多',
icon: 'icon-park-outline:more-three',
items: [
{ text: '常见问题', link: '/faq/', icon: 'wpf:faq' },
{ text: '喝杯奶茶', link: '/sponsor/', icon: 'line-md:coffee-loop' },
{ text: '主题工具', link: '/tools/', icon: 'jam:tools' },
{ text: '友情链接', link: '/friends/', icon: 'carbon:friendship' },
{
text: 'Vuepress',
icon: 'logos:vue',
items: [
{ text: '官方文档', link: 'https://v2.vuepress.vuejs.org', icon: 'logos:vue' },
{ text: '生态系统', link: 'https://ecosystem.vuejs.press/', icon: 'logos:vue' },
],
},
],
},
{
text: `${version}`,
icon: 'codicon:versions',
badge: '新',
items: [
{ text: '更新日志', link: '/changelog/' },
{ text: '参与贡献', link: '/contributing/' },
],
},
])
export const enNavbar: ThemeNavItem[] = defineNavbarConfig([
{
text: 'Guide',
icon: 'icon-park-outline:guide-board',
link: '/en/guide/intro/',
activeMatch: '^/en/guide/',
},
{
text: 'Config',
icon: 'icon-park-outline:setting-two',
link: '/en/config/intro/',
activeMatch: '^/en/config/',
},
{
text: 'Blog',
link: '/en/blog/',
icon: 'material-symbols:article-outline',
activeMatch: '^/en/(blog|article)/',
},
{
text: 'More',
icon: 'icon-park-outline:more-three',
items: [
{ text: 'FAQ', link: '/en/faq/', icon: 'wpf:faq' },
{ text: 'Theme Tools', link: '/en/tools/', icon: 'jam:tools' },
{ text: 'Friend Links', link: '/en/friends/', icon: 'carbon:friendship' },
{
text: 'Vuepress',
icon: 'logos:vue',
items: [
{ text: 'Official Docs', link: 'https://v2.vuepress.vuejs.org', icon: 'logos:vue' },
{ text: 'Ecosystem', link: 'https://ecosystem.vuejs.press/', icon: 'logos:vue' },
],
},
],
},
{
text: `${version}`,
icon: 'codicon:versions',
badge: 'New',
items: [
{ text: 'Changelog', link: '/en/changelog/' },
{ text: 'Contributing', link: '/en/contributing/' },
],
},
])

View File

@ -1,54 +0,0 @@
import type { ThemeConfig } from 'vuepress-theme-plume'
import path from 'node:path'
import { defineThemeConfig } from 'vuepress-theme-plume'
import { enCollections, zhCollections } from './collections/index.js'
import { enNavbar, zhNavbar } from './navbar.js'
export default defineThemeConfig({
logo: '/plume.png',
profile: {
avatar: '/plume.png',
name: 'Plume Theme',
description: 'The Theme for Vuepress 2.0',
location: 'GuangZhou, China',
organization: 'pengzhanbo',
},
transition: { appearance: 'circle-clip' },
social: [
{ icon: 'github', link: 'https://github.com/pengzhanbo/vuepress-theme-plume' },
{ icon: 'qq', link: 'https://qm.qq.com/q/FbPPoOIscE' },
],
navbarSocialInclude: ['github', 'qq'],
footer: {
copyright: 'Copyright © 2021-present pengzhanbo',
},
locales: {
'/': {
navbar: zhNavbar,
collections: zhCollections,
},
'/en/': {
navbar: enNavbar,
collections: enCollections,
},
},
encrypt: {
rules: {
'/article/enx7c9s/': '123456',
},
},
bulletin: {
layout: 'top-right',
lifetime: 'always',
// title: '🎉 公告 🎉',
contentFile: path.join(__dirname, 'bulletin.md'),
enablePage: page => page.path === '/guide/features/bulletin/',
},
}) as ThemeConfig

View File

@ -1 +0,0 @@
plume.pengzhanbo.cn

View File

@ -1,4 +0,0 @@
/llms.md /llms.txt 200!
/llms-full.md /llms-full.txt 200!
/en/llms.md /en/llms.txt 200!
/en/llms-full.md /en/llms-full.txt 200!

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Some files were not shown because too many files have changed in this diff Show More