vitepress 2.0.0-alpha.16 → 2.0.0-alpha.18

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 (73) hide show
  1. package/README.md +11 -0
  2. package/dist/client/app/composables/copyCode.js +1 -1
  3. package/dist/client/app/index.js +1 -1
  4. package/dist/client/app/router.js +25 -27
  5. package/dist/client/app/utils.js +0 -34
  6. package/dist/client/index.d.ts +160 -41
  7. package/dist/client/index.js +1 -1
  8. package/dist/client/shared.js +3 -3
  9. package/dist/client/theme-default/Layout.vue +1 -0
  10. package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +8 -9
  11. package/dist/client/theme-default/components/VPButton.vue +1 -1
  12. package/dist/client/theme-default/components/VPCarbonAds.vue +7 -2
  13. package/dist/client/theme-default/components/VPContent.vue +11 -8
  14. package/dist/client/theme-default/components/VPDocAside.vue +1 -1
  15. package/dist/client/theme-default/components/VPDocAsideCarbonAds.vue +1 -1
  16. package/dist/client/theme-default/components/VPDocAsideOutline.vue +1 -1
  17. package/dist/client/theme-default/components/VPDocAsideSponsors.vue +1 -1
  18. package/dist/client/theme-default/components/VPDocFooter.vue +1 -1
  19. package/dist/client/theme-default/components/VPFeature.vue +9 -2
  20. package/dist/client/theme-default/components/VPHero.vue +6 -0
  21. package/dist/client/theme-default/components/VPHome.vue +4 -3
  22. package/dist/client/theme-default/components/VPHomeHero.vue +1 -0
  23. package/dist/client/theme-default/components/VPLink.vue +4 -6
  24. package/dist/client/theme-default/components/VPLocalSearchBox.vue +101 -22
  25. package/dist/client/theme-default/components/VPMenuLink.vue +12 -9
  26. package/dist/client/theme-default/components/VPNavBarExtra.vue +11 -4
  27. package/dist/client/theme-default/components/VPNavBarMenu.vue +1 -1
  28. package/dist/client/theme-default/components/VPNavBarMenuGroup.vue +17 -12
  29. package/dist/client/theme-default/components/VPNavBarMenuLink.vue +10 -9
  30. package/dist/client/theme-default/components/VPNavBarTranslations.vue +10 -3
  31. package/dist/client/theme-default/components/VPNavScreenMenu.vue +1 -1
  32. package/dist/client/theme-default/components/VPNavScreenMenuGroupLink.vue +1 -1
  33. package/dist/client/theme-default/components/VPNavScreenMenuLink.vue +1 -1
  34. package/dist/client/theme-default/components/VPNavScreenTranslations.vue +3 -0
  35. package/dist/client/theme-default/components/VPSkipLink.vue +1 -1
  36. package/dist/client/theme-default/components/VPSocialLink.vue +2 -1
  37. package/dist/client/theme-default/components/VPSocialLinks.vue +2 -1
  38. package/dist/client/theme-default/components/VPSponsors.vue +1 -1
  39. package/dist/client/theme-default/components/VPSponsorsGrid.vue +1 -1
  40. package/dist/client/theme-default/components/VPTeamMembersItem.vue +2 -4
  41. package/dist/client/theme-default/composables/langs.js +11 -2
  42. package/dist/client/theme-default/composables/layout.js +1 -0
  43. package/dist/client/theme-default/composables/outline.js +4 -4
  44. package/dist/client/theme-default/fonts/inter-italic-cyrillic-ext.woff2 +0 -0
  45. package/dist/client/theme-default/fonts/inter-italic-cyrillic.woff2 +0 -0
  46. package/dist/client/theme-default/fonts/inter-italic-greek-ext.woff2 +0 -0
  47. package/dist/client/theme-default/fonts/inter-italic-greek.woff2 +0 -0
  48. package/dist/client/theme-default/fonts/inter-italic-latin-ext.woff2 +0 -0
  49. package/dist/client/theme-default/fonts/inter-italic-latin.woff2 +0 -0
  50. package/dist/client/theme-default/fonts/inter-italic-vietnamese.woff2 +0 -0
  51. package/dist/client/theme-default/fonts/inter-roman-cyrillic-ext.woff2 +0 -0
  52. package/dist/client/theme-default/fonts/inter-roman-cyrillic.woff2 +0 -0
  53. package/dist/client/theme-default/fonts/inter-roman-greek-ext.woff2 +0 -0
  54. package/dist/client/theme-default/fonts/inter-roman-greek.woff2 +0 -0
  55. package/dist/client/theme-default/fonts/inter-roman-latin-ext.woff2 +0 -0
  56. package/dist/client/theme-default/fonts/inter-roman-latin.woff2 +0 -0
  57. package/dist/client/theme-default/fonts/inter-roman-vietnamese.woff2 +0 -0
  58. package/dist/client/theme-default/styles/base.css +19 -0
  59. package/dist/client/theme-default/styles/components/vp-code.css +8 -0
  60. package/dist/client/theme-default/styles/components/vp-doc.css +14 -6
  61. package/dist/client/theme-default/styles/docsearch.css +4 -0
  62. package/dist/client/theme-default/styles/fonts.css +163 -39
  63. package/dist/client/theme-default/styles/vars.css +23 -3
  64. package/dist/client/theme-default/support/docsearch.js +23 -8
  65. package/dist/client/theme-default/support/sidebar.js +2 -2
  66. package/dist/client/theme-default/support/utils.js +6 -0
  67. package/dist/node/{chunk-CCYAX98b.js → chunk-Cne7GbZY.js} +6678 -16288
  68. package/dist/node/cli.js +8 -16
  69. package/dist/node/index.d.ts +854 -22
  70. package/dist/node/index.js +11 -19
  71. package/package.json +58 -63
  72. package/types/default-theme.d.ts +13 -3
  73. package/types/shared.d.ts +37 -9
package/README.md CHANGED
@@ -26,3 +26,14 @@ Please make sure to read the [Contributing Guide](https://github.com/vuejs/vitep
26
26
  [MIT](https://github.com/vuejs/vitepress/blob/main/LICENSE)
27
27
 
28
28
  Copyright (c) 2019-present, Yuxi (Evan) You
29
+
30
+ ## Special Thanks
31
+
32
+ This project would not be possible without the support of these amazing companies and tools.
33
+
34
+ | | | |
35
+ | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
36
+ | <a href="https://voidzero.dev" target="_blank"><img alt="VoidZero's logo" src="https://github.com/voidzero-dev.png" width="80" /></a> | **[VoidZero](https://voidzero.dev)** | The company behind Vite, Vitest, Rolldown, and Oxc. VoidZero has been a long-time sponsor of VitePress. VitePress itself is built on top of this ecosystem of high-performance JavaScript tooling. |
37
+ | <a href="https://stackblitz.com" target="_blank"><img alt="StackBlitz's logo" src="https://github.com/stackblitz.png" width="80" /></a> | **[StackBlitz](https://stackblitz.com)** | Creators of WebContainers and a browser-based development platform. Their technology powers the VitePress playground and provides instant preview releases for our pull requests and commits. |
38
+ | <a href="https://algolia.com" target="_blank"><img alt="Algolia's logo" src="https://github.com/algolia.png" width="80" /></a> | **[Algolia](https://algolia.com)** | An AI-powered search platform that provides fast, scalable search. Algolia powers DocSearch for VitePress. Their team has been a long-time partner in building and maintaining our search integration. |
39
+ | <a href="https://browserstack.com" target="_blank"><img alt="BrowserStack's logo" src="https://github.com/browserstack.png" width="80" /></a> | **[BrowserStack](https://browserstack.com)** | A cloud-based testing platform for websites and mobile apps. BrowserStack provides the cross-browser and real-device infrastructure used to ensure VitePress works consistently across all browsers. |
@@ -41,7 +41,7 @@ export function useCopyCode() {
41
41
  }
42
42
  async function copyToClipboard(text) {
43
43
  try {
44
- return navigator.clipboard.writeText(text);
44
+ await navigator.clipboard.writeText(text);
45
45
  }
46
46
  catch {
47
47
  const element = document.createElement('textarea');
@@ -136,7 +136,7 @@ if (inBrowser) {
136
136
  app.mount('#app');
137
137
  // scroll to hash on new tab during dev
138
138
  if (import.meta.env.DEV && location.hash) {
139
- scrollTo(location.hash);
139
+ setTimeout(() => scrollTo(location.hash), 100);
140
140
  }
141
141
  });
142
142
  });
@@ -1,7 +1,7 @@
1
1
  import { inject, markRaw, nextTick, reactive, readonly } from 'vue';
2
2
  import { notFoundPageData, treatAsHtml } from '../shared';
3
3
  import { siteDataRef } from './data';
4
- import { getScrollOffset, inBrowser, withBase } from './utils';
4
+ import { 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,17 +18,25 @@ export function createRouter(loadPageModule, fallbackComponent) {
18
18
  const router = {
19
19
  route,
20
20
  async go(href, options) {
21
+ const { hash } = new URL(href, fakeHost);
22
+ const hasTextFragment = inBrowser && document.fragmentDirective && hash.includes(':~:');
21
23
  href = normalizeHref(href);
22
24
  if ((await router.onBeforeRouteChange?.(href)) === false)
23
25
  return;
24
- if (!inBrowser || (await changeRoute(href, options)))
25
- await loadPage(href);
26
+ if (!inBrowser ||
27
+ (await changeRoute(href, { ...options, hasTextFragment }))) {
28
+ await loadPage(href, { initialLoad: !!options?.initialLoad });
29
+ }
30
+ if (hasTextFragment) {
31
+ // this will create a new history entry, but that's almost unavoidable
32
+ location.hash = hash;
33
+ }
26
34
  syncRouteQueryAndHash();
27
35
  await router.onAfterRouteChange?.(href);
28
36
  }
29
37
  };
30
38
  let latestPendingPath = null;
31
- async function loadPage(href, scrollPosition = 0, isRetry = false) {
39
+ async function loadPage(href, { scrollPosition = 0, isRetry = false, initialLoad = false } = {}) {
32
40
  if ((await router.onBeforePageLoad?.(href)) === false)
33
41
  return;
34
42
  const targetLoc = new URL(href, fakeHost);
@@ -61,7 +69,8 @@ export function createRouter(loadPageModule, fallbackComponent) {
61
69
  href = actualPathname + targetLoc.search + targetLoc.hash;
62
70
  history.replaceState({}, '', href);
63
71
  }
64
- return scrollTo(targetLoc.hash, false, scrollPosition);
72
+ if (!initialLoad)
73
+ scrollTo(targetLoc.hash, scrollPosition);
65
74
  });
66
75
  }
67
76
  }
@@ -78,7 +87,7 @@ export function createRouter(loadPageModule, fallbackComponent) {
78
87
  try {
79
88
  const res = await fetch(siteDataRef.value.base + 'hashmap.json');
80
89
  window.__VP_HASH_MAP__ = await res.json();
81
- await loadPage(href, scrollPosition, true);
90
+ await loadPage(href, { scrollPosition, isRetry: true, initialLoad });
82
91
  return;
83
92
  }
84
93
  catch (e) { }
@@ -134,17 +143,14 @@ export function createRouter(loadPageModule, fallbackComponent) {
134
143
  // only intercept inbound html links
135
144
  if (origin === currentLoc.origin && treatAsHtml(pathname)) {
136
145
  e.preventDefault();
137
- router.go(href, {
138
- // use smooth scroll when clicking on header anchor links
139
- smoothScroll: link.classList.contains('header-anchor')
140
- });
146
+ router.go(href);
141
147
  }
142
148
  }, { capture: true });
143
149
  window.addEventListener('popstate', async (e) => {
144
150
  if (e.state === null)
145
151
  return;
146
152
  const href = normalizeHref(location.href);
147
- await loadPage(href, (e.state && e.state.scrollPosition) || 0);
153
+ await loadPage(href, { scrollPosition: e.state.scrollPosition || 0 });
148
154
  syncRouteQueryAndHash();
149
155
  await router.onAfterRouteChange?.(href);
150
156
  });
@@ -165,7 +171,7 @@ export function useRouter() {
165
171
  export function useRoute() {
166
172
  return useRouter().route;
167
173
  }
168
- export function scrollTo(hash, smooth = false, scrollPosition = 0) {
174
+ export function scrollTo(hash, scrollPosition = 0) {
169
175
  if (!hash || scrollPosition) {
170
176
  window.scrollTo(0, scrollPosition);
171
177
  return;
@@ -179,18 +185,8 @@ export function scrollTo(hash, smooth = false, scrollPosition = 0) {
179
185
  }
180
186
  if (!target)
181
187
  return;
182
- const targetTop = window.scrollY +
183
- target.getBoundingClientRect().top -
184
- getScrollOffset() +
185
- Number.parseInt(window.getComputedStyle(target).paddingTop, 10) || 0;
186
- const behavior = window.matchMedia('(prefers-reduced-motion)').matches
187
- ? 'instant'
188
- : // only smooth scroll if distance is smaller than screen height
189
- smooth && Math.abs(targetTop - window.scrollY) <= window.innerHeight
190
- ? 'smooth'
191
- : 'auto';
192
188
  const scrollToTarget = () => {
193
- window.scrollTo({ left: 0, top: targetTop, behavior });
189
+ target.scrollIntoView({ block: 'start' });
194
190
  // focus the target element for better accessibility
195
191
  target.focus({ preventScroll: true });
196
192
  // return if focus worked
@@ -242,15 +238,16 @@ function normalizeHref(href) {
242
238
  else if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
243
239
  url.pathname += '.html';
244
240
  }
245
- return url.pathname + url.search + url.hash;
241
+ return url.pathname + url.search + url.hash.split(':~:')[0];
246
242
  }
247
- async function changeRoute(href, { smoothScroll = false, initialLoad = false, replace = false } = {}) {
243
+ async function changeRoute(href, { initialLoad = false, replace = false, hasTextFragment = false } = {}) {
248
244
  const loc = normalizeHref(location.href);
249
245
  const nextUrl = new URL(href, location.origin);
250
246
  const currentUrl = new URL(loc, location.origin);
251
247
  if (href === loc) {
252
248
  if (!initialLoad) {
253
- scrollTo(nextUrl.hash, smoothScroll);
249
+ if (!hasTextFragment)
250
+ scrollTo(nextUrl.hash);
254
251
  return false;
255
252
  }
256
253
  }
@@ -270,7 +267,8 @@ async function changeRoute(href, { smoothScroll = false, initialLoad = false, re
270
267
  oldURL: currentUrl.href,
271
268
  newURL: nextUrl.href
272
269
  }));
273
- scrollTo(nextUrl.hash, smoothScroll);
270
+ if (!hasTextFragment)
271
+ scrollTo(nextUrl.hash);
274
272
  }
275
273
  return false;
276
274
  }
@@ -84,37 +84,3 @@ export function defineClientComponent(loader, args, cb) {
84
84
  }
85
85
  };
86
86
  }
87
- export function getScrollOffset() {
88
- let scrollOffset = siteDataRef.value.scrollOffset;
89
- let offset = 0;
90
- let padding = 24;
91
- if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) {
92
- padding = scrollOffset.padding;
93
- scrollOffset = scrollOffset.selector;
94
- }
95
- if (typeof scrollOffset === 'number') {
96
- offset = scrollOffset;
97
- }
98
- else if (typeof scrollOffset === 'string') {
99
- offset = tryOffsetSelector(scrollOffset, padding);
100
- }
101
- else if (Array.isArray(scrollOffset)) {
102
- for (const selector of scrollOffset) {
103
- const res = tryOffsetSelector(selector, padding);
104
- if (res) {
105
- offset = res;
106
- break;
107
- }
108
- }
109
- }
110
- return offset;
111
- }
112
- function tryOffsetSelector(selector, padding) {
113
- const el = document.querySelector(selector);
114
- if (!el)
115
- return 0;
116
- const bot = el.getBoundingClientRect().bottom;
117
- if (bot < 0)
118
- return 0;
119
- return bot + padding;
120
- }
@@ -1,7 +1,159 @@
1
1
  import * as vue from 'vue';
2
- import { Component, Ref, InjectionKey, App, AsyncComponentLoader } from 'vue';
3
- import { PageData, Awaitable, SiteData } from '../../types/shared.js';
4
- export { HeadConfig, Header, PageData, SiteData } from '../../types/shared.js';
2
+ import { Ref, Component, App, InjectionKey, AsyncComponentLoader } from 'vue';
3
+ import { UseDarkOptions } from '@vueuse/core';
4
+
5
+ // types shared between server and client
6
+
7
+
8
+ type Awaitable<T> = T | PromiseLike<T>
9
+
10
+ type DeepPartial<T> =
11
+ T extends Record<string, any>
12
+ ? T extends
13
+ | Date
14
+ | RegExp
15
+ | Function
16
+ | ReadonlyMap<any, any>
17
+ | ReadonlySet<any>
18
+ | ReadonlyArray<any>
19
+ ? T
20
+ : { [P in keyof T]?: DeepPartial<T[P]> }
21
+ : T
22
+
23
+ interface PageData {
24
+ relativePath: string
25
+ /**
26
+ * differs from relativePath in case of path rewrites
27
+ * empty string if the page is virtual (e.g. 404 page)
28
+ */
29
+ filePath: string
30
+ title: string
31
+ titleTemplate?: string | boolean
32
+ description: string
33
+ headers: Header[]
34
+ frontmatter: Record<string, any>
35
+ params?: Record<string, any>
36
+ isNotFound?: boolean
37
+ lastUpdated?: number
38
+ }
39
+
40
+ interface Header {
41
+ /**
42
+ * The level of the header
43
+ *
44
+ * `1` to `6` for `<h1>` to `<h6>`
45
+ */
46
+ level: number
47
+ /**
48
+ * The title of the header
49
+ */
50
+ title: string
51
+ /**
52
+ * The slug of the header
53
+ *
54
+ * Typically the `id` attr of the header anchor
55
+ */
56
+ slug: string
57
+ /**
58
+ * Link of the header
59
+ *
60
+ * Typically using `#${slug}` as the anchor hash
61
+ */
62
+ link: string
63
+ /**
64
+ * The children of the header
65
+ */
66
+ children: Header[]
67
+ }
68
+
69
+ interface SiteData<ThemeConfig = any> {
70
+ base: string
71
+ cleanUrls?: boolean
72
+ lang: string
73
+ dir: string
74
+ title: string
75
+ titleTemplate?: string | boolean
76
+ description: string
77
+ head: HeadConfig[]
78
+ appearance:
79
+ | boolean
80
+ | 'dark'
81
+ | 'force-dark'
82
+ | 'force-auto'
83
+ | (Omit<UseDarkOptions, 'initialValue'> & { initialValue?: 'dark' })
84
+ themeConfig: ThemeConfig
85
+ locales: LocaleConfig<ThemeConfig>
86
+ localeIndex?: string
87
+ contentProps?: Record<string, any>
88
+ router: {
89
+ prefetchLinks: boolean
90
+ }
91
+ additionalConfig?:
92
+ AdditionalConfigDict<ThemeConfig> | AdditionalConfigLoader<ThemeConfig>
93
+ }
94
+
95
+ interface VitePressData<T = any> {
96
+ /**
97
+ * Site-level metadata
98
+ */
99
+ site: Ref<SiteData<T>>
100
+ /**
101
+ * themeConfig from .vitepress/config.js
102
+ */
103
+ theme: Ref<T>
104
+ /**
105
+ * Page-level metadata
106
+ */
107
+ page: Ref<PageData>
108
+ /**
109
+ * page frontmatter data
110
+ */
111
+ frontmatter: Ref<PageData['frontmatter']>
112
+ /**
113
+ * dynamic route params
114
+ */
115
+ params: Ref<PageData['params']>
116
+ title: Ref<string>
117
+ description: Ref<string>
118
+ lang: Ref<string>
119
+ dir: Ref<string>
120
+ localeIndex: Ref<string>
121
+ isDark: Ref<boolean>
122
+ /**
123
+ * Current location hash
124
+ */
125
+ hash: Ref<string>
126
+ }
127
+
128
+ type HeadConfig =
129
+ [string, Record<string, string>] | [string, Record<string, string>, string]
130
+
131
+ interface LocaleSpecificConfig<ThemeConfig = any> {
132
+ lang?: string
133
+ dir?: string
134
+ title?: string
135
+ titleTemplate?: string | boolean
136
+ description?: string
137
+ head?: HeadConfig[]
138
+ themeConfig?: DeepPartial<ThemeConfig>
139
+ }
140
+
141
+ type LocaleConfig<ThemeConfig = any> = Record<
142
+ string,
143
+ LocaleSpecificConfig<ThemeConfig> & { label: string; link?: string }
144
+ >
145
+
146
+ type AdditionalConfig<ThemeConfig = any> =
147
+ LocaleSpecificConfig<ThemeConfig>
148
+
149
+ type AdditionalConfigDict<ThemeConfig = any> = Record<
150
+ string,
151
+ AdditionalConfig<ThemeConfig>
152
+ >
153
+
154
+ type AdditionalConfigLoader<ThemeConfig = any> = (
155
+ relativePath: string
156
+ ) => AdditionalConfig<ThemeConfig>[] | void
5
157
 
6
158
  declare const inBrowser: boolean;
7
159
  /**
@@ -26,7 +178,6 @@ interface Router {
26
178
  */
27
179
  go: (to: string, options?: {
28
180
  initialLoad?: boolean;
29
- smoothScroll?: boolean;
30
181
  replace?: boolean;
31
182
  }) => Promise<void>;
32
183
  /**
@@ -50,41 +201,6 @@ interface Router {
50
201
  declare function useRouter(): Router;
51
202
  declare function useRoute(): Route;
52
203
 
53
- declare const dataSymbol: InjectionKey<VitePressData>;
54
- interface VitePressData<T = any> {
55
- /**
56
- * Site-level metadata
57
- */
58
- site: Ref<SiteData<T>>;
59
- /**
60
- * themeConfig from .vitepress/config.js
61
- */
62
- theme: Ref<T>;
63
- /**
64
- * Page-level metadata
65
- */
66
- page: Ref<PageData>;
67
- /**
68
- * page frontmatter data
69
- */
70
- frontmatter: Ref<PageData['frontmatter']>;
71
- /**
72
- * dynamic route params
73
- */
74
- params: Ref<PageData['params']>;
75
- title: Ref<string>;
76
- description: Ref<string>;
77
- lang: Ref<string>;
78
- dir: Ref<string>;
79
- localeIndex: Ref<string>;
80
- isDark: Ref<boolean>;
81
- /**
82
- * Current location hash
83
- */
84
- hash: Ref<string>;
85
- }
86
- declare function useData<T = any>(): VitePressData<T>;
87
-
88
204
  interface EnhanceAppContext {
89
205
  app: App;
90
206
  router: Router;
@@ -104,6 +220,10 @@ interface Theme {
104
220
  NotFound?: Component;
105
221
  }
106
222
 
223
+ declare const dataSymbol: InjectionKey<VitePressData>;
224
+
225
+ declare function useData<T = any>(): VitePressData<T>;
226
+
107
227
  /**
108
228
  * Append base to internal (non-relative) urls
109
229
  */
@@ -118,7 +238,6 @@ declare function defineClientComponent(loader: AsyncComponentLoader, args?: any[
118
238
  [key: string]: any;
119
239
  }> | null;
120
240
  };
121
- declare function getScrollOffset(): number;
122
241
 
123
242
  declare const Content: vue.DefineComponent<vue.ExtractPropTypes<{
124
243
  as: {
@@ -136,4 +255,4 @@ declare const Content: vue.DefineComponent<vue.ExtractPropTypes<{
136
255
  as: string | Record<string, any>;
137
256
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
138
257
 
139
- export { Content, type EnhanceAppContext, type Route, type Router, type Theme, type VitePressData, escapeHtml as _escapeHtml, dataSymbol, defineClientComponent, getScrollOffset, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
258
+ export { Content, type EnhanceAppContext, type HeadConfig, type Header, type PageData, type Route, type Router, type SiteData, type Theme, type VitePressData, escapeHtml as _escapeHtml, dataSymbol, defineClientComponent, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
@@ -4,6 +4,6 @@
4
4
  export { dataSymbol, useData } from './app/data';
5
5
  export { useRoute, useRouter } from './app/router';
6
6
  // utilities
7
- export { _escapeHtml, defineClientComponent, getScrollOffset, inBrowser, onContentUpdated, withBase } from './app/utils';
7
+ export { _escapeHtml, defineClientComponent, inBrowser, onContentUpdated, withBase } from './app/utils';
8
8
  // components
9
9
  export { Content } from './app/components/Content';
@@ -2,9 +2,9 @@ export const EXTERNAL_URL_RE = /^(?:[a-z]+:|\/\/)/i;
2
2
  export const APPEARANCE_KEY = 'vitepress-theme-appearance';
3
3
  export const VP_SOURCE_KEY = '[VP_SOURCE]';
4
4
  const UnpackStackView = Symbol('stack-view:unpack');
5
- const HASH_RE = /#.*$/;
5
+ const HASH_WITHOUT_FRAGMENT_RE = /#.*?(?=:~:|$)/;
6
6
  const HASH_OR_QUERY_RE = /[?#].*$/;
7
- const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/;
7
+ const INDEX_OR_EXT_RE = /(?:(^|\/)index)?(?:\.(?:md|html))?$/;
8
8
  export const inBrowser = typeof document !== 'undefined';
9
9
  export const notFoundPageData = {
10
10
  relativePath: '404.md',
@@ -27,7 +27,7 @@ export function isActive(currentPath, matchPath, asRegex = false) {
27
27
  if (normalize(matchPath) !== currentPath) {
28
28
  return false;
29
29
  }
30
- const hashMatch = matchPath.match(HASH_RE);
30
+ const hashMatch = matchPath.match(HASH_WITHOUT_FRAGMENT_RE);
31
31
  if (hashMatch) {
32
32
  return (inBrowser ? location.hash : '') === hashMatch[0];
33
33
  }
@@ -61,6 +61,7 @@ provide(layoutInfoInjectionKey, { heroImageSlotExists })
61
61
  <template #home-hero-info><slot name="home-hero-info" /></template>
62
62
  <template #home-hero-info-after><slot name="home-hero-info-after" /></template>
63
63
  <template #home-hero-actions-after><slot name="home-hero-actions-after" /></template>
64
+ <template #home-hero-actions-before-actions><slot name="home-hero-actions-before-actions" /></template>
64
65
  <template #home-hero-image><slot name="home-hero-image" /></template>
65
66
  <template #home-hero-after><slot name="home-hero-after" /></template>
66
67
  <template #home-features-before><slot name="home-features-before" /></template>
@@ -1,12 +1,16 @@
1
1
  <script setup lang="ts">
2
2
  import type { DocSearchInstance, DocSearchProps } from '@docsearch/js'
3
- import type { SidepanelInstance, SidepanelProps } from '@docsearch/sidepanel-js'
3
+ import type { SidepanelInstance } from '@docsearch/sidepanel-js'
4
4
  import { inBrowser, useRouter } from 'vitepress'
5
5
  import type { DefaultTheme } from 'vitepress/theme'
6
6
  import { nextTick, onUnmounted, watch } from 'vue'
7
7
  import type { DocSearchAskAi } from '../../../../types/docsearch'
8
8
  import { useData } from '../composables/data'
9
- import { resolveMode, validateCredentials } from '../support/docsearch'
9
+ import {
10
+ buildSidePanelProps,
11
+ resolveMode,
12
+ validateCredentials
13
+ } from '../support/docsearch'
10
14
 
11
15
  import '../styles/docsearch.css'
12
16
 
@@ -105,12 +109,7 @@ async function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
105
109
  if (currentInitialize !== initializeCount) return
106
110
 
107
111
  sidepanelInstance = sidepanel({
108
- ...(askAi.sidePanel === true ? {} : askAi.sidePanel),
109
- container: '#vp-docsearch-sidepanel',
110
- indexName: askAi.indexName ?? userOptions.indexName,
111
- appId: askAi.appId ?? userOptions.appId,
112
- apiKey: askAi.apiKey ?? userOptions.apiKey,
113
- assistantId: askAi.assistantId,
112
+ ...buildSidePanelProps(askAi, userOptions),
114
113
  onOpen: focusInput,
115
114
  onClose: onClose.bind(null, 'sidepanel'),
116
115
  onReady: () => {
@@ -122,7 +121,7 @@ async function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
122
121
  keyboardShortcuts: {
123
122
  'Ctrl/Cmd+I': false
124
123
  }
125
- } as SidepanelProps)
124
+ })
126
125
  }
127
126
 
128
127
  const options = {
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import { normalizeLink } from '../support/utils'
4
3
  import { EXTERNAL_URL_RE } from '../../shared'
4
+ import { normalizeLink } from '../support/utils'
5
5
 
6
6
  interface Props {
7
7
  tag?: string
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { DefaultTheme } from 'vitepress/theme'
3
- import { ref, watch, onMounted } from 'vue'
3
+ import { onMounted, ref, watch } from 'vue'
4
4
  import { useAside } from '../composables/aside'
5
5
  import { useData } from '../composables/data'
6
6
 
@@ -19,9 +19,14 @@ let isInitialized = false
19
19
  function init() {
20
20
  if (!isInitialized) {
21
21
  isInitialized = true
22
+ const params = new URLSearchParams({
23
+ serve: carbonOptions.code,
24
+ placement: carbonOptions.placement,
25
+ format: carbonOptions?.format || 'classic',
26
+ })
22
27
  const s = document.createElement('script')
23
28
  s.id = '_carbonads_js'
24
- s.src = `//cdn.carbonads.com/carbon.js?serve=${carbonOptions.code}&placement=${carbonOptions.placement}`
29
+ s.src = `//cdn.carbonads.com/carbon.js?${params.toString()}`
25
30
  s.async = true
26
31
  container.value.appendChild(s)
27
32
  }
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import { resolveDynamicComponent } from 'vue'
2
3
  import NotFound from '../NotFound.vue'
3
4
  import { useData } from '../composables/data'
4
5
  import { useLayout } from '../composables/layout'
@@ -8,6 +9,10 @@ import VPPage from './VPPage.vue'
8
9
 
9
10
  const { page, frontmatter } = useData()
10
11
  const { isHome, hasSidebar } = useLayout()
12
+
13
+ function isRegistered(component: string): boolean {
14
+ return typeof resolveDynamicComponent(component) !== 'string'
15
+ }
11
16
  </script>
12
17
 
13
18
  <template>
@@ -18,29 +23,25 @@ const { isHome, hasSidebar } = useLayout()
18
23
  >
19
24
  <slot name="not-found" v-if="page.isNotFound"><NotFound /></slot>
20
25
 
21
- <VPPage v-else-if="frontmatter.layout === 'page'">
26
+ <VPPage v-else-if="frontmatter.layout === 'page' && !isRegistered('page')">
22
27
  <template #page-top><slot name="page-top" /></template>
23
28
  <template #page-bottom><slot name="page-bottom" /></template>
24
29
  </VPPage>
25
30
 
26
- <VPHome v-else-if="frontmatter.layout === 'home'">
31
+ <VPHome v-else-if="frontmatter.layout === 'home' && !isRegistered('home')">
27
32
  <template #home-hero-before><slot name="home-hero-before" /></template>
28
33
  <template #home-hero-info-before><slot name="home-hero-info-before" /></template>
29
34
  <template #home-hero-info><slot name="home-hero-info" /></template>
30
35
  <template #home-hero-info-after><slot name="home-hero-info-after" /></template>
31
36
  <template #home-hero-actions-after><slot name="home-hero-actions-after" /></template>
37
+ <template #home-hero-actions-before-actions><slot name="home-hero-actions-before-actions" /></template>
32
38
  <template #home-hero-image><slot name="home-hero-image" /></template>
33
39
  <template #home-hero-after><slot name="home-hero-after" /></template>
34
40
  <template #home-features-before><slot name="home-features-before" /></template>
35
41
  <template #home-features-after><slot name="home-features-after" /></template>
36
42
  </VPHome>
37
43
 
38
- <component
39
- v-else-if="frontmatter.layout && frontmatter.layout !== 'doc'"
40
- :is="frontmatter.layout"
41
- />
42
-
43
- <VPDoc v-else>
44
+ <VPDoc v-else-if="(!frontmatter.layout || frontmatter.layout === 'doc') && !isRegistered('doc')">
44
45
  <template #doc-top><slot name="doc-top" /></template>
45
46
  <template #doc-bottom><slot name="doc-bottom" /></template>
46
47
 
@@ -55,6 +56,8 @@ const { isHome, hasSidebar } = useLayout()
55
56
  <template #aside-ads-after><slot name="aside-ads-after" /></template>
56
57
  <template #aside-bottom><slot name="aside-bottom" /></template>
57
58
  </VPDoc>
59
+
60
+ <component v-else :is="frontmatter.layout || 'doc'" />
58
61
  </div>
59
62
  </template>
60
63
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { useData } from '../composables/data'
3
- import VPDocAsideOutline from './VPDocAsideOutline.vue'
4
3
  import VPDocAsideCarbonAds from './VPDocAsideCarbonAds.vue'
4
+ import VPDocAsideOutline from './VPDocAsideOutline.vue'
5
5
 
6
6
  const { theme } = useData()
7
7
  </script>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { defineAsyncComponent } from 'vue'
3
2
  import type { DefaultTheme } from 'vitepress/theme'
3
+ import { defineAsyncComponent } from 'vue'
4
4
 
5
5
  defineProps<{
6
6
  carbonAds: DefaultTheme.CarbonAdsOptions
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
3
  import { useData } from '../composables/data'
4
+ import { useLayout } from '../composables/layout'
4
5
  import { resolveTitle, useActiveAnchor } from '../composables/outline'
5
6
  import VPDocOutlineItem from './VPDocOutlineItem.vue'
6
- import { useLayout } from '../composables/layout'
7
7
 
8
8
  const { theme } = useData()
9
9
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { Sponsors } from './VPSponsors.vue'
3
- import type { Sponsor } from './VPSponsorsGrid.vue'
4
3
  import VPSponsors from './VPSponsors.vue'
4
+ import type { Sponsor } from './VPSponsorsGrid.vue'
5
5
 
6
6
  defineProps<{
7
7
  tier?: string