Merge branch 'main' into feat/add-build-end-hook

pull/709/head
Divyansh Singh 3 years ago committed by GitHub
commit 19fa1e29c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -26,11 +26,11 @@ jobs:
- name: Install deps - name: Install deps
run: pnpm install run: pnpm install
- name: Build
run: pnpm run build
- name: Lint - name: Lint
run: pnpm run lint-fail run: pnpm run lint-fail
- name: Test - name: Test
run: pnpm run test-run run: pnpm run test-run
- name: Build
run: pnpm run build

@ -1,4 +1,62 @@
# [1.0.0-alpha.1](https://github.com/vuejs/vitepress/compare/v0.22.4...v1.0.0-alpha.1) (2022-06-01) ## [1.0.0-alpha.4](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.3...v1.0.0-alpha.4) (2022-06-22)
### Bug Fixes
* **theme:** home image style is broken in big view port ([2bd960d](https://github.com/vuejs/vitepress/commit/2bd960d5f5a84df614035a4fb941331fdf9d84f2))
## [1.0.0-alpha.3](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.2...v1.0.0-alpha.3) (2022-06-22)
### Bug Fixes
* **theme:** italic fonts are missing ([#759](https://github.com/vuejs/vitepress/issues/759)) ([#777](https://github.com/vuejs/vitepress/issues/777)) ([fa00c83](https://github.com/vuejs/vitepress/commit/fa00c83af4aa5fa619cf2e3da8d5aab77984ba7c))
* **theme:** copy code in non-secure contexts ([#792](https://github.com/vuejs/vitepress/issues/792)) ([2935ed2](https://github.com/vuejs/vitepress/commit/2935ed22954010fa0d48d0625e5f2b0136991e0b))
* **theme:** copy code button has wrong tag closing syntax ([#816](https://github.com/vuejs/vitepress/issues/816)) ([75ca9e4](https://github.com/vuejs/vitepress/commit/75ca9e4302c65e3bcc9518f7df928318380f6cf6))
* **theme:** edit link gets hidden when a page don't have siblings ([#751](https://github.com/vuejs/vitepress/issues/751)) ([9bc4330](https://github.com/vuejs/vitepress/commit/9bc43306a1fe7bfd54b738642fd1737917a3af41))
* **theme:** nav close icon not working correctly ([#744](https://github.com/vuejs/vitepress/issues/744)) ([75c9d80](https://github.com/vuejs/vitepress/commit/75c9d809d21c0484c0ae8ce691d598cf229c9525))
* **theme:** sidebar is shown on home layout ([#825](https://github.com/vuejs/vitepress/issues/825)) ([#829](https://github.com/vuejs/vitepress/issues/829)) ([42cbd31](https://github.com/vuejs/vitepress/commit/42cbd31327b789ff9525919afb39b3092f1d445b))
* **theme:** sidebar collapsed option not working on layout change ([#809](https://github.com/vuejs/vitepress/issues/809)) ([#811](https://github.com/vuejs/vitepress/issues/811)) ([7737699](https://github.com/vuejs/vitepress/commit/773769926b74cabfbb3577d6c6e654fe976c0b76))
* **theme:** `DefaultTheme` type causes error in some cases ([#804](https://github.com/vuejs/vitepress/issues/804)) ([107724a](https://github.com/vuejs/vitepress/commit/107724ac6f24e5272964d3bdbff54169fa4d91ae))
### Features
* **build:** allow setting `base` from command line ([2952638](https://github.com/vuejs/vitepress/commit/295263807df5a0cdff3b04d5131a3cebc76ec491))
* **theme:** add active status to nav menu group ([#820](https://github.com/vuejs/vitepress/issues/820)) ([fdb5720](https://github.com/vuejs/vitepress/commit/fdb5720acda9f8f2dd1e4f33d0810a6e9ca9e7de))
* **theme:** add global layout slots ([#760](https://github.com/vuejs/vitepress/issues/760)) ([#812](https://github.com/vuejs/vitepress/issues/812)) ([1f1e298](https://github.com/vuejs/vitepress/commit/1f1e298864f7b8af9672b55251958ba766678e0b))
* **theme:** support themeable images for logo and hero ([#745](https://github.com/vuejs/vitepress/issues/745)) ([42813ce](https://github.com/vuejs/vitepress/commit/42813ce936d9fb141241969651cb0e3a02345442))
* **theme:** add team page feature ([#828](https://github.com/vuejs/vitepress/issues/828)) ([7cfe0f0](https://github.com/vuejs/vitepress/commit/7cfe0f05ab013904c66c48d8529d2ba4747869cb))
## [1.0.0-alpha.2](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2022-06-14)
### Bug Fixes
* use h1 for title in hero instead of p ([#776](https://github.com/vuejs/vitepress/issues/776)) ([919d230](https://github.com/vuejs/vitepress/commit/919d23079b636c188ea2049039461b88e0c02fc2))
* add background color in navbar to avoid contrast issues ([#695](https://github.com/vuejs/vitepress/issues/695)) ([305bcc0](https://github.com/vuejs/vitepress/commit/305bcc02e68f8f9aea0000e6950e78455cf572f5))
* add default value for base in `createMarkdownRenderer` ([#555](https://github.com/vuejs/vitepress/issues/555)) ([#556](https://github.com/vuejs/vitepress/issues/556)) ([78a2e84](https://github.com/vuejs/vitepress/commit/78a2e84e7bb7acfda50e686bbd404961babb91e8))
* allow lang='ts' on scripts in markdown ([#693](https://github.com/vuejs/vitepress/issues/693)) ([#701](https://github.com/vuejs/vitepress/issues/701)) ([59df105](https://github.com/vuejs/vitepress/commit/59df10590b958bbc39cc2e8c81a2209eda9d431b))
* better nav item types ([#714](https://github.com/vuejs/vitepress/issues/714)) ([263607b](https://github.com/vuejs/vitepress/commit/263607b279cbfd3db80bbe0ea66000560d24993a))
* double base in sidebar links ([#756](https://github.com/vuejs/vitepress/issues/756)) ([aa65cb5](https://github.com/vuejs/vitepress/commit/aa65cb58f508bb8e79c20b6370bdfe1b7e470abf))
* use `pre-wrap` for text and tagline ([#746](https://github.com/vuejs/vitepress/issues/746)) ([94704c9](https://github.com/vuejs/vitepress/commit/94704c95637f1cc844d526d4743818d38d1cbae0))
* nav nested items type error ([#710](https://github.com/vuejs/vitepress/issues/710)) ([#711](https://github.com/vuejs/vitepress/issues/711)) ([e5bf15a](https://github.com/vuejs/vitepress/commit/e5bf15a21ee777b4e56ad86ec5ebb5b0e161b721))
* page layout breaks when page name matches the css class name ([#696](https://github.com/vuejs/vitepress/issues/696)) ([#699](https://github.com/vuejs/vitepress/issues/699)) ([9c0ed93](https://github.com/vuejs/vitepress/commit/9c0ed9397f35827a261d45c789d23ce7faa7ecee))
* remove title bg transition to avoid flush on sidebar on/off ([1942418](https://github.com/vuejs/vitepress/commit/1942418f9570feb81d8066a2413d70b0f36fb8ce))
* sidebar right blur notch ([#712](https://github.com/vuejs/vitepress/issues/712)) ([64c3654](https://github.com/vuejs/vitepress/commit/64c3654b4ba82c16fefdf396106f3077d066c67b))
* typo ([#708](https://github.com/vuejs/vitepress/issues/708)) ([#716](https://github.com/vuejs/vitepress/issues/716)) ([1fe5153](https://github.com/vuejs/vitepress/commit/1fe5153f47465efed05e087119c93d50da6e92a3))
* title in containers not working with markdown content ([#765](https://github.com/vuejs/vitepress/issues/765)) ([#768](https://github.com/vuejs/vitepress/issues/768)) ([c5c3c64](https://github.com/vuejs/vitepress/commit/c5c3c64851b240279a304198fd97e3dc8b5f2fd0))
* use base in links ([#717](https://github.com/vuejs/vitepress/issues/717)) ([#718](https://github.com/vuejs/vitepress/issues/718)) ([8e50154](https://github.com/vuejs/vitepress/commit/8e5015462c8f42c5404525ac8de33af8862c204d))
* use h2 for feature headers ([#774](https://github.com/vuejs/vitepress/issues/774)) ([b1ff725](https://github.com/vuejs/vitepress/commit/b1ff72561182c91b4912ebef44204a53ee3aca5e))
### Features
* add `lastUpdated` option to frontmatter ([b31fbf3](https://github.com/vuejs/vitepress/commit/b31fbf3621bbd7f627a1b80c581b7a8444bc6b0d))
* add doc before and after slot ([#762](https://github.com/vuejs/vitepress/issues/762)) ([#786](https://github.com/vuejs/vitepress/issues/786)) ([9c2a36f](https://github.com/vuejs/vitepress/commit/9c2a36f5428bd98eafb6e2e9bc63f5e532b596b7))
* allow custom edit links ([#698](https://github.com/vuejs/vitepress/issues/698)) ([535e176](https://github.com/vuejs/vitepress/commit/535e176b9a230f692f58a79813a12d2ffbe90be3)), closes [#694](https://github.com/vuejs/vitepress/issues/694) [#697](https://github.com/vuejs/vitepress/issues/697)
* allow custom outline title ([#689](https://github.com/vuejs/vitepress/issues/689)) ([#690](https://github.com/vuejs/vitepress/issues/690)) ([a8a1623](https://github.com/vuejs/vitepress/commit/a8a16237cd8e3e4bb180fbd523a4668a4555b732))
* allow external links in sidebar ([#205](https://github.com/vuejs/vitepress/issues/205)) ([#686](https://github.com/vuejs/vitepress/issues/686)) ([ce17f50](https://github.com/vuejs/vitepress/commit/ce17f5035cbbd1e07373ce0f44913f25269bd80b))
* support custom content in home layout ([#702](https://github.com/vuejs/vitepress/issues/702)) ([92659a2](https://github.com/vuejs/vitepress/commit/92659a2e9dde13e35fadf2d2dca157d648bc9013))
* emit 404.html on build ([#729](https://github.com/vuejs/vitepress/issues/729)) ([#740](https://github.com/vuejs/vitepress/issues/740)) ([23276ba](https://github.com/vuejs/vitepress/commit/23276bae050190b6c1d57347424360fe2c3a57be))
* setup devtools and remove debug component ([#721](https://github.com/vuejs/vitepress/issues/721)) ([421f641](https://github.com/vuejs/vitepress/commit/421f641a76ddc0e8b0f23ab7ad711833fc98c245))
## [1.0.0-alpha.1](https://github.com/vuejs/vitepress/compare/v0.22.4...v1.0.0-alpha.1) (2022-06-01)
Complete rewrite on default theme, with bunch of features added. Please refer to the docs for the new feature and changes. Complete rewrite on default theme, with bunch of features added. Please refer to the docs for the new feature and changes.

@ -0,0 +1,13 @@
import { describe, test, expect } from 'vitest'
import * as Utils from 'client/theme-default/support/utils'
describe('client/theme-default/utils', () => {
describe('ensureStartingSlash', () => {
test('it adds slash to the beginning of the given path', () => {
expect(Utils.ensureStartingSlash('path')).toBe('/path')
expect(Utils.ensureStartingSlash('path/nested')).toBe('/path/nested')
expect(Utils.ensureStartingSlash('/path')).toBe('/path')
expect(Utils.ensureStartingSlash('/path/nested')).toBe('/path/nested')
})
})
})

@ -0,0 +1 @@
export default '{}'

@ -7,8 +7,10 @@ const dir = dirname(fileURLToPath(import.meta.url))
export default defineConfig({ export default defineConfig({
resolve: { resolve: {
alias: { alias: {
'@siteData': resolve(dir, './shims.ts'),
client: resolve(dir, '../src/client'),
node: resolve(dir, '../src/node'), node: resolve(dir, '../src/node'),
client: resolve(dir, '../src/client') vitepress: resolve(dir, '../src/client')
} }
}, },
test: { test: {

@ -1,5 +1,7 @@
import { defineConfig } from '../../src/node' import { defineConfig } from '../../src/node'
import { version } from '../../package.json'
export default defineConfig({ export default defineConfig({
lang: 'en-US', lang: 'en-US',
title: 'VitePress', title: 'VitePress',
@ -16,9 +18,7 @@ export default defineConfig({
}, },
editLink: { editLink: {
repo: 'vuejs/vitepress', pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
branch: 'next',
dir: 'docs',
text: 'Edit this page on GitHub' text: 'Edit this page on GitHub'
}, },
@ -49,9 +49,18 @@ function nav() {
{ text: 'Guide', link: '/guide/what-is-vitepress', activeMatch: '/guide/' }, { text: 'Guide', link: '/guide/what-is-vitepress', activeMatch: '/guide/' },
{ text: 'Configs', link: '/config/introduction', activeMatch: '/config/' }, { text: 'Configs', link: '/config/introduction', activeMatch: '/config/' },
{ {
text: 'Changelog', text: version,
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md' items: [
} {
text: 'Changelog',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'Contributing',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
},
],
},
] ]
} }
@ -89,7 +98,8 @@ function sidebarGuide() {
{ text: 'Edit Link', link: '/guide/theme-edit-link' }, { text: 'Edit Link', link: '/guide/theme-edit-link' },
{ text: 'Last Updated', link: '/guide/theme-last-updated' }, { text: 'Last Updated', link: '/guide/theme-last-updated' },
{ text: 'Layout', link: '/guide/theme-layout' }, { text: 'Layout', link: '/guide/theme-layout' },
{ text: 'Homepage', link: '/guide/theme-homepage' }, { text: 'Home Page', link: '/guide/theme-home-page' },
{ text: 'Team Page', link: '/guide/theme-team-page' },
{ text: 'Footer', link: '/guide/theme-footer' }, { text: 'Footer', link: '/guide/theme-footer' },
{ text: 'Search', link: '/guide/theme-search' }, { text: 'Search', link: '/guide/theme-search' },
{ text: 'Carbon Ads', link: '/guide/theme-carbon-ads' } { text: 'Carbon Ads', link: '/guide/theme-carbon-ads' }

@ -12,6 +12,21 @@ export default {
} }
``` ```
## appearance
- Type: `boolean`
- Default: `true`
Whether to enable "Dark Mode" or not. If the option is set to `true`, it adds `.dark` class to the `<html>` tag depending on the users preference.
It also injects inline script that tries to read users settings from local storage by `vitepress-theme-appearance` key and restores users preferred color mode.
```ts
export default {
appearance: true
}
```
## base ## base
- Type: `string` - Type: `string`
@ -27,57 +42,55 @@ export default {
} }
``` ```
## lang ## description
- Type: `string` - Type: `string`
- Default: `en-US` - Default: `A VitePress site`
The lang attribute for the site. This will render as a `<html lang="en-US">` tag in the page HTML. Description for the site. This will render as a `<meta>` tag in the page HTML.
```ts ```ts
export default { export default {
lang: 'en-US' description: 'A VitePress site'
} }
``` ```
## title ## ignoreDeadLinks
- Type: `string` - Type: `boolean`
- Default: `VitePress` - Default: `false`
Title for the site. This will be displayed in the nav bar. Also used as the suffix for all page titles unless `titleTemplate` is defined. When set to `true`, VitePress will not fail builds due to dead links.
```ts ```ts
export default { export default {
title: 'VitePress' ignoreDeadLinks: true
} }
``` ```
## titleTemplate ## lang
- Type: `string | boolean`
The suffix for the title. For example, if you set `title` as `VitePress` and set `titleTemplate` as `My Site`, the html title becomes `VitePress | My Site`. - Type: `string`
- Default: `en-US`
Set `false` to disable the feature. If the option is `undefined`, then the value of `title` option will be used. The lang attribute for the site. This will render as a `<html lang="en-US">` tag in the page HTML.
```ts ```ts
export default { export default {
title: 'VitePress', lang: 'en-US'
titleTemplate: 'Vite & Vue powered static site generator'
} }
``` ```
## description ## lastUpdated
- Type: `string` - Type: `boolean`
- Default: `A VitePress site` - Default: `false`
Description for the site. This will render as a `<meta>` tag in the page HTML. Use git commit to get the timestamp. This option enables the default theme to display the page's last updated time. You can customize the text via [`themeConfig.lastUpdatedText`](theme-configs#lastupdatedtext) option.
```ts ```ts
export default { export default {
description: 'A VitePress site' lastUpdated: true
} }
``` ```
@ -132,30 +145,31 @@ interface MarkdownOptions extends MarkdownIt.Options {
} }
``` ```
## appearance ## title
- Type: `boolean`
- Default: `true`
Whether to enable "Dark Mode" or not. If the option is set to `true`, it adds `.dark` class to the `<html>` tag depending on the users preference. - Type: `string`
- Default: `VitePress`
It also injects inline script that tries to read users settings from local storage by `vitepress-theme-appearance` key and restores users preferred color mode. Title for the site. This will be displayed in the nav bar. Also used as the suffix for all page titles unless `titleTemplate` is defined.
```ts ```ts
export default { export default {
appearance: true title: 'VitePress'
} }
``` ```
## lastUpdated ## titleTemplate
- Type: `boolean` - Type: `string | boolean`
- Default: `false`
Use git commit to get the timestamp. This option enables the default theme to display the page's last updated time. You can customize the text via [`themeConfig.lastUpdatedText`](theme-configs#lastupdatedtext) option. The suffix for the title. For example, if you set `title` as `VitePress` and set `titleTemplate` as `My Site`, the html title becomes `VitePress | My Site`.
Set `false` to disable the feature. If the option is `undefined`, then the value of `title` option will be used.
```ts ```ts
export default { export default {
lastUpdated: true title: 'VitePress',
titleTemplate: 'Vite & Vue powered static site generator'
} }
``` ```

@ -76,6 +76,19 @@ type Head =
| [string, Record<string, string>, string] | [string, Record<string, string>, string]
``` ```
## lastUpdated
- Type: `boolean`
- Default: `true`
Whether to display [Last Updated](../guide/theme-last-updated) text in the current page.
```yaml
---
lastUpdated: false
---
```
## layout ## layout
- Type: `doc | home | page` - Type: `doc | home | page`

@ -83,6 +83,7 @@ type NavItemWithLink = {
interface NavItemWithChildren { interface NavItemWithChildren {
text?: string text?: string
items: NavItemWithLink[] items: NavItemWithLink[]
activeMatch?: string
} }
``` ```
@ -129,6 +130,22 @@ interface SidebarItem {
} }
``` ```
## outlineTitle
- Type: `string`
- Default: `On this page`
Can be used to customize the title of the right sidebar (on the top of outline links). This is useful when writing documentation in another language.
```js
export default {
themeConfig: {
outlineTitle: 'In hac pagina'
}
}
```
## socialLinks ## socialLinks
- Type: `SocialLink` - Type: `SocialLink`
@ -188,6 +205,30 @@ export interface Footer {
} }
``` ```
## editLink
- Type: `EditLink`
Edit Link lets you display a link to edit the page on Git management services such as GitHub, or GitLab. See [Theme: Edit Link](../guide/theme-edit-link) for more details.
```js
export default {
themeConfig: {
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Edit this page on GitHub'
}
}
}
```
```ts
export interface EditLink {
pattern: string
text?: string
}
```
## lastUpdatedText ## lastUpdatedText
- Type: `string` - Type: `string`
@ -222,9 +263,33 @@ export default {
```ts ```ts
export interface CarbonAds { export interface CarbonAds {
code: string, code: string
placement: string placement: string
} }
``` ```
Learn more in [Theme: Carbon Ads](../guide/theme-carbon-ads) Learn more in [Theme: Carbon Ads](../guide/theme-carbon-ads)
## docFooter
- Type: `DocFooter`
Can be used to customize text appearing above previous and next links. Helpful if not writing docs in English.
```js
export default {
themeConfig: {
docFooter: {
prev: 'Pagina prior',
next: 'Proxima pagina'
}
}
}
```
```ts
export interface DocFooter {
prev?: string
next?: string
}
```

@ -16,7 +16,7 @@ All **static** path references, including absolute paths, should be based on you
## Public Files ## Public Files
Sometimes you may need to provide static assets that are not directly referenced in any of your Markdown or theme components (for example, favicons and PWA icons). The `public` directory under project root can be used as an escape hatch to provide static assets that either are never referenced in source code (e.g. `robots.txt`), or must retain the exact same file name (without hashing). Sometimes you may need to provide static assets that are not directly referenced in any of your Markdown or theme components (for example, favicons and PWA icons). The `public` directory under project root (`docs` folder if you're running `vitepress build docs`) can be used as an escape hatch to provide static assets that either are never referenced in source code (e.g. `robots.txt`), or must retain the exact same file name (without hashing).
Assets placed in `public` will be copied to the root of the dist directory as-is. Assets placed in `public` will be copied to the root of the dist directory as-is.

@ -15,13 +15,13 @@ The essential file for configuring a VitePress site is `.vitepress/config.js`, w
```js ```js
export default { export default {
title: 'Hello VitePress', title: 'VitePress',
description: 'Just playing around.' description: 'Just playing around.'
} }
``` ```
In the above example, the site will have the title of `VitePress`, and `Just playing around` as description meta tag. In the above example, the site will have the title of `VitePress`, and `Just playing around.` as the description meta tag.
Learn everything about VitePress features at [Theme: Introduction](./theme-introduction) to find how to configure specific features with in this config file. Learn everything about VitePress features at [Theme: Introduction](./theme-introduction) to find how to configure specific features within this config file.
You may also find all configuration references at [Configs](../config/introduction). You may also find all configuration references at [Configs](../config/introduction).

@ -2,259 +2,199 @@
The following guides are based on some shared assumptions: The following guides are based on some shared assumptions:
- You are placing your docs inside the `docs` directory of your project; - You are placing your docs inside the `docs` directory of your project.
- You are using the default build output location (`.vitepress/dist`); - You are using the default build output location (`.vitepress/dist`).
- VitePress is installed as a local dependency in your project, and you have setup the following npm scripts: - VitePress is installed as a local dependency in your project, and you have set up the following scripts in your `package.json`:
```json ```json
{ {
"scripts": { "scripts": {
"docs:build": "vitepress build docs", "docs:build": "vitepress build docs",
"docs:serve": "vitepress serve docs" "docs:serve": "vitepress serve docs"
}
} }
} ```
```
## Build and test locally ::: tip
You may run `yarn docs:build` command to build the docs.
```bash
$ yarn docs:build
```
By default, the build output will be placed at `.vitepress/dist`. You may deploy this `dist` folder to any of your preferred platforms.
Once you've built the docs, you may test them locally by running `yarn docs:serve` command.
```bash
$ yarn docs:build
$ yarn docs:serve
```
The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at `http://localhost:5000`. It's an easy way to check if the production build looks OK in your local environment. If your site is to be served at a subdirectory (`https://example.com/subdir/`), then you have to set `'/subdir/'` as the [`base`](../config/app-configs#base) in your `docs/.vitepress/config.js`.
You may configure the port of the server py passing `--port` flag as an argument. :::
```json ## Build and Test Locally
{
"scripts": {
"docs:serve": "vitepress serve docs --port 8080"
}
}
```
Now the `docs:serve` method will launch the server at `http://localhost:8080`. - You may run this command to build the docs:
## GitHub Pages ```sh
$ yarn docs:build
```
1. Set the correct `base` in `docs/.vitepress/config.js`. - Once you've built the docs, you can test them locally by running:
If you are deploying to `https://<USERNAME>.github.io/`, you can omit `base` as it defaults to `'/'`. ```sh
$ yarn docs:serve
```
If you are deploying to `https://<USERNAME>.github.io/<REPO>/`, for example your repository is at `https://github.com/<USERNAME>/<REPO>`, then set `base` to `'/<REPO>/'`. The `serve` command will boot up a local static web server that will serve the files from `.vitepress/dist` at `http://localhost:5000`. It's an easy way to check if the production build looks fine in your local environment.
2. Inside your project, create `deploy.sh` with the following content (with highlighted lines uncommented appropriately), and run it to deploy: - You can configure the port of the server by passing `--port` as an argument.
```bash{13,20,23} ```json
#!/usr/bin/env sh {
"scripts": {
"docs:serve": "vitepress serve docs --port 8080"
}
}
```
# abort on errors Now the `docs:serve` method will launch the server at `http://localhost:8080`.
set -e
# build ## Netlify, Vercel, AWS Amplify, Cloudflare Pages, Render
npm run docs:build
# navigate into the build output directory Set up a new project and change these settings using your dashboard:
cd docs/.vitepress/dist
# if you are deploying to a custom domain - **Build Command:** `yarn docs:build`
# echo 'www.example.com' > CNAME - **Output Directory:** `docs/.vitepress/dist`
- **Node Version:** `14` (or above, by default it usually will be 14 or 16, but on Cloudflare Pages the default is still 12, so you may need to [change that](https://developers.cloudflare.com/pages/platform/build-configuration/))
git init ::: warning
git add -A Don't enable options like _Auto Minify_ for HTML code. It will remove comments from output which have meaning to Vue. You may see hydration mismatch errors if they get removed.
git commit -m 'deploy' :::
# if you are deploying to https://<USERNAME>.github.io ## GitHub Pages
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main
# if you are deploying to https://<USERNAME>.github.io/<REPO> ### Using GitHub Actions
# git push -f git@github.com:<USERNAME>/<REPO>.git main:gh-pages
cd - 1. Create a file named `deploy.yml` inside `.github/workflows` directory of your project with the following content:
```
::: tip ```yaml
You can also run the above script in your CI setup to enable automatic deployment on each push. name: Deploy
:::
## GitHub Pages and Travis CI on:
push:
branches:
- main
1. Set the correct `base` in `docs/.vitepress/config.js`. jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
cache: yarn
- run: yarn install --frozen-lockfile
If you are deploying to `https://<USERNAME or GROUP>.github.io/`, you can omit `base` as it defaults to `'/'`. - name: Build
run: yarn docs:build
If you are deploying to `https://<USERNAME or GROUP>.github.io/<REPO>/`, for example your repository is at `https://github.com/<USERNAME>/<REPO>`, then set `base` to `'/<REPO>/'`. - name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/.vitepress/dist
```
2. Create a file named `.travis.yml` in the root of your project. ::: tip
Please replace the corresponding branch name. For example, if the branch you want to build is `master`, then you should replace `main` with `master` in the above file.
:::
3. Run `yarn` or `npm install` locally and commit the generated lockfile (that is `yarn.lock` or `package-lock.json`). 2. Now commit your code and push it to the `main` branch.
4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages). 3. Wait for actions to complete. Then select `gh-pages` branch as GitHub Pages source in your repository settings. Now your docs will automatically deploy each time you push.
```yaml ## GitLab Pages
language: node_js
node_js:
- lts/*
install:
- yarn install # npm ci
script:
- yarn docs:build # npm run docs:build
deploy:
provider: pages
skip_cleanup: true
local_dir: docs/.vitepress/dist
# A token generated on GitHub allowing Travis to push code on you repository.
# Set in the Travis settings page of your repository, as a secure variable.
github_token: $GITHUB_TOKEN
keep_history: true
on:
branch: main
```
## GitLab Pages and GitLab CI ### Using GitLab CI
1. Set the correct `base` in `docs/.vitepress/config.js`. 1. Set the correct `base` in `docs/.vitepress/config.js`.
If you are deploying to `https://<USERNAME or GROUP>.gitlab.io/`, you can omit `base` as it defaults to `'/'`. If you are deploying to `https://<USERNAME or GROUP>.gitlab.io/`, you can omit `base` as it defaults to `'/'`.
If you are deploying to `https://<USERNAME or GROUP>.gitlab.io/<REPO>/`, for example your repository is at `https://gitlab.com/<USERNAME>/<REPO>`, then set `base` to `'/<REPO>/'`. If you are deploying to `https://<USERNAME or GROUP>.gitlab.io/<REPO>/` (your repository is at `https://gitlab.com/<USERNAME>/<REPO>`), then set `base` to `'/<REPO>/'`.
2. Set `outDir` in `.vitepress/config.js` to `../public`. 2. Set `outDir` in `docs/.vitepress/config.js` to `../public`.
3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: 3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content:
```yaml ```yaml
image: node:10.22.0 image: node:16
pages: pages:
cache: cache:
paths: paths:
- node_modules/ - node_modules/
script: script:
- yarn install # npm install - yarn install
- yarn docs:build # npm run docs:build - yarn docs:build
artifacts: artifacts:
paths: paths:
- public - public
only: only:
- main - main
``` ```
## Netlify ## Azure Static Web Apps
1. On [Netlify](https://www.netlify.com/), setup up a new project from GitHub with the following settings: 1. Follow the [official documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/build-configuration).
- **Build Command:** `vitepress build docs` or `yarn docs:build` or `npm run docs:build` 2. Set these values in your configuration file (and remove the ones you don't require, like `api_location`):
- **Publish directory:** `docs/.vitepress/dist`
2. Hit the deploy button. - **`app_location`**: `/`
- **`output_location`**: `docs/.vitepress/dist`
- **`app_build_command`**: `yarn docs:build`
## Google Firebase ## Firebase
1. Make sure you have [firebase-tools](https://www.npmjs.com/package/firebase-tools) installed. 1. Create `firebase.json` and `.firebaserc` at the root of your project:
2. Create `firebase.json` and `.firebaserc` at the root of your project with the following content: `firebase.json`:
`firebase.json`: ```json
{
"hosting": {
"public": "docs/.vitepress/dist",
"ignore": []
}
}
```
```json `.firebaserc`:
{
"hosting": {
"public": "./docs/.vitepress/dist",
"ignore": []
}
}
```
`.firebaserc`: ```json
{
"projects": {
"default": "<YOUR_FIREBASE_ID>"
}
}
```
```js 2. After running `yarn docs:build`, run this command to deploy:
{
"projects": {
"default": "<YOUR_FIREBASE_ID>"
}
}
```
3. After running `yarn docs:build` or `npm run docs:build`, deploy using the command `firebase deploy`. ```sh
firebase deploy
```
## Surge ## Surge
1. First install [surge](https://www.npmjs.com/package/surge), if you havent already. 1. After running `yarn docs:build`, run this command to deploy:
2. Run `yarn docs:build` or `npm run docs:build`.
3. Deploy to surge by typing `surge docs/.vitepress/dist`. ```sh
npx surge docs/.vitepress/dist
You can also deploy to a [custom domain](https://surge.sh/help/adding-a-custom-domain) by adding `surge docs/.vitepress/dist yourdomain.com`. ```
## Heroku ## Heroku
1. Install [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). 1. Follow documentation and guide given in [`heroku-buildpack-static`](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-static).
2. Create a Heroku account by [signing up](https://signup.heroku.com).
3. Run `heroku login` and fill in your Heroku credentials:
```bash
$ heroku login
```
4. Create a file called `static.json` in the root of your project with the below content:
`static.json`:
```json
{
"root": "./docs/.vitepress/dist"
}
```
This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static).
5. Set up your Heroku git remote:
```bash
# version change
$ git init
$ git add .
$ git commit -m "My site ready for deployment."
# creates a new app with a specified name
$ heroku apps:create example
# set buildpack for static sites
$ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git
```
6. Deploy your site:
```bash
# publish site
$ git push heroku main
# opens a browser to view the Dashboard version of Heroku CI
$ heroku open
```
## Vercel
To deploy your VitePress app with a [Vercel for Git](https://vercel.com/docs/concepts/git), make sure it has been pushed to a Git repository.
Go to https://vercel.com/new and import the project into Vercel using your Git of choice (GitHub, GitLab or BitBucket). Follow the wizard to select the project root with the project's `package.json` and override the build step using `yarn docs:build` or `npm run docs:build` and the output dir to be `./docs/.vitepress/dist` 2. Create a file called `static.json` in the root of your project with the below content:
![Override Vercel Configuration](../images/vercel-configuration.png) ```json
{
"root": "docs/.vitepress/dist"
}
```
After your project has been imported, all subsequent pushes to branches will generate Preview Deployments, and all changes made to the Production Branch (commonly "main") will result in a Production Deployment. ## Layer0
Once deployed, you will get a URL to see your app live, such as the following: https://vitepress.vercel.app Refer [Creating and Deploying a VitePress App with Layer0](https://docs.layer0.co/guides/vitepress).

@ -22,12 +22,45 @@ $ yarn init
## Step. 2: Install VitePress ## Step. 2: Install VitePress
Add VitePress as a dependency for the project. Add VitePress and Vue as dev dependencies for the project.
```bash ```bash
$ yarn add --dev vitepress $ yarn add --dev vitepress vue
``` ```
::: details Getting missing peer deps warnings?
`@docsearch/js` has certain issues with its peer dependencies. If you see some commands failing due to them, you can try this workaround for now:
On Yarn v2/v3, add this inside your rc file (`.yarnrc.yml` by default):
```yaml
packageExtensions:
'@docsearch/react@*':
peerDependenciesMeta:
'@types/react':
optional: true
'react':
optional: true
'react-dom':
optional: true
```
On PNPM, add this in your `package.json`:
```json
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@types/react",
"react",
"react-dom"
]
}
}
```
:::
Create your first document. Create your first document.
```bash ```bash

@ -364,12 +364,12 @@ It also supports [line highlighting](#line-highlighting-in-code-blocks):
The value of `@` corresponds to the source root. By default it's the VitePress project root, unless `srcDir` is configured. The value of `@` corresponds to the source root. By default it's the VitePress project root, unless `srcDir` is configured.
::: :::
You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/codebasics#_folding) to only include the corresponding part of the code file. You can provide a custom region name after a `#` following the filepath (`snippet` by default): You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/codebasics#_folding) to only include the corresponding part of the code file. You can provide a custom region name after a `#` following the filepath:
**Input** **Input**
```md ```md
<<< @/snippets/snippet-with-region.js{1} <<< @/snippets/snippet-with-region.js#snippet{1}
``` ```
**Code file** **Code file**
@ -388,6 +388,48 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co
<!--lint enable strong-marker--> <!--lint enable strong-marker-->
## Markdown File Inclusion
You can include a markdown file in another markdown file like this:
**Input**
```md
# Docs
## Basics
<!--@include: ./parts/basics.md-->
```
**Part file** (`parts/basics.md`)
```md
Some getting started stuff.
### Configuration
Can be created using `.foorc.json`.
```
**Equivalent code**
```md
# Docs
## Basics
Some getting started stuff.
### Configuration
Can be created using `.foorc.json`.
```
::: warning
Note that this does not throw errors if your file is not present. Hence, when using this feature make sure that the contents are being rendered as expected.
:::
## Advanced Configuration ## Advanced Configuration
VitePress uses [markdown-it](https://github.com/markdown-it/markdown-it) as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the `markdown-it` instance using the `markdown` option in `.vitepress/config.js`: VitePress uses [markdown-it](https://github.com/markdown-it/markdown-it) as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the `markdown-it` instance using the `markdown` option in `.vitepress/config.js`:

@ -19,5 +19,5 @@ If you're coming from VitePress 0.x version, there're several breaking changes d
## Frontmatter Config ## Frontmatter Config
- `home: true` option has changed to `layout: home`. Also, many Homepage related settings have been modified to provide additional features. See [Homepage guide](./theme-homepage) for details. - `home: true` option has changed to `layout: home`. Also, many Homepage related settings have been modified to provide additional features. See [Home Page guide](./theme-home-page) for details.
- `footer` option is moved to [`themeConfig.footer`](../config/theme-configs#footer). - `footer` option is moved to [`themeConfig.footer`](../config/theme-configs#footer).

@ -1,3 +1,24 @@
# Migration from VuePress # Migration from VuePress
Coming soon... ## Markdown
### Images
Unlike VuePress, VitePress handles [`base`](/guide/asset-handling.html#base-url) of your config automatically when you use static image.
Hence, now you can render images without `img` tag.
```diff
- <img :src="$withBase('/foo.png')" alt="foo">
+ ![foo](/foo.png)
```
::: warning
For dynamic images you still need `withBase` as shown in [Base URL guide](/guide/asset-handling.html#base-url).
:::
Use `<img.*withBase\('(.*)'\).*alt="([^"]*)".*>` regex to find and replace it with `![$2]($1)` to replace all the images with `![](...)` syntax.
---
more to follow...

@ -1,3 +1,28 @@
# Edit Link # Edit Link
Documentation coming soon... Edit Link lets you display a link to edit the page on Git management services such as GitHub, or GitLab. To enable it, add `themeConfig.editLink` options to your config.
```js
export default {
themeConfig: {
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path'
}
}
}
```
The `pattern` option defines the URL structure for the link, and `:path` is going to be replaced with the page path.
By default, this will add the link text "Edit this page" at the bottom of the doc page. You may customize this text by defining the `text` option.
```js
export default {
themeConfig: {
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Edit this page on GitHub'
}
}
}
```

@ -1,4 +1,4 @@
# Homepage # Home Page
VitePress default theme provides a homepage layout, which you can also see used on [the homepage of this site](../). You may use it on any of your pages by specifying `layout: home` in the [frontmatter](./frontmatter). VitePress default theme provides a homepage layout, which you can also see used on [the homepage of this site](../). You may use it on any of your pages by specifying `layout: home` in the [frontmatter](./frontmatter).
@ -8,7 +8,7 @@ layout: home
--- ---
``` ```
However, this option alone wouldn't do much. You can add several different pre templated "sections" to the homepage by setting additional other options such as `hero` and `feaures`. However, this option alone wouldn't do much. You can add several different pre templated "sections" to the homepage by setting additional other options such as `hero` and `features`.
## Hero Section ## Hero Section
@ -82,7 +82,7 @@ Also you may customize it further by combining `--vp-home-hero-name-background`
## Features Section ## Features Section
In Features section, you can list any number of features you would like to show right after the Hero section. To cinfugure it, pass `features` option to the frontmatter. In Features section, you can list any number of features you would like to show right after the Hero section. To configure it, pass `features` option to the frontmatter.
```yaml ```yaml
--- ---

@ -8,16 +8,17 @@ VitePress comes with its default theme providing many features out of the box. L
- [Edit Link](./theme-edit-link) - [Edit Link](./theme-edit-link)
- [Last Updated](./theme-last-updated) - [Last Updated](./theme-last-updated)
- [Layout](./theme-layout) - [Layout](./theme-layout)
- [Homepage](./theme-homepage) - [Home Page](./theme-home-page)
- [Team Page](./theme-team-page)
- [Footer](./theme-footer) - [Footer](./theme-footer)
- [Search](./theme-search) - [Search](./theme-search)
- [Carbon Ads](./theme-carbon-ads) - [Carbon Ads](./theme-carbon-ads)
If you don't find the features you're looking for, or you would rather create your own theme, you may customize VitePress to fit your requirements. If you don't find the features you're looking for, or you would rather create your own theme, you may customize VitePress to fit your requirements. In the following sections, we'll go through each way of customizing the VitePress theme.
## Using a Custom Theme ## Using a Custom Theme
You can enable a custom theme by adding the `.vitepress/theme/index.js` file (the "theme entry file"). You can enable a custom theme by adding the `.vitepress/theme/index.js` or `.vitepress/theme/index.ts` file (the "theme entry file").
``` ```
. .
@ -53,6 +54,7 @@ The theme entry file should export the theme as its default export:
import Layout from './Layout.vue' import Layout from './Layout.vue'
export default { export default {
// root component to wrap each page
Layout, Layout,
// this is a Vue 3 functional component // this is a Vue 3 functional component
@ -177,7 +179,7 @@ export default {
...DefaultTheme, ...DefaultTheme,
Layout() { Layout() {
return h(DefaultTheme.Layout, null, { return h(DefaultTheme.Layout, null, {
'sidebar-top': () => h(MyComponent) 'aside-outline-before': () => h(MyComponent)
}) })
} }
} }
@ -186,6 +188,8 @@ export default {
Full list of slots available in the default theme layout: Full list of slots available in the default theme layout:
- When `layout: 'doc'` (default) is enabled via frontmatter: - When `layout: 'doc'` (default) is enabled via frontmatter:
- `doc-before`
- `doc-after`
- `aside-top` - `aside-top`
- `aside-bottom` - `aside-bottom`
- `aside-outline-before` - `aside-outline-before`
@ -197,3 +201,12 @@ Full list of slots available in the default theme layout:
- `home-hero-after` - `home-hero-after`
- `home-features-before` - `home-features-before`
- `home-features-after` - `home-features-after`
- Always:
- `layout-top`
- `layout-bottom`
- `nav-bar-title-before`
- `nav-bar-title-after`
- `nav-bar-content-before`
- `nav-bar-content-after`
- `nav-screen-content-before`
- `nav-screen-content-after`

@ -35,4 +35,4 @@ Note that even in this layout, sidebar will still show up if the page has a matc
## Home Layout ## Home Layout
Option `home` will generate templated "Homepage". In this layout, you can set extra options such as `hero` and `features` to customize the content further. Please visit [Theme: Home Page](./theme-homepage) for more details. Option `home` will generate templated "Homepage". In this layout, you can set extra options such as `hero` and `features` to customize the content further. Please visit [Theme: Home Page](./theme-home-page) for more details.

@ -51,7 +51,7 @@ export default {
} }
``` ```
The `text` is the actual text displayed in nav, and the `link` is the link that will be navigated to when the text is clicked. For the link, set path to the actual file without `.md` prefix, and alsways start with `/`. The `text` is the actual text displayed in nav, and the `link` is the link that will be navigated to when the text is clicked. For the link, set path to the actual file without `.md` prefix, and always start with `/`.
Nav links can also be dropdown menus. To do this, set `items` key on link option. Nav links can also be dropdown menus. To do this, set `items` key on link option.
@ -114,7 +114,7 @@ export default {
### Customize link's "active" state ### Customize link's "active" state
Nav menu items will be highlighted when the current page is under the matching path. if you would like to customize the path to be mathced, define `activeMatch` property and regex as a string value. Nav menu items will be highlighted when the current page is under the matching path. if you would like to customize the path to be matched, define `activeMatch` property and regex as a string value.
```js ```js
export default { export default {

@ -1,3 +1,29 @@
# Prev Next Link # Prev Next Link
Documentation coming soon... You can customize the text of previous and next links. This is helpful if you want to show different text on previous/next links than what you have on your sidebar.
## prev
- Type: `string`
- Details:
Specify the text to show on the link to the previous page.
If you don't set this in frontmatter, the text will be inferred from the sidebar config.
- Example:
```yaml
---
prev: 'Get Started | Markdown'
---
```
## next
- Type: `string`
- Details:
Same as `prev` but for the next page.

@ -68,7 +68,7 @@ export default {
## Multiple Sidebars ## Multiple Sidebars
You may show different sidebar depending on the page path. For example, as shown on this site, you might want to create a separate sections of content in your documentation like "Guide" page and `Config` page. You may show different sidebar depending on the page path. For example, as shown on this site, you might want to create a separate sections of content in your documentation like "Guide" page and "Config" page.
To do so, first organize your pages into directories for each desired section: To do so, first organize your pages into directories for each desired section:
@ -92,27 +92,31 @@ export default {
sidebar: { sidebar: {
// This sidebar gets displayed when user is // This sidebar gets displayed when user is
// under `guide` directory. // under `guide` directory.
'/guide/': { '/guide/': [
text: 'Guide', {
items: [ text: 'Guide',
// This shows `/guide/index.md` page. items: [
{ text: 'Index', link: '/guide/' }, // /guide/index.md // This shows `/guide/index.md` page.
{ text: 'One', link: '/guide/one' }, // /guide/one.md { text: 'Index', link: '/guide/' }, // /guide/index.md
{ text: 'Two', link: '/guide/two' } // /guide/two.md { text: 'One', link: '/guide/one' }, // /guide/one.md
] { text: 'Two', link: '/guide/two' } // /guide/two.md
}, ]
}
],
// This sidebar gets displayed when user is // This sidebar gets displayed when user is
// under `config` directory. // under `config` directory.
'/config/': { '/config/': [
text: 'Config', {
items: [ text: 'Config',
// This shows `/guide/index.md` page. items: [
{ text: 'Index', link: '/config/' }, // /config/index.mdasdfasdfasdfasdfaf // This shows `/config/index.md` page.
{ text: 'Three', link: '/config/three' }, // /config/three.md { text: 'Index', link: '/config/' }, // /config/index.md
{ text: 'Four', link: '/config/four' } // /config/four.md { text: 'Three', link: '/config/three' }, // /config/three.md
] { text: 'Four', link: '/config/four' } // /config/four.md
} ]
}
]
} }
} }
} }

@ -0,0 +1,255 @@
<script setup>
import { VPTeamMembers } from 'vitepress/theme'
const members = [
{
avatar: 'https://github.com/yyx990803.png',
name: 'Evan You',
title: 'Creator',
links: [
{ icon: 'github', link: 'https://github.com/yyx990803' },
{ icon: 'twitter', link: 'https://twitter.com/youyuxi' }
]
},
{
avatar: 'https://github.com/kiaking.png',
name: 'Kia King Ishii',
title: 'Developer',
links: [
{ icon: 'github', link: 'https://github.com/kiaking' },
{ icon: 'twitter', link: 'https://twitter.com/KiaKing85' }
]
}
]
</script>
# Team Page
If you would like to introduce your team, you may use Team components to construct the Team Page. There're 2 ways of using these components. One is to embbed it in doc page, and another is to create a full Team Page.
## Show team members in a page
You may use `<VPTeamMembers>` component exposed from `vitepress/theme` to display a list of team members on any page.
```html
<script setup>
import { VPTeamMembers } from 'vitepress/theme'
const members = [
{
avatar: 'https://www.github.com/yyx990803.png',
name: 'Evan You',
title: 'Creator',
links: [
{ icon: 'github', link: 'https://github.com/yyx990803' },
{ icon: 'twitter', link: 'https://twitter.com/youyuxi' }
]
},
...
]
</script>
# Our Team
Say hello to our awesome team.
<VPTeamMembers size="small" :members="members" />
```
The above will display a team member in card looking element. It should display something similar to below.
<VPTeamMembers size="small" :members="members" />
`<VPTeamMembers>` component comes in 2 different sizes, `small` and `medium`. While it boils down to your preference, usually `small` size should fit better when used in doc page. Also, you may add more properties to each member such as adding "description" or "sponsor" button. Learn more about it in [`<VPTeamMembers>`](#vpteammembers).
Embbeding team members in doc page is good for small size team where having dedicated full team page might be too much, or introducing partial members as a reference to documentation context.
If you have large number of members, or simply would like to have more space to show team members, consider [creating a full team page](#create-a-full-team-page).
## Create a full Team Page
Instead of adding team members to doc page, you may also create a full Team Page, similar to how you can create a custom [Home Page](./theme-home-page).
To create a team page, first, create a new md file. The file name doesn't matter, but here lets call it `team.md`. In this file, set frontmatter option `layout: page`, and then you may compose your page structure using `TeamPage` components.
```html
---
layout: page
---
<script setup>
import {
VPTeamPage,
VPTeamPageTitle,
VPTeamMembers
} from 'vitepress/theme'
const members = [
{
avatar: 'https://www.github.com/yyx990803.png',
name: 'Evan You',
title: 'Creator',
links: [
{ icon: 'github', link: 'https://github.com/yyx990803' },
{ icon: 'twitter', link: 'https://twitter.com/youyuxi' }
]
},
...
]
</script>
<VPTeamPage>
<VPTeamPageTitle>
<template #title>
Our Team
</template>
<template #lead>
The development of VitePress is guided by an international
team, some of whom have chosen to be featured below.
</template>
</VPTeamPageTitle>
<VPTeamMembers
:members="members"
/>
</VPTeamPage>
```
When creating a full team page, remember to wrap all components with `<VPTeamPage>` component. This component will ensure all nested team related components get the proper layout structure like spacings.
`<VPPageTitle>` component adds the page title section. The title being `<h1>` heading. Use `#title` and `#lead` slot to document about your team.
`<VPMembers>` works as same as when used in a doc page. It will display list of members.
### Add sections to divide team members
You may add "sections" to the team page. For example, you may have different types of team members such as Core Team Members and Community Partners. You can devide these members into sections to better explain the roles of each group.
To do so, add `<VPTeamPageSection>` component to the `team.md` file we created previously.
```html
---
layout: page
---
<script setup>
import {
VPTeamPage,
VPTeamPageTitle,
VPTeamMembers,
VPTeamPageSection
} from 'vitepress/theme'
const coreMembers = [...]
const partners = [...]
</script>
<VPTeamPage>
<VPTeamPageTitle>
<template #title>Our Team</template>
<template #lead>...</template>
</VPTeamPageTitle>
<VPTeamMembers size="medium" :members="coreMembers" />
<VPTeamPageSection>
<template #title>Partners</template>
<template #lead>...</template>
<template #members>
<VPTeamMembers size="small" :members="partners" />
</template>
</VPTeamPageSection>
</VPTeamPage>
```
The `<VPTeamPageSection>` component can have `#title` and `#lead` slot similar to `VPTeamPageTitle` component, and also `#members` slot for displaying team members.
Remember to put in `<VPTeamMembers>` component within `#members` slot.
## `<VPTeamMembers>`
The `<VPTeamMembers>` component displays a given list of members.
```html
<VPTeamMembers
size="medium"
:members="[
{ avatar: '...', name: '...' },
{ avatar: '...', name: '...' },
...
]"
/>
```
```ts
interface Props {
// Size of each members. Defaults to `medium`.
size?: 'small' | 'meidum'
// List of members to display.
members: TeamMember[]
}
interface TeamMember {
// Avatar image for the member.
avatar: string
// Name of the member.
name: string
// Title to be shown below member's name.
// e.g. Developer, Software Engineer, etc.
title?: string
// Organization that the member belongs.
org?: string
// URL for the organization.
orgLink?: string
// Description for the member.
desc?: string
// Social links. e.g. GitHub, Twitter, etc. You may pass in
// the Social Links object here.
// See: https://vitepress.vuejs.org/config/theme-configs.html#sociallinks
links?: SocialLink[]
// URL for the sponsor page for the member.
sponsor?: string
}
```
## `<VPTeamPage>`
The root component when creating a full team page. It only accepts a single slot. It will style all passed in team related components.
## `<VPTeamPageTitle>`
Adds "title" section of the page. Best use at the very beginning under `<VPTeamPage>`. It accepts `#title` and `#lead` slot.
```html
<VPTeamPage>
<VPTeamPageTitle>
<template #title>
Our Team
</template>
<template #lead>
The development of VitePress is guided by an international
team, some of whom have chosen to be featured below.
</template>
</VPTeamPageTitle>
</VPTeamPage>
```
## `<VPTeamPageSection>`
Creates a "section" with in team page. It accepts `#title`, `#lead`, and `#members` slot. You may add as many sections as you like inside `<VPTeamPage>`.
```html
<VPTeamPage>
...
<VPTeamPageSection>
<template #title>Partners</template>
<template #lead>Lorem ipsum...</template>
<template #members>
<VPTeamMembers :members="data" />
</template>
</VPTeamPageSection>
</VPTeamPage>
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

@ -1,6 +1,6 @@
{ {
"name": "vitepress", "name": "vitepress",
"version": "1.0.0-alpha.1", "version": "1.0.0-alpha.4",
"description": "Vite & Vue powered static site generator", "description": "Vite & Vue powered static site generator",
"type": "module", "type": "module",
"packageManager": "pnpm@7.1.7", "packageManager": "pnpm@7.1.7",
@ -74,6 +74,7 @@
"@docsearch/css": "^3.0.0", "@docsearch/css": "^3.0.0",
"@docsearch/js": "^3.0.0", "@docsearch/js": "^3.0.0",
"@vitejs/plugin-vue": "^2.3.2", "@vitejs/plugin-vue": "^2.3.2",
"@vue/devtools-api": "^6.1.4",
"@vueuse/core": "^8.5.0", "@vueuse/core": "^8.5.0",
"body-scroll-lock": "^4.0.0-beta.0", "body-scroll-lock": "^4.0.0-beta.0",
"shiki": "^0.10.1", "shiki": "^0.10.1",
@ -138,7 +139,7 @@
"sirv": "^1.0.12", "sirv": "^1.0.12",
"supports-color": "^9.2.2", "supports-color": "^9.2.2",
"typescript": "^4.7.2", "typescript": "^4.7.2",
"vitest": "^0.10.4" "vitest": "^0.14.2"
}, },
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {

@ -26,6 +26,7 @@ importers:
'@types/polka': ^0.5.3 '@types/polka': ^0.5.3
'@types/prompts': ^2.0.14 '@types/prompts': ^2.0.14
'@vitejs/plugin-vue': ^2.3.2 '@vitejs/plugin-vue': ^2.3.2
'@vue/devtools-api': ^6.1.4
'@vueuse/core': ^8.5.0 '@vueuse/core': ^8.5.0
body-scroll-lock: ^4.0.0-beta.0 body-scroll-lock: ^4.0.0-beta.0
chokidar: ^3.5.1 chokidar: ^3.5.1
@ -68,12 +69,13 @@ importers:
supports-color: ^9.2.2 supports-color: ^9.2.2
typescript: ^4.7.2 typescript: ^4.7.2
vite: ^2.9.7 vite: ^2.9.7
vitest: ^0.10.4 vitest: ^0.14.2
vue: ^3.2.33 vue: ^3.2.33
dependencies: dependencies:
'@docsearch/css': 3.1.0 '@docsearch/css': 3.1.0
'@docsearch/js': 3.1.0 '@docsearch/js': 3.1.0
'@vitejs/plugin-vue': 2.3.3_vite@2.9.9+vue@3.2.33 '@vitejs/plugin-vue': 2.3.3_vite@2.9.9+vue@3.2.33
'@vue/devtools-api': 6.1.4
'@vueuse/core': 8.5.0_vue@3.2.33 '@vueuse/core': 8.5.0_vue@3.2.33
body-scroll-lock: 4.0.0-beta.0 body-scroll-lock: 4.0.0-beta.0
shiki: 0.10.1 shiki: 0.10.1
@ -137,7 +139,7 @@ importers:
sirv: 1.0.17 sirv: 1.0.17
supports-color: 9.2.2 supports-color: 9.2.2
typescript: 4.7.2 typescript: 4.7.2
vitest: 0.10.5 vitest: 0.14.2_supports-color@9.2.2
docs: docs:
specifiers: {} specifiers: {}
@ -722,6 +724,10 @@ packages:
'@vue/shared': 3.2.33 '@vue/shared': 3.2.33
dev: false dev: false
/@vue/devtools-api/6.1.4:
resolution: {integrity: sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==}
dev: false
/@vue/reactivity-transform/3.2.33: /@vue/reactivity-transform/3.2.33:
resolution: {integrity: sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==} resolution: {integrity: sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==}
dependencies: dependencies:
@ -1397,6 +1403,19 @@ packages:
supports-color: 9.2.2 supports-color: 9.2.2
dev: true dev: true
/debug/4.3.4_supports-color@9.2.2:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
supports-color: 9.2.2
dev: true
/decamelize-keys/1.1.0: /decamelize-keys/1.1.0:
resolution: {integrity: sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=} resolution: {integrity: sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1510,6 +1529,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-android-64/0.14.43:
resolution: {integrity: sha512-kqFXAS72K6cNrB6RiM7YJ5lNvmWRDSlpi7ZuRZ1hu1S3w0zlwcoCxWAyM23LQUyZSs1PbjHgdbbfYAN8IGh6xg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-android-arm64/0.14.3: /esbuild-android-arm64/0.14.3:
@ -1526,6 +1555,16 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-android-arm64/0.14.43:
resolution: {integrity: sha512-bKS2BBFh+7XZY9rpjiHGRNA7LvWYbZWP87pLehggTG7tTaCDvj8qQGOU/OZSjCSKDYbgY7Q+oDw8RlYQ2Jt2BA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-darwin-64/0.14.3: /esbuild-darwin-64/0.14.3:
@ -1542,6 +1581,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-darwin-64/0.14.43:
resolution: {integrity: sha512-/3PSilx011ttoieRGkSZ0XV8zjBf2C9enV4ScMMbCT4dpx0mFhMOpFnCHkOK0pWGB8LklykFyHrWk2z6DENVUg==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-darwin-arm64/0.14.3: /esbuild-darwin-arm64/0.14.3:
@ -1558,6 +1607,16 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-darwin-arm64/0.14.43:
resolution: {integrity: sha512-1HyFUKs8DMCBOvw1Qxpr5Vv/ThNcVIFb5xgXWK3pyT40WPvgYIiRTwJCvNs4l8i5qWF8/CK5bQxJVDjQvtv0Yw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-freebsd-64/0.14.3: /esbuild-freebsd-64/0.14.3:
@ -1574,6 +1633,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-freebsd-64/0.14.43:
resolution: {integrity: sha512-FNWc05TPHYgaXjbPZO5/rJKSBslfG6BeMSs8GhwnqAKP56eEhvmzwnIz1QcC9cRVyO+IKqWNfmHFkCa1WJTULA==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-freebsd-arm64/0.14.3: /esbuild-freebsd-arm64/0.14.3:
@ -1590,6 +1659,16 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-freebsd-arm64/0.14.43:
resolution: {integrity: sha512-amrYopclz3VohqisOPR6hA3GOWA3LZC1WDLnp21RhNmoERmJ/vLnOpnrG2P/Zao+/erKTCUqmrCIPVtj58DRoA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-32/0.14.3: /esbuild-linux-32/0.14.3:
@ -1606,6 +1685,16 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-32/0.14.43:
resolution: {integrity: sha512-KoxoEra+9O3AKVvgDFvDkiuddCds6q71owSQEYwjtqRV7RwbPzKxJa6+uyzUulHcyGVq0g15K0oKG5CFBcvYDw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-64/0.14.3: /esbuild-linux-64/0.14.3:
@ -1622,6 +1711,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-64/0.14.43:
resolution: {integrity: sha512-EwINwGMyiJMgBby5/SbMqKcUhS5AYAZ2CpEBzSowsJPNBJEdhkCTtEjk757TN/wxgbu3QklqDM6KghY660QCUw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-arm/0.14.3: /esbuild-linux-arm/0.14.3:
@ -1638,6 +1737,16 @@ packages:
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-arm/0.14.43:
resolution: {integrity: sha512-e6YzQUoDxxtyamuF12eVzzRC7bbEFSZohJ6igQB9tBqnNmIQY3fI6Cns3z2wxtbZ3f2o6idkD2fQnlvs2902Dg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-arm64/0.14.3: /esbuild-linux-arm64/0.14.3:
@ -1654,6 +1763,16 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-arm64/0.14.43:
resolution: {integrity: sha512-UlSpjMWllAc70zYbHxWuDS3FJytyuR/gHJYBr8BICcTNb/TSOYVBg6U7b3jZ3mILTrgzwJUHwhEwK18FZDouUQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-mips64le/0.14.3: /esbuild-linux-mips64le/0.14.3:
@ -1670,6 +1789,16 @@ packages:
cpu: [mips64el] cpu: [mips64el]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-mips64le/0.14.43:
resolution: {integrity: sha512-f+v8cInPEL1/SDP//CfSYzcDNgE4CY3xgDV81DWm3KAPWzhvxARrKxB1Pstf5mB56yAslJDxu7ryBUPX207EZA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-ppc64le/0.14.3: /esbuild-linux-ppc64le/0.14.3:
@ -1686,6 +1815,16 @@ packages:
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-ppc64le/0.14.43:
resolution: {integrity: sha512-5wZYMDGAL/K2pqkdIsW+I4IR41kyfHr/QshJcNpUfK3RjB3VQcPWOaZmc+74rm4ZjVirYrtz+jWw0SgxtxRanA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-riscv64/0.14.39: /esbuild-linux-riscv64/0.14.39:
@ -1694,6 +1833,16 @@ packages:
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-riscv64/0.14.43:
resolution: {integrity: sha512-lYcAOUxp85hC7lSjycJUVSmj4/9oEfSyXjb/ua9bNl8afonaduuqtw7hvKMoKuYnVwOCDw4RSfKpcnIRDWq+Bw==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-linux-s390x/0.14.39: /esbuild-linux-s390x/0.14.39:
@ -1702,6 +1851,16 @@ packages:
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-linux-s390x/0.14.43:
resolution: {integrity: sha512-27e43ZhHvhFE4nM7HqtUbMRu37I/4eNSUbb8FGZWszV+uLzMIsHDwLoBiJmw7G9N+hrehNPeQ4F5Ujad0DrUKQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-netbsd-64/0.14.3: /esbuild-netbsd-64/0.14.3:
@ -1718,6 +1877,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [netbsd] os: [netbsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-netbsd-64/0.14.43:
resolution: {integrity: sha512-2mH4QF6hHBn5zzAfxEI/2eBC0mspVsZ6UVo821LpAJKMvLJPBk3XJO5xwg7paDqSqpl7p6IRrAenW999AEfJhQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-openbsd-64/0.14.3: /esbuild-openbsd-64/0.14.3:
@ -1734,6 +1903,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [openbsd] os: [openbsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-openbsd-64/0.14.43:
resolution: {integrity: sha512-ZhQpiZjvqCqO8jKdGp9+8k9E/EHSA+zIWOg+grwZasI9RoblqJ1QiZqqi7jfd6ZrrG1UFBNGe4m0NFxCFbMVbg==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-sunos-64/0.14.3: /esbuild-sunos-64/0.14.3:
@ -1750,6 +1929,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [sunos] os: [sunos]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-sunos-64/0.14.43:
resolution: {integrity: sha512-DgxSi9DaHReL9gYuul2rrQCAapgnCJkh3LSHPKsY26zytYppG0HgkgVF80zjIlvEsUbGBP/GHQzBtrezj/Zq1Q==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-windows-32/0.14.3: /esbuild-windows-32/0.14.3:
@ -1766,6 +1955,16 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-windows-32/0.14.43:
resolution: {integrity: sha512-Ih3+2O5oExiqm0mY6YYE5dR0o8+AspccQ3vIAtRodwFvhuyGLjb0Hbmzun/F3Lw19nuhPMu3sW2fqIJ5xBxByw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-windows-64/0.14.3: /esbuild-windows-64/0.14.3:
@ -1782,6 +1981,16 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-windows-64/0.14.43:
resolution: {integrity: sha512-8NsuNfI8xwFuJbrCuI+aBqNTYkrWErejFO5aYM+yHqyHuL8mmepLS9EPzAzk8rvfaJrhN0+RvKWAcymViHOKEw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild-windows-arm64/0.14.3: /esbuild-windows-arm64/0.14.3:
@ -1798,6 +2007,16 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: false
optional: true
/esbuild-windows-arm64/0.14.43:
resolution: {integrity: sha512-7ZlD7bo++kVRblJEoG+cepljkfP8bfuTPz5fIXzptwnPaFwGS6ahvfoYzY7WCf5v/1nX2X02HDraVItTgbHnKw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true optional: true
/esbuild/0.14.3: /esbuild/0.14.3:
@ -1850,6 +2069,35 @@ packages:
esbuild-windows-32: 0.14.39 esbuild-windows-32: 0.14.39
esbuild-windows-64: 0.14.39 esbuild-windows-64: 0.14.39
esbuild-windows-arm64: 0.14.39 esbuild-windows-arm64: 0.14.39
dev: false
/esbuild/0.14.43:
resolution: {integrity: sha512-Uf94+kQmy/5jsFwKWiQB4hfo/RkM9Dh7b79p8yqd1tshULdr25G2szLz631NoH3s2ujnKEKVD16RmOxvCNKRFA==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
esbuild-android-64: 0.14.43
esbuild-android-arm64: 0.14.43
esbuild-darwin-64: 0.14.43
esbuild-darwin-arm64: 0.14.43
esbuild-freebsd-64: 0.14.43
esbuild-freebsd-arm64: 0.14.43
esbuild-linux-32: 0.14.43
esbuild-linux-64: 0.14.43
esbuild-linux-arm: 0.14.43
esbuild-linux-arm64: 0.14.43
esbuild-linux-mips64le: 0.14.43
esbuild-linux-ppc64le: 0.14.43
esbuild-linux-riscv64: 0.14.43
esbuild-linux-s390x: 0.14.43
esbuild-netbsd-64: 0.14.43
esbuild-openbsd-64: 0.14.43
esbuild-sunos-64: 0.14.43
esbuild-windows-32: 0.14.43
esbuild-windows-64: 0.14.43
esbuild-windows-arm64: 0.14.43
dev: true
/escalade/3.1.1: /escalade/3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@ -1983,7 +2231,7 @@ packages:
dev: true dev: true
/get-func-name/2.0.0: /get-func-name/2.0.0:
resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=} resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
dev: true dev: true
/get-intrinsic/1.1.1: /get-intrinsic/1.1.1:
@ -2099,7 +2347,7 @@ packages:
source-map: 0.6.1 source-map: 0.6.1
wordwrap: 1.0.0 wordwrap: 1.0.0
optionalDependencies: optionalDependencies:
uglify-js: 3.14.2 uglify-js: 3.16.0
dev: true dev: true
/hard-rejection/2.1.0: /hard-rejection/2.1.0:
@ -3229,6 +3477,15 @@ packages:
hasBin: true hasBin: true
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
dev: false
/rollup/2.75.6:
resolution: {integrity: sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==}
engines: {node: '>=10.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/run-parallel/1.2.0: /run-parallel/1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@ -3579,8 +3836,8 @@ packages:
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
dev: true dev: true
/tinyspy/0.3.2: /tinyspy/0.3.3:
resolution: {integrity: sha512-2+40EP4D3sFYy42UkgkFFB+kiX2Tg3URG/lVvAZFfLxgGpnWl5qQJuBw1gaLttq8UOS+2p3C0WrhJnQigLTT2Q==} resolution: {integrity: sha512-gRiUR8fuhUf0W9lzojPf1N1euJYA30ISebSfgca8z76FOvXtVXqd5ojEIaKLWbDQhAaC3ibxZIjqbyi4ybjcTw==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
dev: true dev: true
@ -3652,8 +3909,8 @@ packages:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
dev: true dev: true
/uglify-js/3.14.2: /uglify-js/3.16.0:
resolution: {integrity: sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==} resolution: {integrity: sha512-FEikl6bR30n0T3amyBh3LoiBdqHRy/f4H80+My34HOesOKyHfOsxAPAxOoqC0JUnC1amnO0IwkYC3sko51caSw==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
@ -3696,6 +3953,30 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: true dev: true
/vite/2.9.12:
resolution: {integrity: sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==}
engines: {node: '>=12.2.0'}
hasBin: true
peerDependencies:
less: '*'
sass: '*'
stylus: '*'
peerDependenciesMeta:
less:
optional: true
sass:
optional: true
stylus:
optional: true
dependencies:
esbuild: 0.14.43
postcss: 8.4.14
resolve: 1.22.0
rollup: 2.75.6
optionalDependencies:
fsevents: 2.3.2
dev: true
/vite/2.9.9: /vite/2.9.9:
resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==} resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==}
engines: {node: '>=12.2.0'} engines: {node: '>=12.2.0'}
@ -3718,9 +3999,10 @@ packages:
rollup: 2.60.1 rollup: 2.60.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
dev: false
/vitest/0.10.5: /vitest/0.14.2_supports-color@9.2.2:
resolution: {integrity: sha512-4qXdNbHwAd9YcsztJoVMWUQGcMATVlY9Xd95I3KQ2JJwDLTL97f/jgfGRotqptvNxdlmme5TBY0Gv+l6+JSYvA==} resolution: {integrity: sha512-vXQUl8OUCqHmxKWscMGL+6Xl1pBJmYHZ8N85iNpLGrirAC2vhspu7b73ShRcLonmZT44BYZW+LBAVvn0L4jyVA==}
engines: {node: '>=v14.16.0'} engines: {node: '>=v14.16.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -3741,14 +4023,16 @@ packages:
'@types/chai': 4.3.1 '@types/chai': 4.3.1
'@types/chai-subset': 1.3.3 '@types/chai-subset': 1.3.3
chai: 4.3.6 chai: 4.3.6
debug: 4.3.4_supports-color@9.2.2
local-pkg: 0.4.1 local-pkg: 0.4.1
tinypool: 0.1.3 tinypool: 0.1.3
tinyspy: 0.3.2 tinyspy: 0.3.3
vite: 2.9.9 vite: 2.9.12
transitivePeerDependencies: transitivePeerDependencies:
- less - less
- sass - sass
- stylus - stylus
- supports-color
dev: true dev: true
/vscode-oniguruma/1.6.2: /vscode-oniguruma/1.6.2:

@ -1,7 +1,9 @@
import { copy } from 'fs-extra' import { copy } from 'fs-extra'
import fg from 'fast-glob' import fg from 'fast-glob'
fg.sync('src/shared/**/*.ts').map(async (file) => { fg.sync('src/shared/**/*.ts').forEach(async (file) => {
await copy(file, file.replace(/^src\/shared\//, 'src/node/')) await Promise.all([
await copy(file, file.replace(/^src\/shared\//, 'src/client/')) copy(file, file.replace(/^src\/shared\//, 'src/node/')),
copy(file, file.replace(/^src\/shared\//, 'src/client/'))
])
}) })

@ -33,7 +33,7 @@ async function main() {
message: 'Select release type', message: 'Select release type',
choices: versions choices: versions
}) })
console.log(release, release === 3)
if (release === 3) { if (release === 3) {
targetVersion = ( targetVersion = (
await prompts({ await prompts({

@ -1,86 +0,0 @@
<script setup lang="ts">
import { ref, watch, reactive } from 'vue'
import { useData } from '../data'
const data = useData()
const el = ref<HTMLElement | null>(null)
const open = ref(false)
// FIXME: remove in next Vue release
const tempData = reactive(data)
watch(open, (value) => {
if (!value) {
el.value!.scrollTop = 0
}
})
</script>
<template>
<div class="debug" :class="{ open }" ref="el" @click="open = !open">
<p class="title">Debug</p>
<pre class="block">{{ tempData }}</pre>
</div>
</template>
<style scoped>
.debug {
box-sizing: border-box;
position: fixed;
right: 8px;
bottom: 8px;
z-index: 9999;
border-radius: 4px;
width: 74px;
height: 32px;
color: #eeeeee;
overflow: hidden;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.85);
transition: all 0.15s ease;
}
.debug:hover {
background-color: rgba(0, 0, 0, 0.75);
}
.debug.open {
right: 0;
bottom: 0;
width: 100%;
height: 100%;
margin-top: 0;
border-radius: 0;
padding: 0 0;
overflow: scroll;
}
@media (min-width: 512px) {
.debug.open {
width: 512px;
}
}
.debug.open:hover {
background-color: rgba(0, 0, 0, 0.85);
}
.title {
margin: 0;
padding: 6px 16px 6px;
line-height: 20px;
font-size: 13px;
}
.block {
margin: 2px 0 0;
border-top: 1px solid rgba(255, 255, 255, 0.16);
padding: 8px 16px;
font-family: Hack, monospace;
font-size: 13px;
}
.block + .block {
margin-top: 8px;
}
</style>

@ -1,6 +1,6 @@
import { InjectionKey, Ref, shallowRef, readonly, computed, inject } from 'vue' import { InjectionKey, Ref, shallowRef, readonly, computed, inject } from 'vue'
import { Route } from './router' import { Route } from './router'
import serializedSiteData from '@siteData' import siteData from '@siteData'
import { import {
PageData, PageData,
SiteData, SiteData,
@ -25,17 +25,16 @@ export interface VitePressData<T = any> {
// site data is a singleton // site data is a singleton
export type SiteDataRef<T = any> = Ref<SiteData<T>> export type SiteDataRef<T = any> = Ref<SiteData<T>>
export const siteDataRef: Ref<SiteData> = shallowRef(parse(serializedSiteData)) export const siteDataRef: Ref<SiteData> = shallowRef(
import.meta.env.PROD ? siteData : readonly(siteData)
function parse(data: string): SiteData { )
const parsed = JSON.parse(data)
return (import.meta.env.DEV ? readonly(parsed) : parsed) as SiteData
}
// hmr // hmr
if (import.meta.hot) { if (import.meta.hot) {
import.meta.hot!.accept('/@siteData', (m) => { import.meta.hot.accept('/@siteData', (m) => {
siteDataRef.value = parse(m.default) if (m) {
siteDataRef.value = m.default
}
}) })
} }

@ -0,0 +1,41 @@
import { setupDevtoolsPlugin } from '@vue/devtools-api'
import type { App } from 'vue'
import type { Router } from './router'
import type { VitePressData } from './data'
const COMPONENT_STATE_TYPE = 'VitePress'
export const setupDevtools = (
app: App,
router: Router,
data: VitePressData
): void => {
setupDevtoolsPlugin(
{
// fix recursive reference
app: app as any,
id: 'org.vuejs.vitepress',
label: 'VitePress',
packageName: 'vitepress',
homepage: 'https://vitepress.vuejs.org',
componentStateTypes: [COMPONENT_STATE_TYPE]
},
(api) => {
api.on.inspectComponent((payload) => {
payload.instanceData.state.push({
type: COMPONENT_STATE_TYPE,
key: 'route',
value: router.route,
editable: false
})
payload.instanceData.state.push({
type: COMPONENT_STATE_TYPE,
key: 'data',
value: data,
editable: false
})
})
}
)
}

@ -2,7 +2,6 @@ import {
App, App,
createApp as createClientApp, createApp as createClientApp,
createSSRApp, createSSRApp,
defineAsyncComponent,
h, h,
onMounted, onMounted,
watch watch
@ -46,8 +45,6 @@ const VitePressApp = {
export function createApp() { export function createApp() {
const router = newRouter() const router = newRouter()
handleHMR(router)
const app = newApp() const app = newApp()
app.provide(RouterSymbol, router) app.provide(RouterSymbol, router)
@ -58,12 +55,6 @@ export function createApp() {
// install global components // install global components
app.component('Content', Content) app.component('Content', Content)
app.component('ClientOnly', ClientOnly) app.component('ClientOnly', ClientOnly)
app.component(
'Debug',
import.meta.env.PROD
? () => null
: defineAsyncComponent(() => import('./components/Debug.vue'))
)
// expose $frontmatter // expose $frontmatter
Object.defineProperty(app.config.globalProperties, '$frontmatter', { Object.defineProperty(app.config.globalProperties, '$frontmatter', {
@ -80,6 +71,13 @@ export function createApp() {
}) })
} }
// setup devtools in dev mode
if (import.meta.env.DEV || __VUE_PROD_DEVTOOLS__) {
import('./devtools').then(({ setupDevtools }) =>
setupDevtools(app, router, data)
)
}
return { app, router, data } return { app, router, data }
} }
@ -115,25 +113,6 @@ function newRouter(): Router {
}, NotFound) }, NotFound)
} }
function handleHMR(router: Router): void {
// update route.data on HMR updates of active page
if (import.meta.hot) {
// hot reload pageData
import.meta.hot!.on('vitepress:pageData', (payload) => {
if (shouldHotReload(payload)) {
router.route.data = payload.pageData
}
})
}
}
function shouldHotReload(payload: any): boolean {
const payloadPath = payload.path.replace(/(\bindex)?\.md$/, '')
const locationPath = location.pathname.replace(/(\bindex)?\.html$/, '')
return payloadPath === locationPath
}
if (inBrowser) { if (inBrowser) {
const { app, router, data } = createApp() const { app, router, data } = createApp()

@ -1,6 +1,7 @@
import { reactive, inject, markRaw, nextTick, readonly } from 'vue' import { reactive, inject, markRaw, nextTick, readonly } from 'vue'
import type { Component, InjectionKey } from 'vue' import type { Component, InjectionKey } from 'vue'
import { PageData } from '../shared' import { notFoundPageData } from '../shared'
import type { PageData, PageDataPayload } from '../shared'
import { inBrowser, withBase } from './utils' import { inBrowser, withBase } from './utils'
import { siteDataRef } from './data' import { siteDataRef } from './data'
@ -21,15 +22,6 @@ export const RouterSymbol: InjectionKey<Router> = Symbol()
// matter and is only passed to support same-host hrefs. // matter and is only passed to support same-host hrefs.
const fakeHost = `http://a.com` const fakeHost = `http://a.com`
const notFoundPageData: PageData = {
relativePath: '',
title: '404',
description: 'Not Found',
headers: [],
frontmatter: {},
lastUpdated: 0
}
const getDefaultRoute = (): Route => ({ const getDefaultRoute = (): Route => ({
path: '/', path: '/',
component: null, component: null,
@ -37,7 +29,7 @@ const getDefaultRoute = (): Route => ({
}) })
interface PageModule { interface PageModule {
__pageData: string __pageData: PageData
default: Component default: Component
} }
@ -85,8 +77,8 @@ export function createRouter(
route.path = inBrowser ? pendingPath : withBase(pendingPath) route.path = inBrowser ? pendingPath : withBase(pendingPath)
route.component = markRaw(comp) route.component = markRaw(comp)
route.data = import.meta.env.PROD route.data = import.meta.env.PROD
? markRaw(JSON.parse(__pageData)) ? markRaw(__pageData)
: (readonly(JSON.parse(__pageData)) as PageData) : (readonly(__pageData) as PageData)
if (inBrowser) { if (inBrowser) {
nextTick(() => { nextTick(() => {
@ -109,7 +101,7 @@ export function createRouter(
} }
} }
} catch (err: any) { } catch (err: any) {
if (!err.message.match(/fetch/)) { if (!err.message.match(/fetch/) && !href.match(/^[\\/]404\.html$/)) {
console.error(err) console.error(err)
} }
@ -182,6 +174,8 @@ export function createRouter(
}) })
} }
handleHMR(route)
return { return {
route, route,
go go
@ -239,3 +233,21 @@ function scrollTo(el: HTMLElement, hash: string, smooth = false) {
} }
} }
} }
function handleHMR(route: Route): void {
// update route.data on HMR updates of active page
if (import.meta.hot) {
// hot reload pageData
import.meta.hot!.on('vitepress:pageData', (payload: PageDataPayload) => {
if (shouldHotReload(payload)) {
route.data = payload.pageData
}
})
}
}
function shouldHotReload(payload: PageDataPayload): boolean {
const payloadPath = payload.path.replace(/(\bindex)?\.md$/, '')
const locationPath = location.pathname.replace(/(\bindex)?\.html$/, '')
return payloadPath === locationPath
}

@ -24,6 +24,3 @@ export { inBrowser, withBase } from './app/utils'
// components // components
export { Content } from './app/components/Content' export { Content } from './app/components/Content'
import _Debug from './app/components/Debug.vue'
export const Debug = _Debug as import('vue').ComponentOptions

@ -1,6 +1,7 @@
declare const __VP_HASH_MAP__: Record<string, string> declare const __VP_HASH_MAP__: Record<string, string>
declare const __ALGOLIA__: boolean declare const __ALGOLIA__: boolean
declare const __CARBON__: boolean declare const __CARBON__: boolean
declare const __VUE_PROD_DEVTOOLS__: boolean
declare module '*.vue' { declare module '*.vue' {
import { ComponentOptions } from 'vue' import { ComponentOptions } from 'vue'
@ -9,7 +10,8 @@ declare module '*.vue' {
} }
declare module '@siteData' { declare module '@siteData' {
const data: string import type { SiteData } from './shared'
const data: SiteData
export default data export default data
} }
@ -18,12 +20,3 @@ declare module '@docsearch/js' {
function docsearch<T = any>(props: T): void function docsearch<T = any>(props: T): void
export default docsearch export default docsearch
} }
declare module '@docsearch/react/dist/esm/types' {
export type DocSearchHit = any
}
declare module '@docsearch/css' {
const css: string
export default css
}

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { provide } from 'vue' import { provide, watch } from 'vue'
import { useRoute } from 'vitepress'
import { useSidebar, useCloseSidebarOnEscape } from './composables/sidebar' import { useSidebar, useCloseSidebarOnEscape } from './composables/sidebar'
import VPSkipLink from './components/VPSkipLink.vue' import VPSkipLink from './components/VPSkipLink.vue'
import VPBackdrop from './components/VPBackdrop.vue' import VPBackdrop from './components/VPBackdrop.vue'
@ -15,6 +16,9 @@ const {
close: closeSidebar close: closeSidebar
} = useSidebar() } = useSidebar()
const route = useRoute()
watch(() => route.path, closeSidebar)
useCloseSidebarOnEscape(isSidebarOpen, closeSidebar) useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
provide('close-sidebar', closeSidebar) provide('close-sidebar', closeSidebar)
@ -22,9 +26,17 @@ provide('close-sidebar', closeSidebar)
<template> <template>
<div class="Layout"> <div class="Layout">
<slot name="layout-top" />
<VPSkipLink /> <VPSkipLink />
<VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" /> <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" />
<VPNav /> <VPNav>
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
<template #nav-bar-content-before><slot name="nav-bar-content-before" /></template>
<template #nav-bar-content-after><slot name="nav-bar-content-after" /></template>
<template #nav-screen-content-before><slot name="nav-screen-content-before" /></template>
<template #nav-screen-content-after><slot name="nav-screen-content-after" /></template>
</VPNav>
<VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar" /> <VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar" />
<VPSidebar :open="isSidebarOpen" /> <VPSidebar :open="isSidebarOpen" />
@ -34,6 +46,9 @@ provide('close-sidebar', closeSidebar)
<template #home-features-before><slot name="home-features-before" /></template> <template #home-features-before><slot name="home-features-before" /></template>
<template #home-features-after><slot name="home-features-after" /></template> <template #home-features-after><slot name="home-features-after" /></template>
<template #doc-before><slot name="doc-before" /></template>
<template #doc-after><slot name="doc-after" /></template>
<template #aside-top><slot name="aside-top" /></template> <template #aside-top><slot name="aside-top" /></template>
<template #aside-bottom><slot name="aside-bottom" /></template> <template #aside-bottom><slot name="aside-bottom" /></template>
<template #aside-outline-before><slot name="aside-outline-before" /></template> <template #aside-outline-before><slot name="aside-outline-before" /></template>
@ -43,6 +58,7 @@ provide('close-sidebar', closeSidebar)
</VPContent> </VPContent>
<VPFooter /> <VPFooter />
<slot name="layout-bottom" />
</div> </div>
</template> </template>

@ -1,9 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import docsearch from '@docsearch/js' import docsearch from '@docsearch/js'
import { DocSearchHit } from '@docsearch/react/dist/esm/types'
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useRouter, useRoute, useData } from 'vitepress' import { useRouter, useRoute, useData } from 'vitepress'
import { DefaultTheme } from '../config'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@ -52,7 +51,7 @@ function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
} }
}, },
transformItems(items: DocSearchHit[]) { transformItems(items: any[]) {
return items.map((item) => { return items.map((item) => {
return Object.assign({}, item, { return Object.assign({}, item, {
url: getRelativePath(item.url) url: getRelativePath(item.url)
@ -60,7 +59,7 @@ function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
}) })
}, },
hitComponent({ hit, children }: { hit: DocSearchHit, children: any }) { hitComponent({ hit, children }: { hit: any; children: any }) {
const relativeHit = hit.url.startsWith('http') const relativeHit = hit.url.startsWith('http')
? getRelativePath(hit.url as string) ? getRelativePath(hit.url as string)
: hit.url : hit.url

@ -1,5 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { normalizeLink } from '../support/utils'
import { EXTERNAL_URL_RE } from '../../shared'
const props = defineProps<{ const props = defineProps<{
tag?: string tag?: string
@ -14,7 +16,7 @@ const classes = computed(() => [
props.theme ?? 'brand' props.theme ?? 'brand'
]) ])
const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href)) const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href))
const component = computed(() => { const component = computed(() => {
if (props.tag) { if (props.tag) {
@ -30,7 +32,7 @@ const component = computed(() => {
:is="component" :is="component"
class="VPButton" class="VPButton"
:class="classes" :class="classes"
:href="href" :href="href ? normalizeLink(href) : undefined"
:target="isExternal ? '_blank' : undefined" :target="isExternal ? '_blank' : undefined"
:rel="isExternal ? 'noopener noreferrer' : undefined" :rel="isExternal ? 'noopener noreferrer' : undefined"
> >

@ -35,6 +35,9 @@ useCopyCode()
</VPHome> </VPHome>
<VPDoc v-else> <VPDoc v-else>
<template #doc-before><slot name="doc-before" /></template>
<template #doc-after><slot name="doc-after" /></template>
<template #aside-top><slot name="aside-top" /></template> <template #aside-top><slot name="aside-top" /></template>
<template #aside-outline-before><slot name="aside-outline-before" /></template> <template #aside-outline-before><slot name="aside-outline-before" /></template>
<template #aside-outline-after><slot name="aside-outline-after" /></template> <template #aside-outline-after><slot name="aside-outline-after" /></template>

@ -1,16 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useData } from 'vitepress' import { useRoute } from 'vitepress'
import { useSidebar } from '../composables/sidebar' import { useSidebar } from '../composables/sidebar'
import VPDocAside from './VPDocAside.vue' import VPDocAside from './VPDocAside.vue'
import VPDocFooter from './VPDocFooter.vue' import VPDocFooter from './VPDocFooter.vue'
const { page } = useData() const route = useRoute()
const { hasSidebar } = useSidebar() const { hasSidebar } = useSidebar()
const pageName = computed(() => { const pageName = computed(() =>
return page.value.relativePath.slice(0, page.value.relativePath.indexOf('/')) route.path.replace(/[./]+/g, '_').replace(/_html$/, '')
}) )
</script> </script>
<template> <template>
@ -34,11 +34,15 @@ const pageName = computed(() => {
<div class="content"> <div class="content">
<div class="content-container"> <div class="content-container">
<slot name="doc-before" />
<main class="main"> <main class="main">
<Content class="vp-doc" :class="pageName" /> <Content class="vp-doc" :class="pageName" />
</main> </main>
<VPDocFooter /> <VPDocFooter />
<slot name="doc-after" />
</div> </div>
</div> </div>
</div> </div>
@ -168,24 +172,4 @@ const pageName = computed(() => {
margin: 0 auto; margin: 0 auto;
max-width: 688px; max-width: 688px;
} }
.edit-link {
margin: 0 0 32px;
}
.edit-link .vt-link {
font-size: 14px;
color: var(--vt-c-brand);
font-weight: 500;
}
.vt-icon {
width: 18px;
height: 18px;
color: var(--vt-c-brand);
display: inline-block;
margin-right: 8px;
position: relative;
top: -1px;
}
</style> </style>

@ -7,7 +7,7 @@ import {
useActiveAnchor useActiveAnchor
} from '../composables/outline' } from '../composables/outline'
const { page, frontmatter } = useData() const { page, frontmatter, theme } = useData()
const { hasOutline } = useOutline() const { hasOutline } = useOutline()
@ -22,7 +22,9 @@ const resolvedHeaders = computed(() => {
function handleClick({ target: el }: Event) { function handleClick({ target: el }: Event) {
const id = '#' + (el as HTMLAnchorElement).href!.split('#')[1] const id = '#' + (el as HTMLAnchorElement).href!.split('#')[1]
const heading = document.querySelector(id) as HTMLAnchorElement const heading = document.querySelector(
decodeURIComponent(id)
) as HTMLAnchorElement
heading?.focus() heading?.focus()
} }
</script> </script>
@ -32,7 +34,9 @@ function handleClick({ target: el }: Event) {
<div class="content"> <div class="content">
<div class="outline-marker" ref="marker" /> <div class="outline-marker" ref="marker" />
<div class="outline-title">On this page</div> <div class="outline-title">
{{ theme.outlineTitle || 'On this page' }}
</div>
<nav aria-labelledby="doc-outline-aria-label"> <nav aria-labelledby="doc-outline-aria-label">
<span class="visually-hidden" id="doc-outline-aria-label"> <span class="visually-hidden" id="doc-outline-aria-label">

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import { normalizeLink } from '../support/utils' import { normalizeLink } from '../support/utils'
import { useEditLink } from '../composables/edit-link' import { useEditLink } from '../composables/edit-link'
@ -11,10 +12,14 @@ const { theme, page, frontmatter } = useData()
const editLink = useEditLink() const editLink = useEditLink()
const control = usePrevNext() const control = usePrevNext()
const hasLastUpdated = computed(() => {
return page.value.lastUpdated && frontmatter.value.lastUpdated !== false
})
</script> </script>
<template> <template>
<footer v-if="control.prev || control.next" class="VPDocFooter"> <footer class="VPDocFooter">
<div class="edit-info"> <div class="edit-info">
<div v-if="theme.editLink && frontmatter.editLink !== false" class="edit-link"> <div v-if="theme.editLink && frontmatter.editLink !== false" class="edit-link">
<VPLink class="edit-link-button" :href="editLink.url" :no-icon="true"> <VPLink class="edit-link-button" :href="editLink.url" :no-icon="true">
@ -23,21 +28,21 @@ const control = usePrevNext()
</VPLink> </VPLink>
</div> </div>
<div v-if="page.lastUpdated" class="last-updated"> <div v-if="hasLastUpdated" class="last-updated">
<VPDocFooterLastUpdated /> <VPDocFooterLastUpdated />
</div> </div>
</div> </div>
<div class="prev-next"> <div v-if="control.prev || control.next" class="prev-next">
<div class="pager"> <div class="pager">
<a v-if="control.prev" class="pager-link prev" :href="normalizeLink(control.prev.link)"> <a v-if="control.prev" class="pager-link prev" :href="normalizeLink(control.prev.link)">
<span class="desc">Previous page</span> <span class="desc">{{ theme.docFooter?.prev ?? 'Previous page' }}</span>
<span class="title">{{ control.prev.text }} </span> <span class="title">{{ control.prev.text }} </span>
</a> </a>
</div> </div>
<div class="pager" :class="{ 'has-prev': control.prev }"> <div class="pager" :class="{ 'has-prev': control.prev }">
<a v-if="control.next" class="pager-link next" :href="normalizeLink(control.next.link)"> <a v-if="control.next" class="pager-link next" :href="normalizeLink(control.next.link)">
<span class="desc">Next page</span> <span class="desc">{{ theme.docFooter?.next ?? 'Next page' }}</span>
<span class="title">{{ control.next.text }}</span> <span class="title">{{ control.next.text }}</span>
</a> </a>
</div> </div>
@ -58,21 +63,18 @@ const control = usePrevNext()
.edit-info { .edit-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: baseline; align-items: center;
padding-bottom: 14px; padding-bottom: 14px;
} }
} }
.edit-link {
line-height: 32px;
font-size: 14px;
font-weight: 500;
}
.edit-link-button { .edit-link-button {
display: flex; display: flex;
align-items: center; align-items: center;
border: 0; border: 0;
line-height: 32px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-brand); color: var(--vp-c-brand);
transition: color 0.25s; transition: color 0.25s;
} }

@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watchEffect, onMounted } from 'vue' import { ref, computed, watchEffect, onMounted } from 'vue'
import { useData } from 'vitepress' import { useData } from 'vitepress'
const { theme, page } = useData() const { theme, page } = useData()
const date = new Date(page.value.lastUpdated!) const date = computed(() => new Date(page.value.lastUpdated!))
const isoDatetime = date.toISOString() const isoDatetime = computed(() => date.value.toISOString())
const datetime = ref('') const datetime = ref('')
// set time on mounted hook because the locale string might be different // set time on mounted hook because the locale string might be different
@ -13,7 +13,7 @@ const datetime = ref('')
// calculated at build time // calculated at build time
onMounted(() => { onMounted(() => {
watchEffect(() => { watchEffect(() => {
datetime.value = date.toLocaleString(window.navigator.language) datetime.value = date.value.toLocaleString(window.navigator.language)
}) })
}) })
</script> </script>

@ -7,16 +7,16 @@ defineProps<{
</script> </script>
<template> <template>
<article class="VPBox"> <article class="VPFeature">
<div v-if="icon" class="icon">{{ icon }}</div> <div v-if="icon" class="icon">{{ icon }}</div>
<h1 class="title">{{ title }}</h1> <h2 class="title">{{ title }}</h2>
<p class="details">{{ details }}</p> <p class="details">{{ details }}</p>
</article> </article>
</template> </template>
<style scoped> <style scoped>
.VPBox { .VPFeature {
border: 1px solid var(--vp-c-divider-light); border: 1px solid var(--vp-c-bg-soft);
border-radius: 12px; border-radius: 12px;
padding: 24px; padding: 24px;
height: 100%; height: 100%;

@ -0,0 +1,107 @@
<script setup lang="ts">
import { computed } from 'vue'
import VPFeature from './VPFeature.vue'
export interface Feature {
icon?: string
title: string
details: string
}
const props = defineProps<{
features: Feature[]
}>()
const grid = computed(() => {
const length = props.features.length
if (!length) {
return
} else if (length === 2) {
return 'grid-2'
} else if (length === 3) {
return 'grid-3'
} else if (length % 3 === 0) {
return 'grid-6'
} else if (length % 2 === 0) {
return 'grid-4'
}
})
</script>
<template>
<div v-if="features" class="VPFeatures">
<div class="container">
<div class="items">
<div v-for="feature in features" :key="feature.title" class="item" :class="[grid]">
<VPFeature
:icon="feature.icon"
:title="feature.title"
:details="feature.details"
/>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.VPFeatures {
position: relative;
padding: 0 24px;
}
@media (min-width: 640px) {
.VPFeatures {
padding: 0 48px;
}
}
@media (min-width: 960px) {
.VPFeatures {
padding: 0 64px;
}
}
.container {
margin: 0 auto;
max-width: 1152px;
}
.items {
display: flex;
flex-wrap: wrap;
margin: -8px;
}
.item {
padding: 8px;
width: 100%;
}
@media (min-width: 640px) {
.item.grid-2,
.item.grid-4,
.item.grid-6 {
width: calc(100% / 2);
}
}
@media (min-width: 768px) {
.item.grid-2,
.item.grid-4 {
width: calc(100% / 2);
}
.item.grid-3,
.item.grid-6 {
width: calc(100% / 3);
}
}
@media (min-width: 960px) {
.item.grid-4 {
width: calc(100% / 4);
}
}
</style>

@ -72,6 +72,14 @@ function onBlur() {
fill: var(--vp-c-text-2); fill: var(--vp-c-text-2);
} }
.VPFlyout.active .text {
color: var(--vp-c-brand);
}
.VPFlyout.active:hover .text {
color: var(--vp-c-brand-dark);
}
.VPFlyout:hover .menu, .VPFlyout:hover .menu,
.button[aria-expanded="true"] + .menu { .button[aria-expanded="true"] + .menu {
opacity: 1; opacity: 1;
@ -98,7 +106,7 @@ function onBlur() {
display: flex; display: flex;
align-items: center; align-items: center;
line-height: var(--vp-nav-height-mobile); line-height: var(--vp-nav-height-mobile);
font-size: 13px; font-size: 14px;
font-weight: 500; font-weight: 500;
color: var(--vp-c-text-1); color: var(--vp-c-text-1);
transition: color 0.25s; transition: color 0.25s;

@ -1,5 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import VPButton from './VPButton.vue' import VPButton from './VPButton.vue'
import VPImage from './VPImage.vue'
export interface HeroAction { export interface HeroAction {
theme?: 'brand' | 'alt' theme?: 'brand' | 'alt'
@ -7,16 +9,11 @@ export interface HeroAction {
link: string link: string
} }
export interface Image {
src: string
alt?: string
}
defineProps<{ defineProps<{
name?: string name?: string
text: string text: string
tagline?: string tagline?: string
image?: Image image?: DefaultTheme.ThemeableImage
actions?: HeroAction[] actions?: HeroAction[]
}>() }>()
</script> </script>
@ -25,8 +22,10 @@ defineProps<{
<div class="VPHero" :class="{ 'has-image': image }"> <div class="VPHero" :class="{ 'has-image': image }">
<div class="container"> <div class="container">
<div class="main"> <div class="main">
<p v-if="name" class="name"><span class="clip">{{ name }}</span></p> <h1 v-if="name" class="name">
<h1 v-if="text" class="text">{{ text }}</h1> <span class="clip">{{ name }}</span>
</h1>
<p v-if="text" class="text">{{ text }}</p>
<p v-if="tagline" class="tagline">{{ tagline }}</p> <p v-if="tagline" class="tagline">{{ tagline }}</p>
<div v-if="actions" class="actions"> <div v-if="actions" class="actions">
@ -45,7 +44,7 @@ defineProps<{
<div v-if="image" class="image"> <div v-if="image" class="image">
<div class="image-container"> <div class="image-container">
<div class="image-bg" /> <div class="image-bg" />
<img class="image-src" :src="image.src" :alt="image.alt"> <VPImage class="image-src" :image="image" />
</div> </div>
</div> </div>
</div> </div>
@ -64,11 +63,17 @@ defineProps<{
} }
} }
@media (min-width: 960px) {
.VPHero {
padding: calc(var(--vp-nav-height) + 80px) 64px 64px;
}
}
.container { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: 0 auto; margin: 0 auto;
max-width: 960px; max-width: 1152px;
} }
@media (min-width: 960px) { @media (min-width: 960px) {
@ -98,13 +103,11 @@ defineProps<{
@media (min-width: 960px) { @media (min-width: 960px) {
.main { .main {
order: 1; order: 1;
width: calc((100% / 3) * 2);
} }
}
@media (min-width: 960px) { .VPHero.has-image .main {
.main { max-width: 592px;
width: 100%;
max-width: calc((100% / 3) * 2);
} }
} }
@ -115,6 +118,7 @@ defineProps<{
line-height: 40px; line-height: 40px;
font-size: 32px; font-size: 32px;
font-weight: 700; font-weight: 700;
white-space: pre-wrap;
} }
.VPHero.has-image .name, .VPHero.has-image .name,
@ -156,11 +160,12 @@ defineProps<{
} }
.tagline { .tagline {
padding-top: 16px; padding-top: 8px;
max-width: 392px; max-width: 392px;
line-height: 28px; line-height: 28px;
font-size: 18px; font-size: 18px;
font-weight: 500; font-weight: 500;
white-space: pre-wrap;
color: var(--vp-c-text-2); color: var(--vp-c-text-2);
} }
@ -170,7 +175,7 @@ defineProps<{
@media (min-width: 640px) { @media (min-width: 640px) {
.tagline { .tagline {
padding-top: 24px; padding-top: 12px;
max-width: 576px; max-width: 576px;
line-height: 32px; line-height: 32px;
font-size: 20px; font-size: 20px;
@ -179,7 +184,6 @@ defineProps<{
@media (min-width: 960px) { @media (min-width: 960px) {
.tagline { .tagline {
padding-top: 24px;
line-height: 36px; line-height: 36px;
font-size: 24px; font-size: 24px;
} }
@ -230,9 +234,9 @@ defineProps<{
@media (min-width: 960px) { @media (min-width: 960px) {
.image { .image {
flex-grow: 1;
order: 2; order: 2;
margin: 0; margin: 0;
width: calc(100% / 3);
min-height: 100%; min-height: 100%;
} }
} }
@ -288,7 +292,7 @@ defineProps<{
} }
} }
.image-src { :deep(.image-src) {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -297,13 +301,13 @@ defineProps<{
} }
@media (min-width: 640px) { @media (min-width: 640px) {
.image-src { :deep(.image-src) {
max-width: 256px; max-width: 256px;
} }
} }
@media (min-width: 960px) { @media (min-width: 960px) {
.image-src { :deep(.image-src) {
max-width: 320px; max-width: 320px;
} }
} }

@ -12,6 +12,8 @@ import VPHomeFeatures from './VPHomeFeatures.vue'
<slot name="home-features-before" /> <slot name="home-features-before" />
<VPHomeFeatures /> <VPHomeFeatures />
<slot name="home-features-after" /> <slot name="home-features-after" />
<Content />
</div> </div>
</template> </template>

@ -1,97 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import VPBox from './VPBox.vue' import VPFeatures from './VPFeatures.vue'
const { frontmatter: fm } = useData() const { frontmatter: fm } = useData()
const grid = computed(() => {
const length = fm.value.features?.length
if (!length) {
return
}
if (length === 2) {
return 'grid-2'
} else if (length === 3) {
return 'grid-3'
} else if (length % 3 === 0) {
return 'grid-6'
} else if (length % 2 === 0) {
return 'grid-4'
}
})
</script> </script>
<template> <template>
<div v-if="fm.features" class="VPHomeFeatures"> <VPFeatures
<div class="container"> v-if="fm.features"
<div class="items"> class="VPHomeFeatures"
<div v-for="feature in fm.features" :key="feature.title" class="item" :class="[grid]"> :features="fm.features"
<VPBox />
:icon="feature.icon"
:title="feature.title"
:details="feature.details"
/>
</div>
</div>
</div>
</div>
</template> </template>
<style scoped>
.VPHomeFeatures {
position: relative;
padding: 0 24px;
}
@media (min-width: 640px) {
.VPHomeFeatures {
padding: 0 48px;
}
}
.container {
margin: 0 auto;
max-width: 960px;
}
.items {
display: flex;
flex-wrap: wrap;
margin: -8px;
}
.item {
padding: 8px;
width: 100%;
}
@media (min-width: 640px) {
.item.grid-2,
.item.grid-4,
.item.grid-6 {
width: calc(100% / 2);
}
}
@media (min-width: 768px) {
.item.grid-2,
.item.grid-4 {
width: calc(100% / 2);
}
.item.grid-3,
.item.grid-6 {
width: calc(100% / 3);
}
}
@media (min-width: 960px) {
.item.grid-4 {
width: calc(100% / 4);
}
}
</style>

@ -6,13 +6,13 @@ const { frontmatter: fm } = useData()
</script> </script>
<template> <template>
<div v-if="fm.hero" class="VPHomeHero"> <VPHero
<VPHero v-if="fm.hero"
:name="fm.hero.name" class="VPHomeHero"
:text="fm.hero.text" :name="fm.hero.name"
:tagline="fm.hero.tagline" :text="fm.hero.text"
:image="fm.hero.image" :tagline="fm.hero.tagline"
:actions="fm.hero.actions" :image="fm.hero.image"
/> :actions="fm.hero.actions"
</div> />
</template> </template>

@ -55,7 +55,7 @@ defineProps<{
.container { .container {
margin: 0 auto; margin: 0 auto;
max-width: 960px; max-width: 1152px;
} }
.love { .love {

@ -0,0 +1,38 @@
<script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import { withBase } from 'vitepress'
defineProps<{
image: DefaultTheme.ThemeableImage
}>()
</script>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>
<template>
<template v-if="image">
<img
v-if="typeof image === 'string' || 'src' in image"
class="VPImage"
v-bind="typeof image === 'string' ? $attrs : { ...image, ...$attrs }"
:src="withBase(typeof image === 'string' ? image : image.src)"
/>
<template v-else>
<VPImage class="dark" :image="image.dark" v-bind="$attrs" />
<VPImage class="light" :image="image.light" v-bind="$attrs" />
</template>
</template>
</template>
<style scoped>
html:not(.dark) .VPImage.dark {
display: none;
}
.dark .VPImage.light {
display: none;
}
</style>

@ -2,13 +2,14 @@
import { computed } from 'vue' import { computed } from 'vue'
import { normalizeLink } from '../support/utils' import { normalizeLink } from '../support/utils'
import VPIconExternalLink from './icons/VPIconExternalLink.vue' import VPIconExternalLink from './icons/VPIconExternalLink.vue'
import { EXTERNAL_URL_RE } from '../../shared'
const props = defineProps<{ const props = defineProps<{
href?: string href?: string
noIcon?: boolean noIcon?: boolean
}>() }>()
const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href)) const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href))
</script> </script>
<template> <template>

@ -13,8 +13,16 @@ provide('close-screen', closeScreen)
<template> <template>
<header class="VPNav" :class="{ 'no-sidebar' : !hasSidebar }"> <header class="VPNav" :class="{ 'no-sidebar' : !hasSidebar }">
<VPNavBar :is-screen-open="isScreenOpen" @toggle-screen="toggleScreen" /> <VPNavBar :is-screen-open="isScreenOpen" @toggle-screen="toggleScreen">
<VPNavScreen :open="isScreenOpen" /> <template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
<template #nav-bar-content-before><slot name="nav-bar-content-before" /></template>
<template #nav-bar-content-after><slot name="nav-bar-content-after" /></template>
</VPNavBar>
<VPNavScreen :open="isScreenOpen">
<template #nav-screen-content-before><slot name="nav-screen-content-before" /></template>
<template #nav-screen-content-after><slot name="nav-screen-content-after" /></template>
</VPNavScreen>
</header> </header>
</template> </template>
@ -33,8 +41,13 @@ provide('close-screen', closeScreen)
} }
.VPNav.no-sidebar { .VPNav.no-sidebar {
backdrop-filter: saturate(50%) blur(8px);
-webkit-backdrop-filter: saturate(50%) blur(8px); -webkit-backdrop-filter: saturate(50%) blur(8px);
backdrop-filter: saturate(50%) blur(8px);
background: rgba(255, 255, 255, 0.7);
}
.dark .VPNav.no-sidebar {
background: rgba(36, 36, 36, 0.7);
} }
@supports not (backdrop-filter: saturate(50%) blur(8px)) { @supports not (backdrop-filter: saturate(50%) blur(8px)) {

@ -23,15 +23,20 @@ const { hasSidebar } = useSidebar()
<template> <template>
<div class="VPNavBar" :class="{ 'has-sidebar' : hasSidebar }"> <div class="VPNavBar" :class="{ 'has-sidebar' : hasSidebar }">
<div class="container"> <div class="container">
<VPNavBarTitle /> <VPNavBarTitle>
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
</VPNavBarTitle>
<div class="content"> <div class="content">
<slot name="nav-bar-content-before" />
<VPNavBarSearch class="search" /> <VPNavBarSearch class="search" />
<VPNavBarMenu class="menu" /> <VPNavBarMenu class="menu" />
<VPNavBarTranslations class="translations" /> <VPNavBarTranslations class="translations" />
<VPNavBarAppearance class="appearance" /> <VPNavBarAppearance class="appearance" />
<VPNavBarSocialLinks class="social-links" /> <VPNavBarSocialLinks class="social-links" />
<VPNavBarExtra class="extra" /> <VPNavBarExtra class="extra" />
<slot name="nav-bar-content-after" />
<VPNavBarHamburger <VPNavBarHamburger
class="hamburger" class="hamburger"
:active="isScreenOpen" :active="isScreenOpen"
@ -48,7 +53,6 @@ const { hasSidebar } = useSidebar()
border-bottom: 1px solid var(--vp-c-divider-light); border-bottom: 1px solid var(--vp-c-divider-light);
padding: 0 8px 0 24px; padding: 0 8px 0 24px;
height: var(--vp-nav-height-mobile); height: var(--vp-nav-height-mobile);
background-color: var(--vt-c-bg);
transition: border-color 0.5s, background-color 0.5s; transition: border-color 0.5s, background-color 0.5s;
} }
@ -65,8 +69,15 @@ const { hasSidebar } = useSidebar()
} }
.VPNavBar.has-sidebar .content { .VPNavBar.has-sidebar .content {
backdrop-filter: saturate(50%) blur(8px); margin-right: -32px;
padding-right: 32px;
-webkit-backdrop-filter: saturate(50%) blur(8px); -webkit-backdrop-filter: saturate(50%) blur(8px);
backdrop-filter: saturate(50%) blur(8px);
background: rgba(255, 255, 255, 0.7);
}
.dark .VPNavBar.has-sidebar .content {
background: rgba(36, 36, 36, 0.7);
} }
@supports not (backdrop-filter: saturate(50%) blur(8px)) { @supports not (backdrop-filter: saturate(50%) blur(8px)) {

@ -48,18 +48,18 @@ defineEmits<{
overflow: hidden; overflow: hidden;
} }
.container:hover .top { top: 0; left: 0; transform: translateX(4px); } .VPNavBarHamburger:hover .top { top: 0; left: 0; transform: translateX(4px); }
.container:hover .middle { top: 6; left: 0; transform: translateX(0); } .VPNavBarHamburger:hover .middle { top: 6px; left: 0; transform: translateX(0); }
.container:hover .bottom { top: 12px; left: 0; transform: translateX(8px); } .VPNavBarHamburger:hover .bottom { top: 12px; left: 0; transform: translateX(8px); }
.container.active .top { top: 6px; transform: translateX(0) rotate(225deg); } .VPNavBarHamburger.active .top { top: 6px; transform: translateX(0) rotate(225deg); }
.container.active .middle { top: 6px; transform: translateX(16px); } .VPNavBarHamburger.active .middle { top: 6px; transform: translateX(16px); }
.container.active .bottom { top: 6px; transform: translateX(0) rotate(135deg); } .VPNavBarHamburger.active .bottom { top: 6px; transform: translateX(0) rotate(135deg); }
.container.active:hover .top, .VPNavBarHamburger.active:hover .top,
.container.active:hover .middle, .VPNavBarHamburger.active:hover .middle,
.container.active:hover .bottom { .VPNavBarHamburger.active:hover .bottom {
background-color: var(--vt-c-text-2); background-color: var(--vp-c-text-2);
transition: top .25s, background-color .25s, transform .25s; transition: top .25s, background-color .25s, transform .25s;
} }

@ -1,12 +1,27 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DefaultTheme } from '../config' import { useData } from 'vitepress'
import type { DefaultTheme } from 'vitepress/theme'
import { isActive } from '../support/utils'
import VPFlyout from './VPFlyout.vue' import VPFlyout from './VPFlyout.vue'
defineProps<{ defineProps<{
item: DefaultTheme.NavItemWithChildren item: DefaultTheme.NavItemWithChildren
}>() }>()
const { page } = useData()
</script> </script>
<template> <template>
<VPFlyout :button="item.text" :items="item.items" /> <VPFlyout
:class="{
VPNavBarMenuGroup: true,
active: isActive(
page.relativePath,
item.activeMatch,
!!item.activeMatch
)
}"
:button="item.text"
:items="item.items"
/>
</template> </template>

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DefaultTheme } from 'vitepress/theme'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import { isActive } from '../support/utils' import { isActive } from '../support/utils'
import { DefaultTheme } from '../config'
import VPLink from './VPLink.vue' import VPLink from './VPLink.vue'
defineProps<{ defineProps<{

@ -76,7 +76,7 @@ function load() {
stroke-linejoin="round" stroke-linejoin="round"
/> />
</svg> </svg>
<span class="DocSearch-Button-Placeholder">Search</span> <span class="DocSearch-Button-Placeholder">{{ theme.algolia?.buttonText || 'Search' }}</span>
</span> </span>
<span class="DocSearch-Button-Keys"> <span class="DocSearch-Button-Keys">
<kbd class="DocSearch-Button-Key" ref="metaKey">Meta</kbd> <kbd class="DocSearch-Button-Key" ref="metaKey">Meta</kbd>

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useData } from 'vitepress' import { useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar' import { useSidebar } from '../composables/sidebar'
import VPImage from './VPImage.vue'
const { site, theme } = useData() const { site, theme } = useData()
const { hasSidebar } = useSidebar() const { hasSidebar } = useSidebar()
@ -8,10 +9,12 @@ const { hasSidebar } = useSidebar()
<template> <template>
<div class="VPNavBarTitle" :class="{ 'has-sidebar': hasSidebar }"> <div class="VPNavBarTitle" :class="{ 'has-sidebar': hasSidebar }">
<a class="title" href="/"> <a class="title" :href="site.base">
<img v-if="theme.logo" class="logo" :src="theme.logo"> <slot name="nav-bar-title-before" />
<VPImage class="logo" :image="theme.logo" />
<template v-if="theme.siteTitle">{{ theme.siteTitle }}</template> <template v-if="theme.siteTitle">{{ theme.siteTitle }}</template>
<template v-else-if="theme.siteTitle === undefined">{{ site.title }}</template> <template v-else-if="theme.siteTitle === undefined">{{ site.title }}</template>
<slot name="nav-bar-title-after" />
</a> </a>
</div> </div>
</template> </template>
@ -20,7 +23,6 @@ const { hasSidebar } = useSidebar()
.VPNavBarTitle { .VPNavBarTitle {
flex-shrink: 0; flex-shrink: 0;
border-bottom: 1px solid transparent; border-bottom: 1px solid transparent;
transition: background-color 0.5s;
} }
@media (min-width: 960px) { @media (min-width: 960px) {
@ -53,7 +55,7 @@ const { hasSidebar } = useSidebar()
} }
} }
.logo { :deep(.logo) {
margin-right: 8px; margin-right: 8px;
height: 24px; height: 24px;
} }

@ -29,10 +29,12 @@ function unlockBodyScroll() {
> >
<div v-if="open" class="VPNavScreen" ref="screen"> <div v-if="open" class="VPNavScreen" ref="screen">
<div class="container"> <div class="container">
<slot name="nav-screen-content-before" />
<VPNavScreenMenu class="menu" /> <VPNavScreenMenu class="menu" />
<VPNavScreenTranslations class="translations" /> <VPNavScreenTranslations class="translations" />
<VPNavScreenAppearance class="appearance" /> <VPNavScreenAppearance class="appearance" />
<VPNavScreenSocialLinks class="social-links" /> <VPNavScreenSocialLinks class="social-links" />
<slot name="nav-screen-content-after" />
</div> </div>
</div> </div>
</transition> </transition>

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DefaultTheme } from '../config' import type { DefaultTheme } from 'vitepress/theme'
import VPNavScreenMenuGroupLink from './VPNavScreenMenuGroupLink.vue' import VPNavScreenMenuGroupLink from './VPNavScreenMenuGroupLink.vue'
defineProps<{ defineProps<{

@ -61,15 +61,14 @@ watchPostEffect(async () => {
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
transform: translateX(-100%); transform: translateX(-100%);
transition: background-color 0.5s, opacity 0.5s, transform 0.25s ease; transition: opacity 0.5s, transform 0.25s ease;
} }
.VPSidebar.open { .VPSidebar.open {
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
transform: translateX(0); transform: translateX(0);
transition: background-color 0.5s, transition: opacity 0.25s,
opacity 0.25s,
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1); transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
} }
@ -90,7 +89,6 @@ watchPostEffect(async () => {
visibility: visible; visibility: visible;
box-shadow: none; box-shadow: none;
transform: translateX(0); transform: translateX(0);
transition: border-color 0.5s, background-color 0.5s;
} }
} }

@ -1,18 +1,21 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import type { DefaultTheme } from 'vitepress/theme'
import { DefaultTheme } from '../config' import { ref, watchEffect } from 'vue'
import VPIconPlusSquare from './icons/VPIconPlusSquare.vue' import VPIconPlusSquare from './icons/VPIconPlusSquare.vue'
import VPIconMinusSquare from './icons/VPIconMinusSquare.vue' import VPIconMinusSquare from './icons/VPIconMinusSquare.vue'
import VPSidebarLink from './VPSidebarLink.vue' import VPSidebarLink from './VPSidebarLink.vue'
const props = defineProps<{ const props = defineProps<{
text: string text?: string
items: DefaultTheme.SidebarItem[] items: DefaultTheme.SidebarItem[]
collapsible?: boolean collapsible?: boolean
collapsed?: boolean collapsed?: boolean
}>() }>()
const collapsed = ref(props.collapsible && props.collapsed) const collapsed = ref(false)
watchEffect(() => {
collapsed.value = !!(props.collapsible && props.collapsed)
})
function toggle() { function toggle() {
if (props.collapsible) { if (props.collapsible) {
@ -23,7 +26,12 @@ function toggle() {
<template> <template>
<section class="VPSidebarGroup" :class="{ collapsible, collapsed }"> <section class="VPSidebarGroup" :class="{ collapsible, collapsed }">
<div class="title" :role="collapsible ? 'button' : undefined" @click="toggle"> <div
v-if="text"
class="title"
:role="collapsible ? 'button' : undefined"
@click="toggle"
>
<h2 class="title-text">{{ text }}</h2> <h2 class="title-text">{{ text }}</h2>
<div class="action"> <div class="action">
<VPIconMinusSquare class="icon minus" /> <VPIconMinusSquare class="icon minus" />

@ -1,8 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DefaultTheme } from 'vitepress/theme'
import { inject } from 'vue' import { inject } from 'vue'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import { DefaultTheme } from '../config' import { isActive } from '../support/utils'
import { isActive, normalizeLink } from '../support/utils' import VPLink from './VPLink.vue'
defineProps<{ defineProps<{
item: DefaultTheme.SidebarItem item: DefaultTheme.SidebarItem
@ -14,37 +15,40 @@ const closeSideBar = inject('close-sidebar') as () => void
</script> </script>
<template> <template>
<a <VPLink
class="link"
:class="{ active: isActive(page.relativePath, item.link) }" :class="{ active: isActive(page.relativePath, item.link) }"
:href="normalizeLink(item.link)" :href="item.link"
@click="closeSideBar" @click="closeSideBar"
> >
<p class="link-text">{{ item.text }}</p> <span class="link-text">{{ item.text }}</span>
</a> </VPLink>
</template> </template>
<style scoped> <style scoped>
.link { .link {
display: block; display: block;
padding: 6px 0; padding: 4px 0;
color: var(--vp-c-text-2);
transition: color 0.5s;
} }
.link:hover .link-text { .link:hover {
color: var(--vp-c-text-1); color: var(--vp-c-text-1);
transition: color 0.25s;
} }
.link.active .link-text { .link.active {
color: var(--vp-c-brand); color: var(--vp-c-brand);
transition: color 0.25s; }
.link :deep(.icon) {
width: 12px;
height: 12px;
fill: currentColor;
} }
.link-text { .link-text {
line-height: 20px; line-height: 20px;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
color: var(--vp-c-text-2);
transition: color 0.5s;
} }
</style> </style>

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DefaultTheme } from '../config' import type { DefaultTheme } from 'vitepress/theme'
import VPIconDiscord from './icons/VPIconDiscord.vue' import VPIconDiscord from './icons/VPIconDiscord.vue'
import VPIconFacebook from './icons/VPIconFacebook.vue' import VPIconFacebook from './icons/VPIconFacebook.vue'
import VPIconGitHub from './icons/VPIconGitHub.vue' import VPIconGitHub from './icons/VPIconGitHub.vue'

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DefaultTheme } from '../config' import type { DefaultTheme } from 'vitepress/theme'
import VPSocialLink from './VPSocialLink.vue' import VPSocialLink from './VPSocialLink.vue'
defineProps<{ defineProps<{

@ -0,0 +1,55 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { DefaultTheme } from '..'
import VPTeamMembersItem from './VPTeamMembersItem.vue'
const props = defineProps<{
size?: 'small' | 'medium'
members: DefaultTheme.TeamMember[]
}>()
const classes = computed(() => [
props.size ?? 'medium',
`count-${props.members.length}`
])
</script>
<template>
<div class="VPTeamMembers" :class="classes">
<div class="container">
<div v-for="member in members" :key="member.name" class="item">
<VPTeamMembersItem :size="size" :member="member" />
</div>
</div>
</div>
</template>
<style scoped>
.VPTeamMembers.small .container {
grid-template-columns: repeat(auto-fit, minmax(224px, 1fr));
}
.VPTeamMembers.small.count-1 .container { max-width: 276px; }
.VPTeamMembers.small.count-2 .container { max-width: calc(276px * 2 + 24px); }
.VPTeamMembers.small.count-3 .container { max-width: calc(276px * 3 + 24px * 2); }
.VPTeamMembers.medium .container {
grid-template-columns: repeat(auto-fit, minmax(256px, 1fr));
}
@media (min-width: 375px) {
.VPTeamMembers.medium .container {
grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
}
}
.VPTeamMembers.medium.count-1 .container { max-width: 368px; }
.VPTeamMembers.medium.count-2 .container { max-width: calc(368px * 2 + 24px); }
.container {
display: grid;
gap: 24px;
margin: 0 auto;
max-width: 1152px;
}
</style>

@ -0,0 +1,215 @@
<script setup lang="ts">
import type { DefaultTheme } from '..'
import VPIconHeart from './icons/VPIconHeart.vue'
import VPLink from './VPLink.vue'
import VPSocialLinks from './VPSocialLinks.vue'
defineProps<{
size?: 'small' | 'medium'
member: DefaultTheme.TeamMember
}>()
</script>
<template>
<article class="VPTeamMembersItem" :class="[size ?? 'medium']">
<div class="profile">
<figure class="avatar">
<img class="avatar-img" :src="member.avatar" :alt="member.name">
</figure>
<div class="data">
<h1 class="name">
{{ member.name }}
</h1>
<p v-if="member.title || member.org" class="affiliation">
<span v-if="member.title" class="title">
{{ member.title }}
</span>
<span v-if="member.title && member.org" class="at">
@
</span>
<VPLink v-if="member.org" class="org" :class="{ link: member.orgLink }" :href="member.orgLink" no-icon>
{{ member.org }}
</VPLink>
</p>
<p v-if="member.desc" class="desc">
{{ member.desc }}
</p>
<div v-if="member.links" class="links">
<VPSocialLinks :links="member.links" />
</div>
</div>
</div>
<div v-if="member.sponsor" class="sp">
<VPLink class="sp-link" :href="member.sponsor" no-icon>
<VPIconHeart class="sp-icon" /> Sponsor
</VPLink>
</div>
</article>
</template>
<style scoped>
.VPTeamMembersItem {
display: flex;
flex-direction: column;
gap: 2px;
border-radius: 12px;
width: 100%;
height: 100%;
overflow: hidden;
}
.VPTeamMembersItem.small .profile {
padding: 32px;
}
.VPTeamMembersItem.small .data {
padding-top: 20px;
}
.VPTeamMembersItem.small .avatar {
width: 64px;
height: 64px;
}
.VPTeamMembersItem.small .name {
line-height: 24px;
font-size: 16px;
}
.VPTeamMembersItem.small .affiliation {
padding-top: 4px;
line-height: 20px;
font-size: 14px;
}
.VPTeamMembersItem.small .desc {
padding-top: 12px;
line-height: 20px;
font-size: 14px;
}
.VPTeamMembersItem.small .links {
margin: 0 -16px -20px;
padding: 10px 0 0;
}
.VPTeamMembersItem.medium .profile {
padding: 48px 32px;
}
.VPTeamMembersItem.medium .data {
padding-top: 24px;
text-align: center;
}
.VPTeamMembersItem.medium .avatar {
width: 96px;
height: 96px;
}
.VPTeamMembersItem.medium .name {
letter-spacing: 0.15px;
line-height: 28px;
font-size: 20px;
}
.VPTeamMembersItem.medium .affiliation {
padding-top: 4px;
font-size: 16px;
}
.VPTeamMembersItem.medium .desc {
padding-top: 16px;
max-width: 288px;
font-size: 16px;
}
.VPTeamMembersItem.medium .links {
margin: 0 -16px -12px;
padding: 16px 12px 0;
}
.profile {
flex-grow: 1;
background-color: var(--vp-c-bg-soft);
}
.data {
text-align: center;
}
.avatar {
position: relative;
flex-shrink: 0;
margin: 0 auto;
border-radius: 50%;
box-shadow: var(--vp-shadow-3);
}
.avatar-img {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: 50%;
object-fit: cover;
}
.name {
margin: 0;
font-weight: 600;
}
.affiliation {
margin: 0;
font-weight: 500;
color: var(--vp-c-text-2);
}
.org.link {
color: var(--vp-c-text-2);
transition: color 0.25s;
}
.org.link:hover {
color: var(--vp-c-brand);
}
.desc {
margin: 0 auto;
}
.links {
display: flex;
justify-content: center;
height: 56px;
}
.sp-link {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 16px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-sponsor);
background-color: var(--vp-c-bg-soft);
transition: color 0.25s, background-color 0.25s;
}
.sp-link:hover,
.sp-link:focus {
outline: none;
color: var(--vp-c-text-dark-1);
background-color: var(--vp-c-sponsor);
}
.sp-icon {
margin-right: 8px;
width: 16px;
height: 16px;
fill: currentColor;
}
</style>

@ -0,0 +1,53 @@
<template>
<div class="VPTeamPage">
<slot />
</div>
</template>
<style scoped>
.VPTeamPage {
padding-bottom: 96px;
}
@media (min-width: 768px) {
.VPTeamPage {
padding-bottom: 128px;
}
}
:slotted(.VPTeamPageSection + .VPTeamPageSection),
:slotted(.VPTeamMembers + .VPTeamPageSection) {
margin-top: 64px;
}
:slotted(.VPTeamMembers + .VPTeamMembers) {
margin-top: 24px;
}
@media (min-width: 768px) {
:slotted(.VPTeamPageTitle + .VPTeamPageSection) {
margin-top: 16px;
}
:slotted(.VPTeamPageSection + .VPTeamPageSection),
:slotted(.VPTeamMembers + .VPTeamPageSection) {
margin-top: 96px;
}
}
:slotted(.VPTeamMembers) {
padding: 0 24px;
}
@media (min-width: 768px) {
:slotted(.VPTeamMembers) {
padding: 0 48px;
}
}
@media (min-width: 960px) {
:slotted(.VPTeamMembers) {
padding: 0 64px;
}
}
</style>

@ -0,0 +1,77 @@
<template>
<section class="VPTeamPageSection">
<div class="title">
<div class="title-line" />
<h2 v-if="$slots.title" class="title-text">
<slot name="title" />
</h2>
</div>
<p v-if="$slots.lead" class="lead">
<slot name="lead" />
</p>
<div v-if="$slots.members" class="members">
<slot name="members" />
</div>
</section>
</template>
<style scoped>
.VPTeamPageSection {
padding: 0 32px;
}
@media (min-width: 768px) {
.VPTeamPageSection {
padding: 0 48px;
}
}
@media (min-width: 960px) {
.VPTeamPageSection {
padding: 0 64px;
}
}
.title {
position: relative;
margin: 0 auto;
max-width: 1152px;
text-align: center;
color: var(--vp-c-text-2);
}
.title-line {
position: absolute;
top: 16px;
left: 0;
width: 100%;
height: 1px;
background-color: var(--vp-c-divider-light);
}
.title-text {
position: relative;
display: inline-block;
padding: 0 24px;
letter-spacing: 0;
line-height: 32px;
font-size: 20px;
font-weight: 500;
background-color: var(--vp-c-bg);
}
.lead {
margin: 0 auto;
max-width: 480px;
padding-top: 12px;
text-align: center;
line-height: 24px;
font-size: 16px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.members {
padding-top: 40px;
}
</style>

@ -0,0 +1,63 @@
<template>
<div class="VPTeamPageTitle">
<h1 v-if="$slots.title" class="title">
<slot name="title" />
</h1>
<p v-if="$slots.lead" class="lead">
<slot name="lead" />
</p>
</div>
</template>
<style scoped>
.VPTeamPageTitle {
padding: 48px 32px;
text-align: center;
}
@media (min-width: 768px) {
.VPTeamPageTitle {
padding: 64px 48px 48px;
}
}
@media (min-width: 960px) {
.VPTeamPageTitle {
padding: 80px 64px 48px;
}
}
.title {
letter-spacing: 0;
line-height: 44px;
font-size: 36px;
font-weight: 500;
}
@media (min-width: 768px) {
.title {
letter-spacing: -0.5px;
line-height: 56px;
font-size: 48px;
}
}
.lead {
margin: 0 auto;
max-width: 512px;
padding-top: 12px;
line-height: 24px;
font-size: 16px;
font-weight: 500;
color: var(--vp-c-text-2);
}
@media (min-width: 768px) {
.lead {
max-width: 592px;
letter-spacing: 0.15px;
line-height: 28px;
font-size: 20px;
}
}
</style>

@ -20,6 +20,50 @@ export function useCopyCode() {
) )
} }
async function copyToClipboard(text: string) {
try {
return navigator.clipboard.writeText(text)
} catch {
const element = document.createElement('textarea')
const previouslyFocusedElement = document.activeElement
element.value = text
// Prevent keyboard from showing on mobile
element.setAttribute('readonly', '')
element.style.contain = 'strict'
element.style.position = 'absolute'
element.style.left = '-9999px'
element.style.fontSize = '12pt' // Prevent zooming on iOS
const selection = document.getSelection()
const originalRange = selection
? selection.rangeCount > 0 && selection.getRangeAt(0)
: null
document.body.appendChild(element)
element.select()
// Explicit selection workaround for iOS
element.selectionStart = 0
element.selectionEnd = text.length
document.execCommand('copy')
document.body.removeChild(element)
if (originalRange) {
selection!.removeAllRanges() // originalRange can't be truthy when selection is falsy
selection!.addRange(originalRange)
}
// Get the focus back on the previously focused element, if any
if (previouslyFocusedElement) {
;(previouslyFocusedElement as HTMLElement).focus()
}
}
}
function handleElement(el: HTMLElement) { function handleElement(el: HTMLElement) {
el.onclick = () => { el.onclick = () => {
const parent = el.parentElement const parent = el.parentElement
@ -38,7 +82,7 @@ function handleElement(el: HTMLElement) {
text = text.replace(/^ *\$ /gm, '') text = text.replace(/^ *\$ /gm, '')
} }
navigator.clipboard.writeText(text).then(() => { copyToClipboard(text).then(() => {
el.classList.add('copied') el.classList.add('copied')
setTimeout(() => { setTimeout(() => {
el.classList.remove('copied') el.classList.remove('copied')

@ -5,22 +5,10 @@ export function useEditLink() {
const { theme, page } = useData() const { theme, page } = useData()
return computed(() => { return computed(() => {
const url = [ const { text = 'Edit this page', pattern } = theme.value.editLink || {}
'https://github.com', const { relativePath } = page.value
theme.value.editLink?.repo || '???', const url = pattern.replace(/:path/g, relativePath)
'edit',
theme.value.editLink?.branch || 'main',
theme.value.editLink?.dir || null,
page.value.relativePath
]
.filter((v) => v)
.join('/')
const text = theme.value.editLink?.text ?? 'Edit this page' return { url, text }
return {
url,
text
}
}) })
} }

@ -1,6 +1,6 @@
import { ref, computed } from 'vue' import type { DefaultTheme } from 'vitepress/theme'
import { ref, computed, watch } from 'vue'
import { useData, useRoute } from 'vitepress' import { useData, useRoute } from 'vitepress'
import type { DefaultTheme } from '../config'
export function useNav() { export function useNav() {
const isScreenOpen = ref(false) const isScreenOpen = ref(false)
@ -26,6 +26,9 @@ export function useNav() {
window.outerWidth >= 768 && closeScreen() window.outerWidth >= 768 && closeScreen()
} }
const route = useRoute()
watch(() => route.path, closeScreen)
return { return {
isScreenOpen, isScreenOpen,
openScreen, openScreen,

@ -4,7 +4,7 @@ import { isActive } from '../support/utils'
import { getSidebar, getFlatSideBarLinks } from '../support/sidebar' import { getSidebar, getFlatSideBarLinks } from '../support/sidebar'
export function usePrevNext() { export function usePrevNext() {
const { page, theme } = useData() const { page, theme, frontmatter } = useData()
return computed(() => { return computed(() => {
const sidebar = getSidebar(theme.value.sidebar, page.value.relativePath) const sidebar = getSidebar(theme.value.sidebar, page.value.relativePath)
@ -15,8 +15,12 @@ export function usePrevNext() {
}) })
return { return {
prev: candidates[index - 1], prev: frontmatter.value.prev
next: candidates[index + 1] ? { ...candidates[index - 1], text: frontmatter.value.prev }
: candidates[index - 1],
next: frontmatter.value.next
? { ...candidates[index + 1], text: frontmatter.value.next }
: candidates[index + 1]
} }
}) })
} }

@ -1,5 +1,5 @@
import { Ref, ref, computed, watchEffect, onMounted, onUnmounted } from 'vue' import { computed, onMounted, onUnmounted, Ref, ref, watchEffect } from 'vue'
import { useRoute, useData } from 'vitepress' import { useData, useRoute } from 'vitepress'
import { getSidebar } from '../support/sidebar' import { getSidebar } from '../support/sidebar'
export function useSidebar() { export function useSidebar() {
@ -10,13 +10,15 @@ export function useSidebar() {
const sidebar = computed(() => { const sidebar = computed(() => {
const sidebarConfig = theme.value.sidebar const sidebarConfig = theme.value.sidebar
const relativePath = route.data.relativePath return sidebarConfig ? getSidebar(sidebarConfig, route.path) : []
return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : []
}) })
const hasSidebar = computed(() => { const hasSidebar = computed(() => {
return frontmatter.value.sidebar !== false && sidebar.value.length > 0 return (
frontmatter.value.sidebar !== false &&
sidebar.value.length > 0 &&
frontmatter.value.layout !== 'home'
)
}) })
function open() { function open() {

@ -1 +0,0 @@
export { DefaultTheme } from '../shared'

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

Loading…
Cancel
Save