vitepress 1.0.0-rc.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -5
- package/dist/client/app/components/Content.js +2 -1
- package/dist/client/app/composables/copyCode.js +7 -5
- package/dist/client/app/composables/head.js +34 -9
- package/dist/client/app/data.js +13 -18
- package/dist/client/app/devtools.js +3 -2
- package/dist/client/app/index.js +24 -15
- package/dist/client/app/router.js +32 -60
- package/dist/client/app/utils.js +34 -0
- package/dist/client/index.d.ts +4 -3
- package/dist/client/index.js +2 -2
- package/dist/client/shared.js +34 -5
- package/dist/client/theme-default/Layout.vue +4 -1
- package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +14 -15
- package/dist/client/theme-default/components/VPBadge.vue +12 -1
- package/dist/client/theme-default/components/VPButton.vue +4 -2
- package/dist/client/theme-default/components/VPContent.vue +3 -0
- package/dist/client/theme-default/components/VPDoc.vue +3 -20
- package/dist/client/theme-default/components/VPDocAsideOutline.vue +3 -4
- package/dist/client/theme-default/components/VPDocFooter.vue +5 -10
- package/dist/client/theme-default/components/VPDocFooterLastUpdated.vue +2 -2
- package/dist/client/theme-default/components/VPDocOutlineItem.vue +5 -3
- package/dist/client/theme-default/components/VPFeature.vue +20 -9
- package/dist/client/theme-default/components/VPFeatures.vue +2 -0
- package/dist/client/theme-default/components/VPFlyout.vue +7 -15
- package/dist/client/theme-default/components/VPFooter.vue +10 -0
- package/dist/client/theme-default/components/VPHero.vue +7 -0
- package/dist/client/theme-default/components/VPHome.vue +13 -8
- package/dist/client/theme-default/components/VPHomeContent.vue +52 -0
- package/dist/client/theme-default/components/VPHomeHero.vue +3 -0
- package/dist/client/theme-default/components/VPHomeSponsors.vue +33 -9
- package/dist/client/theme-default/components/VPLocalNav.vue +61 -33
- package/dist/client/theme-default/components/VPLocalNavOutlineDropdown.vue +45 -17
- package/dist/client/theme-default/components/VPLocalSearchBox.vue +69 -135
- package/dist/client/theme-default/components/VPNav.vue +15 -2
- package/dist/client/theme-default/components/VPNavBar.vue +81 -47
- package/dist/client/theme-default/components/VPNavBarAppearance.vue +1 -1
- package/dist/client/theme-default/components/VPNavBarExtra.vue +1 -1
- package/dist/client/theme-default/components/VPNavBarMenuGroup.vue +17 -2
- package/dist/client/theme-default/components/VPNavBarMenuLink.vue +1 -0
- package/dist/client/theme-default/components/VPNavBarSearch.vue +4 -28
- package/dist/client/theme-default/components/VPNavBarSearchButton.vue +25 -25
- package/dist/client/theme-default/components/VPNavBarTitle.vue +27 -3
- package/dist/client/theme-default/components/VPNavBarTranslations.vue +1 -2
- package/dist/client/theme-default/components/VPNavScreenAppearance.vue +1 -1
- package/dist/client/theme-default/components/VPNavScreenMenuGroup.vue +3 -7
- package/dist/client/theme-default/components/VPNavScreenTranslations.vue +3 -7
- package/dist/client/theme-default/components/VPSidebar.vue +0 -2
- package/dist/client/theme-default/components/VPSidebarItem.vue +3 -6
- package/dist/client/theme-default/components/VPSocialLink.vue +4 -5
- package/dist/client/theme-default/components/VPSwitch.vue +4 -4
- package/dist/client/theme-default/components/VPSwitchAppearance.vue +11 -7
- package/dist/client/theme-default/components/VPTeamMembersItem.vue +2 -5
- package/dist/client/theme-default/components/VPTeamPage.vue +7 -2
- package/dist/client/theme-default/composables/hash.js +9 -0
- package/dist/client/theme-default/composables/langs.js +3 -2
- package/dist/client/theme-default/composables/local-nav.js +18 -0
- package/dist/client/theme-default/composables/outline.js +55 -37
- package/dist/client/theme-default/composables/prev-next.js +10 -1
- package/dist/client/theme-default/composables/sidebar.js +2 -7
- package/dist/client/theme-default/styles/base.css +10 -1
- package/dist/client/theme-default/styles/components/custom-block.css +87 -1
- package/dist/client/theme-default/styles/components/vp-code.css +4 -4
- package/dist/client/theme-default/styles/components/vp-doc.css +11 -0
- package/dist/client/theme-default/styles/components/vp-sponsor.css +3 -3
- package/dist/client/theme-default/styles/icons.css +123 -0
- package/dist/client/theme-default/styles/vars.css +79 -29
- package/dist/client/theme-default/support/lru.js +33 -0
- package/dist/client/theme-default/support/translation.js +3 -2
- package/dist/client/theme-default/support/utils.js +6 -3
- package/dist/client/theme-default/without-fonts.js +3 -3
- package/dist/node/cli.js +14 -15
- package/dist/node/index.d.ts +345 -3386
- package/dist/node/index.js +22 -23
- package/dist/node/{serve-05aeef7d.js → serve-fltVOIdt.js} +44576 -64359
- package/package.json +88 -72
- package/template/.vitepress/theme/index.js +13 -9
- package/template/.vitepress/theme/style.css +2 -2
- package/template/markdown-examples.md +1 -1
- package/theme.d.ts +19 -15
- package/types/default-theme.d.ts +73 -9
- package/types/docsearch.d.ts +1 -0
- package/types/local-search.d.ts +9 -3
- package/types/shared.d.ts +61 -1
- package/dist/client/theme-default/components/VPDocOutlineDropdown.vue +0 -85
- package/dist/client/theme-default/support/socialIcons.js +0 -13
- package/dist/node/serve-59fe4482.js +0 -2819
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
# VitePress
|
|
1
|
+
# VitePress 📝💨
|
|
2
2
|
|
|
3
|
-
[](https://github.com/vuejs/vitepress/actions)
|
|
4
4
|
[](https://www.npmjs.com/package/vitepress)
|
|
5
5
|
[](https://chat.vuejs.org)
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
VitePress is [VuePress](https://vuepress.vuejs.org)
|
|
10
|
-
|
|
11
|
-
Currently, it is in the `release candidate` stage. It is already suitable for out-of-the-box documentation use. We do not plan to introduce any breaking changes from here on until the stable release.
|
|
9
|
+
VitePress is a Vue-powered static site generator and a spiritual successor to [VuePress](https://vuepress.vuejs.org), built on top of [Vite](https://github.com/vitejs/vite).
|
|
12
10
|
|
|
13
11
|
## Documentation
|
|
14
12
|
|
|
@@ -11,11 +11,13 @@ export function useCopyCode() {
|
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test(parent.className);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const ignoredNodes = ['.vp-copy-ignore', '.diff.remove'];
|
|
15
|
+
// Clone the node and remove the ignored nodes
|
|
16
|
+
const clone = sibling.cloneNode(true);
|
|
17
|
+
clone
|
|
18
|
+
.querySelectorAll(ignoredNodes.join(','))
|
|
19
|
+
.forEach((node) => node.remove());
|
|
20
|
+
let text = clone.textContent || '';
|
|
19
21
|
if (isShell) {
|
|
20
22
|
text = text.replace(/^ *(\$|>) /gm, '').trim();
|
|
21
23
|
}
|
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
import { watchEffect } from 'vue';
|
|
2
2
|
import { createTitle, mergeHead } from '../../shared';
|
|
3
3
|
export function useUpdateHead(route, siteDataByRouteRef) {
|
|
4
|
-
let managedHeadTags = [];
|
|
5
4
|
let isFirstUpdate = true;
|
|
5
|
+
let managedHeadElements = [];
|
|
6
6
|
const updateHeadTags = (newTags) => {
|
|
7
7
|
if (import.meta.env.PROD && isFirstUpdate) {
|
|
8
8
|
// in production, the initial meta tags are already pre-rendered so we
|
|
9
9
|
// skip the first update.
|
|
10
10
|
isFirstUpdate = false;
|
|
11
|
+
newTags.forEach((tag) => {
|
|
12
|
+
const headEl = createHeadElement(tag);
|
|
13
|
+
for (const el of document.head.children) {
|
|
14
|
+
if (el.isEqualNode(headEl)) {
|
|
15
|
+
managedHeadElements.push(el);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
11
20
|
return;
|
|
12
21
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
const newElements = newTags.map(createHeadElement);
|
|
23
|
+
managedHeadElements.forEach((oldEl, oldIndex) => {
|
|
24
|
+
const matchedIndex = newElements.findIndex((newEl) => newEl?.isEqualNode(oldEl ?? null));
|
|
25
|
+
if (matchedIndex !== -1) {
|
|
26
|
+
delete newElements[matchedIndex];
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
oldEl?.remove();
|
|
30
|
+
delete managedHeadElements[oldIndex];
|
|
31
|
+
}
|
|
19
32
|
});
|
|
33
|
+
newElements.forEach((el) => el && document.head.appendChild(el));
|
|
34
|
+
managedHeadElements = [...managedHeadElements, ...newElements].filter(Boolean);
|
|
20
35
|
};
|
|
21
36
|
watchEffect(() => {
|
|
22
37
|
const pageData = route.data;
|
|
@@ -24,11 +39,16 @@ export function useUpdateHead(route, siteDataByRouteRef) {
|
|
|
24
39
|
const pageDescription = pageData && pageData.description;
|
|
25
40
|
const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
|
|
26
41
|
// update title and description
|
|
27
|
-
|
|
42
|
+
const title = createTitle(siteData, pageData);
|
|
43
|
+
if (title !== document.title) {
|
|
44
|
+
document.title = title;
|
|
45
|
+
}
|
|
28
46
|
const description = pageDescription || siteData.description;
|
|
29
47
|
let metaDescriptionElement = document.querySelector(`meta[name=description]`);
|
|
30
48
|
if (metaDescriptionElement) {
|
|
31
|
-
metaDescriptionElement.
|
|
49
|
+
if (metaDescriptionElement.getAttribute('content') !== description) {
|
|
50
|
+
metaDescriptionElement.setAttribute('content', description);
|
|
51
|
+
}
|
|
32
52
|
}
|
|
33
53
|
else {
|
|
34
54
|
createHeadElement(['meta', { name: 'description', content: description }]);
|
|
@@ -44,6 +64,11 @@ function createHeadElement([tag, attrs, innerHTML]) {
|
|
|
44
64
|
if (innerHTML) {
|
|
45
65
|
el.innerHTML = innerHTML;
|
|
46
66
|
}
|
|
67
|
+
if (tag === 'script' && !attrs.async) {
|
|
68
|
+
// async is true by default for dynamically created scripts
|
|
69
|
+
;
|
|
70
|
+
el.async = false;
|
|
71
|
+
}
|
|
47
72
|
return el;
|
|
48
73
|
}
|
|
49
74
|
function isMetaDescription(headConfig) {
|
package/dist/client/app/data.js
CHANGED
|
@@ -16,17 +16,16 @@ if (import.meta.hot) {
|
|
|
16
16
|
// per-app data
|
|
17
17
|
export function initData(route) {
|
|
18
18
|
const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath));
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
:
|
|
25
|
-
|
|
26
|
-
?
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
: ref(false);
|
|
19
|
+
const appearance = site.value.appearance; // fine with reactivity being lost here, config change triggers a restart
|
|
20
|
+
const isDark = appearance === 'force-dark'
|
|
21
|
+
? ref(true)
|
|
22
|
+
: appearance
|
|
23
|
+
? useDark({
|
|
24
|
+
storageKey: APPEARANCE_KEY,
|
|
25
|
+
initialValue: () => typeof appearance === 'string' ? appearance : 'auto',
|
|
26
|
+
...(typeof appearance === 'object' ? appearance : {})
|
|
27
|
+
})
|
|
28
|
+
: ref(false);
|
|
30
29
|
return {
|
|
31
30
|
site,
|
|
32
31
|
theme: computed(() => site.value.themeConfig),
|
|
@@ -34,14 +33,10 @@ export function initData(route) {
|
|
|
34
33
|
frontmatter: computed(() => route.data.frontmatter),
|
|
35
34
|
params: computed(() => route.data.params),
|
|
36
35
|
lang: computed(() => site.value.lang),
|
|
37
|
-
dir: computed(() => site.value.dir),
|
|
36
|
+
dir: computed(() => route.data.frontmatter.dir || site.value.dir),
|
|
38
37
|
localeIndex: computed(() => site.value.localeIndex || 'root'),
|
|
39
|
-
title: computed(() =>
|
|
40
|
-
|
|
41
|
-
}),
|
|
42
|
-
description: computed(() => {
|
|
43
|
-
return route.data.description || site.value.description;
|
|
44
|
-
}),
|
|
38
|
+
title: computed(() => createTitle(site.value, route.data)),
|
|
39
|
+
description: computed(() => route.data.description || site.value.description),
|
|
45
40
|
isDark
|
|
46
41
|
};
|
|
47
42
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { setupDevToolsPlugin } from '@vue/devtools-api';
|
|
2
2
|
const COMPONENT_STATE_TYPE = 'VitePress';
|
|
3
3
|
export const setupDevtools = (app, router, data) => {
|
|
4
|
-
|
|
4
|
+
setupDevToolsPlugin({
|
|
5
5
|
// fix recursive reference
|
|
6
6
|
app: app,
|
|
7
7
|
id: 'org.vuejs.vitepress',
|
|
@@ -10,6 +10,7 @@ export const setupDevtools = (app, router, data) => {
|
|
|
10
10
|
homepage: 'https://vitepress.dev',
|
|
11
11
|
componentStateTypes: [COMPONENT_STATE_TYPE]
|
|
12
12
|
}, (api) => {
|
|
13
|
+
// TODO: remove any
|
|
13
14
|
api.on.inspectComponent((payload) => {
|
|
14
15
|
payload.instanceData.state.push({
|
|
15
16
|
type: COMPONENT_STATE_TYPE,
|
package/dist/client/app/index.js
CHANGED
|
@@ -29,15 +29,15 @@ const Theme = resolveThemeExtends(RawTheme);
|
|
|
29
29
|
const VitePressApp = defineComponent({
|
|
30
30
|
name: 'VitePressApp',
|
|
31
31
|
setup() {
|
|
32
|
-
const { site } = useData();
|
|
32
|
+
const { site, lang, dir } = useData();
|
|
33
33
|
// change the language on the HTML element based on the current lang
|
|
34
34
|
onMounted(() => {
|
|
35
35
|
watchEffect(() => {
|
|
36
|
-
document.documentElement.lang =
|
|
37
|
-
document.documentElement.dir =
|
|
36
|
+
document.documentElement.lang = lang.value;
|
|
37
|
+
document.documentElement.dir = dir.value;
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
|
-
if (import.meta.env.PROD) {
|
|
40
|
+
if (import.meta.env.PROD && site.value.router.prefetchLinks) {
|
|
41
41
|
// in prod mode, enable intersectionObserver based pre-fetch
|
|
42
42
|
usePrefetch();
|
|
43
43
|
}
|
|
@@ -51,6 +51,8 @@ const VitePressApp = defineComponent({
|
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
53
|
export async function createApp() {
|
|
54
|
+
;
|
|
55
|
+
globalThis.__VITEPRESS__ = true;
|
|
54
56
|
const router = newRouter();
|
|
55
57
|
const app = newApp();
|
|
56
58
|
app.provide(RouterSymbol, router);
|
|
@@ -95,21 +97,28 @@ function newRouter() {
|
|
|
95
97
|
let initialPath;
|
|
96
98
|
return createRouter((path) => {
|
|
97
99
|
let pageFilePath = pathToFile(path);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
let pageModule = null;
|
|
101
|
+
if (pageFilePath) {
|
|
102
|
+
if (isInitialPageLoad) {
|
|
103
|
+
initialPath = pageFilePath;
|
|
104
|
+
}
|
|
105
|
+
// use lean build if this is the initial page load or navigating back
|
|
106
|
+
// to the initial loaded path (the static vnodes already adopted the
|
|
107
|
+
// static content on that load so no need to re-fetch the page)
|
|
108
|
+
if (isInitialPageLoad || initialPath === pageFilePath) {
|
|
109
|
+
pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js');
|
|
110
|
+
}
|
|
111
|
+
if (import.meta.env.SSR) {
|
|
112
|
+
pageModule = import(/*@vite-ignore*/ pageFilePath + '?t=' + Date.now());
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
pageModule = import(/*@vite-ignore*/ pageFilePath);
|
|
116
|
+
}
|
|
108
117
|
}
|
|
109
118
|
if (inBrowser) {
|
|
110
119
|
isInitialPageLoad = false;
|
|
111
120
|
}
|
|
112
|
-
return
|
|
121
|
+
return pageModule;
|
|
113
122
|
}, Theme.NotFound);
|
|
114
123
|
}
|
|
115
124
|
if (inBrowser) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { notFoundPageData } from '../shared';
|
|
3
|
-
import { inBrowser, withBase } from './utils';
|
|
1
|
+
import { inject, markRaw, nextTick, reactive, readonly } from 'vue';
|
|
2
|
+
import { notFoundPageData, treatAsHtml } from '../shared';
|
|
4
3
|
import { siteDataRef } from './data';
|
|
4
|
+
import { getScrollOffset, inBrowser, withBase } from './utils';
|
|
5
5
|
export const RouterSymbol = Symbol();
|
|
6
6
|
// we are just using URL to parse the pathname and hash - the base doesn't
|
|
7
7
|
// matter and is only passed to support same-host hrefs.
|
|
@@ -18,18 +18,20 @@ export function createRouter(loadPageModule, fallbackComponent) {
|
|
|
18
18
|
go
|
|
19
19
|
};
|
|
20
20
|
async function go(href = inBrowser ? location.href : '/') {
|
|
21
|
+
href = normalizeHref(href);
|
|
21
22
|
if ((await router.onBeforeRouteChange?.(href)) === false)
|
|
22
23
|
return;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
if (inBrowser) {
|
|
25
|
+
const currentUrl = new URL(location.href);
|
|
26
|
+
if (href !== normalizeHref(currentUrl.href)) {
|
|
27
|
+
// save scroll position before changing url
|
|
28
|
+
history.replaceState({ scrollPosition: window.scrollY }, document.title);
|
|
29
|
+
history.pushState(null, '', href);
|
|
30
|
+
if (new URL(href, fakeHost).hash !== currentUrl.hash) {
|
|
31
|
+
window.dispatchEvent(new Event('hashchange'));
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
|
-
updateHistory(href);
|
|
33
35
|
await loadPage(href);
|
|
34
36
|
await router.onAfterRouteChanged?.(href);
|
|
35
37
|
}
|
|
@@ -124,24 +126,22 @@ export function createRouter(loadPageModule, fallbackComponent) {
|
|
|
124
126
|
const { href, origin, pathname, hash, search } = new URL(link.href instanceof SVGAnimatedString
|
|
125
127
|
? link.href.animVal
|
|
126
128
|
: link.href, link.baseURI);
|
|
127
|
-
const currentUrl =
|
|
128
|
-
|
|
129
|
-
// only intercept inbound links
|
|
129
|
+
const currentUrl = new URL(location.href); // copy to keep old data
|
|
130
|
+
// only intercept inbound html links
|
|
130
131
|
if (!e.ctrlKey &&
|
|
131
132
|
!e.shiftKey &&
|
|
132
133
|
!e.altKey &&
|
|
133
134
|
!e.metaKey &&
|
|
134
135
|
!target &&
|
|
135
136
|
origin === currentUrl.origin &&
|
|
136
|
-
|
|
137
|
-
!(extMatch && extMatch[0] !== '.html')) {
|
|
137
|
+
treatAsHtml(pathname)) {
|
|
138
138
|
e.preventDefault();
|
|
139
139
|
if (pathname === currentUrl.pathname &&
|
|
140
140
|
search === currentUrl.search) {
|
|
141
141
|
// scroll between hash anchors in the same page
|
|
142
142
|
// avoid duplicate history entries when the hash is same
|
|
143
143
|
if (hash !== currentUrl.hash) {
|
|
144
|
-
history.pushState(null, '',
|
|
144
|
+
history.pushState(null, '', href);
|
|
145
145
|
// still emit the event so we can listen to it in themes
|
|
146
146
|
window.dispatchEvent(new Event('hashchange'));
|
|
147
147
|
}
|
|
@@ -150,7 +150,6 @@ export function createRouter(loadPageModule, fallbackComponent) {
|
|
|
150
150
|
scrollTo(link, hash, link.classList.contains('header-anchor'));
|
|
151
151
|
}
|
|
152
152
|
else {
|
|
153
|
-
updateHistory(href);
|
|
154
153
|
window.scrollTo(0, 0);
|
|
155
154
|
}
|
|
156
155
|
}
|
|
@@ -160,8 +159,9 @@ export function createRouter(loadPageModule, fallbackComponent) {
|
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
161
|
}, { capture: true });
|
|
163
|
-
window.addEventListener('popstate', (e) => {
|
|
164
|
-
loadPage(location.href, (e.state && e.state.scrollPosition) || 0);
|
|
162
|
+
window.addEventListener('popstate', async (e) => {
|
|
163
|
+
await loadPage(normalizeHref(location.href), (e.state && e.state.scrollPosition) || 0);
|
|
164
|
+
router.onAfterRouteChanged?.(location.href);
|
|
165
165
|
});
|
|
166
166
|
window.addEventListener('hashchange', (e) => {
|
|
167
167
|
e.preventDefault();
|
|
@@ -191,32 +191,10 @@ export function scrollTo(el, hash, smooth = false) {
|
|
|
191
191
|
console.warn(e);
|
|
192
192
|
}
|
|
193
193
|
if (target) {
|
|
194
|
-
let scrollOffset = siteDataRef.value.scrollOffset;
|
|
195
|
-
let offset = 0;
|
|
196
|
-
let padding = 24;
|
|
197
|
-
if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) {
|
|
198
|
-
padding = scrollOffset.padding;
|
|
199
|
-
scrollOffset = scrollOffset.selector;
|
|
200
|
-
}
|
|
201
|
-
if (typeof scrollOffset === 'number') {
|
|
202
|
-
offset = scrollOffset;
|
|
203
|
-
}
|
|
204
|
-
else if (typeof scrollOffset === 'string') {
|
|
205
|
-
offset = tryOffsetSelector(scrollOffset, padding);
|
|
206
|
-
}
|
|
207
|
-
else if (Array.isArray(scrollOffset)) {
|
|
208
|
-
for (const selector of scrollOffset) {
|
|
209
|
-
const res = tryOffsetSelector(selector, padding);
|
|
210
|
-
if (res) {
|
|
211
|
-
offset = res;
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
194
|
const targetPadding = parseInt(window.getComputedStyle(target).paddingTop, 10);
|
|
217
195
|
const targetTop = window.scrollY +
|
|
218
196
|
target.getBoundingClientRect().top -
|
|
219
|
-
|
|
197
|
+
getScrollOffset() +
|
|
220
198
|
targetPadding;
|
|
221
199
|
function scrollToTarget() {
|
|
222
200
|
// only smooth scroll if distance is smaller than screen height.
|
|
@@ -228,15 +206,6 @@ export function scrollTo(el, hash, smooth = false) {
|
|
|
228
206
|
requestAnimationFrame(scrollToTarget);
|
|
229
207
|
}
|
|
230
208
|
}
|
|
231
|
-
function tryOffsetSelector(selector, padding) {
|
|
232
|
-
const el = document.querySelector(selector);
|
|
233
|
-
if (!el)
|
|
234
|
-
return 0;
|
|
235
|
-
const bot = el.getBoundingClientRect().bottom;
|
|
236
|
-
if (bot < 0)
|
|
237
|
-
return 0;
|
|
238
|
-
return bot + padding;
|
|
239
|
-
}
|
|
240
209
|
function handleHMR(route) {
|
|
241
210
|
// update route.data on HMR updates of active page
|
|
242
211
|
if (import.meta.hot) {
|
|
@@ -249,16 +218,19 @@ function handleHMR(route) {
|
|
|
249
218
|
}
|
|
250
219
|
}
|
|
251
220
|
function shouldHotReload(payload) {
|
|
252
|
-
const payloadPath = payload.path.replace(/(
|
|
221
|
+
const payloadPath = payload.path.replace(/(?:(^|\/)index)?\.md$/, '$1');
|
|
253
222
|
const locationPath = location.pathname
|
|
254
|
-
.replace(/(
|
|
223
|
+
.replace(/(?:(^|\/)index)?\.html$/, '')
|
|
255
224
|
.slice(siteDataRef.value.base.length - 1);
|
|
256
225
|
return payloadPath === locationPath;
|
|
257
226
|
}
|
|
258
|
-
function
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
227
|
+
function normalizeHref(href) {
|
|
228
|
+
const url = new URL(href, fakeHost);
|
|
229
|
+
url.pathname = url.pathname.replace(/(^|\/)index(\.html)?$/, '$1');
|
|
230
|
+
// ensure correct deep link so page refresh lands on correct files.
|
|
231
|
+
if (siteDataRef.value.cleanUrls)
|
|
232
|
+
url.pathname = url.pathname.replace(/\.html$/, '');
|
|
233
|
+
else if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html'))
|
|
234
|
+
url.pathname += '.html';
|
|
235
|
+
return url.pathname + url.search + url.hash;
|
|
264
236
|
}
|
package/dist/client/app/utils.js
CHANGED
|
@@ -83,3 +83,37 @@ export function defineClientComponent(loader, args, cb) {
|
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
+
export function getScrollOffset() {
|
|
87
|
+
let scrollOffset = siteDataRef.value.scrollOffset;
|
|
88
|
+
let offset = 0;
|
|
89
|
+
let padding = 24;
|
|
90
|
+
if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) {
|
|
91
|
+
padding = scrollOffset.padding;
|
|
92
|
+
scrollOffset = scrollOffset.selector;
|
|
93
|
+
}
|
|
94
|
+
if (typeof scrollOffset === 'number') {
|
|
95
|
+
offset = scrollOffset;
|
|
96
|
+
}
|
|
97
|
+
else if (typeof scrollOffset === 'string') {
|
|
98
|
+
offset = tryOffsetSelector(scrollOffset, padding);
|
|
99
|
+
}
|
|
100
|
+
else if (Array.isArray(scrollOffset)) {
|
|
101
|
+
for (const selector of scrollOffset) {
|
|
102
|
+
const res = tryOffsetSelector(selector, padding);
|
|
103
|
+
if (res) {
|
|
104
|
+
offset = res;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return offset;
|
|
110
|
+
}
|
|
111
|
+
function tryOffsetSelector(selector, padding) {
|
|
112
|
+
const el = document.querySelector(selector);
|
|
113
|
+
if (!el)
|
|
114
|
+
return 0;
|
|
115
|
+
const bot = el.getBoundingClientRect().bottom;
|
|
116
|
+
if (bot < 0)
|
|
117
|
+
return 0;
|
|
118
|
+
return bot + padding;
|
|
119
|
+
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -61,9 +61,9 @@ interface VitePressData<T = any> {
|
|
|
61
61
|
title: Ref<string>;
|
|
62
62
|
description: Ref<string>;
|
|
63
63
|
lang: Ref<string>;
|
|
64
|
-
isDark: Ref<boolean>;
|
|
65
64
|
dir: Ref<string>;
|
|
66
65
|
localeIndex: Ref<string>;
|
|
66
|
+
isDark: Ref<boolean>;
|
|
67
67
|
}
|
|
68
68
|
declare function useData<T = any>(): VitePressData<T>;
|
|
69
69
|
|
|
@@ -100,6 +100,7 @@ declare function defineClientComponent(loader: AsyncComponentLoader, args?: any[
|
|
|
100
100
|
[key: string]: any;
|
|
101
101
|
}> | null;
|
|
102
102
|
};
|
|
103
|
+
declare function getScrollOffset(): number;
|
|
103
104
|
|
|
104
105
|
declare const Content: vue.DefineComponent<{
|
|
105
106
|
as: {
|
|
@@ -108,7 +109,7 @@ declare const Content: vue.DefineComponent<{
|
|
|
108
109
|
};
|
|
109
110
|
}, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
110
111
|
[key: string]: any;
|
|
111
|
-
}>, unknown, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.
|
|
112
|
+
}>, unknown, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
112
113
|
as: {
|
|
113
114
|
type: (ObjectConstructor | StringConstructor)[];
|
|
114
115
|
default: string;
|
|
@@ -117,4 +118,4 @@ declare const Content: vue.DefineComponent<{
|
|
|
117
118
|
as: string | Record<string, any>;
|
|
118
119
|
}, {}>;
|
|
119
120
|
|
|
120
|
-
export { Content, type EnhanceAppContext, type Route, type Router, type Theme, type VitePressData, dataSymbol, defineClientComponent, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
|
|
121
|
+
export { Content, type EnhanceAppContext, type Route, type Router, type Theme, type VitePressData, dataSymbol, defineClientComponent, getScrollOffset, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
|
package/dist/client/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// exports in this file are exposed to themes and md files via 'vitepress'
|
|
2
|
-
// so the user can do `import { useRoute,
|
|
2
|
+
// so the user can do `import { useRoute, useData } from 'vitepress'`
|
|
3
3
|
// composables
|
|
4
4
|
export { useData, dataSymbol } from './app/data';
|
|
5
5
|
export { useRoute, useRouter } from './app/router';
|
|
6
6
|
// utilities
|
|
7
|
-
export { inBrowser, onContentUpdated, defineClientComponent, withBase } from './app/utils';
|
|
7
|
+
export { inBrowser, onContentUpdated, defineClientComponent, withBase, getScrollOffset } from './app/utils';
|
|
8
8
|
// components
|
|
9
9
|
export { Content } from './app/components/Content';
|
package/dist/client/shared.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export const EXTERNAL_URL_RE = /^[a-z]
|
|
1
|
+
export const EXTERNAL_URL_RE = /^(?:[a-z]+:|\/\/)/i;
|
|
2
2
|
export const APPEARANCE_KEY = 'vitepress-theme-appearance';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const HASH_RE = /#.*$/;
|
|
4
|
+
const HASH_OR_QUERY_RE = /[?#].*$/;
|
|
5
|
+
const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/;
|
|
5
6
|
export const inBrowser = typeof document !== 'undefined';
|
|
6
7
|
export const notFoundPageData = {
|
|
7
8
|
relativePath: '',
|
|
@@ -30,8 +31,10 @@ export function isActive(currentPath, matchPath, asRegex = false) {
|
|
|
30
31
|
}
|
|
31
32
|
return true;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
return decodeURI(path)
|
|
34
|
+
function normalize(path) {
|
|
35
|
+
return decodeURI(path)
|
|
36
|
+
.replace(HASH_OR_QUERY_RE, '')
|
|
37
|
+
.replace(INDEX_OR_EXT_RE, '$1');
|
|
35
38
|
}
|
|
36
39
|
export function isExternal(path) {
|
|
37
40
|
return EXTERNAL_URL_RE.test(path);
|
|
@@ -67,6 +70,9 @@ export function createTitle(siteData, pageData) {
|
|
|
67
70
|
return template.replace(/:title/g, title);
|
|
68
71
|
}
|
|
69
72
|
const templateString = createTitleTemplate(siteData.title, template);
|
|
73
|
+
if (title === templateString.slice(3)) {
|
|
74
|
+
return title;
|
|
75
|
+
}
|
|
70
76
|
return `${title}${templateString}`;
|
|
71
77
|
}
|
|
72
78
|
function createTitleTemplate(siteTitle, template) {
|
|
@@ -108,3 +114,26 @@ export function sanitizeFileName(name) {
|
|
|
108
114
|
export function slash(p) {
|
|
109
115
|
return p.replace(/\\/g, '/');
|
|
110
116
|
}
|
|
117
|
+
const KNOWN_EXTENSIONS = new Set();
|
|
118
|
+
export function treatAsHtml(filename) {
|
|
119
|
+
if (KNOWN_EXTENSIONS.size === 0) {
|
|
120
|
+
const extraExts = (typeof process === 'object' && process.env?.VITE_EXTRA_EXTENSIONS) ||
|
|
121
|
+
import.meta.env?.VITE_EXTRA_EXTENSIONS ||
|
|
122
|
+
'';
|
|
123
|
+
('3g2,3gp,aac,ai,apng,au,avif,bin,bmp,cer,class,conf,crl,css,csv,dll,' +
|
|
124
|
+
'doc,eps,epub,exe,gif,gz,ics,ief,jar,jpe,jpeg,jpg,js,json,jsonld,m4a,' +
|
|
125
|
+
'man,mid,midi,mjs,mov,mp2,mp3,mp4,mpe,mpeg,mpg,mpp,oga,ogg,ogv,ogx,' +
|
|
126
|
+
'opus,otf,p10,p7c,p7m,p7s,pdf,png,ps,qt,roff,rtf,rtx,ser,svg,t,tif,' +
|
|
127
|
+
'tiff,tr,ts,tsv,ttf,txt,vtt,wav,weba,webm,webp,woff,woff2,xhtml,xml,' +
|
|
128
|
+
'yaml,yml,zip' +
|
|
129
|
+
(extraExts && typeof extraExts === 'string' ? ',' + extraExts : ''))
|
|
130
|
+
.split(',')
|
|
131
|
+
.forEach((ext) => KNOWN_EXTENSIONS.add(ext));
|
|
132
|
+
}
|
|
133
|
+
const ext = filename.split('.').pop();
|
|
134
|
+
return ext == null || !KNOWN_EXTENSIONS.has(ext.toLowerCase());
|
|
135
|
+
}
|
|
136
|
+
// https://github.com/sindresorhus/escape-string-regexp/blob/ba9a4473850cb367936417e97f1f2191b7cc67dd/index.js
|
|
137
|
+
export function escapeRegExp(str) {
|
|
138
|
+
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d');
|
|
139
|
+
}
|
|
@@ -35,7 +35,7 @@ provide('hero-image-slot-exists', heroImageSlotExists)
|
|
|
35
35
|
<slot name="layout-top" />
|
|
36
36
|
<VPSkipLink />
|
|
37
37
|
<VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" />
|
|
38
|
-
<VPNav
|
|
38
|
+
<VPNav>
|
|
39
39
|
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
|
|
40
40
|
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
|
|
41
41
|
<template #nav-bar-content-before><slot name="nav-bar-content-before" /></template>
|
|
@@ -56,7 +56,10 @@ provide('hero-image-slot-exists', heroImageSlotExists)
|
|
|
56
56
|
|
|
57
57
|
<template #not-found><slot name="not-found" /></template>
|
|
58
58
|
<template #home-hero-before><slot name="home-hero-before" /></template>
|
|
59
|
+
<template #home-hero-info-before><slot name="home-hero-info-before" /></template>
|
|
59
60
|
<template #home-hero-info><slot name="home-hero-info" /></template>
|
|
61
|
+
<template #home-hero-info-after><slot name="home-hero-info-after" /></template>
|
|
62
|
+
<template #home-hero-actions-after><slot name="home-hero-actions-after" /></template>
|
|
60
63
|
<template #home-hero-image><slot name="home-hero-image" /></template>
|
|
61
64
|
<template #home-hero-after><slot name="home-hero-after" /></template>
|
|
62
65
|
<template #home-features-before><slot name="home-features-before" /></template>
|