diff --git a/.github/contributing.md b/.github/contributing.md index 11c8617f..52708253 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -40,7 +40,7 @@ You may start VitePress local dev environment by running `pnpm run dev`. $ pnpm run dev ``` -The easiest way to start testing out VitePress is to tweak the VitePress docs. You may run `pnpm run docs` folder to boot up VitePress documentation site locally, with live reloading of the source code. +The easiest way to start testing out VitePress is to tweak the VitePress docs. You may run `pnpm run docs` to boot up VitePress documentation site locally, with live reloading of the source code. ```bash $ pnpm run docs diff --git a/__tests__/client/theme-default/support/sideBar.spec.ts b/__tests__/client/theme-default/support/sideBar.spec.ts deleted file mode 100644 index 604c945d..00000000 --- a/__tests__/client/theme-default/support/sideBar.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - isSideBarEmpty, - getSideBarConfig, - getFlatSideBarLinks -} from 'client/theme-default/support/sideBar' - -describe('client/theme-default/support/sideBar', () => { - it('checks if the given sidebar is empty', () => { - expect(isSideBarEmpty(undefined)).toBe(true) - expect(isSideBarEmpty(false)).toBe(true) - expect(isSideBarEmpty([])).toBe(true) - - expect(isSideBarEmpty('auto')).toBe(false) - expect(isSideBarEmpty([{ text: 'a', link: '/a' }])).toBe(false) - }) - - it('gets the correct sidebar items', () => { - expect(getSideBarConfig(false, '')).toEqual(false) - expect(getSideBarConfig('auto', '')).toEqual('auto') - - const sidebar = [{ text: 'Title 01', link: 'title-01' }] - const expected = [{ text: 'Title 01', link: 'title-01' }] - - expect(getSideBarConfig(sidebar, '')).toEqual(expected) - }) - - it('gets the correct sidebar items from the given path', () => { - const sidebar = { - '/guide/': [{ text: 'G', link: 'g' }], - '/': [{ text: 'R', link: 'r' }] - } - - expect(getSideBarConfig(sidebar, '/')).toEqual(sidebar['/']) - expect(getSideBarConfig(sidebar, '/guide/')).toEqual(sidebar['/guide/']) - }) - - it('gets the correct sidebar items with various combination', () => { - const s = { - '/guide/': [{ text: 'G', link: 'g' }], - api: [{ text: 'A', link: 'a' }] - } - - expect(getSideBarConfig(s, '/guide/')).toEqual(s['/guide/']) - // no ending slash should not match - expect(getSideBarConfig(s, '/guide')).not.toEqual(s['/guide/']) - expect(getSideBarConfig(s, 'guide/')).toEqual(s['/guide/']) - expect(getSideBarConfig(s, 'guide/nested')).toEqual(s['/guide/']) - expect(getSideBarConfig(s, '/guide/nested')).toEqual(s['/guide/']) - expect(getSideBarConfig(s, 'guide/nested/')).toEqual(s['/guide/']) - expect(getSideBarConfig(s, '/api/')).toEqual(s['api']) - expect(getSideBarConfig(s, '/api')).toEqual(s['api']) - expect(getSideBarConfig(s, 'api/')).toEqual(s['api']) - expect(getSideBarConfig(s, 'api/nested')).toEqual(s['api']) - expect(getSideBarConfig(s, '/api/nested')).toEqual(s['api']) - expect(getSideBarConfig(s, 'api/nested/')).toEqual(s['api']) - expect(getSideBarConfig(s, '/')).toEqual('auto') - }) - - it('creates flat sidebar links', () => { - const sidebar = [ - { text: 'Title 01', link: '/title-01' }, - { text: 'Title 02', link: '/title-02' }, - { text: 'Title 03', link: '/title-03' } - ] - - const expected = [ - { text: 'Title 01', link: '/title-01' }, - { text: 'Title 02', link: '/title-02' }, - { text: 'Title 03', link: '/title-03' } - ] - - expect(getFlatSideBarLinks(sidebar)).toEqual(expected) - }) - - it('creates flat sidebar links with mixed sidebar group', () => { - const sidebar = [ - { - text: 'Title 01', - link: '/title-01', - children: [ - { text: 'Children 01', link: '/children-01' }, - { text: 'Children 02', link: '/children-02' } - ] - }, - { text: 'Title 02', link: '/title-02' }, - { text: 'Title 03', link: '/title-03' } - ] - - const expected = [ - { text: 'Title 01', link: '/title-01' }, - { text: 'Children 01', link: '/children-01' }, - { text: 'Children 02', link: '/children-02' }, - { text: 'Title 02', link: '/title-02' }, - { text: 'Title 03', link: '/title-03' } - ] - - expect(getFlatSideBarLinks(sidebar)).toEqual(expected) - }) - - it('ignores any items with no `link` property', () => { - const sidebar = [ - { - text: 'Title 01', - children: [ - { text: 'Children 01', link: '/children-01' }, - { text: 'Children 02', link: '/children-02' } - ] - }, - { text: 'Title 02', link: '/title-02' } - ] - - const expected = [ - { text: 'Children 01', link: '/children-01' }, - { text: 'Children 02', link: '/children-02' }, - { text: 'Title 02', link: '/title-02' } - ] - - expect(getFlatSideBarLinks(sidebar)).toEqual(expected) - }) - - it('removes `.md` or `.html` extention', () => { - const sidebar = [ - { text: 'Title 01', link: '/title-01.md' }, - { text: 'Title 02', link: '/title-02.html' } - ] - - const expected = [ - { text: 'Title 01', link: '/title-01' }, - { text: 'Title 02', link: '/title-02' } - ] - - expect(getFlatSideBarLinks(sidebar)).toEqual(expected) - }) -}) diff --git a/__tests__/client/theme-default/utils.spec.ts b/__tests__/client/theme-default/utils.spec.ts deleted file mode 100644 index 52f01d4a..00000000 --- a/__tests__/client/theme-default/utils.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as Utils from 'client/theme-default/utils' - -describe('client/theme-default/utils', () => { - describe('ensureStartingSlash', () => { - it('should add 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') - }) - }) - - describe('ensureEndingSlash', () => { - it('should add slash to the end of the given path', () => { - expect(Utils.ensureEndingSlash('path')).toBe('path/') - expect(Utils.ensureEndingSlash('path/nested')).toBe('path/nested/') - expect(Utils.ensureEndingSlash('path/')).toBe('path/') - expect(Utils.ensureEndingSlash('path/nested/')).toBe('path/nested/') - expect(Utils.ensureEndingSlash('path/page.html')).toBe('path/page.html') - }) - }) - - describe('removeExtention', () => { - it('removes `.md` or `.html` extention from the path', () => { - expect(Utils.removeExtention('/')).toBe('/') - expect(Utils.removeExtention('index')).toBe('/') - expect(Utils.removeExtention('index.md')).toBe('/') - expect(Utils.removeExtention('index.html')).toBe('/') - expect(Utils.removeExtention('/index')).toBe('/') - expect(Utils.removeExtention('/index.md')).toBe('/') - expect(Utils.removeExtention('/index.html')).toBe('/') - expect(Utils.removeExtention('path')).toBe('path') - expect(Utils.removeExtention('path.md')).toBe('path') - expect(Utils.removeExtention('path.html')).toBe('path') - expect(Utils.removeExtention('path/')).toBe('path/') - expect(Utils.removeExtention('path/nested.md')).toBe('path/nested') - expect(Utils.removeExtention('path/nested.html')).toBe('path/nested') - expect(Utils.removeExtention('path/nested/index')).toBe('path/nested/') - }) - }) -}) diff --git a/__tests__/node/utils/deeplyParseHeader.spec.ts b/__tests__/node/utils/deeplyParseHeader.spec.ts index ded20eac..fe1878c4 100644 --- a/__tests__/node/utils/deeplyParseHeader.spec.ts +++ b/__tests__/node/utils/deeplyParseHeader.spec.ts @@ -1,3 +1,4 @@ +import { test, expect } from 'vitest' import { deeplyParseHeader } from 'node/utils/parseHeader' test('deeplyParseHeader', () => { diff --git a/__tests__/node/utils/parseHeader.spec.ts b/__tests__/node/utils/parseHeader.spec.ts index a150cff0..790f4373 100644 --- a/__tests__/node/utils/parseHeader.spec.ts +++ b/__tests__/node/utils/parseHeader.spec.ts @@ -1,3 +1,4 @@ +import { describe, test, expect } from 'vitest' import { parseHeader } from 'node/utils/parseHeader' describe('parseHeader', () => { diff --git a/__tests__/node/utils/removeNonCodeWrappedHTML.spec.ts b/__tests__/node/utils/removeNonCodeWrappedHTML.spec.ts index d5481ad8..17d506fc 100644 --- a/__tests__/node/utils/removeNonCodeWrappedHTML.spec.ts +++ b/__tests__/node/utils/removeNonCodeWrappedHTML.spec.ts @@ -1,3 +1,4 @@ +import { test, expect } from 'vitest' import { removeNonCodeWrappedHTML } from 'node/utils/parseHeader' test('removeNonCodeWrappedHTML', () => { diff --git a/client.d.ts b/client.d.ts index 78cda7e2..f206e730 100644 --- a/client.d.ts +++ b/client.d.ts @@ -1,4 +1,3 @@ -// re-export vite client types -// with strict installers like pnpm, user won't be able to reference vite/client -// in project root +// re-export vite client types. with strict installers like pnpm, user won't +// be able to reference vite/client in project root. /// diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index cfb87e63..d0acaa99 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -4,35 +4,29 @@ export default defineConfig({ lang: 'en-US', title: 'VitePress', description: 'Vite & Vue powered static site generator.', - lastUpdated: true, - themeConfig: { - repo: 'vuejs/vitepress', - docsDir: 'docs', - docsBranch: 'main', - editLinks: true, - editLinkText: 'Edit this page on GitHub', - lastUpdated: 'Last Updated', - - algolia: { - appId: '8J64VVRP8K', - apiKey: 'a18e2f4cc5665f6602c5631fd868adfd', - indexName: 'vitepress' - }, + // TODO: Do something about this. + head: [ + [ + 'script', + {}, + ` + ;(() => { + const saved = localStorage.getItem('vitepress-theme-appearance') + const prefereDark = window.matchMedia('(prefers-color-scheme: dark)').matches - carbonAds: { - carbon: 'CEBDT27Y', - custom: 'CKYD62QM', - placement: 'vuejsorg' - }, + if (!saved || saved === 'auto' ? prefereDark : saved === 'dark') { + document.documentElement.classList.add('dark') + } + })() + ` + ] + ], + themeConfig: { nav: [ - { text: 'Guide', link: '/', activeMatch: '^/$|^/guide/' }, - { - text: 'Config Reference', - link: '/config/basics', - activeMatch: '^/config/' - }, + { text: 'Guide', link: '/guide/what-is-vitepress' }, + { text: 'Config', link: '/config/app-basics' }, { text: 'Release Notes', link: 'https://github.com/vuejs/vitepress/releases' @@ -43,6 +37,23 @@ export default defineConfig({ '/guide/': getGuideSidebar(), '/config/': getConfigSidebar(), '/': getGuideSidebar() + }, + + editLink: { + repo: 'vuejs/vitepress', + branch: 'next', + dir: 'docs', + text: 'Edit this page on GitHub' + }, + + socialLinks: [ + { icon: 'github', link: 'https://github.com/vuejs/vitepress' } + ], + + algolia: { + appId: '8J64VVRP8K', + apiKey: 'a18e2f4cc5665f6602c5631fd868adfd', + indexName: 'vitepress' } } }) @@ -51,27 +62,7 @@ function getGuideSidebar() { return [ { text: 'Introduction', - children: [ - { text: 'What is VitePress?', link: '/' }, - { text: 'Getting Started', link: '/guide/getting-started' }, - { text: 'Configuration', link: '/guide/configuration' }, - { text: 'Asset Handling', link: '/guide/assets' }, - { text: 'Markdown Extensions', link: '/guide/markdown' }, - { text: 'Using Vue in Markdown', link: '/guide/using-vue' }, - { text: 'Deploying', link: '/guide/deploy' } - ] - }, - { - text: 'Advanced', - children: [ - { text: 'Frontmatter', link: '/guide/frontmatter' }, - { text: 'Theming', link: '/guide/theming' }, - { text: 'API Reference', link: '/guide/api' }, - { - text: 'Differences from Vuepress', - link: '/guide/differences-from-vuepress' - } - ] + items: [{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' }] } ] } @@ -80,15 +71,7 @@ function getConfigSidebar() { return [ { text: 'App Config', - children: [{ text: 'Basics', link: '/config/basics' }] - }, - { - text: 'Theme Config', - children: [ - { text: 'Homepage', link: '/config/homepage' }, - { text: 'Algolia Search', link: '/config/algolia-search' }, - { text: 'Carbon Ads', link: '/config/carbon-ads' } - ] + items: [{ text: 'Basics', link: '/config/app-basics' }] } ] } diff --git a/docs/components/ComponentInHeader.vue b/docs/components/ComponentInHeader.vue deleted file mode 100644 index bbccc8ef..00000000 --- a/docs/components/ComponentInHeader.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/config/algolia-search.md b/docs/config/algolia-search.md deleted file mode 100644 index 132b0a56..00000000 --- a/docs/config/algolia-search.md +++ /dev/null @@ -1,53 +0,0 @@ -# Theme Config: Algolia Search - -The `themeConfig.algolia` option allows you to use [Algolia DocSearch](https://docsearch.algolia.com). To enable it, you need to provide at least appId, apiKey and indexName: - -```js -module.exports = { - themeConfig: { - algolia: { - appId: 'your_app_id', - apiKey: 'your_api_key', - indexName: 'index_name' - } - } -} -``` - -For more options, check out [Algolia DocSearch's documentation](https://docsearch.algolia.com/docs/api/). You can pass any extra option alongside other options, e.g. passing `searchParameters`: - -```js -module.exports = { - themeConfig: { - algolia: { - appId: 'your_app_id', - apiKey: 'your_api_key', - indexName: 'index_name', - searchParameters: { - facetFilters: ['tags:guide,api'] - } - } - } -} -``` - -## Internationalization (i18n) - -If you have multiple locales in your documentation and you have defined a `locales` object in your `themeConfig`: - -```js -module.exports = { - themeConfig: { - locales: { - // ... - }, - algolia: { - appId: 'your_app_id', - apiKey: 'your_api_key', - indexName: 'index_name' - } - } -} -``` - -VitePress will automatically add a `lang` _facetFilter_ to the `searchParameters.facetFilter` array with the correct language value. Algolia automatically adds the correct facet filter based on the `lang` attribute on the `` tag. This will match search results with the currently viewed language of the page. diff --git a/docs/config/basics.md b/docs/config/basics.md deleted file mode 100644 index 5955ecb5..00000000 --- a/docs/config/basics.md +++ /dev/null @@ -1,59 +0,0 @@ -# App Config: Basics - -::: tip -The config reference is incomplete since the config format may still receive further changes. For a complete reference of the current available options, refer to [config.ts](https://github.com/vuejs/vitepress/blob/45b65ce8b63bd54f345bfc3383eb2416b6769dc9/src/node/config.ts#L30-L65). -::: - -## base - -- Type: `string` -- Default: `/` - -The base URL the site will be deployed at. You will need to set this if you plan to deploy your site under a sub path, for example, GitHub pages. If you plan to deploy your site to `https://foo.github.io/bar/`, then you should set base to `'/bar/'`. It should always start and end with a slash. - -The `base` is automatically prepended to all the URLs that start with `/` in other options, so you only need to specify it once. - -```js -module.exports = { - base: '/base/' -} -``` - -## lang - -- Type: `string` -- Default: `en-US` - -The `lang` attribute for the site. This will render as a `` tag in the page HTML. - -```js -module.exports = { - lang: 'en-US' -} -``` - -## title - -- Type: `string` -- Default: `VitePress` - -Title for the site. This will be the suffix for all page titles, and displayed in the navbar. - -```js -module.exports = { - title: 'VitePress' -} -``` - -## description - -- Type: `string` -- Default: `A VitePress site` - -Description for the site. This will render as a `` tag in the page HTML. - -```js -module.exports = { - description: 'A VitePress site' -} -``` diff --git a/docs/config/carbon-ads.md b/docs/config/carbon-ads.md deleted file mode 100644 index 3bb074d1..00000000 --- a/docs/config/carbon-ads.md +++ /dev/null @@ -1,15 +0,0 @@ -# Theme Config: Carbon Ads - -VitePress has built in native support for [Carbon Ads](https://www.carbonads.net). By defining the Carbon Ads credentials in config, VitePress will display ads on the page. - -```js -module.exports = { - themeConfig: { - carbonAds: { - carbon: 'your-carbon-key', - custom: 'your-carbon-custom', - placement: 'your-carbon-placement' - } - } -} -``` diff --git a/docs/config/homepage.md b/docs/config/homepage.md deleted file mode 100644 index 0abaad7b..00000000 --- a/docs/config/homepage.md +++ /dev/null @@ -1,23 +0,0 @@ -# Theme Config: Homepage - -VitePress provides a homepage layout. To use it, specify `home: true` plus some other metadata in your root `index.md`'s [YAML frontmatter](../guide/frontmatter). This is an example of how it works: - -```yaml ---- -home: true -heroImage: /logo.png -heroAlt: Logo image -heroText: Hero Title -tagline: Hero subtitle -actionText: Get Started -actionLink: /guide/ -features: - - title: Simplicity First - details: Minimal setup with markdown-centered project structure helps you focus on writing. - - title: Vue-Powered - details: Enjoy the dev experience of Vue + webpack, use Vue components in markdown, and develop custom themes with Vue. - - title: Performant - details: VitePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded. -footer: MIT Licensed | Copyright © 2019-present Evan You ---- -``` diff --git a/docs/guide/api.md b/docs/guide/api.md deleted file mode 100644 index 5db50b8c..00000000 --- a/docs/guide/api.md +++ /dev/null @@ -1,95 +0,0 @@ -# API Reference - -## Helper Methods - -The following methods are globally importable from `vitepress` and are typically used in custom theme Vue components. However, they are also usable inside `.md` pages because markdown files are compiled into Vue single-file components. - -Methods that start with `use*` indicates that it is a [Vue 3 Composition API](https://vuejs.org/guide/introduction.html#composition-api) function that can only be used inside `setup()` or ` - - -``` - -### `useRoute` - -Returns the current route object with the following type: - -```ts -interface Route { - path: string - data: PageData - component: Component | null -} -``` - -### `useRouter` - -Returns the VitePress router instance so you can programmatically navigate to another page. - -```ts -interface Router { - route: Route - go: (href?: string) => Promise -} -``` - -### `withBase` - -- **Type**: `(path: string) => string` - - Appends the configured [`base`](../config/basics#base) to a given URL path. Also see [Base URL](./assets#base-url). - -## Global Components - -VitePress comes with few built-in component that can be used globally. You may use these components in your markdown or your custom theme configuration. - -### `` - -The `` component displays the rendered markdown contents. Useful [when creating your own theme](./theming). - -```vue - -``` - -### `` - -The `` component renders its slot only at client side. - -Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the universal code requirements. In short, make sure to only access Browser / DOM APIs in beforeMount or mounted hooks. - -If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the `ClientOnly` component. - -```html - - - -``` diff --git a/docs/guide/assets.md b/docs/guide/assets.md deleted file mode 100644 index 20969f9c..00000000 --- a/docs/guide/assets.md +++ /dev/null @@ -1,55 +0,0 @@ -# Asset Handling - -All Markdown files are compiled into Vue components and processed by [Vite](https://github.com/vitejs/vite). You can, **and should**, reference any assets using relative URLs: - -```md -![An image](./image.png) -``` - -You can reference static assets in your markdown files, your `*.vue` components in the theme, styles and plain `.css` files either using absolute public paths (based on project root) or relative paths (based on your file system). The latter is similar to the behavior you are used to if you have used `vue-cli` or webpack's `file-loader`. - -Common image, media, and font filetypes are detected and included as assets automatically. - -All referenced assets, including those using absolute paths, will be copied to the dist folder with a hashed file name in the production build. Never-referenced assets will not be copied. Similar to `vue-cli`, image assets smaller than 4kb will be base64 inlined. - -All **static** path references, including absolute paths, should be based on your working directory structure. - -## 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). - -Assets placed in `public` will be copied to the root of the dist directory as-is. - -Note that you should reference files placed in `public` using root absolute path - for example, `public/icon.png` should always be referenced in source code as `/icon.png`. - -## Base URL - -If your site is deployed to a non-root URL, you will need to set the `base` option in `.vitepress/config.js`. For example, if you plan to deploy your site to `https://foo.github.io/bar/`, then `base` should be set to `'/bar/'` (it should always start and end with a slash). - -All your static asset paths are automatically processed to adjust for different `base` config values. For example, if you have an absolute reference to an asset under `public` in your markdown: - -```md -![An image](/image-inside-public.png) -``` - -You do **not** need to update it when you change the `base` config value in this case. - -However, if you are authoring a theme component that links to assets dynamically, e.g. an image whose `src` is based on a theme config value: - -```vue - -``` - -In this case it is recommended to wrap the path with the [`withBase` helper](./api#withbase) provided by VitePress: - -```vue - - - -``` diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md deleted file mode 100644 index f2661653..00000000 --- a/docs/guide/configuration.md +++ /dev/null @@ -1,79 +0,0 @@ -# Configuration - -## Overview - -Without any configuration, the page is pretty minimal, and the user has no way to navigate around the site. To customize your site, let’s first create a `.vitepress` directory inside your docs directory. This is where all VitePress-specific files will be placed. Your project structure is probably like this: - -```bash -. -├─ docs -│ ├─ .vitepress -│ │ └─ config.js -│ └─ index.md -└─ package.json -``` - -The essential file for configuring a VitePress site is `.vitepress/config.js`, which should export a JavaScript object: - -```js -export default { - title: 'Hello VitePress', - description: 'Just playing around.' -} -``` - -Check out the [Config Reference](../config/basics) for a full list of options. - -## Config Intellisense - -Since VitePress ships with TypeScript typings, you can leverage your IDE's intellisense with jsdoc type hints: - -```js -/** - * @type {import('vitepress').UserConfig} - */ -const config = { - // ... -} - -export default config -``` - -Alternatively, you can use the `defineConfig` helper at which should provide intellisense without the need for jsdoc annotations: - -```js -import { defineConfig } from 'vitepress' - -export default defineConfig({ - // ... -}) -``` - -VitePress also directly supports TS config files. You can use `.vitepress/config.ts` with the `defineConfig` helper as well. - -## Typed Theme Config - -By default, `defineConfig` helper leverages the theme config type from default theme: - -```ts -import { defineConfig } from 'vitepress' - -export default defineConfig({ - themeConfig: { - // Type is `DefaultTheme.Config` - } -}) -``` - -If you use a custom theme and want type checks for the theme config, you'll need to use `defineConfigWithTheme` instead, and pass the config type for your custom theme via a generic argument: - -```ts -import { defineConfigWithTheme } from 'vitepress' -import { ThemeConfig } from 'your-theme' - -export default defineConfigWithTheme({ - themeConfig: { - // Type is `ThemeConfig` - } -}) -``` diff --git a/docs/guide/deploy.md b/docs/guide/deploy.md deleted file mode 100644 index 8497ecd6..00000000 --- a/docs/guide/deploy.md +++ /dev/null @@ -1,267 +0,0 @@ ---- -sidebarDepth: 3 ---- - -# Deploying - -The following guides are based on some shared assumptions: - -- You are placing your docs inside the `docs` directory of your project; -- 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: - -```json -{ - "scripts": { - "docs:build": "vitepress build docs", - "docs:serve": "vitepress serve docs" - } -} -``` - -## Building The Docs - -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. - -### Testing The Docs Locally - -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. - -You may configure the port of the server py passing `--port` flag as an argument. - -```json -{ - "scripts": { - "docs:serve": "vitepress serve docs --port 8080" - } -} -``` - -Now the `docs:serve` method will launch the server at `http://localhost:8080`. - -## GitHub Pages - -1. Set the correct `base` in `docs/.vitepress/config.js`. - - If you are deploying to `https://.github.io/`, you can omit `base` as it defaults to `'/'`. - - If you are deploying to `https://.github.io//`, for example your repository is at `https://github.com//`, then set `base` to `'//'`. - -2. Inside your project, create `deploy.sh` with the following content (with highlighted lines uncommented appropriately), and run it to deploy: - -```bash{13,20,23} -#!/usr/bin/env sh - -# abort on errors -set -e - -# build -npm run docs:build - -# navigate into the build output directory -cd docs/.vitepress/dist - -# if you are deploying to a custom domain -# echo 'www.example.com' > CNAME - -git init -git add -A -git commit -m 'deploy' - -# if you are deploying to https://.github.io -# git push -f git@github.com:/.github.io.git main - -# if you are deploying to https://.github.io/ -# git push -f git@github.com:/.git main:gh-pages - -cd - -``` - -::: tip -You can also run the above script in your CI setup to enable automatic deployment on each push. -::: - -### GitHub Pages and Travis CI - -1. Set the correct `base` in `docs/.vitepress/config.js`. - - If you are deploying to `https://.github.io/`, you can omit `base` as it defaults to `'/'`. - - If you are deploying to `https://.github.io//`, for example your repository is at `https://github.com//`, then set `base` to `'//'`. - -2. Create a file named `.travis.yml` in the root of your project. - -3. Run `yarn` or `npm install` locally and commit the generated lockfile (that is `yarn.lock` or `package-lock.json`). - -4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages). - -```yaml -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 - -1. Set the correct `base` in `docs/.vitepress/config.js`. - - If you are deploying to `https://.gitlab.io/`, you can omit `base` as it defaults to `'/'`. - - If you are deploying to `https://.gitlab.io//`, for example your repository is at `https://gitlab.com//`, then set `base` to `'//'`. - -2. Set `outDir` in `.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: - -```yaml -image: node:16.5.0 -pages: - stage: deploy - cache: - paths: - - node_modules/ - script: - - yarn install # npm install - - yarn docs:build # npm run docs:build - artifacts: - paths: - - public - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH -``` - -## Netlify - -1. On [Netlify](https://www.netlify.com/), setup up a new project from GitHub with the following settings: - -- **Build Command:** `vitepress build docs` or `yarn docs:build` or `npm run docs:build` -- **Publish directory:** `docs/.vitepress/dist` - -2. Hit the deploy button. - -## Google Firebase - -1. Make sure you have [firebase-tools](https://www.npmjs.com/package/firebase-tools) installed. - -2. Create `firebase.json` and `.firebaserc` at the root of your project with the following content: - -`firebase.json`: - -```json -{ - "hosting": { - "public": "./docs/.vitepress/dist", - "ignore": [] - } -} -``` - -`.firebaserc`: - -```js -{ - "projects": { - "default": "" - } -} -``` - -3. After running `yarn docs:build` or `npm run docs:build`, deploy using the command `firebase deploy`. - -## Surge - -1. First install [surge](https://www.npmjs.com/package/surge), if you haven’t already. - -2. Run `yarn docs:build` or `npm run docs:build`. - -3. Deploy to surge by typing `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 - -1. Install [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). - -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` - -![Override Vercel Configuration](../images/vercel-configuration.png) - -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. - -Once deployed, you will get a URL to see your app live, such as the following: https://vitepress.vercel.app diff --git a/docs/guide/differences-from-vuepress.md b/docs/guide/differences-from-vuepress.md deleted file mode 100644 index b6c6eab5..00000000 --- a/docs/guide/differences-from-vuepress.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -sidebarDepth: 2 ---- - -# Differences from VuePress - -VitePress and VuePress have different [design goals](../index.md). Both projects share similar config naming conventions. VitePress aims to have the bare minimum features needed for authoring docs. Other features are pushed to Themes. On the other hand, VuePress has more features out-of-the-box or enabled by its ecosystem of plugins. - -::: tip -If you are using VuePress, there is no need to migrate to VitePress. Both projects are going to continue to co-exist for the foreseeable future. -::: - -::: warning -Note this is early WIP! Currently, the focus is on making Vite stable and feature-complete first. It is not recommended to use this for anything serious yet. -::: - -In case you decide to move your project to VitePress, this is a list of differences from [VuePress v1.7.1](https://github.com/vuejs/vuepress/releases/tag/v1.7.1) that you need to take into account. - -## General - -- Missing - - YAML and TOML are not supported formats for site config. Only javascript is supported for `.vitepress/config.js` - - [Plugins](https://vuepress.vuejs.org/plugin/) support, features are implemented in themes - - [permalink support](https://vuepress.vuejs.org/guide/permalinks.html) - - `.vitepress/templates` - - Components in `.vitepress/components` [are not auto registered as global components](https://vuepress.vuejs.org) -- Differences - - [Public files](https://vuepress.vuejs.org/guide/assets.html#public-files) that are directly copied to dist root moved from `.vitepress/public/` is `public/` - - [styling](https://vuepress.vuejs.org/config/#styling) `.vitepress/styles/index.styl` and `.vitepress/styles/palette.styl` is not supported. See [Customizing CSS](./theming#customizing-css). - - [App Level Enhancements](https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements) API, app enhancements `.vitepress/enhanceApp.js` is now done in `.vitepress/theme/index.js`. See [Extending the Default Theme](./theming#extending-the-default-theme). - -## Markdown - -- Missing - - Support for [toml in frontmatter](https://vuepress.vuejs.org/guide/frontmatter.html#alternative-frontmatter-formats) - - [details block](https://vuepress.vuejs.org/guide/markdown.html#custom-containers) - - [markdown slots](https://vuepress.vuejs.org/guide/markdown-slot.html) - - `~` prefix to explicitly specify a url is a [webpack module request](https://vuepress.vuejs.org/guide/assets.html#relative-urls) - -## Site Config - -- Missing - - `temp` - - `dest` - - [`theme` from a dependency](https://vuepress.vuejs.org/theme/using-a-theme.html#using-a-theme-from-a-dependency) - - `permalink` - - [`port`](https://vuepress.vuejs.org/config/#port) - - [`shouldPrefetch`](https://vuepress.vuejs.org/config/#shouldprefetch) - - [`cache`](https://vuepress.vuejs.org/config/#cache) - - [`extraWatchFiles`](https://vuepress.vuejs.org/config/#extrawatchfiles) - - [`patterns`](https://vuepress.vuejs.org/config/#patterns) - - [`plugins`](https://vuepress.vuejs.org/config/#pluggable) - - [`markdown.pageSuffix`](https://vuepress.vuejs.org/config/#markdown-pagesuffix) - - [`markdown.slugify`](https://vuepress.vuejs.org/config/#markdown-slugify) - - [`markdown.plugins`](https://vuepress.vuejs.org/config/#markdown-plugins) - - [`markdown.extractHeaders`](https://vuepress.vuejs.org/config/#markdown-extractheaders) - - `markdown.extendMarkdown` to `markdown.config` - - `configureWebpack`, `chainWebpack`, `postcss`, `Stylus`, `scss`, `Sass`, `less` configs - - [`evergreen`](https://vuepress.vuejs.org/config/#evergreen) - -## Default Theme Config - -- Missing - - [`smoothScroll`](https://vuepress.vuejs.org/theme/default-theme-config.html#smooth-scrolling) - - [`displayAllHeaders`](https://vuepress.vuejs.org/theme/default-theme-config.html#displaying-header-links-of-all-pages) - - [`activeHeaderLinks`](https://vuepress.vuejs.org/theme/default-theme-config.html#active-header-links) - - `sidebarDepth` and `initialOpenGroupIndex` for [sidebar groups](https://vuepress.vuejs.org/theme/default-theme-config.html#sidebar-groups) -- Differences - - `searchMaxSuggestions` is `search.maxSuggestions` - - `algolia` is `search.algolia` - - `searchPlaceholder` is `search.placeholder` - -## Default Theme - -- Missing - - [`` and ``](https://vuepress.vuejs.org/theme/default-theme-config.html#code-groups-and-code-blocks) - -## Computed Globals - -- Missing - - `$lang` - - `$localePath` - -## Frontmatter Predefined Variables - -- Missing - - `description` - - [`meta`](https://vuepress.vuejs.org/guide/frontmatter.html#meta) - - [`metaTitle`](https://vuepress.vuejs.org/guide/frontmatter.html#predefined-variables) - - `lang` - - [`layout`](https://vuepress.vuejs.org/guide/frontmatter.html#layout) - - [`permalink`](https://vuepress.vuejs.org/guide/frontmatter.html#predefined-variables) - - [`canonicalUrl`](https://vuepress.vuejs.org/guide/frontmatter.html#predefined-variables) - -## Frontmatter Default Theme Variables - -- Missing - - `prev`, `next` - - [`search`](https://vuepress.vuejs.org/guide/frontmatter.html#search) - - [`tags`](https://vuepress.vuejs.org/guide/frontmatter.html#tags) - - [`pageClass`](https://vuepress.vuejs.org/theme/default-theme-config.html#custom-page-class) - - [`layout`](https://vuepress.vuejs.org/theme/default-theme-config.html#custom-layout-for-specific-pages) - -## siteData - -- Missing - - [`pages`](https://vuepress.vuejs.org/theme/writing-a-theme.html#site-and-page-metadata) - -## pageData - -- Missing - - `key` - - `path` - - `regularPath` - -## Global Components - -- Missing - - [``](https://vuepress.vuejs.org/guide/using-vue.html#badge) diff --git a/docs/guide/frontmatter.md b/docs/guide/frontmatter.md deleted file mode 100644 index 40fd2691..00000000 --- a/docs/guide/frontmatter.md +++ /dev/null @@ -1,87 +0,0 @@ -# Frontmatter - -Any Markdown file that contains a YAML frontmatter block will be processed by [gray-matter](https://github.com/jonschlinkert/gray-matter). The frontmatter must be at the top of the Markdown file, and must take the form of valid YAML set between triple-dashed lines. Example: - -```md ---- -title: Docs with VitePress -editLink: true ---- -``` - -Between the triple-dashed lines, you can set [predefined variables](#predefined-variables), or even create custom ones of your own. These variables can be used via the special $frontmatter variable. - -Here’s an example of how you could use it in your Markdown file: - -```md ---- -title: Docs with VitePress -editLink: true ---- - -# {{ $frontmatter.title }} - -Guide content -``` - -## Alternative frontmatter Formats - -VitePress also supports JSON frontmatter syntax, starting and ending in curly braces: - -```json ---- -{ - "title": "Blogging Like a Hacker", - "editLink": true -} ---- -``` - -## Predefined Variables - -### title - -- Type: `string` -- Default: `h1_title || siteData.title` - -Title of the current page. - -### head - -- Type: `array` -- Default: `undefined` - -Specify extra head tags to be injected: - -```yaml ---- -head: - - - meta - - name: description - content: hello - - - meta - - name: keywords - content: super duper SEO ---- -``` - -### navbar - -- Type: `boolean` -- Default: `undefined` - -You can disable the navbar on a specific page with `navbar: false` - -### sidebar - -- Type: `boolean|'auto'` -- Default: `undefined` - -You can decide to show the sidebar on a specific page with `sidebar: auto` or disable it with `sidebar: false` - -### editLink - -- Type: `boolean` -- Default: `undefined` - -Define if this page should include an edit link. diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md deleted file mode 100644 index 2b891fd3..00000000 --- a/docs/guide/getting-started.md +++ /dev/null @@ -1,51 +0,0 @@ -# Getting Started - -This section will help you build a basic VitePress documentation site from ground up. If you already have an existing project and would like to keep documentation inside the project, start from Step 3. - -- **Step. 1:** Create and change into a new directory. - - ```bash - $ mkdir vitepress-starter && cd vitepress-starter - ``` - -- **Step. 2:** Initialize with your preferred package manager. - - ```bash - $ yarn init - ``` - -- **Step. 3:** Install VitePress locally. - - ```bash - $ yarn add --dev vitepress - ``` - -- **Step. 4:** Create your first document. - - ```bash - $ mkdir docs && echo '# Hello VitePress' > docs/index.md - ``` - -- **Step. 5:** Add some scripts to `package.json`. - - ```json - { - "scripts": { - "docs:dev": "vitepress dev docs", - "docs:build": "vitepress build docs", - "docs:serve": "vitepress serve docs" - } - } - ``` - -- **Step. 6:** Serve the documentation site in the local server. - - ```bash - $ yarn docs:dev - ``` - - VitePress will start a hot-reloading development server at `http://localhost:3000`. - -By now, you should have a basic but functional VitePress documentation site. - -When your documentation site starts to take shape, be sure to read the [deployment guide](./deploy). diff --git a/docs/guide/global-component.md b/docs/guide/global-component.md deleted file mode 100644 index ce843322..00000000 --- a/docs/guide/global-component.md +++ /dev/null @@ -1,32 +0,0 @@ -# Global Component - -VitePress comes with few built-in component that can be used globally. You may use these components in your markdown or your custom theme configuration. - -## Content - -The `Content` component displays the rendered markdown contents. Useful [when creating your own theme](./theming). - -```vue - -``` - -## ClientOnly - -The `ClientOnly` component renderes its slot only at client side. - -Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the universal code requirements. In short, make sure to only access Browser / DOM APIs in beforeMount or mounted hooks. - -If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the `ClientOnly` component. - -```html - - - -``` - -## OutboundLink - -The indicator `OutboundLink` is used to denote external links. In VitePress, this component has been followed by every external link. diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md deleted file mode 100644 index 693e915c..00000000 --- a/docs/guide/markdown.md +++ /dev/null @@ -1,449 +0,0 @@ ---- -sidebarDepth: 3 ---- - -# Markdown Extensions - -## Header Anchors - -Headers automatically get anchor links applied. Rendering of anchors can be configured using the `markdown.anchor` option. - -## Links - -### Internal Links - -Internal links are converted to router link for SPA navigation. Also, every `index.md` contained in each sub-directory will automatically be converted to `index.html`, with corresponding URL `/`. - -For example, given the following directory structure: - -``` -. -├─ index.md -├─ foo -│ ├─ index.md -│ ├─ one.md -│ └─ two.md -└─ bar - ├─ index.md - ├─ three.md - └─ four.md -``` - -And providing you are in `foo/one.md`: - -```md -[Home](/) -[foo](/foo/) -[foo heading](./#heading) -[bar - three](../bar/three) -[bar - three](../bar/three.md) -[bar - four](../bar/four.html) -``` - -### Page Suffix - -Pages and internal links get generated with the `.html` suffix by default. - -### External Links - -Outbound links automatically get `target="_blank" rel="noopener noreferrer"`: - -- [vuejs.org](https://vuejs.org) -- [VitePress on GitHub](https://github.com/vuejs/vitepress) - -## Frontmatter - -[YAML frontmatter](https://jekyllrb.com/docs/front-matter/) is supported out of the box: - -```yaml ---- -title: Blogging Like a Hacker -lang: en-US ---- -``` - -This data will be available to the rest of the page, along with all custom and theming components. - -For more details, see [Frontmatter](./frontmatter). - -## GitHub-Style Tables - -**Input** - -``` -| Tables | Are | Cool | -| ------------- |:-------------:| -----:| -| col 3 is | right-aligned | $1600 | -| col 2 is | centered | $12 | -| zebra stripes | are neat | $1 | -``` - -**Output** - -| Tables | Are | Cool | -| ------------- | :-----------: | -----: | -| col 3 is | right-aligned | \$1600 | -| col 2 is | centered | \$12 | -| zebra stripes | are neat | \$1 | - -## Emoji :tada: - -**Input** - -``` -:tada: :100: -``` - -**Output** - -:tada: :100: - -A [list of all emojis](https://github.com/markdown-it/markdown-it-emoji/blob/master/lib/data/full.json) is available. - -## Table of Contents - -**Input** - -``` -[[toc]] -``` - -**Output** - -[[toc]] - -Rendering of the TOC can be configured using the `markdown.toc` option. - -## Custom Containers - -Custom containers can be defined by their types, titles, and contents. - -### Default Title - -**Input** - -```md -::: tip -This is a tip -::: - -::: info -This is an info box -::: - -::: warning -This is a warning -::: - -::: danger -This is a dangerous warning -::: - -::: details -This is a details block, which does not work in Internet Explorer or old versions of Edge. -::: -``` - -**Output** - -::: tip -This is a tip -::: - -::: info -This is an info box -::: - -::: warning -This is a warning -::: - -::: danger -This is a dangerous warning -::: - -::: details -This is a details block, which does not work in Internet Explorer or Edge. -::: - -### Custom Title - -**Input** - -````md -::: danger STOP -Danger zone, do not proceed -::: - -::: details Click me to view the code - -```js -console.log('Hello, VitePress!') -``` - -::: -```` - -**Output** - -::: danger STOP -Danger zone, do not proceed -::: - -::: details Click me to view the code - -```js -console.log('Hello, VitePress!') -``` - -::: - -## Syntax Highlighting in Code Blocks - -VitePress uses [Prism](https://prismjs.com) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block: - -**Input** - -```` -```js -export default { - name: 'MyComponent', - // ... -} -``` -```` - -**Output** - -```js -export default { - name: 'MyComponent' - // ... -} -``` - -**Input** - -```` -```html -
    -
  • - {{ todo.text }} -
  • -
-``` -```` - -**Output** - -```html -
    -
  • {{ todo.text }}
  • -
-``` - -A [list of valid languages](https://prismjs.com/#languages-list) is available on Prism’s site. - -## Line Highlighting in Code Blocks - -**Input** - -```` -```js{4} -export default { - data () { - return { - msg: 'Highlighted!' - } - } -} -``` -```` - -**Output** - -```js{4} -export default { - data () { - return { - msg: 'Highlighted!' - } - } -} -``` - -In addition to a single line, you can also specify multiple single lines, ranges, or both: - -- Line ranges: for example `{5-8}`, `{3-10}`, `{10-17}` -- Multiple single lines: for example `{4,7,9}` -- Line ranges and single lines: for example `{4,7-13,16,23-27,40}` - -**Input** - -```` -```js{1,4,6-7} -export default { // Highlighted - data () { - return { - msg: `Highlighted! - This line isn't highlighted, - but this and the next 2 are.`, - motd: 'VitePress is awesome', - lorem: 'ipsum', - } - } -} -``` -```` - -**Output** - -```js{1,4,6-8} -export default { // Highlighted - data () { - return { - msg: `Highlighted! - This line isn't highlighted, - but this and the next 2 are.`, - motd: 'VitePress is awesome', - lorem: 'ipsum', - } - } -} -``` - -## Line Numbers - -You can enable line numbers for each code blocks via config: - -```js -module.exports = { - markdown: { - lineNumbers: true - } -} -``` - -- Demo: - - - - Image - - - - - Image - - - - -## Import Code Snippets - -You can import code snippets from existing files via following syntax: - -```md -<<< @/filepath -``` - -It also supports [line highlighting](#line-highlighting-in-code-blocks): - -```md -<<< @/filepath{highlightLines} -``` - -**Input** - -```md -<<< @/snippets/snippet.js{2} -``` - -**Code file** - - - -<<< @/snippets/snippet.js - - - -**Output** - - - -<<< @/snippets/snippet.js{2} - - - -::: tip -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): - -**Input** - -```md -<<< @/snippets/snippet-with-region.js{1} -``` - -**Code file** - - - -<<< @/snippets/snippet-with-region.js - - - -**Output** - - - -<<< @/snippets/snippet-with-region.js#snippet{1} - - - -## 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`: - -```js -const anchor = require('markdown-it-anchor') - -module.exports = { - markdown: { - // options for markdown-it-anchor - // https://github.com/valeriangalliat/markdown-it-anchor#permalinks - anchor: { - permalink: anchor.permalink.headerLink() - }, - - // options for markdown-it-table-of-contents - toc: { includeLevel: [1, 2] }, - - config: (md) => { - // use more markdown-it plugins! - md.use(require('markdown-it-xxx')) - } - } -} -``` diff --git a/docs/guide/theming.md b/docs/guide/theming.md deleted file mode 100644 index 6e1e349f..00000000 --- a/docs/guide/theming.md +++ /dev/null @@ -1,155 +0,0 @@ -# Theming - -## Using a Custom Theme - -You can enable a custom theme by adding the `.vitepress/theme/index.js` file (the "theme entry file"). - -```bash -. -├─ docs -│ ├─ .vitepress -│ │ ├─ theme -│ │ │ └─ index.js -│ │ └─ config.js -│ └─ index.md -└─ package.json -``` - -A VitePress custom theme is simply an object containing three properties and is defined as follows: - -```ts -interface Theme { - Layout: Component // Vue 3 component - NotFound?: Component - enhanceApp?: (ctx: EnhanceAppContext) => void -} - -interface EnhanceAppContext { - app: App // Vue 3 app instance - router: Router // VitePress router instance - siteData: Ref -} -``` - -The theme entry file should export the theme as its default export: - -```js -// .vitepress/theme/index.js -import Layout from './Layout.vue' - -export default { - Layout, - NotFound: () => 'custom 404', // <- this is a Vue 3 functional component - enhanceApp({ app, router, siteData }) { - // app is the Vue 3 app instance from `createApp()`. router is VitePress' - // custom router. `siteData` is a `ref` of current site-level metadata. - } -} -``` - -...where the `Layout` component could look like this: - -```vue - - -``` - -The default export is the only contract for a custom theme. Inside your custom theme, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./using-vue#browser-api-access-restrictions). - -To distribute a theme, simply export the object in your package entry. To consume an external theme, import and re-export it from the custom theme entry: - -```js -// .vitepress/theme/index.js -import Theme from 'awesome-vitepress-theme' -export default Theme -``` - -## Extending the Default Theme - -If you want to extend and customize the default theme, you can import it from `vitepress/theme` and augment it in a custom theme entry. Here are some examples of common customizations: - -### Registering Global Components - -```js -// .vitepress/theme/index.js -import DefaultTheme from 'vitepress/theme' - -export default { - ...DefaultTheme, - enhanceApp({ app }) { - // register global components - app.component('MyGlobalComponent' /* ... */) - } -} -``` - -Since we are using Vite, you can also leverage Vite's [glob import feature](https://vitejs.dev/guide/features.html#glob-import) to auto register a directory of components. - -### Customizing CSS - -The default theme CSS is customizable by overriding root level CSS variables: - -```js -// .vitepress/theme/index.js -import DefaultTheme from 'vitepress/theme' -import './custom.css' - -export default DefaultTheme -``` - -```css -/* .vitepress/theme/custom.css */ -:root { - --c-brand: #646cff; - --c-brand-light: #747bff; -} -``` - -See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css) that can be overridden. - -### Layout Slots - -The default theme's `` component has a few slots that can be used to inject content at certain locations of the page. Here's an example of injecting a component into the top of the sidebar: - -```js -// .vitepress/theme/index.js -import DefaultTheme from 'vitepress/theme' -import MyLayout from './MyLayout.vue' - -export default { - ...DefaultTheme, - // override the Layout with a wrapper component that injects the slots - Layout: MyLayout -} -``` - -```vue - - - - -``` - -Full list of slots available in the default theme layout: - -- `navbar-search` -- `sidebar-top` -- `sidebar-bottom` -- `page-top-ads` -- `page-top` -- `page-bottom` -- `page-bottom-ads` -- Only when `home: true` is enabled via frontmatter: - - `home-hero` - - `home-features` - - `home-footer` diff --git a/docs/guide/using-vue.md b/docs/guide/using-vue.md deleted file mode 100644 index d6d5ce7c..00000000 --- a/docs/guide/using-vue.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -sidebarDepth: 3 ---- - -# Using Vue in Markdown - -In VitePress, each markdown file is compiled into HTML and then processed as a Vue Single-File Component. This means you can use any Vue features inside the markdown, including dynamic templating, using Vue components, or arbitrary in-page Vue component logic by adding a ` - -
{{ page }}
-``` - -**Output** - -```json -{ - "path": "/using-vue.html", - "title": "Using Vue in Markdown", - "frontmatter": {} -} -``` - -## Escaping - -By default, fenced code blocks are automatically wrapped with `v-pre`. To display raw mustaches or Vue-specific syntax inside inline code snippets or plain text, you need to wrap a paragraph with the `v-pre` custom container: - -**Input** - -```md -::: v-pre -`{{ This will be displayed as-is }}` -::: -``` - -**Output** - -::: v-pre -`{{ This will be displayed as-is }}` -::: - -## Using Components - -When you need to have more flexibility, VitePress allows you to extend your authoring toolbox with your own Vue Components. - -### Importing components in markdown - -If your components are going to be used in only a few places, the recommended way to use them is to importing the components in the file where it is used. - -```md - - -# Docs - -This is a .md using a custom component - - - -## More docs - -... -``` - -### Registering global components in the theme - -If the components are going to be used across several pages in the docs, they can be registered globally in the theme (or as part of extending the default VitePress theme). Check out the [Theming Guide](./theming) for more information. - -In `.vitepress/theme/index.js`, the `enhanceApp` function receives the Vue `app` instance so you can [register components](https://vuejs.org/guide/components/registration.html) as you would do in a regular Vue application. - -```js -import DefaultTheme from 'vitepress/theme' - -export default { - ...DefaultTheme, - enhanceApp({ app }) { - app.component('VueClickAwayExample', VueClickAwayExample) - } -} -``` - -Later in your markdown files, the component can be interleaved between the content - -```md -# Vue Click Away - - -``` - -::: warning IMPORTANT -Make sure a custom component’s name either contains a hyphen or is in PascalCase. Otherwise, it will be treated as an inline element and wrapped inside a `

` tag, which will lead to hydration mismatch because `

` does not allow block elements to be placed inside it. -::: - -### Using Components In Headers - -You can use Vue components in the headers, but note the difference between the following syntaxes: - -| Markdown | Output HTML | Parsed Header | -| ------------------------------------------------------- | ----------------------------------------- | ------------- | -|

 # text <Tag/> 
| `

text

` | `text` | -|
 # text \`<Tag/>\` 
| `

text <Tag/>

` | `text ` | - -The HTML wrapped by `` will be displayed as-is; only the HTML that is **not** wrapped will be parsed by Vue. - -::: tip -The output HTML is accomplished by [markdown-it](https://github.com/markdown-it/markdown-it), while the parsed headers are handled by VitePress (and used for both the sidebar and document title). -::: - -## Using CSS Pre-processors - -VitePress has [built-in support](https://vitejs.dev/guide/features.html#css-pre-processors) for CSS pre-processors: `.scss`, `.sass`, `.less`, `.styl` and `.stylus` files. There is no need to install Vite-specific plugins for them, but the corresponding pre-processor itself must be installed: - -``` -# .scss and .sass -npm install -D sass - -# .less -npm install -D less - -# .styl and .stylus -npm install -D stylus -``` - -Then you can use the following in Markdown and theme components: - -```vue - -``` - -## Script & Style Hoisting - -Sometimes you may need to apply some JavaScript or CSS only to the current page. In those cases, you can directly write root-level ` - -## Built-In Components - -VitePress provides Built-In Vue Components like `ClientOnly` and `OutboundLink`, check out the [Global Component Guide](./global-component) for more information. - -**Also see:** - -- [Using Components In Headers](#using-components-in-headers) - -## Browser API Access Restrictions - -Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the [universal code requirements](https://vuejs.org/guide/scaling-up/ssr.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks. - -If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the built-in `` component: - -```md - - - -``` - -Note this does not fix components or libraries that access Browser APIs **on import**. To use code that assumes a browser environment on import, you need to dynamically import them in proper lifecycle hooks: - -```vue - -``` - -If your module `export default` a Vue component, you can register it dynamically: - -```vue - - - -``` - -**Also see:** - -- [Vue.js > Dynamic Components](https://vuejs.org/guide/essentials/component-basics.html#dynamic-components) diff --git a/docs/guide/what-is-vitepress.md b/docs/guide/what-is-vitepress.md new file mode 100644 index 00000000..204c91b3 --- /dev/null +++ b/docs/guide/what-is-vitepress.md @@ -0,0 +1,3 @@ +# What is VitePress? + +Coming soon... diff --git a/docs/images/line-numbers-desktop.png b/docs/images/line-numbers-desktop.png deleted file mode 100644 index e16e6707..00000000 Binary files a/docs/images/line-numbers-desktop.png and /dev/null differ diff --git a/docs/images/line-numbers-mobile.gif b/docs/images/line-numbers-mobile.gif deleted file mode 100644 index 87af6cf0..00000000 Binary files a/docs/images/line-numbers-mobile.gif and /dev/null differ diff --git a/docs/images/vercel-configuration.png b/docs/images/vercel-configuration.png deleted file mode 100644 index 51874e15..00000000 Binary files a/docs/images/vercel-configuration.png and /dev/null differ diff --git a/docs/index.md b/docs/index.md index 092840b8..5039610d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,56 +1,3 @@ ---- -sidebarDepth: 2 ---- +# Home Page -# What is VitePress? - -::: warning WARNING -VitePress is currently in 0.x status. It is already suitable for out-of-the-box documentation use, but the config and theming API may still change between minor releases. -::: - -VitePress is [VuePress](https://vuepress.vuejs.org)' little brother, built on top of [Vite](https://github.com/vitejs/vite). - -## Motivation - -We love VuePress v1, but being built on top of Webpack, the time it takes to spin up the dev server for a simple doc site with a few pages is just becoming unbearable. Even HMR updates can take up to seconds to reflect in the browser! - -Fundamentally, this is because VuePress v1 is a Webpack app under the hood. Even with just two pages, it's a full on Webpack project (including all the theme source files) being compiled. It gets even worse when the project has many pages – every page must first be fully compiled before the server can even display anything! - -Incidentally, Vite solves these problems really well: nearly instant server start, an on-demand compilation that only compiles the page being served, and lightning-fast HMR. Plus, there are a few additional design issues I have noted in VuePress v1 over time but never had the time to fix due to the amount of refactoring it would require. - -Now, with Vite and Vue 3, it is time to rethink what a "Vue-powered static site generator" can really be. - -## Improvements Over VuePress v1 - -There're couple of things that are improved from VuePress v1.... - -### It Uses Vue 3 - -Leverages Vue 3's improved template static analysis to stringify static content as much as possible. Static content is sent as string literals instead of JavaScript render function code – the JS payload is therefore _much_ cheaper to parse, and hydration also becomes faster. - -Note the optimization is applied while still allowing the user to freely mix Vue components inside markdown content – the compiler does the static/dynamic separation for you automatically and you never need to think about it. - -### It Uses Vite Under The Hood - -- Faster dev server start -- Faster hot updates -- Faster build (uses Rollup internally) - -### Lighter Page Weight - -- Vue 3 tree-shaking + Rollup code splitting -- Does not ship metadata for every page on every request. This decouples page weight from total number of pages. Only the current page's metadata is sent. Client side navigation fetches the new page's component and metadata together. -- Does not use `vue-router` because the need of VitePress is very simple and specific - a simple custom router (under 200 LOC) is used instead. -- (WIP) i18n locale data should also be fetched on demand. - -## Other Differences - -VitePress is more opinionated and less configurable: VitePress aims to scale back the complexity in the current VuePress and restart from its minimalist roots. - -VitePress is future oriented: VitePress only targets browsers that support native ES module imports. It encourages the use of native JavaScript without transpilation, and CSS variables for theming. - -## Will This Become The Next VuePress in The Future? - -We already have [vuepress-next](https://github.com/vuepress/vuepress-next), which would be the next major version of VuePress. It also makes lots of improvements over VuePress v1, and also supports Vite now. - -VitePress is not compatible with the current VuePress ecosystem (mostly themes and plugins). The overall idea is that VitePress will have a drastically more minimal theming API (preferring JavaScript APIs instead of file layout conventions) and likely no plugins (all customization is done in themes). +Coming soon... diff --git a/docs/snippets/snippet-with-region.js b/docs/snippets/snippet-with-region.js deleted file mode 100644 index 9c7faaeb..00000000 --- a/docs/snippets/snippet-with-region.js +++ /dev/null @@ -1,7 +0,0 @@ -// #region snippet -function foo() { - // .. -} -// #endregion snippet - -export default foo diff --git a/docs/snippets/snippet.js b/docs/snippets/snippet.js deleted file mode 100644 index 575039d1..00000000 --- a/docs/snippets/snippet.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function () { - // .. -} diff --git a/package.json b/package.json index 6e1cb106..ea0fb88a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vitepress", - "version": "0.22.4", + "version": "1.0.0-draft.1", "description": "Vite & Vue powered static site generator", "main": "dist/node/index.js", "typings": "types/index.d.ts", @@ -14,6 +14,24 @@ "client.d.ts", "theme.d.ts" ], + "repository": { + "type": "git", + "url": "git+https://github.com/vuejs/vitepress.git" + }, + "keywords": [ + "vite", + "vue", + "vitepress" + ], + "author": "Evan You", + "license": "MIT", + "homepage": "https://github.com/vuejs/vitepress/tree/main/#readme", + "bugs": { + "url": "https://github.com/vuejs/vitepress/issues" + }, + "engines": { + "node": ">=14.0.0" + }, "scripts": { "dev": "run-s dev-shared dev-start", "dev-start": "run-p dev-client dev-node dev-watch", @@ -31,7 +49,8 @@ "lint": "run-s lint:js lint:ts", "lint:js": "prettier --check --write \"{bin,docs,scripts,src}/**/*.js\"", "lint:ts": "prettier --check --write --parser typescript \"{__tests__,src,docs,types}/**/*.ts\"", - "test": "vitest run __tests__ -c __tests__/vitest.config.js --globals", + "test": "vitest run __tests__ -c __tests__/vitest.config.js --global", + "test:all": "run-s lint test", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", "release": "node scripts/release.js", "docs": "run-p dev docs-dev", @@ -42,9 +61,6 @@ "docs-serve": "node ./bin/vitepress serve docs", "ci-docs": "run-s build docs-build" }, - "engines": { - "node": ">=14.0.0" - }, "gitHooks": { "pre-commit": "lint-staged" }, @@ -56,28 +72,15 @@ "prettier --parser=typescript --write" ] }, - "repository": { - "type": "git", - "url": "git+https://github.com/vuejs/vitepress.git" - }, - "keywords": [ - "vite", - "vue", - "vitepress" - ], - "author": "Evan You", - "license": "MIT", - "homepage": "https://github.com/vuejs/vitepress/tree/master/#readme", - "bugs": { - "url": "https://github.com/vuejs/vitepress/issues" - }, "dependencies": { "@docsearch/css": "^3.0.0", "@docsearch/js": "^3.0.0", "@vitejs/plugin-vue": "^2.3.2", + "@vueuse/core": "^8.5.0", + "body-scroll-lock": "^4.0.0-beta.0", "prismjs": "^1.25.0", "vite": "^2.9.7", - "vue": "^3.2.33" + "vue": "3.2.33" }, "devDependencies": { "@microsoft/api-extractor": "^7.23.1", @@ -85,6 +88,7 @@ "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.4", + "@types/body-scroll-lock": "^3.1.0", "@types/compression": "^1.7.0", "@types/cross-spawn": "^6.0.2", "@types/debug": "^4.1.7", diff --git a/src/client/app/components/ClientOnly.ts b/src/client/app/components/ClientOnly.ts index 2e1da7c3..e7b1f17e 100644 --- a/src/client/app/components/ClientOnly.ts +++ b/src/client/app/components/ClientOnly.ts @@ -1,11 +1,13 @@ -import { ref, onMounted, defineComponent } from 'vue' +import { defineComponent, ref, onMounted } from 'vue' export const ClientOnly = defineComponent({ setup(_, { slots }) { const show = ref(false) + onMounted(() => { show.value = true }) + return () => (show.value && slots.default ? slots.default() : null) } }) diff --git a/src/client/app/index.ts b/src/client/app/index.ts index ea5fe60c..96052d19 100644 --- a/src/client/app/index.ts +++ b/src/client/app/index.ts @@ -7,11 +7,11 @@ import { onMounted, watch } from 'vue' +import Theme from '/@theme/index' import { inBrowser, pathToFile } from './utils' import { Router, RouterSymbol, createRouter } from './router' import { siteDataRef, useData } from './data' import { useUpdateHead } from './composables/head' -import Theme from '/@theme/index' import { usePrefetch } from './composables/preFetch' import { dataSymbol, initData } from './data' import { Content } from './components/Content' diff --git a/src/client/shim.d.ts b/src/client/shim.d.ts index c6c262e5..2d25724b 100644 --- a/src/client/shim.d.ts +++ b/src/client/shim.d.ts @@ -1,7 +1,8 @@ declare const __VP_HASH_MAP__: Record +declare const __ALGOLIA__: boolean declare const __CARBON__: boolean declare const __BSA__: boolean -declare const __ALGOLIA__: boolean + declare module '*.vue' { import { ComponentOptions } from 'vue' const comp: ComponentOptions @@ -13,7 +14,7 @@ declare module '@siteData' { export default data } -// this module's typing is broken +// this module's typing is broken. declare module '@docsearch/js' { function docsearch(props: T): void export default docsearch diff --git a/src/client/theme-default/Layout.vue b/src/client/theme-default/Layout.vue index 26ba9b4c..a890b801 100644 --- a/src/client/theme-default/Layout.vue +++ b/src/client/theme-default/Layout.vue @@ -1,189 +1,31 @@ - - diff --git a/src/client/theme-default/NotFound.vue b/src/client/theme-default/NotFound.vue index 35cef3e9..5f2e86e4 100644 --- a/src/client/theme-default/NotFound.vue +++ b/src/client/theme-default/NotFound.vue @@ -1,15 +1,8 @@ - - + + diff --git a/src/client/theme-default/components/AlgoliaSearchBox.vue b/src/client/theme-default/components/AlgoliaSearchBox.vue deleted file mode 100644 index 510e15d4..00000000 --- a/src/client/theme-default/components/AlgoliaSearchBox.vue +++ /dev/null @@ -1,188 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/BuySellAds.vue b/src/client/theme-default/components/BuySellAds.vue deleted file mode 100644 index 6c998b39..00000000 --- a/src/client/theme-default/components/BuySellAds.vue +++ /dev/null @@ -1,152 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/CarbonAds.vue b/src/client/theme-default/components/CarbonAds.vue deleted file mode 100644 index df597fb3..00000000 --- a/src/client/theme-default/components/CarbonAds.vue +++ /dev/null @@ -1,99 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/EditLink.vue b/src/client/theme-default/components/EditLink.vue deleted file mode 100644 index ffdab5ea..00000000 --- a/src/client/theme-default/components/EditLink.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/Home.vue b/src/client/theme-default/components/Home.vue deleted file mode 100644 index c4c976a4..00000000 --- a/src/client/theme-default/components/Home.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/HomeFeatures.vue b/src/client/theme-default/components/HomeFeatures.vue deleted file mode 100644 index c435248e..00000000 --- a/src/client/theme-default/components/HomeFeatures.vue +++ /dev/null @@ -1,143 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/HomeFooter.vue b/src/client/theme-default/components/HomeFooter.vue deleted file mode 100644 index 55f27af4..00000000 --- a/src/client/theme-default/components/HomeFooter.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/HomeHero.vue b/src/client/theme-default/components/HomeHero.vue deleted file mode 100644 index eb396dd0..00000000 --- a/src/client/theme-default/components/HomeHero.vue +++ /dev/null @@ -1,161 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/LastUpdated.vue b/src/client/theme-default/components/LastUpdated.vue deleted file mode 100644 index 7adf7222..00000000 --- a/src/client/theme-default/components/LastUpdated.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NavBar.vue b/src/client/theme-default/components/NavBar.vue deleted file mode 100644 index 5cefd432..00000000 --- a/src/client/theme-default/components/NavBar.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NavBarTitle.vue b/src/client/theme-default/components/NavBarTitle.vue deleted file mode 100644 index 8b9d3709..00000000 --- a/src/client/theme-default/components/NavBarTitle.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NavDropdownLink.vue b/src/client/theme-default/components/NavDropdownLink.vue deleted file mode 100644 index eb1001fd..00000000 --- a/src/client/theme-default/components/NavDropdownLink.vue +++ /dev/null @@ -1,135 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NavDropdownLinkItem.vue b/src/client/theme-default/components/NavDropdownLinkItem.vue deleted file mode 100644 index 907d6ad5..00000000 --- a/src/client/theme-default/components/NavDropdownLinkItem.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NavLink.vue b/src/client/theme-default/components/NavLink.vue deleted file mode 100644 index 29e76747..00000000 --- a/src/client/theme-default/components/NavLink.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NavLinks.vue b/src/client/theme-default/components/NavLinks.vue deleted file mode 100644 index bc221596..00000000 --- a/src/client/theme-default/components/NavLinks.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/NextAndPrevLinks.vue b/src/client/theme-default/components/NextAndPrevLinks.vue deleted file mode 100644 index e826aca4..00000000 --- a/src/client/theme-default/components/NextAndPrevLinks.vue +++ /dev/null @@ -1,88 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/Page.vue b/src/client/theme-default/components/Page.vue deleted file mode 100644 index 7a1d422b..00000000 --- a/src/client/theme-default/components/Page.vue +++ /dev/null @@ -1,53 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/PageFooter.vue b/src/client/theme-default/components/PageFooter.vue deleted file mode 100644 index 93c7ce60..00000000 --- a/src/client/theme-default/components/PageFooter.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/SideBar.vue b/src/client/theme-default/components/SideBar.vue deleted file mode 100644 index 0c112e18..00000000 --- a/src/client/theme-default/components/SideBar.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/SideBarLink.ts b/src/client/theme-default/components/SideBarLink.ts deleted file mode 100644 index 42ba5012..00000000 --- a/src/client/theme-default/components/SideBarLink.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { FunctionalComponent, h, VNode } from 'vue' -import { useRoute, useData } from 'vitepress' -import { Header } from '../../shared' -import { DefaultTheme } from '../config' -import { joinUrl, isActive } from '../utils' - -interface HeaderWithChildren extends Header { - children?: Header[] -} - -export const SideBarLink: FunctionalComponent<{ - item: DefaultTheme.SideBarItem - depth?: number -}> = (props) => { - const route = useRoute() - const { site, frontmatter } = useData() - const depth = props.depth || 1 - const maxDepth = frontmatter.value.sidebarDepth || Infinity - - const headers = route.data.headers - const text = props.item.text - const link = resolveLink(site.value.base, props.item.link) - const children = (props.item as DefaultTheme.SideBarGroup).children - const active = isActive(route, props.item.link) - const childItems = - depth < maxDepth - ? createChildren(active, children, headers, depth + 1) - : null - - return h('li', { class: 'sidebar-link' }, [ - h( - link ? 'a' : 'p', - { - class: { 'sidebar-link-item': true, active }, - href: link - }, - text - ), - childItems - ]) -} - -function resolveLink(base: string, path?: string): string | undefined { - if (path === undefined) { - return path - } - - // keep relative hash to the same page - if (path.startsWith('#')) { - return path - } - - return joinUrl(base, path) -} - -function createChildren( - active: boolean, - children?: DefaultTheme.SideBarItem[], - headers?: Header[], - depth = 1 -): VNode | null { - if (children && children.length > 0) { - return h( - 'ul', - { class: 'sidebar-links' }, - children.map((c) => { - return h(SideBarLink, { item: c, depth }) - }) - ) - } - - return active && headers - ? createChildren(false, resolveHeaders(headers), undefined, depth) - : null -} - -function resolveHeaders(headers: Header[]): DefaultTheme.SideBarItem[] { - return mapHeaders(groupHeaders(headers)) -} - -function groupHeaders(headers: Header[]): HeaderWithChildren[] { - headers = headers.map((h) => Object.assign({}, h)) - let lastH2: HeaderWithChildren - headers.forEach((h) => { - if (h.level === 2) { - lastH2 = h - } else if (lastH2) { - ;(lastH2.children || (lastH2.children = [])).push(h) - } - }) - return headers.filter((h) => h.level === 2) -} - -function mapHeaders(headers: HeaderWithChildren[]): DefaultTheme.SideBarItem[] { - return headers.map((header) => ({ - text: header.title, - link: `#${header.slug}`, - children: header.children ? mapHeaders(header.children) : undefined - })) -} diff --git a/src/client/theme-default/components/SideBarLinks.vue b/src/client/theme-default/components/SideBarLinks.vue deleted file mode 100644 index 768013e3..00000000 --- a/src/client/theme-default/components/SideBarLinks.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/src/client/theme-default/components/ToggleSideBarButton.vue b/src/client/theme-default/components/ToggleSideBarButton.vue deleted file mode 100644 index 07448cc0..00000000 --- a/src/client/theme-default/components/ToggleSideBarButton.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/src/client/theme-default/components/VPAlgoliaSearchBox.vue b/src/client/theme-default/components/VPAlgoliaSearchBox.vue new file mode 100644 index 00000000..c4eede49 --- /dev/null +++ b/src/client/theme-default/components/VPAlgoliaSearchBox.vue @@ -0,0 +1,127 @@ + + + diff --git a/src/client/theme-default/components/VPBackdrop.vue b/src/client/theme-default/components/VPBackdrop.vue new file mode 100644 index 00000000..b19239d5 --- /dev/null +++ b/src/client/theme-default/components/VPBackdrop.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/client/theme-default/components/VPContent.vue b/src/client/theme-default/components/VPContent.vue new file mode 100644 index 00000000..d18af788 --- /dev/null +++ b/src/client/theme-default/components/VPContent.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/client/theme-default/components/VPContentDoc.vue b/src/client/theme-default/components/VPContentDoc.vue new file mode 100644 index 00000000..8b60b4df --- /dev/null +++ b/src/client/theme-default/components/VPContentDoc.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/src/client/theme-default/components/VPContentDocFooter.vue b/src/client/theme-default/components/VPContentDocFooter.vue new file mode 100644 index 00000000..4c2d14b5 --- /dev/null +++ b/src/client/theme-default/components/VPContentDocFooter.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/src/client/theme-default/components/VPContentDocOutline.vue b/src/client/theme-default/components/VPContentDocOutline.vue new file mode 100644 index 00000000..b741c26d --- /dev/null +++ b/src/client/theme-default/components/VPContentDocOutline.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/client/theme-default/components/VPFlyout.vue b/src/client/theme-default/components/VPFlyout.vue new file mode 100644 index 00000000..fbe065f3 --- /dev/null +++ b/src/client/theme-default/components/VPFlyout.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/client/theme-default/components/VPLink.vue b/src/client/theme-default/components/VPLink.vue new file mode 100644 index 00000000..945945b6 --- /dev/null +++ b/src/client/theme-default/components/VPLink.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/client/theme-default/components/VPLocalNav.vue b/src/client/theme-default/components/VPLocalNav.vue new file mode 100644 index 00000000..885bc740 --- /dev/null +++ b/src/client/theme-default/components/VPLocalNav.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/src/client/theme-default/components/VPMenu.vue b/src/client/theme-default/components/VPMenu.vue new file mode 100644 index 00000000..3fde13be --- /dev/null +++ b/src/client/theme-default/components/VPMenu.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/client/theme-default/components/VPMenuGroup.vue b/src/client/theme-default/components/VPMenuGroup.vue new file mode 100644 index 00000000..689fdab8 --- /dev/null +++ b/src/client/theme-default/components/VPMenuGroup.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/client/theme-default/components/VPMenuLink.vue b/src/client/theme-default/components/VPMenuLink.vue new file mode 100644 index 00000000..29f4c8ac --- /dev/null +++ b/src/client/theme-default/components/VPMenuLink.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/src/client/theme-default/components/VPNav.vue b/src/client/theme-default/components/VPNav.vue new file mode 100644 index 00000000..f65f1368 --- /dev/null +++ b/src/client/theme-default/components/VPNav.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBar.vue b/src/client/theme-default/components/VPNavBar.vue new file mode 100644 index 00000000..7d5c3196 --- /dev/null +++ b/src/client/theme-default/components/VPNavBar.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarAppearance.vue b/src/client/theme-default/components/VPNavBarAppearance.vue new file mode 100644 index 00000000..39add39a --- /dev/null +++ b/src/client/theme-default/components/VPNavBarAppearance.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarExtra.vue b/src/client/theme-default/components/VPNavBarExtra.vue new file mode 100644 index 00000000..13abe687 --- /dev/null +++ b/src/client/theme-default/components/VPNavBarExtra.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarHamburger.vue b/src/client/theme-default/components/VPNavBarHamburger.vue new file mode 100644 index 00000000..1e014177 --- /dev/null +++ b/src/client/theme-default/components/VPNavBarHamburger.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarMenu.vue b/src/client/theme-default/components/VPNavBarMenu.vue new file mode 100644 index 00000000..c8fb6df2 --- /dev/null +++ b/src/client/theme-default/components/VPNavBarMenu.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarMenuGroup.vue b/src/client/theme-default/components/VPNavBarMenuGroup.vue new file mode 100644 index 00000000..afa7716a --- /dev/null +++ b/src/client/theme-default/components/VPNavBarMenuGroup.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/client/theme-default/components/VPNavBarMenuLink.vue b/src/client/theme-default/components/VPNavBarMenuLink.vue new file mode 100644 index 00000000..ac495a0e --- /dev/null +++ b/src/client/theme-default/components/VPNavBarMenuLink.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarSearch.vue b/src/client/theme-default/components/VPNavBarSearch.vue new file mode 100644 index 00000000..3359fc49 --- /dev/null +++ b/src/client/theme-default/components/VPNavBarSearch.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarSocialLinks.vue b/src/client/theme-default/components/VPNavBarSocialLinks.vue new file mode 100644 index 00000000..e5dfd630 --- /dev/null +++ b/src/client/theme-default/components/VPNavBarSocialLinks.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarTitle.vue b/src/client/theme-default/components/VPNavBarTitle.vue new file mode 100644 index 00000000..1d4a8b6d --- /dev/null +++ b/src/client/theme-default/components/VPNavBarTitle.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavBarTranslations.vue b/src/client/theme-default/components/VPNavBarTranslations.vue new file mode 100644 index 00000000..a8218f1a --- /dev/null +++ b/src/client/theme-default/components/VPNavBarTranslations.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreen.vue b/src/client/theme-default/components/VPNavScreen.vue new file mode 100644 index 00000000..7204ac8f --- /dev/null +++ b/src/client/theme-default/components/VPNavScreen.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreenAppearance.vue b/src/client/theme-default/components/VPNavScreenAppearance.vue new file mode 100644 index 00000000..452536b6 --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenAppearance.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreenMenu.vue b/src/client/theme-default/components/VPNavScreenMenu.vue new file mode 100644 index 00000000..d75ef61d --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenMenu.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/client/theme-default/components/VPNavScreenMenuGroup.vue b/src/client/theme-default/components/VPNavScreenMenuGroup.vue new file mode 100644 index 00000000..140dcec6 --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenMenuGroup.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreenMenuGroupLink.vue b/src/client/theme-default/components/VPNavScreenMenuGroupLink.vue new file mode 100644 index 00000000..61f5bd83 --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenMenuGroupLink.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue b/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue new file mode 100644 index 00000000..d45a336d --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreenMenuLink.vue b/src/client/theme-default/components/VPNavScreenMenuLink.vue new file mode 100644 index 00000000..f7ae9800 --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenMenuLink.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/client/theme-default/components/VPNavScreenSocialLinks.vue b/src/client/theme-default/components/VPNavScreenSocialLinks.vue new file mode 100644 index 00000000..45f4d318 --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenSocialLinks.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/client/theme-default/components/VPNavScreenTranslations.vue b/src/client/theme-default/components/VPNavScreenTranslations.vue new file mode 100644 index 00000000..7305554e --- /dev/null +++ b/src/client/theme-default/components/VPNavScreenTranslations.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/src/client/theme-default/components/VPSidebar.vue b/src/client/theme-default/components/VPSidebar.vue new file mode 100644 index 00000000..166d90da --- /dev/null +++ b/src/client/theme-default/components/VPSidebar.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/client/theme-default/components/VPSidebarGroup.vue b/src/client/theme-default/components/VPSidebarGroup.vue new file mode 100644 index 00000000..09bf3c0d --- /dev/null +++ b/src/client/theme-default/components/VPSidebarGroup.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/client/theme-default/components/VPSidebarLink.vue b/src/client/theme-default/components/VPSidebarLink.vue new file mode 100644 index 00000000..c94d04bf --- /dev/null +++ b/src/client/theme-default/components/VPSidebarLink.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/client/theme-default/components/VPSkipLink.vue b/src/client/theme-default/components/VPSkipLink.vue new file mode 100644 index 00000000..5324e8bd --- /dev/null +++ b/src/client/theme-default/components/VPSkipLink.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/src/client/theme-default/components/VPSocialLink.vue b/src/client/theme-default/components/VPSocialLink.vue new file mode 100644 index 00000000..38ace87a --- /dev/null +++ b/src/client/theme-default/components/VPSocialLink.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/client/theme-default/components/VPSocialLinks.vue b/src/client/theme-default/components/VPSocialLinks.vue new file mode 100644 index 00000000..80348a2f --- /dev/null +++ b/src/client/theme-default/components/VPSocialLinks.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/src/client/theme-default/components/VPSwitch.vue b/src/client/theme-default/components/VPSwitch.vue new file mode 100644 index 00000000..94c47b70 --- /dev/null +++ b/src/client/theme-default/components/VPSwitch.vue @@ -0,0 +1,66 @@ + + + diff --git a/src/client/theme-default/components/VPSwitchAppearance.vue b/src/client/theme-default/components/VPSwitchAppearance.vue new file mode 100644 index 00000000..cfac77ca --- /dev/null +++ b/src/client/theme-default/components/VPSwitchAppearance.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/client/theme-default/components/icons/OutboundLink.vue b/src/client/theme-default/components/icons/OutboundLink.vue deleted file mode 100644 index 4d74eeeb..00000000 --- a/src/client/theme-default/components/icons/OutboundLink.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/src/client/theme-default/components/icons/VPIconAlignJustify.vue b/src/client/theme-default/components/icons/VPIconAlignJustify.vue new file mode 100644 index 00000000..653dab13 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconAlignJustify.vue @@ -0,0 +1,8 @@ + diff --git a/src/client/theme-default/components/icons/VPIconAlignLeft.vue b/src/client/theme-default/components/icons/VPIconAlignLeft.vue new file mode 100644 index 00000000..8dc84eae --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconAlignLeft.vue @@ -0,0 +1,8 @@ + diff --git a/src/client/theme-default/components/icons/VPIconAlignRight.vue b/src/client/theme-default/components/icons/VPIconAlignRight.vue new file mode 100644 index 00000000..16cbb3c7 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconAlignRight.vue @@ -0,0 +1,8 @@ + diff --git a/src/client/theme-default/components/icons/ArrowLeft.vue b/src/client/theme-default/components/icons/VPIconArrowLeft.vue similarity index 100% rename from src/client/theme-default/components/icons/ArrowLeft.vue rename to src/client/theme-default/components/icons/VPIconArrowLeft.vue diff --git a/src/client/theme-default/components/icons/ArrowRight.vue b/src/client/theme-default/components/icons/VPIconArrowRight.vue similarity index 100% rename from src/client/theme-default/components/icons/ArrowRight.vue rename to src/client/theme-default/components/icons/VPIconArrowRight.vue diff --git a/src/client/theme-default/components/icons/VPIconChevronDown.vue b/src/client/theme-default/components/icons/VPIconChevronDown.vue new file mode 100644 index 00000000..d72f4ee8 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconChevronDown.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconChevronLeft.vue b/src/client/theme-default/components/icons/VPIconChevronLeft.vue new file mode 100644 index 00000000..013eb7fd --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconChevronLeft.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconChevronRight.vue b/src/client/theme-default/components/icons/VPIconChevronRight.vue new file mode 100644 index 00000000..cf72418f --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconChevronRight.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconChevronUp.vue b/src/client/theme-default/components/icons/VPIconChevronUp.vue new file mode 100644 index 00000000..0b6a873e --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconChevronUp.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconDiscord.vue b/src/client/theme-default/components/icons/VPIconDiscord.vue new file mode 100644 index 00000000..59174574 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconDiscord.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconEdit.vue b/src/client/theme-default/components/icons/VPIconEdit.vue new file mode 100644 index 00000000..f30a62dd --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconEdit.vue @@ -0,0 +1,6 @@ + diff --git a/src/client/theme-default/components/icons/VPIconExternalLink.vue b/src/client/theme-default/components/icons/VPIconExternalLink.vue new file mode 100644 index 00000000..72459909 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconExternalLink.vue @@ -0,0 +1,13 @@ + diff --git a/src/client/theme-default/components/icons/VPIconFacebook.vue b/src/client/theme-default/components/icons/VPIconFacebook.vue new file mode 100644 index 00000000..6d9b6740 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconFacebook.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconGitHub.vue b/src/client/theme-default/components/icons/VPIconGitHub.vue new file mode 100644 index 00000000..ef59cdab --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconGitHub.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconInstagram.vue b/src/client/theme-default/components/icons/VPIconInstagram.vue new file mode 100644 index 00000000..2b77d52c --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconInstagram.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconLanguages.vue b/src/client/theme-default/components/icons/VPIconLanguages.vue new file mode 100644 index 00000000..71a728fc --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconLanguages.vue @@ -0,0 +1,9 @@ + diff --git a/src/client/theme-default/components/icons/VPIconLinkedIn.vue b/src/client/theme-default/components/icons/VPIconLinkedIn.vue new file mode 100644 index 00000000..75b321fd --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconLinkedIn.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconMoon.vue b/src/client/theme-default/components/icons/VPIconMoon.vue new file mode 100644 index 00000000..a9b205c6 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconMoon.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconMoreHorizontal.vue b/src/client/theme-default/components/icons/VPIconMoreHorizontal.vue new file mode 100644 index 00000000..6fa7fca2 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconMoreHorizontal.vue @@ -0,0 +1,7 @@ + diff --git a/src/client/theme-default/components/icons/VPIconPlus.vue b/src/client/theme-default/components/icons/VPIconPlus.vue new file mode 100644 index 00000000..74d9f695 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconPlus.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconSlack.vue b/src/client/theme-default/components/icons/VPIconSlack.vue new file mode 100644 index 00000000..9a1896ce --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconSlack.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconSun.vue b/src/client/theme-default/components/icons/VPIconSun.vue new file mode 100644 index 00000000..8ecb25ba --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconSun.vue @@ -0,0 +1,13 @@ + diff --git a/src/client/theme-default/components/icons/VPIconTwitter.vue b/src/client/theme-default/components/icons/VPIconTwitter.vue new file mode 100644 index 00000000..53250404 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconTwitter.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/components/icons/VPIconYouTube.vue b/src/client/theme-default/components/icons/VPIconYouTube.vue new file mode 100644 index 00000000..f5bc35a0 --- /dev/null +++ b/src/client/theme-default/components/icons/VPIconYouTube.vue @@ -0,0 +1,5 @@ + diff --git a/src/client/theme-default/composables/activeSidebarLink.ts b/src/client/theme-default/composables/activeSidebarLink.ts deleted file mode 100644 index 1b3b1d01..00000000 --- a/src/client/theme-default/composables/activeSidebarLink.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { onMounted, onUnmounted, onUpdated } from 'vue' - -export function useActiveSidebarLinks() { - let rootActiveLink: HTMLAnchorElement | null = null - let activeLink: HTMLAnchorElement | null = null - - const onScroll = throttleAndDebounce(setActiveLink, 300) - - function setActiveLink(): void { - const sidebarLinks = getSidebarLinks() - const anchors = getAnchors(sidebarLinks) - - for (let i = 0; i < anchors.length; i++) { - const anchor = anchors[i] - const nextAnchor = anchors[i + 1] - - const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor) - - if (isActive) { - history.replaceState(null, document.title, hash ? hash : ' ') - activateLink(hash) - return - } - } - } - - function activateLink(hash: string | null): void { - deactiveLink(activeLink) - deactiveLink(rootActiveLink) - - activeLink = document.querySelector(`.sidebar a[href="${hash}"]`) - - if (!activeLink) { - return - } - - activeLink.classList.add('active') - - // also add active class to parent h2 anchors - const rootLi = activeLink.closest('.sidebar-links > ul > li') - - if (rootLi && rootLi !== activeLink.parentElement) { - rootActiveLink = rootLi.querySelector('a') - rootActiveLink && rootActiveLink.classList.add('active') - } else { - rootActiveLink = null - } - } - - function deactiveLink(link: HTMLAnchorElement | null): void { - link && link.classList.remove('active') - } - - onMounted(() => { - setActiveLink() - window.addEventListener('scroll', onScroll) - }) - - onUpdated(() => { - // sidebar update means a route change - activateLink(decodeURIComponent(location.hash)) - }) - - onUnmounted(() => { - window.removeEventListener('scroll', onScroll) - }) -} - -function getSidebarLinks(): HTMLAnchorElement[] { - return [].slice.call( - document.querySelectorAll('.sidebar a.sidebar-link-item') - ) -} - -function getAnchors(sidebarLinks: HTMLAnchorElement[]): HTMLAnchorElement[] { - return [].slice - .call(document.querySelectorAll('.header-anchor')) - .filter((anchor: HTMLAnchorElement) => - sidebarLinks.some((sidebarLink) => sidebarLink.hash === anchor.hash) - ) as HTMLAnchorElement[] -} - -function getPageOffset(): number { - return (document.querySelector('.nav-bar') as HTMLElement).offsetHeight -} - -function getAnchorTop(anchor: HTMLAnchorElement): number { - const pageOffset = getPageOffset() - - return anchor.parentElement!.offsetTop - pageOffset - 15 -} - -function isAnchorActive( - index: number, - anchor: HTMLAnchorElement, - nextAnchor: HTMLAnchorElement -): [boolean, string | null] { - const scrollTop = window.scrollY - - if (index === 0 && scrollTop === 0) { - return [true, null] - } - - if (scrollTop < getAnchorTop(anchor)) { - return [false, null] - } - - if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) { - return [true, decodeURIComponent(anchor.hash)] - } - - return [false, null] -} - -function throttleAndDebounce(fn: () => void, delay: number): () => void { - let timeout: number - let called = false - - return () => { - if (timeout) { - clearTimeout(timeout) - } - - if (!called) { - fn() - called = true - setTimeout(() => { - called = false - }, delay) - } else { - timeout = setTimeout(fn, delay) - } - } -} diff --git a/src/client/theme-default/composables/edit-link.ts b/src/client/theme-default/composables/edit-link.ts new file mode 100644 index 00000000..32bf66c2 --- /dev/null +++ b/src/client/theme-default/composables/edit-link.ts @@ -0,0 +1,26 @@ +import { computed } from 'vue' +import { useData } from 'vitepress' + +export function useEditLink() { + const { theme, page } = useData() + + return computed(() => { + const url = [ + 'https://github.com', + theme.value.editLink?.repo || '???', + '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 + } + }) +} diff --git a/src/client/theme-default/composables/editLink.ts b/src/client/theme-default/composables/editLink.ts deleted file mode 100644 index f6cfdcc3..00000000 --- a/src/client/theme-default/composables/editLink.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { computed } from 'vue' -import { useData } from 'vitepress' -import { endingSlashRE, isExternal } from '../utils' - -const bitbucketRE = /bitbucket.org/ - -export function useEditLink() { - const { page, theme, frontmatter } = useData() - - const url = computed(() => { - const { - repo, - docsDir = '', - docsBranch = 'master', - docsRepo = repo, - editLinks - } = theme.value - - const showEditLink = - frontmatter.value.editLink != null - ? frontmatter.value.editLink - : editLinks - const { relativePath } = page.value - - if (!showEditLink || !relativePath || !repo) { - return null - } - - return createUrl(repo, docsRepo, docsDir, docsBranch, relativePath) - }) - - const text = computed(() => { - return theme.value.editLinkText || 'Edit this page' - }) - - return { - url, - text - } -} - -function createUrl( - repo: string, - docsRepo: string, - docsDir: string, - docsBranch: string, - path: string -): string { - return bitbucketRE.test(repo) - ? createBitbucketUrl(repo, docsRepo, docsDir, docsBranch, path) - : createGitHubUrl(repo, docsRepo, docsDir, docsBranch, path) -} - -function createGitHubUrl( - repo: string, - docsRepo: string, - docsDir: string, - docsBranch: string, - path: string -): string { - const base = isExternal(docsRepo) - ? docsRepo - : `https://github.com/${docsRepo}` - - return ( - base.replace(endingSlashRE, '') + - `/edit` + - `/${docsBranch}/` + - (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') + - path - ) -} - -function createBitbucketUrl( - repo: string, - docsRepo: string, - docsDir: string, - docsBranch: string, - path: string -): string { - const base = isExternal(docsRepo) ? docsRepo : repo - - return ( - base.replace(endingSlashRE, '') + - `/src` + - `/${docsBranch}/` + - (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') + - path + - `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default` - ) -} diff --git a/src/client/theme-default/composables/flyout.ts b/src/client/theme-default/composables/flyout.ts new file mode 100644 index 00000000..f71c1d54 --- /dev/null +++ b/src/client/theme-default/composables/flyout.ts @@ -0,0 +1,58 @@ +import { Ref, ref, watch, readonly, onUnmounted } from 'vue' + +interface UseFlyoutOptions { + el: Ref + onFocus?(): void + onBlur?(): void +} + +export const focusedElement = ref() + +let active = false +let listeners = 0 + +export function useFlyout(options: UseFlyoutOptions) { + const focus = ref(false) + + if (typeof window !== 'undefined') { + !active && activateFocusTracking() + + listeners++ + + const unwatch = watch(focusedElement, (el) => { + if (el === options.el.value || options.el.value?.contains(el as Node)) { + focus.value = true + options.onFocus?.() + } else { + focus.value = false + options.onBlur?.() + } + }) + + onUnmounted(() => { + unwatch() + + listeners-- + + if (!listeners) { + deactivateFocusTracking() + } + }) + } + + return readonly(focus) +} + +function activateFocusTracking() { + document.addEventListener('focusin', handleFocusIn) + active = true + focusedElement.value = document.activeElement as HTMLElement +} + +function deactivateFocusTracking() { + document.removeEventListener('focusin', handleFocusIn) +} + +function handleFocusIn() { + focusedElement.value = document.activeElement as HTMLElement +} diff --git a/src/client/theme-default/composables/nav.ts b/src/client/theme-default/composables/nav.ts index 7c935721..d719dbb3 100644 --- a/src/client/theme-default/composables/nav.ts +++ b/src/client/theme-default/composables/nav.ts @@ -1,7 +1,39 @@ -import { computed } from 'vue' +import { ref, computed } from 'vue' import { useData, useRoute } from 'vitepress' import type { DefaultTheme } from '../config' +export function useNav() { + const isScreenOpen = ref(false) + + function openScreen() { + isScreenOpen.value = true + window.addEventListener('resize', closeScreenOnTabletWindow) + } + + function closeScreen() { + isScreenOpen.value = false + window.removeEventListener('resize', closeScreenOnTabletWindow) + } + + function toggleScreen() { + isScreenOpen.value ? closeScreen() : openScreen() + } + + /** + * Close screen when the user resizes the window wider than tablet size. + */ + function closeScreenOnTabletWindow() { + window.outerWidth >= 768 && closeScreen() + } + + return { + isScreenOpen, + openScreen, + closeScreen, + toggleScreen + } +} + export function useLanguageLinks() { const { site, localePath, theme } = useData() diff --git a/src/client/theme-default/composables/navLink.ts b/src/client/theme-default/composables/navLink.ts deleted file mode 100644 index bc7a4d44..00000000 --- a/src/client/theme-default/composables/navLink.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { computed, Ref } from 'vue' -import { useRoute, withBase } from 'vitepress' -import { isExternal as isExternalCheck } from '../utils' -import type { DefaultTheme } from '../config' - -export function useNavLink(item: Ref) { - const route = useRoute() - - const isExternal = isExternalCheck(item.value.link) - - const props = computed(() => { - const routePath = normalizePath(`/${route.data.relativePath}`) - - let active = false - if (item.value.activeMatch) { - active = new RegExp(item.value.activeMatch).test(routePath) - } else { - const itemPath = normalizePath(item.value.link) - active = - itemPath === '/' - ? itemPath === routePath - : routePath.startsWith(itemPath) - } - - return { - class: { - active, - isExternal - }, - href: isExternal ? item.value.link : withBase(item.value.link), - target: item.value.target || (isExternal ? `_blank` : null), - rel: item.value.rel || (isExternal ? `noopener noreferrer` : null), - 'aria-label': item.value.ariaLabel - } - }) - - return { - props, - isExternal - } -} - -function normalizePath(path: string): string { - return path - .replace(/#.*$/, '') - .replace(/\?.*$/, '') - .replace(/\.(html|md)$/, '') - .replace(/\/index$/, '/') -} diff --git a/src/client/theme-default/composables/nextAndPrevLinks.ts b/src/client/theme-default/composables/nextAndPrevLinks.ts deleted file mode 100644 index d7006ea6..00000000 --- a/src/client/theme-default/composables/nextAndPrevLinks.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { computed } from 'vue' -import { useData } from 'vitepress' -import { isArray, ensureStartingSlash, removeExtention } from '../utils' -import { getSideBarConfig, getFlatSideBarLinks } from '../support/sideBar' - -export function useNextAndPrevLinks() { - const { page, theme } = useData() - - const path = computed(() => { - return removeExtention(ensureStartingSlash(page.value.relativePath)) - }) - - const candidates = computed(() => { - const config = getSideBarConfig(theme.value.sidebar, path.value) - - return isArray(config) ? getFlatSideBarLinks(config) : [] - }) - - const index = computed(() => { - return candidates.value.findIndex((item) => { - return item.link === path.value - }) - }) - - const next = computed(() => { - if ( - theme.value.nextLinks !== false && - index.value > -1 && - index.value < candidates.value.length - 1 - ) { - return candidates.value[index.value + 1] - } - }) - - const prev = computed(() => { - if (theme.value.prevLinks !== false && index.value > 0) { - return candidates.value[index.value - 1] - } - }) - - const hasLinks = computed(() => !!next.value || !!prev.value) - - return { - next, - prev, - hasLinks - } -} diff --git a/src/client/theme-default/composables/outline.ts b/src/client/theme-default/composables/outline.ts new file mode 100644 index 00000000..f1ce455a --- /dev/null +++ b/src/client/theme-default/composables/outline.ts @@ -0,0 +1,185 @@ +import { Ref, onMounted, onUpdated, onUnmounted } from 'vue' +import { Header } from 'vitepress' +import { useMediaQuery } from '@vueuse/core' + +interface HeaderWithChildren extends Header { + children?: Header[] + hidden?: boolean +} + +interface MenuItemWithLinkAndChildren { + text: string + link: string + children?: MenuItemWithLinkAndChildren[] + hidden?: boolean +} + +// magic number to avoid repeated retrieval +const PAGE_OFFSET = 56 + +export function resolveHeaders(headers: Header[]) { + return mapHeaders(groupHeaders(headers)) +} + +function groupHeaders(headers: Header[]): HeaderWithChildren[] { + headers = headers.map((h) => Object.assign({}, h)) + + let lastH2: HeaderWithChildren | undefined + + for (const h of headers) { + if (h.level === 2) { + lastH2 = h + } else if (lastH2 && h.level <= 3) { + ;(lastH2.children || (lastH2.children = [])).push(h) + } + } + + return headers.filter((h) => h.level === 2) +} + +function mapHeaders( + headers: HeaderWithChildren[] +): MenuItemWithLinkAndChildren[] { + return headers.map((header) => ({ + text: header.title, + link: `#${header.slug}`, + children: header.children ? mapHeaders(header.children) : undefined, + hidden: header.hidden + })) +} + +export function useActiveAnchor( + container: Ref, + marker: Ref +) { + const isOutlineEnabled = useMediaQuery('(min-width: 1280px)') + const onScroll = throttleAndDebounce(setActiveLink, 100) + + let prevActiveLink: HTMLAnchorElement | null = null + + onMounted(() => { + requestAnimationFrame(setActiveLink) + window.addEventListener('scroll', onScroll) + }) + + onUpdated(() => { + // sidebar update means a route change + activateLink(location.hash) + }) + + onUnmounted(() => { + window.removeEventListener('scroll', onScroll) + }) + + function setActiveLink() { + if (!isOutlineEnabled.value) { + return + } + + const links = [].slice.call( + container.value.querySelectorAll('.outline-link') + ) as HTMLAnchorElement[] + + const anchors = [].slice + .call(document.querySelectorAll('.content .header-anchor')) + .filter((anchor: HTMLAnchorElement) => { + return links.some((link) => { + return link.hash === anchor.hash && anchor.offsetParent !== null + }) + }) as HTMLAnchorElement[] + + const scrollY = window.scrollY + const innerHeight = window.innerHeight + const offsetHeight = document.body.offsetHeight + const isBottom = scrollY + innerHeight === offsetHeight + + // page bottom - highlight last one + if (anchors.length && isBottom) { + activateLink(anchors[anchors.length - 1].hash) + return + } + + for (let i = 0; i < anchors.length; i++) { + const anchor = anchors[i] + const nextAnchor = anchors[i + 1] + + const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor) + + if (isActive) { + history.replaceState(null, document.title, hash ? hash : ' ') + activateLink(hash) + return + } + } + } + + function activateLink(hash: string | null) { + if (prevActiveLink) { + prevActiveLink.classList.remove('active') + } + + if (hash !== null) { + prevActiveLink = container.value.querySelector( + `a[href="${decodeURIComponent(hash)}"]` + ) as HTMLAnchorElement + } + + const activeLink = prevActiveLink + + if (activeLink) { + activeLink.classList.add('active') + marker.value.style.top = activeLink.offsetTop + 33 + 'px' + marker.value.style.opacity = '1' + } else { + marker.value.style.top = '33px' + marker.value.style.opacity = '0' + } + } +} + +function getAnchorTop(anchor: HTMLAnchorElement): number { + return anchor.parentElement!.offsetTop - PAGE_OFFSET - 15 +} + +function isAnchorActive( + index: number, + anchor: HTMLAnchorElement, + nextAnchor: HTMLAnchorElement | undefined +): [boolean, string | null] { + const scrollTop = window.scrollY + + if (index === 0 && scrollTop === 0) { + return [true, null] + } + + if (scrollTop < getAnchorTop(anchor)) { + return [false, null] + } + + if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) { + return [true, anchor.hash] + } + + return [false, null] +} + +function throttleAndDebounce(fn: () => void, delay: number): () => void { + let timeout: number + let called = false + + return () => { + if (timeout) { + clearTimeout(timeout) + } + + if (!called) { + fn() + called = true + setTimeout(() => { + called = false + }, delay) + } else { + timeout = setTimeout(fn, delay) + } + } +} diff --git a/src/client/theme-default/composables/prev-next.ts b/src/client/theme-default/composables/prev-next.ts new file mode 100644 index 00000000..e09cb0e0 --- /dev/null +++ b/src/client/theme-default/composables/prev-next.ts @@ -0,0 +1,22 @@ +import { computed } from 'vue' +import { useData } from 'vitepress' +import { isActive } from '../support/utils' +import { getSidebar, getFlatSideBarLinks } from '../support/sidebar' + +export function usePrevNext() { + const { page, theme } = useData() + + return computed(() => { + const sidebar = getSidebar(theme.value.sidebar, page.value.relativePath) + const candidates = getFlatSideBarLinks(sidebar) + + const index = candidates.findIndex((link) => { + return isActive(page.value.relativePath, link.link) + }) + + return { + prev: candidates[index - 1], + next: candidates[index + 1] + } + }) +} diff --git a/src/client/theme-default/composables/repo.ts b/src/client/theme-default/composables/repo.ts deleted file mode 100644 index 5bb2112b..00000000 --- a/src/client/theme-default/composables/repo.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { computed } from 'vue' -import { useData } from 'vitepress' -import type { DefaultTheme } from '../config' -import { EXTERNAL_URL_RE } from '../../shared' - -export const platforms = ['GitHub', 'GitLab', 'Bitbucket'].map((platform) => { - return [platform, new RegExp(platform, 'i')] as const -}) - -export function useRepo() { - const { site } = useData() - - return computed(() => { - const theme = site.value.themeConfig as DefaultTheme.Config - const name = theme.docsRepo || theme.repo - - if (!name) { - return null - } - - const link = getRepoUrl(name) - const text = getRepoText(link, theme.repoLabel) - - return { text, link } - }) -} - -function getRepoUrl(repo: string): string { - // if the full url is not provided, default to GitHub repo - return EXTERNAL_URL_RE.test(repo) ? repo : `https://github.com/${repo}` -} - -function getRepoText(url: string, text?: string): string { - if (text) { - return text - } - - // if no label is provided, deduce it from the repo url - const hosts = url.match(/^https?:\/\/[^/]+/) - - if (!hosts) { - return 'Source' - } - - const platform = platforms.find(([_p, re]) => re.test(hosts[0])) - - if (platform && platform[0]) { - return platform[0] - } - - return 'Source' -} diff --git a/src/client/theme-default/composables/sideBar.ts b/src/client/theme-default/composables/sideBar.ts index 96fb6b24..2aa34a98 100644 --- a/src/client/theme-default/composables/sideBar.ts +++ b/src/client/theme-default/composables/sideBar.ts @@ -1,77 +1,71 @@ -import { computed } from 'vue' +import { Ref, ref, computed, watchEffect, onMounted, onUnmounted } from 'vue' import { useRoute, useData } from 'vitepress' -import { Header } from '../../shared' -import { useActiveSidebarLinks } from '../composables/activeSidebarLink' -import { getSideBarConfig } from '../support/sideBar' -import { DefaultTheme } from '../config' +import { getSidebar } from '../support/sidebar' -export function useSideBar() { +export function useSidebar() { const route = useRoute() - const { site } = useData() + const { theme, frontmatter } = useData() - useActiveSidebarLinks() + const isOpen = ref(false) - return computed(() => { - // at first, we'll check if we can find the sidebar setting in frontmatter. - const headers = route.data.headers - const frontSidebar = route.data.frontmatter.sidebar - const sidebarDepth = route.data.frontmatter.sidebarDepth + const sidebar = computed(() => { + const sidebarConfig = theme.value.sidebar + const relativePath = route.data.relativePath - // if it's `false`, we'll just return an empty array here. - if (frontSidebar === false) { - return [] - } + return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : [] + }) - // if it's `auto`, render headers of the current page - if (frontSidebar === 'auto') { - return resolveAutoSidebar(headers, sidebarDepth) - } + const hasSidebar = computed(() => { + return frontmatter.value.sidebar !== false && sidebar.value.length > 0 + }) - // now, there's no sidebar setting at frontmatter; let's see the configs - const themeSidebar = getSideBarConfig( - site.value.themeConfig.sidebar, - route.data.relativePath - ) + function open() { + isOpen.value = true + } - if (themeSidebar === false) { - return [] - } + function close() { + isOpen.value = false + } - if (themeSidebar === 'auto') { - return resolveAutoSidebar(headers, sidebarDepth) - } + function toggle() { + isOpen.value ? close() : open() + } - return themeSidebar - }) + return { + isOpen, + sidebar, + hasSidebar, + open, + close, + toggle + } } -function resolveAutoSidebar( - headers: Header[], - depth: number -): DefaultTheme.SideBarItem[] { - const ret: DefaultTheme.SideBarItem[] = [] +/** + * a11y: cache the element that opened the Sidebar (the menu button) then + * focus that button again when Menu is closed with Escape key. + */ +export function useCloseSidebarOnEscape(isOpen: Ref, close: () => {}) { + let triggerElement: HTMLButtonElement | undefined - if (headers === undefined) { - return [] - } + watchEffect(() => { + triggerElement = isOpen.value + ? (document.activeElement as HTMLButtonElement) + : undefined + }) - let lastH2: DefaultTheme.SideBarItem | undefined = undefined - headers.forEach(({ level, title, slug }) => { - if (level - 1 > depth) { - return - } + onMounted(() => { + window.addEventListener('keyup', onEscape) + }) - const item: DefaultTheme.SideBarItem = { - text: title, - link: `#${slug}` - } - if (level === 2) { - lastH2 = item - ret.push(item) - } else if (lastH2) { - ;((lastH2 as any).children || ((lastH2 as any).children = [])).push(item) - } + onUnmounted(() => { + window.removeEventListener('keyup', onEscape) }) - return ret + function onEscape(e: KeyboardEvent) { + if (e.key === 'Escape' && isOpen.value) { + close() + triggerElement?.focus() + } + } } diff --git a/src/client/theme-default/fonts/inter-cyrillic-ext.woff2 b/src/client/theme-default/fonts/inter-cyrillic-ext.woff2 new file mode 100644 index 00000000..e5ccce41 Binary files /dev/null and b/src/client/theme-default/fonts/inter-cyrillic-ext.woff2 differ diff --git a/src/client/theme-default/fonts/inter-cyrillic.woff2 b/src/client/theme-default/fonts/inter-cyrillic.woff2 new file mode 100644 index 00000000..56063960 Binary files /dev/null and b/src/client/theme-default/fonts/inter-cyrillic.woff2 differ diff --git a/src/client/theme-default/fonts/inter-greek-ext.woff2 b/src/client/theme-default/fonts/inter-greek-ext.woff2 new file mode 100644 index 00000000..e07f5c96 Binary files /dev/null and b/src/client/theme-default/fonts/inter-greek-ext.woff2 differ diff --git a/src/client/theme-default/fonts/inter-greek.woff2 b/src/client/theme-default/fonts/inter-greek.woff2 new file mode 100644 index 00000000..94c69138 Binary files /dev/null and b/src/client/theme-default/fonts/inter-greek.woff2 differ diff --git a/src/client/theme-default/fonts/inter-latin-ext.woff2 b/src/client/theme-default/fonts/inter-latin-ext.woff2 new file mode 100644 index 00000000..0e5999b6 Binary files /dev/null and b/src/client/theme-default/fonts/inter-latin-ext.woff2 differ diff --git a/src/client/theme-default/fonts/inter-latin.woff2 b/src/client/theme-default/fonts/inter-latin.woff2 new file mode 100644 index 00000000..2fbab974 Binary files /dev/null and b/src/client/theme-default/fonts/inter-latin.woff2 differ diff --git a/src/client/theme-default/fonts/inter-vietnamese.woff2 b/src/client/theme-default/fonts/inter-vietnamese.woff2 new file mode 100644 index 00000000..f29f5de6 Binary files /dev/null and b/src/client/theme-default/fonts/inter-vietnamese.woff2 differ diff --git a/src/client/theme-default/index.ts b/src/client/theme-default/index.ts index bcd79051..c3d43d7a 100644 --- a/src/client/theme-default/index.ts +++ b/src/client/theme-default/index.ts @@ -1,14 +1,15 @@ +import './styles/fonts.css' import './styles/vars.css' -import './styles/layout.css' -import './styles/code.css' -import './styles/custom-blocks.css' -import './styles/sidebar-links.css' +import './styles/base.css' +import './styles/utils.css' +import './styles/vp-doc.css' import { Theme } from 'vitepress' import Layout from './Layout.vue' import NotFound from './NotFound.vue' export { DefaultTheme } from './config' + const theme: Theme = { Layout, NotFound diff --git a/src/client/theme-default/styles/base.css b/src/client/theme-default/styles/base.css new file mode 100644 index 00000000..6873ce91 --- /dev/null +++ b/src/client/theme-default/styles/base.css @@ -0,0 +1,200 @@ +*, +::before, +::after { + box-sizing: border-box; +} + +html { + line-height: 1.4; + font-size: 16px; + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + width: 100%; + min-width: 320px; + min-height: 100vh; + line-height: 24px; + font-family: var(--vp-font-family-base); + font-size: 16px; + font-weight: 400; + color: var(--vp-c-text-1); + background-color: var(--vp-c-bg-content); + direction: ltr; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +main { + display: block; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; +} + +p { + margin: 0; +} + +strong, +b { + font-weight: 600; +} + +/** + * Avoid 300ms click delay on touch devices that support the `touch-action` + * CSS property. + * + * In particular, unlike most other browsers, IE11+Edge on Windows 10 on + * touch devices and IE Mobile 10-11 DON'T remove the click delay when + * `` is present. + * However, they DO support removing the click delay via + * `touch-action: manipulation`. + * + * See: + * - http://v4-alpha.getbootstrap.com/content/reboot/#click-delay-optimization-for-touch + * - http://caniuse.com/#feat=css-touch-action + * - http://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay + */ +a, +area, +button, +[role="button"], +input, +label, +select, +summary, +textarea { + touch-action: manipulation; +} + +a { + color: inherit; + text-decoration: inherit; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +pre, +code, +kbd, +samp { + font-family: var(--vp-font-family-mono); +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +img, +video { + max-width: 100%; + height: auto; +} + +button, +input, +optgroup, +select, +textarea { + border: 0; + padding: 0; + line-height: inherit; + color: inherit; +} + +button { + padding: 0; + font-family: inherit;; + background-color: transparent; + background-image: none; +} + +button, +[role="button"] { + cursor: pointer; +} + +button:focus, +button:focus-visible { + outline: 1px dotted; + outline: 4px auto -webkit-focus-ring-color; +} + +button:focus:not(:focus-visible) { + outline: none !important; +} + +input:focus, +textarea:focus, +select:focus { + outline: none; +} + +table { + border-collapse: collapse; +} + +input { + background-color: transparent; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: var(--vp-c-text-3); +} + +input::-ms-input-placeholder, +textarea::-ms-input-placeholder { + color: var(--vp-c-text-3); +} + +input::placeholder, +textarea::placeholder { + color: var(--vp-c-text-3); +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type="number"] { + -moz-appearance: textfield; +} + +textarea { + resize: vertical; +} + +select { + -webkit-appearance: none; +} + +fieldset { + margin: 0; + padding: 0; +} diff --git a/src/client/theme-default/styles/code.css b/src/client/theme-default/styles/code.css deleted file mode 100644 index 436e2baa..00000000 --- a/src/client/theme-default/styles/code.css +++ /dev/null @@ -1,299 +0,0 @@ -code { - margin: 0; - border-radius: 3px; - padding: 0.25rem 0.5rem; - font-family: var(--code-font-family); - font-size: 0.85em; - color: var(--c-text-light); - background-color: var(--code-inline-bg-color); -} - -code .token.deleted { - color: #ec5975; -} - -code .token.inserted { - color: var(--c-brand); -} - -div[class*='language-'] { - position: relative; - margin: 1rem -1.5rem; - background-color: var(--code-bg-color); - overflow-x: auto; -} - -li > div[class*='language-'] { - border-radius: 6px 0 0 6px; - margin: 1rem -1.5rem 1rem -1.25rem; - line-height: initial; -} - -@media (min-width: 420px) { - div[class*='language-'] { - margin: 1rem 0; - border-radius: 6px; - } - - li > div[class*='language-'] { - margin: 1rem 0 1rem 0rem; - border-radius: 6px; - } -} - -[class*='language-'] pre, -[class*='language-'] code { - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; - background: transparent; -} - -[class*='language-'] pre { - position: relative; - z-index: 1; - margin: 0; - padding: 1.25rem 1.5rem; - overflow-x: auto; -} - -[class*='language-'] code { - padding: 0; - line-height: var(--code-line-height); - font-size: var(--code-font-size); - color: #eee; -} - -/* Line highlighting */ - -.highlight-lines { - position: absolute; - top: 0; - bottom: 0; - left: 0; - padding: 1.25rem 0; - width: 100%; - line-height: var(--code-line-height); - font-family: var(--code-font-family); - font-size: var(--code-font-size); - user-select: none; - overflow: hidden; -} - -.highlight-lines .highlighted { - background-color: rgba(0, 0, 0, 0.66); -} - -/* Line numbers mode */ - -div[class*='language-'].line-numbers-mode { - padding-left: 3.5rem; -} - -.line-numbers-wrapper { - position: absolute; - top: 0; - bottom: 0; - left: 0; - z-index: 3; - border-right: 1px solid rgba(0, 0, 0, 0.5); - padding: 1.25rem 0; - width: 3.5rem; - text-align: center; - line-height: var(--code-line-height); - font-family: var(--code-font-family); - font-size: var(--code-font-size); - color: #888; -} - -/* Language marker */ - -div[class*='language-']:before { - position: absolute; - top: 0.6em; - right: 1em; - z-index: 2; - font-size: 0.8rem; - color: #888; -} - -div[class~='language-html']:before, -div[class~='language-markup']:before { - content: 'html'; -} - -div[class~='language-md']:before, -div[class~='language-markdown']:before { - content: 'md'; -} - -div[class~='language-css']:before { - content: 'css'; -} - -div[class~='language-sass']:before { - content: 'sass'; -} - -div[class~='language-scss']:before { - content: 'scss'; -} - -div[class~='language-less']:before { - content: 'less'; -} - -div[class~='language-stylus']:before { - content: 'styl'; -} - -div[class~='language-js']:before, -div[class~='language-javascript']:before { - content: 'js'; -} - -div[class~='language-ts']:before, -div[class~='language-typescript']:before { - content: 'ts'; -} - -div[class~='language-json']:before { - content: 'json'; -} - -div[class~='language-rb']:before, -div[class~='language-ruby']:before { - content: 'rb'; -} - -div[class~='language-py']:before, -div[class~='language-python']:before { - content: 'py'; -} - -div[class~='language-sh']:before, -div[class~='language-bash']:before { - content: 'sh'; -} - -div[class~='language-php']:before { - content: 'php'; -} - -div[class~='language-go']:before { - content: 'go'; -} - -div[class~='language-rust']:before { - content: 'rust'; -} - -div[class~='language-java']:before { - content: 'java'; -} - -div[class~='language-c']:before { - content: 'c'; -} - -div[class~='language-yaml']:before { - content: 'yaml'; -} - -div[class~='language-dockerfile']:before { - content: 'dockerfile'; -} - -div[class~='language-vue']:before { - content: 'vue'; -} - -/** - * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML. - * Based on https://github.com/chriskempson/tomorrow-theme - * - * @author Rose Pritchard - */ -.token.comment, -.token.block-comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: #999; -} - -.token.punctuation { - color: #ccc; -} - -.token.tag, -.token.attr-name, -.token.namespace, -.token.deleted { - color: #e2777a; -} - -.token.function-name { - color: #6196cc; -} - -.token.boolean, -.token.number, -.token.function { - color: #f08d49; -} - -.token.property, -.token.class-name, -.token.constant, -.token.symbol { - color: #f8c555; -} - -.token.selector, -.token.important, -.token.atrule, -.token.keyword, -.token.builtin { - color: #cc99cd; -} - -.token.string, -.token.char, -.token.attr-value, -.token.regex, -.token.variable { - color: #7ec699; -} - -.token.operator, -.token.entity, -.token.url { - color: #67cdcc; -} - -.token.important, -.token.bold { - font-weight: bold; -} - -.token.italic { - font-style: italic; -} - -.token.entity { - cursor: help; -} - -.token.inserted { - color: green; -} diff --git a/src/client/theme-default/styles/custom-blocks.css b/src/client/theme-default/styles/custom-blocks.css deleted file mode 100644 index 38507ace..00000000 --- a/src/client/theme-default/styles/custom-blocks.css +++ /dev/null @@ -1,76 +0,0 @@ -.custom-block.tip, -.custom-block.info, -.custom-block.warning, -.custom-block.danger { - margin: 1rem 0; - border-left: 0.5rem solid; - padding: 0.1rem 1.5rem; - overflow-x: auto; -} - -.custom-block.tip { - background-color: #f3f5f7; - border-color: var(--c-brand); -} - -.custom-block.info { - background-color: #f3f5f7; - border-color: var(--c-text-light-2); -} - -.custom-block.warning { - border-color: #e7c000; - color: #6b5900; - background-color: rgba(255, 229, 100, 0.3); -} - -.custom-block.warning .custom-block-title { - color: #b29400; -} - -.custom-block.warning a { - color: var(--c-text); -} - -.custom-block.danger { - border-color: #c00; - color: #4d0000; - background-color: #ffe6e6; -} - -.custom-block.danger .custom-block-title { - color: #900; -} - -.custom-block.danger a { - color: var(--c-text); -} - -.custom-block.details { - position: relative; - display: block; - border-radius: 2px; - margin: 1.6em 0; - padding: 1.6em; - background-color: #eee; -} - -.custom-block.details h4 { - margin-top: 0; -} - -.custom-block.details figure:last-child, -.custom-block.details p:last-child { - margin-bottom: 0; - padding-bottom: 0; -} - -.custom-block.details summary { - outline: none; - cursor: pointer; -} - -.custom-block-title { - margin-bottom: -0.4rem; - font-weight: 600; -} diff --git a/src/client/theme-default/styles/fonts.css b/src/client/theme-default/styles/fonts.css new file mode 100644 index 00000000..e9c9db97 --- /dev/null +++ b/src/client/theme-default/styles/fonts.css @@ -0,0 +1,67 @@ +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url(../fonts/inter-latin.woff2) format('woff2'); + unicode-range: U+00??, U+0131, U+0152-0153, U+02bb-02bc, U+02c6, U+02da, + U+02dc, U+2000-206f, U+2074, U+20ac, U+2122, U+2191, U+2193, U+2212, U+2215, + U+feff, U+fffd; +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400 500 600 700 900; + font-display: swap; + src: url(../fonts/inter-latin-ext.woff2) format('woff2'); + unicode-range: U+0100-024f, U+0259, U+1e??, U+2020, U+20a0-20ab, U+20ad-20cf, + U+2113, U+2c60-2c7f, U+a720-a7ff; +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400 500 600 700 900; + font-display: swap; + src: url(../fonts/inter-cyrillic.woff2) format('woff2'); + unicode-range: U+0400-045f, U+0490-0491, U+04b0-04b1, U+2116; +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400 500 600 700 900; + font-display: swap; + src: url(../fonts/inter-cyrillic-ext.woff2) format('woff2'); + unicode-range: U+0460-052f, U+1c80-1c88, U+20b4, U+2de0-2dff, U+a640-a69f, + U+fe2e-fe2f; +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400 500 600 700 900; + font-display: swap; + src: url(../fonts/inter-greek.woff2) format('woff2'); + unicode-range: U+0370-03ff; +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400 500 600 700 900; + font-display: swap; + src: url(../fonts/inter-greek-ext.woff2) format('woff2'); + unicode-range: U+1f??; +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400 500 600 700 900; + font-display: swap; + src: url(../fonts/inter-vietnamese.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01a0-01a1, + U+01af-01b0, U+1ea0-1ef9, U+20ab; +} diff --git a/src/client/theme-default/styles/layout.css b/src/client/theme-default/styles/layout.css deleted file mode 100644 index 8d9e0a8e..00000000 --- a/src/client/theme-default/styles/layout.css +++ /dev/null @@ -1,236 +0,0 @@ -*, -::before, -::after { - box-sizing: border-box; -} - -html { - line-height: 1.4; - font-size: 16px; - -webkit-text-size-adjust: 100%; -} - -body { - margin: 0; - width: 100%; - min-width: 320px; - min-height: 100vh; - line-height: 1.4; - font-family: var(--font-family-base); - font-size: 16px; - font-weight: 400; - color: var(--c-text); - background-color: var(--c-bg); - direction: ltr; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -main { - display: block; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 0; - line-height: 1.25; -} - -h1, -h2, -h3, -h4, -h5, -h6, -strong, -b { - font-weight: 600; -} - -h1:hover .header-anchor, -h1:focus .header-anchor, -h2:hover .header-anchor, -h2:focus .header-anchor, -h3:hover .header-anchor, -h3:focus .header-anchor, -h4:hover .header-anchor, -h4:focus .header-anchor, -h5:hover .header-anchor, -h5:focus .header-anchor, -h6:hover .header-anchor, -h6:focus .header-anchor { - opacity: 1; -} - -h1 { - margin-top: 1.5rem; - font-size: 1.9rem; -} - -@media screen and (min-width: 420px) { - h1 { - font-size: 2.2rem; - } -} - -h2 { - margin-top: 2.25rem; - margin-bottom: 1.25rem; - border-bottom: 1px solid var(--c-divider); - padding-bottom: 0.3rem; - line-height: 1.25; - font-size: 1.65rem; - /* overflow-x: auto; */ -} - -h2 + h3 { - margin-top: 1.5rem; -} - -h3 { - margin-top: 2rem; - font-size: 1.35rem; -} - -h4 { - font-size: 1.15rem; -} - -p, -ol, -ul { - margin: 1rem 0; - line-height: 1.7; -} - -a, -area, -button, -[role='button'], -input, -label, -select, -summary, -textarea { - touch-action: manipulation; -} - -a { - text-decoration: none; - color: var(--c-brand); -} - -a:hover { - text-decoration: underline; -} - -a.header-anchor { - float: left; - margin-top: 0.125em; - margin-left: -0.87em; - padding-right: 0.23em; - font-size: 0.85em; - opacity: 0; -} - -a.header-anchor:hover, -a.header-anchor:focus { - text-decoration: none; -} - -figure { - margin: 0; -} - -img { - max-width: 100%; -} - -ul, -ol { - padding-left: 1.25em; -} - -li > ul, -li > ol { - margin: 0; -} - -table { - display: block; - border-collapse: collapse; - margin: 1rem 0; - overflow-x: auto; -} - -tr { - border-top: 1px solid #dfe2e5; -} - -tr:nth-child(2n) { - background-color: #f6f8fa; -} - -th, -td { - border: 1px solid #dfe2e5; - padding: 0.6em 1em; -} - -blockquote { - margin: 1rem 0; - border-left: 0.2rem solid #dfe2e5; - padding: 0.25rem 0 0.25rem 1rem; - font-size: 1rem; - color: #999; -} - -blockquote > p { - margin: 0; -} - -form { - margin: 0; -} - -.theme.sidebar-open .sidebar-mask { - display: block; -} - -.theme.no-navbar > h1, -.theme.no-navbar > h2, -.theme.no-navbar > h3, -.theme.no-navbar > h4, -.theme.no-navbar > h5, -.theme.no-navbar > h6 { - margin-top: 1.5rem; - padding-top: 0; -} - -.theme.no-navbar aside { - top: 0; -} - -@media screen and (min-width: 720px) { - .theme.no-sidebar aside { - display: none; - } - - .theme.no-sidebar main { - margin-left: 0; - } -} - -.sidebar-mask { - position: fixed; - z-index: 2; - display: none; - width: 100vw; - height: 100vh; -} diff --git a/src/client/theme-default/styles/sidebar-links.css b/src/client/theme-default/styles/sidebar-links.css deleted file mode 100644 index c15f53fd..00000000 --- a/src/client/theme-default/styles/sidebar-links.css +++ /dev/null @@ -1,107 +0,0 @@ -.sidebar-links { - margin: 0; - padding: 0; - list-style: none; -} - -.sidebar-link-item { - display: block; - margin: 0; - border-left: 0.25rem solid transparent; - color: var(--c-text); -} - -a.sidebar-link-item:hover { - text-decoration: none; - color: var(--c-brand); -} - -a.sidebar-link-item.active { - color: var(--c-brand); -} - -.sidebar > .sidebar-links { - padding: 0.75rem 0 5rem; -} - -@media (min-width: 720px) { - .sidebar > .sidebar-links { - padding: 1.5rem 0; - } -} - -.sidebar > .sidebar-links > .sidebar-link + .sidebar-link { - padding-top: 0.5rem; -} - -@media (min-width: 720px) { - .sidebar > .sidebar-links > .sidebar-link + .sidebar-link { - padding-top: 1.25rem; - } -} - -.sidebar > .sidebar-links > .sidebar-link > .sidebar-link-item { - padding: 0.35rem 1.5rem 0.35rem 1.25rem; - font-size: 1.1rem; - font-weight: 700; -} - -.sidebar > .sidebar-links > .sidebar-link > a.sidebar-link-item.active { - border-left-color: var(--c-brand); - font-weight: 600; -} - -.sidebar - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > .sidebar-link-item { - display: block; - padding: 0.35rem 1.5rem 0.35rem 2rem; - line-height: 1.4; - font-size: 1rem; - font-weight: 400; -} - -.sidebar - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > a.sidebar-link-item.active { - border-left-color: var(--c-brand); - font-weight: 600; -} - -.sidebar - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > .sidebar-link-item { - display: block; - padding: 0.3rem 1.5rem 0.3rem 3rem; - line-height: 1.4; - font-size: 0.9rem; - font-weight: 400; -} - -.sidebar - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > .sidebar-links - > .sidebar-link - > .sidebar-link-item { - display: block; - padding: 0.3rem 1.5rem 0.3rem 4rem; - line-height: 1.4; - font-size: 0.9rem; - font-weight: 400; -} diff --git a/src/client/theme-default/styles/utils.css b/src/client/theme-default/styles/utils.css new file mode 100644 index 00000000..65c7e55e --- /dev/null +++ b/src/client/theme-default/styles/utils.css @@ -0,0 +1,9 @@ +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + white-space: nowrap; + clip: rect(0 0 0 0); + clip-path: inset(50%); + overflow: hidden; +} diff --git a/src/client/theme-default/styles/vars.css b/src/client/theme-default/styles/vars.css index 78ec1203..27a38271 100644 --- a/src/client/theme-default/styles/vars.css +++ b/src/client/theme-default/styles/vars.css @@ -1,74 +1,214 @@ -/** Base Styles */ +/** + * Colors Base + * + * These are the pure base color presets. Most of the time, you should not be + * using these colors directly in the theme but rather use "Colors Theme" + * instead because those are "Theme (light or dark)" dependant. + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-white: #ffffff; + --vp-c-white-soft: #f9f9f9; + --vp-c-white-mute: #f1f1f1; + + --vp-c-black: #1a1a1a; + --vp-c-black-pure: #000000; + --vp-c-black-soft: #242424; + --vp-c-black-mute: #2f2f2f; + + --vp-c-gray: #8e8e8e; + --vp-c-gray-light-1: #aeaeae; + --vp-c-gray-light-2: #c7c7c7; + --vp-c-gray-light-3: #d1d1d1; + --vp-c-gray-light-4: #e5e5e5; + --vp-c-gray-light-5: #f2f2f2; + --vp-c-gray-dark-1: #636363; + --vp-c-gray-dark-2: #484848; + --vp-c-gray-dark-3: #3a3a3a; + --vp-c-gray-dark-4: #282828; + --vp-c-gray-dark-5: #202020; + + --vp-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vp-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vp-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vp-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vp-c-text-light-1: var(--vp-c-indigo); + --vp-c-text-light-2: rgba(60, 60, 60, 0.70); + --vp-c-text-light-3: rgba(60, 60, 60, 0.33); + --vp-c-text-light-4: rgba(60, 60, 60, 0.18); + + --vp-c-text-dark-1: rgba(255, 255, 255, 0.87); + --vp-c-text-dark-2: rgba(235, 235, 235, 0.60); + --vp-c-text-dark-3: rgba(235, 235, 235, 0.38); + --vp-c-text-dark-4: rgba(235, 235, 235, 0.18); + + --vp-c-green: #42b883; + --vp-c-green-light: #42d392; + --vp-c-green-lighter: #35eb9a; + --vp-c-green-dark: #33a06f; + --vp-c-green-darker: #155f3e; + + --vp-c-indigo: #213547; + --vp-c-indigo-soft: #476582; + --vp-c-indigo-light: #aac8e4; + + --vp-c-blue: #3b8eed; + --vp-c-blue-light: #549ced; + --vp-c-blue-lighter: #50a2ff; + --vp-c-blue-dark: #3468a3; + --vp-c-blue-darker: #255489; + + --vp-c-yellow: #ffc517; + --vp-c-yellow-light: #ffe417; + --vp-c-yellow-lighter: #ffff17; + --vp-c-yellow-dark: #e0ad15; + --vp-c-yellow-darker: #bc9112; + + --vp-c-red: #ed3c50; + --vp-c-red-light: #f43771; + --vp-c-red-lighter: #fd1d7c; + --vp-c-red-dark: #cd2d3f; + --vp-c-red-darker: #ab2131; + + --vp-c-purple: #de41e0; + --vp-c-purple-light: #e936eb; + --vp-c-purple-lighter: #f616f8; + --vp-c-purple-dark: #823c83; + --vp-c-purple-darker: #602960; +} + +/** + * Colors Theme + * -------------------------------------------------------------------------- */ + :root { - /** - * Colors - * --------------------------------------------------------------------- */ - - --c-white: #ffffff; - --c-white-dark: #f8f8f8; - --c-black: #000000; - - --c-divider-light: rgba(60, 60, 67, 0.12); - --c-divider-dark: rgba(84, 84, 88, 0.48); - - --c-text-light-1: #2c3e50; - --c-text-light-2: #476582; - --c-text-light-3: #90a4b7; - - --c-brand: #3eaf7c; - --c-brand-light: #4abf8a; - - /** - * Typography - * --------------------------------------------------------------------- */ - - --font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - --font-family-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; - - /** - * Z Indexes - * - * Algolia SearchBox has a z-index of 200, so make sure not to go above - * that value. - * --------------------------------------------------------------------- */ - - --z-index-navbar: 10; - --z-index-sidebar: 6; - - /** - * Shadows - * --------------------------------------------------------------------- */ - - --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06); - --shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07); - --shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08); - --shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12); - --shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16); - - /** - * Sizes - * --------------------------------------------------------------------- */ - - --header-height: 3.6rem; + --vp-c-bg: var(--vp-c-white); + --vp-c-bg-soft: var(--vp-c-white-soft); + --vp-c-bg-mute: var(--vp-c-white-mute); + --vp-c-bg-content: var(--vp-c-white); + --vp-c-bg-sidebar: var(--vp-c-white-soft); + + --vp-c-divider: var(--vp-c-divider-light-1); + --vp-c-divider-light: var(--vp-c-divider-light-2); + + --vp-c-divider-inverse: var(--vp-c-divider-dark-1); + --vp-c-divider-inverse-light: var(--vp-c-divider-dark-2); + + --vp-c-text-1: var(--vp-c-text-light-1); + --vp-c-text-2: var(--vp-c-text-light-2); + --vp-c-text-3: var(--vp-c-text-light-3); + --vp-c-text-4: var(--vp-c-text-light-4); + + --vp-c-text-inverse-1: var(--vp-c-text-dark-1); + --vp-c-text-inverse-2: var(--vp-c-text-dark-2); + --vp-c-text-inverse-3: var(--vp-c-text-dark-3); + --vp-c-text-inverse-4: var(--vp-c-text-dark-4); + + --vp-c-text-code: var(--vp-c-indigo-soft); + + --vp-c-brand: var(--vp-c-green); + --vp-c-brand-light: var(--vp-c-green-light); + --vp-c-brand-dark: var(--vp-c-green-dark); } -/** Fallback Styles */ +.dark { + --vp-c-bg: var(--vp-c-black); + --vp-c-bg-soft: var(--vp-c-black-soft); + --vp-c-bg-mute: var(--vp-c-black-mute); + --vp-c-bg-content: var(--vp-c-bg-soft); + --vp-c-bg-sidebar: var(--vp-c-bg); + + --vp-c-divider: var(--vp-c-divider-dark-1); + --vp-c-divider-light: var(--vp-c-divider-dark-2); + + --vp-c-divider-inverse: var(--vp-c-divider-light-1); + --vp-c-divider-inverse-light: var(--vp-c-divider-light-2); + + --vp-c-text-1: var(--vp-c-text-dark-1); + --vp-c-text-2: var(--vp-c-text-dark-2); + --vp-c-text-3: var(--vp-c-text-dark-3); + --vp-c-text-4: var(--vp-c-text-dark-4); + + --vp-c-text-inverse-1: var(--vp-c-text-light-1); + --vp-c-text-inverse-2: var(--vp-c-text-light-2); + --vp-c-text-inverse-3: var(--vp-c-text-light-3); + --vp-c-text-inverse-4: var(--vp-c-text-light-4); + + --vp-c-text-code: var(--vp-c-indigo-light); +} + +/** + * Typography + * -------------------------------------------------------------------------- */ + :root { - --c-divider: var(--c-divider-light); + --vp-font-family-base: Inter, -apple-system, + BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, + 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + --vp-font-family-mono: Menlo, Monaco, Consolas, 'Courier New', monospace; +} - --c-text: var(--c-text-light-1); - --c-text-light: var(--c-text-light-2); - --c-text-lighter: var(--c-text-light-3); +/** + * Shadows + * -------------------------------------------------------------------------- */ - --c-bg: var(--c-white); - --c-bg-accent: var(--c-white-dark); +:root { + --vp-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06); + --vp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07); + --vp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08); + --vp-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12); + --vp-shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16); +} + +/** + * Z-indexes + * -------------------------------------------------------------------------- */ + +:root { + --vp-z-index-local-nav: 10; + --vp-z-index-nav: 20; + --vp-z-backdrop: 30; + --vp-z-index-sidebar: 40; +} + +/** + * Layouts + * -------------------------------------------------------------------------- */ + +:root { + --vp-layout-max-width: 1376px; +} + +/** + * Component: Nav + * -------------------------------------------------------------------------- */ + +:root { + --vp-nav-height-mobile: 56px; + --vp-nav-height-desktop: 72px; +} + +/** + * Component: Sidebar + * -------------------------------------------------------------------------- */ + +:root { + --vp-sidebar-width: 272px; +} + +/** + * Component: Code + * -------------------------------------------------------------------------- */ + +:root { + --vp-code-line-height: 24px; + --vp-code-font-size: 14px; + + --vp-code-block-color: var(--vp-c-text-dark-1); + --vp-code-block-bg: #292d3e; +} - --code-line-height: 24px; - --code-font-family: var(--font-family-mono); - --code-font-size: 14px; - --code-inline-bg-color: rgba(27, 31, 35, 0.05); - --code-bg-color: #282c34; +.dark { + --vp-code-block-bg: var(--vp-c-bg-sidebar); } diff --git a/src/client/theme-default/styles/vp-doc.css b/src/client/theme-default/styles/vp-doc.css new file mode 100644 index 00000000..6d950394 --- /dev/null +++ b/src/client/theme-default/styles/vp-doc.css @@ -0,0 +1,419 @@ +/** + * Headings + * -------------------------------------------------------------------------- */ + +.vp-doc h1, +.vp-doc h2, +.vp-doc h3, +.vp-doc h4, +.vp-doc h5, +.vp-doc h6 { + position: relative; + font-weight: 600; + outline: none; +} + +.vp-doc h1 { + letter-spacing: -0.02em; + line-height: 40px; + font-size: 28px; +} + +.vp-doc h2 { + margin: 48px 0 0; + border-top: 1px solid var(--vp-c-divider-light); + padding-top: 24px; + letter-spacing: -0.02em; + line-height: 32px; + font-size: 24px; +} + +.vp-doc h3 { + margin: 48px 0 0; + letter-spacing: -0.01em; + line-height: 28px; + font-size: 20px; +} + +.vp-doc .header-anchor { + float: left; + margin-left: -0.87em; + padding-right: 0.23em; + font-weight: 500; + opacity: 0; + transition: color 0.25s, opacity 0.25s; +} + +.vp-doc h1:hover .header-anchor, +.vp-doc h1 .header-anchor:focus, +.vp-doc h2:hover .header-anchor, +.vp-doc h2 .header-anchor:focus, +.vp-doc h3:hover .header-anchor, +.vp-doc h3 .header-anchor:focus, +.vp-doc h4:hover .header-anchor, +.vp-doc h4 .header-anchor:focus, +.vp-doc h5:hover .header-anchor, +.vp-doc h5 .header-anchor:focus, +.vp-doc h6:hover .header-anchor, +.vp-doc h6 .header-anchor:focus { + opacity: 1; +} + +@media (min-width: 768px) { + .vp-doc h1 { + letter-spacing: -0.02em; + line-height: 40px; + font-size: 32px; + } +} + +/** + * Paragraph and inline elements + * -------------------------------------------------------------------------- */ + +.vp-doc p, +.vp-doc summary { + margin: 16px 0; +} + +.vp-doc p { + line-height: 28px; +} + +.vp-doc blockquote { + margin: 16px 0; + border-left: 2px solid var(--vp-c-divider); + padding-left: 16px; + transition: border-color 0.5s; +} + +.vp-doc blockquote > p { + margin: 0; + font-size: 16px; + color: var(--vp-c-text-2); + transition: color 0.5s; +} + +.vp-doc a { + font-weight: 500; + color: var(--vp-c-brand); + text-decoration-style: dotted; + transition: color 0.25s; +} + +.vp-doc a:hover { + color: var(--vp-c-brand-dark); +} + +.vp-doc strong { + font-weight: 600; +} + +/** + * Lists + * -------------------------------------------------------------------------- */ + +.vp-doc ul, +.vp-doc ol { + margin: 16px 0; +} + +.vp-doc ul { + padding-left: 1.25rem; + list-style: disc; +} + +/** + * Table + * -------------------------------------------------------------------------- */ + +.vp-doc table { + display: block; + border-collapse: collapse; + margin: 20px 0; + overflow-x: auto; +} + +.vp-doc tr { + border-top: 1px solid var(--vp-c-divider); + transition: background-color 0.5s; +} + +.vp-doc tr:nth-child(2n) { + background-color: var(--vp-c-bg-soft); +} + +.dark .vp-doc tr:nth-child(2n) { + background-color: var(--vp-c-bg-mute); +} + +.vp-doc th, +.vp-doc td { + border: 1px solid var(--vp-c-divider); + padding: 12px 16px; +} + +.vp-doc th { + font-size: 16px; + font-weight: 600; + background-color: var(--vp-c-white-soft); +} + +.dark .vp-doc th { + background-color: var(--vp-c-black); +} + +/** + * Decorational elements + * -------------------------------------------------------------------------- */ + +.vp-doc hr { + margin: 16px 0; + border: none; + border-top: 1px solid var(--vp-c-divider-light); +} + +/** + * Code + * -------------------------------------------------------------------------- */ + +/* inline code */ +.vp-doc :not(pre, h1, h2, h3, h4, h5, h6) > code { + font-size: var(--vp-code-font-size); +} + +.vp-doc :not(pre) > code { + border-radius: 4px; + padding: 4px 6px; + color: var(--vp-c-text-code); + background-color: var(--vp-c-bg-mute); + transition: color 0.5s, background-color 0.5s; +} + +.vp-doc h1 > code, +.vp-doc h2 > code, +.vp-doc h3 > code { + font-size: 0.9em; +} + +@media (min-width: 768px) { + .vp-doc :not(pre) > code { + white-space: nowrap; + } +} + +.vp-doc a > code { + color: var(--vp-c-brand-dark); +} + +.vp-doc div[class*='language-'] { + position: relative; + margin: 20px -24px; + background-color: var(--vp-code-block-bg); + overflow-x: auto; + transition: background-color 0.5s; +} + +@media (min-width: 640px) { + .vp-doc div[class*='language-'] { + border-radius: 8px; + margin: 20px 0; + } +} + +@media (max-width: 639px) { + .vp-doc li div[class*='language-'] { + border-radius: 8px 0 0 8px; + } +} + +.vp-doc div[class*='language-'] + div[class*='language-'], +.vp-doc div[class$='-api'] + div[class*='language-'], +.vp-doc div[class*='language-'] + div[class$='-api'] > div[class*='language-'] { + margin-top: -12px; +} + +.vp-doc [class*='language-'] pre, +.vp-doc [class*='language-'] code { + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +.vp-doc [class*='language-'] pre { + position: relative; + z-index: 1; + margin: 0; + padding: 16px 24px; + background: transparent; + overflow-x: auto; +} + +.vp-doc [class*='language-'] code { + display: inline-block; + padding: 0; + line-height: var(--vp-code-line-height); + font-size: var(--vp-code-font-size); + color: var(--vp-code-block-color); + transition: color 0.5s; +} + +.vp-doc .highlight-lines { + position: absolute; + top: 0; + bottom: 0; + left: 0; + padding: 13px 0 11px; + width: 100%; + font-family: var(--vp-font-family-mono); + line-height: var(--vp-code-line-height); + font-size: var(--vp-code-font-size); + user-select: none; + overflow: hidden; +} + +.vp-doc .highlight-lines .highlighted { + background-color: rgba(0, 0, 0, 0.3); + transition: background-color 0.5s; +} + +.dark .vp-doc .highlight-lines .highlighted { + background-color: rgba(255, 255, 255, 0.05); +} + +.vp-doc div[class*='language-'].line-numbers-mode { + padding-left: 32px; +} + +.vp-doc .line-numbers-wrapper { + position: absolute; + top: 0; + bottom: 0; + left: 0; + z-index: 3; + border-right: 1px solid var(--vp-c-divider-light); + padding: 13px 0 11px; + width: 32px; + text-align: center; + font-family: var(--vp-font-family-mono); + line-height: var(--vp-code-line-height); + font-size: var(--vp-code-font-size); + color: var(--vp-c-text-3); + transition: border-color 0.5s, color 0.5s; +} + +.vp-doc [class*='language-']:before { + position: absolute; + top: 4px; + right: 10px; + z-index: 2; + font-size: 12px; + font-weight: 500; + color: var(--vp-c-text-dark-3); + transition: color 0.5s; +} + +.vp-doc [class~='language-vue']:before { content: 'vue'; } +.vp-doc [class~='language-html']:before { content: 'html'; } +.vp-doc [class~='language-vue-html']:before { content: 'template'; } +.vp-doc [class~='language-css']:before { content: 'css'; } +.vp-doc [class~='language-js']:before { content: 'js'; } +.vp-doc [class~='language-jsx']:before { content: 'jsx'; } +.vp-doc [class~='language-ts']:before { content: 'ts'; } +.vp-doc [class~='language-tsx']:before { content: 'tsx'; } +.vp-doc [class~='language-json']:before { content: 'json'; } +.vp-doc [class~='language-sh']:before { content: 'sh'; } +.vp-doc [class~='language-bash']:before { content: 'sh'; } + +/** + * Code: Highlight + * + * prism.js tomorrow night eighties theme. + * https://github.com/chriskempson/tomorrow-theme + * + * @author Rose Pritchard + * -------------------------------------------------------------------------- */ + +.token.comment, +.token.block-comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #999; +} + +.token.punctuation { + color: #ccc; +} + +.token.tag, +.token.attr-name, +.token.namespace, +.token.deleted { + color: #e2777a; +} + +.token.function-name { + color: #6196cc; +} + +.token.boolean, +.token.number, +.token.function { + color: #f08d49; +} + +.token.property, +.token.class-name, +.token.constant, +.token.symbol { + color: #f8c555; +} + +.token.selector, +.token.important, +.token.atrule, +.token.keyword, +.token.builtin { + color: #cc99cd; +} + +.token.string, +.token.char, +.token.attr-value, +.token.regex, +.token.variable { + color: #7ec699; +} + +.token.operator, +.token.entity, +.token.url { + color: #67cdcc; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.token.inserted { + color: green; +} diff --git a/src/client/theme-default/support/sideBar.ts b/src/client/theme-default/support/sideBar.ts index 63ccce17..cbb057a1 100644 --- a/src/client/theme-default/support/sideBar.ts +++ b/src/client/theme-default/support/sideBar.ts @@ -1,33 +1,17 @@ import { DefaultTheme } from '../config' -import { isArray, ensureStartingSlash, removeExtention } from '../utils' - -export function isSideBarConfig( - sidebar: DefaultTheme.SideBarConfig | DefaultTheme.MultiSideBarConfig -): sidebar is DefaultTheme.SideBarConfig { - return sidebar === false || sidebar === 'auto' || isArray(sidebar) -} - -export function isSideBarGroup( - item: DefaultTheme.SideBarItem -): item is DefaultTheme.SideBarGroup { - return (item as DefaultTheme.SideBarGroup).children !== undefined -} - -export function isSideBarEmpty(sidebar?: DefaultTheme.SideBarConfig): boolean { - return isArray(sidebar) ? sidebar.length === 0 : !sidebar -} +import { ensureStartingSlash } from './utils' /** - * Get the `SideBarConfig` from sidebar option. This method will ensure to get - * correct sidebar config from `MultiSideBarConfig` with various path - * combinations such as matching `guide/` and `/guide/`. If no matching config - * was found, it will return `auto` as a fallback. + * Get the `Sidebar` from sidebar option. This method will ensure to get correct + * sidebar config from `MultiSideBarConfig` with various path combinations such + * as matching `guide/` and `/guide/`. If no matching config was found, it will + * return empty array. */ -export function getSideBarConfig( - sidebar: DefaultTheme.SideBarConfig | DefaultTheme.MultiSideBarConfig, +export function getSidebar( + sidebar: DefaultTheme.Sidebar, path: string -): DefaultTheme.SideBarConfig { - if (isSideBarConfig(sidebar)) { +): DefaultTheme.SidebarGroup[] { + if (Array.isArray(sidebar)) { return sidebar } @@ -40,27 +24,19 @@ export function getSideBarConfig( } } - return 'auto' + return [] } -/** - * Get flat sidebar links from the sidebar items. This method is useful for - * creating the "next and prev link" feature. It will ignore any items that - * don't have `link` property and removes `.md` or `.html` extension if a - * link contains it. - */ export function getFlatSideBarLinks( - sidebar: DefaultTheme.SideBarItem[] -): DefaultTheme.SideBarLink[] { - return sidebar.reduce((links, item) => { - if (item.link) { - links.push({ text: item.text, link: removeExtention(item.link) }) - } + sidebar: DefaultTheme.SidebarGroup[] +): DefaultTheme.SidebarItem[] { + const links: DefaultTheme.SidebarItem[] = [] - if (isSideBarGroup(item)) { - links = [...links, ...getFlatSideBarLinks(item.children)] + for (const group of sidebar) { + for (const link of group.items) { + links.push(link) } + } - return links - }, []) + return links } diff --git a/src/client/theme-default/support/utils.ts b/src/client/theme-default/support/utils.ts new file mode 100644 index 00000000..bc99d224 --- /dev/null +++ b/src/client/theme-default/support/utils.ts @@ -0,0 +1,64 @@ +import { ref } from 'vue' +import { withBase } from 'vitepress' + +export const HASH_RE = /#.*$/ +export const EXT_RE = /(index)?\.(md|html)$/ +export const OUTBOUND_RE = /^[a-z]+:/i + +const inBrowser = typeof window !== 'undefined' +const hashRef = ref(inBrowser ? location.hash : '') + +export function isExternal(path: string): boolean { + return OUTBOUND_RE.test(path) +} + +export function isActive( + currentPath: string, + matchPath?: string, + asRegex: boolean = false +): boolean { + if (matchPath === undefined) { + return false + } + + currentPath = normalize(`/${currentPath}`) + + if (asRegex) { + return new RegExp(matchPath).test(currentPath) + } + + if (normalize(matchPath) !== currentPath) { + return false + } + + const hashMatch = matchPath.match(HASH_RE) + + if (hashMatch) { + return hashRef.value === hashMatch[0] + } + + return true +} + +export function ensureStartingSlash(path: string): string { + return /^\//.test(path) ? path : `/${path}` +} + +export function normalize(path: string): string { + return decodeURI(path).replace(HASH_RE, '').replace(EXT_RE, '') +} + +export function normalizeLink(url: string): string { + if (isExternal(url)) { + return url + } + + const { pathname, search, hash } = new URL(url, 'http://example.com') + + const normalizedPath = + pathname.endsWith('/') || pathname.endsWith('.html') + ? url + : `${pathname.replace(/(\.md)?$/, '.html')}${search}${hash}` + + return withBase(normalizedPath) +} diff --git a/src/client/theme-default/utils.ts b/src/client/theme-default/utils.ts deleted file mode 100644 index 4c6f88de..00000000 --- a/src/client/theme-default/utils.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Route } from 'vitepress' - -export const hashRE = /#.*$/ -export const extRE = /(index)?\.(md|html)$/ -export const endingSlashRE = /\/$/ -export const outboundRE = /^[a-z]+:/i - -export function isNullish(value: any): value is null | undefined { - return value === null || value === undefined -} - -export function isArray(value: any): value is any[] { - return Array.isArray(value) -} - -export function isExternal(path: string): boolean { - return outboundRE.test(path) -} - -export function isActive(route: Route, path?: string): boolean { - if (path === undefined) { - return false - } - - const routePath = normalize(`/${route.data.relativePath}`) - const pagePath = normalize(path) - - return routePath === pagePath -} - -export function normalize(path: string): string { - return decodeURI(path).replace(hashRE, '').replace(extRE, '') -} - -export function joinUrl(base: string, path: string): string { - const baseEndsWithSlash = base.endsWith('/') - const pathStartsWithSlash = path.startsWith('/') - - if (baseEndsWithSlash && pathStartsWithSlash) { - return base.slice(0, -1) + path - } - - if (!baseEndsWithSlash && !pathStartsWithSlash) { - return `${base}/${path}` - } - - return base + path -} - -/** - * get the path without filename (the last segment). for example, if the given - * path is `/guide/getting-started.html`, this method will return `/guide/`. - * Always with a trailing slash. - */ -export function getPathDirName(path: string): string { - const segments = path.split('/') - - if (segments[segments.length - 1]) { - segments.pop() - } - - return ensureEndingSlash(segments.join('/')) -} - -export function ensureSlash(path: string): string { - return ensureEndingSlash(ensureStartingSlash(path)) -} - -export function ensureStartingSlash(path: string): string { - return /^\//.test(path) ? path : `/${path}` -} - -export function ensureEndingSlash(path: string): string { - return /(\.html|\/)$/.test(path) ? path : `${path}/` -} - -/** - * Remove `.md` or `.html` extention from the given path. It also converts - * `index` to slush. - */ -export function removeExtention(path: string): string { - return path.replace(/(index)?(\.(md|html))?$/, '') || '/' -} diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts index 60a45fa7..2cfd0ea7 100644 --- a/types/default-theme.d.ts +++ b/types/default-theme.d.ts @@ -1,125 +1,156 @@ export namespace DefaultTheme { export interface Config { - logo?: string - nav?: NavItem[] | false - sidebar?: SideBarConfig | MultiSideBarConfig - /** - * GitHub repository following the format /. + * The logo file of the site. * - * @example `"vuejs/vue-next"` + * @example '/logo.svg' */ - repo?: string + logo?: string /** - * Customize the header label. Defaults to GitHub/Gitlab/Bitbucket - * depending on the provided repo. - * - * @example `"Contribute!"` + * The nav items. */ - repoLabel?: string + nav?: NavItem[] /** - * If your docs are in a different repository from your main project. - * - * @example `"vuejs/docs-next"` + * The sidebar items. */ - docsRepo?: string + sidebar?: Sidebar /** - * If your docs are not at the root of the repo. - * - * @example `"docs"` + * Info for the edit link. If it's undefined, the edit link feature will + * be disabled. */ - docsDir?: string + editLink?: EditLink /** - * If your docs are in a different branch. Defaults to `master`. - * - * @example `"next"` + * The social links to be displayed at the end of the nav bar. Perfect for + * placing links to social services such as GitHub, Twitter, Facebook, etc. */ - docsBranch?: string + socialLinks?: SocialLink[] /** - * Enable links to edit pages at the bottom of the page. + * Adds locale menu to the nav. This option should be used when you have + * your translated sites outside of the project. */ - editLinks?: boolean + localeLinks?: LocaleLinks /** - * Custom text for edit link. Defaults to "Edit this page". + * The algolia options. Leave it undefined to disable the search feature. */ - editLinkText?: string + algolia?: AlgoliaSearchOptions /** - * Show last updated time at the bottom of the page. Defaults to `false`. - * If given a string, it will be displayed as a prefix (default value: - * "Last Updated"). + * The carbon ads options. Leave it undefined to disable the ads feature. */ - lastUpdated?: string | boolean - - prevLinks?: boolean - nextLinks?: boolean - - locales?: Record> - - algolia?: AlgoliaSearchOptions - - carbonAds?: { - carbon: string - custom?: string - placement: string - } + carbonAds?: CarbonAdsOptions } - // navbar -------------------------------------------------------------------- + // nav ----------------------------------------------------------------------- export type NavItem = NavItemWithLink | NavItemWithChildren - export interface NavItemBase { + export type NavItemWithLink = { text: string - target?: string - rel?: string - ariaLabel?: string - activeMatch?: string - } - - export interface NavItemWithLink extends NavItemBase { link: string + + /** + * `activeMatch` is expected to be a regex string. We can't use actual + * RegExp object here because it isn't serializable + */ + activeMatch?: string } - export interface NavItemWithChildren extends NavItemBase { + export interface NavItemWithChildren { + text?: string items: NavItemWithLink[] } // sidebar ------------------------------------------------------------------- - export type SideBarConfig = SideBarItem[] | 'auto' | false + export type Sidebar = SidebarGroup[] | SidebarMulti - export interface MultiSideBarConfig { - [path: string]: SideBarConfig + export interface SidebarMulti { + [path: string]: SidebarGroup[] } - export type SideBarItem = SideBarLink | SideBarGroup + export interface SidebarGroup { + text: string + items: SidebarItem[] + } - export interface SideBarLink { + export interface SidebarItem { text: string link: string } - export interface SideBarGroup { - text: string - link?: string + // edit link ----------------------------------------------------------------- + + export interface EditLink { + /** + * Repo of the site. + * + * @example 'vuejs/docs' + */ + repo: string + + /** + * Branch of the repo. + * + * @default 'main' + */ + branch?: string /** - * @default false + * If your docs are not at the root of the repo. + * + * @example 'docs' */ - collapsable?: boolean + dir?: string - children: SideBarItem[] + /** + * Custom text for edit link. + * + * @default 'Edit this page' + */ + text?: string } - // algolia ------------------------------------------------------------------ - // partially copied from @docsearch/react/dist/esm/DocSearch.d.ts + // social link --------------------------------------------------------------- + + export interface SocialLink { + icon: SocialLinkIcon + link: string + } + + export type SocialLinkIcon = + | 'discord' + | 'facebook' + | 'github' + | 'instagram' + | 'linkedin' + | 'slack' + | 'twitter' + | 'youtube' + + // locales ------------------------------------------------------------------- + + export interface LocaleLinks { + text: string + items: LocaleLink[] + } + + export interface LocaleLink { + text: string + link: string + } + + // algolia ------------------------------------------------------------------ + + /** + * The Algolia search options. Partially copied from + * `@docsearch/react/dist/esm/DocSearch.d.ts` + */ export interface AlgoliaSearchOptions { appId: string apiKey: string @@ -130,17 +161,11 @@ export namespace DefaultTheme { initialQuery?: string } - // locales ------------------------------------------------------------------- + // carbon ads ---------------------------------------------------------------- - export interface LocaleConfig { - /** - * Text for the language dropdown. - */ - selectText?: string - - /** - * Label for this locale in the language dropdown. - */ - label?: string + export interface CarbonAdsOptions { + carbon: string + custom?: string + placement: string } }