From 5c201e3ed0b49ffb94432d940eeb1d951e221bec Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 14 Feb 2026 14:53:41 +0800 Subject: [PATCH] docs: improve jsdoc (#852) --- cli/src/constants.ts | 59 +++++ cli/src/generate.ts | 26 +++ cli/src/packageJson.ts | 17 ++ cli/src/render.ts | 17 ++ cli/src/run.ts | 8 + cli/src/translate.ts | 24 ++ cli/src/types.ts | 221 +++++++++++++++++- cli/src/utils/fs.ts | 25 ++ cli/src/utils/index.ts | 16 ++ .../src/node/container/align.ts | 41 +++- .../src/node/container/card.ts | 22 ++ .../src/node/container/chat.ts | 44 +++- .../src/node/container/codeTabs.ts | 16 ++ .../src/node/container/codeTree.ts | 56 +++-- .../src/node/container/collapse.ts | 44 +++- .../src/node/container/createContainer.ts | 83 ++++--- .../src/node/container/demoWrapper.ts | 15 +- .../src/node/container/encrypt.ts | 32 ++- .../src/node/container/field.ts | 15 ++ .../src/node/container/fileTree.ts | 64 +++-- .../src/node/container/index.ts | 10 + .../src/node/container/langRepl.ts | 34 +++ .../src/node/container/npmTo.ts | 68 ++++-- .../src/node/container/npmToPreset.ts | 40 ++++ .../src/node/container/steps.ts | 9 + .../src/node/container/table.ts | 36 ++- .../src/node/container/tabs.ts | 17 ++ .../src/node/container/timeline.ts | 81 ++++++- .../src/node/embed/createEmbedRuleBlock.ts | 65 +++++- .../plugin-md-power/src/node/embed/index.ts | 8 + .../src/node/enhance/docsTitle.ts | 24 +- .../src/node/enhance/imageSize.ts | 110 +++++++++ .../plugin-md-power/src/node/enhance/links.ts | 43 ++++ .../plugin-md-power/src/node/inline/abbr.ts | 61 +++++ .../src/node/inline/annotation.ts | 99 ++++++++ .../src/node/inline/env-preset.ts | 7 +- .../plugin-md-power/src/node/inline/index.ts | 8 + .../plugin-md-power/src/node/inline/plot.ts | 21 ++ .../src/node/utils/cleanMarkdownEnv.ts | 34 +++ .../src/node/utils/encryptContent.ts | 27 ++- .../src/node/utils/findLocales.ts | 9 + .../plugin-md-power/src/node/utils/logger.ts | 67 +++++- .../plugin-md-power/src/node/utils/nanoid.ts | 8 + .../plugin-md-power/src/node/utils/package.ts | 16 ++ .../src/node/utils/parseRect.ts | 9 + .../src/node/utils/resolveAttrs.ts | 23 ++ .../src/node/utils/stringifyAttrs.ts | 11 + .../src/node/utils/stringifyProp.ts | 11 +- plugins/plugin-md-power/src/shared/caniuse.ts | 36 +++ .../plugin-md-power/src/shared/codeSandbox.ts | 45 ++++ .../plugin-md-power/src/shared/codeTabs.ts | 15 ++ plugins/plugin-md-power/src/shared/codepen.ts | 40 ++++ plugins/plugin-md-power/src/shared/demo.ts | 90 +++++++ plugins/plugin-md-power/src/shared/encrypt.ts | 34 ++- plugins/plugin-md-power/src/shared/env.ts | 12 +- .../plugin-md-power/src/shared/fileTree.ts | 18 ++ .../plugin-md-power/src/shared/jsfiddle.ts | 25 ++ plugins/plugin-md-power/src/shared/locale.ts | 30 +++ plugins/plugin-md-power/src/shared/npmTo.ts | 15 ++ plugins/plugin-md-power/src/shared/pdf.ts | 44 +++- plugins/plugin-md-power/src/shared/plugin.ts | 121 ++++++++-- plugins/plugin-md-power/src/shared/qrcode.ts | 57 +++++ plugins/plugin-md-power/src/shared/repl.ts | 60 +++++ plugins/plugin-md-power/src/shared/size.ts | 20 ++ plugins/plugin-md-power/src/shared/video.ts | 152 +++++++++++- theme/src/client/composables/dark-mode.ts | 37 ++- theme/src/client/composables/data.ts | 5 + theme/src/client/composables/encrypt-data.ts | 76 +++++- theme/src/client/composables/encrypt.ts | 124 ++++++++++ theme/src/client/composables/layout.ts | 5 + theme/src/client/composables/link.ts | 36 ++- theme/src/client/composables/nav.ts | 51 ++++ theme/src/client/composables/outline.ts | 158 ++++++++++++- theme/src/client/composables/page.ts | 5 + theme/src/client/composables/posts-data.ts | 15 ++ .../src/client/composables/posts-post-list.ts | 10 + theme/src/client/composables/posts-tags.ts | 15 ++ theme/src/client/composables/prev-next.ts | 10 + theme/src/client/composables/sidebar-data.ts | 5 + theme/src/client/composables/sidebar.ts | 22 +- theme/src/client/composables/theme-data.ts | 60 ++++- theme/src/client/utils/animate.ts | 29 ++- theme/src/client/utils/dom.ts | 42 ++++ theme/src/client/utils/resolveEditLink.ts | 32 +++ theme/src/client/utils/resolveNavLink.ts | 36 +++ theme/src/client/utils/resolveRepoType.ts | 17 ++ theme/src/client/utils/shared.ts | 76 +++++- theme/src/node/defineConfig.ts | 14 +- theme/src/node/detector/options.ts | 2 + theme/src/node/detector/versions.ts | 5 + theme/src/node/index.ts | 2 + theme/src/node/locales/index.ts | 4 + theme/src/node/pages/autoCategory.ts | 5 + theme/src/node/pages/createPages.ts | 5 + theme/src/node/pages/encryptPage.ts | 5 + theme/src/node/pages/extendsPage.ts | 5 + theme/src/node/pages/pageBulletin.ts | 5 + theme/src/node/plugins/code.ts | 5 + theme/src/node/plugins/git.ts | 5 + theme/src/node/plugins/llms.ts | 5 + theme/src/node/plugins/markdown.ts | 5 + theme/src/node/plugins/setupPlugins.ts | 5 + theme/src/node/prepare/index.ts | 10 + .../node/prepare/prepareArticleTagColor.ts | 10 + theme/src/node/prepare/prepareCollections.ts | 5 + theme/src/node/prepare/prepareEncrypt.ts | 10 + .../node/prepare/prepareHomeHeroEffects.ts | 5 + theme/src/node/prepare/prepareIcons.ts | 5 + theme/src/node/prepare/preparePostsData.ts | 5 + theme/src/node/prepare/prepareSidebar.ts | 5 + theme/src/node/prepare/prepareThemeData.ts | 5 + theme/src/node/theme.ts | 5 +- theme/src/node/utils/constants.ts | 5 + theme/src/node/utils/createFsCache.ts | 55 +++++ theme/src/node/utils/encrypt.ts | 10 + theme/src/node/utils/hash.ts | 18 ++ theme/src/node/utils/interopDefault.ts | 5 + theme/src/node/utils/logger.ts | 46 ++++ theme/src/node/utils/package.ts | 15 ++ theme/src/node/utils/path.ts | 76 ++++++ theme/src/node/utils/pinyin.ts | 10 + theme/src/shared/common/base.ts | 51 +++- theme/src/shared/common/social.ts | 9 + theme/src/shared/data.ts | 44 +++- theme/src/shared/features/autoFrontmatter.ts | 15 ++ theme/src/shared/features/bulletin.ts | 2 + theme/src/shared/features/collection.ts | 11 + theme/src/shared/features/copyright.ts | 6 + theme/src/shared/features/encrypt.ts | 14 +- theme/src/shared/features/latestUpdated.ts | 5 + theme/src/shared/features/markdown.ts | 5 + theme/src/shared/features/navbar.ts | 69 +++++- theme/src/shared/features/notes.ts | 4 + theme/src/shared/features/posts.ts | 45 +++- theme/src/shared/features/profile.ts | 2 + theme/src/shared/features/search.ts | 21 ++ theme/src/shared/features/sidebar.ts | 49 +++- theme/src/shared/features/transition.ts | 11 + theme/src/shared/frontmatter/friends.ts | 8 +- theme/src/shared/frontmatter/home.ts | 5 + .../src/shared/frontmatter/homeHeroEffects.ts | 50 ++++ theme/src/shared/frontmatter/normal.ts | 19 +- theme/src/shared/frontmatter/page.ts | 38 +++ theme/src/shared/frontmatter/post.ts | 33 ++- theme/src/shared/locale.ts | 6 + theme/src/shared/options.ts | 51 ++-- theme/src/shared/pageData.ts | 13 ++ theme/src/shared/plugins.ts | 15 ++ theme/src/shared/resolved/navbar.ts | 109 ++++++++- theme/src/shared/resolved/sidebar.ts | 40 +++- theme/src/shared/utils.ts | 23 ++ 151 files changed, 4411 insertions(+), 225 deletions(-) diff --git a/cli/src/constants.ts b/cli/src/constants.ts index 1a8e524a..2e55756c 100644 --- a/cli/src/constants.ts +++ b/cli/src/constants.ts @@ -1,27 +1,86 @@ import type { Bundler, Langs, Options } from './types.js' +/** + * Language options for VuePress configuration + * + * 语言选项,用于 VuePress 配置 + */ export const languageOptions: Options = [ { label: 'English', value: 'en-US' }, { label: '简体中文', value: 'zh-CN' }, ] +/** + * Bundler options for VuePress build tool + * + * 构建器选项,用于 VuePress 构建工具 + */ export const bundlerOptions: Options = [ { 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 = [ { label: 'Custom', value: DeployType.custom }, { label: 'GitHub Pages', value: DeployType.github }, diff --git a/cli/src/generate.ts b/cli/src/generate.ts index 34fe594c..439a1222 100644 --- a/cli/src/generate.ts +++ b/cli/src/generate.ts @@ -8,6 +8,15 @@ 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, @@ -106,6 +115,14 @@ export async function generate( }) } +/** + * Create documentation files based on configuration + * + * 根据配置创建文档文件 + * + * @param data - Resolved configuration data / 解析后的配置数据 + * @returns Array of file objects / 文件对象数组 + */ async function createDocsFiles(data: ResolvedData): Promise { const fileList: File[] = [] if (data.multiLanguage) { @@ -131,6 +148,15 @@ async function createDocsFiles(data: ResolvedData): Promise { 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), diff --git a/cli/src/packageJson.ts b/cli/src/packageJson.ts index a665ac4a..6f068a59 100644 --- a/cli/src/packageJson.ts +++ b/cli/src/packageJson.ts @@ -11,6 +11,23 @@ function sortPackageJson(json: Record) { }) } +/** + * 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, diff --git a/cli/src/render.ts b/cli/src/render.ts index 66ef3961..abbeaf4f 100644 --- a/cli/src/render.ts +++ b/cli/src/render.ts @@ -2,16 +2,33 @@ 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, diff --git a/cli/src/run.ts b/cli/src/run.ts index 92d11818..70d82729 100644 --- a/cli/src/run.ts +++ b/cli/src/run.ts @@ -11,6 +11,14 @@ 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 { intro(colors.cyan('Welcome to VuePress and vuepress-theme-plume !')) diff --git a/cli/src/translate.ts b/cli/src/translate.ts index dfa0fd18..a840c571 100644 --- a/cli/src/translate.ts +++ b/cli/src/translate.ts @@ -6,6 +6,14 @@ interface Translate { 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' @@ -19,5 +27,21 @@ function createTranslate(lang?: Langs): Translate { 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 diff --git a/cli/src/types.ts b/cli/src/types.ts index 3332af9b..066f0f31 100644 --- a/cli/src/types.ts +++ b/cli/src/types.ts @@ -1,57 +1,276 @@ 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 = { 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 { - displayLang: string // cli display language + /** + * 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 } diff --git a/cli/src/utils/fs.ts b/cli/src/utils/fs.ts index 0be5b96a..38dffeb9 100644 --- a/cli/src/utils/fs.ts +++ b/cli/src/utils/fs.ts @@ -2,6 +2,14 @@ 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 { const filepaths = await fs.readdir(root, { recursive: true }) const files: File[] = [] @@ -18,6 +26,15 @@ export async function readFiles(root: string): Promise { 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, @@ -32,6 +49,14 @@ export async function writeFiles( } } +/** + * 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 = Record>(filepath: string): Promise { try { const content = await fs.readFile(filepath, 'utf-8') diff --git a/cli/src/utils/index.ts b/cli/src/utils/index.ts index d28afa05..0673b9fd 100644 --- a/cli/src/utils/index.ts +++ b/cli/src/utils/index.ts @@ -3,8 +3,24 @@ 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' diff --git a/plugins/plugin-md-power/src/node/container/align.ts b/plugins/plugin-md-power/src/node/container/align.ts index 205b0a6d..4997f5ae 100644 --- a/plugins/plugin-md-power/src/node/container/align.ts +++ b/plugins/plugin-md-power/src/node/container/align.ts @@ -3,6 +3,36 @@ import { parseRect } from '../utils/parseRect.js' import { resolveAttrs } from '../utils/resolveAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Flex container attributes + * + * Flex 容器属性 + */ +interface FlexContainerAttrs { + center?: boolean + wrap?: boolean + between?: boolean + around?: boolean + start?: boolean + end?: boolean + gap?: string + column?: boolean +} + +/** + * Align plugin - Enable text alignment containers + * + * 对齐插件 - 启用文本对齐容器 + * + * Syntax: + * - ::: left + * - ::: center + * - ::: right + * - ::: justify + * - ::: flex [options] + * + * @param md - Markdown instance / Markdown 实例 + */ export function alignPlugin(md: Markdown): void { const alignList = ['left', 'center', 'right', 'justify'] @@ -38,14 +68,3 @@ export function alignPlugin(md: Markdown): void { }, }) } - -interface FlexContainerAttrs { - center?: boolean - wrap?: boolean - between?: boolean - around?: boolean - start?: boolean - end?: boolean - gap?: string - column?: boolean -} diff --git a/plugins/plugin-md-power/src/node/container/card.ts b/plugins/plugin-md-power/src/node/container/card.ts index d3455b50..d79dbb92 100644 --- a/plugins/plugin-md-power/src/node/container/card.ts +++ b/plugins/plugin-md-power/src/node/container/card.ts @@ -3,16 +3,38 @@ import { resolveAttrs } from '../utils/resolveAttrs.js' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Card container attributes + * + * 卡片容器属性 + */ interface CardAttrs { title?: string icon?: string } +/** + * Card masonry container attributes + * + * 卡片瀑布流容器属性 + */ interface CardMasonryAttrs { cols?: number gap?: number } +/** + * Card plugin - Enable card containers + * + * 卡片插件 - 启用卡片容器 + * + * Syntax: + * - ::: card title="xxx" icon="xxx" + * - ::: card-grid + * - ::: card-masonry cols="2" gap="10" + * + * @param md - Markdown instance / Markdown 实例 + */ export function cardPlugin(md: Markdown): void { /** * ::: card title="xxx" icon="xxx" diff --git a/plugins/plugin-md-power/src/node/container/chat.ts b/plugins/plugin-md-power/src/node/container/chat.ts index db967016..7e7220ee 100644 --- a/plugins/plugin-md-power/src/node/container/chat.ts +++ b/plugins/plugin-md-power/src/node/container/chat.ts @@ -1,4 +1,10 @@ /** + * Chat container plugin + * + * 聊天容器插件 + * + * Syntax: + * ```md * ::: chat * {:time} * @@ -8,12 +14,18 @@ * {.} * xxxxxx * ::: + * ``` */ import type { PluginSimple } from 'markdown-it' import type { Markdown, MarkdownEnv } from 'vuepress/markdown' import { cleanMarkdownEnv } from '../utils/cleanMarkdownEnv.js' import { createContainerSyntaxPlugin } from './createContainer.js' +/** + * Chat message structure + * + * 聊天消息结构 + */ interface ChatMessage { sender: 'user' | 'self' date: string @@ -21,6 +33,16 @@ interface ChatMessage { content: string[] } +/** + * Render chat messages to HTML + * + * 渲染聊天消息为 HTML + * + * @param md - Markdown instance / Markdown 实例 + * @param env - Markdown environment / Markdown 环境 + * @param messages - Chat messages / 聊天消息数组 + * @returns Rendered HTML / 渲染后的 HTML + */ function chatMessagesRender(md: Markdown, env: MarkdownEnv, messages: ChatMessage[]): string { let currentDate = '' return messages.map(({ sender, username, date, content }) => { @@ -43,7 +65,14 @@ function chatMessagesRender(md: Markdown, env: MarkdownEnv, messages: ChatMessag .join('\n') } -// 解析聊天内容的核心逻辑 +/** + * Parse chat content to messages + * + * 解析聊天内容为消息数组 + * + * @param content - Raw chat content / 原始聊天内容 + * @returns Parsed chat messages / 解析后的聊天消息 + */ function parseChatContent(content: string): ChatMessage[] { const lines = content.split('\n') const messages: ChatMessage[] = [] @@ -53,13 +82,13 @@ function parseChatContent(content: string): ChatMessage[] { for (const line of lines) { const lineStr = line.trim() - // 解析时间标记 + // Parse time marker if (lineStr.startsWith('{:') && lineStr.endsWith('}')) { currentDate = lineStr.slice(2, -1).trim() continue } - // 解析用户 / 本人标记 + // Parse user/self marker if (lineStr.startsWith('{') && lineStr.endsWith('}')) { const username = lineStr.slice(1, -1).trim() message = { @@ -72,7 +101,7 @@ function parseChatContent(content: string): ChatMessage[] { continue } - // 收集消息内容 + // Collect message content if (message?.sender) { message.content.push(line) } @@ -80,6 +109,13 @@ function parseChatContent(content: string): ChatMessage[] { return messages } +/** + * Chat plugin - Enable chat container + * + * 聊天插件 - 启用聊天容器 + * + * @param md - Markdown-it instance / Markdown-it 实例 + */ export const chatPlugin: PluginSimple = md => createContainerSyntaxPlugin( md, 'chat', diff --git a/plugins/plugin-md-power/src/node/container/codeTabs.ts b/plugins/plugin-md-power/src/node/container/codeTabs.ts index 7b114dd4..542503d8 100644 --- a/plugins/plugin-md-power/src/node/container/codeTabs.ts +++ b/plugins/plugin-md-power/src/node/container/codeTabs.ts @@ -6,6 +6,14 @@ import { definitions, getFileIconName, getFileIconTypeFromExtension } from '../f import { cleanMarkdownEnv } from '../utils/cleanMarkdownEnv.js' import { stringifyProp } from '../utils/stringifyProp.js' +/** + * Create code tab icon getter function + * + * 创建代码标签页图标获取函数 + * + * @param options - Code tabs options / 代码标签页选项 + * @returns Icon getter function / 图标获取函数 + */ export function createCodeTabIconGetter( options: CodeTabsOptions = {}, ): (filename: string) => string | void { @@ -35,6 +43,14 @@ export function createCodeTabIconGetter( } } +/** + * Code tabs plugin - Enable code tabs container + * + * 代码标签页插件 - 启用代码标签页容器 + * + * @param md - Markdown-it instance / Markdown-it 实例 + * @param options - Code tabs options / 代码标签页选项 + */ export const codeTabs: PluginWithOptions = (md, options: CodeTabsOptions = {}) => { const getIcon = createCodeTabIconGetter(options) diff --git a/plugins/plugin-md-power/src/node/container/codeTree.ts b/plugins/plugin-md-power/src/node/container/codeTree.ts index 59a46a91..36f355a2 100644 --- a/plugins/plugin-md-power/src/node/container/codeTree.ts +++ b/plugins/plugin-md-power/src/node/container/codeTree.ts @@ -1,5 +1,5 @@ /** - * @module CodeTree + * Code tree container plugin * * code-tree 容器 * ````md @@ -10,7 +10,7 @@ * ::: * ```` * - * embed syntax + * Embed syntax * * `@[code-tree title="Project Name" height="400px" entry="filepath"](dir_path)` */ @@ -32,6 +32,11 @@ import { resolveAttr, resolveAttrs } from '../utils/resolveAttrs.js' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Unsupported file types for code tree + * + * 代码树不支持的文件类型 + */ const UNSUPPORTED_FILE_TYPES = [ /* image */ 'jpg', @@ -62,26 +67,36 @@ const UNSUPPORTED_FILE_TYPES = [ ] /** + * Code tree metadata + * * code-tree 容器元信息 */ interface CodeTreeMeta { title?: string /** + * File icon type + * * 文件图标类型 */ icon?: FileTreeIconMode /** + * Code tree container height + * * 代码树容器高度 */ height?: string /** + * Entry file, opened by default + * * 入口文件,默认打开 */ entry?: string } /** + * File tree node type + * * 文件树节点类型 */ interface FileTreeNode { @@ -92,9 +107,12 @@ interface FileTreeNode { } /** + * Parse file paths to file tree node structure + * * 将文件路径数组解析为文件树节点结构 - * @param files 文件路径数组 - * @returns 文件树节点数组 + * + * @param files - File path array / 文件路径数组 + * @returns File tree node array / 文件树节点数组 */ function parseFileNodes(files: string[]): FileTreeNode[] { const nodes: FileTreeNode[] = [] @@ -123,13 +141,18 @@ function parseFileNodes(files: string[]): FileTreeNode[] { } /** + * Code tree plugin - Register code-tree container and embed syntax + * * 注册 code-tree 容器和嵌入语法的 markdown 插件 - * @param md markdown-it 实例 - * @param app vuepress app 实例 - * @param options code-tree 配置项 + * + * @param md - Markdown-it instance / markdown-it 实例 + * @param app - VuePress app instance / vuepress app 实例 + * @param options - Code tree options / code-tree 配置项 */ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions = {}): void { /** + * Get file or folder icon + * * 获取文件或文件夹的图标 */ const getIcon = (filename: string, type: 'folder' | 'file', mode?: FileTreeIconMode): string => { @@ -140,6 +163,8 @@ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions } /** + * Render file tree nodes to component string + * * 渲染文件树节点为组件字符串 */ function renderFileTree(nodes: FileTreeNode[], mode?: FileTreeIconMode): string { @@ -159,10 +184,10 @@ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions .join('\n') } - // 注册 ::: code-tree 容器 + // Register ::: code-tree container createContainerPlugin(md, 'code-tree', { before: (info, tokens, index) => { - // 收集 code-tree 容器内的文件名和激活文件 + // Collect filenames and active file in code-tree container const files: string[] = [] let activeFile: string | undefined for ( @@ -197,7 +222,7 @@ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions after: () => '', }) - // 注册 @[code-tree](dir) 语法 + // Register @[code-tree](dir) syntax createEmbedRuleBlock(md, { type: 'code-tree', syntaxPattern: /^@\[code-tree([^\]]*)\]\(([^)]*)\)/, @@ -213,10 +238,10 @@ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions } }, content: ({ dir, icon, ...props }, _, env) => { - // codeTreeFiles 用于页面依赖收集 + // codeTreeFiles for page dependency collection const codeTreeFiles = ((env as any).codeTreeFiles ??= []) as string[] const root = findFile(app, env, dir) - // 获取目录下所有文件 + // Get all files in directory const files = tinyglobby.globSync('**/*', { cwd: root, onlyFiles: true, @@ -229,7 +254,7 @@ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions }) props.entryFile ||= files[0] - // 生成所有文件的代码块内容 + // Generate code block content for all files const codeContent = files.map((file) => { const ext = path.extname(file).slice(1) if (UNSUPPORTED_FILE_TYPES.includes(ext)) { @@ -250,8 +275,11 @@ export function codeTreePlugin(md: Markdown, app: App, options: CodeTreeOptions } /** + * Extend page dependencies with codeTreeFiles + * * 扩展页面依赖,将 codeTreeFiles 添加到页面依赖中 - * @param page vuepress 页面对象 + * + * @param page - VuePress page object / vuepress 页面对象 */ export function extendsPageWithCodeTree(page: Page): void { const markdownEnv = page.markdownEnv diff --git a/plugins/plugin-md-power/src/node/container/collapse.ts b/plugins/plugin-md-power/src/node/container/collapse.ts index cad58761..8042bac2 100644 --- a/plugins/plugin-md-power/src/node/container/collapse.ts +++ b/plugins/plugin-md-power/src/node/container/collapse.ts @@ -1,10 +1,17 @@ /** + * Collapse container plugin + * + * 折叠面板容器插件 + * + * Syntax: + * ```md * ::: collapse accordion * - + 标题 * 内容 * - - 标题 * 内容 * ::: + * ``` */ import type Token from 'markdown-it/lib/token.mjs' import type { Markdown } from 'vuepress/markdown' @@ -12,16 +19,33 @@ import { resolveAttrs } from '../utils/resolveAttrs.js' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Collapse metadata + * + * 折叠面板元数据 + */ interface CollapseMeta { accordion?: boolean expand?: boolean } +/** + * Collapse item metadata + * + * 折叠面板项元数据 + */ interface CollapseItemMeta { expand?: boolean index?: number } +/** + * Collapse plugin - Enable collapse container + * + * 折叠面板插件 - 启用折叠面板容器 + * + * @param md - Markdown instance / Markdown 实例 + */ export function collapsePlugin(md: Markdown): void { createContainerPlugin(md, 'collapse', { before: (info, tokens, index) => { @@ -43,9 +67,19 @@ export function collapsePlugin(md: Markdown): void { md.renderer.rules.collapse_item_title_close = () => '' } +/** + * Parse collapse tokens + * + * 解析折叠面板令牌 + * + * @param tokens - Token array / 令牌数组 + * @param index - Start index / 起始索引 + * @param attrs - Collapse metadata / 折叠面板元数据 + * @returns Default expanded index / 默认展开索引 + */ function parseCollapse(tokens: Token[], index: number, attrs: CollapseMeta): number | void { - const listStack: number[] = [] // 记录列表嵌套深度 - let idx = -1 // 记录当前列表项下标 + const listStack: number[] = [] // Track list nesting depth + let idx = -1 // Current list item index let defaultIndex: number | undefined let hashExpand = false for (let i = index + 1; i < tokens.length; i++) { @@ -53,9 +87,9 @@ function parseCollapse(tokens: Token[], index: number, attrs: CollapseMeta): num if (token.type === 'container_collapse_close') { break } - // 列表层级追踪 + // Track list level if (token.type === 'bullet_list_open' || token.type === 'ordered_list_open') { - listStack.push(0) // 每个新列表初始层级为0 + listStack.push(0) // Each new list starts at level 0 if (listStack.length === 1) token.hidden = true } @@ -66,7 +100,7 @@ function parseCollapse(tokens: Token[], index: number, attrs: CollapseMeta): num } else if (token.type === 'list_item_open') { const currentLevel = listStack.length - // 仅处理根级列表项(层级1) + // Only process root level list items (level 1) if (currentLevel === 1) { token.type = 'collapse_item_open' tokens[i + 1].type = 'collapse_item_title_open' diff --git a/plugins/plugin-md-power/src/node/container/createContainer.ts b/plugins/plugin-md-power/src/node/container/createContainer.ts index e0c5c3fc..5352ac0c 100644 --- a/plugins/plugin-md-power/src/node/container/createContainer.ts +++ b/plugins/plugin-md-power/src/node/container/createContainer.ts @@ -5,59 +5,73 @@ import container from 'markdown-it-container' import { resolveAttrs } from '../utils/resolveAttrs.js' /** - * RenderRuleParams 类型用于获取 RenderRule 的参数类型。 + * Type for getting RenderRule parameters + * + * 获取 RenderRule 参数的类型 */ type RenderRuleParams = Parameters extends [...infer Args, infer _] ? Args : never /** - * 自定义容器的配置项。 - * - before: 渲染容器起始标签时的回调 - * - after: 渲染容器结束标签时的回调 + * Container options + * + * 自定义容器的配置项 */ export interface ContainerOptions { + /** + * Callback for rendering container opening tag + * + * 渲染容器起始标签时的回调 + */ before?: (info: string, ...args: RenderRuleParams) => string + /** + * Callback for rendering container closing tag + * + * 渲染容器结束标签时的回调 + */ after?: (info: string, ...args: RenderRuleParams) => string } /** - * 创建 markdown-it 的自定义容器插件。 + * Create markdown-it custom container plugin * - * @param md markdown-it 实例 - * @param type 容器类型(如 'tip', 'warning' 等) - * @param options 可选的 before/after 渲染钩子 - * @param options.before 渲染容器起始标签时的回调函数 - * @param options.after 渲染容器结束标签时的回调函数 + * 创建 markdown-it 的自定义容器插件 + * + * @param md - Markdown-it instance / Markdown-it 实例 + * @param type - Container type (e.g., 'tip', 'warning') / 容器类型(如 'tip', 'warning' 等) + * @param options - Optional before/after render hooks / 可选的 before/after 渲染钩子 + * @param options.before - Callback for rendering container opening tag / 渲染容器起始标签时的回调函数 + * @param options.after - Callback for rendering container closing tag / 渲染容器结束标签时的回调函数 */ export function createContainerPlugin( md: Markdown, type: string, { before, after }: ContainerOptions = {}, ): void { - // 自定义渲染规则 + // Custom render rule const render: RenderRule = (tokens, index, options, env): string => { const token = tokens[index] - // 提取 ::: 后的 info 信息 + // Extract info after ::: const info = token.info.trim().slice(type.length).trim() || '' if (token.nesting === 1) { - // 容器起始标签 + // Container opening tag return before?.(info, tokens, index, options, env) ?? `
` } else { - // 容器结束标签 + // Container closing tag return after?.(info, tokens, index, options, env) ?? '
' } } - // 注册 markdown-it-container 插件 + // Register markdown-it-container plugin md.use(container, type, { render }) } /** - * 创建一个自定义的容器规则,内容不会交给 markdown-it 处理。 - * 需要自定义 content 的处理逻辑 + * Create a custom container rule where content is not processed by markdown-it + * Requires custom content processing logic * ```md * ::: type - * xxxx <-- content: 这部分的内容不会交给 markdown-it 处理 + * xxxx <-- content: this part will not be processed by markdown-it * ::: * ``` * @@ -68,6 +82,10 @@ export function createContainerPlugin( * return `
${meta.title} | ${content}
` * }) * ``` + * + * @param md - Markdown-it instance / Markdown-it 实例 + * @param type - Container type / 容器类型 + * @param render - Custom render rule / 自定义渲染规则 */ export function createContainerSyntaxPlugin( md: Markdown, @@ -78,12 +96,15 @@ export function createContainerSyntaxPlugin( const markerMinLen = 3 /** - * 自定义容器的 block 规则定义。 - * @param state 当前 block 状态 - * @param startLine 起始行 - * @param endLine 结束行 - * @param silent 是否为静默模式 - * @returns 是否匹配到自定义容器 + * Custom container block rule definition + * + * 自定义容器的 block 规则定义 + * + * @param state - Current block state / 当前 block 状态 + * @param startLine - Start line / 起始行 + * @param endLine - End line / 结束行 + * @param silent - Silent mode / 是否为静默模式 + * @returns Whether matched / 是否匹配到自定义容器 */ function defineContainer(state: StateBlock, startLine: number, endLine: number, silent: boolean): boolean { const start = state.bMarks[startLine] + state.tShift[startLine] @@ -91,11 +112,11 @@ export function createContainerSyntaxPlugin( let pos = start // check marker - // 检查是否以指定的 maker(:)开头 + // Check if starts with specified maker (:) if (state.src[pos] !== maker) return false - // 检查 marker 长度是否满足要求 + // Check if marker length meets requirements for (pos = start + 1; pos <= max; pos++) { if (state.src[pos] !== maker) break @@ -108,7 +129,7 @@ export function createContainerSyntaxPlugin( const info = state.src.slice(pos, max).trim() // ::: type - // 检查 info 是否以 type 开头 + // Check if info starts with type if (!info.startsWith(type)) return false @@ -118,7 +139,7 @@ export function createContainerSyntaxPlugin( let line = startLine let content = '' - // 收集容器内容,直到遇到结束的 marker + // Collect container content until end marker while (++line < endLine) { if (state.src.slice(state.bMarks[line], state.eMarks[line]).trim() === markup) { break @@ -127,7 +148,7 @@ export function createContainerSyntaxPlugin( content += `${state.src.slice(state.bMarks[line], state.eMarks[line])}\n` } - // 创建 token,保存内容和属性 + // Create token, save content and attributes const token = state.push(`${type}_container`, '', 0) token.meta = resolveAttrs(info.slice(type.length)).attrs token.content = content @@ -139,13 +160,13 @@ export function createContainerSyntaxPlugin( return true } - // 默认渲染函数 + // Default render function const defaultRender: RenderRule = (tokens, index) => { const { content } = tokens[index] return `
${content}
` } - // 注册 block 规则和渲染规则 + // Register block rule and render rule md.block.ruler.before('fence', `${type}_definition`, defineContainer) md.renderer.rules[`${type}_container`] = render ?? defaultRender } diff --git a/plugins/plugin-md-power/src/node/container/demoWrapper.ts b/plugins/plugin-md-power/src/node/container/demoWrapper.ts index 446d3558..928829d8 100644 --- a/plugins/plugin-md-power/src/node/container/demoWrapper.ts +++ b/plugins/plugin-md-power/src/node/container/demoWrapper.ts @@ -2,6 +2,11 @@ import type { Markdown } from 'vuepress/markdown' import { resolveAttrs } from '.././utils/resolveAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Demo wrapper attributes + * + * 演示包装器属性 + */ interface DemoWrapperAttrs { title?: string img?: string @@ -10,8 +15,14 @@ interface DemoWrapperAttrs { } /** - * :::demo-wrapper img no-padding title="xxx" height="100px" - * ::: + * Demo wrapper plugin - Enable demo wrapper container + * + * 演示包装器插件 - 启用演示包装器容器 + * + * Syntax: :::demo-wrapper img no-padding title="xxx" height="100px" + * 语法::::demo-wrapper img no-padding title="xxx" height="100px" + * + * @param md - Markdown instance / Markdown 实例 */ export function demoWrapperPlugin(md: Markdown): void { createContainerPlugin(md, 'demo-wrapper', { diff --git a/plugins/plugin-md-power/src/node/container/encrypt.ts b/plugins/plugin-md-power/src/node/container/encrypt.ts index 51fdd07b..2c04ea22 100644 --- a/plugins/plugin-md-power/src/node/container/encrypt.ts +++ b/plugins/plugin-md-power/src/node/container/encrypt.ts @@ -10,16 +10,35 @@ import { encryptContent } from '../utils/encryptContent' import { logger } from '../utils/logger' import { createContainerSyntaxPlugin } from './createContainer' +/** + * Encryption options + * + * 加密选项 + */ interface EncryptOptions { password: string salt: Uint8Array iv: Uint8Array } +/** + * Encrypt plugin - Enable encrypted content container + * + * 加密插件 - 启用加密内容容器 + * + * @param app - VuePress app / VuePress 应用 + * @param md - Markdown instance / Markdown 实例 + * @param options - Encrypt snippet options / 加密片段选项 + */ export function encryptPlugin(app: App, md: Markdown, options: EncryptSnippetOptions): void { const encrypted: Set = new Set() const entryFile = 'internal/encrypt-snippets/index.js' + /** + * Write encrypted content to temp file + * + * 将加密内容写入临时文件 + */ const writeTemp = async ( hash: string, content: string, @@ -29,6 +48,11 @@ export function encryptPlugin(app: App, md: Markdown, options: EncryptSnippetOpt await app.writeTemp(`internal/encrypt-snippets/${hash}.js`, `export default ${JSON.stringify(encrypted)}`) } + /** + * Write entry file with all encrypted snippets + * + * 写入包含所有加密片段的入口文件 + */ const writeEntry = debounce(150, async () => { let content = `export default {\n` for (const hash of encrypted) { @@ -39,11 +63,17 @@ export function encryptPlugin(app: App, md: Markdown, options: EncryptSnippetOpt }) if (!fs.existsSync(app.dir.temp(entryFile))) { - // 初始化 + // Initialize app.writeTemp(entryFile, 'export default {}\n') } const localKeys = Object.keys(app.options.locales || {}).filter(key => key !== '/') + + /** + * Get locale from relative path + * + * 从相对路径获取本地化 + */ const getLocale = (relativePath: string) => { const relative = ensureLeadingSlash(relativePath) return localKeys.find(key => relative.startsWith(key)) || '/' diff --git a/plugins/plugin-md-power/src/node/container/field.ts b/plugins/plugin-md-power/src/node/container/field.ts index 82f4456a..1edd833a 100644 --- a/plugins/plugin-md-power/src/node/container/field.ts +++ b/plugins/plugin-md-power/src/node/container/field.ts @@ -4,6 +4,11 @@ import { resolveAttrs } from '../utils/resolveAttrs.js' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Field attributes + * + * 字段属性 + */ interface FieldAttrs { name: string type?: string @@ -13,6 +18,16 @@ interface FieldAttrs { default?: string } +/** + * Field plugin - Enable field container for API documentation + * + * 字段插件 - 启用用于 API 文档的字段容器 + * + * Syntax: ::: field name="xxx" type="string" required + * 语法:::: field name="xxx" type="string" required + * + * @param md - Markdown instance / Markdown 实例 + */ export function fieldPlugin(md: Markdown): void { createContainerPlugin(md, 'field', { before: (info) => { diff --git a/plugins/plugin-md-power/src/node/container/fileTree.ts b/plugins/plugin-md-power/src/node/container/fileTree.ts index aa4300a2..01ec93fa 100644 --- a/plugins/plugin-md-power/src/node/container/fileTree.ts +++ b/plugins/plugin-md-power/src/node/container/fileTree.ts @@ -7,6 +7,8 @@ import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerSyntaxPlugin } from './createContainer.js' /** + * File tree node structure + * * 文件树节点结构 */ interface FileTreeNode extends FileTreeNodeProps { @@ -15,6 +17,8 @@ interface FileTreeNode extends FileTreeNodeProps { } /** + * File tree container attributes + * * 文件树容器属性 */ interface FileTreeAttrs { @@ -23,6 +27,8 @@ interface FileTreeAttrs { } /** + * File tree node props (for rendering component) + * * 文件树节点属性(用于渲染组件) */ export interface FileTreeNodeProps { @@ -36,25 +42,28 @@ export interface FileTreeNodeProps { } /** + * Parse raw file tree content to node tree structure + * * 解析原始文件树内容为节点树结构 - * @param content 文件树的原始文本内容 - * @returns 文件树节点数组 + * + * @param content - Raw file tree text content / 文件树的原始文本内容 + * @returns File tree node array / 文件树节点数组 */ export function parseFileTreeRawContent(content: string): FileTreeNode[] { const root: FileTreeNode = { level: -1, children: [] } as unknown as FileTreeNode const stack: FileTreeNode[] = [root] const lines = content.trimEnd().split('\n') - const spaceLength = lines[0].match(/^\s*/)?.[0].length ?? 0 // 去除行首空格/) + const spaceLength = lines[0].match(/^\s*/)?.[0].length ?? 0 // Remove leading spaces for (const line of lines) { const match = line.match(/^(\s*)-(.*)$/) if (!match) continue - const level = Math.floor((match[1].length - spaceLength) / 2) // 每两个空格为一个层级 + const level = Math.floor((match[1].length - spaceLength) / 2) // Two spaces per level const info = match[2].trim() - // 检索当前层级的父节点 + // Find parent node at current level while (stack.length > 0 && stack[stack.length - 1].level >= level) { stack.pop() } @@ -68,12 +77,20 @@ export function parseFileTreeRawContent(content: string): FileTreeNode[] { return root.children } +/** + * Regex for focus marker + * + * 高亮标记正则 + */ const RE_FOCUS = /^\*\*(.*)\*\*(?:$|\s+)/ /** + * Parse single node info string, extract filename, comment, type, etc. + * * 解析单个节点的 info 字符串,提取文件名、注释、类型等属性 - * @param info 节点描述字符串 - * @returns 文件树节点属性 + * + * @param info - Node description string / 节点描述字符串 + * @returns File tree node props / 文件树节点属性 */ export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps { let filename = '' @@ -83,7 +100,7 @@ export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps { let type: 'folder' | 'file' = 'file' let diff: 'add' | 'remove' | undefined - // 处理 diff 标记 + // Process diff marker if (info.startsWith('++')) { info = info.slice(2).trim() diff = 'add' @@ -93,14 +110,14 @@ export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps { diff = 'remove' } - // 处理高亮(focus)标记 + // Process focus marker info = info.replace(RE_FOCUS, (_, matched) => { filename = matched focus = true return '' }) - // 提取文件名和注释 + // Extract filename and comment if (filename === '' && !focus) { const spaceIndex = info.indexOf(' ') filename = info.slice(0, spaceIndex === -1 ? info.length : spaceIndex) @@ -109,7 +126,7 @@ export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps { comment = info.trim() - // 判断是否为文件夹 + // Determine if folder if (filename.endsWith('/')) { type = 'folder' expanded = false @@ -120,9 +137,13 @@ export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps { } /** + * File tree markdown plugin main function + * * 文件树 markdown 插件主函数 - * @param md markdown 实例 - * @param options 文件树渲染选项 + * + * @param md - Markdown instance / markdown 实例 + * @param options - File tree render options / 文件树渲染选项 + * @param locales - Locale data / 本地化数据 */ export function fileTreePlugin( md: Markdown, @@ -130,6 +151,8 @@ export function fileTreePlugin( locales: Record, ): void { /** + * Get file or folder icon + * * 获取文件或文件夹的图标 */ const getIcon = (filename: string, type: 'folder' | 'file', mode?: FileTreeIconMode): string => { @@ -140,6 +163,8 @@ export function fileTreePlugin( } /** + * Recursively render file tree nodes + * * 递归渲染文件树节点 */ const renderFileTree = (nodes: FileTreeNode[], meta: FileTreeAttrs): string => @@ -147,7 +172,7 @@ export function fileTreePlugin( const { level, children, filename, comment, focus, expanded, type, diff } = node const isOmit = filename === '…' || filename === '...' /* fallback */ - // 文件夹无子节点时补充省略号 + // Add ellipsis for folder without children if (children.length === 0 && type === 'folder') { children.push({ level: level + 1, children: [], filename: '…', type: 'file' } as unknown as FileTreeNode) } @@ -172,7 +197,7 @@ ${renderedIcon}${renderedComment}${children.length > 0 ? renderFileTree(children ` }).join('\n') - // 注册自定义容器语法插件 + // Register custom container syntax plugin return createContainerSyntaxPlugin( md, 'file-tree', @@ -192,6 +217,15 @@ ${renderedIcon}${renderedComment}${children.length > 0 ? renderFileTree(children ) } +/** + * Convert file tree to command line text format + * + * 将文件树转换为命令行文本格式 + * + * @param nodes - File tree nodes / 文件树节点 + * @param prefix - Line prefix / 行前缀 + * @returns CMD text / CMD 文本 + */ function fileTreeToCMDText(nodes: FileTreeNode[], prefix = ''): string { let content = prefix ? '' : '.\n' for (let i = 0, l = nodes.length; i < l; i++) { diff --git a/plugins/plugin-md-power/src/node/container/index.ts b/plugins/plugin-md-power/src/node/container/index.ts index e0bd9c00..f993df67 100644 --- a/plugins/plugin-md-power/src/node/container/index.ts +++ b/plugins/plugin-md-power/src/node/container/index.ts @@ -21,6 +21,16 @@ import { tablePlugin } from './table.js' import { tabs } from './tabs.js' import { timelinePlugin } from './timeline.js' +/** + * Container plugin - Register all container plugins + * + * 容器插件 - 注册所有容器插件 + * + * @param app - VuePress app / VuePress 应用 + * @param md - Markdown instance / Markdown 实例 + * @param options - Plugin options / 插件选项 + * @param locales - Locale configuration / 本地化配置 + */ export async function containerPlugin( app: App, md: Markdown, diff --git a/plugins/plugin-md-power/src/node/container/langRepl.ts b/plugins/plugin-md-power/src/node/container/langRepl.ts index cd59e5f9..2c96e221 100644 --- a/plugins/plugin-md-power/src/node/container/langRepl.ts +++ b/plugins/plugin-md-power/src/node/container/langRepl.ts @@ -8,11 +8,32 @@ import { resolveAttrs } from '../utils/resolveAttrs.js' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Code REPL metadata + * + * 代码 REPL 元数据 + */ interface CodeReplMeta { editable?: boolean title?: string } +/** + * Language REPL plugin - Enable interactive code playgrounds + * + * 语言 REPL 插件 - 启用交互式代码游乐场 + * + * Supports: kotlin, go, rust, python + * + * @param app - VuePress app / VuePress 应用 + * @param md - Markdown-it instance / Markdown-it 实例 + * @param options - REPL options / REPL 选项 + * @param options.theme - Editor theme / 编辑器主题 + * @param options.go - Enable Go playground / 启用 Go 游乐场 + * @param options.kotlin - Enable Kotlin playground / 启用 Kotlin 游乐场 + * @param options.rust - Enable Rust playground / 启用 Rust 游乐场 + * @param options.python - Enable Python playground / 启用 Python 游乐场 + */ export async function langReplPlugin(app: App, md: markdownIt, { theme, go = false, @@ -20,6 +41,11 @@ export async function langReplPlugin(app: App, md: markdownIt, { rust = false, python = false, }: ReplOptions): Promise { + /** + * Create container for specific language + * + * 为特定语言创建容器 + */ const container = (lang: string): void => createContainerPlugin(md, `${lang}-repl`, { before(info) { const { attrs } = resolveAttrs(info) @@ -85,6 +111,14 @@ export async function langReplPlugin(app: App, md: markdownIt, { ) } +/** + * Read JSON file + * + * 读取 JSON 文件 + * + * @param file - File path / 文件路径 + * @returns Parsed JSON / 解析后的 JSON + */ async function read(file: string): Promise { try { const content = await fs.readFile(file, 'utf-8') diff --git a/plugins/plugin-md-power/src/node/container/npmTo.ts b/plugins/plugin-md-power/src/node/container/npmTo.ts index 0a9772e0..0c92fd0f 100644 --- a/plugins/plugin-md-power/src/node/container/npmTo.ts +++ b/plugins/plugin-md-power/src/node/container/npmTo.ts @@ -1,4 +1,6 @@ /** + * npm-to container plugin + * * 只写一个 npm 代码块,自动转为 代码块分组 [npm, pnpm, yarn, bun, deno] * * ::: npm-to @@ -34,7 +36,12 @@ import { createContainerPlugin } from './createContainer.js' import { ALLOW_LIST, BOOL_FLAGS, DEFAULT_TABS, MANAGERS_CONFIG } from './npmToPreset.js' /** + * npm-to plugin - Convert npm commands to multiple package manager commands + * * 注册 npm-to 容器插件,将 npm 代码块自动转换为多包管理器命令分组 + * + * @param md - Markdown instance / Markdown 实例 + * @param options - npm-to options / npm-to 选项 */ export function npmToPlugins(md: Markdown, options: NpmToOptions = {}): void { const opt = isArray(options) ? { tabs: options } : options @@ -50,14 +57,14 @@ export function npmToPlugins(md: Markdown, options: NpmToOptions = {}): void { token.hidden = true token.type = 'text' token.content = '' - // 拆分命令行内容,转换为多包管理器命令 + // Split command line content, convert to multiple package manager commands const lines = content.split(/(\n|\s*&&\s*)/) return md.render( resolveNpmTo(lines, token.info.trim(), idx, tabs), cleanMarkdownEnv(env), ) } - // 非法容器警告 + // Invalid container warning logger.warn('npm-to', `Invalid npm-to container in ${colors.gray(env.filePathRelative || env.filePath)}`) return '' }, @@ -66,11 +73,15 @@ export function npmToPlugins(md: Markdown, options: NpmToOptions = {}): void { } /** + * Convert npm commands to package manager command groups + * * 将 npm 命令转换为各包管理器命令分组 - * @param lines 命令行数组 - * @param info 代码块类型 - * @param idx token 索引 - * @param tabs 需要支持的包管理器 + * + * @param lines - Command line array / 命令行数组 + * @param info - Code block type / 代码块类型 + * @param idx - Token index / token 索引 + * @param tabs - Package managers to support / 需要支持的包管理器 + * @returns code-tabs formatted string / code-tabs 格式字符串 */ function resolveNpmTo(lines: string[], info: string, idx: number, tabs: NpmToPackageManager[]): string { tabs = validateTabs(tabs) @@ -81,7 +92,7 @@ function resolveNpmTo(lines: string[], info: string, idx: number, tabs: NpmToPac for (const line of lines) { const config = findConfig(line) if (config && config[tab]) { - // 解析并替换命令参数 + // Parse and replace command arguments const parsed = (map[line] ??= parseLine(line)) as LineParsed const { cli, flags } = config[tab] as CommandConfigItem @@ -105,14 +116,19 @@ function resolveNpmTo(lines: string[], info: string, idx: number, tabs: NpmToPac newLines.push(line) } } - // 拼接为 code-tabs 格式 + // Concatenate as code-tabs format res.push(`@tab ${tab}\n\`\`\`${info}\n${newLines.join('')}\n\`\`\``) } return `:::code-tabs#npm-to-${tabs.join('-')}\n${res.join('\n')}\n:::` } /** + * Find package manager config by command line content + * * 根据命令行内容查找对应的包管理器配置 + * + * @param line - Command line / 命令行 + * @returns Command config / 命令配置 */ function findConfig(line: string): CommandConfig | undefined { for (const { pattern, ...config } of Object.values(MANAGERS_CONFIG)) { @@ -124,7 +140,12 @@ function findConfig(line: string): CommandConfig | undefined { } /** + * Validate tabs, return allowed package manager list + * * 校验 tabs 合法性,返回允许的包管理器列表 + * + * @param tabs - Package managers / 包管理器 + * @returns Validated tabs / 验证后的标签 */ function validateTabs(tabs: NpmToPackageManager[]): NpmToPackageManager[] { tabs = tabs.filter(tab => ALLOW_LIST.includes(tab)) @@ -135,20 +156,32 @@ function validateTabs(tabs: NpmToPackageManager[]): NpmToPackageManager[] { } /** + * Command line parse result type + * * 命令行解析结果类型 */ interface LineParsed { - env: string // 环境变量前缀 - cli: string // 命令行工具(npm/npx ...) - cmd: string // 命令/脚本名 - args?: string // 参数 - scriptArgs?: string // 脚本参数 + env: string // Environment variable prefix / 环境变量前缀 + cli: string // CLI tool (npm/npx ...) / 命令行工具 + cmd: string // Command/script name / 命令/脚本名 + args?: string // Arguments / 参数 + scriptArgs?: string // Script arguments / 脚本参数 } +/** + * Regex for parsing npm/npx commands + * + * 解析 npm/npx 命令的正则 + */ const LINE_REG = /(.*)(npm|npx)\s+(.*)/ /** + * Parse a line of npm/npx command, split env, command, args, etc. + * * 解析一行 npm/npx 命令,拆分出环境变量、命令、参数等 + * + * @param line - Command line / 命令行 + * @returns Parsed result / 解析结果 */ export function parseLine(line: string): false | LineParsed { const match = line.match(LINE_REG) @@ -177,7 +210,12 @@ export function parseLine(line: string): false | LineParsed { } /** + * Parse npm command arguments, distinguish command, args, script args + * * 解析 npm 命令参数,区分命令、参数、脚本参数 + * + * @param line - Arguments line / 参数字符串 + * @returns Parsed args / 解析后的参数 */ function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: string } { line = line?.trim() @@ -186,7 +224,7 @@ function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: str let cmd = '' let args = '' if (npmArgs[0] !== '-') { - // 处理命令和参数 + // Process command and args if (npmArgs[0] === '"' || npmArgs[0] === '\'') { const idx = npmArgs.slice(1).indexOf(npmArgs[0]) cmd = npmArgs.slice(0, idx + 2) @@ -204,7 +242,7 @@ function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: str } } else { - // 处理仅有参数的情况 + // Process args only let newLine = '' let value = '' let isQuote = false diff --git a/plugins/plugin-md-power/src/node/container/npmToPreset.ts b/plugins/plugin-md-power/src/node/container/npmToPreset.ts index b8d3aef9..4a66f7d2 100644 --- a/plugins/plugin-md-power/src/node/container/npmToPreset.ts +++ b/plugins/plugin-md-power/src/node/container/npmToPreset.ts @@ -1,22 +1,62 @@ import type { NpmToPackageManager } from '../../shared/index.js' +/** + * Package command types + * + * 包命令类型 + */ export type PackageCommand = 'install' | 'add' | 'remove' | 'run' | 'create' | 'init' | 'npx' | 'ci' +/** + * Command config item + * + * 命令配置项 + */ export interface CommandConfigItem { cli: string flags?: Record } +/** + * Command config for package managers (excluding npm) + * + * 包管理器的命令配置(不包括 npm) + */ export type CommandConfig = Record, CommandConfigItem | false> +/** + * Command configs for all package commands + * + * 所有包命令的配置 + */ export type CommandConfigs = Record +/** + * Allowed package managers list + * + * 允许的包管理器列表 + */ export const ALLOW_LIST = ['npm', 'pnpm', 'yarn', 'bun', 'deno'] as const +/** + * Boolean flags for npm commands + * + * npm 命令的布尔标志 + */ export const BOOL_FLAGS: string[] = ['--no-save', '-B', '--save-bundle', '--save-dev', '-D', '--save-prod', '-P', '--save-peer', '-O', '--save-optional', '-E', '--save-exact', '-y', '--yes', '-g', '--global'] +/** + * Default tabs to display + * + * 默认显示的标签 + */ export const DEFAULT_TABS: NpmToPackageManager[] = ['npm', 'pnpm', 'yarn'] +/** + * Package manager configurations + * + * 包管理器配置 + */ export const MANAGERS_CONFIG: CommandConfigs = { install: { pattern: /(?:^|\s)npm\s+(?:install|i)$/, diff --git a/plugins/plugin-md-power/src/node/container/steps.ts b/plugins/plugin-md-power/src/node/container/steps.ts index 112b4383..33e15227 100644 --- a/plugins/plugin-md-power/src/node/container/steps.ts +++ b/plugins/plugin-md-power/src/node/container/steps.ts @@ -2,6 +2,12 @@ import type { Markdown } from 'vuepress/markdown' import { createContainerPlugin } from './createContainer.js' /** + * Steps container plugin + * + * 步骤容器插件 + * + * Syntax: + * ```md * :::steps * 1. 步骤 1 * xxx @@ -9,6 +15,9 @@ import { createContainerPlugin } from './createContainer.js' * xxx * 3. ... * ::: + * ``` + * + * @param md - Markdown instance / Markdown 实例 */ export function stepsPlugin(md: Markdown): void { createContainerPlugin(md, 'steps', { diff --git a/plugins/plugin-md-power/src/node/container/table.ts b/plugins/plugin-md-power/src/node/container/table.ts index af8448e3..f8ca07d0 100644 --- a/plugins/plugin-md-power/src/node/container/table.ts +++ b/plugins/plugin-md-power/src/node/container/table.ts @@ -4,19 +4,30 @@ import { encodeData } from '@vuepress/helper' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerSyntaxPlugin } from './createContainer.js' +/** + * Table container attributes + * + * 表格容器属性 + */ export interface TableContainerAttrs extends TableContainerOptions { /** + * Table title + * * 表格标题 */ title?: string /** + * Highlighted rows + * * 表格高亮的行 * * @example hl-rows="warning:1,2,3;error:4,5,6" */ hlRows?: string /** + * Highlighted columns + * * 表格高亮的列 * * @example hl-cols="warning:1;error:2,3" @@ -24,6 +35,8 @@ export interface TableContainerAttrs extends TableContainerOptions { hlCols?: string /** + * Highlighted cells + * * 表格高亮的单元格 * * @example hl-cells="warning:(1,2)(2,3);error:(3,4)(4,5)" @@ -32,7 +45,9 @@ export interface TableContainerAttrs extends TableContainerOptions { } /** - * 在不破坏表格语法的前提下,通过容器语法将表格包裹起来,为表格提供增强功能 + * Table plugin - Wrap table with container for enhanced features + * + * 表格插件 - 通过容器语法将表格包裹起来,为表格提供增强功能 * * @example * ```md @@ -43,6 +58,9 @@ export interface TableContainerAttrs extends TableContainerOptions { * | xx | xx | xx | * ::: * ``` + * + * @param md - Markdown instance / Markdown 实例 + * @param options - Table container options / 表格容器选项 */ export function tablePlugin(md: Markdown, options: TableContainerOptions = {}): void { createContainerSyntaxPlugin(md, 'table', (tokens, index, opt, env) => { @@ -96,6 +114,14 @@ export function tablePlugin(md: Markdown, options: TableContainerOptions = {}): }) } +/** + * Parse highlight string + * + * 解析高亮字符串 + * + * @param hl - Highlight string / 高亮字符串 + * @returns Parsed highlight map / 解析后的高亮映射 + */ function parseHl(hl: string) { const res: Record = {} if (!hl) @@ -110,6 +136,14 @@ function parseHl(hl: string) { return res } +/** + * Parse highlight cells string + * + * 解析高亮单元格字符串 + * + * @param hl - Highlight cells string / 高亮单元格字符串 + * @returns Parsed highlight cells map / 解析后的高亮单元格映射 + */ function parseHlCells(hl: string) { const res: Record> = {} if (!hl) diff --git a/plugins/plugin-md-power/src/node/container/tabs.ts b/plugins/plugin-md-power/src/node/container/tabs.ts index 1ea9482e..b5472f4c 100644 --- a/plugins/plugin-md-power/src/node/container/tabs.ts +++ b/plugins/plugin-md-power/src/node/container/tabs.ts @@ -3,6 +3,23 @@ import { tab } from '@mdit/plugin-tab' import { cleanMarkdownEnv } from '../utils/cleanMarkdownEnv.js' import { stringifyProp } from '../utils/stringifyProp.js' +/** + * Tabs plugin - Enable tabs container + * + * 标签页插件 - 启用标签页容器 + * + * Syntax: + * ```md + * :::tabs + * @tab Tab 1 + * Content 1 + * @tab Tab 2 + * Content 2 + * ::: + * ``` + * + * @param md - Markdown-it instance / Markdown-it 实例 + */ export const tabs: PluginSimple = (md) => { tab(md, { name: 'tabs', diff --git a/plugins/plugin-md-power/src/node/container/timeline.ts b/plugins/plugin-md-power/src/node/container/timeline.ts index 5ec03819..2d78963b 100644 --- a/plugins/plugin-md-power/src/node/container/timeline.ts +++ b/plugins/plugin-md-power/src/node/container/timeline.ts @@ -1,4 +1,10 @@ /** + * Timeline container plugin + * + * 时间线容器插件 + * + * Syntax: + * ```md * ::: timeline * * - title @@ -11,6 +17,7 @@ * * content * ::: + * ``` */ import type Token from 'markdown-it/lib/token.mjs' import type { Markdown } from 'vuepress/markdown' @@ -19,6 +26,11 @@ import { resolveAttrs } from '.././utils/resolveAttrs.js' import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerPlugin } from './createContainer.js' +/** + * Timeline attributes + * + * 时间线属性 + */ export interface TimelineAttrs { horizontal?: boolean card?: boolean @@ -26,6 +38,11 @@ export interface TimelineAttrs { line?: string } +/** + * Timeline item metadata + * + * 时间线项元数据 + */ export interface TimelineItemMeta { time?: string type?: string @@ -36,10 +53,34 @@ export interface TimelineItemMeta { placement?: string } +/** + * Regex for matching attribute keys + * + * 匹配属性键的正则 + */ const RE_KEY = /(\w+)=\s*/ + +/** + * Regex for searching next attribute key + * + * 搜索下一个属性键的正则 + */ const RE_SEARCH_KEY = /\s+\w+=\s*|$/ + +/** + * Regex for cleaning quote values + * + * 清理引号值的正则 + */ const RE_CLEAN_VALUE = /(?["'])(.*?)(\k)/ +/** + * Timeline plugin - Enable timeline container + * + * 时间线插件 - 启用时间线容器 + * + * @param md - Markdown instance / Markdown 实例 + */ export function timelinePlugin(md: Markdown): void { createContainerPlugin(md, 'timeline', { before(info, tokens, index) { @@ -65,17 +106,25 @@ export function timelinePlugin(md: Markdown): void { md.renderer.rules.timeline_item_title_close = () => '' } +/** + * Parse timeline tokens + * + * 解析时间线令牌 + * + * @param tokens - Token array / 令牌数组 + * @param index - Start index / 起始索引 + */ function parseTimeline(tokens: Token[], index: number) { - const listStack: number[] = [] // 记录列表嵌套深度 + const listStack: number[] = [] // Track list nesting depth for (let i = index + 1; i < tokens.length; i++) { const token = tokens[i] if (token.type === 'container_timeline_close') { break } - // 列表层级追踪 + // Track list level if (token.type === 'bullet_list_open') { - listStack.push(0) // 每个新列表初始层级为0 + listStack.push(0) // Each new list starts at level 0 if (listStack.length === 1) token.hidden = true } @@ -86,7 +135,7 @@ function parseTimeline(tokens: Token[], index: number) { } else if (token.type === 'list_item_open') { const currentLevel = listStack.length - // 仅处理根级列表项(层级1) + // Only process root level list items (level 1) if (currentLevel === 1) { token.type = 'timeline_item_open' tokens[i + 1].type = 'timeline_item_title_open' @@ -94,9 +143,9 @@ function parseTimeline(tokens: Token[], index: number) { // - title // attrs - // 列表项 `-` 后面包括紧跟随的后续行均在 type=inline 的 token 中, 并作为 children + // List item `-` followed by subsequent lines are in type=inline token as children const inlineToken = tokens[i + 2] - // 找到最后一个 softbreak,最后一行作为 attrs 进行解析 + // Find last softbreak, last line as attrs const softbreakIndex = inlineToken.children!.findLastIndex( token => token.type === 'softbreak', ) @@ -121,39 +170,47 @@ function parseTimeline(tokens: Token[], index: number) { } } +/** + * Extract timeline attributes from raw text + * + * 从原始文本中提取时间线属性 + * + * @param rawText - Raw attribute text / 原始属性文本 + * @returns Timeline item metadata / 时间线项元数据 + */ export function extractTimelineAttributes(rawText: string): TimelineItemMeta { const attrKeys = ['time', 'type', 'icon', 'line', 'color', 'card', 'placement'] as const const attrs: TimelineItemMeta = {} let buffer = rawText.trim() while (buffer.length) { - // 匹配属性键 (支持大小写) + // Match attribute key (case insensitive) const keyMatch = buffer.match(RE_KEY) if (!keyMatch) { break } - // 提取可能的关键字 + // Extract possible keyword const matchedKey = keyMatch[1].toLowerCase() if (!attrKeys.includes(matchedKey as any)) { break } const keyStart = keyMatch.index! - // 跳过已匹配的 key: + // Skip matched key: const keyEnd = keyStart + keyMatch[0].length buffer = buffer.slice(keyEnd) - // 提取属性值 (到下一个属性或行尾) + // Extract attribute value (to next attribute or end of line) let valueEnd = buffer.search(RE_SEARCH_KEY) /* istanbul ignore if -- @preserve */ if (valueEnd === -1) valueEnd = buffer.length const value = buffer.slice(0, valueEnd).trim() - // 存储属性 + // Store attribute attrs[matchedKey as keyof TimelineItemMeta] = value.replace(RE_CLEAN_VALUE, '$2') - // 跳过已处理的值 + // Skip processed value buffer = buffer.slice(valueEnd) } diff --git a/plugins/plugin-md-power/src/node/embed/createEmbedRuleBlock.ts b/plugins/plugin-md-power/src/node/embed/createEmbedRuleBlock.ts index f749e78e..b9b5b61f 100644 --- a/plugins/plugin-md-power/src/node/embed/createEmbedRuleBlock.ts +++ b/plugins/plugin-md-power/src/node/embed/createEmbedRuleBlock.ts @@ -1,23 +1,78 @@ import type { RuleOptions } from 'markdown-it/lib/ruler.mjs' import type { Markdown, MarkdownEnv } from 'vuepress/markdown' +/** + * Embed rule block options + * + * 嵌入规则块选项 + * + * @typeParam Meta - Metadata type / 元数据类型 + */ export interface EmbedRuleBlockOptions> { /** - * @[type]() + * Embed type syntax: @[type]() + * + * 嵌入类型语法:@[type]() */ type: string /** - * token name + * Token name + * + * 令牌名称 */ name?: string + /** + * Name of the rule to insert before + * + * 要插入在其前面的规则名称 + */ beforeName?: string + /** + * Syntax pattern regular expression + * + * 语法模式正则表达式 + */ syntaxPattern: RegExp + /** + * Rule options + * + * 规则选项 + */ ruleOptions?: RuleOptions + /** + * Extract metadata from match + * + * 从匹配中提取元数据 + * + * @param match - RegExp match array / 正则表达式匹配数组 + * @returns Metadata object / 元数据对象 + */ meta: (match: RegExpMatchArray) => Meta + /** + * Generate content from metadata + * + * 从元数据生成内容 + * + * @param meta - Metadata / 元数据 + * @param content - Original content / 原始内容 + * @param env - Markdown environment / Markdown 环境 + * @returns Generated content / 生成的内容 + */ content: (meta: Meta, content: string, env: MarkdownEnv) => string } -// @[name]() +/** + * Create embed rule block + * + * 创建嵌入规则块 + * + * Syntax: @[name]() + * 语法:@[name]() + * + * @param md - Markdown instance / Markdown 实例 + * @param options - Embed rule block options / 嵌入规则块选项 + * @typeParam Meta - Metadata type / 元数据类型 + */ export function createEmbedRuleBlock = Record>( md: Markdown, { @@ -41,22 +96,26 @@ export function createEmbedRuleBlock = Record max) return false // check if it's matched the start + // 检查是否匹配开始 for (let i = 0; i < START_CODES.length; i += 1) { if (state.src.charCodeAt(pos + i) !== START_CODES[i]) return false } // check if it's matched the syntax + // 检查是否匹配语法 const content = state.src.slice(pos, max) const match = content.match(syntaxPattern) if (!match) return false // return true as we have matched the syntax + // 返回 true 表示已匹配语法 /* istanbul ignore if -- @preserve */ if (silent) return true diff --git a/plugins/plugin-md-power/src/node/embed/index.ts b/plugins/plugin-md-power/src/node/embed/index.ts index f7ab6446..2e6f2080 100644 --- a/plugins/plugin-md-power/src/node/embed/index.ts +++ b/plugins/plugin-md-power/src/node/embed/index.ts @@ -13,6 +13,14 @@ import { artPlayerPlugin } from './video/artPlayer.js' import { bilibiliPlugin } from './video/bilibili.js' import { youtubePlugin } from './video/youtube.js' +/** + * Embed syntax plugin - Register all embed syntax plugins + * + * 嵌入语法插件 - 注册所有嵌入语法插件 + * + * @param md - Markdown instance / Markdown 实例 + * @param options - Plugin options / 插件选项 + */ export function embedSyntaxPlugin(md: Markdown, options: MarkdownPowerPluginOptions): void { if (options.caniuse) { const caniuse = options.caniuse === true ? {} : options.caniuse diff --git a/plugins/plugin-md-power/src/node/enhance/docsTitle.ts b/plugins/plugin-md-power/src/node/enhance/docsTitle.ts index 47a77d11..da783008 100644 --- a/plugins/plugin-md-power/src/node/enhance/docsTitle.ts +++ b/plugins/plugin-md-power/src/node/enhance/docsTitle.ts @@ -1,10 +1,24 @@ import type { Markdown, MarkdownEnv } from 'vuepress/markdown' +/** + * Regular expression for matching h1 heading + * + * 匹配 h1 标题的正则表达式 + */ const REG_HEADING = /^#\s*?([^#\s].*)?\n/ /** - * 适配 主题的 文档页面标题,将 markdown 中的 h1 标题提取到 frontmatter 中,并将其删除, + * Docs title plugin - Extract h1 title from markdown to frontmatter + * + * 文档标题插件 - 从 markdown 中提取 h1 标题到 frontmatter + * + * Adapts to theme's document page title by extracting h1 title from markdown to frontmatter + * and removing it from content to avoid duplicate display. + * + * 适配主题的文档页面标题,将 markdown 中的 h1 标题提取到 frontmatter 中,并将其删除, * 以避免重复显示标题。 + * + * @param md - Markdown instance / Markdown 实例 */ export function docsTitlePlugin(md: Markdown): void { const render = md.render @@ -28,6 +42,14 @@ export function docsTitlePlugin(md: Markdown): void { } } +/** + * Parse markdown source to separate frontmatter and content + * + * 解析 markdown 源文件,分离 frontmatter 和内容 + * + * @param source - Markdown source / Markdown 源文件 + * @returns Object with matter and content / 包含 matter 和 content 的对象 + */ function parseSource(source: string) { const char = '---' diff --git a/plugins/plugin-md-power/src/node/enhance/imageSize.ts b/plugins/plugin-md-power/src/node/enhance/imageSize.ts index 2cd07ff8..852d20d2 100644 --- a/plugins/plugin-md-power/src/node/enhance/imageSize.ts +++ b/plugins/plugin-md-power/src/node/enhance/imageSize.ts @@ -10,14 +10,49 @@ import imageSize from 'image-size' import { fs, logger, path } from 'vuepress/utils' import { resolveAttrs } from '../utils/resolveAttrs.js' +/** + * Image size interface + * + * 图片尺寸接口 + */ interface ImgSize { + /** + * Image width + * + * 图片宽度 + */ width: number + /** + * Image height + * + * 图片高度 + */ height: number } +/** + * Regular expression for matching markdown image syntax + * + * 匹配 markdown 图片语法的正则表达式 + */ const REG_IMG = /!\[.*?\]\(.*?\)/g +/** + * Regular expression for matching HTML img tag + * + * 匹配 HTML img 标签的正则表达式 + */ const REG_IMG_TAG = //g +/** + * Regular expression for matching src/srcset attribute + * + * 匹配 src/srcset 属性的正则表达式 + */ const REG_IMG_TAG_SRC = /src(?:set)?=(['"])(.+?)\1/g +/** + * List of badge URLs to exclude + * + * 要排除的徽章 URL 列表 + */ const BADGE_LIST = [ 'https://img.shields.io', 'https://badge.fury.io', @@ -26,8 +61,22 @@ const BADGE_LIST = [ 'https://vercel.com/button', ] +/** + * Cache for image sizes + * + * 图片尺寸缓存 + */ const cache = new Map() +/** + * Image size plugin - Add width and height attributes to images + * + * 图片尺寸插件 - 为图片添加宽度和高度属性 + * + * @param app - VuePress app / VuePress 应用 + * @param md - Markdown instance / Markdown 实例 + * @param type - Image size type: 'local', 'all', or false / 图片尺寸类型:'local'、'all' 或 false + */ export async function imageSizePlugin( app: App, md: Markdown, @@ -71,6 +120,14 @@ export async function imageSizePlugin( md.renderer.rules.html_block = createHtmlRule(rawHtmlBlockRule) md.renderer.rules.html_inline = createHtmlRule(rawHtmlInlineRule) + /** + * Create HTML rule for processing img tags + * + * 创建处理 img 标签的 HTML 规则 + * + * @param rawHtmlRule - Original HTML rule / 原始 HTML 规则 + * @returns New HTML rule / 新的 HTML 规则 + */ function createHtmlRule(rawHtmlRule: RenderRule): RenderRule { return (tokens, idx, options, env, self) => { const token = tokens[idx] @@ -95,6 +152,17 @@ export async function imageSizePlugin( } } + /** + * Resolve image size from source + * + * 从源解析图片尺寸 + * + * @param src - Image source / 图片源 + * @param width - Existing width / 现有宽度 + * @param height - Existing height / 现有高度 + * @param env - Markdown environment / Markdown 环境 + * @returns Image size or false / 图片尺寸或 false + */ function resolveSize( src: string | null | undefined, width: string | null | undefined, @@ -150,6 +218,16 @@ export async function imageSizePlugin( } } +/** + * Resolve image URL from source + * + * 从源解析图片 URL + * + * @param src - Image source / 图片源 + * @param env - Markdown environment / Markdown 环境 + * @param app - VuePress app / VuePress 应用 + * @returns Resolved image URL / 解析后的图片 URL + */ function resolveImageUrl(src: string, env: MarkdownEnv, app: App): string { if (src[0] === '/') return app.dir.public(src.slice(1)) @@ -164,6 +242,13 @@ function resolveImageUrl(src: string, env: MarkdownEnv, app: App): string { return path.resolve(src) } +/** + * Scan remote image sizes in markdown files + * + * 扫描 markdown 文件中的远程图片尺寸 + * + * @param app - VuePress app / VuePress 应用 + */ export async function scanRemoteImageSize(app: App): Promise { if (!app.env.isBuild) return @@ -194,6 +279,13 @@ export async function scanRemoteImageSize(app: App): Promise { } } + /** + * Add source to image list + * + * 将源添加到图片列表 + * + * @param src - Image source / 图片源 + */ function addList(src: string) { if (src && isLinkHttp(src) && !imgList.includes(src) @@ -211,6 +303,14 @@ export async function scanRemoteImageSize(app: App): Promise { })) } +/** + * Fetch image size from remote URL + * + * 从远程 URL 获取图片尺寸 + * + * @param src - Image URL / 图片 URL + * @returns Image size / 图片尺寸 + */ function fetchImageSize(src: string): Promise { const link = new URL(src) @@ -248,6 +348,16 @@ function fetchImageSize(src: string): Promise { } } +/** + * Resolve image size from URL + * + * 从 URL 解析图片尺寸 + * + * @param app - VuePress app / VuePress 应用 + * @param url - Image URL / 图片 URL + * @param remote - Whether to fetch remote images / 是否获取远程图片 + * @returns Image size / 图片尺寸 + */ export async function resolveImageSize(app: App, url: string, remote = false): Promise { if (cache.has(url)) return cache.get(url)! diff --git a/plugins/plugin-md-power/src/node/enhance/links.ts b/plugins/plugin-md-power/src/node/enhance/links.ts index 91f2a7ff..6cf55b79 100644 --- a/plugins/plugin-md-power/src/node/enhance/links.ts +++ b/plugins/plugin-md-power/src/node/enhance/links.ts @@ -4,8 +4,16 @@ import { removeLeadingSlash } from '@vuepress/shared' import { path } from '@vuepress/utils' import { isLinkWithProtocol } from 'vuepress/shared' +/** + * Links plugin - Process internal and external links + * + * 链接插件 - 处理内部和外部链接 + * + * @param md - Markdown instance / Markdown 实例 + */ export function linksPlugin(md: Markdown): void { // attrs that going to be added to external links + // 要添加到外部链接的属性 const externalAttrs = { target: '_blank', rel: 'noopener noreferrer', @@ -14,24 +22,37 @@ export function linksPlugin(md: Markdown): void { let hasOpenInternalLink = false const internalTag = 'VPLink' + /** + * Handle link open token + * + * 处理链接打开令牌 + * + * @param tokens - Token array / 令牌数组 + * @param idx - Token index / 令牌索引 + * @param env - Markdown environment / Markdown 环境 + */ function handleLinkOpen(tokens: Token[], idx: number, env: MarkdownEnv) { hasOpenInternalLink = false const token = tokens[idx] // get `href` attr index + // 获取 `href` 属性索引 const hrefIndex = token.attrIndex('href') // if `href` attr does not exist, skip + // 如果 `href` 属性不存在,跳过 /* istanbul ignore if -- @preserve */ if (hrefIndex < 0) { return } // if `href` attr exists, `token.attrs` is not `null` + // 如果 `href` 属性存在,`token.attrs` 不为 `null` const hrefAttr = token.attrs![hrefIndex] const hrefLink: string = hrefAttr[1] if (isLinkWithProtocol(hrefLink)) { // set `externalAttrs` to current token + // 将 `externalAttrs` 设置到当前令牌 Object.entries(externalAttrs).forEach(([key, val]) => { token.attrSet(key, val) }) @@ -42,6 +63,7 @@ export function linksPlugin(md: Markdown): void { return // convert starting tag of internal link + // 转换内部链接的开始标签 hasOpenInternalLink = true token.tag = internalTag @@ -72,6 +94,7 @@ export function linksPlugin(md: Markdown): void { md.renderer.rules.link_close = (tokens, idx, opts, _env, self) => { // convert ending tag of internal link + // 转换内部链接的结束标签 if (hasOpenInternalLink) { hasOpenInternalLink = false tokens[idx].tag = internalTag @@ -82,6 +105,13 @@ export function linksPlugin(md: Markdown): void { /** * Resolve relative and absolute paths according to the `base` and `filePathRelative` + * + * 根据 `base` 和 `filePathRelative` 解析相对和绝对路径 + * + * @param rawPath - Raw path / 原始路径 + * @param base - Base URL / 基础 URL + * @param filePathRelative - Relative file path / 相对文件路径 + * @returns Object with absolutePath and relativePath / 包含 absolutePath 和 relativePath 的对象 */ export function resolvePaths(rawPath: string, base: string, filePathRelative: string | null): { absolutePath: string | null @@ -91,37 +121,50 @@ export function resolvePaths(rawPath: string, base: string, filePathRelative: st let relativePath: string // if raw path is absolute + // 如果原始路径是绝对路径 if (rawPath.startsWith('/')) { // if raw path is a link to markdown file + // 如果原始路径是 markdown 文件链接 if (rawPath.endsWith('.md')) { // prepend `base` to the link + // 将 `base` 添加到链接前面 absolutePath = path.join(base, rawPath) relativePath = removeLeadingSlash(rawPath) } // if raw path is a link to other kind of file + // 如果原始路径是其他类型文件链接 else { // keep the link as is + // 保持链接不变 absolutePath = rawPath relativePath = path.relative(base, absolutePath) } } // if raw path is relative + // 如果原始路径是相对路径 // if `filePathRelative` is available + // 如果 `filePathRelative` 可用 else if (filePathRelative) { // resolve relative path according to `filePathRelative` + // 根据 `filePathRelative` 解析相对路径 relativePath = path.join( // file path may contain non-ASCII characters + // 文件路径可能包含非 ASCII 字符 path.dirname(encodeURI(filePathRelative)), rawPath, ) // resolve absolute path according to `base` + // 根据 `base` 解析绝对路径 absolutePath = path.join(base, relativePath) } // if `filePathRelative` is not available + // 如果 `filePathRelative` 不可用 else { // remove leading './' + // 移除开头的 './' relativePath = rawPath.replace(/^(?:\.\/)?(.*)$/, '$1') // just take relative link as absolute link + // 将相对链接视为绝对链接 absolutePath = null } diff --git a/plugins/plugin-md-power/src/node/inline/abbr.ts b/plugins/plugin-md-power/src/node/inline/abbr.ts index 5ba7a4a2..021982e5 100644 --- a/plugins/plugin-md-power/src/node/inline/abbr.ts +++ b/plugins/plugin-md-power/src/node/inline/abbr.ts @@ -1,5 +1,7 @@ /** * Forked and modified from https://github.com/markdown-it/markdown-it-abbr/blob/master/index.mjs + * + * 从 https://github.com/markdown-it/markdown-it-abbr/blob/master/index.mjs 分叉并修改 */ import type { PluginWithOptions } from 'markdown-it' @@ -11,18 +13,59 @@ import type Token from 'markdown-it/lib/token.mjs' import { isEmptyObject, objectMap } from '@pengzhanbo/utils' import { cleanMarkdownEnv } from '../utils/cleanMarkdownEnv.js' +/** + * Abbreviation state block + * + * 缩写词状态块 + */ interface AbbrStateBlock extends StateBlock { + /** + * Environment + * + * 环境 + */ env: { + /** + * Abbreviations record + * + * 缩写词记录 + */ abbreviations?: Record } } +/** + * Abbreviation state core + * + * 缩写词核心状态 + */ interface AbbrStateCore extends StateCore { + /** + * Environment + * + * 环境 + */ env: { + /** + * Abbreviations record + * + * 缩写词记录 + */ abbreviations?: Record } } +/** + * Abbreviation plugin - Enable abbreviation syntax + * + * 缩写词插件 - 启用缩写词语法 + * + * Definition syntax: *[ABBREV]: Full description + * 定义语法:*[缩写]: 完整描述 + * + * @param md - Markdown-it instance / Markdown-it 实例 + * @param globalAbbreviations - Global abbreviations preset / 全局缩写词预设 + */ export const abbrPlugin: PluginWithOptions> = (md, globalAbbreviations = {}) => { const { arrayReplaceAt, escapeRE, lib } = md.utils globalAbbreviations = objectMap( @@ -37,6 +80,17 @@ export const abbrPlugin: PluginWithOptions> = (md, global const UNICODE_SPACE_REGEXP = (lib.ucmicro.Z as RegExp).source const WORDING_REGEXP_TEXT = `${UNICODE_PUNCTUATION_REGEXP}|${UNICODE_SPACE_REGEXP}|[${OTHER_CHARS.split('').map(escapeRE).join('')}]` + /** + * Abbreviation definition rule + * + * 缩写词定义规则 + * + * @param state - State block / 状态块 + * @param startLine - Start line number / 开始行号 + * @param _endLine - End line number / 结束行号 + * @param silent - Silent mode / 静默模式 + * @returns Whether matched / 是否匹配 + */ const abbrDefinition: RuleBlock = ( state: AbbrStateBlock, startLine, @@ -90,6 +144,13 @@ export const abbrPlugin: PluginWithOptions> = (md, global return true } + /** + * Abbreviation replace rule + * + * 缩写词替换规则 + * + * @param state - State core / 核心状态 + */ const abbrReplace: RuleCore = (state: AbbrStateCore) => { const tokens = state.tokens const { abbreviations: localAbbreviations } = state.env diff --git a/plugins/plugin-md-power/src/node/inline/annotation.ts b/plugins/plugin-md-power/src/node/inline/annotation.ts index ceebed76..8758c801 100644 --- a/plugins/plugin-md-power/src/node/inline/annotation.ts +++ b/plugins/plugin-md-power/src/node/inline/annotation.ts @@ -7,29 +7,105 @@ import type Token from 'markdown-it/lib/token.mjs' import { objectMap, toArray } from '@pengzhanbo/utils' import { cleanMarkdownEnv } from '../utils/cleanMarkdownEnv' +/** + * Annotation token with meta information + * + * 带元信息的注释令牌 + */ interface AnnotationToken extends Token { + /** + * Token meta information + * + * 令牌元信息 + */ meta: { + /** + * Annotation label + * + * 注释标签 + */ label: string } } +/** + * Annotation environment + * + * 注释环境 + */ interface AnnotationEnv extends Record { + /** + * Annotations record + * + * 注释记录 + */ annotations: Record } +/** + * Annotation state block + * + * 注释状态块 + */ interface AnnotationStateBlock extends StateBlock { + /** + * Tokens array + * + * 令牌数组 + */ tokens: AnnotationToken[] + /** + * Environment + * + * 环境 + */ env: AnnotationEnv } +/** + * Annotation state inline + * + * 注释行内状态 + */ interface AnnotationStateInline extends StateInline { + /** + * Tokens array + * + * 令牌数组 + */ tokens: AnnotationToken[] + /** + * Environment + * + * 环境 + */ env: AnnotationEnv } +/** + * Annotation definition rule + * + * 注释定义规则 + * + * @param state - State block / 状态块 + * @param startLine - Start line number / 开始行号 + * @param endLine - End line number / 结束行号 + * @param silent - Silent mode / 静默模式 + * @returns Whether matched / 是否匹配 + */ const annotationDef: RuleBlock = ( state: AnnotationStateBlock, startLine: number, @@ -100,6 +176,15 @@ const annotationDef: RuleBlock = ( return true } +/** + * Annotation reference rule + * + * 注释引用规则 + * + * @param state - State inline / 行内状态 + * @param silent - Silent mode / 静默模式 + * @returns Whether matched / 是否匹配 + */ const annotationRef: RuleInline = ( state: AnnotationStateInline, silent: boolean, @@ -155,6 +240,20 @@ const annotationRef: RuleInline = ( return true } +/** + * Annotation plugin - Enable annotation syntax + * + * 注释插件 - 启用注释语法 + * + * Definition syntax: [+label]: annotation content + * Reference syntax: [+label] + * + * 定义语法:[+label]: 注释内容 + * 引用语法:[+label] + * + * @param md - Markdown-it instance / Markdown-it 实例 + * @param globalAnnotations - Global annotations preset / 全局注释预设 + */ export const annotationPlugin: PluginWithOptions> = ( md, globalAnnotations = {}, diff --git a/plugins/plugin-md-power/src/node/inline/env-preset.ts b/plugins/plugin-md-power/src/node/inline/env-preset.ts index c81ecc37..c4a5cf53 100644 --- a/plugins/plugin-md-power/src/node/inline/env-preset.ts +++ b/plugins/plugin-md-power/src/node/inline/env-preset.ts @@ -3,7 +3,12 @@ import type { MarkdownEnvPreset } from '../../shared/index.js' import { isEmptyObject, isString, objectMap } from '@pengzhanbo/utils' /** - * inject preset to markdown env + * Environment preset plugin - Inject preset references to markdown env + * + * 环境预设插件 - 向 Markdown 环境注入预设引用 + * + * @param md - Markdown-it instance / Markdown-it 实例 + * @param env - Environment preset configuration / 环境预设配置 */ export const envPresetPlugin: PluginWithOptions = (md, env = {}) => { if (isEmptyObject(env)) diff --git a/plugins/plugin-md-power/src/node/inline/index.ts b/plugins/plugin-md-power/src/node/inline/index.ts index dfb2e97a..3b874087 100644 --- a/plugins/plugin-md-power/src/node/inline/index.ts +++ b/plugins/plugin-md-power/src/node/inline/index.ts @@ -13,6 +13,14 @@ import { annotationPlugin } from './annotation.js' import { envPresetPlugin } from './env-preset.js' import { plotPlugin } from './plot.js' +/** + * Inline syntax plugin - Register all inline markdown syntax plugins + * + * 行内语法插件 - 注册所有行内 Markdown 语法插件 + * + * @param md - Markdown instance / Markdown 实例 + * @param options - Plugin options / 插件选项 + */ export function inlineSyntaxPlugin( md: Markdown, options: MarkdownPowerPluginOptions, diff --git a/plugins/plugin-md-power/src/node/inline/plot.ts b/plugins/plugin-md-power/src/node/inline/plot.ts index 2262ffa4..260e41a9 100644 --- a/plugins/plugin-md-power/src/node/inline/plot.ts +++ b/plugins/plugin-md-power/src/node/inline/plot.ts @@ -1,9 +1,20 @@ /** * !!这里的文本将被黑幕隐藏,通过点击或者 hover 才可重现!! + * + * !!The text here will be hidden by a spoiler, and can only be revealed by clicking or hovering!! */ import type { PluginWithOptions } from 'markdown-it' import type { RuleInline } from 'markdown-it/lib/parser_inline.mjs' +/** + * Plot inline rule definition + * + * 黑幕行内规则定义 + * + * @param state - Markdown-it state / Markdown-it 状态 + * @param silent - Silent mode / 静默模式 + * @returns Whether matched / 是否匹配 + */ const plotDef: RuleInline = (state, silent) => { let found = false const max = state.posMax @@ -76,6 +87,16 @@ const plotDef: RuleInline = (state, silent) => { return true } +/** + * Plot plugin - Hide text with spoiler effect + * + * 黑幕插件 - 使用黑幕效果隐藏文本 + * + * Syntax: !!hidden text!! + * 语法:!!隐藏文本!! + * + * @param md - Markdown-it instance / Markdown-it 实例 + */ export const plotPlugin: PluginWithOptions = (md) => { md.inline.ruler.before('emphasis', 'plot', plotDef) } diff --git a/plugins/plugin-md-power/src/node/utils/cleanMarkdownEnv.ts b/plugins/plugin-md-power/src/node/utils/cleanMarkdownEnv.ts index 3fd0774b..533978c1 100644 --- a/plugins/plugin-md-power/src/node/utils/cleanMarkdownEnv.ts +++ b/plugins/plugin-md-power/src/node/utils/cleanMarkdownEnv.ts @@ -1,15 +1,49 @@ import type { MarkdownEnv } from 'vuepress/markdown' +/** + * Clean Markdown Environment + * + * 清理后的 Markdown 环境 + */ export interface CleanMarkdownEnv extends MarkdownEnv { + /** + * References + * + * 引用链接 + */ references?: unknown + /** + * Abbreviations + * + * 缩写词 + */ abbreviations?: unknown + /** + * Annotations + * + * 注释 + */ annotations?: unknown } +/** + * Whitelist of environment keys to preserve + * + * 要保留的环境键白名单 + */ const WHITE_LIST = ['base', 'filePath', 'filePathRelative', 'references', 'abbreviations', 'annotations'] as const type WhiteListUnion = (typeof WHITE_LIST)[number] +/** + * Clean markdown environment, keeping only whitelisted keys + * + * 清理 Markdown 环境,仅保留白名单中的键 + * + * @param env - Markdown environment / Markdown 环境 + * @param excludes - Keys to exclude / 要排除的键 + * @returns Cleaned environment / 清理后的环境 + */ export function cleanMarkdownEnv(env: CleanMarkdownEnv, excludes: WhiteListUnion[] = []): CleanMarkdownEnv { const result: CleanMarkdownEnv = {} for (const key of WHITE_LIST) { diff --git a/plugins/plugin-md-power/src/node/utils/encryptContent.ts b/plugins/plugin-md-power/src/node/utils/encryptContent.ts index 08acf0d8..1a7aea93 100644 --- a/plugins/plugin-md-power/src/node/utils/encryptContent.ts +++ b/plugins/plugin-md-power/src/node/utils/encryptContent.ts @@ -1,8 +1,13 @@ import { webcrypto } from 'node:crypto' /** + * Get key material from password + * + * 从密码获取密钥材料 + * * @see https://developer.mozilla.org/zh-CN/docs/Web/API/SubtleCrypto/deriveKey#pbkdf2_2 - * @param password + * @param password - Password string / 密码字符串 + * @returns CryptoKey / 加密密钥 */ function getKeyMaterial(password: string) { const enc = new TextEncoder() @@ -15,6 +20,15 @@ function getKeyMaterial(password: string) { ) } +/** + * Get derived key from key material + * + * 从密钥材料获取派生密钥 + * + * @param keyMaterial - Key material / 密钥材料 + * @param salt - Salt for key derivation / 密钥派生盐值 + * @returns Derived CryptoKey / 派生加密密钥 + */ function getCryptoDeriveKey(keyMaterial: CryptoKey | webcrypto.CryptoKey, salt: Uint8Array) { return webcrypto.subtle.deriveKey( { @@ -34,8 +48,17 @@ function getCryptoDeriveKey(keyMaterial: CryptoKey | webcrypto.CryptoKey, salt: } /** + * Encrypt content using AES-CBC + * + * 使用 AES-CBC 加密内容 + * * @see https://github.com/mdn/dom-examples/blob/main/web-crypto/encrypt-decrypt/aes-cbc.js - * @param content + * @param content - Content to encrypt / 要加密的内容 + * @param options - Encryption options / 加密选项 + * @param options.password - Password for encryption / 加密密码 + * @param options.iv - Initialization vector / 初始化向量 + * @param options.salt - Salt for key derivation / 密钥派生盐值 + * @returns Encrypted content / 加密后的内容 */ export async function encryptContent(content: string, options: { password: string diff --git a/plugins/plugin-md-power/src/node/utils/findLocales.ts b/plugins/plugin-md-power/src/node/utils/findLocales.ts index 11485cb2..ec82ee7b 100644 --- a/plugins/plugin-md-power/src/node/utils/findLocales.ts +++ b/plugins/plugin-md-power/src/node/utils/findLocales.ts @@ -1,6 +1,15 @@ import type { LocaleConfig } from 'vuepress' import type { MDPowerLocaleData } from '../../shared/index.js' +/** + * Find locale configurations for a specific key + * + * 查找特定键的本地化配置 + * + * @param locales - Locale configuration / 本地化配置 + * @param key - Key to find / 要查找的键 + * @returns Record of locale paths to locale data / 本地化路径到本地化数据的记录 + */ export function findLocales< T extends MDPowerLocaleData, K extends keyof T, diff --git a/plugins/plugin-md-power/src/node/utils/logger.ts b/plugins/plugin-md-power/src/node/utils/logger.ts index ece2b416..a2af3016 100644 --- a/plugins/plugin-md-power/src/node/utils/logger.ts +++ b/plugins/plugin-md-power/src/node/utils/logger.ts @@ -5,16 +5,31 @@ import { colors, ora } from 'vuepress/utils' type Ora = ReturnType /** - * Logger utils + * Logger utility class for plugin + * + * 插件日志工具类 */ export class Logger { + /** + * Create a logger instance + * + * 创建日志记录器实例 + * + * @param name - Plugin/Theme name / 插件/主题名称 + */ public constructor( - /** - * Plugin/Theme name - */ private readonly name = '', ) {} + /** + * Initialize spinner + * + * 初始化加载动画 + * + * @param subname - Subname / 子名称 + * @param text - Loading text / 加载文本 + * @returns Ora spinner instance / Ora 加载动画实例 + */ private init(subname: string, text: string): Ora { return ora({ prefixText: colors.blue(`${this.name}${subname ? `:${subname}` : ''}: `), @@ -24,6 +39,12 @@ export class Logger { /** * Create a loading spinner with text + * + * 创建带文本的加载动画 + * + * @param subname - Subname / 子名称 + * @param msg - Message / 消息 + * @returns Object with succeed and fail methods / 包含 succeed 和 fail 方法的对象 */ public load(subname: string, msg: string): { succeed: (text?: string) => void @@ -37,6 +58,15 @@ export class Logger { } } + /** + * Log info message + * + * 记录信息消息 + * + * @param subname - Subname / 子名称 + * @param text - Message text / 消息文本 + * @param args - Additional arguments / 额外参数 + */ public info(subname: string, text = '', ...args: unknown[]): void { this.init(subname, colors.blue(text)).info() @@ -45,7 +75,13 @@ export class Logger { } /** - * Log success msg + * Log success message + * + * 记录成功消息 + * + * @param subname - Subname / 子名称 + * @param text - Message text / 消息文本 + * @param args - Additional arguments / 额外参数 */ public succeed(subname: string, text = '', ...args: unknown[]): void { this.init(subname, colors.green(text)).succeed() @@ -55,7 +91,13 @@ export class Logger { } /** - * Log warning msg + * Log warning message + * + * 记录警告消息 + * + * @param subname - Subname / 子名称 + * @param text - Message text / 消息文本 + * @param args - Additional arguments / 额外参数 */ public warn(subname: string, text = '', ...args: unknown[]): void { this.init(subname, colors.yellow(text)).warn() @@ -65,7 +107,13 @@ export class Logger { } /** - * Log error msg + * Log error message + * + * 记录错误消息 + * + * @param subname - Subname / 子名称 + * @param text - Message text / 消息文本 + * @param args - Additional arguments / 额外参数 */ public error(subname: string, text = '', ...args: unknown[]): void { this.init(subname, colors.red(text)).fail() @@ -75,4 +123,9 @@ export class Logger { } } +/** + * Default logger instance for vuepress-plugin-md-power + * + * vuepress-plugin-md-power 的默认日志记录器实例 + */ export const logger: Logger = new Logger('vuepress-plugin-md-power') diff --git a/plugins/plugin-md-power/src/node/utils/nanoid.ts b/plugins/plugin-md-power/src/node/utils/nanoid.ts index d177694e..9df15c9e 100644 --- a/plugins/plugin-md-power/src/node/utils/nanoid.ts +++ b/plugins/plugin-md-power/src/node/utils/nanoid.ts @@ -1,3 +1,11 @@ import { customAlphabet } from 'nanoid' +/** + * Generate a nanoid with custom alphabet + * + * 使用自定义字符集生成 nanoid + * + * @param size - ID length / ID 长度 + * @returns Nanoid string / Nanoid 字符串 + */ export const nanoid: (size?: number) => string = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5) diff --git a/plugins/plugin-md-power/src/node/utils/package.ts b/plugins/plugin-md-power/src/node/utils/package.ts index 5be4386c..4ef89388 100644 --- a/plugins/plugin-md-power/src/node/utils/package.ts +++ b/plugins/plugin-md-power/src/node/utils/package.ts @@ -1,5 +1,21 @@ +/** + * Type for values that can be either a value or a Promise of that value + * + * 可以是值或该值 Promise 的类型 + * + * @typeParam T - The type of the value / 值的类型 + */ export type Awaitable = T | Promise +/** + * Get the default export from a module, or the module itself if no default export + * + * 从模块获取默认导出,如果没有默认导出则返回模块本身 + * + * @param m - Module or Promise of module / 模块或模块的 Promise + * @returns Default export or module itself / 默认导出或模块本身 + * @typeParam T - Module type / 模块类型 + */ export async function interopDefault(m: Awaitable): Promise { const resolved = await m return (resolved as any).default || resolved diff --git a/plugins/plugin-md-power/src/node/utils/parseRect.ts b/plugins/plugin-md-power/src/node/utils/parseRect.ts index 8e8c41a1..9961d4ee 100644 --- a/plugins/plugin-md-power/src/node/utils/parseRect.ts +++ b/plugins/plugin-md-power/src/node/utils/parseRect.ts @@ -1,3 +1,12 @@ +/** + * Parse rect size string, add unit if it's a number + * + * 解析矩形尺寸字符串,如果是数字则添加单位 + * + * @param str - Size string / 尺寸字符串 + * @param unit - Unit to append (default: 'px') / 要添加的单位(默认:'px') + * @returns Size string with unit / 带单位的尺寸字符串 + */ export function parseRect(str: string, unit = 'px'): string { if (Number.parseFloat(str) === Number(str)) return `${str}${unit}` diff --git a/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts b/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts index 493885f8..d4ad2ac8 100644 --- a/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts +++ b/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts @@ -1,7 +1,21 @@ import { camelCase } from '@pengzhanbo/utils' +/** + * Regular expression for matching attribute values + * + * 匹配属性值的正则表达式 + */ const RE_ATTR_VALUE = /(?:^|\s+)(?[\w-]+)(?:=(?['"])(?.+?)\k|=(?\S+))?(?:\s+|$)/ +/** + * Resolve attribute string to object + * + * 将属性字符串解析为对象 + * + * @param info - Attribute string / 属性字符串 + * @returns Object with attrs and rawAttrs / 包含 attrs 和 rawAttrs 的对象 + * @typeParam T - Attribute type / 属性类型 + */ export function resolveAttrs = Record>(info: string): { attrs: T rawAttrs: string @@ -35,6 +49,15 @@ export function resolveAttrs = Record return { attrs: attrs as T, rawAttrs } } +/** + * Resolve single attribute value from info string + * + * 从信息字符串中解析单个属性值 + * + * @param info - Info string / 信息字符串 + * @param key - Attribute key / 属性键 + * @returns Attribute value or undefined / 属性值或 undefined + */ export function resolveAttr(info: string, key: string): string | undefined { const pattern = new RegExp(`(?:^|\\s+)${key}(?:=(?['"])(?.+?)\\k|=(?\\S+))?(?:\\s+|$)`) const groups = info.match(pattern)?.groups diff --git a/plugins/plugin-md-power/src/node/utils/stringifyAttrs.ts b/plugins/plugin-md-power/src/node/utils/stringifyAttrs.ts index 283a5038..c0b54fdd 100644 --- a/plugins/plugin-md-power/src/node/utils/stringifyAttrs.ts +++ b/plugins/plugin-md-power/src/node/utils/stringifyAttrs.ts @@ -1,5 +1,16 @@ import { isBoolean, isNull, isNumber, isString, isUndefined, kebabCase } from '@pengzhanbo/utils' +/** + * Stringify attributes object to HTML attribute string + * + * 将属性对象字符串化为 HTML 属性字符串 + * + * @param attrs - Attributes object / 属性对象 + * @param withUndefinedOrNull - Whether to include undefined/null values / 是否包含 undefined/null 值 + * @param forceStringify - Keys to force stringify / 强制字符串化的键 + * @returns HTML attribute string / HTML 属性字符串 + * @typeParam T - Attribute type / 属性类型 + */ export function stringifyAttrs( attrs: T, withUndefinedOrNull = false, diff --git a/plugins/plugin-md-power/src/node/utils/stringifyProp.ts b/plugins/plugin-md-power/src/node/utils/stringifyProp.ts index b5156410..23e797ed 100644 --- a/plugins/plugin-md-power/src/node/utils/stringifyProp.ts +++ b/plugins/plugin-md-power/src/node/utils/stringifyProp.ts @@ -1,4 +1,13 @@ -// Single quote will break @vue/compiler-sfc +/** + * Stringify a property value for use in Vue templates + * + * 将属性值字符串化为可在 Vue 模板中使用的格式 + * + * @param data - Data to stringify / 要字符串化的数据 + * @returns Stringified data with single quotes escaped / 字符串化后的数据,单引号已转义 + */ export function stringifyProp(data: unknown): string { + // Single quote will break @vue/compiler-sfc + // 单引号会破坏 @vue/compiler-sfc return JSON.stringify(data).replace(/'/g, ''') } diff --git a/plugins/plugin-md-power/src/shared/caniuse.ts b/plugins/plugin-md-power/src/shared/caniuse.ts index e00623a5..e4283237 100644 --- a/plugins/plugin-md-power/src/shared/caniuse.ts +++ b/plugins/plugin-md-power/src/shared/caniuse.ts @@ -1,17 +1,53 @@ +/** + * CanIUse Display Mode + * + * CanIUse 显示模式 + */ export type CanIUseMode = | 'embed' | 'baseline' /** @deprecated */ | 'image' +/** + * CanIUse Token Metadata + * + * CanIUse 令牌元数据 + */ export interface CanIUseTokenMeta { + /** + * Feature name + * + * 特性名称 + */ feature: string + /** + * Display mode + * + * 显示模式 + */ mode: CanIUseMode + /** + * Browser versions to display + * + * 要显示的浏览器版本 + */ versions: string } +/** + * CanIUse Options + * + * CanIUse 配置选项 + */ export interface CanIUseOptions { /** + * Embed mode + * + * embed - embed via iframe, providing interactive view + * + * image - embed via image, static + * * 嵌入模式 * * embed 通过iframe嵌入,提供可交互视图 diff --git a/plugins/plugin-md-power/src/shared/codeSandbox.ts b/plugins/plugin-md-power/src/shared/codeSandbox.ts index f9acae93..c0e0b0aa 100644 --- a/plugins/plugin-md-power/src/shared/codeSandbox.ts +++ b/plugins/plugin-md-power/src/shared/codeSandbox.ts @@ -1,12 +1,57 @@ import type { SizeOptions } from './size.js' +/** + * CodeSandbox Token Metadata + * + * CodeSandbox 令牌元数据 + */ export interface CodeSandboxTokenMeta extends SizeOptions { + /** + * User name + * + * 用户名 + */ user?: string + /** + * Sandbox ID + * + * 沙箱 ID + */ id?: string + /** + * Layout + * + * 布局 + */ layout?: string + /** + * Embed type + * + * 嵌入类型 + */ type?: 'button' | 'embed' + /** + * Title + * + * 标题 + */ title?: string + /** + * File path + * + * 文件路径 + */ filepath?: string + /** + * Whether to show navbar + * + * 是否显示导航栏 + */ navbar?: boolean + /** + * Whether to show console + * + * 是否显示控制台 + */ console?: boolean } diff --git a/plugins/plugin-md-power/src/shared/codeTabs.ts b/plugins/plugin-md-power/src/shared/codeTabs.ts index 6e2bea8a..de43e128 100644 --- a/plugins/plugin-md-power/src/shared/codeTabs.ts +++ b/plugins/plugin-md-power/src/shared/codeTabs.ts @@ -1,3 +1,18 @@ +/** + * Code tabs options + * + * 代码选项卡配置选项 + */ export interface CodeTabsOptions { + /** + * Icon configuration for code tabs + * + * 代码选项卡的图标配置 + * + * - `boolean`: Whether to enable icons / 是否启用图标 + * - `object`: Detailed icon configuration / 详细的图标配置 + * - `named`: Named icons to use / 要使用的命名图标 + * - `extensions`: File extensions to show icons for / 要显示图标的文件扩展名 + */ icon?: boolean | { named?: false | string[], extensions?: false | string[] } } diff --git a/plugins/plugin-md-power/src/shared/codepen.ts b/plugins/plugin-md-power/src/shared/codepen.ts index 004fdbe1..179f697a 100644 --- a/plugins/plugin-md-power/src/shared/codepen.ts +++ b/plugins/plugin-md-power/src/shared/codepen.ts @@ -1,11 +1,51 @@ import type { SizeOptions } from './size' +/** + * CodePen Token Metadata + * + * CodePen 令牌元数据 + */ export interface CodepenTokenMeta extends SizeOptions { + /** + * Pen title + * + * Pen 标题 + */ title?: string + /** + * User name + * + * 用户名 + */ user?: string + /** + * Pen slug + * + * Pen 标识 + */ slash?: string + /** + * Display tabs + * + * 显示的选项卡 + */ tab?: string + /** + * Theme + * + * 主题 + */ theme?: string + /** + * Whether to show preview + * + * 是否显示预览 + */ preview?: boolean + /** + * Whether editable + * + * 是否可编辑 + */ editable?: boolean } diff --git a/plugins/plugin-md-power/src/shared/demo.ts b/plugins/plugin-md-power/src/shared/demo.ts index e2d6568d..6dc5e48e 100644 --- a/plugins/plugin-md-power/src/shared/demo.ts +++ b/plugins/plugin-md-power/src/shared/demo.ts @@ -2,27 +2,107 @@ import type Token from 'markdown-it/lib/token.mjs' import type { App } from 'vuepress' import type { Markdown, MarkdownEnv } from 'vuepress/markdown' +/** + * Demo File Type + * + * 演示文件类型 + */ export interface DemoFile { + /** + * File type + * + * 文件类型 + */ type: 'vue' | 'normal' | 'css' | 'markdown' + /** + * Export name + * + * 导出名称 + */ export?: string + /** + * File path + * + * 文件路径 + */ path: string + /** + * Whether to add to gitignore + * + * 是否添加到 gitignore + */ gitignore?: boolean } +/** + * Markdown Demo Environment + * + * Markdown 演示环境 + */ export interface MarkdownDemoEnv extends MarkdownEnv { + /** + * Demo files + * + * 演示文件列表 + */ demoFiles?: DemoFile[] } +/** + * Demo Metadata + * + * 演示元数据 + */ export interface DemoMeta { + /** + * Demo type + * + * 演示类型 + */ type: 'vue' | 'normal' | 'markdown' + /** + * URL + * + * 链接地址 + */ url: string + /** + * Title + * + * 标题 + */ title?: string + /** + * Description + * + * 描述 + */ desc?: string + /** + * Code settings + * + * 代码设置 + */ codeSetting?: string + /** + * Whether expanded + * + * 是否展开 + */ expanded?: boolean } +/** + * Demo Container Render + * + * 演示容器渲染器 + */ export interface DemoContainerRender { + /** + * Before render + * + * 渲染前 + */ before: ( app: App, md: Markdown, @@ -30,6 +110,16 @@ export interface DemoContainerRender { meta: DemoMeta, codeMap: Record, ) => string + /** + * After render + * + * 渲染后 + */ after: () => string + /** + * Token processor + * + * 令牌处理器 + */ token?: (token: Token, tokens: Token[], index: number) => void } diff --git a/plugins/plugin-md-power/src/shared/encrypt.ts b/plugins/plugin-md-power/src/shared/encrypt.ts index c7c7a9ef..b4b886e7 100644 --- a/plugins/plugin-md-power/src/shared/encrypt.ts +++ b/plugins/plugin-md-power/src/shared/encrypt.ts @@ -1,36 +1,66 @@ import type { LocaleData } from 'vuepress' +/** + * Encrypt Snippet Locale + * + * 加密片段本地化 + */ export interface EncryptSnippetLocale extends LocaleData { /** - * @default 'The content is encrypted, please unlock to view.'' + * Hint message + * + * 提示信息 + * @default 'The content is encrypted, please unlock to view.' */ hint?: string /** + * Password placeholder + * + * 密码占位符 * @default 'Enter password' */ placeholder?: string /** + * Incorrect password message + * + * 密码错误消息 * @default 'Incorrect password' */ incPwd?: string /** + * No content message + * + * 无内容消息 * @default 'Unlocked, but content failed to load, please try again later.' */ noContent?: string /** + * Security warning title + * + * 安全警告标题 * @default '🚨 Security Warning:' */ warningTitle?: string /** + * Security warning text + * + * 安全警告文本 * @default 'Your connection is not encrypted with HTTPS, posing a risk of content leakage and preventing access to encrypted content.' */ warningText?: string } +/** + * Encrypt Snippet Options + * + * 加密片段选项 + */ export interface EncryptSnippetOptions { /** - * default password + * Default password + * + * 默认密码 */ password?: string } diff --git a/plugins/plugin-md-power/src/shared/env.ts b/plugins/plugin-md-power/src/shared/env.ts index 183b9980..3dde4d7d 100644 --- a/plugins/plugin-md-power/src/shared/env.ts +++ b/plugins/plugin-md-power/src/shared/env.ts @@ -1,4 +1,10 @@ /* eslint-disable jsdoc/no-multi-asterisks */ + +/** + * Markdown Environment Preset Configuration + * + * Markdown 环境预设配置 + */ export interface MarkdownEnvPreset { /** * markdown reference preset, use in any markdown file @@ -20,7 +26,7 @@ export interface MarkdownEnvPreset { * [link][label-1] * [link][label-2] * ``` - * same as 等价于 + * same as / 等价于 * ```markdown * [label-1]: http://example.com/ * [label-2]: http://example.com/ "title" @@ -47,7 +53,7 @@ export interface MarkdownEnvPreset { * ```markdown * The HTML specification is maintained by the W3C. * ``` - * same as 等价于 + * same as / 等价于 * ```markdown * *[HTML]: Hyper Text Markup Language * *[W3C]: World Wide Web Consortium @@ -73,7 +79,7 @@ export interface MarkdownEnvPreset { * ```markdown * [+vuepress-theme-plume] is a theme for [+vuepress] * ``` - * same as 等价于 + * same as / 等价于 * ```markdown * [+vuepress]: vuepress is a Vue.js based documentation generator * [+vuepress-theme-plume]: vuepress-theme-plume is a theme for vuepress diff --git a/plugins/plugin-md-power/src/shared/fileTree.ts b/plugins/plugin-md-power/src/shared/fileTree.ts index e3070e01..2e880ce7 100644 --- a/plugins/plugin-md-power/src/shared/fileTree.ts +++ b/plugins/plugin-md-power/src/shared/fileTree.ts @@ -1,5 +1,23 @@ +/** + * File tree icon mode + * + * 文件树图标模式 + */ export type FileTreeIconMode = 'simple' | 'colored' +/** + * File tree options + * + * 文件树配置选项 + */ export interface FileTreeOptions { + /** + * Icon mode for file tree + * + * 文件树的图标模式 + * + * - `simple`: Simple icons / 简单图标 + * - `colored`: Colored icons / 彩色图标 + */ icon?: FileTreeIconMode } diff --git a/plugins/plugin-md-power/src/shared/jsfiddle.ts b/plugins/plugin-md-power/src/shared/jsfiddle.ts index 8ced954e..b013f2d7 100644 --- a/plugins/plugin-md-power/src/shared/jsfiddle.ts +++ b/plugins/plugin-md-power/src/shared/jsfiddle.ts @@ -1,8 +1,33 @@ import type { SizeOptions } from './size' +/** + * JSFiddle token metadata + * + * JSFiddle 令牌元数据 + */ export interface JSFiddleTokenMeta extends SizeOptions { + /** + * Source URL + * + * 源 URL + */ source: string + /** + * Fiddle title + * + * Fiddle 标题 + */ title?: string + /** + * Theme + * + * 主题 + */ theme?: string + /** + * Display tabs + * + * 显示的选项卡 + */ tab?: string } diff --git a/plugins/plugin-md-power/src/shared/locale.ts b/plugins/plugin-md-power/src/shared/locale.ts index 491b6ff2..4c678936 100644 --- a/plugins/plugin-md-power/src/shared/locale.ts +++ b/plugins/plugin-md-power/src/shared/locale.ts @@ -1,12 +1,42 @@ import type { LocaleData } from 'vuepress' import type { EncryptSnippetLocale } from './encrypt' +/** + * Markdown Power Plugin Locale Data + * + * Markdown Power 插件本地化数据 + */ export interface MDPowerLocaleData extends LocaleData { + /** + * Common locale data + * + * 通用本地化数据 + */ common?: CommonLocaleData + /** + * Encrypt snippet locale data + * + * 加密片段本地化数据 + */ encrypt?: EncryptSnippetLocale } +/** + * Common Locale Data + * + * 通用本地化数据 + */ export interface CommonLocaleData extends LocaleData { + /** + * Copy button text + * + * 复制按钮文本 + */ copy?: string + /** + * Copied button text + * + * 已复制按钮文本 + */ copied?: string } diff --git a/plugins/plugin-md-power/src/shared/npmTo.ts b/plugins/plugin-md-power/src/shared/npmTo.ts index b07a90ed..bd4bd8a8 100644 --- a/plugins/plugin-md-power/src/shared/npmTo.ts +++ b/plugins/plugin-md-power/src/shared/npmTo.ts @@ -1,5 +1,20 @@ +/** + * Supported package managers + * + * 支持的包管理器 + */ export type NpmToPackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' +/** + * npm-to options + * + * npm-to 配置选项 + */ export type NpmToOptions = NpmToPackageManager[] | { + /** + * Tabs to display + * + * 要显示的选项卡 + */ tabs?: NpmToPackageManager[] } diff --git a/plugins/plugin-md-power/src/shared/pdf.ts b/plugins/plugin-md-power/src/shared/pdf.ts index 001ca69d..5e13ea5b 100644 --- a/plugins/plugin-md-power/src/shared/pdf.ts +++ b/plugins/plugin-md-power/src/shared/pdf.ts @@ -1,18 +1,60 @@ import type { SizeOptions } from './size' +/** + * PDF embed type + * + * PDF 嵌入类型 + */ export type PDFEmbedType = 'iframe' | 'embed' | 'pdfjs' +/** + * PDF token metadata + * + * PDF 令牌元数据 + */ export interface PDFTokenMeta extends SizeOptions { + /** + * Page number to display + * + * 要显示的页码 + */ page?: number + /** + * Whether to hide toolbar + * + * 是否隐藏工具栏 + */ noToolbar?: boolean + /** + * Zoom level + * + * 缩放级别 + */ zoom?: number + /** + * PDF source URL + * + * PDF 源 URL + */ src?: string + /** + * Title of the PDF + * + * PDF 标题 + */ title?: string } +/** + * PDF options + * + * PDF 配置选项 + */ export interface PDFOptions { /** - * pdfjs url + * PDF.js library URL + * + * PDF.js 库 URL */ pdfjsUrl?: string } diff --git a/plugins/plugin-md-power/src/shared/plugin.ts b/plugins/plugin-md-power/src/shared/plugin.ts index 0879e48f..91b008a4 100644 --- a/plugins/plugin-md-power/src/shared/plugin.ts +++ b/plugins/plugin-md-power/src/shared/plugin.ts @@ -14,11 +14,20 @@ import type { PlotOptions } from './plot.js' import type { ReplOptions } from './repl.js' import type { TableContainerOptions } from './table.js' +/** + * Markdown Power Plugin Options + * + * Markdown Power 插件配置选项 + */ export interface MarkdownPowerPluginOptions { /** + * Whether to preset markdown env, such as preset link references, abbreviations, content annotations, etc. + * * 是否预设 markdown env,如 预设链接引用、缩写词、内容注释等 * + * Presets can be used in any markdown file + * * 预设可以在任何 markdown 文件中使用 * * @example @@ -43,78 +52,98 @@ export interface MarkdownPowerPluginOptions { */ env?: MarkdownEnvPreset /** + * Whether to enable annotation, or preset content annotations + * * 是否启用注释, 或者预设内容注释 * @default false */ annotation?: boolean | MarkdownEnvPreset['annotations'] /** + * Whether to enable abbr syntax, or preset abbreviations + * * 是否启用 abbr 语法, 或者预设缩写词 * @default false */ abbr?: boolean | MarkdownEnvPreset['abbreviations'] /** + * Mark pen animation mode + * * 马克笔动画模式 * @default 'eager' */ mark?: MarkOptions /** + * Whether to enable content snippet encryption container + * * 是否启用 内容片段加密容器 * * @default false */ encrypt?: boolean | EncryptSnippetOptions /** + * Configure code block grouping + * * 配置代码块分组 */ codeTabs?: CodeTabsOptions /** + * Whether to enable npm-to container + * * 是否启用 npm-to 容器 */ npmTo?: boolean | NpmToOptions /** - * 是否启用 PDF 嵌入语法 + * Whether to enable PDF embed syntax * * `@[pdf](pdf_url)` * + * 是否启用 PDF 嵌入语法 + * * @default false */ pdf?: boolean | PDFOptions // new syntax /** - * 是否启用 图标支持 + * Whether to enable icon support * - iconify - `::collect:icon_name::` => `` * - iconfont - `::name::` => `` * - fontawesome - `::fas:name::` => `` * + * 是否启用 图标支持 + * * @default false */ icon?: IconOptions /** - * 是否启用 iconify 图标嵌入语法 + * Whether to enable iconify icon embed syntax * * `::collect:icon_name::` * + * 是否启用 iconify 图标嵌入语法 + * * @default false - * @deprecated use `icon` instead 该配置已弃用,请使用 `icon` 代替 + * @deprecated use `icon` instead / 该配置已弃用,请使用 `icon` 代替 */ icons?: boolean | IconOptions /** - * 是否启用 隐秘文本 语法 + * Whether to enable hidden text syntax * * `!!plot_content!!` * + * 是否启用 隐秘文本 语法 + * * @default false */ plot?: boolean | PlotOptions /** - * 是否启用 timeline 语法 + * Whether to enable timeline syntax * * ```md * ::: timeline @@ -125,12 +154,14 @@ export interface MarkdownPowerPluginOptions { * ::: * ``` * + * 是否启用 timeline 语法 + * * @default false */ timeline?: boolean /** - * 是否启用 collapse 折叠面板 语法 + * Whether to enable collapse folding panel syntax * * ```md * ::: collapse accordion @@ -144,12 +175,14 @@ export interface MarkdownPowerPluginOptions { * ::: * ``` * + * 是否启用 collapse 折叠面板 语法 + * * @default false */ collapse?: boolean /** - * 是否启用 chat 容器 语法 + * Whether to enable chat container syntax * * ```md * ::: chat @@ -162,11 +195,16 @@ export interface MarkdownPowerPluginOptions { * message * ::: * ``` + * + * 是否启用 chat 容器 语法 + * * @default false */ chat?: boolean /** + * Whether to enable field / field-group container + * * 是否启用 field / field-group 容器 * * @default false @@ -174,50 +212,62 @@ export interface MarkdownPowerPluginOptions { field?: boolean // video embed /** - * 是否启用 acfun 视频嵌入 + * Whether to enable acfun video embed * * `@[acfun](acid)` * + * 是否启用 acfun 视频嵌入 + * * @default false */ acfun?: boolean /** - * 是否启用 bilibili 视频嵌入 + * Whether to enable bilibili video embed * * `@[bilibili](bid)` * + * 是否启用 bilibili 视频嵌入 + * * @default false */ bilibili?: boolean /** - * 是否启用 youtube 视频嵌入 + * Whether to enable youtube video embed * * `@[youtube](video_id)` * + * 是否启用 youtube 视频嵌入 + * * @default false */ youtube?: boolean /** - * 是否启用 artPlayer 视频嵌入 + * Whether to enable artPlayer video embed * * `@[artPlayer](url)` + * + * 是否启用 artPlayer 视频嵌入 */ artPlayer?: boolean /** - * 是否启用 audioReader 音频嵌入 + * Whether to enable audioReader audio embed * * `@[audioReader](url)` + * + * 是否启用 audioReader 音频嵌入 */ audioReader?: boolean // code embed /** - * 是否启用 codepen 嵌入 + * Whether to enable codepen embed * * `@[codepen](pen_id)` * + * 是否启用 codepen 嵌入 + * * @default false */ codepen?: boolean @@ -226,30 +276,38 @@ export interface MarkdownPowerPluginOptions { */ replit?: boolean /** - * 是否启用 codeSandbox 嵌入 + * Whether to enable codeSandbox embed * * `@[codesandbox](codesandbox_id)` * + * 是否启用 codeSandbox 嵌入 + * * @default false */ codeSandbox?: boolean /** - * 是否启用 jsfiddle 嵌入 + * Whether to enable jsfiddle embed * * `@[jsfiddle](jsfiddle_id)` * + * 是否启用 jsfiddle 嵌入 + * * @default false */ jsfiddle?: boolean // container /** + * Whether to enable REPL container syntax + * * 是否启用 REPL 容器语法 * * @default false */ repl?: false | ReplOptions /** + * Whether to enable file tree container syntax + * * 是否启用 文件树 容器语法 * * @default false @@ -257,7 +315,7 @@ export interface MarkdownPowerPluginOptions { fileTree?: boolean | FileTreeOptions /** - * 是否启用 代码树 容器语法 和 嵌入语法 + * Whether to enable code tree container syntax and embed syntax * * ```md * ::: code-tree @@ -267,25 +325,35 @@ export interface MarkdownPowerPluginOptions { * `@[code-tree](file_path)` * * + * 是否启用 代码树 容器语法 和 嵌入语法 + * * @default false */ codeTree?: boolean | CodeTreeOptions /** + * Whether to enable demo syntax + * * 是否启用 demo 语法 */ demo?: boolean /** - * 是否启用 caniuse 嵌入语法 + * Whether to enable caniuse embed syntax * * `@[caniuse](feature_name)` * + * 是否启用 caniuse 嵌入语法 + * * @default false */ caniuse?: boolean | CanIUseOptions /** + * Whether to enable table container syntax, providing enhanced functionality for tables + * + * - `copy`: Whether to enable copy functionality, supports copying as html format and markdown format + * * 是否启用 table 容器语法,为表格提供增强功能 * * - `copy`: 是否启用复制功能,支持复制为 html 格式 和 markdown 格式 @@ -295,6 +363,8 @@ export interface MarkdownPowerPluginOptions { table?: boolean | TableContainerOptions /** + * Whether to enable QR code embed syntax + * * 是否启用 二维码 嵌入语法 * * @default false @@ -303,6 +373,21 @@ export interface MarkdownPowerPluginOptions { // enhance /** + * Whether to enable automatic filling of image width and height attributes + * + * __Please note that regardless of whether it is enabled, this feature only takes effect when building production packages__ + * + * - If `true`, equivalent to `'local'` + * - If `local`, only add width and height for local images + * - If `all`, add width and height for all images (including local and remote) + * + * If images load slowly, the process from loading to completion can cause unstable page layout and content flickering. + * This feature solves this problem by adding `width` and `height` attributes to images. + * + * Please use the `all` option with caution. This option will initiate network requests during the build phase, + * attempting to load remote images to obtain image size information, + * which may cause build times to become longer (fortunately, obtaining size information only requires loading a few KB of image data packets, so the time consumption will not be too long) + * * 是否启用 自动填充 图片宽高属性 * * __请注意,无论是否启用,该功能仅在构建生产包时生效__ diff --git a/plugins/plugin-md-power/src/shared/qrcode.ts b/plugins/plugin-md-power/src/shared/qrcode.ts index 5af63ff1..c6680cab 100644 --- a/plugins/plugin-md-power/src/shared/qrcode.ts +++ b/plugins/plugin-md-power/src/shared/qrcode.ts @@ -1,27 +1,51 @@ +/** + * QR code metadata + * + * 二维码元数据 + */ export interface QRCodeMeta extends QRCodeProps { /** + * Alias for mode: 'card' + * * mode: 'card' 的别名 */ card?: boolean } +/** + * QR code props + * + * 二维码属性 + */ export interface QRCodeProps { /** + * QR code title + * Used as HTML `title` and `alt` attributes + * * 二维码标题 * 作为 HTML 标签的 `title` 属性、`alt` 属性 */ title?: string /** + * QR code content + * * 二维码内容 */ text?: string /** + * QR code width + * * 二维码宽度 */ width?: number | string /** + * Display mode + * - img: Display QR code as image + * - card: Display as card with left-right layout, QR code on left, title + content on right + * @default 'img' + * * 显示模式 * - img: 以图片的形式显示二维码 * - card: 以卡片的形式显示,卡片以左右布局,左侧二维码,右侧 标题 + 内容 @@ -30,50 +54,79 @@ export interface QRCodeProps { mode?: 'img' | 'card' /** + * Whether to reverse layout in card mode + * * 在 card 模式下是否翻转布局 */ reverse?: boolean /** + * QR code alignment + * @default 'left' + * * 二维码的对齐方式 * @default 'left' */ align?: 'left' | 'center' | 'right' /** + * Whether to render as SVG format + * Default output is PNG format dataURL + * @default false + * * 是否渲染为 SVG 格式的二维码 * 默认输出为 PNG 格式的 dataURL * @default false */ svg?: boolean /** + * Error correction level. + * Possible values: Low, Medium, Quartile, High, corresponding to L, M, Q, H. + * @default 'M' + * * 纠错等级。 * 可能的取值为低、中、四分位、高,分别对应 L、M、Q、H。 * @default 'M' */ level?: 'L' | 'M' | 'Q' | 'H' | 'l' | 'm' | 'q' | 'h' /** + * QR code version. If not specified, will automatically calculate more suitable value. + * Range: 1-40 + * * 二维码版本。若未指定,将自动计算更合适的值。 * 取值范围 1-40 */ version?: number /** + * Mask pattern used to mask symbols. + * Possible values: 0, 1, 2, 3, 4, 5, 6, 7. + * If not specified, system will automatically calculate more suitable value. + * * 用于遮蔽符号的掩码模式。 * 可能的取值为0、1、2、3、4、5、6、7。 * 若未指定,系统将自动计算更合适的值。 */ mask?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 /** + * Define how wide the quiet zone should be. + * @default 4 + * * 定义静区应有多宽。 * @default 4 */ margin?: number /** + * Scale factor. Value of 1 means 1 pixel per module (black dot). + * * 缩放因子。值为1表示每个模块(黑点)对应1像素。 */ scale?: number /** + * Color of dark modules. Value must be in hexadecimal format (RGBA). + * Note: Dark should always be darker than light module color. + * @default '#000000ff' + * * 暗色模块的颜色。值必须为十六进制格式(RGBA)。 * 注意:暗色应始终比浅色模块的颜色更深。 * @default '#000000ff' @@ -81,6 +134,10 @@ export interface QRCodeProps { light?: string /** + * Color of light modules. Value must be in hexadecimal format (RGBA). + * Note: Light should always be lighter than dark module color. + * @default '#ffffffff' + * * 亮色模块的颜色。值必须为十六进制格式(RGBA)。 * 注意:亮色应始终比暗色模块的颜色更浅。 * @default '#ffffffff' diff --git a/plugins/plugin-md-power/src/shared/repl.ts b/plugins/plugin-md-power/src/shared/repl.ts index e0ef3f03..30874e94 100644 --- a/plugins/plugin-md-power/src/shared/repl.ts +++ b/plugins/plugin-md-power/src/shared/repl.ts @@ -1,28 +1,88 @@ import type { BuiltinTheme, ThemeRegistration } from 'shiki' +/** + * Theme options for REPL + * + * REPL 主题选项 + */ export type ThemeOptions = | BuiltinTheme | { + /** + * Light theme + * + * 浅色主题 + */ light: BuiltinTheme + /** + * Dark theme + * + * 深色主题 + */ dark: BuiltinTheme } +/** + * REPL options + * + * REPL 配置选项 + */ export interface ReplOptions { + /** + * Theme for code editor + * + * 代码编辑器主题 + */ theme?: ThemeOptions + /** + * Whether to enable Go language support + * + * 是否启用 Go 语言支持 + */ go?: boolean + /** + * Whether to enable Kotlin language support + * + * 是否启用 Kotlin 语言支持 + */ kotlin?: boolean + /** + * Whether to enable Rust language support + * + * 是否启用 Rust 语言支持 + */ rust?: boolean + /** + * Whether to enable Python language support + * + * 是否启用 Python 语言支持 + */ python?: boolean } +/** + * REPL editor data + * + * REPL 编辑器数据 + */ export interface ReplEditorData { + /** + * Grammar definitions for languages + * + * 语言的语法定义 + */ grammars: { go?: any kotlin?: any rust?: any python?: any } + /** + * Theme registration + * + * 主题注册 + */ theme: ThemeRegistration | { light: ThemeRegistration dark: ThemeRegistration diff --git a/plugins/plugin-md-power/src/shared/size.ts b/plugins/plugin-md-power/src/shared/size.ts index b679c8e4..e4e87577 100644 --- a/plugins/plugin-md-power/src/shared/size.ts +++ b/plugins/plugin-md-power/src/shared/size.ts @@ -1,5 +1,25 @@ +/** + * Size Options + * + * 尺寸选项 + */ export interface SizeOptions { + /** + * Width + * + * 宽度 + */ width?: string + /** + * Height + * + * 高度 + */ height?: string + /** + * Aspect ratio + * + * 宽高比 + */ ratio?: number | string } diff --git a/plugins/plugin-md-power/src/shared/video.ts b/plugins/plugin-md-power/src/shared/video.ts index 781e2f49..5e4fe338 100644 --- a/plugins/plugin-md-power/src/shared/video.ts +++ b/plugins/plugin-md-power/src/shared/video.ts @@ -1,41 +1,191 @@ import type { SizeOptions } from './size' +/** + * Video options + * + * 视频配置选项 + */ export interface VideoOptions { + /** + * Whether to enable Bilibili video embed + * + * 是否启用 Bilibili 视频嵌入 + */ bilibili?: boolean + /** + * Whether to enable YouTube video embed + * + * 是否启用 YouTube 视频嵌入 + */ youtube?: boolean } +/** + * AcFun token metadata + * + * AcFun 令牌元数据 + */ export interface AcFunTokenMeta extends SizeOptions { + /** + * Video title + * + * 视频标题 + */ title?: string + /** + * Video ID + * + * 视频 ID + */ id: string } +/** + * Bilibili token metadata + * + * Bilibili 令牌元数据 + */ export interface BilibiliTokenMeta extends SizeOptions { + /** + * Video title + * + * 视频标题 + */ title?: string + /** + * BV ID + * + * BV ID + */ bvid?: string + /** + * AV ID + * + * AV ID + */ aid?: string + /** + * CID + * + * CID + */ cid?: string + /** + * Whether to autoplay + * + * 是否自动播放 + */ autoplay?: boolean + /** + * Start time + * + * 开始时间 + */ time?: string | number + /** + * Page number + * + * 页码 + */ page?: number } +/** + * YouTube token metadata + * + * YouTube 令牌元数据 + */ export interface YoutubeTokenMeta extends SizeOptions { + /** + * Video title + * + * 视频标题 + */ title?: string + /** + * Video ID + * + * 视频 ID + */ id: string + /** + * Whether to autoplay + * + * 是否自动播放 + */ autoplay?: boolean + /** + * Whether to loop + * + * 是否循环播放 + */ loop?: boolean + /** + * Start time + * + * 开始时间 + */ start?: string | number + /** + * End time + * + * 结束时间 + */ end?: string | number } +/** + * ArtPlayer token metadata + * + * ArtPlayer 令牌元数据 + */ export interface ArtPlayerTokenMeta extends SizeOptions { + /** + * Whether muted + * + * 是否静音 + */ muted?: boolean + /** + * Whether to autoplay + * + * 是否自动播放 + */ autoplay?: boolean + /** + * Whether auto mini mode + * + * 是否自动迷你模式 + */ autoMini?: boolean + /** + * Whether to loop + * + * 是否循环播放 + */ loop?: boolean - volume?: number // 0-1 + /** + * Volume level (0-1) + * + * 音量级别 (0-1) + */ + volume?: number + /** + * Poster image URL + * + * 封面图片 URL + */ poster?: string + /** + * Video URL + * + * 视频 URL + */ url: string + /** + * Video type + * + * 视频类型 + */ type?: string } diff --git a/theme/src/client/composables/dark-mode.ts b/theme/src/client/composables/dark-mode.ts index 07daf76c..79971e74 100644 --- a/theme/src/client/composables/dark-mode.ts +++ b/theme/src/client/composables/dark-mode.ts @@ -3,12 +3,31 @@ import { useDark, useEventListener } from '@vueuse/core' import { inject, ref } from 'vue' import { useThemeData } from './theme-data.js' +/** + * Dark mode reference type + * + * 暗黑模式引用类型 + */ type DarkModeRef = Ref +/** + * Injection key for dark mode + * + * 暗黑模式的注入键 + */ export const darkModeSymbol: InjectionKey = Symbol( __VUEPRESS_DEV__ ? 'darkMode' : '', ) +/** + * Check if view transitions are enabled and supported + * Considers prefers-reduced-motion preference + * + * 检查视图过渡是否启用且受支持 + * 考虑 prefers-reduced-motion 偏好设置 + * + * @returns Whether transitions are enabled / 过渡是否启用 + */ export function enableTransitions(): boolean { if (typeof document === 'undefined') return false @@ -16,6 +35,15 @@ export function enableTransitions(): boolean { && window.matchMedia('(prefers-reduced-motion: no-preference)').matches } +/** + * Setup dark mode for the Vue application + * Configures dark mode based on theme settings and user preferences + * + * 为 Vue 应用设置暗黑模式 + * 根据主题设置和用户偏好配置暗黑模式 + * + * @param app - Vue application instance / Vue 应用实例 + */ export function setupDarkMode(app: App): void { const theme = useThemeData() @@ -51,6 +79,7 @@ export function setupDarkMode(app: App): void { get: () => isDark, }) + // Handle print events - switch to light mode for printing useEventListener('beforeprint', () => { if (isDark.value) document.documentElement.dataset.theme = 'light' @@ -63,7 +92,13 @@ export function setupDarkMode(app: App): void { } /** - * Inject dark mode global computed + * Use dark mode + * Returns the dark mode reactive reference + * + * 获取暗黑模式状态 + * + * @returns Dark mode reference / 暗黑模式引用 + * @throws Error if called without provider / 如果没有提供者则抛出错误 */ export function useDarkMode(): DarkModeRef { const isDarkMode = inject(darkModeSymbol) diff --git a/theme/src/client/composables/data.ts b/theme/src/client/composables/data.ts index 82a5a06b..9ddd0c77 100644 --- a/theme/src/client/composables/data.ts +++ b/theme/src/client/composables/data.ts @@ -45,6 +45,11 @@ export interface Data } +/** + * Use data + * + * 获取页面数据,包括主题配置、页面数据、frontmatter、语言、站点信息、暗黑模式和集合信息 + */ export function useData(): Data { const theme = useThemeLocaleData() const page = usePageData() diff --git a/theme/src/client/composables/encrypt-data.ts b/theme/src/client/composables/encrypt-data.ts index 85019895..ef4a194d 100644 --- a/theme/src/client/composables/encrypt-data.ts +++ b/theme/src/client/composables/encrypt-data.ts @@ -3,34 +3,94 @@ import { encrypt as rawEncrypt } from '@internal/encrypt' import { decodeData } from '@vuepress/helper/client' import { ref } from 'vue' +/** + * Encrypt configuration tuple type + * Contains keys, rules, global flag, and admin passwords + * + * 加密配置元组类型 + * 包含密钥、规则、全局标志和管理员密码 + */ export type EncryptConfig = readonly [ - keys: string, // keys - rules: string, // rules - global: number, // global - admin: string, // admin + keys: string, // keys / 密钥 + rules: string, // rules / 规则 + global: number, // global / 全局标志 + admin: string, // admin / 管理员密码 ] +/** + * Encrypt data rule interface + * Defines a single encryption rule with matching pattern and passwords + * + * 加密数据规则接口 + * 定义单个加密规则,包含匹配模式和密码 + */ export interface EncryptDataRule { + /** Unique key for the rule / 规则的唯一键 */ key: string + /** Match pattern for the rule / 规则的匹配模式 */ match: string + /** Array of valid passwords / 有效密码数组 */ rules: string[] } +/** + * Encrypt data interface + * Contains all encryption configuration and rules + * + * 加密数据接口 + * 包含所有加密配置和规则 + */ export interface EncryptData { + /** Whether global encryption is enabled / 是否启用全局加密 */ global: boolean + /** Array of admin password hashes / 管理员密码哈希数组 */ admins: string[] + /** Array of match patterns / 匹配模式数组 */ matches: string[] + /** Array of encryption rules / 加密规则数组 */ ruleList: EncryptDataRule[] } +/** + * Encrypt data reference type + * + * 加密数据引用类型 + */ export type EncryptRef = Ref +/** + * Global encrypt data reference + * + * 全局加密数据引用 + */ export const encrypt: EncryptRef = ref(resolveEncryptData(rawEncrypt)) +/** + * Use encrypt data + * Returns the global encrypt data reference + * + * 获取加密数据 + * 返回全局加密数据引用 + * + * @returns Encrypt data reference / 加密数据引用 + */ export function useEncryptData(): EncryptRef { return encrypt as EncryptRef } +/** + * Resolve encrypt data from raw configuration + * Decodes and parses the raw encrypt configuration + * + * 从原始配置解析加密数据 + * 解码并解析原始加密配置 + * @param data - Raw encrypt configuration / 原始加密配置 + * @param data."0" rawKeys - Encoded keys string / 编码的密钥字符串 + * @param data."1" rawRules - Encoded rules string / 编码的规则字符串 + * @param data."2" global - Global encryption flag / 全局加密标志 + * @param data."3" admin - Admin passwords string / 管理员密码字符串 + * @returns Parsed encrypt data / 解析后的加密数据 + */ function resolveEncryptData( [rawKeys, rawRules, global, admin]: EncryptConfig, ): EncryptData { @@ -49,6 +109,14 @@ function resolveEncryptData( } } +/** + * Unwrap and decode data from raw string + * + * 从原始字符串解包和解码数据 + * + * @param raw - Raw encoded string / 原始编码字符串 + * @returns Parsed data / 解析后的数据 + */ function unwrapData(raw: string): T { return JSON.parse(decodeData(raw)) as T } diff --git a/theme/src/client/composables/encrypt.ts b/theme/src/client/composables/encrypt.ts index 485fc620..9b268ef9 100644 --- a/theme/src/client/composables/encrypt.ts +++ b/theme/src/client/composables/encrypt.ts @@ -8,17 +8,40 @@ import { removeLeadingSlash } from 'vuepress/shared' import { useData } from './data.js' import { useEncryptData } from './encrypt-data.js' +/** + * Encrypt interface + * Provides encryption-related reactive states and properties + * + * 加密接口 + * 提供加密相关的响应式状态和属性 + */ export interface Encrypt { + /** Whether the current page has encryption / 当前页面是否有加密 */ hasPageEncrypt: Ref + /** Whether global encryption is decrypted / 全局加密是否已解密 */ isGlobalDecrypted: Ref + /** Whether page encryption is decrypted / 页面加密是否已解密 */ isPageDecrypted: Ref + /** List of encryption rules for the current page / 当前页面的加密规则列表 */ hashList: Ref } +/** + * Injection key for encrypt functionality + * + * 加密功能的注入键 + */ export const EncryptSymbol: InjectionKey = Symbol( __VUEPRESS_DEV__ ? 'Encrypt' : '', ) +/** + * Session storage for encryption state + * Stores global and page decryption states + * + * 加密状态的会话存储 + * 存储全局和页面解密状态 + */ const storage = useSessionStorage('2a0a3d6afb2fdf1f', () => { if (__VUEPRESS_SSR__) { return { g: '', p: [] as string[] } @@ -29,8 +52,27 @@ const storage = useSessionStorage('2a0a3d6afb2fdf1f', () => { } }) +/** + * Cache for password comparison results + * Improves performance by caching bcrypt verification results + * + * 密码比较结果的缓存 + * 通过缓存 bcrypt 验证结果提高性能 + */ const compareCache = new Map() const separator = ':' + +/** + * Compare password with hash using bcrypt + * Caches results to avoid repeated computations + * + * 使用 bcrypt 比较密码和哈希 + * 缓存结果以避免重复计算 + * + * @param content - Password to verify / 要验证的密码 + * @param hash - Bcrypt hash to compare against / 要比较的 bcrypt 哈希 + * @returns Whether the password matches / 密码是否匹配 + */ async function compareDecrypt(content: string, hash: string): Promise { const key = [content, hash].join(separator) if (compareCache.has(key)) @@ -47,7 +89,23 @@ async function compareDecrypt(content: string, hash: string): Promise { } } +/** + * Cache for regex patterns + * Improves performance by caching compiled regexes + * + * 正则表达式模式的缓存 + * 通过缓存编译后的正则表达式提高性能 + */ const matchCache = new Map() + +/** + * Create or retrieve cached regex pattern + * + * 创建或获取缓存的正则表达式模式 + * + * @param match - Pattern string / 模式字符串 + * @returns Compiled regex / 编译后的正则表达式 + */ function createMatchRegex(match: string) { if (matchCache.has(match)) return matchCache.get(match)! @@ -57,6 +115,18 @@ function createMatchRegex(match: string) { return regex } +/** + * Check if a match pattern applies to the current page + * Supports regex patterns (starting with ^) and path patterns + * + * 检查匹配模式是否适用于当前页面 + * 支持正则表达式模式(以 ^ 开头)和路径模式 + * + * @param match - Match pattern / 匹配模式 + * @param pagePath - Current page path / 当前页面路径 + * @param filePathRelative - Relative file path / 相对文件路径 + * @returns Whether the pattern matches / 模式是否匹配 + */ function toMatch(match: string, pagePath: string, filePathRelative?: string | null) { const relativePath = filePathRelative || '' if (match[0] === '^') { @@ -69,11 +139,22 @@ function toMatch(match: string, pagePath: string, filePathRelative?: string | nu return pagePath.startsWith(match) || relativePath.startsWith(removeLeadingSlash(match)) } +/** + * Setup encrypt functionality for the application + * Initializes encryption state and provides it to child components + * + * 为应用程序设置加密功能 + * 初始化加密状态并提供给子组件 + */ export function setupEncrypt(): void { const { page } = useData() const route = useRoute() const encrypt = useEncryptData() + /** + * Whether the current page has encryption enabled + * Checks page-specific encryption and rule-based encryption + */ const hasPageEncrypt = computed(() => { const pagePath = route.path const filePathRelative = page.value.filePathRelative @@ -85,6 +166,10 @@ export function setupEncrypt(): void { : false }) + /** + * Whether global encryption is decrypted + * Checks if any admin password hash matches the stored hash + */ const isGlobalDecrypted = computedAsync(async () => { const hash = storage.value.g if (!encrypt.value.global) @@ -97,6 +182,10 @@ export function setupEncrypt(): void { return false }, !encrypt.value.global) + /** + * List of encryption rules applicable to the current page + * Includes page-specific rules and matching pattern rules + */ const hashList = computed(() => { const pagePath = route.path const filePathRelative = page.value.filePathRelative @@ -112,6 +201,10 @@ export function setupEncrypt(): void { return [pageRule, ...rules].filter(Boolean) as EncryptDataRule[] }) + /** + * Whether the current page is decrypted + * Checks admin passwords and page-specific passwords + */ const isPageDecrypted = computedAsync(async () => { if (!hasPageEncrypt.value) return true @@ -142,6 +235,15 @@ export function setupEncrypt(): void { }) } +/** + * Use encrypt + * Returns the encryption state and properties + * + * 获取加密功能的状态和方法,包括全局解密、页面解密、密码验证等 + * + * @returns Encrypt state and properties / 加密状态和属性 + * @throws Error if called without setup / 如果没有设置则抛出错误 + */ export function useEncrypt(): Encrypt { const result = inject(EncryptSymbol) @@ -151,6 +253,14 @@ export function useEncrypt(): Encrypt { return result } +/** + * Use encrypt compare + * Provides password verification functions for global and page encryption + * + * 密码比较功能,提供全局密码和页面密码的验证方法 + * + * @returns Object with compareGlobal and comparePage functions / 包含 compareGlobal 和 comparePage 函数的对象 + */ export function useEncryptCompare(): { compareGlobal: (password: string) => Promise comparePage: (password: string) => Promise @@ -160,6 +270,13 @@ export function useEncryptCompare(): { const route = useRoute() const { hashList } = useEncrypt() + /** + * Compare global password + * Verifies against admin passwords + * + * @param password - Password to verify / 要验证的密码 + * @returns Whether the password is valid / 密码是否有效 + */ async function compareGlobal(password: string): Promise { if (!password) return false @@ -174,6 +291,13 @@ export function useEncryptCompare(): { return false } + /** + * Compare page password + * Verifies against page-specific rules and falls back to global password + * + * @param password - Password to verify / 要验证的密码 + * @returns Whether the password is valid / 密码是否有效 + */ async function comparePage(password: string): Promise { if (!password) return false diff --git a/theme/src/client/composables/layout.ts b/theme/src/client/composables/layout.ts index a2d02dfb..f17e02b1 100644 --- a/theme/src/client/composables/layout.ts +++ b/theme/src/client/composables/layout.ts @@ -10,6 +10,11 @@ import { useCloseSidebarOnEscape, useSidebarControl } from './sidebar.js' const is960 = shallowRef(false) const is1280 = shallowRef(false) +/** + * Use layout + * + * 获取当前页面布局相关信息,包括是否首页、是否有侧边栏、是否显示目录等 + */ export function useLayout() { const { frontmatter, theme } = useData() const { isPageDecrypted } = useEncrypt() diff --git a/theme/src/client/composables/link.ts b/theme/src/client/composables/link.ts index c9e521d4..0f83a9b2 100644 --- a/theme/src/client/composables/link.ts +++ b/theme/src/client/composables/link.ts @@ -4,19 +4,44 @@ import { computed, toValue } from 'vue' import { resolveRoute, resolveRouteFullPath, useRoute } from 'vuepress/client' import { useData } from './data.js' +/** + * Link resolution result interface + * Provides information about the resolved link + * + * 链接解析结果接口 + * 提供有关解析后链接的信息 + */ interface UseLinkResult { /** - * 外部链接 + * Whether the link is external + * 是否为外部链接 */ isExternal: ComputedRef /** - * 外部链接协议 + * Whether the link uses an external protocol + * Does not include target="_blank" cases + * 是否使用外部链接协议 * 此项不包含 target="_blank" 的情况 */ isExternalProtocol: ComputedRef + /** + * The resolved link URL + * 解析后的链接 URL + */ link: ComputedRef } +/** + * Use link + * Resolves and processes a link URL with smart handling of internal/external links + * + * 使用链接 + * 解析并处理链接 URL,智能处理内部/外部链接 + * + * @param href - Link URL or reference / 链接 URL 或引用 + * @param target - Link target or reference / 链接目标或引用 + * @returns Link resolution result / 链接解析结果 + */ export function useLink( href: MaybeRefOrGetter, target?: MaybeRefOrGetter, @@ -24,6 +49,8 @@ export function useLink( const route = useRoute() const { page } = useData() + // Pre-determine if it can be considered an external link + // At this point, it cannot be fully confirmed if it must be an internal link // 预判断是否可以直接认为是外部链接 // 在此时并不能完全确认是否一定是内部链接 const maybeIsExternal = computed(() => { @@ -36,6 +63,7 @@ export function useLink( return false }) + // Pre-process link, try to convert to internal link // 预处理链接,尝试转为内部的链接 const preProcessLink = computed(() => { const link = toValue(href) @@ -45,6 +73,8 @@ export function useLink( const currentPath = page.value.filePathRelative ? `/${page.value.filePathRelative}` : undefined const path = resolveRouteFullPath(link, currentPath) if (path.includes('#')) { + // Compare path + anchor with current route path + // Convert to anchor link to avoid page refresh // 将路径 + 锚点 与 当前路由路径进行比较 // 转为锚点链接,避免页面发生刷新 if (path.slice(0, path.indexOf('#')) === route.path) { @@ -62,6 +92,7 @@ export function useLink( if (!link || link[0] === '#') return false + // Check if it's a non-existent route // 判断是否为不存在的路由 const routePath = link.split(/[?#]/)[0] const currentPath = page.value.filePathRelative ? `/${page.value.filePathRelative}` : undefined @@ -74,6 +105,7 @@ export function useLink( }) const link = computed(() => { + // Keep external links as-is // 外部链接保持原样 if (isExternal.value) { return toValue(href) diff --git a/theme/src/client/composables/nav.ts b/theme/src/client/composables/nav.ts index f5e82463..2d8af2a8 100644 --- a/theme/src/client/composables/nav.ts +++ b/theme/src/client/composables/nav.ts @@ -10,12 +10,32 @@ import { useRoute } from 'vuepress/client' import { normalizeLink, resolveNavLink } from '../utils/index.js' import { useData } from './data.js' +/** + * Use navbar data + * Returns the resolved navbar items based on theme configuration + * + * 获取导航栏数据 + * 根据主题配置返回解析后的导航栏项目 + * + * @returns Reactive reference to resolved navbar items / 解析后的导航栏项目的响应式引用 + */ export function useNavbarData(): Ref { const { theme } = useData() return computed(() => resolveNavbar(theme.value.navbar || [])) } +/** + * Resolve navbar configuration to resolved items + * Recursively processes navbar items and resolves links + * + * 将导航栏配置解析为解析后的项目 + * 递归处理导航栏项目并解析链接 + * + * @param navbar - Raw navbar configuration / 原始导航栏配置 + * @param _prefix - URL prefix for nested items / 嵌套项目的 URL 前缀 + * @returns Array of resolved navbar items / 解析后的导航栏项目数组 + */ function resolveNavbar(navbar: ThemeNavItem[], _prefix = ''): ResolvedNavItem[] { const resolved: ResolvedNavItem[] = [] navbar.forEach((item) => { @@ -40,26 +60,56 @@ function resolveNavbar(navbar: ThemeNavItem[], _prefix = ''): ResolvedNavItem[] return resolved } +/** + * Navigation control return type + * Provides state and methods for mobile navigation menu + * + * 导航控制返回类型 + * 提供移动端导航菜单的状态和方法 + */ export interface UseNavReturn { + /** Whether the mobile screen menu is open / 移动端屏幕菜单是否打开 */ isScreenOpen: Ref + /** Open the mobile screen menu / 打开移动端屏幕菜单 */ openScreen: () => void + /** Close the mobile screen menu / 关闭移动端屏幕菜单 */ closeScreen: () => void + /** Toggle the mobile screen menu / 切换移动端屏幕菜单 */ toggleScreen: () => void } +/** + * Use nav + * Provides mobile navigation menu control functionality + * + * 导航栏状态控制,提供移动端导航菜单的打开、关闭和切换功能 + * + * @returns Navigation control state and methods / 导航控制状态和方法 + */ export function useNav(): UseNavReturn { const isScreenOpen = ref(false) + /** + * Open the mobile navigation screen + * Adds resize listener to auto-close on larger screens + */ function openScreen(): void { isScreenOpen.value = true window.addEventListener('resize', closeScreenOnTabletWindow) } + /** + * Close the mobile navigation screen + * Removes resize listener + */ function closeScreen(): void { isScreenOpen.value = false window.removeEventListener('resize', closeScreenOnTabletWindow) } + /** + * Toggle the mobile navigation screen + */ function toggleScreen(): void { if (isScreenOpen.value) { closeScreen() @@ -71,6 +121,7 @@ export function useNav(): UseNavReturn { /** * Close screen when the user resizes the window wider than tablet size. + * Automatically closes the mobile menu on larger screens. */ function closeScreenOnTabletWindow(): void { if (window.outerWidth >= 768) { diff --git a/theme/src/client/composables/outline.ts b/theme/src/client/composables/outline.ts index 9ab2730f..843820dc 100644 --- a/theme/src/client/composables/outline.ts +++ b/theme/src/client/composables/outline.ts @@ -7,46 +7,81 @@ import { onContentUpdated, useRouter } from 'vuepress/client' import { useData } from './data.js' import { useLayout } from './layout.js' +/** + * Header interface representing a page heading + * + * Header 接口表示页面标题 + */ export interface Header { /** * The level of the header - * * `1` to `6` for `

` to `

` + * + * 标题级别 + * `1` 到 `6` 对应 `

` 到 `

` */ level: number /** * The title of the header + * + * 标题文本 */ title: string /** * The slug of the header - * * Typically the `id` attr of the header anchor + * + * 标题的 slug + * 通常是标题锚点的 `id` 属性 */ slug: string /** * Link of the header - * * Typically using `#${slug}` as the anchor hash + * + * 标题链接 + * 通常使用 `#${slug}` 作为锚点哈希 */ link: string /** * The children of the header + * + * 子标题 */ children: Header[] } // cached list of anchor elements from resolveHeaders +// 从 resolveHeaders 缓存的锚点元素列表 const resolvedHeaders: { element: HTMLHeadElement, link: string }[] = [] +/** + * Menu item type for outline navigation + * Extends Header with element reference and additional properties + * + * 目录导航的菜单项类型 + * 扩展 Header 并添加元素引用和额外属性 + */ export type MenuItem = Omit & { + /** Reference to the DOM element / DOM 元素引用 */ element: HTMLHeadElement + /** Child menu items / 子菜单项 */ children?: MenuItem[] + /** Lowest level for outline display / 目录显示的最低级别 */ lowLevel?: number } const headers = ref([]) +/** + * Setup headers for the current page + * Initializes header extraction based on frontmatter and theme configuration + * + * 为当前页面设置标题 + * 根据 frontmatter 和主题配置初始化标题提取 + * + * @returns Reference to the headers array / 标题数组的引用 + */ export function setupHeaders(): Ref { const { frontmatter, theme } = useData() @@ -57,10 +92,28 @@ export function setupHeaders(): Ref { return headers } +/** + * Use headers + * Returns the reactive headers reference for the current page + * + * 获取当前页面的标题列表 + * + * @returns Reactive reference to menu items / 菜单项的响应式引用 + */ export function useHeaders(): Ref { return headers } +/** + * Get headers from the page content + * Extracts and filters headings based on the outline configuration + * + * 从页面内容获取标题 + * 根据目录配置提取和过滤标题 + * + * @param range - Outline configuration for header levels / 标题级别的目录配置 + * @returns Array of menu items representing headers / 表示标题的菜单项数组 + */ export function getHeaders(range?: ThemeOutline): MenuItem[] { const heading = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] const ignores = Array.from(document.querySelectorAll( @@ -87,6 +140,14 @@ export function getHeaders(range?: ThemeOutline): MenuItem[] { return resolveSubRangeHeader(resolveHeaders(headers, high), low) } +/** + * Get the header level range from outline configuration + * + * 从目录配置获取标题级别范围 + * + * @param range - Outline configuration / 目录配置 + * @returns Tuple of [high, low] levels / [高, 低] 级别元组 + */ function getRange(range?: Exclude): readonly [number, number] { const levelsRange = range || 2 // [high, low] @@ -97,6 +158,17 @@ function getRange(range?: Exclude): readonly [number, num : levelsRange } +/** + * Get the lowest outline level for a specific header + * Checks for data-outline or outline attributes + * + * 获取特定标题的最低目录级别 + * 检查 data-outline 或 outline 属性 + * + * @param el - Header element / 标题元素 + * @param level - Current header level / 当前标题级别 + * @returns Lowest level or undefined / 最低级别或未定义 + */ function getLowLevel(el: HTMLHeadElement, level: number): number | undefined { if (!el.hasAttribute('data-outline') && !el.hasAttribute('outline')) return @@ -114,6 +186,16 @@ function getLowLevel(el: HTMLHeadElement, level: number): number | undefined { return undefined } +/** + * Serialize a header element to text + * Extracts text content while ignoring badges and ignored elements + * + * 将标题元素序列化为文本 + * 提取文本内容,同时忽略徽章和被忽略的元素 + * + * @param h - Header element / 标题元素 + * @returns Serialized header text / 序列化的标题文本 + */ function serializeHeader(h: Element): string { // title const anchor = h.firstChild @@ -146,6 +228,15 @@ function serializeHeader(h: Element): string { return ret.trim() } +/** + * Clear ignored nodes from a list of child nodes + * Recursively removes elements with 'ignore-header' class + * + * 从子节点列表中清除被忽略的节点 + * 递归移除带有 'ignore-header' 类的元素 + * + * @param list - Array of child nodes / 子节点数组 + */ function clearHeaderNodeList(list?: ChildNode[]) { if (list?.length) { for (const node of list) { @@ -161,6 +252,17 @@ function clearHeaderNodeList(list?: ChildNode[]) { } } +/** + * Resolve headers into a hierarchical structure + * Organizes flat headers into nested menu items based on levels + * + * 将标题解析为层次结构 + * 根据级别将平面标题组织为嵌套菜单项 + * + * @param headers - Flat array of menu items / 平面菜单项数组 + * @param high - Minimum header level to include / 要包含的最小标题级别 + * @returns Hierarchical array of menu items / 层次化的菜单项数组 + */ export function resolveHeaders(headers: MenuItem[], high: number): MenuItem[] { headers = headers.filter(h => h.level >= high) // clear previous caches @@ -175,6 +277,7 @@ export function resolveHeaders(headers: MenuItem[], high: number): MenuItem[] { const cur = headers[i] if (i === 0) { ret.push(cur) + continue } else { for (let j = i - 1; j >= 0; j--) { @@ -192,6 +295,17 @@ export function resolveHeaders(headers: MenuItem[], high: number): MenuItem[] { return ret } +/** + * Resolve sub range header + * Filters children headers based on the lowest level + * + * 解析子范围标题 + * 根据最低级别过滤子标题 + * + * @param headers - Array of menu items / 菜单项数组 + * @param low - Lowest level to include / 要包含的最低级别 + * @returns Filtered menu items / 过滤后的菜单项 + */ function resolveSubRangeHeader(headers: MenuItem[], low: number): MenuItem[] { return headers.map((header) => { if (header.children?.length) { @@ -203,6 +317,15 @@ function resolveSubRangeHeader(headers: MenuItem[], low: number): MenuItem[] { }) } +/** + * Use active anchor + * Tracks scroll position and updates the active outline item + * + * 活跃锚点处理,监听滚动并更新目录中高亮的锚点 + * + * @param container - Reference to the outline container / 目录容器的引用 + * @param marker - Reference to the active marker element / 活动标记元素的引用 + */ export function useActiveAnchor(container: Ref, marker: Ref): void { const { isAsideEnabled } = useLayout() const router = useRouter() @@ -210,6 +333,10 @@ export function useActiveAnchor(container: Ref, marker: Ref< let prevActiveLink: HTMLAnchorElement | null = null + /** + * Set the active link based on scroll position + * Determines which header is currently in view + */ const setActiveLink = (): void => { if (!isAsideEnabled.value) return @@ -257,6 +384,12 @@ export function useActiveAnchor(container: Ref, marker: Ref< activateLink(activeLink) } + /** + * Activate a specific link in the outline + * Updates visual indicators and marker position + * + * @param hash - Hash of the link to activate / 要激活的链接哈希 + */ function activateLink(hash: string | null): void { routeHash.value = hash || '' if (prevActiveLink) @@ -311,6 +444,16 @@ export function useActiveAnchor(container: Ref, marker: Ref< }) } +/** + * Get the absolute top position of an element + * Accounts for fixed positioned ancestors + * + * 获取元素的绝对顶部位置 + * 考虑固定定位的祖先元素 + * + * @param element - Element to measure / 要测量的元素 + * @returns Absolute top position or NaN / 绝对顶部位置或 NaN + */ function getAbsoluteTop(element: HTMLElement): number { let offsetTop = 0 while (element && element !== document.body) { @@ -324,7 +467,14 @@ function getAbsoluteTop(element: HTMLElement): number { } /** - * Update current hash and do not trigger `scrollBehavior` + * Update current hash without triggering scrollBehavior + * Temporarily disables scroll behavior during hash update + * + * 更新当前哈希而不触发 scrollBehavior + * 在哈希更新期间临时禁用滚动行为 + * + * @param router - Vue Router instance / Vue Router 实例 + * @param hash - New hash value / 新的哈希值 */ async function updateHash(router: Router, hash: string): Promise { const { path, query } = router.currentRoute.value diff --git a/theme/src/client/composables/page.ts b/theme/src/client/composables/page.ts index d7977f0d..80b66e5d 100644 --- a/theme/src/client/composables/page.ts +++ b/theme/src/client/composables/page.ts @@ -2,6 +2,11 @@ import type { ComputedRef } from 'vue' import { computed } from 'vue' import { useData } from './data.js' +/** + * Use posts page data + * + * 获取文章页面数据,判断当前页面是否为文章类型或文章列表布局 + */ export function usePostsPageData(): { isPosts: ComputedRef isPostsLayout: ComputedRef diff --git a/theme/src/client/composables/posts-data.ts b/theme/src/client/composables/posts-data.ts index cde60a6c..c8ca50c1 100644 --- a/theme/src/client/composables/posts-data.ts +++ b/theme/src/client/composables/posts-data.ts @@ -9,12 +9,27 @@ import { useCollection } from './collections.js' export type PostsDataRef = Ref> +/** + * Posts data ref + * + * 文章数据引用,包含所有语言环境的文章列表 + */ export const postsData: PostsDataRef = ref(postsDataRaw) +/** + * Use posts data + * + * 获取文章数据 + */ export function usePostsData(): PostsDataRef { return postsData as PostsDataRef } +/** + * Use locale post list + * + * 获取当前语言环境的文章列表 + */ export function useLocalePostList(): ComputedRef { const collection = useCollection() const routeLocale = useRouteLocale() diff --git a/theme/src/client/composables/posts-post-list.ts b/theme/src/client/composables/posts-post-list.ts index 3ebb25bd..27ec75f6 100644 --- a/theme/src/client/composables/posts-post-list.ts +++ b/theme/src/client/composables/posts-post-list.ts @@ -8,6 +8,11 @@ import { useRouteQuery } from './route-query.js' const DEFAULT_PER_PAGE = 15 +/** + * Use post list control result + * + * 文章列表控制结果类型,包含分页相关的数据和方法 + */ interface UsePostListControlResult { postList: ComputedRef page: Ref @@ -22,6 +27,11 @@ interface UsePostListControlResult { changePage: (page: number) => void } +/** + * Use post list control + * + * 文章列表控制,管理分页逻辑和文章列表数据 + */ export function usePostListControl(homePage: Ref): UsePostListControlResult { const { collection } = useData<'page', 'post'>() diff --git a/theme/src/client/composables/posts-tags.ts b/theme/src/client/composables/posts-tags.ts index 57a8f3b1..253097e8 100644 --- a/theme/src/client/composables/posts-tags.ts +++ b/theme/src/client/composables/posts-tags.ts @@ -9,12 +9,22 @@ import { useTagColors } from './tag-colors.js' type ShortPostItem = Pick +/** + * Posts tag item + * + * 标签项,包含名称、数量和样式类名 + */ interface PostsTagItem { name: string count: string | number className: string } +/** + * Use tags result + * + * 标签结果类型 + */ interface UseTagsResult { tags: ComputedRef currentTag: Ref @@ -22,6 +32,11 @@ interface UseTagsResult { handleTagClick: (tag: string) => void } +/** + * Use tags + * + * 获取标签列表和当前选中的标签,处理标签相关逻辑 + */ export function useTags(): UseTagsResult { const { collection } = useData<'page', 'post'>() const list = useLocalePostList() diff --git a/theme/src/client/composables/prev-next.ts b/theme/src/client/composables/prev-next.ts index f4aa8027..867dc721 100644 --- a/theme/src/client/composables/prev-next.ts +++ b/theme/src/client/composables/prev-next.ts @@ -9,6 +9,11 @@ import { usePostsPageData } from './page.js' import { useLocalePostList } from './posts-data.js' import { useSidebar } from './sidebar.js' +/** + * Use prev next result + * + * 上一页/下一页结果类型 + */ interface UsePrevNextResult { prev: ComputedRef next: ComputedRef @@ -16,6 +21,11 @@ interface UsePrevNextResult { const SEPARATOR_RE = /^-{3,}$/ +/** + * Use prev next + * + * 获取上一页和下一页链接 + */ export function usePrevNext(): UsePrevNextResult { const route = useRoute() const { frontmatter, theme } = useData() diff --git a/theme/src/client/composables/sidebar-data.ts b/theme/src/client/composables/sidebar-data.ts index cc5c07da..6b2b556b 100644 --- a/theme/src/client/composables/sidebar-data.ts +++ b/theme/src/client/composables/sidebar-data.ts @@ -66,6 +66,11 @@ export function setupSidebar(): void { }, { immediate: true }) } +/** + * Use sidebar data + * + * 获取侧边栏数据 + */ export function useSidebarData(): Ref { return sidebar } diff --git a/theme/src/client/composables/sidebar.ts b/theme/src/client/composables/sidebar.ts index d70e5795..c103a171 100644 --- a/theme/src/client/composables/sidebar.ts +++ b/theme/src/client/composables/sidebar.ts @@ -10,6 +10,8 @@ import { getSidebarGroups, sidebarData, useSidebarData } from './sidebar-data.js /** * Check if the given sidebar item contains any active link. + * + * 检查给定的侧边栏项是否包含活动链接 */ export function hasActiveLink(path: string, items: ResolvedSidebarItem | ResolvedSidebarItem[]): boolean { if (Array.isArray(items)) { @@ -31,6 +33,11 @@ const containsActiveLink = hasActiveLink const isSidebarEnabled = ref(false) const isSidebarCollapsed = ref(false) +/** + * Use sidebar control + * + * 侧边栏控制,提供启用/禁用侧边栏和折叠/展开的控制函数 + */ export function useSidebarControl() { const enableSidebar = (): void => { isSidebarEnabled.value = true @@ -63,6 +70,11 @@ export function useSidebarControl() { } } +/** + * Use sidebar + * + * 侧边栏数据,获取当前路由的侧边栏项目和分组 + */ export function useSidebar(): { sidebar: Ref sidebarKey: ComputedRef @@ -93,8 +105,9 @@ export function useSidebar(): { } /** - * a11y: cache the element that opened the Sidebar (the menu button) then - * focus that button again when Menu is closed with Escape key. + * Use close sidebar on escape + * + * a11y: 缓存打开侧边栏的元素(菜单按钮),当使用 Escape 关闭菜单时重新聚焦该按钮 */ export function useCloseSidebarOnEscape(): void { const { disableSidebar } = useSidebarControl() @@ -122,6 +135,11 @@ export function useCloseSidebarOnEscape(): void { } } +/** + * Use sidebar item control + * + * 侧边栏项目控制,管理单个侧边栏项目的折叠状态、激活状态等 + */ export function useSidebarItemControl(item: ComputedRef): SidebarItemControl { const { page } = useData() const route = useRoute() diff --git a/theme/src/client/composables/theme-data.ts b/theme/src/client/composables/theme-data.ts index 60847843..48a4213f 100644 --- a/theme/src/client/composables/theme-data.ts +++ b/theme/src/client/composables/theme-data.ts @@ -7,16 +7,47 @@ import { clientDataSymbol } from 'vuepress/client' declare const __VUE_HMR_RUNTIME__: Record +/** + * Theme data reference type + * + * 主题数据引用类型 + */ export type ThemeDataRef = Ref +/** + * Theme locale data reference type + * + * 主题本地化数据引用类型 + */ export type ThemeLocaleDataRef = ComputedRef +/** + * Injection key for theme locale data + * + * 主题本地化数据的注入键 + */ export const themeLocaleDataSymbol: InjectionKey = Symbol( __VUEPRESS_DEV__ ? 'themeLocaleData' : '', ) +/** + * Theme data ref + * Global reference to the theme configuration data + * + * 主题数据引用 + * 主题配置数据的全局引用 + */ export const themeData: ThemeDataRef = ref(themeDataRaw) +/** + * Use theme data + * Returns the global theme data reference + * + * 获取主题数据 + * 返回全局主题数据引用 + * + * @returns Theme data reference / 主题数据引用 + */ export function useThemeData< T extends ThemeData = ThemeData, >(): ThemeDataRef { @@ -29,6 +60,15 @@ if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { } } +/** + * Use theme locale data + * Returns the theme data for the current route's locale + * + * 获取当前路由对应的语言环境的主题数据 + * + * @returns Theme locale data computed reference / 主题本地化数据计算引用 + * @throws Error if called without provider / 如果没有提供者则抛出错误 + */ export function useThemeLocaleData< T extends ThemeData = ThemeData, >(): ThemeLocaleDataRef { @@ -40,8 +80,15 @@ export function useThemeLocaleData< } /** - * Merge the locales fields to the root fields - * according to the route path + * Merge the locales fields to the root fields according to the route path + * Combines base theme options with locale-specific options + * + * 根据路由路径将本地化字段合并到根字段 + * 将基础主题选项与特定语言环境的选项合并 + * + * @param theme - Base theme data / 基础主题数据 + * @param routeLocale - Current route locale / 当前路由的语言环境 + * @returns Merged theme data for the locale / 合并后的语言环境主题数据 */ function resolveThemeLocaleData(theme: ThemeData, routeLocale: RouteLocale): ThemeData { const { locales, ...baseOptions } = theme @@ -52,6 +99,15 @@ function resolveThemeLocaleData(theme: ThemeData, routeLocale: RouteLocale): The } } +/** + * Setup theme data for the Vue app + * Provides theme data and theme locale data to the application + * + * 为 Vue 应用设置主题数据 + * 向应用程序提供主题数据和主题本地化数据 + * + * @param app - Vue application instance / Vue 应用实例 + */ export function setupThemeData(app: App): void { // provide theme data & theme locale data const themeData = useThemeData() diff --git a/theme/src/client/utils/animate.ts b/theme/src/client/utils/animate.ts index 696706ea..677fb373 100644 --- a/theme/src/client/utils/animate.ts +++ b/theme/src/client/utils/animate.ts @@ -1,14 +1,33 @@ /** - * @method 缓动算法 - * t: current time(当前时间); - * b: beginning value(初始值); - * c: change in value(变化量); - * d: duration(持续时间)。 + * Tweening algorithm for smooth animation + * Implements cubic easing function for natural motion + * + * 缓动算法,用于平滑动画 + * 实现立方缓动函数以获得自然的运动效果 + * + * @param t - Current time (progress from 0 to d) / 当前时间(从 0 到 d 的进度) + * @param b - Beginning value (initial value) / 初始值 + * @param c - Change in value (target - initial) / 变化量(目标值 - 初始值) + * @param d - Duration (total time) / 持续时间(总时间) + * @returns The current value at time t / 时间 t 时的当前值 */ export function tween(t: number, b: number, c: number, d: number): number { return c * (t /= d) * t * t + b } +/** + * Linear interpolation for animation + * Provides constant speed motion without easing + * + * 线性插值动画 + * 提供匀速运动,无缓动效果 + * + * @param t - Current time (progress from 0 to d) / 当前时间(从 0 到 d 的进度) + * @param b - Beginning value (initial value) / 初始值 + * @param c - Change in value (target - initial) / 变化量(目标值 - 初始值) + * @param d - Duration (total time) / 持续时间(总时间) + * @returns The current value at time t / 时间 t 时的当前值 + */ export function linear(t: number, b: number, c: number, d: number): number { return (c * t) / d + b } diff --git a/theme/src/client/utils/dom.ts b/theme/src/client/utils/dom.ts index 3b77a9ed..cc86a8aa 100644 --- a/theme/src/client/utils/dom.ts +++ b/theme/src/client/utils/dom.ts @@ -1,5 +1,14 @@ import { tween } from './animate.js' +/** + * Get the computed CSS value of an element as a number + * + * 获取元素的计算 CSS 值并转换为数字 + * + * @param el - Target element / 目标元素 + * @param property - CSS property name / CSS 属性名 + * @returns The numeric value of the CSS property, 0 if not found or invalid / CSS 属性的数值,如果未找到或无效则返回 0 + */ export function getCssValue(el: HTMLElement | null, property: string): number { const val = el?.ownerDocument?.defaultView?.getComputedStyle(el, null)?.[ property as any @@ -8,6 +17,14 @@ export function getCssValue(el: HTMLElement | null, property: string): number { return Number.isNaN(num) ? 0 : num } +/** + * Get the scrollTop value of a target element or document + * + * 获取目标元素或文档的 scrollTop 值 + * + * @param target - Target element or document, defaults to document / 目标元素或文档,默认为 document + * @returns Current scrollTop value / 当前 scrollTop 值 + */ export function getScrollTop( target: Document | HTMLElement = document, ): number { @@ -24,6 +41,14 @@ export function getScrollTop( } } +/** + * Set the scrollTop value of a target element or document + * + * 设置目标元素或文档的 scrollTop 值 + * + * @param target - Target element or document, defaults to document / 目标元素或文档,默认为 document + * @param scrollTop - ScrollTop value to set / 要设置的 scrollTop 值 + */ export function setScrollTop( target: Document | HTMLElement = document, scrollTop = 0, @@ -45,6 +70,15 @@ export function setScrollTop( } } +/** + * Smoothly scroll to a specific position + * + * 平滑滚动到指定位置 + * + * @param target - Target element or document / 目标元素或文档 + * @param top - Target scrollTop position / 目标 scrollTop 位置 + * @param time - Animation duration in milliseconds, defaults to 300ms / 动画持续时间(毫秒),默认为 300ms + */ export function scrollTo( target: Document | HTMLElement, top: number, @@ -68,6 +102,14 @@ export function scrollTo( } } +/** + * Get the offset top of an element relative to the document + * + * 获取元素相对于文档的 offsetTop 值 + * + * @param target - Target element / 目标元素 + * @returns The total offsetTop value / 总的 offsetTop 值 + */ export function getOffsetTop(target: T | null): number { if (!target) return 0 diff --git a/theme/src/client/utils/resolveEditLink.ts b/theme/src/client/utils/resolveEditLink.ts index 1301eba5..c92f4c6c 100644 --- a/theme/src/client/utils/resolveEditLink.ts +++ b/theme/src/client/utils/resolveEditLink.ts @@ -6,6 +6,13 @@ import { } from 'vuepress/shared' import { resolveRepoType } from './resolveRepoType.js' +/** + * Edit link patterns for different repository platforms + * Maps repository types to their respective edit URL patterns + * + * 不同仓库平台的编辑链接模式 + * 将仓库类型映射到各自的编辑 URL 模式 + */ export const editLinkPatterns: Record, string> = { GitHub: ':repo/edit/:branch/:path', GitLab: ':repo/-/edit/:branch/:path', @@ -14,6 +21,16 @@ export const editLinkPatterns: Record, string> = { ':repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default', } +/** + * Resolve the edit link pattern based on repository configuration + * + * 根据仓库配置解析编辑链接模式 + * + * @param params - Parameters object / 参数对象 + * @param params.docsRepo - Repository URL / 仓库 URL + * @param params.editLinkPattern - Custom edit link pattern / 自定义编辑链接模式 + * @returns The resolved edit link pattern or null / 解析后的编辑链接模式,如果没有则返回 null + */ function resolveEditLinkPatterns({ docsRepo, editLinkPattern, @@ -31,6 +48,21 @@ function resolveEditLinkPatterns({ return null } +/** + * Resolve the complete edit link URL for a file + * Generates an edit link based on repository configuration and file path + * + * 解析文件的完整编辑链接 URL + * 根据仓库配置和文件路径生成编辑链接 + * + * @param params - Parameters object / 参数对象 + * @param params.docsRepo - Repository URL / 仓库 URL + * @param params.docsBranch - Branch name / 分支名称 + * @param params.docsDir - Documentation directory / 文档目录 + * @param params.filePathRelative - Relative file path / 相对文件路径 + * @param params.editLinkPattern - Custom edit link pattern / 自定义编辑链接模式 + * @returns The complete edit link URL or null / 完整的编辑链接 URL,如果无法生成则返回 null + */ export function resolveEditLink({ docsRepo, docsBranch, diff --git a/theme/src/client/utils/resolveNavLink.ts b/theme/src/client/utils/resolveNavLink.ts index e57278fd..434e16a0 100644 --- a/theme/src/client/utils/resolveNavLink.ts +++ b/theme/src/client/utils/resolveNavLink.ts @@ -9,7 +9,13 @@ import { resolveRoute } from 'vuepress/client' /** * Resolve NavLink props from string + * Converts a link string to a resolved navigation item with metadata * + * 从字符串解析 NavLink 属性 + * 将链接字符串转换为带有元数据的解析导航项 + * + * @param link - The link string to resolve / 要解析的链接字符串 + * @returns Resolved navigation item with link, text, icon and badge / 解析后的导航项,包含链接、文本、图标和徽章 * @example * - Input: '/README.md' * - Output: { text: 'Home', link: '/' } @@ -31,17 +37,47 @@ export function resolveNavLink(link: string): ResolvedNavItemWithLink { } } +/** + * Normalize a path to extract a readable title + * Removes index.html, .html extension and trailing slash + * + * 规范化路径以提取可读的标题 + * 移除 index.html、.html 扩展名和尾部斜杠 + * + * @param path - The path to normalize / 要规范化的路径 + * @returns The extracted title from path / 从路径提取的标题 + */ function normalizeTitleWithPath(path: string): string { path = path.replace(/index\.html?$/i, '').replace(/\.html?$/i, '').replace(/\/$/, '') return decodeURIComponent(path.slice(path.lastIndexOf('/') + 1)) } +/** + * Normalize a link by combining base and link + * Handles absolute links and protocol links correctly + * + * 通过组合 base 和 link 来规范化链接 + * 正确处理绝对链接和协议链接 + * + * @param base - Base URL / 基础 URL + * @param link - Link to normalize / 要规范化的链接 + * @returns Normalized link / 规范化后的链接 + */ export function normalizeLink(base = '', link = ''): string { return isLinkAbsolute(link) || isLinkWithProtocol(link) ? link : ensureLeadingSlash(`${base}/${link}`.replace(/\/+/g, '/')) } +/** + * Normalize a prefix by ensuring it ends with a slash + * + * 规范化前缀,确保它以斜杠结尾 + * + * @param base - Base URL / 基础 URL + * @param link - Link to normalize / 要规范化的链接 + * @returns Normalized prefix with trailing slash / 带有尾部斜杠的规范化前缀 + */ export function normalizePrefix(base: string, link = ''): string { return ensureEndingSlash(normalizeLink(base, link)) } diff --git a/theme/src/client/utils/resolveRepoType.ts b/theme/src/client/utils/resolveRepoType.ts index 6bc3c962..839b43e5 100644 --- a/theme/src/client/utils/resolveRepoType.ts +++ b/theme/src/client/utils/resolveRepoType.ts @@ -1,7 +1,24 @@ import { isLinkHttp } from 'vuepress/shared' +/** + * Supported repository types + * Represents the type of code hosting platform + * + * 支持的仓库类型 + * 表示代码托管平台的类型 + */ export type RepoType = 'GitHub' | 'GitLab' | 'Gitee' | 'Bitbucket' | null +/** + * Resolve the repository type from a repository URL + * Detects the platform based on URL patterns + * + * 从仓库 URL 解析仓库类型 + * 基于 URL 模式检测平台 + * + * @param repo - Repository URL or path / 仓库 URL 或路径 + * @returns The detected repository type or null if unknown / 检测到的仓库类型,如果未知则返回 null + */ export function resolveRepoType(repo: string): RepoType { if (!isLinkHttp(repo) || /github\.com/.test(repo)) return 'GitHub' diff --git a/theme/src/client/utils/shared.ts b/theme/src/client/utils/shared.ts index 5031c322..e651260b 100644 --- a/theme/src/client/utils/shared.ts +++ b/theme/src/client/utils/shared.ts @@ -1,14 +1,62 @@ +/** + * Regular expression to match external URLs + * + * 匹配外部 URL 的正则表达式 + */ export const EXTERNAL_URL_RE: RegExp = /^[a-z]+:/i + +/** + * Regular expression to match pathname protocol + * + * 匹配 pathname 协议的正则表达式 + */ export const PATHNAME_PROTOCOL_RE: RegExp = /^pathname:\/\// -export const HASH_RE: RegExp = /#.*$/ + +/** + * Regular expression to match hash + * + * 匹配哈希值的正则表达式 + */ +export const HASH_RE: RegExp = /#.*/ + +/** + * Regular expression to match file extension + * + * 匹配文件扩展名的正则表达式 + */ export const EXT_RE: RegExp = /(index|README)?\.(md|html)$/ +/** + * Whether running in browser + * + * 是否在浏览器中运行 + */ export const inBrowser: boolean = typeof document !== 'undefined' +/** + * Convert value to array + * + * 将值转换为数组 + * + * @param value - Value to convert, can be single value or array / 要转换的值,可以是单个值或数组 + * @returns Array containing the value(s) / 包含值的数组 + */ export function toArray(value: T | T[]): T[] { return Array.isArray(value) ? value : [value] } +/** + * Check if the current path matches the given match path + * Supports both exact matching and regex matching + * + * 检查当前路径是否匹配给定的匹配路径 + * 支持精确匹配和正则匹配 + * + * @param currentPath - Current path to check / 要检查的当前路径 + * @param matchPath - Path pattern to match against / 要匹配的路径模式 + * @param asRegex - Whether to treat matchPath as regex / 是否将 matchPath 视为正则表达式 + * @returns True if paths match / 如果路径匹配则返回 true + */ export function isActive( currentPath: string, matchPath?: string, @@ -33,10 +81,28 @@ export function isActive( return true } +/** + * Normalize a path by removing hash and file extension + * + * 通过移除哈希值和文件扩展名来规范化路径 + * + * @param path - Path to normalize / 要规范化的路径 + * @returns Normalized path / 规范化后的路径 + */ export function normalize(path: string): string { return decodeURI(path).replace(HASH_RE, '').replace(EXT_RE, '') } +/** + * Convert a numeric value to CSS unit string + * Adds 'px' suffix if the value is a plain number + * + * 将数值转换为 CSS 单位字符串 + * 如果值是纯数字则添加 'px' 后缀 + * + * @param value - Value to convert, can be number or string with unit / 要转换的值,可以是数字或带单位的字符串 + * @returns CSS unit string / CSS 单位字符串 + */ export function numToUnit(value?: string | number): string { if (typeof value === 'undefined') return '' @@ -48,6 +114,14 @@ export function numToUnit(value?: string | number): string { const gradient: string[] = ['linear-gradient', 'radial-gradient', 'repeating-linear-gradient', 'repeating-radial-gradient', 'conic-gradient'] +/** + * Check if a value is a CSS gradient + * + * 检查值是否为 CSS 渐变 + * + * @param value - Value to check / 要检查的值 + * @returns True if value is a gradient / 如果值是渐变则返回 true + */ export function isGradient(value: string): boolean { return gradient.some(v => value.startsWith(v)) } diff --git a/theme/src/node/defineConfig.ts b/theme/src/node/defineConfig.ts index 5c502220..6180d595 100644 --- a/theme/src/node/defineConfig.ts +++ b/theme/src/node/defineConfig.ts @@ -1,6 +1,8 @@ import type { ThemeCollectionItem, ThemeCollections, ThemeConfig, ThemeNavItem } from '../shared/index.js' /** + * Theme configuration helper function, used in separate `plume.config.ts` + * * 主题配置,在单独的 `plume.config.ts` 中使用的类型帮助函数 */ export function defineThemeConfig(config: ThemeConfig): ThemeConfig { @@ -8,6 +10,8 @@ export function defineThemeConfig(config: ThemeConfig): ThemeConfig { } /** + * Theme navbar configuration helper function + * * 主题导航栏配置帮助函数 */ export function defineNavbarConfig(navbar: ThemeNavItem[]): ThemeNavItem[] { @@ -15,6 +19,8 @@ export function defineNavbarConfig(navbar: ThemeNavItem[]): ThemeNavItem[] { } /** + * Theme notes configuration helper function + * * 主题 notes 配置帮助函数 * @deprecated 使用 `defineCollections` 代替 */ @@ -23,6 +29,8 @@ export function defineNotesConfig(notes: unknown): unknown { } /** + * Theme note item configuration helper function + * * 主题 notes item 配置帮助函数 * @deprecated 使用 `defineCollection` 代替 */ @@ -31,6 +39,8 @@ export function defineNoteConfig(note: unknown): unknown { } /** + * Theme collections configuration helper function + * * 主题 collections 配置帮助函数 */ export function defineCollections(collections: ThemeCollections): ThemeCollections { @@ -38,7 +48,9 @@ export function defineCollections(collections: ThemeCollections): ThemeCollectio } /** - * 主题 collections item 配置帮助函数 + * Theme collection item configuration helper function + * + * 主题 collection item 配置帮助函数 */ export function defineCollection(collection: ThemeCollectionItem): ThemeCollectionItem { return collection diff --git a/theme/src/node/detector/options.ts b/theme/src/node/detector/options.ts index 031bd739..48b18bd3 100644 --- a/theme/src/node/detector/options.ts +++ b/theme/src/node/detector/options.ts @@ -5,6 +5,8 @@ import { detectMarkdown } from './markdown.js' import { detectPlugins } from './plugins.js' /** + * Detect theme options + * * 检测主题选项 */ export function detectThemeOptions({ diff --git a/theme/src/node/detector/versions.ts b/theme/src/node/detector/versions.ts index 6c0fd8ab..0d08acb0 100644 --- a/theme/src/node/detector/versions.ts +++ b/theme/src/node/detector/versions.ts @@ -16,6 +16,11 @@ const t = createTranslate({ }, }) +/** + * Detect version compatibility + * + * 检测版本兼容性 + */ export function detectVersions(app: App): void { detectVuepressVersion() detectThemeVersion(app) diff --git a/theme/src/node/index.ts b/theme/src/node/index.ts index a37691df..c1fa00c3 100644 --- a/theme/src/node/index.ts +++ b/theme/src/node/index.ts @@ -7,5 +7,7 @@ export { plumeTheme } /** * @deprecated 请使用 具名导出 替代 默认导出 + * + * @deprecated Please use named exports instead of default export */ export default plumeTheme diff --git a/theme/src/node/locales/index.ts b/theme/src/node/locales/index.ts index 144dab47..3d9b1fb1 100644 --- a/theme/src/node/locales/index.ts +++ b/theme/src/node/locales/index.ts @@ -1,4 +1,8 @@ /** + * Multilingual presets + * Except for /zh/ and /en/, other language presets are generated by AI and are not guaranteed to be accurate + * If there are any errors, welcome to submit an issue + * * 多语言预设 * 除 /zh/ 、 /en/ 外,其它语言预设通过 AI 生成,不保证准确 * 如有错误,欢迎提 issue diff --git a/theme/src/node/pages/autoCategory.ts b/theme/src/node/pages/autoCategory.ts index 31f8cc7d..96fdc746 100644 --- a/theme/src/node/pages/autoCategory.ts +++ b/theme/src/node/pages/autoCategory.ts @@ -10,6 +10,11 @@ const cache: Record = {} const RE_CATEGORY = /^(?:(\d+)\.)?([\s\S]+)$/ +/** + * Auto category for page + * + * 自动为页面生成分类信息,根据文件路径和集合配置自动确定页面所属的分类 + */ export function autoCategory(page: Page): void { const collection = findCollection(page) diff --git a/theme/src/node/pages/createPages.ts b/theme/src/node/pages/createPages.ts index 289a79d7..78523a1e 100644 --- a/theme/src/node/pages/createPages.ts +++ b/theme/src/node/pages/createPages.ts @@ -13,6 +13,11 @@ function getRootLang(app: App): string { return app.siteData.lang } +/** + * Create additional pages + * + * 创建额外页面,根据集合配置生成文章列表页、标签页、分类页、归档页等 + */ export async function createPages(app: App): Promise { const options = getThemeConfig() diff --git a/theme/src/node/pages/encryptPage.ts b/theme/src/node/pages/encryptPage.ts index a4cf6927..77fc3877 100644 --- a/theme/src/node/pages/encryptPage.ts +++ b/theme/src/node/pages/encryptPage.ts @@ -4,6 +4,11 @@ import { toArray } from '@pengzhanbo/utils' import pMap from 'p-map' import { genEncrypt } from '../utils/index.js' +/** + * Encrypt page + * + * 加密页面,将页面的密码转换为加密后的哈希值并存储在页面数据中 + */ export async function encryptPage( page: Page, ): Promise { diff --git a/theme/src/node/pages/extendsPage.ts b/theme/src/node/pages/extendsPage.ts index ab2bcbd2..249696c5 100644 --- a/theme/src/node/pages/extendsPage.ts +++ b/theme/src/node/pages/extendsPage.ts @@ -5,6 +5,11 @@ import { autoCategory } from './autoCategory.js' import { encryptPage } from './encryptPage.js' import { enableBulletin } from './pageBulletin.js' +/** + * Extend page data + * + * 扩展页面数据,清理页面数据、自动分类、启用公告栏、加密页面 + */ export async function extendsPageData( page: Page, ): Promise { diff --git a/theme/src/node/pages/pageBulletin.ts b/theme/src/node/pages/pageBulletin.ts index 0935ab08..314633b8 100644 --- a/theme/src/node/pages/pageBulletin.ts +++ b/theme/src/node/pages/pageBulletin.ts @@ -3,6 +3,11 @@ import type { ThemePageData } from '../../shared/index.js' import { isFunction, isPlainObject } from '@vuepress/helper' import { getThemeConfig } from '../loadConfig/index.js' +/** + * Enable bulletin for page + * + * 为页面启用公告栏,根据全局或语言环境配置决定是否显示公告栏 + */ export function enableBulletin( page: Page, ): void { diff --git a/theme/src/node/plugins/code.ts b/theme/src/node/plugins/code.ts index 7e16e0b4..bb7f4cf8 100644 --- a/theme/src/node/plugins/code.ts +++ b/theme/src/node/plugins/code.ts @@ -7,6 +7,11 @@ import { shikiPlugin } from '@vuepress/plugin-shiki' import { createCodeTabIconGetter } from 'vuepress-plugin-md-power' import { getThemeConfig } from '../loadConfig/index.js' +/** + * Setup code-related plugins + * + * 设置代码相关插件,包括代码复制和代码高亮 + */ export function codePlugins(pluginOptions: ThemeBuiltinPlugins): PluginConfig { const options = getThemeConfig() const plugins: PluginConfig = [] diff --git a/theme/src/node/plugins/git.ts b/theme/src/node/plugins/git.ts index 87f506d3..de5aa557 100644 --- a/theme/src/node/plugins/git.ts +++ b/theme/src/node/plugins/git.ts @@ -4,6 +4,11 @@ import { isPlainObject } from '@vuepress/helper' import { gitPlugin as rawGitPlugin } from '@vuepress/plugin-git' import { getThemeConfig } from '../loadConfig/index.js' +/** + * Setup git plugin + * + * 设置 Git 插件,用于显示文章的最后更新时间、贡献者和变更历史 + */ export function gitPlugin(app: App, pluginOptions: ThemeBuiltinPlugins): PluginConfig { const options = getThemeConfig() diff --git a/theme/src/node/plugins/llms.ts b/theme/src/node/plugins/llms.ts index 670ad1d1..6f725fef 100644 --- a/theme/src/node/plugins/llms.ts +++ b/theme/src/node/plugins/llms.ts @@ -11,6 +11,11 @@ const CODE_BLOCK_RE = /(?:^|\n)(?\s*`{3,})([\s\w])[\s\S]*?\n\k(? const ENCRYPT_CONTAINER_RE = /(?:^|\n)(?\s*:{3,})\s*encrypt\b[\s\S]*?\n\k(?:\n|$)/g const RESTORE_RE = //g +/** + * Setup LLMs plugin + * + * 设置 LLM 插件,用于生成 LLM 友好的网站内容,支持自定义 Markdown 转换和模板 + */ export function llmsPlugin(app: App, userOptions: true | LlmsPluginOptions): PluginConfig { if (!app.env.isBuild) return [] diff --git a/theme/src/node/plugins/markdown.ts b/theme/src/node/plugins/markdown.ts index 91621d96..190cdcf4 100644 --- a/theme/src/node/plugins/markdown.ts +++ b/theme/src/node/plugins/markdown.ts @@ -16,6 +16,11 @@ import { markdownPowerPlugin } from 'vuepress-plugin-md-power' import { MARKDOWN_CHART_FIELDS, MARKDOWN_POWER_FIELDS } from '../detector/index.js' import { getThemeConfig } from '../loadConfig/index.js' +/** + * Setup markdown plugins + * + * 设置 Markdown 增强插件,包括提示、图像、数学公式、图表等功能 + */ export function markdownPlugins(pluginOptions: ThemeBuiltinPlugins): PluginConfig { const options = getThemeConfig() const plugins: PluginConfig = [] diff --git a/theme/src/node/plugins/setupPlugins.ts b/theme/src/node/plugins/setupPlugins.ts index b39ddb23..6801fece 100644 --- a/theme/src/node/plugins/setupPlugins.ts +++ b/theme/src/node/plugins/setupPlugins.ts @@ -21,6 +21,11 @@ import { gitPlugin } from './git.js' import { llmsPlugin } from './llms.js' import { markdownPlugins } from './markdown.js' +/** + * Setup theme plugins + * + * 设置主题插件,根据配置初始化搜索、评论、SEO、水印等插件 + */ export function setupPlugins( app: App, pluginOptions: ThemeBuiltinPlugins, diff --git a/theme/src/node/prepare/index.ts b/theme/src/node/prepare/index.ts index 4d1afe03..e55a2108 100644 --- a/theme/src/node/prepare/index.ts +++ b/theme/src/node/prepare/index.ts @@ -9,6 +9,11 @@ import { prepareIcons } from './prepareIcons.js' import { preparedPostsData } from './preparePostsData.js' import { prepareSidebar } from './prepareSidebar.js' +/** + * Prepare all theme data + * + * 准备所有主题数据,包括文章标签颜色、文章列表、侧边栏、集合、加密、图标、Hero 动画效果等 + */ export async function prepareData(app: App): Promise { perf.mark('prepare:data') @@ -25,6 +30,11 @@ export async function prepareData(app: App): Promise { perf.log('prepare:data') } +/** + * Watch for changes in prepared data and re-prepare when needed + * + * 监听准备数据的变化,并在需要时重新准备数据 + */ export function watchPrepare( app: App, watchers: any[], diff --git a/theme/src/node/prepare/prepareArticleTagColor.ts b/theme/src/node/prepare/prepareArticleTagColor.ts index 6b258bca..f6d21bac 100644 --- a/theme/src/node/prepare/prepareArticleTagColor.ts +++ b/theme/src/node/prepare/prepareArticleTagColor.ts @@ -32,6 +32,11 @@ export const PRESET: TagsColorsItem[] = [ // { index: className } let cache: Record = {} +/** + * Prepare article tag colors + * + * 准备文章标签颜色,收集页面中使用的标签并生成对应的 CSS 和 JS 文件 + */ export async function prepareArticleTagColors(app: App): Promise { perf.mark('prepare:tag-colors') const { js, css } = genCode(app) @@ -46,6 +51,11 @@ export async function prepareArticleTagColors(app: App): Promise { perf.log('prepare:tag-colors') } +/** + * Generate tag color CSS and JS code + * + * 生成标签颜色的 CSS 和 JS 代码,遍历所有页面收集标签并生成对应的样式和脚本 + */ export function genCode(app: App): { js: string, css: string } { const articleTagColors: Record = {} const tagList = new Set() diff --git a/theme/src/node/prepare/prepareCollections.ts b/theme/src/node/prepare/prepareCollections.ts index 4c2367b4..b8621f5c 100644 --- a/theme/src/node/prepare/prepareCollections.ts +++ b/theme/src/node/prepare/prepareCollections.ts @@ -4,6 +4,11 @@ import { omit } from '@pengzhanbo/utils' import { getThemeConfig } from '../loadConfig/index.js' import { perf, resolveContent, writeTemp } from '../utils/index.js' +/** + * Prepare collections data + * + * 准备集合数据,为每个语言环境处理集合配置并生成临时文件 + */ export async function prepareCollections(app: App): Promise { perf.mark('prepare:collections') diff --git a/theme/src/node/prepare/prepareEncrypt.ts b/theme/src/node/prepare/prepareEncrypt.ts index c59d4496..d43216f1 100644 --- a/theme/src/node/prepare/prepareEncrypt.ts +++ b/theme/src/node/prepare/prepareEncrypt.ts @@ -21,6 +21,11 @@ const separator = ':' let contentHash = '' let fsCache: FsCache<[string, EncryptConfig]> | null = null +/** + * Prepare encryption configuration + * + * 准备加密配置,处理主题的加密选项并生成加密相关的临时文件 + */ export async function prepareEncrypt(app: App): Promise { perf.mark('prepare:encrypt') const { encrypt } = getThemeConfig() @@ -80,6 +85,11 @@ async function resolveEncrypt(encrypt?: EncryptOptions): Promise ] } +/** + * Check if a page is encrypted + * + * 检查页面是否需要加密,根据页面的路径或文件相对路径匹配加密规则 + */ export function isEncryptPage(page: Page, encrypt?: EncryptOptions): boolean { if (!encrypt) return false diff --git a/theme/src/node/prepare/prepareHomeHeroEffects.ts b/theme/src/node/prepare/prepareHomeHeroEffects.ts index 38274809..d9462388 100644 --- a/theme/src/node/prepare/prepareHomeHeroEffects.ts +++ b/theme/src/node/prepare/prepareHomeHeroEffects.ts @@ -47,6 +47,11 @@ const t = createTranslate({ }, }) +/** + * Prepare home page hero effects + * + * 准备首页 Hero 动画效果,从 frontmatter 收集效果配置并写入临时文件,同时检测缺失的依赖 + */ export async function prepareHomeHeroEffects(app: App): Promise { perf.mark('prepare:home-hero-effects') diff --git a/theme/src/node/prepare/prepareIcons.ts b/theme/src/node/prepare/prepareIcons.ts index c6a0b632..972540a7 100644 --- a/theme/src/node/prepare/prepareIcons.ts +++ b/theme/src/node/prepare/prepareIcons.ts @@ -48,6 +48,11 @@ const socialFallbacks: Record = { weibo: 'sinaweibo', } +/** + * Prepare icon data for theme + * + * 准备主题图标数据,收集页面中使用的图标并生成对应的 CSS 和 JS 文件 + */ export async function prepareIcons(app: App): Promise { perf.mark('prepare:icons:total') const options = getThemeConfig() diff --git a/theme/src/node/prepare/preparePostsData.ts b/theme/src/node/prepare/preparePostsData.ts index 0e1f07cf..1738c5f6 100644 --- a/theme/src/node/prepare/preparePostsData.ts +++ b/theme/src/node/prepare/preparePostsData.ts @@ -79,6 +79,11 @@ function processPostData( return data } +/** + * Prepare posts data + * + * 准备文章数据,过滤非草稿文章并为每个集合和语言环境生成文章列表数据 + */ export async function preparedPostsData(app: App): Promise { const isBuild = app.env.isBuild const { encrypt, locales } = getThemeConfig() diff --git a/theme/src/node/prepare/prepareSidebar.ts b/theme/src/node/prepare/prepareSidebar.ts index b87fbfc3..4cd43698 100644 --- a/theme/src/node/prepare/prepareSidebar.ts +++ b/theme/src/node/prepare/prepareSidebar.ts @@ -18,6 +18,11 @@ import { findCollection } from '../collections/index.js' import { getThemeConfig } from '../loadConfig/index.js' import { normalizeLink, perf, resolveContent, writeTemp } from '../utils/index.js' +/** + * Prepare sidebar data + * + * 准备侧边栏数据,处理所有语言环境的侧边栏配置并生成临时文件 + */ export async function prepareSidebar(app: App): Promise { perf.mark('prepare:sidebar') const sidebar = getAllSidebar() diff --git a/theme/src/node/prepare/prepareThemeData.ts b/theme/src/node/prepare/prepareThemeData.ts index 1884a731..991d2aa7 100644 --- a/theme/src/node/prepare/prepareThemeData.ts +++ b/theme/src/node/prepare/prepareThemeData.ts @@ -15,6 +15,11 @@ const bulletinFiles: Record = {} process.on('exit', () => bulletinFileWatcher?.close()) +/** + * Prepare theme data + * + * 准备主题数据,解析主题配置、处理头像尺寸、解析公告栏并更新主题数据 + */ export async function prepareThemeData( app: App, plugins: ThemeBuiltinPlugins, diff --git a/theme/src/node/theme.ts b/theme/src/node/theme.ts index fde119e2..1bd5929d 100644 --- a/theme/src/node/theme.ts +++ b/theme/src/node/theme.ts @@ -21,7 +21,10 @@ import { perf, resolve, setTranslateLang, templates, THEME_NAME } from './utils/ /** * VuePress Theme Plume - * @param options 主题配置 + * + * VuePress 主题 Plume + * + * @param options Theme options / 主题配置 * @example * ```ts * import { defineUserConfig } from 'vuepress' diff --git a/theme/src/node/utils/constants.ts b/theme/src/node/utils/constants.ts index 253414ba..1235ab6c 100644 --- a/theme/src/node/utils/constants.ts +++ b/theme/src/node/utils/constants.ts @@ -1 +1,6 @@ +/** + * Theme name constant + * + * 主题名称常量 + */ export const THEME_NAME = 'vuepress-theme-plume' diff --git a/theme/src/node/utils/createFsCache.ts b/theme/src/node/utils/createFsCache.ts index 0204c047..84021c64 100644 --- a/theme/src/node/utils/createFsCache.ts +++ b/theme/src/node/utils/createFsCache.ts @@ -3,24 +3,68 @@ import fs from 'node:fs/promises' import path from 'node:path' import { hash } from 'vuepress/utils' +/** + * Cache data structure + * Stores data with its hash for change detection + * + * 缓存数据结构 + * 存储数据及其哈希值用于变更检测 + */ interface CacheData { + /** Hash of the cached data / 缓存数据的哈希值 */ hash: string + /** Cached data / 缓存的数据 */ data: T | null } +/** + * File system cache interface + * Provides methods for reading and writing cached data + * + * 文件系统缓存接口 + * 提供读写缓存数据的方法 + */ export interface FsCache { + /** Current hash of cached data / 缓存数据的当前哈希值 */ hash: string + /** Current cached data / 当前缓存的数据 */ data: T | null + /** + * Read data from cache + * 从缓存读取数据 + */ read: () => Promise + /** + * Write data to cache + * 将数据写入缓存 + */ write: (data: T, clear?: boolean) => Promise } const CACHE_BASE = 'markdown' +/** + * Create a file system cache instance + * Provides persistent caching using the file system + * + * 创建文件系统缓存实例 + * 使用文件系统提供持久化缓存 + * + * @param app - VuePress application instance / VuePress 应用实例 + * @param name - Cache file name / 缓存文件名 + * @returns File system cache instance / 文件系统缓存实例 + */ export function createFsCache(app: App, name: string): FsCache { const filepath = app.dir.cache(`${CACHE_BASE}/${name}.json`) const cache: CacheData = { hash: '', data: null } + /** + * Read cached data from file + * Loads and parses the cache file if it exists + * + * 从文件读取缓存数据 + * 如果存在则加载并解析缓存文件 + */ const read = async (): Promise => { if (!cache.data) { try { @@ -37,6 +81,17 @@ export function createFsCache(app: App, name: string): FsCache { } let timer: NodeJS.Timeout | null = null + + /** + * Write data to cache file + * Only writes if the data has changed (hash comparison) + * + * 将数据写入缓存文件 + * 仅在数据已更改时写入(哈希比较) + * + * @param data - Data to cache / 要缓存的数据 + * @param clear - Whether to clear cache after writing / 写入后是否清除缓存 + */ const write = async (data: T, clear?: boolean) => { const currentHash = hash(data) if (cache.hash && currentHash === cache.hash) diff --git a/theme/src/node/utils/encrypt.ts b/theme/src/node/utils/encrypt.ts index 3412cb25..d778f934 100644 --- a/theme/src/node/utils/encrypt.ts +++ b/theme/src/node/utils/encrypt.ts @@ -1,6 +1,16 @@ import crypto from 'node:crypto' import { bcrypt } from 'hash-wasm' +/** + * Generate encrypted password using bcrypt + * Creates a secure hash with random salt for password storage + * + * 使用 bcrypt 生成加密密码 + * 使用随机盐创建安全的哈希值用于密码存储 + * + * @param password - Plain text password to encrypt / 要加密的明文密码 + * @returns Bcrypt hashed password / Bcrypt 哈希后的密码 + */ export async function genEncrypt(password: string): Promise { const salt = new Uint8Array(16) crypto.getRandomValues(salt) diff --git a/theme/src/node/utils/hash.ts b/theme/src/node/utils/hash.ts index 10f714e8..fadf551e 100644 --- a/theme/src/node/utils/hash.ts +++ b/theme/src/node/utils/hash.ts @@ -1,6 +1,24 @@ import { createHash } from 'node:crypto' import { customAlphabet } from 'nanoid' +/** + * Generate MD5 hash of content + * + * 生成内容的 MD5 哈希值 + * + * @param content - Content to hash / 要哈希的内容 + * @returns MD5 hash string / MD5 哈希字符串 + */ export const hash = (content: string): string => createHash('md5').update(content).digest('hex') +/** + * Generate random ID (nanoid) + * Uses alphanumeric characters for URL-safe IDs + * + * 生成随机 ID (nanoid) + * 使用字母数字字符生成 URL 安全的 ID + * + * @param size - Length of the generated ID, defaults to 8 / 生成 ID 的长度,默认为 8 + * @returns Random alphanumeric string / 随机字母数字字符串 + */ export const nanoid: (size?: number) => string = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8) diff --git a/theme/src/node/utils/interopDefault.ts b/theme/src/node/utils/interopDefault.ts index 57b062cc..b0506846 100644 --- a/theme/src/node/utils/interopDefault.ts +++ b/theme/src/node/utils/interopDefault.ts @@ -1,5 +1,10 @@ import type { Awaitable } from '@pengzhanbo/utils' +/** + * Interop default export + * + * 兼容默认导出 + */ export async function interopDefault(m: Awaitable): Promise { const resolved = await m return (resolved as any).default || resolved diff --git a/theme/src/node/utils/logger.ts b/theme/src/node/utils/logger.ts index ce772736..6a8bc633 100644 --- a/theme/src/node/utils/logger.ts +++ b/theme/src/node/utils/logger.ts @@ -2,20 +2,61 @@ import { Logger } from '@vuepress/helper' import { colors } from 'vuepress/utils' import { THEME_NAME } from './constants.js' +/** + * Theme logger instance + * Used for logging messages with the theme name prefix + * + * 主题日志记录器实例 + * 用于记录带有主题名称前缀的消息 + */ export const logger: Logger = new Logger(THEME_NAME) +/** + * Performance monitor class + * Tracks and logs performance metrics for debugging + * + * 性能监控类 + * 跟踪和记录性能指标用于调试 + */ class Perf { + /** Whether debug mode is enabled / 是否启用调试模式 */ isDebug: boolean = false + /** Collection of performance marks / 性能标记集合 */ collect: Record = {} + /** + * Initialize performance monitor + * + * 初始化性能监控器 + * + * @param isDebug - Whether to enable debug mode / 是否启用调试模式 + */ init(isDebug = false): void { this.isDebug = isDebug } + /** + * Mark a performance checkpoint + * Records the current timestamp for the given mark + * + * 标记性能检查点 + * 记录给定标记的当前时间戳 + * + * @param mark - Name of the performance mark / 性能标记的名称 + */ mark(mark: string): void { this.collect[mark] = performance.now() } + /** + * Log the time spent since a mark + * Outputs the elapsed time if debug mode is enabled + * + * 记录自标记以来的耗时 + * 如果启用调试模式则输出经过的时间 + * + * @param mark - Name of the performance mark to log / 要记录的性能标记名称 + */ log(mark: string): void { const startTime = this.collect[mark] if (!this.isDebug || !startTime) @@ -24,4 +65,9 @@ class Perf { } } +/** + * Global performance monitor instance + * + * 全局性能监控实例 + */ export const perf: Perf = new Perf() diff --git a/theme/src/node/utils/package.ts b/theme/src/node/utils/package.ts index e0011c31..982d75c6 100644 --- a/theme/src/node/utils/package.ts +++ b/theme/src/node/utils/package.ts @@ -2,6 +2,11 @@ import process from 'node:process' import { fs, path } from 'vuepress/utils' import { resolve } from './path.js' +/** + * Read and parse JSON file asynchronously + * + * 异步读取和解析 JSON 文件 + */ export function readJsonFileAsync = Record>(filePath: string): T { try { const content = fs.readFileSync(filePath, 'utf-8') @@ -11,10 +16,20 @@ export function readJsonFileAsync = Record { return readJsonFileAsync(path.join(process.cwd(), 'package.json')) } +/** + * Get theme package.json + * + * 获取主题的 package.json + */ export function getThemePackage(): Record { return readJsonFileAsync(resolve('../package.json')) } diff --git a/theme/src/node/utils/path.ts b/theme/src/node/utils/path.ts index ce3d06f2..591ef6c0 100644 --- a/theme/src/node/utils/path.ts +++ b/theme/src/node/utils/path.ts @@ -3,18 +3,71 @@ import { getDirname, path } from 'vuepress/utils' const __dirname = getDirname(import.meta.url) +/** + * Resolve theme directory path + * Resolves paths relative to the theme's root directory + * + * 解析主题目录路径 + * 解析相对于主题根目录的路径 + * + * @param args - Path segments to resolve / 要解析的路径段 + * @returns Resolved absolute path / 解析后的绝对路径 + */ export const resolve = (...args: string[]): string => path.resolve(__dirname, '../', ...args) + +/** + * Resolve template path + * Resolves paths relative to the templates directory + * + * 解析模板路径 + * 解析相对于模板目录的路径 + * + * @param url - Template file path / 模板文件路径 + * @returns Resolved template path / 解析后的模板路径 + */ export const templates = (url: string): string => resolve('../templates', url) const RE_SLASH = /(\\|\/)+/g + +/** + * Normalize path separators + * Converts backslashes to forward slashes for cross-platform compatibility + * + * 规范化路径分隔符 + * 将反斜杠转换为正斜杠以实现跨平台兼容性 + * + * @param path - Path to normalize / 要规范化的路径 + * @returns Normalized path with forward slashes / 带有正斜杠的规范化路径 + */ export function normalizePath(path: string): string { return path.replace(RE_SLASH, '/') } +/** + * Join path segments + * Combines multiple path segments and normalizes the result + * + * 连接路径段 + * 组合多个路径段并规范化结果 + * + * @param args - Path segments to join / 要连接的路径段 + * @returns Joined and normalized path / 连接并规范化的路径 + */ export function pathJoin(...args: string[]): string { return normalizePath(path.join(...args)) } +/** + * Normalize link with base path + * Combines base path with link, handling absolute and protocol links + * + * 规范化带基础路径的链接 + * 将基础路径与链接组合,处理绝对链接和协议链接 + * + * @param base - Base path / 基础路径 + * @param link - Link to normalize / 要规范化的链接 + * @returns Normalized link / 规范化后的链接 + */ export function normalizeLink(base: string, link = ''): string { return isLinkAbsolute(link) || isLinkWithProtocol(link) ? link @@ -22,6 +75,18 @@ export function normalizeLink(base: string, link = ''): string { } const RE_START_END_SLASH = /^\/|\/$/g + +/** + * Get current directory name from path + * Extracts the last segment of a path as the directory name + * + * 从路径获取当前目录名 + * 提取路径的最后一段作为目录名 + * + * @param basePath - Base path to extract from / 要提取的基础路径 + * @param filepath - File path as fallback / 作为后备的文件路径 + * @returns Directory name / 目录名 + */ export function getCurrentDirname(basePath: string | undefined, filepath: string): string { const dirList = normalizePath(basePath || path.dirname(filepath)) .replace(RE_START_END_SLASH, '') @@ -29,6 +94,17 @@ export function getCurrentDirname(basePath: string | undefined, filepath: string return dirList.length > 0 ? dirList[dirList.length - 1] : '' } +/** + * Add base path to path + * Prepends base path to a given path if not already present + * + * 为路径添加基础路径 + * 如果给定路径尚未包含基础路径,则在前面添加 + * + * @param path - Path to modify / 要修改的路径 + * @param base - Base path to prepend / 要添加的基础路径 + * @returns Path with base prepended / 添加了基础路径的路径 + */ export function withBase(path = '', base = '/'): string { path = ensureEndingSlash(ensureLeadingSlash(path)) if (path.startsWith(base)) diff --git a/theme/src/node/utils/pinyin.ts b/theme/src/node/utils/pinyin.ts index 8448c551..586f21b4 100644 --- a/theme/src/node/utils/pinyin.ts +++ b/theme/src/node/utils/pinyin.ts @@ -3,9 +3,19 @@ import { interopDefault } from './interopDefault' let _pinyin: typeof import('pinyin-pro').pinyin | null = null +/** + * Check if pinyin-pro package is installed + * + * 检查是否安装了 pinyin-pro 包 + */ export const hasPinyin = isPackageExists('pinyin-pro') const hasPinyinData = isPackageExists('@pinyin-pro/data') +/** + * Get pinyin function + * + * 获取拼音函数 + */ export async function getPinyin() { if (hasPinyin && !_pinyin) { const { pinyin, addDict } = (await import('pinyin-pro')) diff --git a/theme/src/shared/common/base.ts b/theme/src/shared/common/base.ts index c248ea5e..2dc613a2 100644 --- a/theme/src/shared/common/base.ts +++ b/theme/src/shared/common/base.ts @@ -1,5 +1,11 @@ /** - * 图片 + * Image configuration type + * Supports simple string path or detailed configuration object + * Also supports dark/light mode variants + * + * 图片配置类型 + * 支持简单的字符串路径或详细的配置对象 + * 同时支持深色/浅色模式变体 */ export type ThemeImage = | string @@ -7,29 +13,64 @@ export type ThemeImage | { dark: string, light: string, alt?: string, width?: string | number, height?: string | number } /** - * 图标 + * Icon configuration type + * Supports string name or inline SVG + * + * 图标配置类型 + * 支持字符串名称或内联 SVG */ export type ThemeIcon = string | { svg: string } /** - * 颜色 + * Color configuration type + * Supports single color or dark/light mode variants + * + * 颜色配置类型 + * 支持单一颜色或深色/浅色模式变体 */ export type ThemeColor = string | { light: string, dark: string } /** - * 页内 heading 导航栏 + * Page outline navigation configuration + * Controls the display of heading navigation in the sidebar + * + * 页面目录导航配置 + * 控制侧边栏中标题导航的显示 + * + * - `false`: Disable outline / 禁用目录 + * - `number`: Display headings up to this level / 显示到此级别的标题 + * - `[number, number]`: Display headings within level range / 显示级别范围内的标题 + * - `'deep'`: Display all headings including deeply nested ones / 显示所有标题,包括深层嵌套的 */ export type ThemeOutline = false | number | [number, number] | 'deep' /** - * 徽章 + * Badge configuration interface + * Defines the appearance of a badge element + * + * 徽章配置接口 + * 定义徽章元素的外观 */ export interface ThemeBadge { + /** Badge text content / 徽章文本内容 */ text?: string + /** Badge type for preset styles / 徽章类型,用于预设样式 */ type?: string + /** Badge text color / 徽章文本颜色 */ color?: string + /** Badge background color / 徽章背景颜色 */ bgColor?: string + /** Badge border color / 徽章边框颜色 */ borderColor?: string } +/** + * Light/Dark mode value type + * Supports single value or mode-specific values + * + * 浅色/深色模式值类型 + * 支持单一值或模式特定的值 + * + * @template T - The value type / 值类型 + */ export type ThemeLightDark = T | { light?: T, dark?: T } diff --git a/theme/src/shared/common/social.ts b/theme/src/shared/common/social.ts index 769276e3..80a16512 100644 --- a/theme/src/shared/common/social.ts +++ b/theme/src/shared/common/social.ts @@ -1,5 +1,7 @@ import type { LiteralUnion } from '../utils.js' /** + * Social link + * * 社交链接 */ export interface SocialLink { @@ -9,10 +11,17 @@ export interface SocialLink { } /** + * Social link icon + * * 社交链接图标 */ export type SocialLinkIcon = LiteralUnion | { svg: string, name?: string } +/** + * Social link icon type + * + * 社交链接图标类型 + */ export type SocialLinkIconUnion = | 'discord' | 'telegram' diff --git a/theme/src/shared/data.ts b/theme/src/shared/data.ts index 12aadb8d..d818e131 100644 --- a/theme/src/shared/data.ts +++ b/theme/src/shared/data.ts @@ -2,75 +2,101 @@ import type { LocaleConfig } from 'vuepress' import type { LastUpdatedOptions } from './features/index.js' import type { ThemeLocale, ThemeLocaleDeprecated, ThemeLocaleText } from './locale.js' +/** + * Theme locale data type + * Combines locale settings, deprecated options, and text translations + * + * 主题本地化数据类型 + * 组合本地化设置、弃用选项和文本翻译 + */ export type ThemeLocaleData = ThemeLocale & ThemeLocaleDeprecated & ThemeLocaleText /** - * 主题注入到 客户端的数据 + * Theme data injected to client + * Contains all theme configuration options + * + * 主题注入到客户端的数据 + * 包含所有主题配置选项 */ export interface ThemeData extends ThemeBaseData, ThemeLocaleData { /** - * 部署站点域名。 - * 用于生成 sitemap、 seo等。 - * + * Deployment site hostname + * Used for generating sitemap, SEO, etc. + * 部署站点域名 + * 用于生成 sitemap、seo等 */ hostname?: string /** + * Blog configuration * 博客配置 * @deprecated */ blog?: never /** + * Article link prefix * 文章链接前缀 - * * @default '/article/' * @deprecated */ article?: string /** + * Whether to show "Edit this page" link * 是否显示 "编辑此页" - * * @default true */ editLink?: boolean /** + * Last updated time configuration * 最后更新时间 - * * @default { formatOptions: { dateStyle: 'short', timeStyle: 'short' } } */ lastUpdated?: false | LastUpdatedOptions /** + * Whether to show contributors * 是否显示贡献者 */ contributors?: boolean | { mode?: 'inline' | 'block' } /** + * Whether to show page changelog * 是否显示页面更新日志 */ changelog?: boolean /** + * Documentation repository configuration for generating "Edit this page" link * 文档仓库配置, 用于生成 Edit this page 链接 */ docsRepo?: string /** - * 文档仓库分支配置,用于生成 `Edit this page` 链接。 + * Documentation repository branch for generating "Edit this page" link + * 文档仓库分支配置,用于生成 `Edit this page` 链接 */ docsBranch?: string /** - * 文档仓库目录配置,用于生成 `Edit this page` 链接。 + * Documentation repository directory for generating "Edit this page" link + * 文档仓库目录配置,用于生成 `Edit this page` 链接 */ docsDir?: string } +/** + * Base theme data with locale support + * Provides foundation for multi-language theme configuration + * + * 支持多语言的基础主题数据 + * 为多语言主题配置提供基础 + */ export interface ThemeBaseData extends ThemeLocaleData { /** + * Multi-language locale configuration * 多语言配置 */ locales?: LocaleConfig diff --git a/theme/src/shared/features/autoFrontmatter.ts b/theme/src/shared/features/autoFrontmatter.ts index 1ba3c40d..f241c13f 100644 --- a/theme/src/shared/features/autoFrontmatter.ts +++ b/theme/src/shared/features/autoFrontmatter.ts @@ -1,5 +1,10 @@ import type { LiteralUnion } from '@pengzhanbo/utils' +/** + * Auto frontmatter data fields + * + * 自动 frontmatter 数据字段 + */ export type AutoFrontmatterData = Record< LiteralUnion<'title' | 'createTime' | 'permalink'>, any @@ -48,6 +53,11 @@ export type AutoFrontmatterHandle< D extends AutoFrontmatterData = AutoFrontmatterData, > = (data: D, context: AutoFrontmatterContext) => D | Promise +/** + * Auto frontmatter rule + * + * 自动 frontmatter 规则 + */ export interface AutoFrontmatterRule { /** * File filter, matches the relative path of the file @@ -77,6 +87,11 @@ export interface AutoFrontmatterRule { handle: AutoFrontmatterHandle } +/** + * Auto frontmatter options + * + * 自动 frontmatter 选项 + */ export interface AutoFrontmatterOptions { /** * 是否自动生成 permalink diff --git a/theme/src/shared/features/bulletin.ts b/theme/src/shared/features/bulletin.ts index 81ba3ba7..f2b3ae18 100644 --- a/theme/src/shared/features/bulletin.ts +++ b/theme/src/shared/features/bulletin.ts @@ -1,6 +1,8 @@ import type { Page } from 'vuepress' /** + * Bulletin board configuration + * * 公告栏配置 */ export type BulletinOptions = Record> = T & { diff --git a/theme/src/shared/features/collection.ts b/theme/src/shared/features/collection.ts index c15d1e05..305d1833 100644 --- a/theme/src/shared/features/collection.ts +++ b/theme/src/shared/features/collection.ts @@ -5,6 +5,8 @@ import type { ProfileOptions } from './profile.js' import type { ThemeSidebarItem } from './sidebar.js' /** + * Document collection + * * 文档集合 * * 主题通过 集合的方式,聚合某个目录下的文章,作为一个独立的文档。 @@ -15,6 +17,11 @@ export type ThemeCollections = ThemeCollectionItem[] export type ThemeCollectionItem = ThemePostCollection | ThemeDocCollection +/** + * Base collection configuration + * + * 基础集合配置 + */ export interface ThemeBaseCollection { /** * 文档集合类型 @@ -83,6 +90,8 @@ export interface ThemeBaseCollection { } /** + * Post type article collection + * * post 类型的文章集合 */ export interface ThemePostCollection extends ThemeBaseCollection { @@ -216,6 +225,8 @@ export interface ThemePostCollection extends ThemeBaseCollection { } /** + * Document type article collection + * * 文档类型的文章集合 */ export interface ThemeDocCollection extends ThemeBaseCollection { diff --git a/theme/src/shared/features/copyright.ts b/theme/src/shared/features/copyright.ts index 7b941c04..50915e9f 100644 --- a/theme/src/shared/features/copyright.ts +++ b/theme/src/shared/features/copyright.ts @@ -1,6 +1,8 @@ import type { LiteralUnion } from '../utils.js' /** + * Built-in supported copyright licenses + * * 内置支持的版权协议 */ export type KnownCopyrightLicense @@ -13,6 +15,8 @@ export type KnownCopyrightLicense | 'CC0' /** + * Article copyright license + * * 文章版权协议 * * @see https://creativecommons.org/licenses/ @@ -20,6 +24,8 @@ export type KnownCopyrightLicense export type CopyrightLicense = LiteralUnion /** + * Copyright configuration + * * 版权配置 */ export interface CopyrightOptions { diff --git a/theme/src/shared/features/encrypt.ts b/theme/src/shared/features/encrypt.ts index 7871bcbe..e44cca21 100644 --- a/theme/src/shared/features/encrypt.ts +++ b/theme/src/shared/features/encrypt.ts @@ -1,16 +1,28 @@ +/** + * Encryption options + * Configuration for site-wide and article-level encryption + * + * 加密配置 + * 用于全站和文章级别加密的配置 + */ export interface EncryptOptions { /** + * Enable site-wide encryption * 是否启用全站加密 * @default false */ global?: boolean /** + * Super admin password, can decrypt the entire site and any encrypted articles * 超级权限密码, 该密码可以解密全站,以及任意加密的文章 - * */ admin?: string | string[] /** + * Article passwords, can encrypt single articles or entire directories + * by matching the article's markdown file relative path, page access path, + * or directory path. If starts with `^`, it's treated as a regex pattern. + * * 文章密码, 可以通过 文章的 markdown 文件相对路径、页面访问路径、 * 目录路径 等,对 单个文章 或者 整个目录 进行 加密。 * 如果是以 `^` 开头,则被认为是类似于正则表达式进行匹配。 diff --git a/theme/src/shared/features/latestUpdated.ts b/theme/src/shared/features/latestUpdated.ts index 3d7fd8f9..d741d5f6 100644 --- a/theme/src/shared/features/latestUpdated.ts +++ b/theme/src/shared/features/latestUpdated.ts @@ -1,3 +1,8 @@ +/** + * Last updated options + * + * 最后更新时间选项 + */ export interface LastUpdatedOptions { /** * Set options for last updated time formatting. diff --git a/theme/src/shared/features/markdown.ts b/theme/src/shared/features/markdown.ts index 0f51270a..d06f2841 100644 --- a/theme/src/shared/features/markdown.ts +++ b/theme/src/shared/features/markdown.ts @@ -5,6 +5,11 @@ import type { MarkdownIncludePluginOptions } from '@vuepress/plugin-markdown-inc import type { MarkdownMathPluginOptions } from '@vuepress/plugin-markdown-math' import type { MarkdownPowerPluginOptions } from 'vuepress-plugin-md-power' +/** + * Markdown enhancement options + * + * Markdown 增强选项 + */ export interface MarkdownOptions extends MarkdownPowerPluginOptions, MarkdownChartPluginOptions, Pick { diff --git a/theme/src/shared/features/navbar.ts b/theme/src/shared/features/navbar.ts index 27fd84bc..2d47c3b5 100644 --- a/theme/src/shared/features/navbar.ts +++ b/theme/src/shared/features/navbar.ts @@ -1,34 +1,54 @@ import type { ThemeBadge, ThemeIcon } from '../common/index.js' /** + * Navigation item + * Can be a simple link string or detailed configuration + * * 导航项 + * 可以是简单的链接字符串或详细配置 */ export type ThemeNavItem = string | NavItemWithLink | NavItemWithChildren +/** + * Navigation item with link + * Single link navigation item + * + * 带链接的导航项 + * 单个链接导航项 + */ export interface NavItemWithLink { /** + * Navigation text * 导航文本 */ text: string /** + * Navigation link * 导航链接 */ link: string /** + * Navigation icon * 导航图标 */ icon?: ThemeIcon /** + * Badge displayed next to the link * 徽章 */ badge?: string | ThemeBadge /** + * Page prefix for current group (not used for link items) * 当前分组的页面前缀 */ prefix?: never + /** + * Child items (not used for link items) + * 子项 + */ items?: never /** @@ -40,57 +60,98 @@ export interface NavItemWithLink { */ activeMatch?: string + /** + * Link relationship attribute + * 链接关系属性 + */ rel?: string + + /** + * Link target attribute + * 链接目标属性 + */ target?: string + + /** + * Whether to hide the icon + * 是否隐藏图标 + */ noIcon?: boolean } +/** + * Navigation item children (dropdown menu items) + * Group of items in a dropdown menu + * + * 导航下拉菜单子项 + * 下拉菜单中的项目组 + */ export interface NavItemChildren { /** + * Dropdown menu text * 下拉菜单的文本 */ text?: string /** - * + * Page prefix for current group * 当前分组的页面前缀 */ prefix?: string /** + * Navigation icon * 导航图标 */ icon?: ThemeIcon /** + * Badge displayed next to the text * 徽章 */ badge?: string | ThemeBadge /** + * Dropdown menu items * 导航栏下拉菜单 */ items: (string | NavItemWithLink)[] } +/** + * Navigation item with children (dropdown) + * Dropdown menu navigation item + * + * 带子项的导航项(下拉菜单) + * 下拉菜单导航项 + */ export interface NavItemWithChildren { - text?: string /** + * Navigation text + * 导航文本 + */ + text?: string + + /** + * Page prefix for current group * 当前分组的页面前缀 */ prefix?: string /** + * Navigation icon * 导航图标 */ icon?: ThemeIcon /** + * Badge displayed next to the text * 徽章 */ badge?: string | ThemeBadge /** + * Dropdown menu items * 导航栏下拉菜单 */ items: (string | NavItemChildren | NavItemWithLink)[] @@ -99,8 +160,8 @@ export interface NavItemWithChildren { * `activeMatch` is expected to be a regex string. We can't use actual * RegExp object here because it isn't serializable * - * `activeMatch` 应为正则表达式字符串,但必须将其定义为字符串。 - * 我们不能在这里使用实际的 RegExp 对象,因为它在构建期间不可序列化。 + * `activeMatch` 应为正则表达式字符串,但必须将其定义为字符串 + * 我们不能在这里使用实际的 RegExp 对象,因为它在构建期间不可序列化 */ activeMatch?: string } diff --git a/theme/src/shared/features/notes.ts b/theme/src/shared/features/notes.ts index 53065b60..7159f4f9 100644 --- a/theme/src/shared/features/notes.ts +++ b/theme/src/shared/features/notes.ts @@ -1,6 +1,8 @@ import type { ThemeSidebarItem } from './sidebar.js' /** + * Notes list options + * * @deprecated */ export interface ThemeNoteListOptions { @@ -21,6 +23,8 @@ export interface ThemeNoteListOptions { } /** + * Note item + * * @deprecated */ export interface ThemeNote { diff --git a/theme/src/shared/features/posts.ts b/theme/src/shared/features/posts.ts index 37d415e4..89c57eb8 100644 --- a/theme/src/shared/features/posts.ts +++ b/theme/src/shared/features/posts.ts @@ -1,47 +1,67 @@ import type { ReadingTime } from '@vuepress/plugin-reading-time' +/** + * Posts category item + * Represents a category for blog posts + * + * 文章分类项 + * 表示博客文章的分类 + */ export interface PostsCategoryItem { /** + * Category ID * 分类 ID */ id: string /** + * Category sort order * 分类排序 */ sort: number /** + * Category name * 分类名称 */ name: string } /** + * Posts cover layout + * Defines the position of post cover images + * * 文章封面图布局 + * 定义文章封面图的位置 */ export type PostsCoverLayout = 'left' | 'right' | 'odd-left' | 'odd-right' | 'top' /** + * Posts cover style + * Configuration for post cover image appearance + * * 文章封面图样式 + * 文章封面图外观配置 */ export interface PostsCoverStyle { /** + * Cover image position/layout * 文章封面图的位置 */ layout?: PostsCoverLayout /** + * Cover image aspect ratio * 文章封面图的比例 - * * @default '4:3' */ ratio?: number | `${number}:${number}` | `${number}/${number}` /** + * Cover image width, only effective when layout is 'left' or 'right' * 封面图的宽度, 仅在 layout 为 'left' 或 'right' 时生效 - * * @default 240 */ width?: number /** + * Whether to use compact mode where cover image touches container edges * 是否使用紧凑模式,紧凑模式下,封面图紧贴容器边缘 * @default false */ @@ -49,67 +69,88 @@ export interface PostsCoverStyle { } /** + * Blog post item + * Represents a single blog post with all its metadata + * * 博客文章 + * 表示单个博客文章及其所有元数据 */ export interface ThemePostsItem { /** + * Article title * 文章标题 */ title: string /** + * Article excerpt/summary * 文章摘要 */ excerpt: string /** + * Article path/URL * 文章路径 */ path: string /** + * Article tags * 文章标签 */ tags?: string[] /** + * Whether article is pinned, number indicates priority * 文章是否置顶,数字表示置顶的优先级 * @default false */ sticky?: boolean | number /** + * Article categories * 文章所属分类 */ categoryList?: PostsCategoryItem[] /** + * Article creation time * 文章创建时间 */ createTime: string /** + * Article language * 文章语言 */ lang: string /** + * Whether article is encrypted * 文章是否加密 */ encrypt?: boolean /** + * Article cover image URL * 文章封面图 */ cover?: string /** + * Article cover image style * 文章封面图样式 */ coverStyle?: PostsCoverStyle /** + * Is article draft * 文章是否为草稿 */ draft?: boolean /** + * Reading statistics * 阅读统计 */ readingTime?: ReadingTime } /** + * Blog post list + * Array of blog post items + * * 博客文章列表 + * 博客文章项的数组 */ export type ThemePosts = ThemePostsItem[] diff --git a/theme/src/shared/features/profile.ts b/theme/src/shared/features/profile.ts index 239941a6..857dce5c 100644 --- a/theme/src/shared/features/profile.ts +++ b/theme/src/shared/features/profile.ts @@ -1,4 +1,6 @@ /** + * Profile options + * * 个人资料 */ export interface ProfileOptions { diff --git a/theme/src/shared/features/search.ts b/theme/src/shared/features/search.ts index 2f433e27..76710638 100644 --- a/theme/src/shared/features/search.ts +++ b/theme/src/shared/features/search.ts @@ -1,16 +1,37 @@ import type { SearchPluginOptions } from '@vuepress-plume/plugin-search' import type { DocSearchPluginOptions } from '@vuepress/plugin-docsearch' +/** + * Base search options + * + * 基础搜索选项 + */ interface SearchBaseOptions { + /** Search provider / 搜索提供者 */ provider: 'local' | 'algolia' } +/** + * Local search options + * + * 本地搜索选项 + */ export interface LocalSearchOptions extends SearchBaseOptions, SearchPluginOptions { provider: 'local' } +/** + * Algolia DocSearch options + * + * Algolia DocSearch 选项 + */ export interface DocSearchOptions extends SearchBaseOptions, DocSearchPluginOptions { provider: 'algolia' } +/** + * Search options (union type) + * + * 搜索选项(联合类型) + */ export type SearchOptions = LocalSearchOptions | DocSearchOptions diff --git a/theme/src/shared/features/sidebar.ts b/theme/src/shared/features/sidebar.ts index e3e8680a..2aa3a25d 100644 --- a/theme/src/shared/features/sidebar.ts +++ b/theme/src/shared/features/sidebar.ts @@ -1,7 +1,21 @@ import type { ThemeBadge, ThemeIcon } from '../common/index.js' +/** + * Sidebar configuration + * Can be auto-generated, single sidebar, or multiple sidebars + * + * 侧边栏配置 + * 可以是自动生成、单个侧边栏或多个侧边栏 + */ export type ThemeSidebar = 'auto' | (string | ThemeSidebarItem)[] | ThemeSidebarMulti +/** + * Multi-sidebar configuration + * Maps paths to different sidebar configurations + * + * 多侧边栏配置 + * 将路径映射到不同的侧边栏配置 + */ export type ThemeSidebarMulti = Record< string, | 'auto' @@ -9,51 +23,76 @@ export type ThemeSidebarMulti = Record< | { items: 'auto' | (string | ThemeSidebarItem)[], prefix?: string } > +/** + * Sidebar item configuration + * Defines a single item in the sidebar + * + * 侧边栏项 + * 定义侧边栏中的单个项目 + */ export interface ThemeSidebarItem { /** + * Sidebar item text * 侧边栏文本 */ text?: string /** + * Sidebar item link * 侧边栏链接 */ link?: string /** + * Sidebar item icon * 侧边栏图标 */ icon?: ThemeIcon /** + * Sidebar item badge * 侧边栏徽章 */ badge?: string | ThemeBadge /** + * Child sidebar items * 次级侧边栏分组 */ items?: 'auto' | (string | ThemeSidebarItem)[] /** - * 如果未指定,组不可折叠。 + * Whether the group is collapsible + * - If not specified, group is not collapsible + * - If `true`, group is collapsible and collapsed by default + * - If `false`, group is collapsible but expanded by default * - * 如果为`true`,组可折叠,并默认折叠。 - * - * 如果为`false`,组可折叠,但默认展开。 + * 如果未指定,组不可折叠 + * 如果为`true`,组可折叠,并默认折叠 + * 如果为`false`,组可折叠,但默认展开 */ collapsed?: boolean /** + * Link prefix for current group * 当前分组的链接前缀 */ prefix?: string /** - * @deprecated 使用 `prefix` 替代 + * @deprecated Use `prefix` instead / 使用 `prefix` 替代 */ dir?: string + /** + * Link relationship attribute + * 链接关系属性 + */ rel?: string + + /** + * Link target attribute + * 链接目标属性 + */ target?: string } diff --git a/theme/src/shared/features/transition.ts b/theme/src/shared/features/transition.ts index 91f04f92..7d907f45 100644 --- a/theme/src/shared/features/transition.ts +++ b/theme/src/shared/features/transition.ts @@ -1,15 +1,26 @@ +/** + * Transition animation options + * + * 过渡动画选项 + */ export interface TransitionOptions { /** + * Enable page transition animation + * * 是否启用 页面间跳转过渡动画 * @default true */ page?: boolean /** + * Enable blog post list transition animation + * * 是否启用 博客文章列表过渡动画 * @default true */ postList?: boolean /** + * Enable dark/light mode switch transition animation + * * 是否启用 深色/浅色 模式切换过渡动画 * @default 'fade' */ diff --git a/theme/src/shared/frontmatter/friends.ts b/theme/src/shared/frontmatter/friends.ts index 22d11273..4772d2fe 100644 --- a/theme/src/shared/frontmatter/friends.ts +++ b/theme/src/shared/frontmatter/friends.ts @@ -2,6 +2,8 @@ import type { SocialLink, ThemeColor } from '../common/index.js' import type { ThemeNormalFrontmatter } from './normal.js' /** + * Friend link item + * * 友链 */ export interface FriendsItem { @@ -48,6 +50,8 @@ export interface FriendsItem { } /** + * Friend group + * * 友链分组 */ export interface FriendGroup { @@ -66,7 +70,9 @@ export interface FriendGroup { } /** - * 友情链接 + * Friends page frontmatter + * + * 友链页面 frontmatter */ export interface ThemeFriendsFrontmatter extends ThemeNormalFrontmatter { /** diff --git a/theme/src/shared/frontmatter/home.ts b/theme/src/shared/frontmatter/home.ts index cf36b862..4e6ad411 100644 --- a/theme/src/shared/frontmatter/home.ts +++ b/theme/src/shared/frontmatter/home.ts @@ -3,6 +3,11 @@ import type { LiteralUnion } from '../utils.js' import type { ThemeHomeHeroEffect, ThemeHomeHeroEffectConfig, ThemeHomeHeroTintPlate } from './homeHeroEffects.js' import type { ThemeNormalFrontmatter } from './normal.js' +/** + * Home page frontmatter + * + * 首页 frontmatter + */ export interface ThemeHomeFrontmatter extends ThemeNormalFrontmatter, Omit { home?: true friends?: never diff --git a/theme/src/shared/frontmatter/homeHeroEffects.ts b/theme/src/shared/frontmatter/homeHeroEffects.ts index b40ba26f..f870ab74 100644 --- a/theme/src/shared/frontmatter/homeHeroEffects.ts +++ b/theme/src/shared/frontmatter/homeHeroEffects.ts @@ -3,8 +3,18 @@ import type { CSSProperties } from 'vue' import type { ThemeLightDark } from '../common/index.js' import type { LiteralUnion } from '../utils.js' +/** + * Home page hero effect type + * + * 首页 Hero 动画效果类型 + */ export type ThemeHomeHeroEffect = LiteralUnion<'tint-plate' | 'prism' | 'pixel-blast' | 'hyper-speed' | 'liquid-ether' | 'dot-grid' | 'iridescence' | 'orb' | 'beams' | 'lightning' | 'dark-veil'> +/** + * Home page hero effect configuration + * + * 首页 Hero 动画效果配置 + */ export type ThemeHomeHeroEffectConfig = | ThemeHomeHeroTintPlate | ThemeHomeHeroPrism @@ -18,12 +28,22 @@ export type ThemeHomeHeroEffectConfig | ThemeHomeHeroLightning | ThemeHomeHeroDarkVeil +/** + * Tint plate effect configuration + * + * 色调板效果配置 + */ export type ThemeHomeHeroTintPlate = ThemeLightDark<{ rgb: string | number } | { r: { value: number, offset: number } g: { value: number, offset: number } b: { value: number, offset: number } }> +/** + * Prism effect configuration + * + * 棱镜效果配置 + */ export interface ThemeHomeHeroPrism { /** * Apex height of the prism (world units) @@ -102,8 +122,18 @@ export interface ThemeHomeHeroPrism { timeScale?: number } +/** + * Pixel blast variant type + * + * 像素爆炸变体类型 + */ export type ThemeHomeHeroPixelBlastVariant = 'square' | 'circle' | 'triangle' | 'diamond' +/** + * Pixel blast effect configuration + * + * 像素爆炸效果配置 + */ export interface ThemeHomeHeroPixelPixelBlast { /** * Pixel shape variant @@ -220,16 +250,31 @@ export interface ThemeHomeHeroPixelPixelBlast { backgroundAttachment?: 'fixed' | 'local' } +/** + * Hyper speed distortion + * + * 高速扭曲配置 + */ export interface ThemeHomeHeroHyperSpeedDistortion { uniforms: Record getDistortion: string getJS?: (progress: number, time: number) => THREE.Vector3 } +/** + * Hyper speed distortions + * + * 高速扭曲集合 + */ export interface ThemeHomeHeroHyperSpeedDistortions { [key: string]: ThemeHomeHeroHyperSpeedDistortion } +/** + * Hyper speed colors + * + * 高速颜色配置 + */ export interface ThemeHomeHeroHyperSpeedColors { roadColor: number islandColor: number @@ -241,6 +286,11 @@ export interface ThemeHomeHeroHyperSpeedColors { sticks: number } +/** + * Hyper speed effect configuration + * + * 高速效果配置 + */ export interface ThemeHomeHeroHyperSpeed { onSpeedUp?: (ev: MouseEvent) => void onSlowDown?: (ev: MouseEvent) => void diff --git a/theme/src/shared/frontmatter/normal.ts b/theme/src/shared/frontmatter/normal.ts index 9946c192..52a09392 100644 --- a/theme/src/shared/frontmatter/normal.ts +++ b/theme/src/shared/frontmatter/normal.ts @@ -1,5 +1,10 @@ import type { PageFrontmatter } from 'vuepress' +/** + * Normal page frontmatter + * + * 普通页面 frontmatter + */ export type ThemeNormalFrontmatter = PageFrontmatter<{ /** @@ -17,16 +22,22 @@ export type ThemeNormalFrontmatter = PageFrontmatter<{ friends?: boolean /** - * page layout + * Page layout + * + * 页面布局 */ pageLayout?: false | 'home' | 'posts' | 'doc' | 'custom' | 'page' | 'friends' /** + * Custom page class + * * 自定义页面 class */ pageClass?: string /** + * Show navigation bar + * * 是否显示导航栏 * * @default true @@ -34,6 +45,8 @@ export type ThemeNormalFrontmatter = PageFrontmatter<{ navbar?: boolean /** + * Show back to top button + * * 是否显示返回顶部按钮 * * @default true @@ -41,6 +54,8 @@ export type ThemeNormalFrontmatter = PageFrontmatter<{ backToTop?: boolean /** + * Show down arrow sign + * * 是否显示向下箭头标志 * * @default false @@ -48,6 +63,8 @@ export type ThemeNormalFrontmatter = PageFrontmatter<{ signDown?: boolean /** + * Show external link icon on current page + * * 当前页面是否显示 外部链接图标 * * @default true diff --git a/theme/src/shared/frontmatter/page.ts b/theme/src/shared/frontmatter/page.ts index c325696a..f91db282 100644 --- a/theme/src/shared/frontmatter/page.ts +++ b/theme/src/shared/frontmatter/page.ts @@ -3,64 +3,100 @@ import type { ThemeBadge, ThemeIcon, ThemeOutline } from '../common/index.js' import type { NavItemWithLink } from '../features/index.js' import type { ThemeNormalFrontmatter } from './normal.js' +/** + * Document page frontmatter + * + * 文档页面 frontmatter + */ export interface ThemePageFrontmatter extends ThemeNormalFrontmatter { home?: never friends?: never /** + * Enable comments + * * 是否开启评论 */ comments?: boolean /** + * Show edit button + * * 是否显示编辑按钮 */ editLink?: boolean /** + * Edit link pattern + * * 编辑链接模式 */ editLinkPattern?: string /** + * Show last updated time + * * 是否显示最近更新时间 */ lastUpdated?: boolean /** + * Show contributors + * * 是否显示贡献者 */ contributors?: boolean | string[] /** + * Show changelog + * * 是否显示变更历史 */ changelog?: boolean /** + * Previous page + * * 上一篇 */ prev?: string | NavItemWithLink /** + * Next page + * * 下一篇 */ next?: string | NavItemWithLink /** + * Show sidebar, can also force specify which sidebar to show on current page + * * 是否显示侧边栏,也可以强制指定当前页面显示哪个侧边栏 */ sidebar?: string | false /** + * Show page aside + * * 是否显示页内侧边栏 */ aside?: boolean | 'left' /** + * Show content outline, only works when page aside is enabled + * * 是否显示内容大纲,仅在页内侧边栏开启时生效 */ outline?: ThemeOutline /** + * Show reading time and word count + * * 是否显示阅读时间、字数 */ readingTime?: boolean /** + * Watermark configuration + * * 水印配置 */ watermark?: WatermarkPluginFrontmatter['watermark'] & { fullPage?: boolean } /** + * Icon used for navbar and sidebar + * Supports iconify icons, use iconify name directly for auto-loading + * Supports local and remote SVG icons, use SVG URL directly + * Or pass SVG string directly + * * 用作 navbar 、 sidebar 的图标 * 支持 iconify 图标,直接使用 iconify name 即可自动加载 * 支持 本地、远程 svg 图标,直接使用 svg 的 url 即可 @@ -69,6 +105,8 @@ export interface ThemePageFrontmatter extends ThemeNormalFrontmatter { icon?: ThemeIcon /** + * Title badge + * * 标题徽章 */ badge?: string | ThemeBadge diff --git a/theme/src/shared/frontmatter/post.ts b/theme/src/shared/frontmatter/post.ts index 4d95b45c..05aabbef 100644 --- a/theme/src/shared/frontmatter/post.ts +++ b/theme/src/shared/frontmatter/post.ts @@ -1,23 +1,34 @@ import type { CopyrightLicense, CopyrightOptions, PostsCoverStyle } from '../features/index.js' import type { ThemePageFrontmatter } from './page.js' +/** + * Post/Blog page frontmatter + * + * 文章/博客页面 frontmatter + */ export interface ThemePostFrontmatter extends ThemePageFrontmatter { /** + * Creation time + * * 创建时间 */ createTime?: string | false /** + * Article tags + * * 文章标签 */ tags?: string[] /** + * Sticky post + * * 是否置顶 */ sticky?: boolean | number /** - * 标记当前文章是否为博客文章 + * Mark current article as blog post * * `false` 时,从博客列表中排除 * @@ -26,6 +37,9 @@ export interface ThemePostFrontmatter extends ThemePageFrontmatter { article?: boolean /** + * Mark current article as draft + * Draft posts will not appear in blog list + * * 标记当前文章是否为草稿状态, * 草稿状态下的文章不会出现在博客列表中 * @@ -34,36 +48,53 @@ export interface ThemePostFrontmatter extends ThemePageFrontmatter { draft?: boolean /** + * Article cover image + * * 文章封面图 */ cover?: string /** + * Article cover style + * * 文章封面图样式 */ coverStyle?: PostsCoverStyle /** + * Whether to show article excerpt, when passing string it will be custom excerpt, then `` is invalid + * * 是否展示文章摘要,传入 string 时为自定义摘要,此时 `` 无效 */ excerpt?: boolean | string /** + * Copyright information + * * 版权信息 */ copyright?: boolean | CopyrightLicense | CopyrightFrontmatter /** + * Article encryption password + * * 文章加密密码 */ password?: string | string[] /** + * Article encryption password hint text + * * 文章加密密码提示文本 */ passwordHint?: string } +/** + * Copyright frontmatter + * + * 版权 frontmatter + */ export interface CopyrightFrontmatter extends CopyrightOptions { /** * 作品的作者 diff --git a/theme/src/shared/locale.ts b/theme/src/shared/locale.ts index ffffc9d0..20fdb2a4 100644 --- a/theme/src/shared/locale.ts +++ b/theme/src/shared/locale.ts @@ -13,6 +13,8 @@ import type { import type { LiteralUnion } from './utils.js' /** + * Built-in multilingual configuration + * * 内置的多语言配置 */ export interface PresetLocale extends Record {} @@ -177,6 +179,8 @@ export interface ThemeLocale extends LocaleData { } /** + * Deprecated theme locale configuration + * * 已弃用的配置 */ export interface ThemeLocaleDeprecated { @@ -187,6 +191,8 @@ export interface ThemeLocaleDeprecated { } /** + * Theme multilingual text configuration + * * 主题多语言文本配置 */ export interface ThemeLocaleText { diff --git a/theme/src/shared/options.ts b/theme/src/shared/options.ts index 5d2449c2..c6dad6f4 100644 --- a/theme/src/shared/options.ts +++ b/theme/src/shared/options.ts @@ -18,127 +18,152 @@ import type { import type { ThemeBuiltinPlugins } from './plugins.js' /** - * 主题配置 (支持热更新) + * Theme configuration (supports hot reload) + * Core theme settings that can be updated during development + * + * 主题配置(支持热更新) + * 可在开发过程中更新的核心主题设置 */ export interface ThemeConfig extends ThemeBaseData { /** + * Site encryption configuration * 站点加密配置 */ encrypt?: EncryptOptions /** + * Automatic frontmatter insertion * 自动插入 frontmatter */ autoFrontmatter?: false | Omit } /** + * Full theme configuration + * Complete theme options including all features + * * 主题全量配置 + * 包含所有功能的完整主题选项 */ export interface ThemeOptions extends ThemeConfig, ThemeFeatureOptions, Omit { /** - * 主题内置插件配置 + * Theme built-in plugins configuration + * Do not confuse this with [vuepress plugins](https://v2.vuepress.vuejs.org/zh/reference/config.html#plugins) * + * 主题内置插件配置 * 请勿将此配置与 [vuepress plugins](https://v2.vuepress.vuejs.org/zh/reference/config.html#plugins) 混淆 */ plugins?: ThemeBuiltinPlugins } /** - * 主题功能配置,此配置用于扁平化管理内置插件、功能 + * Theme feature configuration + * Used for flat management of built-in plugins and features + * + * 主题功能配置 + * 此配置用于扁平化管理内置插件、功能 */ export interface ThemeFeatureOptions { /** + * Whether to enable compilation cache * 是否启用编译缓存 - * * @default 'filesystem' */ cache?: false | 'memory' | 'filesystem' /** + * Custom theme configuration file path * 自定义主题配置文件路径 */ configFile?: string /** + * Blog configuration * 博客配置 - * - * @deprecated 使用 {@link collections} 代替 + * @deprecated Use {@link collections} instead / 使用 {@link collections} 代替 */ blog?: never /** + * Whether to show "Edit this page" link * 是否显示 "编辑此页" - * * @default true */ editLink?: boolean /** + * Last updated time configuration * 最后更新时间 - * * @default { formatOptions: { dateStyle: 'short', timeStyle: 'short' } } */ lastUpdated?: false | LastUpdatedOptions /** + * Contributors configuration * 贡献者配置 */ contributors?: boolean | Prettify /** + * Page changelog configuration * 页面变更记录配置 */ changelog?: boolean | ChangelogOptions /** + * Site search configuration * 站点搜索配置 - * - * @default - * ``` - * { provider: 'local' } - * ``` + * @default { provider: 'local' } */ search?: boolean | SearchOptions /** + * Markdown enhancement configuration * markdown 功能增强配置 */ markdown?: MarkdownOptions /** + * Code block highlight configuration + * Theme uses `shiki` as the code block highlighter * 代码块高亮配置,主题使用 `shiki` 作为代码块高亮器 */ codeHighlighter?: false | ShikiPluginOptions /** + * Comment configuration * 评论配置 */ comment?: false | CommentPluginOptions /** + * Watermark configuration * 水印配置 */ watermark?: boolean | Prettify /** + * Reading time and word count statistics * 阅读时间、字数统计 */ readingTime?: false | ReadingTimePluginOptions /** + * Code copy configuration * 代码复制 */ copyCode?: false | CopyCodePluginOptions /** + * Asset link replacement configuration * 资源链接替换 */ replaceAssets?: false | ReplaceAssetsPluginOptions /** + * llmstxt configuration * llmstxt 配置 */ llmstxt?: boolean | LlmsPluginOptions diff --git a/theme/src/shared/pageData.ts b/theme/src/shared/pageData.ts index 122182e5..fc8c16a1 100644 --- a/theme/src/shared/pageData.ts +++ b/theme/src/shared/pageData.ts @@ -3,25 +3,38 @@ import type { GitPluginPageData } from '@vuepress/plugin-git' import type { ReadingTime } from '@vuepress/plugin-reading-time' import type { PostsCategoryItem } from './features/index.js' +/** + * Theme page data interface + * Extends GitPluginPageData with theme-specific properties + * + * 主题页面数据接口 + * 扩展 GitPluginPageData,添加主题特定的属性 + */ export interface ThemePageData extends GitPluginPageData { /** + * Page layout type * 页面布局类型 + * * @internal */ type: 'friends' | 'posts' | 'posts-tags' | 'posts-archives' | 'posts-categories' /** + * Blog post category list * 博客文章分类列表 */ categoryList?: PostsCategoryItem[] /** + * File path relative to root directory * 相对于根目录的文件路径 */ filePathRelative: Nullable /** + * Reading time information * 阅读时间 */ readingTime?: ReadingTime /** + * Whether to enable bulletin * 是否启用公告 */ bulletin?: boolean diff --git a/theme/src/shared/plugins.ts b/theme/src/shared/plugins.ts index b01889c6..3b74022d 100644 --- a/theme/src/shared/plugins.ts +++ b/theme/src/shared/plugins.ts @@ -18,28 +18,43 @@ import type { SitemapPluginOptions } from '@vuepress/plugin-sitemap' import type { WatermarkPluginOptions } from '@vuepress/plugin-watermark' import type { MarkdownPowerPluginOptions } from 'vuepress-plugin-md-power' +/** + * Theme built-in plugins configuration + * + * 主题内置插件配置 + */ export interface ThemeBuiltinPlugins { /** + * plugin-search configuration + * * plugin-search 配置 */ search?: false | Partial /** + * plugin-docsearch configuration + * * plugin-docsearch 配置 */ docsearch?: false | DocSearchOptions /** + * Code block copy button configuration + * * 代码块复制按钮配置 */ copyCode?: false | CopyCodePluginOptions /** + * Code highlighting configuration + * * 代码高亮 配置 */ shiki?: false | ShikiPluginOptions /** + * Git plugin configuration + * * git 插件 配置 */ git?: boolean diff --git a/theme/src/shared/resolved/navbar.ts b/theme/src/shared/resolved/navbar.ts index fc6824c0..29bb0bcf 100644 --- a/theme/src/shared/resolved/navbar.ts +++ b/theme/src/shared/resolved/navbar.ts @@ -1,7 +1,11 @@ import type { ThemeBadge, ThemeIcon } from '../common/index.js' /** + * Resolved navigation item for internal theme use + * Union type for resolved navigation items + * * 主题内部使用的已解析的导航项 + * 已解析导航项的联合类型 * @internal */ export type ResolvedNavItem @@ -9,40 +13,139 @@ export type ResolvedNavItem | ResolvedNavItemWithChildren /** - * 主题内部使用的已解析的导航子项 + * Resolved navigation link item for internal theme use + * Single link navigation item ready for rendering + * + * 主题内部使用的已解析的导航链接项 + * 准备用于渲染的单个链接导航项 * @internal */ export interface ResolvedNavItemWithLink { + /** + * Navigation text + * 导航文本 + */ text: string + + /** + * Navigation link + * 导航链接 + */ link: string + + /** + * Navigation icon + * 导航图标 + */ icon?: ThemeIcon + + /** + * Badge displayed next to the link + * 徽章 + */ badge?: string | ThemeBadge + + /** + * Child items (not used for link items) + * 子项 + */ items?: never + + /** + * Regex pattern for active state matching + * 用于激活状态匹配的正则表达式模式 + */ activeMatch?: string + + /** + * Link relationship attribute + * 链接关系属性 + */ rel?: string + + /** + * Link target attribute + * 链接目标属性 + */ target?: string + + /** + * Whether to hide the icon + * 是否隐藏图标 + */ noIcon?: boolean } /** - * 主题内部使用的已解析的导航子项 + * Resolved navigation children for internal theme use + * Group of items in a dropdown menu + * + * 主题内部使用的已解析的导航子项(下拉菜单) + * 下拉菜单中的项目组 * @internal */ export interface ResolvedNavItemChildren { + /** + * Navigation text + * 导航文本 + */ text?: string + + /** + * Navigation icon + * 导航图标 + */ icon?: ThemeIcon + + /** + * Badge displayed next to the text + * 徽章 + */ badge?: string | ThemeBadge + + /** + * Child navigation items + * 子导航项 + */ items: ResolvedNavItemWithLink[] } /** - * 主题内部使用的已解析的导航子项 + * Resolved navigation item with children for internal theme use + * Dropdown menu navigation item + * + * 主题内部使用的已解析的带子项的导航项 + * 下拉菜单导航项 * @internal */ export interface ResolvedNavItemWithChildren { + /** + * Navigation text + * 导航文本 + */ text?: string + + /** + * Navigation icon + * 导航图标 + */ icon?: ThemeIcon + + /** + * Badge displayed next to the text + * 徽章 + */ badge?: string | ThemeBadge + + /** + * Dropdown menu items + * 下拉菜单项 + */ items: (ResolvedNavItemChildren | ResolvedNavItemWithLink)[] + + /** + * Regex pattern for active state matching + * 用于激活状态匹配的正则表达式模式 + */ activeMatch?: string } diff --git a/theme/src/shared/resolved/sidebar.ts b/theme/src/shared/resolved/sidebar.ts index 75ff03fb..4b722631 100644 --- a/theme/src/shared/resolved/sidebar.ts +++ b/theme/src/shared/resolved/sidebar.ts @@ -1,13 +1,21 @@ import type { ThemeBadge, ThemeIcon } from '../common/index.js' /** + * Resolved sidebar for internal theme use + * Processed sidebar configuration ready for rendering + * * 已解析的侧边栏 + * 已处理的侧边栏配置,准备用于渲染 * @internal */ export type ResolvedSidebar = ResolvedSidebarItem[] | ResolvedSidebarMulti /** + * Resolved multiple sidebars for internal theme use + * Maps paths to resolved sidebar items + * * 已解析的多个侧边栏 + * 将路径映射到已解析的侧边栏项目 * @internal */ export type ResolvedSidebarMulti = Record< @@ -16,54 +24,76 @@ export type ResolvedSidebarMulti = Record< > /** + * Resolved sidebar item for internal theme use + * Processed sidebar item ready for rendering + * * 已解析的侧边栏子项 + * 已处理的侧边栏项目,准备用于渲染 * @internal */ export interface ResolvedSidebarItem { /** + * Sidebar item text * 侧边栏文本 */ text?: string /** + * Sidebar item link * 侧边栏链接 */ link?: string /** + * Sidebar item icon * 侧边栏图标 */ icon?: ThemeIcon /** + * Sidebar item badge * 侧边栏徽章 */ badge?: string | ThemeBadge /** + * Child sidebar items * 次级侧边栏分组 */ items?: ResolvedSidebarItem[] /** - * 如果未指定,组不可折叠。 + * Whether the group is collapsible + * - If not specified, group is not collapsible + * - If `true`, group is collapsible and collapsed by default + * - If `false`, group is collapsible but expanded by default * - * 如果为`true`,组可折叠,并默认折叠。 - * - * 如果为`false`,组可折叠,但默认展开。 + * 如果未指定,组不可折叠 + * 如果为`true`,组可折叠,并默认折叠 + * 如果为`false`,组可折叠,但默认展开 */ collapsed?: boolean /** + * Link prefix for current group * 当前分组的链接前缀 */ prefix?: string /** - * @deprecated 使用 `prefix` 替代 + * @deprecated Use `prefix` instead / 使用 `prefix` 替代 */ dir?: string + /** + * Link relationship attribute + * 链接关系属性 + */ rel?: string + + /** + * Link target attribute + * 链接目标属性 + */ target?: string } diff --git a/theme/src/shared/utils.ts b/theme/src/shared/utils.ts index abb1146f..6969977d 100644 --- a/theme/src/shared/utils.ts +++ b/theme/src/shared/utils.ts @@ -1,17 +1,40 @@ /** * A literal type that supports custom further strings but preserves autocompletion in IDEs. * + * 支持自定义字符串字面量的类型,同时保留 IDE 中的自动补全功能。 + * * @see [copied from issue](https://github.com/microsoft/TypeScript/issues/29729#issuecomment-471566609) + * + * @template Union - The union type of allowed literal values / 允许的字符串字面量联合类型 + * @template Base - The base type, defaults to string / 基础类型,默认为 string */ export type LiteralUnion = | Union | (Base & { zz_IGNORE_ME?: never }) +/** + * Convert union type to intersection type + * Uses distributive conditional types and function parameter inference + * + * 将联合类型转换为交叉类型 + * 使用分配条件类型和函数参数推断 + * + * @template U - The union type to convert / 要转换的联合类型 + */ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never +/** + * Convert union type to tuple type + * Recursively extracts each member of the union into a tuple + * + * 将联合类型转换为元组类型 + * 递归地将联合类型的每个成员提取到元组中 + * + * @template T - The union type to convert / 要转换的联合类型 + */ export type UnionToTuple = UnionToIntersection< T extends any ? () => T : never