merge upstream
This commit is contained in:
commit
d91a20e65c
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,3 +15,6 @@ dist/
|
||||
|
||||
coverage/
|
||||
.idea
|
||||
|
||||
.claude/
|
||||
!.claude/skills/
|
||||
|
||||
497
CHANGELOG.md
497
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
111
CLAUDE.md
Normal file
111
CLAUDE.md
Normal file
@ -0,0 +1,111 @@
|
||||
# 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`
|
||||
@ -19,7 +19,7 @@ In the `plugins` directory:
|
||||
|
||||
Development requirements:
|
||||
|
||||
- [Node.js](http://nodejs.org/) version 20.6.0+
|
||||
- [Node.js](http://nodejs.org/) version 20.19.0+
|
||||
- [pnpm](https://pnpm.io/zh/) version 9+
|
||||
|
||||
Clone the repository and install dependencies:
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
开发要求:
|
||||
|
||||
- [Node.js](http://nodejs.org/) version 20.6.0+
|
||||
- [Node.js](http://nodejs.org/) version 20.19.0+
|
||||
- [pnpm](https://pnpm.io/zh/) version 9+
|
||||
|
||||
克隆代码仓库,并安装依赖:
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ---------------- | ------------------ |
|
||||
| >= 1.0.0-rc.170 | :white_check_mark: |
|
||||
| < 1.0.0-rc.170 | :x: |
|
||||
| >= 1.0.0-rc.190 | :white_check_mark: |
|
||||
| < 1.0.0-rc.190 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "create-vuepress-theme-plume",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.192",
|
||||
"version": "1.0.0-rc.193",
|
||||
"description": "The cli for create vuepress-theme-plume's project",
|
||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||
"license": "MIT",
|
||||
@ -27,7 +27,7 @@
|
||||
"templates"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsdown"
|
||||
"build": "tsdown --config-loader unrun"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "catalog:prod",
|
||||
|
||||
@ -93,6 +93,12 @@ docs:
|
||||
logo: https://official.skycraft.cn/i/3.jpg
|
||||
url: https://docs.skycraft.cn/
|
||||
preview: https://bbsimage.skycraft.cn/docs-preview.jpg
|
||||
-
|
||||
name: mcenahle Docs
|
||||
desc: mcenahle 的文档网站。
|
||||
logo: https://d.mcenahle.cn/images/logo.png
|
||||
url: https://d.mcenahle.cn/
|
||||
preview: https://mcenahle.cn/resources/docs-site-preview.jpg
|
||||
|
||||
blog:
|
||||
-
|
||||
|
||||
@ -3,6 +3,9 @@ title: File Tree
|
||||
createTime: 2025/10/08 14:41:57
|
||||
icon: mdi:file-tree
|
||||
permalink: /en/guide/markdown/file-tree/
|
||||
badge:
|
||||
text: Change
|
||||
type: warning
|
||||
---
|
||||
|
||||
## Overview
|
||||
@ -18,7 +21,7 @@ displayed, simply add a slash `/` at the end of the list item.
|
||||
The following syntax can be used to customize the appearance of the file tree:
|
||||
|
||||
- Emphasize file or directory names by making them bold, e.g., `**README.md**`
|
||||
- Add comments to files or directories by adding additional text after the name
|
||||
- Add comments to files or directories by appending a comment starting with `#` after the name, for example, `README.md This is a README file`
|
||||
- Mark files or directories as **added** or **deleted** by prefixing the name with `++` or `--`
|
||||
- Use `...` or `…` as the name to add placeholder files and directories.
|
||||
- Add `icon="simple"` or `icon="colored"` after `:::file-tree` to switch to simple icons or colored icons. The default is colored icons.
|
||||
@ -34,7 +37,7 @@ The following syntax can be used to customize the appearance of the file tree:
|
||||
- ++ config.ts
|
||||
- -- page1.md
|
||||
- README.md
|
||||
- theme A **theme** directory
|
||||
- theme # A **theme** directory
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
@ -61,7 +64,7 @@ The following syntax can be used to customize the appearance of the file tree:
|
||||
- ++ config.ts
|
||||
- -- page1.md
|
||||
- README.md
|
||||
- theme A **theme** directory
|
||||
- theme # A **theme** directory
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
|
||||
@ -28,10 +28,10 @@ A typical VuePress static site has the following file structure:
|
||||
:::file-tree
|
||||
|
||||
- my-site
|
||||
- docs \# Source directory
|
||||
- docs # Source directory
|
||||
- .vuepress/
|
||||
- …
|
||||
- README.md \# Homepage
|
||||
- README.md # Homepage
|
||||
- package.json
|
||||
|
||||
:::
|
||||
|
||||
@ -13,22 +13,22 @@ For projects created via the [command-line tool](./usage.md#command-line-install
|
||||
::: file-tree
|
||||
|
||||
- .git/
|
||||
- **docs** \# Documentation source directory
|
||||
- .vuepress \# VuePress configuration directory
|
||||
- public/ \# Static assets
|
||||
- client.ts \# Client configuration (optional)
|
||||
- collections.ts \# Collections configuration (optional)
|
||||
- config.ts \# VuePress main configuration
|
||||
- navbar.ts \# Navbar configuration (optional)
|
||||
- plume.config.ts \# Theme configuration file (optional)
|
||||
- demo \# `doc` type collection
|
||||
- **docs** # Documentation source directory
|
||||
- .vuepress/ # VuePress configuration directory
|
||||
- public/ # Static assets
|
||||
- client.ts # Client configuration (optional)
|
||||
- collections.ts # Collections configuration (optional)
|
||||
- config.ts # VuePress main configuration
|
||||
- navbar.ts # Navbar configuration (optional)
|
||||
- plume.config.ts # Theme configuration file (optional)
|
||||
- demo # `doc` type collection
|
||||
- foo.md
|
||||
- bar.md
|
||||
- blog \# `post` type collection
|
||||
- preview \# Blog category
|
||||
- markdown.md \# Category article
|
||||
- article.md \# Blog article
|
||||
- README.md \# Site homepage
|
||||
- blog # `post` type collection
|
||||
- preview # Blog category
|
||||
- markdown.md # Category article
|
||||
- article.md # Blog article
|
||||
- README.md # Site homepage
|
||||
- …
|
||||
- package.json
|
||||
- pnpm-lock.yaml
|
||||
|
||||
@ -139,14 +139,14 @@ The numeric part serves as the **sorting basis**. Directories without numbers ar
|
||||
::: file-tree
|
||||
|
||||
- docs
|
||||
- blog \# post type collection
|
||||
- blog # post type collection
|
||||
- 1.Frontend
|
||||
- 1.html/
|
||||
- 2.css/
|
||||
- 3.javascript/
|
||||
- 2.Backend/
|
||||
- DevOps/
|
||||
- typescript \# doc type collection
|
||||
- typescript # doc type collection
|
||||
- 1.Basics
|
||||
- 1.Variables.md
|
||||
- 2.Types.md
|
||||
|
||||
@ -3,6 +3,9 @@ title: 文件树
|
||||
createTime: 2024/09/30 14:41:57
|
||||
icon: mdi:file-tree
|
||||
permalink: /guide/markdown/file-tree/
|
||||
badge:
|
||||
text: 变更
|
||||
type: warning
|
||||
---
|
||||
|
||||
## 概述
|
||||
@ -17,12 +20,19 @@ permalink: /guide/markdown/file-tree/
|
||||
以下语法可用于自定义文件树的外观:
|
||||
|
||||
- 通过加粗文件名或目录名来突出显示,例如 `**README.md**`
|
||||
- 通过在名称后添加更多文本来为文件或目录添加注释
|
||||
- 通过在名称后添加以 `#` 开头的注释来为文件或目录添加注释,例如 `README.md # 这是一个 README 文件`
|
||||
- 通过在名称前添加 `++` 或 `--` 来标记文件或目录为 **新增** 或 **删除**
|
||||
- 使用 `...` 或 `…` 作为名称来添加占位符文件和目录。
|
||||
- 在 `:::file-tree` 后添加 `icon="simple"` 或 添加 `icon="colored"` 可以切换为简单图标或彩色图标,默认为彩色图标。
|
||||
- 在 `:::file-tree` 后添加 `title="xxxx"` 可以为文件树添加标题。
|
||||
|
||||
::: important `rc.193` 主题更新说明
|
||||
过去 `file-tree` 使用 **空格** 来区分文件名和注释,这在某些情况下会导致问题,例如文件名中包含空格时。
|
||||
为了解决这个问题,我们引入了 **# 号注释** 语法,您可以在文件名后添加以 `#` 开头的注释,例如 `README.md # 这是一个 README 文件`。
|
||||
|
||||
**此修改为 ==破坏性更新=={.danger} 更新。**
|
||||
:::
|
||||
|
||||
**输入:**
|
||||
|
||||
```md /++/ /--/
|
||||
@ -33,7 +43,7 @@ permalink: /guide/markdown/file-tree/
|
||||
- ++ config.ts
|
||||
- -- page1.md
|
||||
- README.md
|
||||
- theme 一个 **主题** 目录
|
||||
- theme # 一个 **主题** 目录
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
@ -60,7 +70,7 @@ permalink: /guide/markdown/file-tree/
|
||||
- ++ config.ts
|
||||
- -- page1.md
|
||||
- README.md
|
||||
- theme 一个 **主题** 目录
|
||||
- theme # 一个 **主题** 目录
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
|
||||
@ -27,10 +27,10 @@ permalink: /guide/collection/
|
||||
:::file-tree
|
||||
|
||||
- my-site
|
||||
- docs \# 源目录
|
||||
- docs # 源目录
|
||||
- .vuepress/
|
||||
- …
|
||||
- README.md \# 首页
|
||||
- README.md # 首页
|
||||
- package.json
|
||||
|
||||
:::
|
||||
|
||||
@ -12,22 +12,22 @@ permalink: /guide/project-structure/
|
||||
::: file-tree
|
||||
|
||||
- .git/
|
||||
- **docs** \# 文档源目录
|
||||
- .vuepress \# VuePress 配置目录
|
||||
- public/ \# 静态资源
|
||||
- client.ts \# 客户端配置(可选)
|
||||
- collections.ts \# Collections 配置(可选)
|
||||
- config.ts \# VuePress 主配置
|
||||
- navbar.ts \# 导航栏配置(可选)
|
||||
- plume.config.ts \# 主题配置文件(可选)
|
||||
- demo \# `doc` 类型 collection
|
||||
- **docs** # 文档源目录
|
||||
- .vuepress/ # VuePress 配置目录
|
||||
- public/ # 静态资源
|
||||
- client.ts # 客户端配置(可选)
|
||||
- collections.ts # Collections 配置(可选)
|
||||
- config.ts # VuePress 主配置
|
||||
- navbar.ts # 导航栏配置(可选)
|
||||
- plume.config.ts # 主题配置文件(可选)
|
||||
- demo # `doc` 类型 collection
|
||||
- foo.md
|
||||
- bar.md
|
||||
- blog \# `post` 类型 collection
|
||||
- preview \# 博客分类
|
||||
- markdown.md \# 分类文章
|
||||
- article.md \# 博客文章
|
||||
- README.md \# 站点首页
|
||||
- blog # `post` 类型 collection
|
||||
- preview # 博客分类
|
||||
- markdown.md # 分类文章
|
||||
- article.md # 博客文章
|
||||
- README.md # 站点首页
|
||||
- …
|
||||
- package.json
|
||||
- pnpm-lock.yaml
|
||||
|
||||
@ -130,14 +130,14 @@ const dir = /\d+\.[\s\S]+/
|
||||
::: file-tree
|
||||
|
||||
- docs
|
||||
- blog \# post 类型 collection
|
||||
- blog # post 类型 collection
|
||||
- 1.前端
|
||||
- 1.html/
|
||||
- 2.css/
|
||||
- 3.javascript/
|
||||
- 2.后端/
|
||||
- 运维/
|
||||
- typescript \# doc 类型 collection
|
||||
- typescript # doc 类型 collection
|
||||
- 1.基础
|
||||
- 1.变量.md
|
||||
- 2.类型.md
|
||||
|
||||
20
package.json
20
package.json
@ -3,7 +3,7 @@
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.192",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.30.3",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -18,9 +18,9 @@
|
||||
"pnpm": ">=9"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm clean && pnpm build:package",
|
||||
"build": "pnpm run clean && pnpm build:package",
|
||||
"build:package": "pnpm -r --stream build",
|
||||
"clean": "pnpm -r --stream clean",
|
||||
"clean": "pnpm -r --stream run clean",
|
||||
"dev": "pnpm --stream '/(dev:package|docs:dev)/'",
|
||||
"dev:package": "pnpm --parallel dev",
|
||||
"docs:dev": "wait-on -d 100 theme/lib/node/index.js && pnpm -F=docs docs:dev",
|
||||
@ -36,7 +36,7 @@
|
||||
"lint:css": "stylelint **/*.{css,vue}",
|
||||
"test": "cross-env TZ=Etc/UTC vitest --coverage",
|
||||
"prepare": "husky",
|
||||
"release:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"release:changelog": "conventional-changelog -p angular",
|
||||
"release:check": "pnpm lint && pnpm build",
|
||||
"release:sync": "node scripts/mirror-sync.mjs",
|
||||
"release:publish": "pnpm -r publish --tag latest",
|
||||
@ -60,7 +60,8 @@
|
||||
"@vitest/coverage-v8": "catalog:dev",
|
||||
"bumpp": "catalog:dev",
|
||||
"commitizen": "catalog:dev",
|
||||
"conventional-changelog-cli": "catalog:dev",
|
||||
"conventional-changelog": "catalog:dev",
|
||||
"conventional-changelog-angular": "catalog:dev",
|
||||
"cpx2": "catalog:dev",
|
||||
"cross-env": "catalog:dev",
|
||||
"cz-conventional-changelog": "catalog:dev",
|
||||
@ -90,12 +91,19 @@
|
||||
"resolutions": {
|
||||
"@bufbuild/protobuf": "^2.11.0",
|
||||
"@eslint-community/eslint-utils": "catalog:peer",
|
||||
"@shikijs/core": "^4.0.2",
|
||||
"@shikijs/twoslash": "^4.0.2",
|
||||
"@typescript-eslint/types": "catalog:peer",
|
||||
"@typescript-eslint/utils": "catalog:peer",
|
||||
"baseline-browser-mapping": "^2.10.0",
|
||||
"@xmldom/xmldom": ">=0.9.9",
|
||||
"baseline-browser-mapping": "^2.10.13",
|
||||
"chokidar": "catalog:prod",
|
||||
"esbuild": "catalog:prod",
|
||||
"lodash": ">=4.18.1",
|
||||
"lodash-es": ">=4.18.1",
|
||||
"sass-embedded": "catalog:peer",
|
||||
"shiki": "^4.0.2",
|
||||
"tmp": ">=0.2.5",
|
||||
"vite": "catalog:dev",
|
||||
"vue-router": "catalog:prod"
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vuepress-plume/plugin-fonts",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.192",
|
||||
"version": "1.0.0-rc.193",
|
||||
"description": "The Plugin for VuePress 2 - fonts",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"license": "MIT",
|
||||
@ -30,7 +30,7 @@
|
||||
"build": "pnpm run tsdown && pnpm run copy",
|
||||
"clean": "rimraf --glob ./lib",
|
||||
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png,woff2}\" lib",
|
||||
"tsdown": "tsdown"
|
||||
"tsdown": "tsdown --config-loader unrun"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vuepress": "catalog:vuepress"
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { defineConfig } from 'tsdown'
|
||||
import { argv } from '../../scripts/tsdown-args.mjs'
|
||||
|
||||
/** @import {Options} from 'tsdown' */
|
||||
import { defineConfig, type UserConfig } from 'tsdown'
|
||||
import { argv } from '../../scripts/tsdown-args'
|
||||
|
||||
const clientExternal = [
|
||||
/.*\.vue$/,
|
||||
@ -9,15 +7,13 @@ const clientExternal = [
|
||||
]
|
||||
|
||||
export default defineConfig(() => {
|
||||
/** @type {Options} */
|
||||
const DEFAULT_OPTIONS = {
|
||||
const DEFAULT_OPTIONS: UserConfig = {
|
||||
dts: true,
|
||||
sourcemap: false,
|
||||
format: 'esm',
|
||||
fixedExtension: false,
|
||||
}
|
||||
/** @type {Options[]} */
|
||||
const options = []
|
||||
const options: UserConfig[] = []
|
||||
|
||||
if (argv.node) {
|
||||
options.push({
|
||||
@ -36,7 +32,7 @@ export default defineConfig(() => {
|
||||
entry: ['./src/client/config.ts'],
|
||||
outDir: './lib/client',
|
||||
dts: false,
|
||||
external: clientExternal,
|
||||
deps: { neverBundle: clientExternal },
|
||||
},
|
||||
])
|
||||
}
|
||||
@ -72,7 +72,7 @@ describe('fileTreePlugin', () => {
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
- index.ts \# comment
|
||||
- index.ts # comment
|
||||
- node
|
||||
- index.ts
|
||||
- .gitignore
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vuepress-plugin-md-power",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.192",
|
||||
"version": "1.0.0-rc.193",
|
||||
"description": "The Plugin for VuePress 2 - markdown power",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"license": "MIT",
|
||||
@ -36,8 +36,8 @@
|
||||
"clean": "rimraf --glob ./lib",
|
||||
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||
"copy:watch": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib -w",
|
||||
"tsdown": "tsdown",
|
||||
"tsdown:watch": "tsdown --watch -- -c"
|
||||
"tsdown": "tsdown --config-loader unrun",
|
||||
"tsdown:watch": "tsdown --config-loader unrun --watch -- -c"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"artplayer": "catalog:peer",
|
||||
|
||||
@ -119,9 +119,9 @@ export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps {
|
||||
|
||||
// Extract filename and comment
|
||||
if (filename === '' && !focus) {
|
||||
const spaceIndex = info.indexOf(' ')
|
||||
filename = info.slice(0, spaceIndex === -1 ? info.length : spaceIndex)
|
||||
info = spaceIndex === -1 ? '' : info.slice(spaceIndex)
|
||||
const sharpIndex = info.indexOf('#')
|
||||
filename = info.slice(0, sharpIndex === -1 ? info.length : sharpIndex).trim()
|
||||
info = sharpIndex === -1 ? '' : info.slice(sharpIndex)
|
||||
}
|
||||
|
||||
comment = info.trim()
|
||||
|
||||
@ -559,11 +559,11 @@ export const definitions: Definitions = {
|
||||
'rolldown.config.prod.cjs': 'vscode-icons:file-type-light-rolldown',
|
||||
'rolldown.config.prod.mjs': 'vscode-icons:file-type-light-rolldown',
|
||||
'rolldown.config.prod.ts': 'vscode-icons:file-type-light-rolldown',
|
||||
'tsdown.config.js': 'vscode-icons:file-type-light-tsdown',
|
||||
'tsdown.config.cjs': 'vscode-icons:file-type-light-tsdown',
|
||||
'tsdown.config.mjs': 'vscode-icons:file-type-light-tsdown',
|
||||
'tsdown.config.ts': 'vscode-icons:file-type-light-tsdown',
|
||||
'tsdown.config.json': 'vscode-icons:file-type-light-tsdown',
|
||||
'tsdown.config.js': 'vscode-icons:file-type-tsdown',
|
||||
'tsdown.config.cjs': 'vscode-icons:file-type-tsdown',
|
||||
'tsdown.config.mjs': 'vscode-icons:file-type-tsdown',
|
||||
'tsdown.config.ts': 'vscode-icons:file-type-tsdown',
|
||||
'tsdown.config.json': 'vscode-icons:file-type-tsdown',
|
||||
|
||||
'.oxlintignore': 'vscode-icons:file-type-oxc',
|
||||
'.oxlintrc.json': 'vscode-icons:file-type-oxc',
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { defineConfig } from 'tsdown'
|
||||
import { argv } from '../../scripts/tsdown-args.mjs'
|
||||
|
||||
/** @import {Options} from 'tsdown' */
|
||||
import { defineConfig, type UserConfig } from 'tsdown'
|
||||
import { argv } from '../../scripts/tsdown-args'
|
||||
|
||||
const config = [
|
||||
{ dir: 'composables', files: ['codeRepl.ts', 'pdf.ts', 'rustRepl.ts', 'size.ts', 'audio.ts', 'demo.ts', 'mark.ts', 'decrypt.ts'] },
|
||||
@ -18,8 +16,7 @@ const clientExternal = [
|
||||
]
|
||||
|
||||
export default defineConfig((cli) => {
|
||||
/** @type {Options} */
|
||||
const DEFAULT_OPTIONS = {
|
||||
const DEFAULT_OPTIONS: UserConfig = {
|
||||
dts: true,
|
||||
sourcemap: false,
|
||||
format: 'esm',
|
||||
@ -27,8 +24,7 @@ export default defineConfig((cli) => {
|
||||
fixedExtension: false,
|
||||
}
|
||||
|
||||
/** @type {Options[]} */
|
||||
const options = []
|
||||
const options: UserConfig[] = []
|
||||
|
||||
// shared
|
||||
options.push({
|
||||
@ -43,7 +39,7 @@ export default defineConfig((cli) => {
|
||||
entry: ['./src/node/index.ts'],
|
||||
outDir: './lib/node',
|
||||
target: 'node20.19.0',
|
||||
external: ['markdown-it', /^@?vuepress/],
|
||||
deps: { neverBundle: ['markdown-it', /^@?vuepress/] },
|
||||
})
|
||||
}
|
||||
|
||||
@ -52,7 +48,7 @@ export default defineConfig((cli) => {
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: files.map(file => `./src/client/${dir}/${file}`),
|
||||
outDir: `./lib/client/${dir}`,
|
||||
external: clientExternal,
|
||||
deps: { neverBundle: clientExternal },
|
||||
})))
|
||||
}
|
||||
return options
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vuepress-plume/plugin-search",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.192",
|
||||
"version": "1.0.0-rc.193",
|
||||
"description": "The Plugin for VuePress 2 - local search",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"license": "MIT",
|
||||
@ -34,7 +34,7 @@
|
||||
"build": "pnpm run tsdown && pnpm run copy",
|
||||
"clean": "rimraf --glob ./lib",
|
||||
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||
"tsdown": "tsdown"
|
||||
"tsdown": "tsdown --config-loader unrun"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vuepress": "catalog:vuepress"
|
||||
|
||||
@ -31,6 +31,11 @@ export interface SearchIndexOptions {
|
||||
isSearchable: SearchPluginOptions['isSearchable']
|
||||
}
|
||||
|
||||
interface UpdateSearchIndexOptions extends Omit<SearchIndexOptions, 'app'> {
|
||||
/** VuePress page instance / VuePress 页面实例 */
|
||||
page: Page
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal index object structure for MiniSearch.
|
||||
*
|
||||
@ -165,53 +170,50 @@ export async function prepareSearchIndex({
|
||||
*
|
||||
* 当页面被修改时处理搜索索引更新。
|
||||
*
|
||||
* @param filepath - Path of the modified file relative to project root / 相对于项目根目录的修改文件路径
|
||||
* @param app - VuePress application instance / VuePress 应用实例
|
||||
* @param options - Search index preparation options / 搜索索引准备选项
|
||||
* @param options.app - VuePress application instance / VuePress 应用实例
|
||||
* @param options.page - VuePress page instance / VuePress 页面实例
|
||||
* @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
|
||||
* @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
|
||||
*/
|
||||
export async function onSearchIndexUpdated(
|
||||
filepath: string,
|
||||
app: App,
|
||||
{
|
||||
app,
|
||||
page,
|
||||
isSearchable,
|
||||
searchOptions,
|
||||
}: SearchIndexOptions,
|
||||
}: UpdateSearchIndexOptions,
|
||||
): Promise<void> {
|
||||
const pages = isSearchable ? app.pages.filter(isSearchable) : app.pages
|
||||
if (pages.some(p => p.filePathRelative?.endsWith(filepath))) {
|
||||
await indexFile(
|
||||
app.pages.find(p => p.filePathRelative?.endsWith(filepath))!,
|
||||
searchOptions,
|
||||
isSearchable,
|
||||
)
|
||||
if (isSearchable && !isSearchable(page))
|
||||
return
|
||||
|
||||
await indexFile(page, searchOptions, isSearchable)
|
||||
await writeTemp(app)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle search index update when a page is removed.
|
||||
*
|
||||
* 当页面被删除时处理搜索索引更新。
|
||||
*
|
||||
* @param filepath - Path of the removed file relative to project root / 相对于项目根目录的删除文件路径
|
||||
* @param app - VuePress application instance / VuePress 应用实例
|
||||
* @param options - Search index preparation options / 搜索索引准备选项
|
||||
* @param options.app - VuePress application instance / VuePress 应用实例
|
||||
* @param options.page - VuePress page instance / VuePress 页面实例
|
||||
* @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
|
||||
* @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
|
||||
*/
|
||||
export async function onSearchIndexRemoved(
|
||||
filepath: string,
|
||||
app: App,
|
||||
{
|
||||
app,
|
||||
page,
|
||||
isSearchable,
|
||||
searchOptions,
|
||||
}: SearchIndexOptions,
|
||||
}: UpdateSearchIndexOptions,
|
||||
): Promise<void> {
|
||||
const pages = isSearchable ? app.pages.filter(isSearchable) : app.pages
|
||||
if (pages.some(p => p.filePathRelative?.endsWith(filepath))) {
|
||||
const page = app.pages.find(p => p.filePathRelative?.endsWith(filepath))!
|
||||
if (isSearchable && !isSearchable(page))
|
||||
return
|
||||
|
||||
if (page.filePathRelative) {
|
||||
const fileId = page.path
|
||||
const locale = page.pathLocale
|
||||
const lang = page.lang
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import type { SearchPluginOptions } from '../shared/index.js'
|
||||
import { addViteOptimizeDepsInclude, getFullLocaleConfig } from '@vuepress/helper'
|
||||
import chokidar from 'chokidar'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import { SEARCH_LOCALES } from './locales/index.js'
|
||||
import { onSearchIndexRemoved, onSearchIndexUpdated, prepareSearchIndex, prepareSearchIndexPlaceholder } from './prepareSearchIndex.js'
|
||||
@ -72,22 +71,16 @@ export function searchPlugin({
|
||||
}
|
||||
},
|
||||
|
||||
onWatched: (app, watchers) => {
|
||||
const searchIndexWatcher = chokidar.watch('pages', {
|
||||
cwd: app.dir.temp(),
|
||||
ignoreInitial: true,
|
||||
ignored: (filepath, stats) => Boolean(stats?.isFile()) && !filepath.endsWith('.js'),
|
||||
})
|
||||
searchIndexWatcher.on('add', (filepath) => {
|
||||
onSearchIndexUpdated(filepath, { app, isSearchable, searchOptions })
|
||||
})
|
||||
searchIndexWatcher.on('change', (filepath) => {
|
||||
onSearchIndexUpdated(filepath, { app, isSearchable, searchOptions })
|
||||
})
|
||||
searchIndexWatcher.on('unlink', (filepath) => {
|
||||
onSearchIndexRemoved(filepath, { app, isSearchable, searchOptions })
|
||||
})
|
||||
watchers.push(searchIndexWatcher)
|
||||
onPageUpdated: async (app, type, page) => {
|
||||
if (!page?.filePathRelative)
|
||||
return
|
||||
|
||||
if (type === 'create' || type === 'update') {
|
||||
await onSearchIndexUpdated(app, { page, isSearchable, searchOptions })
|
||||
}
|
||||
else if (type === 'delete') {
|
||||
await onSearchIndexRemoved(app, { page, isSearchable, searchOptions })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { defineConfig } from 'tsdown'
|
||||
import { argv } from '../../scripts/tsdown-args.mjs'
|
||||
|
||||
/** @import {Options} from 'tsdown' */
|
||||
import { defineConfig, type UserConfig } from 'tsdown'
|
||||
import { argv } from '../../scripts/tsdown-args'
|
||||
|
||||
const sharedExternal = [
|
||||
/.*\/shared\/index\.js$/,
|
||||
@ -15,16 +13,14 @@ const clientExternal = [
|
||||
]
|
||||
|
||||
export default defineConfig(() => {
|
||||
/** @type {Options} */
|
||||
const DEFAULT_OPTIONS = {
|
||||
const DEFAULT_OPTIONS: UserConfig = {
|
||||
dts: true,
|
||||
sourcemap: false,
|
||||
format: 'esm',
|
||||
fixedExtension: false,
|
||||
}
|
||||
|
||||
/** @type {Options[]} */
|
||||
const options = []
|
||||
const options: UserConfig[] = []
|
||||
|
||||
// shared
|
||||
options.push({
|
||||
@ -38,7 +34,7 @@ export default defineConfig(() => {
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/node/index.ts'],
|
||||
outDir: './lib/node',
|
||||
external: sharedExternal,
|
||||
deps: { neverBundle: sharedExternal },
|
||||
target: 'node20.19.0',
|
||||
})
|
||||
}
|
||||
@ -50,21 +46,21 @@ export default defineConfig(() => {
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/client/utils/index.ts'],
|
||||
outDir: './lib/client/utils',
|
||||
external: clientExternal,
|
||||
deps: { neverBundle: clientExternal },
|
||||
},
|
||||
// client/composables/index.js
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/client/composables/index.ts'],
|
||||
outDir: './lib/client/composables',
|
||||
external: clientExternal,
|
||||
deps: { neverBundle: clientExternal },
|
||||
},
|
||||
// client/config.js
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/client/config.ts'],
|
||||
outDir: './lib/client',
|
||||
external: clientExternal,
|
||||
deps: { neverBundle: clientExternal },
|
||||
dts: false,
|
||||
},
|
||||
// client/index.js
|
||||
@ -72,10 +68,10 @@ export default defineConfig(() => {
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/client/index.ts'],
|
||||
outDir: './lib/client',
|
||||
external: [
|
||||
deps: { neverBundle: [
|
||||
...clientExternal,
|
||||
'./composables/index.js',
|
||||
],
|
||||
] },
|
||||
},
|
||||
])
|
||||
}
|
||||
7220
pnpm-lock.yaml
generated
7220
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
catalogMode: prefer
|
||||
|
||||
ignoreWorkspaceRootCheck: true
|
||||
|
||||
shamefullyHoist: true
|
||||
shellEmulator: true
|
||||
strictPeerDependencies: false
|
||||
@ -9,117 +11,110 @@ trustPolicyExclude:
|
||||
- cached-factory
|
||||
- memfs
|
||||
- semver
|
||||
- '@mdit/plugin-attrs'
|
||||
- '@mdit/plugin-mark'
|
||||
- '@mdit/plugin-sup'
|
||||
- '@mdit/plugin-sub'
|
||||
- '@mdit/plugin-tab'
|
||||
|
||||
packages:
|
||||
- docs
|
||||
- theme
|
||||
- cli
|
||||
- plugins/*
|
||||
- examples/*
|
||||
|
||||
patchedDependencies:
|
||||
floating-vue: patches/floating-vue.patch
|
||||
|
||||
catalogs:
|
||||
dev:
|
||||
'@commitlint/cli': ^20.4.3
|
||||
'@commitlint/config-conventional': ^20.4.3
|
||||
'@commitlint/cli': ^20.5.0
|
||||
'@commitlint/config-conventional': ^20.5.0
|
||||
'@lunariajs/core': ^0.1.1
|
||||
'@pengzhanbo/eslint-config-vue': ^2.1.0
|
||||
'@pengzhanbo/stylelint-config': ^2.1.0
|
||||
'@pengzhanbo/eslint-config-vue': ^2.2.0
|
||||
'@pengzhanbo/stylelint-config': ^2.2.0
|
||||
'@simonwep/pickr': ^1.9.1
|
||||
'@types/express': ^5.0.6
|
||||
'@types/js-yaml': ^4.0.9
|
||||
'@types/less': ^3.0.8
|
||||
'@types/markdown-it': ^14.1.2
|
||||
'@types/minimist': ^1.2.5
|
||||
'@types/node': ^25.3.3
|
||||
'@types/node': ^25.5.0
|
||||
'@types/picomatch': ^4.0.2
|
||||
'@types/qrcode': ^1.5.6
|
||||
'@types/stylus': ^0.48.43
|
||||
'@types/three': ^0.183.1
|
||||
'@types/webpack-env': ^1.18.8
|
||||
'@vitest/coverage-v8': ^4.0.18
|
||||
bumpp: ^10.4.1
|
||||
'@vitest/coverage-v8': ^4.1.2
|
||||
bumpp: ^11.0.1
|
||||
commitizen: ^4.3.1
|
||||
conventional-changelog-cli: ^5.0.0
|
||||
conventional-changelog: ^7.2.0
|
||||
conventional-changelog-angular: ^8.3.1
|
||||
cpx2: ^8.0.0
|
||||
cross-env: 7.0.3
|
||||
cz-conventional-changelog: ^3.3.0
|
||||
eslint: ^10.0.2
|
||||
eslint: ^10.1.0
|
||||
http-server: ^14.1.1
|
||||
husky: ^9.1.7
|
||||
less: ^4.5.1
|
||||
lint-staged: ^16.3.2
|
||||
less: ^4.6.4
|
||||
lint-staged: ^16.4.0
|
||||
markdown-it: ^14.1.1
|
||||
memfs: ^4.56.11
|
||||
mermaid: ^11.12.3
|
||||
memfs: ^4.57.1
|
||||
mermaid: ^11.14.0
|
||||
minimist: ^1.2.8
|
||||
postcss: ^8.5.8
|
||||
rimraf: ^6.1.3
|
||||
stylelint: ^17.4.0
|
||||
stylelint: ^17.6.0
|
||||
stylus: ^0.64.0
|
||||
tsconfig-vuepress: ^7.0.0
|
||||
tsdown: ^0.20.3
|
||||
tsdown: ^0.21.7
|
||||
typescript: ^5.9.3
|
||||
vite: ^8.0.0-beta.16
|
||||
vitest: ^4.0.18
|
||||
vite: ^8.0.3
|
||||
vitest: ^4.1.2
|
||||
wait-on: ^9.0.4
|
||||
peer:
|
||||
'@eslint-community/eslint-utils': ^4.9.1
|
||||
'@iconify/json': ^2.2.446
|
||||
'@iconify/json': ^2.2.458
|
||||
'@mathjax/src': ^4.1.1
|
||||
'@pinyin-pro/data': ^1.3.1
|
||||
'@typescript-eslint/types': ^8.56.1
|
||||
'@typescript-eslint/utils': ^8.56.1
|
||||
artplayer: ^5.3.0
|
||||
'@typescript-eslint/types': ^8.58.0
|
||||
'@typescript-eslint/utils': ^8.58.0
|
||||
artplayer: ^5.4.0
|
||||
dashjs: ^5.1.1
|
||||
gsap: ^3.14.2
|
||||
hls.js: ^1.6.15
|
||||
mpegts.js: 1.7.3
|
||||
ogl: ^1.0.11
|
||||
pinyin-pro: ^3.28.0
|
||||
postprocessing: ^6.38.3
|
||||
postprocessing: ^6.39.0
|
||||
pyodide: ^0.29.3
|
||||
sass: ^1.97.3
|
||||
sass-embedded: ^1.97.3
|
||||
swiper: ^12.1.2
|
||||
sass: ^1.98.0
|
||||
sass-embedded: ^1.98.0
|
||||
swiper: ^12.1.3
|
||||
three: ^0.183.2
|
||||
prod:
|
||||
'@clack/prompts': ^1.1.0
|
||||
'@clack/prompts': ^1.2.0
|
||||
'@iconify/utils': ^3.1.0
|
||||
'@iconify/vue': ^5.0.0
|
||||
'@mdit/plugin-attrs': ^0.25.1
|
||||
'@mdit/plugin-footnote': ^0.23.1
|
||||
'@mdit/plugin-mark': ^0.23.1
|
||||
'@mdit/plugin-sub': ^0.24.1
|
||||
'@mdit/plugin-sup': ^0.24.1
|
||||
'@mdit/plugin-tab': ^0.24.1
|
||||
'@mdit/plugin-tasklist': ^0.23.1
|
||||
'@mdit/plugin-attrs': ^0.25.2
|
||||
'@mdit/plugin-footnote': ^0.23.2
|
||||
'@mdit/plugin-mark': ^0.23.2
|
||||
'@mdit/plugin-sub': ^0.24.2
|
||||
'@mdit/plugin-sup': ^0.24.2
|
||||
'@mdit/plugin-tab': ^0.24.2
|
||||
'@mdit/plugin-tasklist': ^0.23.2
|
||||
'@pengzhanbo/utils': ^3.3.1
|
||||
'@vueuse/core': ^14.2.1
|
||||
'@vueuse/integrations': ^14.2.1
|
||||
cac: ^7.0.0
|
||||
chart.js: ^4.5.1
|
||||
chokidar: 5.0.0
|
||||
dayjs: ^1.11.19
|
||||
dayjs: ^1.11.20
|
||||
echarts: ^6.0.0
|
||||
esbuild: ^0.27.3
|
||||
esbuild: ^0.27.5
|
||||
flowchart.ts: ^3.0.1
|
||||
focus-trap: ^8.0.0
|
||||
focus-trap: ^8.0.1
|
||||
gray-matter: ^4.0.3
|
||||
handlebars: ^4.7.8
|
||||
handlebars: ^4.7.9
|
||||
hash-wasm: ^4.12.0
|
||||
image-size: ^2.0.2
|
||||
js-yaml: ^4.1.1
|
||||
katex: ^0.16.33
|
||||
katex: ^0.16.44
|
||||
local-pkg: ^1.1.2
|
||||
lru-cache: ^11.2.6
|
||||
lru-cache: ^11.2.7
|
||||
mark.js: ^8.11.1
|
||||
markdown-it-cjk-friendly: ^2.0.2
|
||||
markdown-it-container: ^4.0.0
|
||||
@ -127,45 +122,44 @@ catalogs:
|
||||
markmap-toolbar: ^0.18.12
|
||||
markmap-view: ^0.18.12
|
||||
minisearch: ^7.2.0
|
||||
nano-spawn: ^2.0.0
|
||||
nanoid: ^5.1.6
|
||||
nano-spawn: ^2.1.0
|
||||
nanoid: ^5.1.7
|
||||
os-locale: ^8.0.0
|
||||
p-map: ^7.0.4
|
||||
package-manager-detector: ^1.6.0
|
||||
picocolors: ^1.1.1
|
||||
picomatch: ^4.0.3
|
||||
picomatch: ^4.0.4
|
||||
qrcode: ^1.5.4
|
||||
shiki: ^4.0.1
|
||||
shiki: ^4.0.2
|
||||
sort-package-json: ^3.6.1
|
||||
tm-grammars: ^1.31.5
|
||||
tm-themes: ^1.12.1
|
||||
vue: ^3.5.29
|
||||
vue-router: ^5.0.3
|
||||
tm-grammars: ^1.31.15
|
||||
tm-themes: ^1.12.2
|
||||
vue: ^3.5.31
|
||||
vue-router: ^5.0.4
|
||||
vuepress:
|
||||
'@vuepress/bundler-vite': 2.0.0-rc.26
|
||||
'@vuepress/helper': 2.0.0-rc.123
|
||||
'@vuepress/plugin-cache': 2.0.0-rc.123
|
||||
'@vuepress/plugin-comment': 2.0.0-rc.123
|
||||
'@vuepress/plugin-copy-code': 2.0.0-rc.123
|
||||
'@vuepress/plugin-docsearch': 2.0.0-rc.123
|
||||
'@vuepress/plugin-git': 2.0.0-rc.123
|
||||
'@vuepress/plugin-llms': 2.0.0-rc.123
|
||||
'@vuepress/plugin-markdown-chart': 2.0.0-rc.123
|
||||
'@vuepress/plugin-markdown-hint': 2.0.0-rc.123
|
||||
'@vuepress/plugin-markdown-image': 2.0.0-rc.123
|
||||
'@vuepress/plugin-markdown-include': 2.0.0-rc.123
|
||||
'@vuepress/plugin-markdown-math': 2.0.0-rc.123
|
||||
'@vuepress/plugin-nprogress': 2.0.0-rc.123
|
||||
'@vuepress/plugin-photo-swipe': 2.0.0-rc.123
|
||||
'@vuepress/plugin-reading-time': 2.0.0-rc.123
|
||||
'@vuepress/plugin-replace-assets': 2.0.0-rc.123
|
||||
'@vuepress/plugin-seo': 2.0.0-rc.123
|
||||
'@vuepress/plugin-shiki': 2.0.0-rc.123
|
||||
'@vuepress/plugin-sitemap': 2.0.0-rc.123
|
||||
'@vuepress/plugin-watermark': 2.0.0-rc.123
|
||||
'@vuepress/shiki-twoslash': 2.0.0-rc.123
|
||||
vuepress: 2.0.0-rc.26
|
||||
|
||||
'@vuepress/bundler-vite': 2.0.0-rc.28
|
||||
'@vuepress/helper': 2.0.0-rc.128
|
||||
'@vuepress/plugin-cache': 2.0.0-rc.128
|
||||
'@vuepress/plugin-comment': 2.0.0-rc.128
|
||||
'@vuepress/plugin-copy-code': 2.0.0-rc.128
|
||||
'@vuepress/plugin-docsearch': 2.0.0-rc.128
|
||||
'@vuepress/plugin-git': 2.0.0-rc.128
|
||||
'@vuepress/plugin-llms': 2.0.0-rc.128
|
||||
'@vuepress/plugin-markdown-chart': 2.0.0-rc.128
|
||||
'@vuepress/plugin-markdown-hint': 2.0.0-rc.128
|
||||
'@vuepress/plugin-markdown-image': 2.0.0-rc.128
|
||||
'@vuepress/plugin-markdown-include': 2.0.0-rc.128
|
||||
'@vuepress/plugin-markdown-math': 2.0.0-rc.128
|
||||
'@vuepress/plugin-nprogress': 2.0.0-rc.128
|
||||
'@vuepress/plugin-photo-swipe': 2.0.0-rc.128
|
||||
'@vuepress/plugin-reading-time': 2.0.0-rc.128
|
||||
'@vuepress/plugin-replace-assets': 2.0.0-rc.128
|
||||
'@vuepress/plugin-seo': 2.0.0-rc.128
|
||||
'@vuepress/plugin-shiki': 2.0.0-rc.128
|
||||
'@vuepress/plugin-sitemap': 2.0.0-rc.128
|
||||
'@vuepress/plugin-watermark': 2.0.0-rc.128
|
||||
'@vuepress/shiki-twoslash': 2.0.0-rc.128
|
||||
vuepress: 2.0.0-rc.28
|
||||
onlyBuiltDependencies:
|
||||
- '@parcel/watcher'
|
||||
- core-js
|
||||
|
||||
@ -33,9 +33,7 @@ async function npmMirrorSync() {
|
||||
resolve()
|
||||
})
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
req.on('error', reject)
|
||||
|
||||
req.end()
|
||||
})
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
import process from 'node:process'
|
||||
import minimist from 'minimist'
|
||||
|
||||
// interface ArgvOptions {
|
||||
// client: boolean
|
||||
// node: boolean
|
||||
// }
|
||||
|
||||
/**
|
||||
* @typedef {object} ArgvOptions
|
||||
* @property {boolean} client - 是否构建客户端
|
||||
* @property {boolean} node - 是否构建 node 端
|
||||
*/
|
||||
interface ArgvOptions {
|
||||
client: boolean
|
||||
node: boolean
|
||||
}
|
||||
|
||||
const rawArgv = process.argv.slice(2)
|
||||
const tsupArgv = rawArgv.includes('--') ? rawArgv.slice(rawArgv.indexOf('--') + 1) : []
|
||||
@ -30,8 +24,7 @@ const parsed = tsupArgv.length
|
||||
all: true,
|
||||
}
|
||||
|
||||
/** @type {ArgvOptions} */
|
||||
export const argv = {
|
||||
export const argv: ArgvOptions = {
|
||||
client: parsed.client || parsed.all,
|
||||
node: parsed.node || parsed.all,
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vuepress-theme-plume",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.192",
|
||||
"version": "1.0.0-rc.193",
|
||||
"description": "A Blog&Document Theme for VuePress 2.0",
|
||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||
"license": "MIT",
|
||||
@ -62,8 +62,8 @@
|
||||
"clean": "rimraf --glob ./lib",
|
||||
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png,woff2}\" lib",
|
||||
"copy:watch": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png,woff2}\" lib -w",
|
||||
"tsdown": "tsdown",
|
||||
"tsdown:watch": "tsdown --watch -- -c"
|
||||
"tsdown": "tsdown --config-loader unrun",
|
||||
"tsdown:watch": "tsdown --config-loader unrun --watch -- -c"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@iconify/json": ">=2",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import VPIcon from '@theme/VPIcon.vue'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { useRouter, withBase } from 'vuepress/client'
|
||||
import { useLink } from '../composables/index.js'
|
||||
import { useData, useLink } from '../composables/index.js'
|
||||
|
||||
interface Props {
|
||||
tag?: string
|
||||
@ -21,7 +21,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
text: '',
|
||||
})
|
||||
const router = useRouter()
|
||||
|
||||
const { theme: themeData } = useData()
|
||||
const component = computed(() => {
|
||||
return props.tag || props.href ? 'a' : 'button'
|
||||
})
|
||||
@ -44,12 +44,15 @@ function linkTo(e: Event) {
|
||||
:class="[size, theme]"
|
||||
:href=" link ? link[0] === '#' || isExternalProtocol ? link : withBase(link) : undefined"
|
||||
:target="target ?? (isExternal ? '_blank' : undefined)"
|
||||
:rel="rel ?? (isExternal ? 'noreferrer' : undefined)"
|
||||
:rel="rel ?? (isExternal ? 'noopener noreferrer' : undefined)"
|
||||
@click="linkTo($event)"
|
||||
>
|
||||
<span class="button-content">
|
||||
<VPIcon v-if="icon" :name="icon" />
|
||||
<slot><span>{{ text }}</span></slot>
|
||||
<span v-if="isExternal" class="visually-hidden">
|
||||
{{ themeData.openNewWindowText || '(Open in new window)' }}
|
||||
</span>
|
||||
<VPIcon v-if="suffixIcon" :name="suffixIcon" />
|
||||
</span>
|
||||
</Component>
|
||||
@ -73,6 +76,10 @@ function linkTo(e: Event) {
|
||||
background-color 0.1s;
|
||||
}
|
||||
|
||||
.vp-button:focus-visible {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.vp-button.medium {
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
|
||||
@ -51,6 +51,8 @@ async function onSubmit() {
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:placeholder="theme.encryptPlaceholder ?? 'Enter Password'"
|
||||
:aria-invalid="errorCode === 1"
|
||||
:aria-describedby="errorCode === 1 ? 'encrypt-error' : undefined"
|
||||
@keyup.enter="onSubmit"
|
||||
@focus="!password && (errorCode = 0)"
|
||||
@input="password && (errorCode = 0)"
|
||||
|
||||
@ -138,6 +138,10 @@ function onBlur() {
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.vp-flyout .button:focus-visible {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.option-icon {
|
||||
margin-right: 0;
|
||||
font-size: 16px;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue'
|
||||
import { useRouter, withBase } from 'vuepress/client'
|
||||
import { useLink } from '../composables/index.js'
|
||||
import { useData, useLink } from '../composables/index.js'
|
||||
|
||||
const props = defineProps<{
|
||||
tag?: string
|
||||
@ -13,6 +13,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const { theme } = useData()
|
||||
|
||||
const tag = computed(() => props.tag ?? (props.href ? 'a' : 'span'))
|
||||
|
||||
@ -32,11 +33,20 @@ function linkTo(e: Event) {
|
||||
class="vp-link" :class="{ link, 'no-icon': noIcon, 'vp-external-link-icon': isExternal }"
|
||||
:href="link ? isExternalProtocol ? link : isExternal ? link : withBase(link) : undefined"
|
||||
:target="target ?? (isExternal ? '_blank' : undefined)"
|
||||
:rel="rel ?? (isExternal ? 'noreferrer' : undefined)"
|
||||
:rel="rel ?? (isExternal ? 'noopener noreferrer' : undefined)"
|
||||
@click="linkTo($event)"
|
||||
>
|
||||
<slot>
|
||||
{{ text || href }}
|
||||
</slot>
|
||||
<span v-if="isExternal && !noIcon" class="visually-hidden">
|
||||
{{ theme.openNewWindowText || '(Open in new window)' }}
|
||||
</span>
|
||||
</Component>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.vp-link:focus-visible {
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -58,12 +58,6 @@ function onItemInteraction(e: MouseEvent | Event) {
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
|
||||
function onCaretClick() {
|
||||
if (item.link) {
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -107,17 +101,16 @@ function onCaretClick() {
|
||||
/>
|
||||
</Component>
|
||||
|
||||
<div
|
||||
<button
|
||||
v-if="item.collapsed != null"
|
||||
type="button"
|
||||
class="caret"
|
||||
role="button"
|
||||
aria-label="toggle section"
|
||||
tabindex="0"
|
||||
@click="onCaretClick"
|
||||
@keydown.enter="onCaretClick"
|
||||
:aria-label="`${collapsed ? 'Expand' : 'Collapse'} ${item.text}`"
|
||||
:aria-expanded="!collapsed"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span class="vpi-chevron-right caret-icon" />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<template v-if="item.items && item.items.length && depth < 5">
|
||||
|
||||
@ -56,6 +56,11 @@ const label = computed(() => {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.vp-social-link:focus-visible {
|
||||
border-radius: 50%;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.vp-social-link > :deep([class*="vpi-"]),
|
||||
.vp-social-link > :deep(.vp-icon.is-svg) {
|
||||
width: 20px;
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
ariaChecked?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- eslint-disable-next-line vue-a11y/role-has-required-aria-props -->
|
||||
<button class="vp-switch" type="button" role="switch">
|
||||
<button
|
||||
class="vp-switch"
|
||||
type="button"
|
||||
role="switch"
|
||||
:aria-checked="ariaChecked ?? false"
|
||||
>
|
||||
<span class="check">
|
||||
<span v-if="$slots.default" class="icon">
|
||||
<slot />
|
||||
@ -28,6 +38,10 @@
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.vp-switch:focus-visible {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.check {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
|
||||
@ -6,6 +6,10 @@ import { enableTransitions, resolveTransitionKeyframes, useData } from '../compo
|
||||
const checked = ref(false)
|
||||
const { theme, isDark } = useData()
|
||||
|
||||
watchPostEffect(() => {
|
||||
checked.value = isDark.value
|
||||
})
|
||||
|
||||
const transitionMode = computed(() => {
|
||||
const transition = theme.value.transition
|
||||
const options = typeof transition === 'object' ? transition : {}
|
||||
@ -15,8 +19,12 @@ const transitionMode = computed(() => {
|
||||
return typeof options.appearance === 'string' ? options.appearance : 'fade'
|
||||
})
|
||||
|
||||
function shouldReduceMotion(): boolean {
|
||||
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
}
|
||||
|
||||
const toggleAppearance = inject('toggle-appearance', async ({ clientX, clientY }: MouseEvent) => {
|
||||
if (!enableTransitions() || transitionMode.value === false) {
|
||||
if (!enableTransitions() || transitionMode.value === false || shouldReduceMotion()) {
|
||||
isDark.value = !isDark.value
|
||||
return
|
||||
}
|
||||
@ -80,6 +88,12 @@ watchPostEffect(() => {
|
||||
/* rtl:ignore */
|
||||
transform: translateX(18px);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.vp-switch-appearance :deep(.check) {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
||||
@ -104,6 +104,8 @@ export function useHeaders(): Ref<MenuItem[]> {
|
||||
return headers
|
||||
}
|
||||
|
||||
const IGNORE_WRAPPERS = ['.vp-bulletin', '.vp-demo-wrapper']
|
||||
|
||||
/**
|
||||
* Get headers from the page content
|
||||
* Extracts and filters headings based on the outline configuration
|
||||
@ -117,7 +119,7 @@ export function useHeaders(): Ref<MenuItem[]> {
|
||||
export function getHeaders(range?: ThemeOutline): MenuItem[] {
|
||||
const heading = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
|
||||
const ignores = Array.from(document.querySelectorAll(
|
||||
heading.map(h => `.vp-demo-wrapper ${h}`).join(','),
|
||||
heading.map(h => IGNORE_WRAPPERS.map(w => `${w} ${h}`)).flat().join(','),
|
||||
))
|
||||
const headers = Array.from(
|
||||
document.querySelectorAll(heading.map(h => `.vp-doc ${h}`).join(',')),
|
||||
|
||||
5
theme/src/client/styles/normalize.css
vendored
5
theme/src/client/styles/normalize.css
vendored
@ -262,6 +262,11 @@ figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:where(#app) :focus-visible {
|
||||
outline: var(--vp-focus-ring-width) solid var(--vp-focus-ring-color);
|
||||
outline-offset: var(--vp-focus-ring-offset);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
|
||||
@ -703,3 +703,12 @@
|
||||
--vp-c-control-hover: var(--vp-c-default-2);
|
||||
--vp-c-control-disabled: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus ring
|
||||
* -------------------------------------------------------------------------- */
|
||||
:root {
|
||||
--vp-focus-ring-color: var(--vp-c-brand-1);
|
||||
--vp-focus-ring-width: 2px;
|
||||
--vp-focus-ring-offset: 2px;
|
||||
}
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Temporary enhancement for VuePress app.
|
||||
* This enhancement will be removed in the next version of vuepress/core.
|
||||
*
|
||||
* VuePress 应用的临时增强。
|
||||
* 此增强将在 vuepress/core 的下一个版本中被移除。
|
||||
*/
|
||||
|
||||
import type { App } from 'vuepress'
|
||||
import { fs, hash } from 'vuepress/utils'
|
||||
|
||||
/**
|
||||
* Cache structure for writeTemp operations.
|
||||
* Tracks content hash and writing promises for optimization.
|
||||
*
|
||||
* writeTemp 操作的缓存结构。
|
||||
* 跟踪内容哈希和写入承诺以进行优化。
|
||||
*/
|
||||
interface WriteTempCache {
|
||||
/** Content hash for change detection / 用于变更检测的内容哈希 */
|
||||
hash?: string
|
||||
/** Current writing promise / 当前写入承诺 */
|
||||
current?: Promise<void>
|
||||
/** Next writing promise to chain / 要链接的下一个写入承诺 */
|
||||
next?: () => Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhance the VuePress app with optimized writeTemp method.
|
||||
* Implements caching and promise chaining for better performance.
|
||||
*
|
||||
* 使用优化的 writeTemp 方法增强 VuePress 应用。
|
||||
* 实现缓存和承诺链接以获得更好的性能。
|
||||
*
|
||||
* @param app - VuePress application instance / VuePress 应用实例
|
||||
*/
|
||||
export function enhanceApp(app: App): void {
|
||||
// rewrite writeTemp to cache the writing promise
|
||||
const cache = new Map<string, WriteTempCache>()
|
||||
app.writeTemp = async function (file: string, content: string): Promise<string> {
|
||||
const filePath = app.dir.temp(file)
|
||||
const contentHash = hash(content)
|
||||
|
||||
let item = cache.get(filePath)
|
||||
if (!item) {
|
||||
cache.set(filePath, (item = {}))
|
||||
}
|
||||
|
||||
// if content hash is the same as the last one, skip writing
|
||||
if (item.hash === contentHash) {
|
||||
return filePath
|
||||
}
|
||||
|
||||
item.hash = contentHash
|
||||
|
||||
if (!item.current) {
|
||||
item.current = (async () => {
|
||||
await fs.outputFile(filePath, content)
|
||||
// if there is a next writing promise, chain it with the current one
|
||||
item.current = item.next?.()
|
||||
return item.current
|
||||
})()
|
||||
}
|
||||
else {
|
||||
// if there is a current writing promise, save the next writing promise
|
||||
item.next = async () => {
|
||||
await fs.outputFile(filePath, content)
|
||||
item.next = undefined
|
||||
item.current = undefined
|
||||
}
|
||||
}
|
||||
await item.current
|
||||
return filePath
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,8 @@ export const deLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: 'Nachdruck von:',
|
||||
copyrightLicenseText: 'Lizenz:',
|
||||
|
||||
openNewWindowText: '(In neuem Fenster öffnen)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: 'Seite nicht gefunden',
|
||||
|
||||
@ -22,6 +22,8 @@ export const enLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: 'This article is reprint from:',
|
||||
copyrightLicenseText: 'License under:',
|
||||
|
||||
openNewWindowText: '(Open in new window)',
|
||||
|
||||
encryptButtonText: 'Confirm',
|
||||
encryptPlaceholder: 'Enter password',
|
||||
encryptGlobalText: 'Only password can access this site',
|
||||
|
||||
@ -28,6 +28,8 @@ export const frLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: 'Reproduit de :',
|
||||
copyrightLicenseText: 'Licence :',
|
||||
|
||||
openNewWindowText: '(Ouvrir dans une nouvelle fenêtre)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: 'Page non trouvée',
|
||||
|
||||
@ -28,6 +28,8 @@ export const jaLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: '本文の転載元:',
|
||||
copyrightLicenseText: 'ライセンス:',
|
||||
|
||||
openNewWindowText: '(新しいウィンドウで開く)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: 'ページが見つかりません',
|
||||
|
||||
@ -40,6 +40,8 @@ export const koLocale: ThemeLocaleText = {
|
||||
categoryText: '카테고리',
|
||||
archiveTotalText: '{count}개의 글',
|
||||
|
||||
openNewWindowText: '(새 창에서 열기)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: '페이지를 찾을 수 없습니다',
|
||||
|
||||
@ -28,6 +28,8 @@ export const ruLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: 'Перепечатано из:',
|
||||
copyrightLicenseText: 'Лицензия:',
|
||||
|
||||
openNewWindowText: '(Открыть в новой вкладке)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: 'Страница не найдена',
|
||||
|
||||
@ -28,6 +28,8 @@ export const zhTwLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: '本文轉載自:',
|
||||
copyrightLicenseText: '授權條款:',
|
||||
|
||||
openNewWindowText: '(在新窗口打開)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: '頁面未找到',
|
||||
|
||||
@ -27,6 +27,8 @@ export const zhLocale: ThemeLocaleText = {
|
||||
copyrightCreationReprintText: '本文转载自:',
|
||||
copyrightLicenseText: '许可证:',
|
||||
|
||||
openNewWindowText: '(在新窗口打开)',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: '页面未找到',
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { App } from 'vuepress'
|
||||
import { watch } from 'chokidar'
|
||||
import { perf } from '../utils/index.js'
|
||||
import { prepareArticleTagColors } from './prepareArticleTagColor.js'
|
||||
import { prepareCollections } from './prepareCollections.js'
|
||||
@ -29,24 +28,3 @@ export async function prepareData(app: App): Promise<void> {
|
||||
|
||||
perf.log('prepare:data')
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for changes in prepared data and re-prepare when needed
|
||||
*
|
||||
* 监听准备数据的变化,并在需要时重新准备数据
|
||||
*/
|
||||
export function watchPrepare(
|
||||
app: App,
|
||||
watchers: any[],
|
||||
): void {
|
||||
const pagesWatcher = watch('pages', {
|
||||
cwd: app.dir.temp(),
|
||||
ignoreInitial: true,
|
||||
ignored: (filepath, stats) => Boolean(stats?.isFile()) && !filepath.endsWith('.js'),
|
||||
})
|
||||
watchers.push(pagesWatcher)
|
||||
|
||||
pagesWatcher.on('change', () => prepareData(app))
|
||||
pagesWatcher.on('add', () => prepareData(app))
|
||||
pagesWatcher.on('unlink', () => prepareData(app))
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ function getAutoDirSidebar(
|
||||
current.text = title
|
||||
}
|
||||
}
|
||||
if (frontmatter.icon) {
|
||||
if (frontmatter.icon && dir.endsWith('.md')) {
|
||||
current.icon = frontmatter.icon as ThemeIcon
|
||||
}
|
||||
if (parent?.items?.length) {
|
||||
@ -235,7 +235,12 @@ function getAllSidebar(): Record<string, ThemeSidebar> {
|
||||
const sidebar = locale === '/' ? (opt.sidebar || options.sidebar) : opt.sidebar
|
||||
locales[locale] = {}
|
||||
for (const [key, value] of entries(sidebar || {})) {
|
||||
locales[locale][ensureLeadingSlash(key)] = value
|
||||
locales[locale][ensureLeadingSlash(key)] = isPlainObject(value) && 'items' in value
|
||||
? { ...value, prefix: value.prefix?.startsWith('/') ? value.prefix : normalizeLink(locale, removeLeadingSlash(key)) }
|
||||
: {
|
||||
items: value,
|
||||
prefix: normalizeLink(locale, removeLeadingSlash(key)),
|
||||
}
|
||||
}
|
||||
const collections = rawCollections?.filter(item => item.type === 'doc')
|
||||
if (collections?.length) {
|
||||
|
||||
@ -12,11 +12,10 @@ import {
|
||||
templateBuildRenderer,
|
||||
} from './config/index.js'
|
||||
import { detectThemeOptions, detectVersions } from './detector/index.js'
|
||||
import { enhanceApp } from './enhance.js'
|
||||
import { configLoader } from './loadConfig/index.js'
|
||||
import { createPages, extendsPageData } from './pages/index.js'
|
||||
import { setupPlugins } from './plugins/index.js'
|
||||
import { prepareData, watchPrepare } from './prepare/index.js'
|
||||
import { prepareData } from './prepare/index.js'
|
||||
import { prepareThemeData } from './prepare/prepareThemeData.js'
|
||||
import { perf, resolve, setTranslateLang, templates, THEME_NAME } from './utils/index.js'
|
||||
|
||||
@ -40,8 +39,6 @@ import { perf, resolve, setTranslateLang, templates, THEME_NAME } from './utils/
|
||||
*/
|
||||
export function plumeTheme(options: ThemeOptions = {}): Theme {
|
||||
return (app) => {
|
||||
enhanceApp(app)
|
||||
|
||||
setTranslateLang(app.options.lang)
|
||||
perf.init(app.env.isDebug)
|
||||
|
||||
@ -87,9 +84,12 @@ export function plumeTheme(options: ThemeOptions = {}): Theme {
|
||||
await prepareData(app)
|
||||
},
|
||||
|
||||
onPageUpdated: async (app) => {
|
||||
await prepareData(app)
|
||||
},
|
||||
|
||||
onWatched: async (app, watchers) => {
|
||||
configLoader.watch(watchers as any)
|
||||
watchPrepare(app, watchers)
|
||||
watchAutoFrontmatter(app, watchers as any)
|
||||
},
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import type { MarkdownPowerPluginOptions } from 'vuepress-plugin-md-power'
|
||||
*/
|
||||
export interface MarkdownOptions extends MarkdownPowerPluginOptions,
|
||||
MarkdownChartPluginOptions,
|
||||
Pick<MarkdownHintPluginOptions, 'alert' | 'hint'> {
|
||||
Partial<Pick<MarkdownHintPluginOptions, 'alert' | 'hint'>> {
|
||||
/**
|
||||
* 已弃用
|
||||
* @deprecated use `demo` instead
|
||||
|
||||
@ -339,6 +339,13 @@ export interface ThemeLocaleText {
|
||||
*/
|
||||
nextPageLabel?: string
|
||||
|
||||
/**
|
||||
* 打开新窗口的文本
|
||||
*
|
||||
* @default '(Open in new window)'
|
||||
*/
|
||||
openNewWindowText?: string
|
||||
|
||||
/**
|
||||
* 404 页面配置
|
||||
*/
|
||||
@ -404,13 +411,44 @@ export interface ThemeLocaleText {
|
||||
*/
|
||||
encryptPlaceholder?: string
|
||||
|
||||
// 以下字段与 PageContextMenu 相关 ------ start
|
||||
|
||||
/**
|
||||
* 复制页面的文本
|
||||
*/
|
||||
copyPageText?: string
|
||||
/**
|
||||
* 复制页面成功的文本
|
||||
*/
|
||||
copiedPageText?: string
|
||||
/**
|
||||
* 复制页面中的文本
|
||||
*/
|
||||
copingPageText?: string
|
||||
/**
|
||||
* 复制页面的标签文本
|
||||
*/
|
||||
copyTagline?: string
|
||||
/**
|
||||
* 查看 Markdown 文本
|
||||
*/
|
||||
viewMarkdown?: string
|
||||
/**
|
||||
* 查看 Markdown 标签文本
|
||||
*/
|
||||
viewMarkdownTagline?: string
|
||||
/**
|
||||
* 咨询 AI 文本
|
||||
*/
|
||||
askAIText?: string
|
||||
/**
|
||||
* 咨询 AI 标签文本
|
||||
*/
|
||||
askAITagline?: string
|
||||
/**
|
||||
* 咨询 AI 消息文本
|
||||
*/
|
||||
askAIMessage?: string
|
||||
|
||||
// 以上字段与 PageContextMenu 相关 ------ end
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { defineConfig } from 'tsdown'
|
||||
import { argv } from '../scripts/tsdown-args.mjs'
|
||||
import { defineConfig, type UserConfig } from 'tsdown'
|
||||
import { argv } from '../scripts/tsdown-args'
|
||||
|
||||
/** @import {Options} from 'tsdown' */
|
||||
|
||||
@ -24,25 +24,22 @@ const featuresComposables = fs.readdirSync(
|
||||
)
|
||||
|
||||
export default defineConfig((cli) => {
|
||||
/** @type {Options} */
|
||||
const DEFAULT_OPTIONS = {
|
||||
const DEFAULT_OPTIONS: UserConfig = {
|
||||
dts: true,
|
||||
sourcemap: false,
|
||||
watch: cli.watch,
|
||||
format: 'esm',
|
||||
silent: !!cli.watch,
|
||||
clean: !cli.watch,
|
||||
fixedExtension: false,
|
||||
}
|
||||
/** @type {Options[]} */
|
||||
const options = []
|
||||
const options: UserConfig[] = []
|
||||
|
||||
// shared
|
||||
options.push({
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/shared/index.ts'],
|
||||
outDir: './lib/shared',
|
||||
external: ['sax'],
|
||||
deps: { neverBundle: ['sax'] },
|
||||
})
|
||||
|
||||
if (argv.node) {
|
||||
@ -50,20 +47,20 @@ export default defineConfig((cli) => {
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/node/index.ts'],
|
||||
outDir: './lib/node',
|
||||
external: [...sharedExternal, '@pinyin-pro/data/complete'],
|
||||
deps: { neverBundle: [...sharedExternal, '@pinyin-pro/data/complete'] },
|
||||
target: 'node20.19.0',
|
||||
watch: false,
|
||||
})
|
||||
}
|
||||
if (argv.client) {
|
||||
options.push(...[
|
||||
options.push(
|
||||
// client/utils/index.js
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: ['./src/client/utils/index.ts'],
|
||||
outDir: './lib/client/utils',
|
||||
platform: 'browser',
|
||||
external: clientExternal,
|
||||
deps: { neverBundle: clientExternal },
|
||||
},
|
||||
// client/composables/index.js
|
||||
{
|
||||
@ -71,10 +68,10 @@ export default defineConfig((cli) => {
|
||||
entry: ['./src/client/composables/index.ts'],
|
||||
outDir: './lib/client/composables',
|
||||
platform: 'browser',
|
||||
external: [
|
||||
deps: { neverBundle: [
|
||||
...clientExternal,
|
||||
'../utils/index.js',
|
||||
],
|
||||
] },
|
||||
},
|
||||
// client/config.js
|
||||
{
|
||||
@ -83,11 +80,11 @@ export default defineConfig((cli) => {
|
||||
outDir: './lib/client',
|
||||
dts: false,
|
||||
platform: 'browser',
|
||||
external: [
|
||||
deps: { neverBundle: [
|
||||
...clientExternal,
|
||||
'./composables/index.js',
|
||||
'./utils/index.js',
|
||||
],
|
||||
] },
|
||||
},
|
||||
// client/index.js
|
||||
{
|
||||
@ -95,26 +92,26 @@ export default defineConfig((cli) => {
|
||||
entry: ['./src/client/index.ts'],
|
||||
outDir: './lib/client',
|
||||
platform: 'browser',
|
||||
external: [
|
||||
deps: { neverBundle: [
|
||||
...clientExternal,
|
||||
'./composables/index.js',
|
||||
'./utils/index.js',
|
||||
'./config.js',
|
||||
],
|
||||
] },
|
||||
},
|
||||
...featuresComposables.map(file => ({
|
||||
...DEFAULT_OPTIONS,
|
||||
entry: [`./src/client/features/composables/${file}`],
|
||||
outDir: `./lib/client/features/composables/`,
|
||||
platform: 'browser',
|
||||
external: [
|
||||
deps: { neverBundle: [
|
||||
...clientExternal,
|
||||
'../../composables/index.js',
|
||||
'../../utils/index.js',
|
||||
...featuresComposables.map(file => `./${file.replace('.ts', '.js')}`),
|
||||
],
|
||||
})),
|
||||
])
|
||||
] },
|
||||
} as UserConfig)),
|
||||
)
|
||||
}
|
||||
return options
|
||||
})
|
||||
@ -14,7 +14,8 @@
|
||||
"theme/**/*",
|
||||
"docs/.vuepress/**/*",
|
||||
"cli/**/*",
|
||||
"scripts/**/*"
|
||||
"scripts/**/*",
|
||||
"**/tsdown.config.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user