vitepress 1.0.0-beta.2 → 1.0.0-beta.4

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.
Files changed (37) hide show
  1. package/dist/client/app/composables/copyCode.js +2 -3
  2. package/dist/client/app/composables/preFetch.js +3 -3
  3. package/dist/client/app/index.js +11 -10
  4. package/dist/client/app/router.js +26 -15
  5. package/dist/client/app/utils.js +2 -0
  6. package/dist/client/index.d.ts +22 -4
  7. package/dist/client/index.js +1 -1
  8. package/dist/client/theme-default/Layout.vue +10 -9
  9. package/dist/client/theme-default/components/VPContent.vue +4 -4
  10. package/dist/client/theme-default/components/VPDoc.vue +15 -2
  11. package/dist/client/theme-default/components/VPDocFooterLastUpdated.vue +9 -3
  12. package/dist/client/theme-default/components/VPDocOutlineItem.vue +2 -4
  13. package/dist/client/theme-default/components/VPFooter.vue +1 -1
  14. package/dist/client/theme-default/components/VPLink.vue +1 -16
  15. package/dist/client/theme-default/components/VPLocalNav.vue +2 -2
  16. package/dist/client/theme-default/components/VPLocalSearchBox.vue +3 -3
  17. package/dist/client/theme-default/components/VPNavBar.vue +7 -7
  18. package/dist/client/theme-default/components/VPNavBarSearch.vue +3 -1
  19. package/dist/client/theme-default/components/VPNavBarTitle.vue +1 -1
  20. package/dist/client/theme-default/components/VPSidebar.vue +2 -2
  21. package/dist/client/theme-default/components/VPSkipLink.vue +2 -2
  22. package/dist/client/theme-default/composables/aside.js +2 -2
  23. package/dist/client/theme-default/styles/components/vp-doc.css +21 -0
  24. package/dist/client/theme-default/support/utils.js +1 -1
  25. package/dist/client/theme-default/without-fonts.js +1 -0
  26. package/dist/node/cli.js +2 -2
  27. package/dist/node/index.d.ts +5 -3
  28. package/dist/node/index.js +2 -2
  29. package/dist/node/{serve-9050e258.js → serve-d0f912b9.js} +1233 -1088
  30. package/dist/node-cjs/cli.cjs +2 -2
  31. package/dist/node-cjs/index.cjs +1 -1
  32. package/dist/node-cjs/{serve-1a76c42b.cjs → serve-3371ae61.cjs} +1242 -1097
  33. package/package.json +52 -51
  34. package/template/.vitepress/theme/index.js +1 -1
  35. package/theme.d.ts +2 -1
  36. package/types/default-theme.d.ts +48 -2
  37. package/dist/client/theme-default/components/icons/VPIconExternalLink.vue +0 -13
@@ -1,13 +1,12 @@
1
1
  import { inBrowser } from 'vitepress';
2
2
  export function useCopyCode() {
3
3
  if (inBrowser) {
4
- const timeoutIdMap = new Map();
4
+ const timeoutIdMap = new WeakMap();
5
5
  window.addEventListener('click', (e) => {
6
6
  const el = e.target;
7
7
  if (el.matches('div[class*="language-"] > button.copy')) {
8
8
  const parent = el.parentElement;
9
- const sibling = el.nextElementSibling
10
- ?.nextElementSibling;
9
+ const sibling = el.nextElementSibling?.nextElementSibling;
11
10
  if (!parent || !sibling) {
12
11
  return;
13
12
  }
@@ -52,7 +52,8 @@ export function usePrefetch() {
52
52
  if (!hasFetched.has(pathname)) {
53
53
  hasFetched.add(pathname);
54
54
  const pageChunkPath = pathToFile(pathname);
55
- doFetch(pageChunkPath);
55
+ if (pageChunkPath)
56
+ doFetch(pageChunkPath);
56
57
  }
57
58
  }
58
59
  });
@@ -61,7 +62,6 @@ export function usePrefetch() {
61
62
  document
62
63
  .querySelectorAll('#app a')
63
64
  .forEach((link) => {
64
- const { target } = link;
65
65
  const { hostname, pathname } = new URL(link.href instanceof SVGAnimatedString
66
66
  ? link.href.animVal
67
67
  : link.href, link.baseURI);
@@ -72,7 +72,7 @@ export function usePrefetch() {
72
72
  if (
73
73
  // only prefetch same tab navigation, since a new tab will load
74
74
  // the lean js chunk instead.
75
- target !== `_blank` &&
75
+ link.target !== '_blank' &&
76
76
  // only prefetch inbound links
77
77
  hostname === location.hostname) {
78
78
  if (pathname !== location.pathname) {
@@ -1,15 +1,14 @@
1
- import { createApp as createClientApp, createSSRApp, defineComponent, h, onMounted, watchEffect } from 'vue';
2
1
  import RawTheme from '@theme/index';
3
- import { inBrowser, pathToFile } from './utils';
4
- import { RouterSymbol, createRouter, scrollTo } from './router';
5
- import { siteDataRef, useData } from './data';
6
- import { useUpdateHead } from './composables/head';
7
- import { usePrefetch } from './composables/preFetch';
8
- import { dataSymbol, initData } from './data';
9
- import { Content } from './components/Content';
2
+ import { createApp as createClientApp, createSSRApp, defineComponent, h, onMounted, watchEffect } from 'vue';
10
3
  import { ClientOnly } from './components/ClientOnly';
11
- import { useCopyCode } from './composables/copyCode';
4
+ import { Content } from './components/Content';
12
5
  import { useCodeGroups } from './composables/codeGroups';
6
+ import { useCopyCode } from './composables/copyCode';
7
+ import { useUpdateHead } from './composables/head';
8
+ import { usePrefetch } from './composables/preFetch';
9
+ import { dataSymbol, initData, siteDataRef, useData } from './data';
10
+ import { RouterSymbol, createRouter, scrollTo } from './router';
11
+ import { inBrowser, pathToFile } from './utils';
13
12
  function resolveThemeExtends(theme) {
14
13
  if (theme.extends) {
15
14
  const base = resolveThemeExtends(theme.extends);
@@ -96,6 +95,8 @@ function newRouter() {
96
95
  let initialPath;
97
96
  return createRouter((path) => {
98
97
  let pageFilePath = pathToFile(path);
98
+ if (!pageFilePath)
99
+ return null;
99
100
  if (isInitialPageLoad) {
100
101
  initialPath = pageFilePath;
101
102
  }
@@ -120,7 +121,7 @@ if (inBrowser) {
120
121
  app.mount('#app');
121
122
  // scroll to hash on new tab during dev
122
123
  if (import.meta.env.DEV && location.hash) {
123
- const target = document.querySelector(decodeURIComponent(location.hash));
124
+ const target = document.getElementById(decodeURIComponent(location.hash).slice(1));
124
125
  if (target) {
125
126
  scrollTo(target, location.hash);
126
127
  }
@@ -5,7 +5,7 @@ import { siteDataRef } from './data';
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.
8
- const fakeHost = `http://a.com`;
8
+ const fakeHost = 'http://a.com';
9
9
  const getDefaultRoute = () => ({
10
10
  path: '/',
11
11
  component: null,
@@ -18,7 +18,8 @@ export function createRouter(loadPageModule, fallbackComponent) {
18
18
  go
19
19
  };
20
20
  async function go(href = inBrowser ? location.href : '/') {
21
- await router.onBeforeRouteChange?.(href);
21
+ if ((await router.onBeforeRouteChange?.(href)) === false)
22
+ return;
22
23
  const url = new URL(href, fakeHost);
23
24
  if (!siteDataRef.value.cleanUrls) {
24
25
  // ensure correct deep link so page refresh lands on correct files.
@@ -38,10 +39,15 @@ export function createRouter(loadPageModule, fallbackComponent) {
38
39
  }
39
40
  let latestPendingPath = null;
40
41
  async function loadPage(href, scrollPosition = 0, isRetry = false) {
42
+ if ((await router.onBeforePageLoad?.(href)) === false)
43
+ return;
41
44
  const targetLoc = new URL(href, fakeHost);
42
45
  const pendingPath = (latestPendingPath = targetLoc.pathname);
43
46
  try {
44
47
  let page = await loadPageModule(pendingPath);
48
+ if (!page) {
49
+ throw new Error(`Page not found: ${pendingPath}`);
50
+ }
45
51
  if (latestPendingPath === pendingPath) {
46
52
  latestPendingPath = null;
47
53
  const { default: comp, __pageData } = page;
@@ -68,7 +74,7 @@ export function createRouter(loadPageModule, fallbackComponent) {
68
74
  if (targetLoc.hash && !scrollPosition) {
69
75
  let target = null;
70
76
  try {
71
- target = document.querySelector(decodeURIComponent(targetLoc.hash));
77
+ target = document.getElementById(decodeURIComponent(targetLoc.hash).slice(1));
72
78
  }
73
79
  catch (e) {
74
80
  console.warn(e);
@@ -84,7 +90,8 @@ export function createRouter(loadPageModule, fallbackComponent) {
84
90
  }
85
91
  }
86
92
  catch (err) {
87
- if (!/fetch/.test(err.message) && !/^\/404(\.html|\/)?$/.test(href)) {
93
+ if (!/fetch|Page not found/.test(err.message) &&
94
+ !/^\/404(\.html|\/)?$/.test(href)) {
88
95
  console.error(err);
89
96
  }
90
97
  // retry on fetch fail: the page to hash map may have been invalidated
@@ -128,7 +135,7 @@ export function createRouter(loadPageModule, fallbackComponent) {
128
135
  !e.shiftKey &&
129
136
  !e.altKey &&
130
137
  !e.metaKey &&
131
- target !== `_blank` &&
138
+ !target &&
132
139
  origin === currentUrl.origin &&
133
140
  // don't intercept if non-html extension is present
134
141
  !(extMatch && extMatch[0] !== '.html')) {
@@ -178,7 +185,7 @@ export function scrollTo(el, hash, smooth = false) {
178
185
  try {
179
186
  target = el.classList.contains('header-anchor')
180
187
  ? el
181
- : document.querySelector(decodeURIComponent(hash));
188
+ : document.getElementById(decodeURIComponent(hash).slice(1));
182
189
  }
183
190
  catch (e) {
184
191
  console.warn(e);
@@ -207,16 +214,20 @@ export function scrollTo(el, hash, smooth = false) {
207
214
  offset +
208
215
  targetPadding;
209
216
  // only smooth scroll if distance is smaller than screen height.
210
- if (!smooth || Math.abs(targetTop - window.scrollY) > window.innerHeight) {
211
- window.scrollTo(0, targetTop);
212
- }
213
- else {
214
- window.scrollTo({
215
- left: 0,
216
- top: targetTop,
217
- behavior: 'smooth'
218
- });
217
+ function scrollToTarget() {
218
+ if (!smooth ||
219
+ Math.abs(targetTop - window.scrollY) > window.innerHeight) {
220
+ window.scrollTo(0, targetTop);
221
+ }
222
+ else {
223
+ window.scrollTo({
224
+ left: 0,
225
+ top: targetTop,
226
+ behavior: 'smooth'
227
+ });
228
+ }
219
229
  }
230
+ requestAnimationFrame(scrollToTarget);
220
231
  }
221
232
  }
222
233
  function tryOffsetSelector(selector) {
@@ -41,6 +41,8 @@ export function pathToFile(path) {
41
41
  : pagePath.slice(0, -3) + '_index.md';
42
42
  pageHash = __VP_HASH_MAP__[pagePath.toLowerCase()];
43
43
  }
44
+ if (!pageHash)
45
+ return null;
44
46
  pagePath = `${base}assets/${pagePath}.${pageHash}.js`;
45
47
  }
46
48
  else {
@@ -1,5 +1,5 @@
1
1
  import * as vue from 'vue';
2
- import { Component, Ref, App, AsyncComponentLoader } from 'vue';
2
+ import { Component, InjectionKey, Ref, App, AsyncComponentLoader } from 'vue';
3
3
  import { PageData, Awaitable, SiteData } from '../../types/shared.js';
4
4
  export { HeadConfig, Header, PageData, SiteData } from '../../types/shared.js';
5
5
 
@@ -11,14 +11,32 @@ interface Route {
11
11
  component: Component | null;
12
12
  }
13
13
  interface Router {
14
+ /**
15
+ * Current route.
16
+ */
14
17
  route: Route;
15
- go: (href?: string) => Promise<void>;
16
- onBeforeRouteChange?: (to: string) => Awaitable<void>;
18
+ /**
19
+ * Navigate to a new URL.
20
+ */
21
+ go: (to?: string) => Promise<void>;
22
+ /**
23
+ * Called before the route changes. Return `false` to cancel the navigation.
24
+ */
25
+ onBeforeRouteChange?: (to: string) => Awaitable<void | boolean>;
26
+ /**
27
+ * Called before the page component is loaded (after the history state is
28
+ * updated). Return `false` to cancel the navigation.
29
+ */
30
+ onBeforePageLoad?: (to: string) => Awaitable<void | boolean>;
31
+ /**
32
+ * Called after the route changes.
33
+ */
17
34
  onAfterRouteChanged?: (to: string) => Awaitable<void>;
18
35
  }
19
36
  declare function useRouter(): Router;
20
37
  declare function useRoute(): Route;
21
38
 
39
+ declare const dataSymbol: InjectionKey<VitePressData>;
22
40
  interface VitePressData<T = any> {
23
41
  /**
24
42
  * Site-level metadata
@@ -96,4 +114,4 @@ declare const Content: vue.DefineComponent<{
96
114
  as: string | Record<string, any>;
97
115
  }, {}>;
98
116
 
99
- export { Content, EnhanceAppContext, Route, Router, Theme, VitePressData, defineClientComponent, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
117
+ export { Content, EnhanceAppContext, Route, Router, Theme, VitePressData, dataSymbol, defineClientComponent, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
@@ -1,7 +1,7 @@
1
1
  // exports in this file are exposed to themes and md files via 'vitepress'
2
2
  // so the user can do `import { useRoute, useSiteData } from 'vitepress'`
3
3
  // composables
4
- export { useData } from './app/data';
4
+ export { useData, dataSymbol } from './app/data';
5
5
  export { useRoute, useRouter } from './app/router';
6
6
  // utilities
7
7
  export { inBrowser, onContentUpdated, defineClientComponent, withBase } from './app/utils';
@@ -1,15 +1,16 @@
1
1
  <script setup lang="ts">
2
- import { computed, provide, useSlots, watch } from 'vue'
3
2
  import { useRoute } from 'vitepress'
4
- import { useData } from './composables/data'
5
- import { useSidebar, useCloseSidebarOnEscape } from './composables/sidebar'
6
- import VPSkipLink from './components/VPSkipLink.vue'
3
+ import { useSidebar } from 'vitepress/theme'
4
+ import { computed, provide, useSlots, watch } from 'vue'
7
5
  import VPBackdrop from './components/VPBackdrop.vue'
8
- import VPNav from './components/VPNav.vue'
9
- import VPLocalNav from './components/VPLocalNav.vue'
10
- import VPSidebar from './components/VPSidebar.vue'
11
6
  import VPContent from './components/VPContent.vue'
12
7
  import VPFooter from './components/VPFooter.vue'
8
+ import VPLocalNav from './components/VPLocalNav.vue'
9
+ import VPNav from './components/VPNav.vue'
10
+ import VPSidebar from './components/VPSidebar.vue'
11
+ import VPSkipLink from './components/VPSkipLink.vue'
12
+ import { useData } from './composables/data'
13
+ import { useCloseSidebarOnEscape } from './composables/sidebar'
13
14
 
14
15
  const {
15
16
  isOpen: isSidebarOpen,
@@ -38,7 +39,7 @@ provide('hero-image-slot-exists', heroImageSlotExists)
38
39
  <slot name="layout-top" />
39
40
  <VPSkipLink />
40
41
  <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" />
41
- <VPNav>
42
+ <VPNav v-if="frontmatter.navbar !== false">
42
43
  <template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
43
44
  <template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
44
45
  <template #nav-bar-content-before><slot name="nav-bar-content-before" /></template>
@@ -56,7 +57,7 @@ provide('hero-image-slot-exists', heroImageSlotExists)
56
57
  <VPContent>
57
58
  <template #page-top><slot name="page-top" /></template>
58
59
  <template #page-bottom><slot name="page-bottom" /></template>
59
-
60
+
60
61
  <template #not-found><slot name="not-found" /></template>
61
62
  <template #home-hero-before><slot name="home-hero-before" /></template>
62
63
  <template #home-hero-info><slot name="home-hero-info" /></template>
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
+ import { useSidebar } from 'vitepress/theme'
3
+ import NotFound from '../NotFound.vue'
2
4
  import { useData } from '../composables/data'
3
- import { useSidebar } from '../composables/sidebar'
4
- import VPPage from './VPPage.vue'
5
- import VPHome from './VPHome.vue'
6
5
  import VPDoc from './VPDoc.vue'
7
- import NotFound from '../NotFound.vue'
6
+ import VPHome from './VPHome.vue'
7
+ import VPPage from './VPPage.vue'
8
8
 
9
9
  const { page, frontmatter } = useData()
10
10
  const { hasSidebar } = useSidebar()
@@ -1,11 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  import { useRoute } from 'vitepress'
3
+ import { useSidebar } from 'vitepress/theme'
3
4
  import { computed } from 'vue'
4
- import { useSidebar } from '../composables/sidebar'
5
+ import { useData } from '../composables/data'
5
6
  import VPDocAside from './VPDocAside.vue'
6
7
  import VPDocFooter from './VPDocFooter.vue'
7
8
  import VPDocOutlineDropdown from './VPDocOutlineDropdown.vue'
8
9
 
10
+ const { theme } = useData()
11
+
9
12
  const route = useRoute()
10
13
  const { hasSidebar, hasAside, leftAside } = useSidebar()
11
14
 
@@ -42,7 +45,13 @@ const pageName = computed(() =>
42
45
  <slot name="doc-before" />
43
46
  <VPDocOutlineDropdown />
44
47
  <main class="main">
45
- <Content class="vp-doc" :class="pageName" />
48
+ <Content
49
+ class="vp-doc"
50
+ :class="[
51
+ pageName,
52
+ theme.externalLinkIcon && 'external-link-icon-enabled'
53
+ ]"
54
+ />
46
55
  </main>
47
56
  <VPDocFooter>
48
57
  <template #doc-footer-before><slot name="doc-footer-before" /></template>
@@ -193,4 +202,8 @@ const pageName = computed(() =>
193
202
  .VPDoc.has-aside .content-container {
194
203
  max-width: 688px;
195
204
  }
205
+
206
+ .external-link-icon-enabled :is(.vp-doc a[href*='://'], .vp-doc a[target='_blank'])::after {
207
+ content: '';
208
+ }
196
209
  </style>
@@ -2,7 +2,7 @@
2
2
  import { ref, computed, watchEffect, onMounted } from 'vue'
3
3
  import { useData } from '../composables/data'
4
4
 
5
- const { theme, page, lang } = useData()
5
+ const { theme, page } = useData()
6
6
 
7
7
  const date = computed(() => new Date(page.value.lastUpdated!))
8
8
  const isoDatetime = computed(() => date.value.toISOString())
@@ -12,14 +12,20 @@ const datetime = ref('')
12
12
  // potential differences in timezones of the server and clients
13
13
  onMounted(() => {
14
14
  watchEffect(() => {
15
- datetime.value = date.value.toLocaleString(lang.value)
15
+ datetime.value = new Intl.DateTimeFormat(
16
+ undefined,
17
+ theme.value.lastUpdated?.formatOptions ?? {
18
+ dateStyle: 'short',
19
+ timeStyle: 'short'
20
+ }
21
+ ).format(date.value)
16
22
  })
17
23
  })
18
24
  </script>
19
25
 
20
26
  <template>
21
27
  <p class="VPLastUpdated">
22
- {{ theme.lastUpdatedText || 'Last updated' }}:
28
+ {{ theme.lastUpdated?.text || theme.lastUpdatedText || 'Last updated' }}:
23
29
  <time :datetime="isoDatetime">{{ datetime }}</time>
24
30
  </p>
25
31
  </template>
@@ -7,10 +7,8 @@ defineProps<{
7
7
  }>()
8
8
 
9
9
  function onClick({ target: el }: Event) {
10
- const id = '#' + (el as HTMLAnchorElement).href!.split('#')[1]
11
- const heading = document.querySelector<HTMLAnchorElement>(
12
- decodeURIComponent(id)
13
- )
10
+ const id = (el as HTMLAnchorElement).href!.split('#')[1]
11
+ const heading = document.getElementById(decodeURIComponent(id))
14
12
  heading?.focus()
15
13
  }
16
14
  </script>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
+ import { useSidebar } from 'vitepress/theme'
2
3
  import { useData } from '../composables/data'
3
- import { useSidebar } from '../composables/sidebar'
4
4
 
5
5
  const { theme } = useData()
6
6
  const { hasSidebar } = useSidebar()
@@ -1,7 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed } from 'vue'
3
3
  import { normalizeLink } from '../support/utils'
4
- import VPIconExternalLink from './icons/VPIconExternalLink.vue'
5
4
  import { EXTERNAL_URL_RE } from '../../shared'
6
5
 
7
6
  const props = defineProps<{
@@ -20,25 +19,11 @@ const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href)
20
19
  <component
21
20
  :is="tag"
22
21
  class="VPLink"
23
- :class="{ link: href }"
22
+ :class="{ link: href, 'vp-external-link-icon': isExternal && !noIcon }"
24
23
  :href="href ? normalizeLink(href) : undefined"
25
24
  :target="target || (isExternal ? '_blank' : undefined)"
26
25
  :rel="rel || (isExternal ? 'noreferrer' : undefined)"
27
26
  >
28
27
  <slot />
29
- <VPIconExternalLink v-if="isExternal && !noIcon" class="icon" />
30
28
  </component>
31
29
  </template>
32
-
33
- <style scoped>
34
- .icon {
35
- display: inline-block;
36
- margin-top: -1px;
37
- margin-left: 4px;
38
- width: 11px;
39
- height: 11px;
40
- fill: var(--vp-c-text-3);
41
- transition: fill 0.25s;
42
- flex-shrink: 0;
43
- }
44
- </style>
@@ -1,10 +1,10 @@
1
1
  <script lang="ts" setup>
2
2
  import { useWindowScroll } from '@vueuse/core'
3
- import { computed, shallowRef } from 'vue'
4
3
  import { onContentUpdated } from 'vitepress'
4
+ import { useSidebar } from 'vitepress/theme'
5
+ import { computed, shallowRef } from 'vue'
5
6
  import { useData } from '../composables/data'
6
7
  import { getHeaders, type MenuItem } from '../composables/outline'
7
- import { useSidebar } from '../composables/sidebar'
8
8
  import VPLocalNavOutlineDropdown from './VPLocalNavOutlineDropdown.vue'
9
9
  import VPIconAlignLeft from './icons/VPIconAlignLeft.vue'
10
10
 
@@ -12,7 +12,7 @@ import {
12
12
  import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
13
13
  import Mark from 'mark.js/src/vanilla.js'
14
14
  import MiniSearch, { type SearchResult } from 'minisearch'
15
- import { useRouter } from 'vitepress'
15
+ import { useRouter, dataSymbol } from 'vitepress'
16
16
  import {
17
17
  computed,
18
18
  createApp,
@@ -27,7 +27,6 @@ import {
27
27
  type Ref
28
28
  } from 'vue'
29
29
  import type { ModalTranslations } from '../../../../types/local-search'
30
- import { dataSymbol } from '../../app/data'
31
30
  import { pathToFile } from '../../app/utils'
32
31
  import { useData } from '../composables/data'
33
32
  import { createTranslate } from '../support/translation'
@@ -231,6 +230,7 @@ debouncedWatch(
231
230
  async function fetchExcerpt(id: string) {
232
231
  const file = pathToFile(id.slice(0, id.indexOf('#')))
233
232
  try {
233
+ if (!file) throw new Error(`Cannot find file for id: ${id}`)
234
234
  return { id, mod: await import(/*@vite-ignore*/ file) }
235
235
  } catch (e) {
236
236
  console.error(e)
@@ -421,7 +421,7 @@ function formMarkRegex(terms: Set<string>) {
421
421
  <button
422
422
  class="back-button"
423
423
  :title="$t('modal.backButtonTitle')"
424
- @click="selectedIndex > -1 && $emit('close')"
424
+ @click="$emit('close')"
425
425
  >
426
426
  <svg
427
427
  width="18"
@@ -1,15 +1,15 @@
1
1
  <script lang="ts" setup>
2
- import { computed } from 'vue'
3
2
  import { useWindowScroll } from '@vueuse/core'
4
- import { useSidebar } from '../composables/sidebar'
5
- import VPNavBarTitle from './VPNavBarTitle.vue'
6
- import VPNavBarSearch from './VPNavBarSearch.vue'
7
- import VPNavBarMenu from './VPNavBarMenu.vue'
8
- import VPNavBarTranslations from './VPNavBarTranslations.vue'
3
+ import { useSidebar } from 'vitepress/theme'
4
+ import { computed } from 'vue'
9
5
  import VPNavBarAppearance from './VPNavBarAppearance.vue'
10
- import VPNavBarSocialLinks from './VPNavBarSocialLinks.vue'
11
6
  import VPNavBarExtra from './VPNavBarExtra.vue'
12
7
  import VPNavBarHamburger from './VPNavBarHamburger.vue'
8
+ import VPNavBarMenu from './VPNavBarMenu.vue'
9
+ import VPNavBarSearch from './VPNavBarSearch.vue'
10
+ import VPNavBarSocialLinks from './VPNavBarSocialLinks.vue'
11
+ import VPNavBarTitle from './VPNavBarTitle.vue'
12
+ import VPNavBarTranslations from './VPNavBarTranslations.vue'
13
13
 
14
14
  defineProps<{
15
15
  isScreenOpen: boolean
@@ -26,6 +26,7 @@ const { theme, localeIndex } = useData()
26
26
  // payload), we delay initializing it until the user has actually clicked or
27
27
  // hit the hotkey to invoke it.
28
28
  const loaded = ref(false)
29
+ const actuallyLoaded = ref(false)
29
30
 
30
31
  const buttonText = computed(() => {
31
32
  const options = theme.value.search?.options ?? theme.value.algolia
@@ -169,9 +170,10 @@ const provider = __ALGOLIA__ ? 'algolia' : __VP_LOCAL_SEARCH__ ? 'local' : ''
169
170
  <VPAlgoliaSearchBox
170
171
  v-if="loaded"
171
172
  :algolia="theme.search?.options ?? theme.algolia"
173
+ @vue:beforeMount="actuallyLoaded = true"
172
174
  />
173
175
 
174
- <div v-else id="docsearch">
176
+ <div v-if="!actuallyLoaded" id="docsearch">
175
177
  <VPNavBarSearchButton :placeholder="buttonText" @click="load" />
176
178
  </div>
177
179
  </template>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
+ import { useSidebar } from 'vitepress/theme'
2
3
  import { useData } from '../composables/data'
3
- import { useSidebar } from '../composables/sidebar'
4
4
  import { useLangs } from '../composables/langs'
5
5
  import { normalizeLink } from '../support/utils'
6
6
  import VPImage from './VPImage.vue'
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" setup>
2
+ import { clearAllBodyScrollLocks, disableBodyScroll } from 'body-scroll-lock'
3
+ import { useSidebar } from 'vitepress/theme'
2
4
  import { ref, watchPostEffect } from 'vue'
3
- import { disableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'
4
- import { useSidebar } from '../composables/sidebar'
5
5
  import VPSidebarItem from './VPSidebarItem.vue'
6
6
 
7
7
  const { sidebarGroups, hasSidebar } = useSidebar()
@@ -8,8 +8,8 @@ const backToTop = ref()
8
8
  watch(() => route.path, () => backToTop.value.focus())
9
9
 
10
10
  function focusOnTargetAnchor({ target }: Event) {
11
- const el = document.querySelector<HTMLAnchorElement>(
12
- decodeURIComponent((target as HTMLAnchorElement).hash)
11
+ const el = document.getElementById(
12
+ decodeURIComponent((target as HTMLAnchorElement).hash).slice(1)
13
13
  )
14
14
 
15
15
  if (el) {
@@ -1,6 +1,6 @@
1
- import { computed } from 'vue';
2
1
  import { useMediaQuery } from '@vueuse/core';
3
- import { useSidebar } from './sidebar';
2
+ import { useSidebar } from 'vitepress/theme';
3
+ import { computed } from 'vue';
4
4
  export function useAside() {
5
5
  const { hasSidebar } = useSidebar();
6
6
  const is960 = useMediaQuery('(min-width: 960px)');
@@ -522,3 +522,24 @@
522
522
  margin: 0 !important;
523
523
  max-width: calc((100% - 24px) / 2) !important;
524
524
  }
525
+
526
+ :is(
527
+ .vp-external-link-icon,
528
+ .vp-doc a[href*='://'],
529
+ .vp-doc a[target='_blank']
530
+ )::after {
531
+ display: inline-block;
532
+ margin-top: -1px;
533
+ margin-left: 4px;
534
+ width: 11px;
535
+ height: 11px;
536
+ background: currentColor;
537
+ flex-shrink: 0;
538
+ --icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");
539
+ -webkit-mask-image: var(--icon);
540
+ mask-image: var(--icon);
541
+ }
542
+
543
+ .vp-external-link-icon::after {
544
+ content: '';
545
+ }
@@ -28,7 +28,7 @@ export function normalizeLink(url) {
28
28
  return url.replace(PATHNAME_PROTOCOL_RE, '');
29
29
  }
30
30
  const { site } = useData();
31
- const { pathname, search, hash } = new URL(url, 'http://example.com');
31
+ const { pathname, search, hash } = new URL(url, 'http://a.com');
32
32
  const normalizedPath = pathname.endsWith('/') || pathname.endsWith('.html')
33
33
  ? url
34
34
  : url.replace(/(?:(^\.+)\/)?.*$/, `$1${pathname.replace(/(\.md)?$/, site.value.cleanUrls ? '' : '.html')}${search}${hash}`);
@@ -19,6 +19,7 @@ export { default as VPTeamPage } from './components/VPTeamPage.vue';
19
19
  export { default as VPTeamPageTitle } from './components/VPTeamPageTitle.vue';
20
20
  export { default as VPTeamPageSection } from './components/VPTeamPageSection.vue';
21
21
  export { default as VPTeamMembers } from './components/VPTeamMembers.vue';
22
+ export { useSidebar } from './composables/sidebar';
22
23
  const theme = {
23
24
  Layout,
24
25
  enhanceApp: ({ app }) => {
package/dist/node/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { o as getDefaultExportFromCjs, p as c, q as clearCache, k as build, s as serve, l as init, j as createServer } from './serve-9050e258.js';
1
+ import { o as getDefaultExportFromCjs, p as c, q as clearCache, k as build, s as serve, l as init, j as createServer } from './serve-d0f912b9.js';
2
2
  import { createLogger } from 'vite';
3
3
  import 'url';
4
4
  import 'path';
@@ -294,7 +294,7 @@ var minimist = function (args, opts) {
294
294
 
295
295
  var minimist$1 = /*@__PURE__*/getDefaultExportFromCjs(minimist);
296
296
 
297
- var version = "1.0.0-beta.2";
297
+ var version = "1.0.0-beta.4";
298
298
 
299
299
  function bindShortcuts(server, createDevServer) {
300
300
  if (!server.httpServer || !process.stdin.isTTY || process.env.CI) {