Merge pull request #171 from pengzhanbo/perf-docs
feat(plugin-md-power): add support remote image in `imageSize`
This commit is contained in:
commit
24920486cb
@ -1,12 +1,12 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import fs from 'node:fs'
|
||||
import { execaCommand } from 'execa'
|
||||
import { DeployType, Mode } from './constants.js'
|
||||
import { createPackageJson } from './packageJson.js'
|
||||
import { createRender } from './render.js'
|
||||
import { getTemplate, readFiles, readJsonFile, writeFiles } from './utils/index.js'
|
||||
import type { File, ResolvedData } from './types.js'
|
||||
import { DeployType, Mode } from './constants.js'
|
||||
|
||||
export async function generate(mode: Mode, data: ResolvedData): Promise<void> {
|
||||
const cwd = process.cwd()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import cac from 'cac'
|
||||
import { run } from './run.js'
|
||||
import { Mode } from './constants.js'
|
||||
import { run } from './run.js'
|
||||
|
||||
declare const __CLI_VERSION__: string
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Langs, Locale } from '../types.js'
|
||||
import { en } from './en.js'
|
||||
import { zh } from './zh.js'
|
||||
import type { Langs, Locale } from '../types.js'
|
||||
|
||||
export const locales: Record<Langs, Locale> = {
|
||||
'zh-CN': zh,
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { execaCommand } from 'execa'
|
||||
import { kebabCase } from '@pengzhanbo/utils'
|
||||
import { execaCommand } from 'execa'
|
||||
import { Mode } from './constants.js'
|
||||
import { getDependenciesVersion, readJsonFile, resolve } from './utils/index.js'
|
||||
import type { File, ResolvedData } from './types.js'
|
||||
import { Mode } from './constants.js'
|
||||
|
||||
export async function createPackageJson(
|
||||
mode: Mode,
|
||||
pkg: Record<string, any>,
|
||||
{
|
||||
packageManager,
|
||||
docsDir,
|
||||
siteName,
|
||||
siteDescription,
|
||||
@ -20,11 +21,20 @@ export async function createPackageJson(
|
||||
pkg.type = 'module'
|
||||
pkg.version = '1.0.0'
|
||||
pkg.description = siteDescription
|
||||
|
||||
if (packageManager !== 'npm') {
|
||||
const version = await getPackageManagerVersion(packageManager)
|
||||
if (version) {
|
||||
pkg.packageManager = `${packageManager}@${version}`
|
||||
}
|
||||
}
|
||||
|
||||
const userInfo = await getUserInfo()
|
||||
if (userInfo) {
|
||||
pkg.author = userInfo.username + (userInfo.email ? ` <${userInfo.email}>` : '')
|
||||
}
|
||||
pkg.license = 'MIT'
|
||||
pkg.engines = { node: '^18.20.0 || >=20.0.0' }
|
||||
}
|
||||
|
||||
if (injectNpmScripts) {
|
||||
@ -77,3 +87,13 @@ async function getUserInfo() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function getPackageManagerVersion(pkg: string) {
|
||||
try {
|
||||
const { stdout } = await execaCommand(`${pkg} -v`)
|
||||
return stdout
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import process from 'node:process'
|
||||
import { createRequire } from 'node:module'
|
||||
import process from 'node:process'
|
||||
import { cancel, confirm, group, select, text } from '@clack/prompts'
|
||||
import { bundlerOptions, deployOptions, DeployType, languageOptions, Mode } from './constants.js'
|
||||
import { setLang, t } from './translate.js'
|
||||
import type { Bundler, Langs, Options, PromptResult } from './types.js'
|
||||
import { DeployType, Mode, bundlerOptions, deployOptions, languageOptions } from './constants.js'
|
||||
|
||||
const require = createRequire(process.cwd())
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import handlebars from 'handlebars'
|
||||
import { kebabCase } from '@pengzhanbo/utils'
|
||||
import handlebars from 'handlebars'
|
||||
import type { ResolvedData } from './types.js'
|
||||
|
||||
export interface RenderData extends ResolvedData {
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import process from 'node:process'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { intro, outro, spinner } from '@clack/prompts'
|
||||
import { execaCommand } from 'execa'
|
||||
import colors from 'picocolors'
|
||||
import { prompt } from './prompt.js'
|
||||
import { generate } from './generate.js'
|
||||
import { t } from './translate.js'
|
||||
import { Mode } from './constants.js'
|
||||
import type { PromptResult, ResolvedData } from './types.js'
|
||||
import { generate } from './generate.js'
|
||||
import { prompt } from './prompt.js'
|
||||
import { t } from './translate.js'
|
||||
import { getPackageManager } from './utils/index.js'
|
||||
import type { PromptResult, ResolvedData } from './types.js'
|
||||
|
||||
export async function run(mode: Mode, root?: string) {
|
||||
intro(colors.cyan('Welcome to VuePress and vuepress-theme-plume !'))
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Langs, Locale } from './types.js'
|
||||
import { locales } from './locales/index.js'
|
||||
import type { Langs, Locale } from './types.js'
|
||||
|
||||
function createTranslate(lang?: Langs) {
|
||||
let current: Langs = lang || 'en-US'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import type { File } from '../types.js'
|
||||
|
||||
export async function readFiles(root: string): Promise<File[]> {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
export const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
@ -7,6 +7,6 @@ export const resolve = (...args: string[]) => path.resolve(__dirname, '../', ...
|
||||
|
||||
export const getTemplate = (dir: string) => resolve('templates', dir)
|
||||
|
||||
export * from './fs.js'
|
||||
export * from './depsVersion.js'
|
||||
export * from './fs.js'
|
||||
export * from './getPackageManager.js'
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { type ClientConfig, defineClientConfig } from 'vuepress/client'
|
||||
import HeroTintPlateConfig from './themes/components/HeroTintPlateConfig.vue'
|
||||
import CanIUseConfig from './themes/components/CanIUseConfig.vue'
|
||||
import Demos from './themes/components/Demos.vue'
|
||||
import ThemeColors from './themes/components/ThemeColors.vue'
|
||||
import Contributors from './themes/components/Contributors.vue'
|
||||
import Demos from './themes/components/Demos.vue'
|
||||
import HeroTintPlateConfig from './themes/components/HeroTintPlateConfig.vue'
|
||||
import ThemeColors from './themes/components/ThemeColors.vue'
|
||||
import { setupThemeColors } from './themes/composables/theme-colors.js'
|
||||
|
||||
export default defineClientConfig({
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import * as path from 'node:path'
|
||||
import { type UserConfig, defineUserConfig } from 'vuepress'
|
||||
import { viteBundler } from '@vuepress/bundler-vite'
|
||||
import { addViteOptimizeDepsInclude, addViteSsrExternal } from '@vuepress/helper'
|
||||
import { defineUserConfig, type UserConfig } from 'vuepress'
|
||||
import { peerDependencies } from '../package.json'
|
||||
import { theme } from './theme.js'
|
||||
|
||||
|
||||
@ -16,13 +16,13 @@ export const zhNavbar = [
|
||||
link: '/notes/theme/config/配置说明.md',
|
||||
activeMatch: '^/config/',
|
||||
},
|
||||
{
|
||||
text: '插件',
|
||||
icon: 'clarity:plugin-line',
|
||||
// link: '/plugins/',
|
||||
link: '/notes/plugins/README.md',
|
||||
activeMatch: '^/plugins/',
|
||||
},
|
||||
// {
|
||||
// text: '插件',
|
||||
// icon: 'clarity:plugin-line',
|
||||
// // link: '/plugins/',
|
||||
// link: '/notes/plugins/README.md',
|
||||
// activeMatch: '^/plugins/',
|
||||
// },
|
||||
{
|
||||
text: '博客',
|
||||
link: '/blog/',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { defineNotesConfig } from 'vuepress-theme-plume'
|
||||
import { themeGuide } from './theme-guide'
|
||||
import { themeConfig } from './theme-config'
|
||||
import { plugins } from './plugins'
|
||||
import { themeConfig } from './theme-config'
|
||||
import { themeGuide } from './theme-guide'
|
||||
import { tools } from './tools'
|
||||
|
||||
export const zhNotes = defineNotesConfig({
|
||||
|
||||
@ -38,7 +38,6 @@ export const themeConfig = defineNoteConfig({
|
||||
'阅读统计',
|
||||
'markdown增强',
|
||||
'markdownPower',
|
||||
'百度统计',
|
||||
'水印',
|
||||
],
|
||||
},
|
||||
|
||||
@ -16,6 +16,7 @@ export const themeGuide = defineNoteConfig({
|
||||
'编写文章',
|
||||
'国际化',
|
||||
'部署',
|
||||
'构建优化',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -121,6 +122,7 @@ export const themeGuide = defineNoteConfig({
|
||||
'首页布局容器',
|
||||
'repoCard',
|
||||
'npmBadge',
|
||||
'轮播图',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { defineThemeConfig } from 'vuepress-theme-plume'
|
||||
import { enNotes, zhNotes } from './notes/index.js'
|
||||
import { enNavbar, zhNavbar } from './navbar.js'
|
||||
import { enNotes, zhNotes } from './notes/index.js'
|
||||
|
||||
export default defineThemeConfig({
|
||||
logo: '/plume.png',
|
||||
|
||||
@ -17,6 +17,7 @@ export const theme: Theme = plumeTheme({
|
||||
flowchart: true,
|
||||
},
|
||||
markdownPower: {
|
||||
imageSize: 'all',
|
||||
pdf: true,
|
||||
caniuse: true,
|
||||
plot: true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import '@simonwep/pickr/dist/themes/nano.min.css'
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import '@simonwep/pickr/dist/themes/nano.min.css'
|
||||
|
||||
const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
|
||||
|
||||
|
||||
@ -1,14 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
contributors: string[]
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
contributors: ({ github: string, name: string } | string)[]
|
||||
}>()
|
||||
|
||||
const list = computed(() =>
|
||||
props.contributors.map(contributor =>
|
||||
typeof contributor === 'string' ? { github: contributor, name: contributor } : contributor),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="contributors">
|
||||
<div v-for="contributor in contributors" :key="contributor" class="contributor">
|
||||
<img :src="`https://github.com/${contributor}.png`" :alt="contributor">
|
||||
<a :href="`https://github.com/${contributor}`" target="_blank" rel="noopener noreferrer">{{ contributor }}</a>
|
||||
<div v-for="contributor in list" :key="contributor.github" class="contributor">
|
||||
<img :src="`https://avatars.githubusercontent.com/${contributor.github}?v=4`" :alt="contributor.name">
|
||||
<a :href="`https://github.com/${contributor.github}`" target="_blank" rel="noopener noreferrer">{{ contributor.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PlumeThemeHomeHeroTintPlate } from 'vuepress-theme-plume/client'
|
||||
import { computed, watch } from 'vue'
|
||||
import type { PlumeThemeHomeHeroTintPlate } from 'vuepress-theme-plume/client'
|
||||
import InputRange from './InputRange.vue'
|
||||
|
||||
const min = 20
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import VPHomeHero from 'vuepress-theme-plume/components/Home/VPHomeHero.vue'
|
||||
import { useDarkMode } from 'vuepress-theme-plume/composables'
|
||||
import type { PlumeThemeHomeHeroTintPlate } from 'vuepress-theme-plume/client'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import CodeViewer from './CodeViewer.vue'
|
||||
import CustomTintPlate from './CustomTintPlate.vue'
|
||||
import DemoWrapper from './DemoWrapper.vue'
|
||||
import SingleTintPlate from './SingleTintPlate.vue'
|
||||
import TripletTintPlate from './TripletTintPlate.vue'
|
||||
import CustomTintPlate from './CustomTintPlate.vue'
|
||||
import CodeViewer from './CodeViewer.vue'
|
||||
|
||||
type Mode = 'single' | 'triplet' | 'custom'
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import VPButton from '@theme/VPButton.vue'
|
||||
import { useThemeColors } from '../composables/theme-colors.js'
|
||||
import ColorPick from './ColorPick.vue'
|
||||
import CodeViewer from './CodeViewer.vue'
|
||||
import ColorPick from './ColorPick.vue'
|
||||
|
||||
const { lightColors, darkColors, css, reset } = useThemeColors()
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Ref, computed, onMounted, readonly, ref, watch } from 'vue'
|
||||
import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core'
|
||||
import { computed, onMounted, readonly, type Ref, ref, watch } from 'vue'
|
||||
|
||||
interface Feature {
|
||||
label: string
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type InjectionKey, type Ref, inject, provide, watch } from 'vue'
|
||||
import { useSessionStorage, useStyleTag } from '@vueuse/core'
|
||||
import { inject, type InjectionKey, provide, type Ref, watch } from 'vue'
|
||||
|
||||
export interface ThemeColor {
|
||||
name: string
|
||||
|
||||
@ -169,6 +169,16 @@ export default defineUserConfig({
|
||||
|
||||
### 贡献者
|
||||
|
||||
<Contributors :contributors="['pengzhanbo', 'huankong233', 'northword', 'KrLite', 'shylock-wu', 'hrradev']" />
|
||||
<Contributors
|
||||
:contributors="[
|
||||
'pengzhanbo',
|
||||
{ github: 'huankong233', name: 'huan_kong' },
|
||||
{ github: 'northword', name: 'Northword' },
|
||||
'KrLite',
|
||||
'shylock-wu',
|
||||
'hrradev',
|
||||
{ github: 'TheCoderAlex', name: 'Tang Zifeng' },
|
||||
]"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
@ -49,8 +49,8 @@ pnpm add @vuepress-plume/plugin-caniuse
|
||||
@tab .vuepress/config.ts
|
||||
|
||||
``` ts
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { caniusePlugin } from '@vuepress-plume/plugin-caniuse'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
|
||||
export default defineUserConfig({
|
||||
plugins: [
|
||||
|
||||
@ -56,8 +56,8 @@ yarn add @vuepress-plume/plugin-content-update
|
||||
@tab .vuepress/config.ts
|
||||
|
||||
``` ts
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { contentUpdatePlugin } from '@vuepress-plume/plugin-content-update'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
|
||||
export default defineUserConfig({
|
||||
plugins: [
|
||||
@ -86,8 +86,8 @@ onContentUpdated(() => {
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { onContentUpdated } from '@vuepress-plume/plugin-content-update/client'
|
||||
import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
|
||||
import { onContentUpdated } from '@vuepress-plume/plugin-content-update/client'
|
||||
|
||||
const mediumZoom = useMediumZoom()
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@ permalink: /config/frontmatter/basic/
|
||||
|
||||
主题会在文件创建时:
|
||||
|
||||
- 博客类型的文章,自动填充 `/article/` + `nanoid 生成的 6 位随机字符串` 作为 文章永久链接
|
||||
- 博客类型的文章,自动填充 `/article/` + `nanoid 生成的 8 位随机字符串` 作为 文章永久链接
|
||||
- notes 目录下的文章,会根据 notes 的配置,自动填充 文章永久链接
|
||||
|
||||
### externalLinkIcon
|
||||
|
||||
@ -5,15 +5,15 @@ createTime: 2024/03/06 8:26:44
|
||||
permalink: /config/plugins/
|
||||
---
|
||||
|
||||
主题内置的使用的插件,扩展了主题的众多功能,你可以在这个 字段中, 实现对内部使用的各个插件的自定义配置。
|
||||
主题内置的使用的插件,扩展了主题的众多功能,你可以在 `plugins` 配置中, 实现对内部使用的各个插件的自定义配置。
|
||||
|
||||
## 配置
|
||||
|
||||
所有主题内部使用的插件, 均在 `plugins` 字段中进行配置。
|
||||
|
||||
``` js
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
@ -23,3 +23,7 @@ export default defineUserConfig({
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
:::tip
|
||||
您无需重复安装这些内置插件,也无需在 [vuepress配置 > plugins](https://v2.vuepress.vuejs.org/zh/reference/config.html#plugins) 中添加它们。主题已在内部完成了这些工作。
|
||||
:::
|
||||
|
||||
@ -9,13 +9,15 @@ permalink: /config/plugin/markdown-power/
|
||||
|
||||
提供 Markdown 增强功能。
|
||||
|
||||
关联插件: [@vuepress-plume/plugin-md-power](/)
|
||||
关联插件: [@vuepress-plume/plugin-md-power](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-md-power)
|
||||
|
||||
## 配置
|
||||
|
||||
默认配置:
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
@ -32,12 +34,216 @@ export default defineUserConfig({
|
||||
// jsfiddle: true, // @[jsfiddle](id) 嵌入 jsfiddle
|
||||
// caniuse: true, // @[caniuse](feature) 嵌入 caniuse
|
||||
// repl: true, // :::go-repl :::kotlin-repl :::rust-repl
|
||||
// plot: true, // !!plot!! 隐秘文本
|
||||
// fileTree: true, // :::file-tree 文件树容器
|
||||
|
||||
// imageSize: true, // 在构建阶段为 图片添加 width/height 属性
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
## 配置
|
||||
## 功能
|
||||
|
||||
查看 [文档](../../../plugins/md-power.md)
|
||||
### 嵌入 PDF
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `pdf` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[pdf](url)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/嵌入/pdf.md)
|
||||
|
||||
### iconify 图标
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `icons` 为 `true`
|
||||
|
||||
得益于 [iconify](https://iconify.design/), 你可以在 Markdown 中使用 iconify __200k+__ 的图标
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
:[collect:name]:
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/markdown/进阶.md#iconify-图标)
|
||||
|
||||
### bilibili 视频
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `bilibili` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[bilibili](bvid)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/嵌入/bilibili.md)
|
||||
|
||||
### youtube 视频
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `youtube` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[youtube](id)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/嵌入/youtube.md)
|
||||
|
||||
### codePen 代码演示
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `codepen` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[codepen](user/slash)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/代码演示/codepen.md)
|
||||
|
||||
### codeSandbox 代码演示
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `codeSandbox` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[codesandbox](id)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/代码演示/codeSandbox.md)
|
||||
|
||||
### jsfiddle 代码演示
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `jsfiddle` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[jsfiddle](id)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/代码演示/jsFiddle.md)
|
||||
|
||||
### caniuse 浏览器支持
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `caniuse` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
@[caniuse](feature)
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/markdown/进阶.md#can-i-use)
|
||||
|
||||
### Repl 代码演示容器
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `repl` 为 `true`
|
||||
|
||||
支持在线运行 Rust、Golang、Kotlin 代码,还支持在线编辑。
|
||||
|
||||
或者开启部分功能,如下所示
|
||||
|
||||
``` ts
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
plugins: {
|
||||
markdownPower: {
|
||||
repl: {
|
||||
rust: true,
|
||||
go: true,
|
||||
kotlin: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
__语法:__
|
||||
|
||||
````md
|
||||
::: rust-repl
|
||||
```rust
|
||||
// rust code
|
||||
```
|
||||
:::
|
||||
|
||||
::: go-repl
|
||||
```go
|
||||
// go code
|
||||
```
|
||||
:::
|
||||
|
||||
::: kotlin-repl
|
||||
```kotlin
|
||||
// kotlin code
|
||||
```
|
||||
:::
|
||||
````
|
||||
|
||||
请查看完整使用文档:
|
||||
|
||||
- [代码演示 > Rust](../../guide/代码演示/rust.md)
|
||||
- [代码演示 > Golang](../../guide/代码演示/golang.md)
|
||||
- [代码演示 > Kotlin](../../guide/代码演示/kotlin.md)
|
||||
|
||||
### Plot 隐秘文本
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `plot` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
!!content!!
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/markdown/进阶.md#隐秘文本)
|
||||
|
||||
### 文件树
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `fileTree` 为 `true`
|
||||
|
||||
__语法:__
|
||||
|
||||
```md
|
||||
::: file-tree
|
||||
|
||||
- folder1
|
||||
- file1.md
|
||||
- file2.ts
|
||||
- folder2
|
||||
- file3.md
|
||||
- folder3
|
||||
|
||||
:::
|
||||
```
|
||||
|
||||
请查看 [完整使用文档](../../guide/markdown/进阶.md#文件树)
|
||||
|
||||
### 图片尺寸
|
||||
|
||||
该功能会为 markdown 文件中的 图片引用 添加当前图片的 `width` 和 `height` 属性。
|
||||
通过读取 图片的原始尺寸大小,为 图片设置默认的 图片尺寸 和 比例。
|
||||
从而解决页面在图片加载未完成到完成时,布局闪烁的问题。
|
||||
|
||||
插件默认不启用该功能,你需要手动设置 `imageSize`:
|
||||
|
||||
- 如果 `imageSize` 为 `true`,则插件仅处理本地图片,等同于 `local` 选项;
|
||||
- 如果 `imageSize` 为 `'local'`,则插件仅处理本地图片;
|
||||
- 如果 `imageSize` 为 `'all'`,则插件同时处理本地图片和远程图片。
|
||||
|
||||
::: important
|
||||
__此功能仅在构建生产包时生效。__
|
||||
|
||||
请谨慎 使用 `'all'` 选项,由于该选项会在 构建生产包时,请求远程图片资源,这会使得构建时间变长。
|
||||
虽然主题做了优化仅会加载图片 __几 KB__ 的数据包 用于分析尺寸,但还是会实际影响构建时间。
|
||||
:::
|
||||
|
||||
@ -14,8 +14,8 @@ permalink: /config/plugins/markdown-enhance/
|
||||
默认配置:
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
|
||||
@ -16,9 +16,16 @@ Shiki 支持多种编程语言。
|
||||
|
||||
关联插件: [@vuepress-plume/plugin-shikiji](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-shikiji)
|
||||
|
||||
相比于 官方的 [@vuepress/plugin-prismjs](https://ecosystem.vuejs.press/zh/plugins/prismjs.html) 和
|
||||
[@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/shiki.html) 两个代码高亮插件,
|
||||
提供了更为丰富的功能支持,包括:
|
||||
::: details 为什么不用 官方的 @vuepress/plugin-shiki ?
|
||||
|
||||
你可以认为本插件是 官方 [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/shiki.html) 的
|
||||
一个分支版本,但本插件更为激进,支持更多新的特性。
|
||||
|
||||
同时,我也是 [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/shiki.html) 的主要维护者之一
|
||||
,在 `@vuepress-plume/plugin-shikiji` 插件中新增的试验性的新特性,会在未来合适的时候合并到 官方插件中。
|
||||
:::
|
||||
|
||||
## 特性
|
||||
|
||||
- [代码行高亮](../../guide/代码/特性支持.md#在代码块中实现行高亮)
|
||||
- [代码聚焦](../../guide/代码/特性支持.md#代码块中聚焦)
|
||||
@ -26,13 +33,15 @@ Shiki 支持多种编程语言。
|
||||
- [代码高亮“错误”和“警告”](../../guide/代码/特性支持.md#高亮-错误-和-警告)
|
||||
- [代码词高亮](../../guide/代码/特性支持.md#代码块中-词高亮)
|
||||
- [代码块折叠](../../guide/代码/特性支持.md#折叠代码块)
|
||||
- [twoslash](../../guide/代码/twoslash.md#twoslash) ,在代码块内提供内联类型提示。
|
||||
- [twoslash](../../guide/代码/twoslash.md#twoslash) :在代码块内提供内联类型提示。
|
||||
|
||||
## 配置
|
||||
|
||||
默认配置:
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
@ -45,8 +54,6 @@ export default defineUserConfig({
|
||||
})
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
### theme
|
||||
|
||||
- 类型: `string | { light: string, dark: string }`
|
||||
@ -123,14 +130,7 @@ interface CopyCodeOptions {
|
||||
}
|
||||
```
|
||||
|
||||
### codeTransformers
|
||||
|
||||
- 类型: `ShikiTransformer[]`
|
||||
- 默认值: `[]`
|
||||
|
||||
代码转换器, 查看 [shiki transformers](https://shiki.style/guide/transformers) 了解更多信息。
|
||||
|
||||
### twoslash <Badge type="tip" text="实验性" />
|
||||
### twoslash
|
||||
|
||||
- 类型: `boolean`
|
||||
- 默认值: `false`
|
||||
@ -156,3 +156,10 @@ interface CopyCodeOptions {
|
||||
- 默认值: `false`
|
||||
|
||||
将代码块折叠到指定行数。
|
||||
|
||||
### codeTransformers
|
||||
|
||||
- 类型: `ShikiTransformer[]`
|
||||
- 默认值: `[]`
|
||||
|
||||
代码转换器, 查看 [shiki transformers](https://shiki.style/guide/transformers) 了解更多信息。
|
||||
|
||||
@ -14,8 +14,8 @@ permalink: /config/plugins/reading-time/
|
||||
默认配置:
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
|
||||
@ -30,8 +30,8 @@ VuePress 站点的基本配置文件是 `.vuepress/config.js` ,但也同样支
|
||||
|
||||
```ts
|
||||
import { viteBundler } from '@vuepress/bundler-vite'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
bundler: viteBundler(),
|
||||
@ -61,8 +61,8 @@ export default defineUserConfig({
|
||||
一般我们使用 `.vuepress/config.js` 或者 `.vuepress/config.ts` 来配置主题。
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
@ -122,8 +122,8 @@ export default defineThemeConfig({
|
||||
|
||||
```ts
|
||||
import path from 'node:path'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
|
||||
@ -33,8 +33,8 @@ import { Layout, NotFound } from 'vuepress-theme-plume/client'
|
||||
更多其他组件请查看 [源代码](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/theme/src/client/components)
|
||||
|
||||
```ts
|
||||
import VPLink from 'vuepress-theme-plume/components/VPLink.vue'
|
||||
import VPButton from 'vuepress-theme-plume/components/VPButton.vue'
|
||||
import VPLink from 'vuepress-theme-plume/components/VPLink.vue'
|
||||
```
|
||||
|
||||
## 组合式 API
|
||||
|
||||
@ -189,8 +189,8 @@ config:
|
||||
(还可以在重新修改 分类页/标签页/归档页的链接地址)
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
|
||||
@ -54,8 +54,8 @@ export default defineUserConfig({
|
||||
`locales` 支持 所有主题配置项。
|
||||
|
||||
```js
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from '@vuepress-plume/vuepress-theme-plume'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
|
||||
export default defineUserConfig({
|
||||
lang: 'en-US',
|
||||
|
||||
@ -196,8 +196,8 @@ cd open-source # 进入 D: 分区下的 open-source 目录
|
||||
@tab docs/.vuepress/config.js
|
||||
|
||||
``` ts :no-line-numbers
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { viteBundler } from '@vuepress/bundler-vite'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
|
||||
@ -53,8 +53,8 @@ import { Layout } from 'vuepress-theme-plume/client'
|
||||
也可以使用 渲染函数 实现注入内容,在 `.vuepress/client.ts` 中:
|
||||
|
||||
```ts
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import { h } from 'vue'
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import { Layout } from 'vuepress-theme-plume/client'
|
||||
import CustomContent from './components/CustomContent.vue'
|
||||
|
||||
|
||||
75
docs/notes/theme/guide/构建优化.md
Normal file
75
docs/notes/theme/guide/构建优化.md
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
title: 构建优化
|
||||
icon: clarity:bundle-solid
|
||||
createTime: 2024/09/10 01:50:20
|
||||
permalink: /guide/optimize-build/
|
||||
---
|
||||
|
||||
## 图片优化 <Badge type="warning" text="试验性" />
|
||||
|
||||
当我们在 markdown 中使用 `[alt](url)` 或者 `<img src="url">` 嵌入图片后,虽然页面按照预期的显示了
|
||||
图片。
|
||||
|
||||
由于图片的体积不同,当图片体积较小,网络情况良好的时候,我们不会感受到页面的布局有产生明显的抖动。
|
||||
然而,当图片体积较大,或者网络情况较差时,由于图片为完成加载,原本页面上应该显示图片的位置被后面的
|
||||
内容挤压,等到图片加载完成后,页面布局会发生变化,图片重新占据应该显示的位置,其它的内容被排开。
|
||||
|
||||
事实上这个体验相当不友好。特别是对于页面内的图片数量较多时,页面会频繁发生布局变化,这一过程还可能
|
||||
感知到卡顿,较为明显的是布局的抖动。
|
||||
|
||||
因此,让页面布局稳定下来,图片是一个必须解决的问题。
|
||||
|
||||
从 [MDN > img](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/img#height) 我们可以知道:
|
||||
|
||||
::: info
|
||||
`<img>` 同时包括 `height` 和 `width` 使浏览器在加载图像之前计算图像的长宽比。
|
||||
此长宽比用于保留显示图像所需的空间,减少甚至防止在下载图像并将其绘制到屏幕上时布局的偏移。
|
||||
减少布局偏移是良好用户体验和 Web 性能的主要组成部分。
|
||||
:::
|
||||
|
||||
因此,主题围绕这个问题,提供了 一个解决方案:
|
||||
|
||||
为 markdown 文件中的 `[alt](url)` 或者 `<img src="url">` 自动添加 `width` 和 `height` 属性。
|
||||
|
||||
你可以通过配置 `markdownPower` 来启用它:
|
||||
|
||||
```ts
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
plugins: {
|
||||
markdownPower: {
|
||||
imageSize: true, // 'local' | 'all'
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
启用此功能后,主题会通过图片资源地址,获取图片的原始尺寸,然后为图片添加 `width` 和 `height` 属性。
|
||||
|
||||
- 如果设置为 `'local'`, 则仅为 本地图片添加 `width` 和 `height` 属性。
|
||||
- 如果设置为 `'all'`, 则包括 __本地图片__ 和 __网络图片__ 都 添加 `width` 和 `height` 属性。
|
||||
- 如果设置为 `true`, 则等同于 `'local'`
|
||||
|
||||
::: important
|
||||
请注意,出于性能考虑,即使您启用了此功能,该功能也仅在 构建生产包时生效。
|
||||
:::
|
||||
|
||||
::: important
|
||||
请谨慎使用 `'all'` 选项,该选项会在构建生产包时,请求所有 markdown 中包含的 远程图片资源,
|
||||
这对于包含较多图片资源的站点而言,会使得构建时间变长。
|
||||
|
||||
主题也针对此类情况做了优化,请求远程图片仅在获取 __几 KB__ 的数据包足够分析尺寸后不再等待请求完成,
|
||||
同时并发请求其他图片资源。这在一定程度上能够改善构建时间。
|
||||
:::
|
||||
|
||||
::: details 还有其他方案吗?
|
||||
事实上有的,当前的方案其实是一个折中的方案。
|
||||
|
||||
我考虑过使用 [thumbhash](https://github.com/evanw/thumbhash) 为图片生成缩略图,通过 占位图 和 懒加载
|
||||
等技术方案实现更为平滑优雅的图片加载体验。
|
||||
|
||||
然而这是有代价的,这需要使用的 [sharp](https://github.com/lovell/sharp) 或 [canvaskit](https://github.com/google/skia/tree/main/modules/canvaskit) 等图片处理库,对图片进行处理分析,
|
||||
再通过 [thumbhash](https://github.com/evanw/thumbhash) 生成大概 `20byte` 大小的缩略图。这需要花费更多的时间,
|
||||
这可能对于用户而言是不可接受的。
|
||||
:::
|
||||
@ -171,6 +171,12 @@ interface SidebarItem {
|
||||
}
|
||||
```
|
||||
|
||||
::: info
|
||||
以下代码块中 `sidebar` 表示传入到 `notes` 中的 `sidebar` 参数。
|
||||
|
||||
这里为了方便演示说明,将其单独使用 `const sidebar: Sidebar = [...]` 进行说明。
|
||||
:::
|
||||
|
||||
当 传入类型为 `string` 时,表示 markdown 文件的路径:
|
||||
|
||||
```ts
|
||||
|
||||
308
docs/notes/theme/guide/组件/轮播图.md
Normal file
308
docs/notes/theme/guide/组件/轮播图.md
Normal file
@ -0,0 +1,308 @@
|
||||
---
|
||||
title: 轮播图
|
||||
icon: dashicons:images-alt2
|
||||
createTime: 2024/09/12 22:00:22
|
||||
permalink: /guide/components/swiper/
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
使用 `<Swiper>` 组件在 页面中显示轮播图片。
|
||||
|
||||
## 使用
|
||||
|
||||
使用该组件,首先需要手动安装 `swiper` 库:
|
||||
|
||||
::: code-tabs
|
||||
@tab pnpm
|
||||
|
||||
```sh
|
||||
pnpm add swiper
|
||||
```
|
||||
|
||||
@tab yarn
|
||||
|
||||
```sh
|
||||
yarn add swiper
|
||||
```
|
||||
|
||||
@tab npm
|
||||
|
||||
```sh
|
||||
npm install swiper
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
然后,手动导入 `Swiper` 组件:
|
||||
|
||||
```md :no-line-numbers
|
||||
<!-- 在 markdown 中导入 -->
|
||||
<script setup>
|
||||
import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
|
||||
</script>
|
||||
|
||||
<!-- 导入后,即可在 markdown 中使用 -->
|
||||
<Swiper :items="['img_link1', 'img_link2']" />
|
||||
```
|
||||
|
||||
注册为全局组件:
|
||||
|
||||
::: code-tabs
|
||||
@tab .vuepress/client.ts
|
||||
|
||||
```ts
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
|
||||
|
||||
export default defineClientConfig({
|
||||
enhance({ app }) {
|
||||
app.component('Swiper', Swiper)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
全局组件可在 其他任意 markdown 文件中使用
|
||||
|
||||
```md
|
||||
<Swiper :items="['img_link1', 'img_link2']" />
|
||||
```
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
|
||||
|
||||
const images = ref([])
|
||||
|
||||
async function fetchImage() {
|
||||
const res = await fetch('https://api.pengzhanbo.cn/wallpaper/bing/list/zh/').then((res) => res.json())
|
||||
images.value = res.map(item => ({
|
||||
name: item.title,
|
||||
link: item.url,
|
||||
}))
|
||||
}
|
||||
|
||||
if (!__VUEPRESS_SSR__) {
|
||||
fetchImage()
|
||||
}
|
||||
</script>
|
||||
|
||||
**示例:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" />
|
||||
|
||||
## Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| ----------------- | ---------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------- |
|
||||
| items | `string \| { link: string; href?: string; alt?: string}[]` | `[]` | 图片链接数组,传入对象时,`link`表示图片链接,`href`表示跳转链接,`alt`表示图片描述 |
|
||||
| width | `number \| string` | `100%` | 轮播区域宽度 |
|
||||
| height | `number \| string` | `100%` | 轮播区域高度 |
|
||||
| mode | `'banner' \| 'carousel' \| 'broadcast'` | `'banner'` | 轮播模式, `banner`: 轮播图; `carousel`: 走马灯; `broadcast`: 信息展播 |
|
||||
| navigation | `boolean` | `true` | 是否显示导航按钮 |
|
||||
| effect | `'slide' \| 'fade' \| 'cube' \| 'coverflow' \| 'flip' \| 'cards' \| 'creative'` | `'slide'` | 轮播效果 |
|
||||
| delay | `number` | `3000` | 轮播间隔时间, 仅当 `mode: 'banner'` 时生效,单位 `ms` |
|
||||
| speed | `number` | `300` | 动画持续时间,单位 `ms` |
|
||||
| loop | `boolean` | `true` | 是否循环 |
|
||||
| pauseOnMouseEnter | `boolean` | `false` | 是否鼠标悬停时暂停轮播 |
|
||||
| swipe | `boolean` | `true` | 是否开启手势滑动 |
|
||||
|
||||
更多 props 请参考 [Swiper 文档](https://swiperjs.com/swiper-api#parameters)
|
||||
|
||||
## 参考示例
|
||||
|
||||
### 预设动画效果
|
||||
|
||||
**cube:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="cube" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="cube" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**fade:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="fade" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="fade" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**coverflow:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="coverflow" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="coverflow" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**flip:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="flip" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="flip" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**cards:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="cards" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="cards" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 自定义动画效果
|
||||
|
||||
**示例 1:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, translate: [0, 0, -400] },
|
||||
next: { translate: ['100%', 0, 0] },
|
||||
}" />
|
||||
|
||||
::: details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, translate: [0, 0, -400] },
|
||||
next: { translate: ['100%', 0, 0] },
|
||||
}"
|
||||
/>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**示例 2:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, translate: [0, 0, -800], rotate: [180, 0, 0] },
|
||||
next: { shadow: true, translate: [0, 0, -800], rotate: [-180, 0, 0] },
|
||||
}" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, translate: [0, 0, -800], rotate: [180, 0, 0] },
|
||||
next: { shadow: true, translate: [0, 0, -800], rotate: [-180, 0, 0] },
|
||||
}"
|
||||
/>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**示例 3:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, translate: ['-125%', 0, -800], rotate: [0, 0, -90] },
|
||||
next: { shadow: true, translate: ['125%', 0, -800], rotate: [0, 0, 90] },
|
||||
}" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, translate: ['-125%', 0, -800], rotate: [0, 0, -90] },
|
||||
next: { shadow: true, translate: ['125%', 0, -800], rotate: [0, 0, 90] },
|
||||
}"
|
||||
/>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**示例 4:**
|
||||
|
||||
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, origin: 'left center', translate: ['-5%', 0, -200], rotate: [0, 100, 0] },
|
||||
next: { origin: 'right center', translate: ['5%', 0, -200], rotate: [0, -100, 0] },
|
||||
}" />
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper :items="images" effect="creative" :creativeEffect="{
|
||||
prev: { shadow: true, origin: 'left center', translate: ['-5%', 0, -200], rotate: [0, 100, 0] },
|
||||
next: { origin: 'right center', translate: ['5%', 0, -200], rotate: [0, -100, 0] },
|
||||
}"
|
||||
/>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 走马灯
|
||||
|
||||
<Swiper
|
||||
v-if="images.length"
|
||||
:items="images"
|
||||
mode="carousel"
|
||||
:height="200"
|
||||
:slides-per-view="3"
|
||||
:space-between="20"
|
||||
:speed="5500"
|
||||
/>
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper
|
||||
:items="images"
|
||||
mode="carousel"
|
||||
:height="200"
|
||||
:slides-per-view="3"
|
||||
:space-between="20"
|
||||
:speed="5500"
|
||||
/>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 信息展播
|
||||
|
||||
<Swiper
|
||||
v-if="images.length"
|
||||
:items="images"
|
||||
mode="broadcast"
|
||||
:height="200"
|
||||
:slides-per-view="3"
|
||||
:space-between="20"
|
||||
mousewheel
|
||||
/>
|
||||
|
||||
:::details 查看代码
|
||||
|
||||
```md
|
||||
<Swiper
|
||||
:items="images"
|
||||
mode="broadcast"
|
||||
:height="200"
|
||||
:slides-per-view="3"
|
||||
:space-between="20"
|
||||
mousewheel
|
||||
/>
|
||||
```
|
||||
|
||||
:::
|
||||
@ -24,9 +24,9 @@ permalink: /guide/component-overrides/
|
||||
如果你想要覆写 `VPFooter.vue` 组件,只需要在配置文件 `.vuepress/config.ts` 中覆盖这个别名即可:
|
||||
|
||||
```ts
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
const __dirname = getDirname(import.meta.url)
|
||||
|
||||
|
||||
@ -21,10 +21,10 @@ permalink: /guide/custom-style/
|
||||
@tab .vuepress/client.ts
|
||||
|
||||
```ts {1}
|
||||
import './styles/index.css'
|
||||
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
|
||||
import './styles/index.css'
|
||||
|
||||
export default defineClientConfig({
|
||||
// ...
|
||||
})
|
||||
|
||||
@ -49,8 +49,8 @@ cupiditate sequi.
|
||||
|
||||
@tab TS
|
||||
```ts
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { include } from '@mdit/plugin-include'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
|
||||
// #region snippet
|
||||
const mdIt = MarkdownIt().use(include, {
|
||||
@ -66,8 +66,8 @@ mdIt.render('<!-- @include: ./path/to/include/file.md -->', {
|
||||
|
||||
@tab JS
|
||||
```js
|
||||
const MarkdownIt = require('markdown-it')
|
||||
const { include } = require('@mdit/plugin-include')
|
||||
const MarkdownIt = require('markdown-it')
|
||||
|
||||
// #region snippet
|
||||
const mdIt = MarkdownIt().use(include, {
|
||||
|
||||
@ -12,15 +12,16 @@
|
||||
"vuepress": "2.0.0-rc.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/json": "^2.2.245",
|
||||
"@iconify/json": "^2.2.247",
|
||||
"@simonwep/pickr": "^1.9.1",
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.15",
|
||||
"chart.js": "^4.4.4",
|
||||
"echarts": "^5.5.1",
|
||||
"flowchart.ts": "^3.0.1",
|
||||
"http-server": "^14.1.1",
|
||||
"mermaid": "^11.1.1",
|
||||
"vue": "^3.5.3",
|
||||
"mermaid": "^11.2.0",
|
||||
"swiper": "^11.1.12",
|
||||
"vue": "^3.5.4",
|
||||
"vuepress-theme-plume": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -31,5 +31,3 @@ readingTime: false
|
||||
| **锋 | 2024-04-18 | 6.88 | 支持下作者,加油! |
|
||||
| *杰 | 2024-05-25 | 6.00 | 请你喝杯茶,加油 |
|
||||
| **党 | 2024-08-22 | 8.80 | 感谢开源,加油 |
|
||||
|
||||
1
|
||||
|
||||
18
package.json
18
package.json
@ -3,7 +3,7 @@
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.97",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.9.0",
|
||||
"packageManager": "pnpm@9.10.0",
|
||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -38,10 +38,10 @@
|
||||
"release:version": "bumpp package.json plugins/*/package.json theme/package.json cli/package.json --execute=\"pnpm release:changelog\" --commit \"build: publish v%s\" --all --tag --push"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.4.1",
|
||||
"@commitlint/config-conventional": "^19.4.1",
|
||||
"@pengzhanbo/eslint-config-vue": "^1.15.0",
|
||||
"@pengzhanbo/stylelint-config": "^1.15.0",
|
||||
"@commitlint/cli": "^19.5.0",
|
||||
"@commitlint/config-conventional": "^19.5.0",
|
||||
"@pengzhanbo/eslint-config-vue": "^1.16.0",
|
||||
"@pengzhanbo/stylelint-config": "^1.16.0",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "20.12.10",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
@ -51,14 +51,14 @@
|
||||
"cpx2": "^7.0.1",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^9.10.0",
|
||||
"husky": "^9.1.5",
|
||||
"husky": "^9.1.6",
|
||||
"lint-staged": "^15.2.10",
|
||||
"rimraf": "^6.0.1",
|
||||
"stylelint": "^16.9.0",
|
||||
"tsconfig-vuepress": "^5.0.0",
|
||||
"tsconfig-vuepress": "^5.2.0",
|
||||
"tsup": "^8.2.4",
|
||||
"typescript": "^5.5.4",
|
||||
"wait-on": "^8.0.0"
|
||||
"typescript": "^5.6.2",
|
||||
"wait-on": "^8.0.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "eslint --fix",
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
"vuepress": "2.0.0-rc.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.3"
|
||||
"vue": "^3.5.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
|
||||
const __dirname = getDirname(import.meta.url)
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Options, defineConfig } from 'tsup'
|
||||
import { defineConfig, type Options } from 'tsup'
|
||||
|
||||
const clientExternal: (string | RegExp)[] = [
|
||||
/.*\.vue$/,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
|
||||
export function fontsPlugin(): Plugin {
|
||||
return {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Options, defineConfig } from 'tsup'
|
||||
import { defineConfig, type Options } from 'tsup'
|
||||
|
||||
const clientExternal: (string | RegExp)[] = [
|
||||
/.*\.vue$/,
|
||||
|
||||
@ -45,10 +45,10 @@
|
||||
"image-size": "^1.1.1",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.16.2",
|
||||
"shiki": "^1.17.0",
|
||||
"tm-grammars": "^1.17.18",
|
||||
"tm-themes": "^1.8.1",
|
||||
"vue": "^3.5.3"
|
||||
"vue": "^3.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/markdown-it": "^14.1.2"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
|
||||
interface MessageData {
|
||||
type: string
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { getHighlighterCore } from 'shiki/core'
|
||||
import type { HighlighterCore } from 'shiki/core'
|
||||
import editorData from '@internal/md-power/replEditorData'
|
||||
import { getHighlighterCore } from 'shiki/core'
|
||||
import { onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
||||
import type { HighlighterCore } from 'shiki/core'
|
||||
import { resolveCodeInfo } from '../composables/codeRepl.js'
|
||||
|
||||
let highlighter: HighlighterCore | null = null
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, shallowRef } from 'vue'
|
||||
import { useCodeRepl } from '../composables/codeRepl.js'
|
||||
import IconClose from './IconClose.vue'
|
||||
import IconConsole from './IconConsole.vue'
|
||||
import IconRun from './IconRun.vue'
|
||||
import Loading from './Loading.vue'
|
||||
import IconConsole from './IconConsole.vue'
|
||||
import IconClose from './IconClose.vue'
|
||||
|
||||
defineProps<{
|
||||
editable?: boolean
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
type: 'file' | 'folder'
|
||||
@ -10,13 +10,22 @@ const props = defineProps<{
|
||||
const active = ref(!!props.expanded)
|
||||
const el = ref<HTMLElement>()
|
||||
|
||||
function toggle() {
|
||||
active.value = !active.value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!el.value || props.type !== 'folder')
|
||||
return
|
||||
|
||||
el.value.querySelector('.tree-node.folder')?.addEventListener('click', () => {
|
||||
active.value = !active.value
|
||||
})
|
||||
el.value.querySelector('.tree-node.folder')?.addEventListener('click', toggle)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (!el.value || props.type !== 'folder')
|
||||
return
|
||||
|
||||
el.value.querySelector('.tree-node.folder')?.removeEventListener('click', toggle)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -45,6 +54,15 @@ onMounted(() => {
|
||||
transition: border var(--t-color), background-color var(--t-color);
|
||||
}
|
||||
|
||||
.vp-file-tree .vp-file-tree-title {
|
||||
padding-left: 16px;
|
||||
margin: -16px -16px 0;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-1);
|
||||
border-bottom: solid 1px var(--vp-c-divider);
|
||||
transition: color var(--t-color), border-color var(--t-color);
|
||||
}
|
||||
|
||||
.vp-file-tree ul {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, toRefs } from 'vue'
|
||||
import type { PDFTokenMeta } from '../../shared/index.js'
|
||||
import { useSize } from '../composables/size.js'
|
||||
import { usePDF } from '../composables/pdf.js'
|
||||
import { useSize } from '../composables/size.js'
|
||||
import type { PDFTokenMeta } from '../../shared/index.js'
|
||||
|
||||
const props = defineProps<PDFTokenMeta>()
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, shallowRef } from 'vue'
|
||||
import { onClickOutside, useMediaQuery } from '@vueuse/core'
|
||||
import { computed, ref, shallowRef } from 'vue'
|
||||
import { usePageFrontmatter } from 'vuepress/client'
|
||||
import type { PlotOptions } from '../../shared/index.js'
|
||||
import { pluginOptions } from '../options.js'
|
||||
import type { PlotOptions } from '../../shared/index.js'
|
||||
|
||||
const props = defineProps<Omit<PlotOptions, 'tag'>>()
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import type { ReplitTokenMeta } from '../../shared/index.js'
|
||||
import Loading from './Loading.vue'
|
||||
import type { ReplitTokenMeta } from '../../shared/index.js'
|
||||
|
||||
const props = defineProps<ReplitTokenMeta>()
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Ref, onMounted, ref } from 'vue'
|
||||
import { onMounted, type Ref, ref } from 'vue'
|
||||
import { http } from '../utils/http.js'
|
||||
import { sleep } from '../utils/sleep.js'
|
||||
import { rustExecute } from './rustRepl.js'
|
||||
|
||||
@ -11,11 +11,11 @@
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { ensureEndingSlash, isLinkHttp } from 'vuepress/shared'
|
||||
import { withBase } from 'vuepress/client'
|
||||
import type { PDFEmbedType, PDFTokenMeta } from '../../shared/index.js'
|
||||
import { ensureEndingSlash, isLinkHttp } from 'vuepress/shared'
|
||||
import { pluginOptions } from '../options.js'
|
||||
import { checkIsMobile, checkIsSafari, checkIsiPad } from '../utils/is.js'
|
||||
import { checkIsiPad, checkIsMobile, checkIsSafari } from '../utils/is.js'
|
||||
import type { PDFEmbedType, PDFTokenMeta } from '../../shared/index.js'
|
||||
|
||||
function queryStringify(options: PDFTokenMeta): string {
|
||||
const { page, noToolbar, zoom } = options
|
||||
@ -105,7 +105,7 @@ export function usePDF(
|
||||
// We're moving into the age of MIME-less browsers. They mostly all support PDF rendering without plugins.
|
||||
&& (isModernBrowser
|
||||
// Modern versions of Firefox come bundled with PDFJS
|
||||
|| isFirefoxWithPDFJS)
|
||||
|| isFirefoxWithPDFJS)
|
||||
|
||||
if (!url)
|
||||
return
|
||||
|
||||
@ -11,10 +11,10 @@
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { MaybeRef } from '@vueuse/core'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import type { Ref, ShallowRef, ToRefs } from 'vue'
|
||||
import { computed, isRef, onMounted, ref, shallowRef, toValue, watch } from 'vue'
|
||||
import type { MaybeRef } from '@vueuse/core'
|
||||
import type { Ref, ShallowRef, ToRefs } from 'vue'
|
||||
import type { SizeOptions } from '../../shared/index.js'
|
||||
|
||||
export interface SizeInfo<T extends HTMLElement> {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { isLinkHttp } from 'vuepress/shared'
|
||||
import { withBase } from 'vuepress/client'
|
||||
import { isLinkHttp } from 'vuepress/shared'
|
||||
|
||||
export function normalizeLink(url: string): string {
|
||||
return isLinkHttp(url) ? url : withBase(url)
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
* @[caniuse embed{1,2,3,4}](feature_name)
|
||||
* @[caniuse image](feature_name)
|
||||
*/
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import type Token from 'markdown-it/lib/token.mjs'
|
||||
import type MarkdownIt from 'markdown-it'
|
||||
import container from 'markdown-it-container'
|
||||
import { customAlphabet } from 'nanoid'
|
||||
import type { CanIUseMode, CanIUseOptions, CanIUseTokenMeta } from '../../shared/index.js'
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import type MarkdownIt from 'markdown-it'
|
||||
import type Token from 'markdown-it/lib/token.mjs'
|
||||
import { createRuleBlock } from '../utils/createRuleBlock.js'
|
||||
import type { CanIUseMode, CanIUseOptions, CanIUseTokenMeta } from '../../shared/index.js'
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5)
|
||||
const UNDERLINE_RE = /_+/g
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
* @[codesanbox title="xxx" layout="Editor+Preview" height="500px" navbar="false" console="false"](id#filepath)
|
||||
*/
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import type { CodeSandboxTokenMeta } from '../../shared/index.js'
|
||||
import { createRuleBlock } from '../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import type { CodeSandboxTokenMeta } from '../../shared/index.js'
|
||||
|
||||
export const codeSandboxPlugin: PluginWithOptions<never> = (md) => {
|
||||
createRuleBlock<CodeSandboxTokenMeta>(md, {
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
* @[codepen preview editable title="" height="400px" tab="css,result" theme="dark"](user/slash)
|
||||
*/
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import type { CodepenTokenMeta } from '../../shared/index.js'
|
||||
import { createRuleBlock } from '../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import type { CodepenTokenMeta } from '../../shared/index.js'
|
||||
|
||||
const CODEPEN_LINK = 'https://codepen.io/'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { FileIcons, definitions } from './icons.js'
|
||||
import { definitions, FileIcons } from './icons.js'
|
||||
|
||||
export interface FileIcon {
|
||||
name: string
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import fs from 'node:fs'
|
||||
import container from 'markdown-it-container'
|
||||
import type { Markdown } from 'vuepress/markdown'
|
||||
import type Token from 'markdown-it/lib/token.mjs'
|
||||
import type { App } from 'vuepress/core'
|
||||
import { resolveTreeNodeInfo, updateInlineToken } from './resolveTreeNodeInfo.js'
|
||||
import type { Markdown } from 'vuepress/markdown'
|
||||
import { type FileIcon, folderIcon, getFileIcon } from './findIcon.js'
|
||||
import { resolveTreeNodeInfo, updateInlineToken } from './resolveTreeNodeInfo.js'
|
||||
|
||||
const type = 'file-tree'
|
||||
const closeType = `container_${type}_close`
|
||||
@ -21,7 +21,7 @@ export async function fileTreePlugin(app: App, md: Markdown) {
|
||||
for (
|
||||
let i = idx + 1;
|
||||
!(tokens[i].nesting === -1
|
||||
&& tokens[i].type === closeType);
|
||||
&& tokens[i].type === closeType);
|
||||
++i
|
||||
) {
|
||||
const token = tokens[i]
|
||||
@ -46,7 +46,10 @@ export async function fileTreePlugin(app: App, md: Markdown) {
|
||||
token.tag = componentName
|
||||
}
|
||||
}
|
||||
return '<div class="vp-file-tree">'
|
||||
const info = tokens[idx].info.trim()
|
||||
|
||||
const title = info.slice(type.length).trim()
|
||||
return `<div class="vp-file-tree">${title ? `<p class="vp-file-tree-title">${title}</p>` : ''}`
|
||||
}
|
||||
else {
|
||||
return '</div>'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { removeLeadingSlash } from 'vuepress/shared'
|
||||
import Token from 'markdown-it/lib/token.mjs'
|
||||
import { removeLeadingSlash } from 'vuepress/shared'
|
||||
|
||||
interface FileTreeNode {
|
||||
filename: string
|
||||
|
||||
@ -1,14 +1,46 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import http from 'node:https'
|
||||
import { URL } from 'node:url'
|
||||
import { isLinkExternal, isLinkHttp } from '@vuepress/helper'
|
||||
import imageSize from 'image-size'
|
||||
import { fs, path } from 'vuepress/utils'
|
||||
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
|
||||
import type { App } from 'vuepress'
|
||||
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
|
||||
import { isLinkExternal } from '@vuepress/helper'
|
||||
import { fs, path } from '@vuepress/utils'
|
||||
import imageSize from 'image-size'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
|
||||
export function imageSizePlugin(app: App, md: Markdown): void {
|
||||
if (!app.env.isBuild)
|
||||
interface ImgSize {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
const REG_IMG = /!\[.*?\]\(.*?\)/g
|
||||
const REG_IMG_TAG = /<img(.*?)>/g
|
||||
const REG_IMG_TAG_SRC = /src(?:set)?=(['"])(.+?)\1/g
|
||||
const BADGE_LIST = [
|
||||
'https://img.shields.io',
|
||||
'https://badge.fury.io',
|
||||
'https://badgen.net',
|
||||
'https://forthebadge.com',
|
||||
'https://vercel.com/button',
|
||||
]
|
||||
|
||||
const cache = new Map<string, ImgSize>()
|
||||
|
||||
export async function imageSizePlugin(
|
||||
app: App,
|
||||
md: Markdown,
|
||||
type: boolean | 'local' | 'all' = false,
|
||||
) {
|
||||
if (!app.env.isBuild || !type)
|
||||
return
|
||||
|
||||
const cache = new Map<string, { width: number, height: number }>()
|
||||
if (type === 'all') {
|
||||
try {
|
||||
await scanRemoteImageSize(app)
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
const imageRule = md.renderer.rules.image!
|
||||
md.renderer.rules.image = (tokens, idx, options, env: MarkdownEnv, self) => {
|
||||
@ -17,25 +49,77 @@ export function imageSizePlugin(app: App, md: Markdown): void {
|
||||
|
||||
const token = tokens[idx]
|
||||
const src = token.attrGet('src')
|
||||
|
||||
if (!src || src.startsWith('data:') || isLinkExternal(src))
|
||||
return imageRule(tokens, idx, options, env, self)
|
||||
|
||||
const width = token.attrGet('width')
|
||||
const height = token.attrGet('height')
|
||||
const size = resolveSize(src, width, height, env)
|
||||
|
||||
if (size) {
|
||||
token.attrSet('width', `${size.width}`)
|
||||
token.attrSet('height', `${size.height}`)
|
||||
}
|
||||
|
||||
return imageRule(tokens, idx, options, env, self)
|
||||
}
|
||||
|
||||
const rawHtmlBlockRule = md.renderer.rules.html_block!
|
||||
const rawHtmlInlineRule = md.renderer.rules.html_inline!
|
||||
md.renderer.rules.html_block = createHtmlRule(rawHtmlBlockRule)
|
||||
md.renderer.rules.html_inline = createHtmlRule(rawHtmlInlineRule)
|
||||
|
||||
function createHtmlRule(rawHtmlRule: RenderRule): RenderRule {
|
||||
return (tokens, idx, options, env, self) => {
|
||||
const token = tokens[idx]
|
||||
token.content = token.content.replace(REG_IMG_TAG, (raw, info) => {
|
||||
const { attrs } = resolveAttrs(info)
|
||||
const src = attrs.src || attrs.srcset
|
||||
const size = resolveSize(src, attrs.width, attrs.height, env)
|
||||
|
||||
if (!size)
|
||||
return raw
|
||||
|
||||
attrs.width = size.width
|
||||
attrs.height = size.height
|
||||
|
||||
const imgAttrs = Object.entries(attrs)
|
||||
.map(([key, value]) => typeof value === 'boolean' ? key : `${key}="${value}"`)
|
||||
.join(' ')
|
||||
|
||||
return `<img ${imgAttrs}>`
|
||||
})
|
||||
return rawHtmlRule(tokens, idx, options, env, self)
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSize(
|
||||
src: string | null | undefined,
|
||||
width: string | null | undefined,
|
||||
height: string | null | undefined,
|
||||
env: MarkdownEnv,
|
||||
): false | ImgSize {
|
||||
if (!src || src.startsWith('data:'))
|
||||
return false
|
||||
|
||||
if (width && height)
|
||||
return imageRule(tokens, idx, options, env, self)
|
||||
return false
|
||||
|
||||
const filepath = resolveImageUrl(src, env)
|
||||
const isExternal = isLinkExternal(src, env.base)
|
||||
const filepath = isExternal ? src : resolveImageUrl(src, env, app)
|
||||
|
||||
if (!cache.has(filepath)) {
|
||||
if (!filepath || !fs.existsSync(filepath))
|
||||
return imageRule(tokens, idx, options, env, self)
|
||||
if (isExternal) {
|
||||
if (!cache.has(filepath))
|
||||
return false
|
||||
}
|
||||
else {
|
||||
if (!cache.has(filepath)) {
|
||||
if (!fs.existsSync(filepath))
|
||||
return false
|
||||
|
||||
const { width: w, height: h } = imageSize(filepath)
|
||||
if (!w || !h)
|
||||
return imageRule(tokens, idx, options, env, self)
|
||||
cache.set(filepath, { width: w, height: h })
|
||||
const { width: w, height: h } = imageSize(filepath)
|
||||
if (!w || !h)
|
||||
return false
|
||||
|
||||
cache.set(filepath, { width: w, height: h })
|
||||
}
|
||||
}
|
||||
|
||||
const { width: originalWidth, height: originalHeight } = cache.get(filepath)!
|
||||
@ -44,32 +128,117 @@ export function imageSizePlugin(app: App, md: Markdown): void {
|
||||
|
||||
if (width && !height) {
|
||||
const w = Number.parseInt(width, 10)
|
||||
token.attrSet('width', `${w}`)
|
||||
token.attrSet('height', `${Math.round(w / ratio)}`)
|
||||
return { width: w, height: Math.round(w / ratio) }
|
||||
}
|
||||
else if (height && !width) {
|
||||
const h = Number.parseInt(height, 10)
|
||||
token.attrSet('width', `${Math.round(h * ratio)}`)
|
||||
token.attrSet('height', `${h}`)
|
||||
return { width: Math.round(h * ratio), height: h }
|
||||
}
|
||||
else {
|
||||
token.attrSet('width', `${originalWidth}`)
|
||||
token.attrSet('height', `${originalHeight}`)
|
||||
return { width: originalWidth, height: originalHeight }
|
||||
}
|
||||
|
||||
return imageRule(tokens, idx, options, env, self)
|
||||
}
|
||||
|
||||
function resolveImageUrl(src: string, env: MarkdownEnv): string {
|
||||
if (src[0] === '/')
|
||||
return app.dir.public(src.slice(1))
|
||||
|
||||
if (env.filePathRelative)
|
||||
return app.dir.source(path.join(path.dirname(env.filePathRelative), src))
|
||||
|
||||
if (env.filePath)
|
||||
return path.resolve(env.filePath, src)
|
||||
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function resolveImageUrl(src: string, env: MarkdownEnv, app: App): string {
|
||||
if (src[0] === '/')
|
||||
return app.dir.public(src.slice(1))
|
||||
|
||||
if (env.filePathRelative && src[0] === '.')
|
||||
return app.dir.source(path.join(path.dirname(env.filePathRelative), src))
|
||||
|
||||
// fallback
|
||||
if (env.filePath && (src[0] === '.' || src[0] === '/'))
|
||||
return path.resolve(env.filePath, src)
|
||||
|
||||
return path.resolve(src)
|
||||
}
|
||||
|
||||
export async function scanRemoteImageSize(app: App) {
|
||||
if (!app.env.isBuild)
|
||||
return
|
||||
const cwd = app.dir.source()
|
||||
const files = await fs.readdir(cwd, { recursive: true })
|
||||
const imgList: string[] = []
|
||||
for (const file of files) {
|
||||
const filepath = path.join(cwd, file)
|
||||
if (
|
||||
(await (fs.stat(filepath))).isFile()
|
||||
&& !filepath.includes('.vuepress')
|
||||
&& !filepath.includes('node_modules')
|
||||
&& filepath.endsWith('.md')
|
||||
) {
|
||||
const content = await fs.readFile(filepath, 'utf-8')
|
||||
// [xx](xxx)
|
||||
const syntaxMatched = content.match(REG_IMG) ?? []
|
||||
for (const img of syntaxMatched) {
|
||||
const src = img.slice(img.indexOf('](') + 2, -1)
|
||||
addList(src.split(/\s+/)[0])
|
||||
}
|
||||
// <img src=""> or <img srcset="xxx">
|
||||
const tagMatched = content.match(REG_IMG_TAG) ?? []
|
||||
for (const img of tagMatched) {
|
||||
const src = img.match(REG_IMG_TAG_SRC)?.[2] ?? ''
|
||||
addList(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addList(src: string) {
|
||||
if (src && isLinkHttp(src)
|
||||
&& !imgList.includes(src)
|
||||
&& !BADGE_LIST.some(badge => src.startsWith(badge))
|
||||
) {
|
||||
imgList.push(src)
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(imgList.map(async (src) => {
|
||||
if (!cache.has(src)) {
|
||||
const { width, height } = await fetchImageSize(src)
|
||||
if (width && height)
|
||||
cache.set(src, { width, height })
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
function fetchImageSize(src: string): Promise<ImgSize> {
|
||||
const link = new URL(src)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
http.get(link, async (stream) => {
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
try {
|
||||
const { width, height } = imageSize(Buffer.concat(chunks))
|
||||
if (width && height) {
|
||||
return resolve({ width, height })
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
const { width, height } = imageSize(Buffer.concat(chunks))
|
||||
resolve({ width: width!, height: height! })
|
||||
}).on('error', () => resolve({ width: 0, height: 0 }))
|
||||
})
|
||||
}
|
||||
|
||||
export async function resolveImageSize(app: App, url: string, remote = false): Promise<ImgSize> {
|
||||
if (cache.has(url))
|
||||
return cache.get(url)!
|
||||
|
||||
if (isLinkHttp(url) && remote) {
|
||||
return await fetchImageSize(url)
|
||||
}
|
||||
|
||||
if (url[0] === '/') {
|
||||
const filepath = app.dir.public(url.slice(1))
|
||||
if (fs.existsSync(filepath)) {
|
||||
const { width, height } = imageSize(filepath)
|
||||
return { width: width!, height: height! }
|
||||
}
|
||||
}
|
||||
|
||||
return { width: 0, height: 0 }
|
||||
}
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
* @[jsfiddle theme="dark" tab="js,css,html,result"](user/id)
|
||||
*/
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import type { JSFiddleTokenMeta } from '../../shared/index.js'
|
||||
import { createRuleBlock } from '../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import type { JSFiddleTokenMeta } from '../../shared/index.js'
|
||||
|
||||
export const jsfiddlePlugin: PluginWithOptions<never> = (md) => {
|
||||
createRuleBlock<JSFiddleTokenMeta>(md, {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type markdownIt from 'markdown-it'
|
||||
import container from 'markdown-it-container'
|
||||
import { fs, getDirname, path } from 'vuepress/utils'
|
||||
import type markdownIt from 'markdown-it'
|
||||
import type Token from 'markdown-it/lib/token.mjs'
|
||||
import type { App } from 'vuepress/core'
|
||||
import { fs, getDirname, path } from 'vuepress/utils'
|
||||
import type { ReplEditorData, ReplOptions } from '../../shared/index.js'
|
||||
|
||||
const RE_INFO = /^(#editable)?(.*)$/
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
*/
|
||||
import { path } from 'vuepress/utils'
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import type { PDFTokenMeta } from '../../shared/index.js'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import { createRuleBlock } from '../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import type { PDFTokenMeta } from '../../shared/index.js'
|
||||
|
||||
export const pdfPlugin: PluginWithOptions<never> = (md) => {
|
||||
createRuleBlock<PDFTokenMeta>(md, {
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
* @[replit title="" height="400px" width="100%" theme="dark"](user/repl-name)
|
||||
*/
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import type { ReplitTokenMeta } from '../../shared/index.js'
|
||||
import { createRuleBlock } from '../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
||||
import type { ReplitTokenMeta } from '../../shared/index.js'
|
||||
|
||||
export const replitPlugin: PluginWithOptions<never> = (md) => {
|
||||
createRuleBlock<ReplitTokenMeta>(md, {
|
||||
|
||||
@ -6,11 +6,11 @@
|
||||
*/
|
||||
import { URLSearchParams } from 'node:url'
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import type { BilibiliTokenMeta } from '../../../shared/index.js'
|
||||
import { resolveAttrs } from '../../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../../utils/parseRect.js'
|
||||
import { timeToSeconds } from '../../utils/timeToSeconds.js'
|
||||
import { createRuleBlock } from '../../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../../utils/resolveAttrs.js'
|
||||
import { timeToSeconds } from '../../utils/timeToSeconds.js'
|
||||
import type { BilibiliTokenMeta } from '../../../shared/index.js'
|
||||
|
||||
const BILIBILI_LINK = 'https://player.bilibili.com/player.html'
|
||||
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
*/
|
||||
import { URLSearchParams } from 'node:url'
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import type { YoutubeTokenMeta } from '../../../shared/index.js'
|
||||
import { resolveAttrs } from '../../utils/resolveAttrs.js'
|
||||
import { parseRect } from '../../utils/parseRect.js'
|
||||
import { timeToSeconds } from '../../utils/timeToSeconds.js'
|
||||
import { createRuleBlock } from '../../utils/createRuleBlock.js'
|
||||
import { parseRect } from '../../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../../utils/resolveAttrs.js'
|
||||
import { timeToSeconds } from '../../utils/timeToSeconds.js'
|
||||
import type { YoutubeTokenMeta } from '../../../shared/index.js'
|
||||
|
||||
const YOUTUBE_LINK = 'https://www.youtube.com/embed/'
|
||||
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export * from './plugin.js'
|
||||
export * from '../shared/index.js'
|
||||
export { resolveImageSize } from './features/imageSize.js'
|
||||
export * from './plugin.js'
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import type MarkdownIt from 'markdown-it'
|
||||
import { addViteOptimizeDepsInclude } from '@vuepress/helper'
|
||||
import type { CanIUseOptions, MarkdownPowerPluginOptions } from '../shared/index.js'
|
||||
import type MarkdownIt from 'markdown-it'
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import { caniusePlugin, legacyCaniuse } from './features/caniuse.js'
|
||||
import { pdfPlugin } from './features/pdf.js'
|
||||
import { codepenPlugin } from './features/codepen.js'
|
||||
import { codeSandboxPlugin } from './features/codeSandbox.js'
|
||||
import { fileTreePlugin } from './features/fileTree/index.js'
|
||||
import { iconsPlugin } from './features/icons.js'
|
||||
import { imageSizePlugin } from './features/imageSize.js'
|
||||
import { jsfiddlePlugin } from './features/jsfiddle.js'
|
||||
import { langReplPlugin } from './features/langRepl.js'
|
||||
import { pdfPlugin } from './features/pdf.js'
|
||||
import { plotPlugin } from './features/plot.js'
|
||||
import { replitPlugin } from './features/replit.js'
|
||||
import { bilibiliPlugin } from './features/video/bilibili.js'
|
||||
import { youtubePlugin } from './features/video/youtube.js'
|
||||
import { codepenPlugin } from './features/codepen.js'
|
||||
import { replitPlugin } from './features/replit.js'
|
||||
import { codeSandboxPlugin } from './features/codeSandbox.js'
|
||||
import { jsfiddlePlugin } from './features/jsfiddle.js'
|
||||
import { plotPlugin } from './features/plot.js'
|
||||
import { langReplPlugin } from './features/langRepl.js'
|
||||
import { prepareConfigFile } from './prepareConfigFile.js'
|
||||
import { fileTreePlugin } from './features/fileTree/index.js'
|
||||
import { imageSizePlugin } from './features/imageSize.js'
|
||||
import type { CanIUseOptions, MarkdownPowerPluginOptions } from '../shared/index.js'
|
||||
|
||||
export function markdownPowerPlugin(options: MarkdownPowerPluginOptions = {}): Plugin {
|
||||
return (app) => {
|
||||
@ -39,7 +39,7 @@ export function markdownPowerPlugin(options: MarkdownPowerPluginOptions = {}): P
|
||||
},
|
||||
|
||||
extendsMarkdown: async (md: MarkdownIt, app) => {
|
||||
imageSizePlugin(app, md)
|
||||
await imageSizePlugin(app, md, options.imageSize)
|
||||
|
||||
if (options.caniuse) {
|
||||
const caniuse = options.caniuse === true ? {} : options.caniuse
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import { ensureEndingSlash } from '@vuepress/helper'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import type { App } from 'vuepress/core'
|
||||
import type { MarkdownPowerPluginOptions } from '../shared/index.js'
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
export * from './caniuse.js'
|
||||
export * from './pdf.js'
|
||||
export * from './icons.js'
|
||||
export * from './video.js'
|
||||
export * from './codepen.js'
|
||||
export * from './codeSandbox.js'
|
||||
export * from './icons.js'
|
||||
export * from './jsfiddle.js'
|
||||
export * from './pdf.js'
|
||||
export * from './plot.js'
|
||||
export * from './plugin.js'
|
||||
export * from './repl.js'
|
||||
export * from './replit.js'
|
||||
export * from './jsfiddle.js'
|
||||
export * from './plot.js'
|
||||
|
||||
export * from './plugin.js'
|
||||
|
||||
export * from './size.js'
|
||||
|
||||
export * from './video.js'
|
||||
|
||||
@ -1,32 +1,125 @@
|
||||
import type { CanIUseOptions } from './caniuse.js'
|
||||
import type { PDFOptions } from './pdf.js'
|
||||
import type { IconsOptions } from './icons.js'
|
||||
import type { PDFOptions } from './pdf.js'
|
||||
import type { PlotOptions } from './plot.js'
|
||||
import type { ReplOptions } from './repl.js'
|
||||
|
||||
export interface MarkdownPowerPluginOptions {
|
||||
/**
|
||||
* 是否启用 PDF 嵌入语法
|
||||
*
|
||||
* `@[pdf](pdf_url)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
pdf?: boolean | PDFOptions
|
||||
|
||||
// new syntax
|
||||
/**
|
||||
* 是否启用 iconify 图标嵌入语法
|
||||
*
|
||||
* `:[collect:icon_name]:`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
icons?: boolean | IconsOptions
|
||||
/**
|
||||
* 是否启用 隐秘文本 语法
|
||||
*
|
||||
* `!!plot_content!!`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
plot?: boolean | PlotOptions
|
||||
|
||||
// video embed
|
||||
/**
|
||||
* 是否启用 bilibili 视频嵌入
|
||||
*
|
||||
* `@[bilibili](bid)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
bilibili?: boolean
|
||||
/**
|
||||
* 是否启用 youtube 视频嵌入
|
||||
*
|
||||
* `@[youtube](video_id)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
youtube?: boolean
|
||||
|
||||
// code embed
|
||||
/**
|
||||
* 是否启用 codepen 嵌入
|
||||
*
|
||||
* `@[codepen](pen_id)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
codepen?: boolean
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
replit?: boolean
|
||||
/**
|
||||
* 是否启用 codeSandbox 嵌入
|
||||
*
|
||||
* `@[codesandbox](codesandbox_id)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
codeSandbox?: boolean
|
||||
/**
|
||||
* 是否启用 jsfiddle 嵌入
|
||||
*
|
||||
* `@[jsfiddle](jsfiddle_id)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
jsfiddle?: boolean
|
||||
|
||||
// container
|
||||
/**
|
||||
* 是否启用 REPL 容器语法
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
repl?: false | ReplOptions
|
||||
/**
|
||||
* 是否启用 文件树 容器语法
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
fileTree?: boolean
|
||||
|
||||
/**
|
||||
* 是否启用 caniuse 嵌入语法
|
||||
*
|
||||
* `@[caniuse](feature_name)`
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
caniuse?: boolean | CanIUseOptions
|
||||
|
||||
// enhance
|
||||
/**
|
||||
* 是否启用 自动填充 图片宽高属性
|
||||
*
|
||||
* __请注意,无论是否启用,该功能仅在构建生产包时生效__
|
||||
*
|
||||
* - 如果为 `true` ,等同于 `'local'`
|
||||
* - 如果为 `local`,则仅对本地图片 添加 width 和 height
|
||||
* - 如果为 `all`,则对所有图片(即包括 本地 和 远程) 添加 width 和 height
|
||||
*
|
||||
* 图片在加载过程中如果比较慢,从加载到完成的过程会导致页面布局不稳定,导致内容闪烁等。
|
||||
* 此功能通过给图片添加 `width` 和 `height` 属性来解决该问题。
|
||||
*
|
||||
* 请谨慎使用 `all` 选项,该选项会在构建阶段发起网络请求,尝试加载远程图片以获取图片尺寸信息,
|
||||
* 这可能会导致 构建时间变得更长(幸运的是获取尺寸信息只需要加载图片 几 KB 的数据包,因此耗时不会过长)
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
imageSize?: boolean | 'local' | 'all'
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Options, defineConfig } from 'tsup'
|
||||
import { defineConfig, type Options } from 'tsup'
|
||||
|
||||
const config = [
|
||||
{ dir: 'composables', files: ['codeRepl.ts', 'pdf.ts', 'rustRepl.ts', 'size.ts'] },
|
||||
|
||||
@ -44,11 +44,11 @@
|
||||
"@vueuse/core": "^11.0.3",
|
||||
"@vueuse/integrations": "^11.0.3",
|
||||
"chokidar": "^3.6.0",
|
||||
"focus-trap": "^7.5.4",
|
||||
"focus-trap": "^7.6.0",
|
||||
"mark.js": "^8.11.1",
|
||||
"minisearch": "^7.1.0",
|
||||
"p-map": "^7.0.2",
|
||||
"vue": "^3.5.3"
|
||||
"vue": "^3.5.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -4,8 +4,8 @@ import {
|
||||
defineAsyncComponent,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import type { SearchBoxLocales, SearchOptions } from '../../shared/index.js'
|
||||
import SearchButton from './SearchButton.vue'
|
||||
import type { SearchBoxLocales, SearchOptions } from '../../shared/index.js'
|
||||
|
||||
defineProps<{
|
||||
locales: SearchBoxLocales
|
||||
|
||||
@ -1,17 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
type Ref,
|
||||
computed,
|
||||
markRaw,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
toRef,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { useRouteLocale, useRouter } from 'vuepress/client'
|
||||
import {
|
||||
computedAsync,
|
||||
debouncedWatch,
|
||||
@ -20,15 +7,28 @@ import {
|
||||
useScrollLock,
|
||||
useSessionStorage,
|
||||
} from '@vueuse/core'
|
||||
import Mark from 'mark.js/src/vanilla.js'
|
||||
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
||||
import Mark from 'mark.js/src/vanilla.js'
|
||||
import MiniSearch, { type SearchResult } from 'minisearch'
|
||||
import {
|
||||
computed,
|
||||
markRaw,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
type Ref,
|
||||
ref,
|
||||
shallowRef,
|
||||
toRef,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { useRouteLocale, useRouter } from 'vuepress/client'
|
||||
import { useLocale, useSearchIndex } from '../composables/index.js'
|
||||
import type { SearchBoxLocales, SearchOptions } from '../../shared/index.js'
|
||||
import { LRUCache } from '../utils/index.js'
|
||||
import SearchIcon from './icons/SearchIcon.vue'
|
||||
import ClearIcon from './icons/ClearIcon.vue'
|
||||
import BackIcon from './icons/BackIcon.vue'
|
||||
import ClearIcon from './icons/ClearIcon.vue'
|
||||
import SearchIcon from './icons/SearchIcon.vue'
|
||||
import type { SearchBoxLocales, SearchOptions } from '../../shared/index.js'
|
||||
|
||||
const props = defineProps<{
|
||||
locales: SearchBoxLocales
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { toRef } from 'vue'
|
||||
import type { SearchBoxLocales } from '../../shared/index.js'
|
||||
import { useLocale } from '../composables/index.js'
|
||||
import type { SearchBoxLocales } from '../../shared/index.js'
|
||||
|
||||
const props = defineProps<{
|
||||
locales: SearchBoxLocales
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
export * from './searchIndex.js'
|
||||
export * from './locale.js'
|
||||
export * from './searchIndex.js'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { MaybeRef } from 'vue'
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import type { MaybeRef } from 'vue'
|
||||
import type { SearchBoxLocales } from '../../shared/index.js'
|
||||
|
||||
const defaultLocales: SearchBoxLocales = {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { h } from 'vue'
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import type { ClientConfig } from 'vuepress/client'
|
||||
import { h } from 'vue'
|
||||
import type { SearchBoxLocales, SearchOptions } from '../shared/index.js'
|
||||
import Search from './components/Search.vue'
|
||||
import type { SearchBoxLocales, SearchOptions } from '../shared/index.js'
|
||||
|
||||
declare const __SEARCH_LOCALES__: SearchBoxLocales
|
||||
declare const __SEARCH_OPTIONS__: SearchOptions
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { searchPlugin } from './searchPlugin.js'
|
||||
|
||||
export { prepareSearchIndex } from './prepareSearchIndex.js'
|
||||
export * from '../shared/index.js'
|
||||
export { prepareSearchIndex } from './prepareSearchIndex.js'
|
||||
|
||||
export {
|
||||
searchPlugin,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { App, Page } from 'vuepress/core'
|
||||
import MiniSearch from 'minisearch'
|
||||
import pMap from 'p-map'
|
||||
import { colors, logger } from 'vuepress/utils'
|
||||
import type { App, Page } from 'vuepress/core'
|
||||
import type { SearchOptions, SearchPluginOptions } from '../shared/index.js'
|
||||
|
||||
export interface SearchIndexOptions {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import chokidar from 'chokidar'
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import { addViteOptimizeDepsInclude } from '@vuepress/helper'
|
||||
import type { SearchPluginOptions } from '../shared/index.js'
|
||||
import chokidar from 'chokidar'
|
||||
import { getDirname, path } from 'vuepress/utils'
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import { onSearchIndexRemoved, onSearchIndexUpdated, prepareSearchIndex } from './prepareSearchIndex.js'
|
||||
import type { SearchPluginOptions } from '../shared/index.js'
|
||||
|
||||
const __dirname = getDirname(import.meta.url)
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { LocaleConfig, Page } from 'vuepress/core'
|
||||
import type { Options as MiniSearchOptions } from 'minisearch'
|
||||
import type { LocaleConfig, Page } from 'vuepress/core'
|
||||
|
||||
export interface SearchLocaleOptions {
|
||||
placeholder: string
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Options, defineConfig } from 'tsup'
|
||||
import { defineConfig, type Options } from 'tsup'
|
||||
|
||||
const sharedExternal: (string | RegExp)[] = [
|
||||
/.*\/shared\/index\.js$/,
|
||||
|
||||
@ -36,8 +36,8 @@
|
||||
"vuepress": "2.0.0-rc.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"@shikijs/transformers": "^1.16.2",
|
||||
"@shikijs/twoslash": "^1.16.2",
|
||||
"@shikijs/transformers": "^1.17.0",
|
||||
"@shikijs/twoslash": "^1.17.0",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@vuepress/helper": "2.0.0-rc.42",
|
||||
"@vueuse/core": "^11.0.3",
|
||||
@ -46,7 +46,7 @@
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"mdast-util-to-hast": "^13.2.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.16.2",
|
||||
"shiki": "^1.17.0",
|
||||
"twoslash": "^0.2.11",
|
||||
"twoslash-vue": "^0.2.11"
|
||||
},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { App } from 'vue'
|
||||
import FloatingVue, { recomputeAllPoppers } from 'floating-vue'
|
||||
import type { App } from 'vue'
|
||||
import 'floating-vue/dist/style.css'
|
||||
|
||||
const isMobile = typeof navigator !== 'undefined' && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { App } from 'vuepress'
|
||||
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
|
||||
import type { CopyCodeOptions } from '../types.js'
|
||||
import { createCopyCodeButtonRender } from './createCopyCodeButtonRender.js'
|
||||
import type { CopyCodeOptions } from '../types.js'
|
||||
|
||||
/**
|
||||
* This plugin should work after `preWrapperPlugin`,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user