vitepress 1.0.0-alpha.2 → 1.0.0-alpha.5

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 (92) hide show
  1. package/client.d.ts +2 -0
  2. package/dist/client/app/composables/head.js +7 -41
  3. package/dist/client/app/data.js +5 -7
  4. package/dist/client/app/router.js +5 -4
  5. package/dist/client/shared.js +14 -2
  6. package/dist/client/theme-default/Layout.vue +16 -2
  7. package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +4 -46
  8. package/dist/client/theme-default/components/VPButton.vue +5 -4
  9. package/dist/client/theme-default/components/VPContent.vue +4 -1
  10. package/dist/client/theme-default/components/VPDoc.vue +14 -27
  11. package/dist/client/theme-default/components/VPDocAsideOutline.vue +3 -1
  12. package/dist/client/theme-default/components/VPDocFooter.vue +12 -6
  13. package/dist/client/theme-default/components/VPDocFooterLastUpdated.vue +4 -4
  14. package/dist/client/theme-default/components/VPFlyout.vue +9 -1
  15. package/dist/client/theme-default/components/VPFooter.vue +2 -2
  16. package/dist/client/theme-default/components/VPHero.vue +10 -11
  17. package/dist/client/theme-default/components/VPImage.vue +38 -0
  18. package/dist/client/theme-default/components/VPLink.vue +3 -2
  19. package/dist/client/theme-default/components/VPMenu.vue +8 -0
  20. package/dist/client/theme-default/components/VPNav.vue +11 -2
  21. package/dist/client/theme-default/components/VPNavBar.vue +12 -2
  22. package/dist/client/theme-default/components/VPNavBarHamburger.vue +10 -10
  23. package/dist/client/theme-default/components/VPNavBarMenuGroup.vue +17 -2
  24. package/dist/client/theme-default/components/VPNavBarMenuLink.vue +1 -1
  25. package/dist/client/theme-default/components/VPNavBarSearch.vue +23 -6
  26. package/dist/client/theme-default/components/VPNavBarTitle.vue +5 -2
  27. package/dist/client/theme-default/components/VPNavScreen.vue +2 -0
  28. package/dist/client/theme-default/components/VPNavScreenMenuGroupSection.vue +1 -1
  29. package/dist/client/theme-default/components/VPSidebar.vue +1 -2
  30. package/dist/client/theme-default/components/VPSidebarGroup.vue +15 -3
  31. package/dist/client/theme-default/components/VPSidebarLink.vue +26 -11
  32. package/dist/client/theme-default/components/VPSocialLink.vue +13 -27
  33. package/dist/client/theme-default/components/VPSocialLinks.vue +1 -1
  34. package/dist/client/theme-default/components/VPTeamMembers.vue +55 -0
  35. package/dist/client/theme-default/components/VPTeamMembersItem.vue +215 -0
  36. package/dist/client/theme-default/components/VPTeamPage.vue +53 -0
  37. package/dist/client/theme-default/components/VPTeamPageSection.vue +77 -0
  38. package/dist/client/theme-default/components/VPTeamPageTitle.vue +63 -0
  39. package/dist/client/theme-default/composables/copy-code.js +46 -8
  40. package/dist/client/theme-default/composables/nav.js +3 -1
  41. package/dist/client/theme-default/composables/outline.js +1 -2
  42. package/dist/client/theme-default/composables/prev-next.js +7 -3
  43. package/dist/client/theme-default/composables/sidebar.js +12 -3
  44. package/dist/client/theme-default/fonts/inter-cyrillic-ext.woff2 +0 -0
  45. package/dist/client/theme-default/fonts/inter-cyrillic.woff2 +0 -0
  46. package/dist/client/theme-default/fonts/inter-greek-ext.woff2 +0 -0
  47. package/dist/client/theme-default/fonts/inter-greek.woff2 +0 -0
  48. package/dist/client/theme-default/fonts/inter-italic-cyrillic-ext.woff2 +0 -0
  49. package/dist/client/theme-default/fonts/inter-italic-cyrillic.woff2 +0 -0
  50. package/dist/client/theme-default/fonts/inter-italic-greek-ext.woff2 +0 -0
  51. package/dist/client/theme-default/fonts/inter-italic-greek.woff2 +0 -0
  52. package/dist/client/theme-default/fonts/inter-italic-latin-ext.woff2 +0 -0
  53. package/dist/client/theme-default/fonts/inter-italic-latin.woff2 +0 -0
  54. package/dist/client/theme-default/fonts/inter-italic-vietnamese.woff2 +0 -0
  55. package/dist/client/theme-default/fonts/inter-latin-ext.woff2 +0 -0
  56. package/dist/client/theme-default/fonts/inter-latin.woff2 +0 -0
  57. package/dist/client/theme-default/fonts/inter-roman-cyrillic-ext.woff2 +0 -0
  58. package/dist/client/theme-default/fonts/inter-roman-cyrillic.woff2 +0 -0
  59. package/dist/client/theme-default/fonts/inter-roman-greek-ext.woff2 +0 -0
  60. package/dist/client/theme-default/fonts/inter-roman-greek.woff2 +0 -0
  61. package/dist/client/theme-default/fonts/inter-roman-latin-ext.woff2 +0 -0
  62. package/dist/client/theme-default/fonts/inter-roman-latin.woff2 +0 -0
  63. package/dist/client/theme-default/fonts/inter-roman-vietnamese.woff2 +0 -0
  64. package/dist/client/theme-default/fonts/inter-vietnamese.woff2 +0 -0
  65. package/dist/client/theme-default/index.js +4 -0
  66. package/dist/client/theme-default/styles/base.css +4 -4
  67. package/dist/client/theme-default/styles/components/vp-doc.css +47 -61
  68. package/dist/client/theme-default/styles/components/vp-sponsor.css +40 -15
  69. package/dist/client/theme-default/styles/fonts.css +185 -35
  70. package/dist/client/theme-default/styles/vars.css +6 -6
  71. package/dist/client/theme-default/support/sidebar.js +11 -3
  72. package/dist/client/theme-default/support/socialIcons.js +11 -0
  73. package/dist/client/theme-default/support/utils.js +2 -2
  74. package/dist/node/cli.js +10 -4
  75. package/dist/node/index.d.ts +95 -11
  76. package/dist/node/index.js +1 -1
  77. package/dist/node/{serve-42a92e2f.js → serve-111eb887.js} +179 -115
  78. package/dist/node-cjs/cli.cjs +10 -4
  79. package/dist/node-cjs/index.cjs +1 -1
  80. package/dist/node-cjs/{serve-507b373b.cjs → serve-ce0aeb68.cjs} +182 -118
  81. package/package.json +22 -4
  82. package/theme.d.ts +9 -5
  83. package/types/default-theme.d.ts +53 -5
  84. package/dist/client/theme-default/components/icons/VPIconDiscord.vue +0 -5
  85. package/dist/client/theme-default/components/icons/VPIconFacebook.vue +0 -5
  86. package/dist/client/theme-default/components/icons/VPIconGitHub.vue +0 -5
  87. package/dist/client/theme-default/components/icons/VPIconInstagram.vue +0 -5
  88. package/dist/client/theme-default/components/icons/VPIconLinkedIn.vue +0 -5
  89. package/dist/client/theme-default/components/icons/VPIconSlack.vue +0 -5
  90. package/dist/client/theme-default/components/icons/VPIconTwitter.vue +0 -5
  91. package/dist/client/theme-default/components/icons/VPIconYouTube.vue +0 -5
  92. package/dist/client/theme-default/config.js +0 -1
package/client.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  // re-export vite client types. with strict installers like pnpm, user won't
2
2
  // be able to reference vite/client in project root.
3
3
  /// <reference types="vite/client" />
4
+
5
+ export * from './dist/client'
@@ -1,5 +1,5 @@
1
1
  import { watchEffect } from 'vue';
2
- import { createTitle } from '../../shared';
2
+ import { createTitle, mergeHead } from '../../shared';
3
3
  export function useUpdateHead(route, siteDataByRouteRef) {
4
4
  let managedHeadTags = [];
5
5
  let isFirstUpdate = true;
@@ -10,59 +10,25 @@ export function useUpdateHead(route, siteDataByRouteRef) {
10
10
  isFirstUpdate = false;
11
11
  return;
12
12
  }
13
- const newEls = [];
14
- const commonLength = Math.min(managedHeadTags.length, newTags.length);
15
- for (let i = 0; i < commonLength; i++) {
16
- let el = managedHeadTags[i];
17
- const [tag, attrs, innerHTML = ''] = newTags[i];
18
- if (el.tagName.toLocaleLowerCase() === tag) {
19
- for (const key in attrs) {
20
- if (el.getAttribute(key) !== attrs[key]) {
21
- el.setAttribute(key, attrs[key]);
22
- }
23
- }
24
- for (let i = 0; i < el.attributes.length; i++) {
25
- const name = el.attributes[i].name;
26
- if (!(name in attrs)) {
27
- el.removeAttribute(name);
28
- }
29
- }
30
- if (el.innerHTML !== innerHTML) {
31
- el.innerHTML = innerHTML;
32
- }
33
- }
34
- else {
35
- document.head.removeChild(el);
36
- el = createHeadElement(newTags[i]);
37
- document.head.append(el);
38
- }
39
- newEls.push(el);
40
- }
41
- managedHeadTags
42
- .slice(commonLength)
43
- .forEach((el) => document.head.removeChild(el));
44
- newTags.slice(commonLength).forEach((headConfig) => {
13
+ managedHeadTags.forEach((el) => document.head.removeChild(el));
14
+ managedHeadTags = [];
15
+ newTags.forEach((headConfig) => {
45
16
  const el = createHeadElement(headConfig);
46
17
  document.head.appendChild(el);
47
- newEls.push(el);
18
+ managedHeadTags.push(el);
48
19
  });
49
- managedHeadTags = newEls;
50
20
  };
51
21
  watchEffect(() => {
52
22
  const pageData = route.data;
53
23
  const siteData = siteDataByRouteRef.value;
54
24
  const pageDescription = pageData && pageData.description;
55
- const frontmatterHead = pageData && pageData.frontmatter.head;
25
+ const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
56
26
  // update title and description
57
27
  document.title = createTitle(siteData, pageData);
58
28
  document
59
29
  .querySelector(`meta[name=description]`)
60
30
  .setAttribute('content', pageDescription || siteData.description);
61
- updateHeadTags([
62
- // site head can only change during dev
63
- ...(import.meta.env.DEV ? siteData.head : []),
64
- ...(frontmatterHead ? filterOutHeadDescription(frontmatterHead) : [])
65
- ]);
31
+ updateHeadTags(mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead)));
66
32
  });
67
33
  }
68
34
  function createHeadElement([tag, attrs, innerHTML]) {
@@ -1,17 +1,15 @@
1
1
  import { shallowRef, readonly, computed, inject } from 'vue';
2
- import serializedSiteData from '@siteData';
2
+ import siteData from '@siteData';
3
3
  import { resolveSiteDataByRoute, createTitle } from '../shared';
4
4
  import { withBase } from './utils';
5
5
  export const dataSymbol = Symbol();
6
- export const siteDataRef = shallowRef(parse(serializedSiteData));
7
- function parse(data) {
8
- const parsed = JSON.parse(data);
9
- return (import.meta.env.DEV ? readonly(parsed) : parsed);
10
- }
6
+ export const siteDataRef = shallowRef(import.meta.env.PROD ? siteData : readonly(siteData));
11
7
  // hmr
12
8
  if (import.meta.hot) {
13
9
  import.meta.hot.accept('/@siteData', (m) => {
14
- siteDataRef.value = parse(m.default);
10
+ if (m) {
11
+ siteDataRef.value = m.default;
12
+ }
15
13
  });
16
14
  }
17
15
  // per-app data
@@ -97,7 +97,7 @@ export function createRouter(loadPageModule, fallbackComponent) {
97
97
  window.addEventListener('click', (e) => {
98
98
  const link = e.target.closest('a');
99
99
  if (link) {
100
- const { href, protocol, hostname, pathname, hash, target } = link;
100
+ const { href, origin, pathname, hash, search, target } = link;
101
101
  const currentUrl = window.location;
102
102
  const extMatch = pathname.match(/\.\w+$/);
103
103
  // only intercept inbound links
@@ -106,11 +106,12 @@ export function createRouter(loadPageModule, fallbackComponent) {
106
106
  !e.altKey &&
107
107
  !e.metaKey &&
108
108
  target !== `_blank` &&
109
- protocol === currentUrl.protocol &&
110
- hostname === currentUrl.hostname &&
109
+ origin === currentUrl.origin &&
110
+ // don't intercept if non-html extension is present
111
111
  !(extMatch && extMatch[0] !== '.html')) {
112
112
  e.preventDefault();
113
- if (pathname === currentUrl.pathname) {
113
+ if (pathname === currentUrl.pathname &&
114
+ search === currentUrl.search) {
114
115
  // scroll between hash anchors in the same page
115
116
  if (hash && hash !== currentUrl.hash) {
116
117
  history.pushState(null, '', hash);
@@ -1,4 +1,4 @@
1
- export const EXTERNAL_URL_RE = /^https?:/i;
1
+ export const EXTERNAL_URL_RE = /^[a-z]+:/i;
2
2
  export const APPEARANCE_KEY = 'vitepress-theme-appearance';
3
3
  // @ts-ignore
4
4
  export const inBrowser = typeof window !== 'undefined';
@@ -7,7 +7,7 @@ export const notFoundPageData = {
7
7
  title: '404',
8
8
  description: 'Not Found',
9
9
  headers: [],
10
- frontmatter: {},
10
+ frontmatter: { sidebar: false, layout: 'page' },
11
11
  lastUpdated: 0
12
12
  };
13
13
  function findMatchRoot(route, roots) {
@@ -93,3 +93,15 @@ function cleanRoute(siteData, route) {
93
93
  const baseWithoutSuffix = base.endsWith('/') ? base.slice(0, -1) : base;
94
94
  return route.slice(baseWithoutSuffix.length);
95
95
  }
96
+ function hasTag(head, tag) {
97
+ const [tagType, tagAttrs] = tag;
98
+ if (tagType !== 'meta')
99
+ return false;
100
+ const keyAttr = Object.entries(tagAttrs)[0]; // First key
101
+ if (keyAttr == null)
102
+ return false;
103
+ return head.some(([type, attrs]) => type === tagType && attrs[keyAttr[0]] === keyAttr[1]);
104
+ }
105
+ export function mergeHead(prev, curr) {
106
+ return [...prev.filter((tagAttrs) => !hasTag(curr, tagAttrs)), ...curr];
107
+ }
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { provide } from 'vue'
2
+ import { provide, watch } from 'vue'
3
+ import { useRoute } from 'vitepress'
3
4
  import { useSidebar, useCloseSidebarOnEscape } from './composables/sidebar'
4
5
  import VPSkipLink from './components/VPSkipLink.vue'
5
6
  import VPBackdrop from './components/VPBackdrop.vue'
@@ -15,6 +16,9 @@ const {
15
16
  close: closeSidebar
16
17
  } = useSidebar()
17
18
 
19
+ const route = useRoute()
20
+ watch(() => route.path, closeSidebar)
21
+
18
22
  useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
19
23
 
20
24
  provide('close-sidebar', closeSidebar)
@@ -22,9 +26,17 @@ provide('close-sidebar', closeSidebar)
22
26
 
23
27
  <template>
24
28
  <div class="Layout">
29
+ <slot name="layout-top" />
25
30
  <VPSkipLink />
26
31
  <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" />
27
- <VPNav />
32
+ <VPNav>
33
+ <template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
34
+ <template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
35
+ <template #nav-bar-content-before><slot name="nav-bar-content-before" /></template>
36
+ <template #nav-bar-content-after><slot name="nav-bar-content-after" /></template>
37
+ <template #nav-screen-content-before><slot name="nav-screen-content-before" /></template>
38
+ <template #nav-screen-content-after><slot name="nav-screen-content-after" /></template>
39
+ </VPNav>
28
40
  <VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar" />
29
41
  <VPSidebar :open="isSidebarOpen" />
30
42
 
@@ -34,6 +46,7 @@ provide('close-sidebar', closeSidebar)
34
46
  <template #home-features-before><slot name="home-features-before" /></template>
35
47
  <template #home-features-after><slot name="home-features-after" /></template>
36
48
 
49
+ <template #doc-footer-before><slot name="doc-footer-before" /></template>
37
50
  <template #doc-before><slot name="doc-before" /></template>
38
51
  <template #doc-after><slot name="doc-after" /></template>
39
52
 
@@ -46,6 +59,7 @@ provide('close-sidebar', closeSidebar)
46
59
  </VPContent>
47
60
 
48
61
  <VPFooter />
62
+ <slot name="layout-bottom" />
49
63
  </div>
50
64
  </template>
51
65
 
@@ -1,9 +1,8 @@
1
1
  <script setup lang="ts">
2
+ import type { DefaultTheme } from 'vitepress/theme'
2
3
  import docsearch from '@docsearch/js'
3
- import { DocSearchHit } from '@docsearch/react/dist/esm/types'
4
4
  import { onMounted } from 'vue'
5
5
  import { useRouter, useRoute, useData } from 'vitepress'
6
- import { DefaultTheme } from '../config'
7
6
 
8
7
  const router = useRouter()
9
8
  const route = useRoute()
@@ -52,7 +51,7 @@ function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
52
51
  }
53
52
  },
54
53
 
55
- transformItems(items: DocSearchHit[]) {
54
+ transformItems(items: any[]) {
56
55
  return items.map((item) => {
57
56
  return Object.assign({}, item, {
58
57
  url: getRelativePath(item.url)
@@ -60,44 +59,14 @@ function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
60
59
  })
61
60
  },
62
61
 
63
- hitComponent({ hit, children }: { hit: DocSearchHit, children: any }) {
64
- const relativeHit = hit.url.startsWith('http')
65
- ? getRelativePath(hit.url as string)
66
- : hit.url
67
-
62
+ hitComponent({ hit, children }: { hit: any; children: any }) {
68
63
  return {
69
64
  __v: null,
70
65
  type: 'a',
71
66
  ref: undefined,
72
67
  constructor: undefined,
73
68
  key: undefined,
74
-
75
- props: {
76
- href: hit.url,
77
-
78
- onClick(event: MouseEvent) {
79
- if (isSpecialClick(event)) {
80
- return
81
- }
82
-
83
- // we rely on the native link scrolling when user is already on
84
- // the right anchor because Router doesn't support duplicated
85
- // history entries.
86
- if (route.path === relativeHit) {
87
- return
88
- }
89
-
90
- // if the hits goes to another page, we prevent the native link
91
- // behavior to leverage the Router loading feature.
92
- if (route.path !== relativeHit) {
93
- event.preventDefault()
94
- }
95
-
96
- router.go(relativeHit)
97
- },
98
-
99
- children
100
- }
69
+ props: { href: hit.url, children }
101
70
  }
102
71
  }
103
72
  })
@@ -105,19 +74,8 @@ function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
105
74
  docsearch(options)
106
75
  }
107
76
 
108
- function isSpecialClick(event: MouseEvent) {
109
- return (
110
- event.button === 1 ||
111
- event.altKey ||
112
- event.ctrlKey ||
113
- event.metaKey ||
114
- event.shiftKey
115
- )
116
- }
117
-
118
77
  function getRelativePath(absoluteUrl: string) {
119
78
  const { pathname, hash } = new URL(absoluteUrl)
120
-
121
79
  return pathname + hash
122
80
  }
123
81
  </script>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import { withBase } from 'vitepress'
3
+ import { normalizeLink } from '../support/utils'
4
+ import { EXTERNAL_URL_RE } from '../../shared'
4
5
 
5
6
  const props = defineProps<{
6
7
  tag?: string
@@ -15,7 +16,7 @@ const classes = computed(() => [
15
16
  props.theme ?? 'brand'
16
17
  ])
17
18
 
18
- const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href))
19
+ const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href))
19
20
 
20
21
  const component = computed(() => {
21
22
  if (props.tag) {
@@ -31,9 +32,9 @@ const component = computed(() => {
31
32
  :is="component"
32
33
  class="VPButton"
33
34
  :class="classes"
34
- :href="href ? withBase(href) : undefined"
35
+ :href="href ? normalizeLink(href) : undefined"
35
36
  :target="isExternal ? '_blank' : undefined"
36
- :rel="isExternal ? 'noopener noreferrer' : undefined"
37
+ :rel="isExternal ? 'noreferrer' : undefined"
37
38
  >
38
39
  {{ text }}
39
40
  </component>
@@ -2,7 +2,7 @@
2
2
  import { useRoute, useData } from 'vitepress'
3
3
  import { useCopyCode } from '../composables/copy-code'
4
4
  import { useSidebar } from '../composables/sidebar'
5
- import NotFound from '../NotFound.vue'
5
+ import Theme from '/@theme/index'
6
6
  import VPPage from './VPPage.vue'
7
7
  import VPHome from './VPHome.vue'
8
8
  import VPDoc from './VPDoc.vue'
@@ -11,6 +11,8 @@ const route = useRoute()
11
11
  const { frontmatter } = useData()
12
12
  const { hasSidebar } = useSidebar()
13
13
 
14
+ const NotFound = Theme.NotFound || (() => '404 Not Found')
15
+
14
16
  useCopyCode()
15
17
  </script>
16
18
 
@@ -35,6 +37,7 @@ useCopyCode()
35
37
  </VPHome>
36
38
 
37
39
  <VPDoc v-else>
40
+ <template #doc-footer-before><slot name="doc-footer-before" /></template>
38
41
  <template #doc-before><slot name="doc-before" /></template>
39
42
  <template #doc-after><slot name="doc-after" /></template>
40
43
 
@@ -1,19 +1,25 @@
1
1
  <script setup lang="ts">
2
+ import { computed } from 'vue'
2
3
  import { useRoute } from 'vitepress'
3
4
  import { useSidebar } from '../composables/sidebar'
4
5
  import VPDocAside from './VPDocAside.vue'
5
6
  import VPDocFooter from './VPDocFooter.vue'
6
7
 
7
- const { path } = useRoute()
8
- const { hasSidebar } = useSidebar()
8
+ const route = useRoute()
9
+ const { hasSidebar, hasAside } = useSidebar()
9
10
 
10
- const pageName = path.replace(/[./]+/g, '_').replace(/_html$/, '')
11
+ const pageName = computed(() =>
12
+ route.path.replace(/[./]+/g, '_').replace(/_html$/, '')
13
+ )
11
14
  </script>
12
15
 
13
16
  <template>
14
- <div class="VPDoc" :class="{ 'has-sidebar': hasSidebar }">
17
+ <div
18
+ class="VPDoc"
19
+ :class="{ 'has-sidebar': hasSidebar, 'has-aside': hasAside }"
20
+ >
15
21
  <div class="container">
16
- <div class="aside">
22
+ <div v-if="hasAside" class="aside">
17
23
  <div class="aside-curtain" />
18
24
  <div class="aside-container">
19
25
  <div class="aside-content">
@@ -32,13 +38,11 @@ const pageName = path.replace(/[./]+/g, '_').replace(/_html$/, '')
32
38
  <div class="content">
33
39
  <div class="content-container">
34
40
  <slot name="doc-before" />
35
-
36
41
  <main class="main">
37
42
  <Content class="vp-doc" :class="pageName" />
38
43
  </main>
39
-
44
+ <slot name="doc-footer-before" />
40
45
  <VPDocFooter />
41
-
42
46
  <slot name="doc-after" />
43
47
  </div>
44
48
  </div>
@@ -167,26 +171,9 @@ const pageName = path.replace(/[./]+/g, '_').replace(/_html$/, '')
167
171
 
168
172
  .content-container {
169
173
  margin: 0 auto;
170
- max-width: 688px;
171
- }
172
-
173
- .edit-link {
174
- margin: 0 0 32px;
175
- }
176
-
177
- .edit-link .vt-link {
178
- font-size: 14px;
179
- color: var(--vt-c-brand);
180
- font-weight: 500;
181
174
  }
182
175
 
183
- .vt-icon {
184
- width: 18px;
185
- height: 18px;
186
- color: var(--vt-c-brand);
187
- display: inline-block;
188
- margin-right: 8px;
189
- position: relative;
190
- top: -1px;
176
+ .VPDoc.has-aside .content-container {
177
+ max-width: 688px;
191
178
  }
192
179
  </style>
@@ -22,7 +22,9 @@ const resolvedHeaders = computed(() => {
22
22
 
23
23
  function handleClick({ target: el }: Event) {
24
24
  const id = '#' + (el as HTMLAnchorElement).href!.split('#')[1]
25
- const heading = document.querySelector(id) as HTMLAnchorElement
25
+ const heading = document.querySelector(
26
+ decodeURIComponent(id)
27
+ ) as HTMLAnchorElement
26
28
  heading?.focus()
27
29
  }
28
30
  </script>
@@ -13,15 +13,21 @@ const { theme, page, frontmatter } = useData()
13
13
  const editLink = useEditLink()
14
14
  const control = usePrevNext()
15
15
 
16
+ const hasEditLink = computed(() => {
17
+ return theme.value.editLink && frontmatter.value.editLink !== false
18
+ })
16
19
  const hasLastUpdated = computed(() => {
17
20
  return page.value.lastUpdated && frontmatter.value.lastUpdated !== false
18
21
  })
22
+ const showFooter = computed(() => {
23
+ return hasEditLink.value || hasLastUpdated.value || control.value.prev || control.value.next
24
+ })
19
25
  </script>
20
26
 
21
27
  <template>
22
- <footer v-if="control.prev || control.next" class="VPDocFooter">
23
- <div class="edit-info">
24
- <div v-if="theme.editLink && frontmatter.editLink !== false" class="edit-link">
28
+ <footer v-if="showFooter" class="VPDocFooter">
29
+ <div v-if="hasEditLink || hasLastUpdated" class="edit-info">
30
+ <div v-if="hasEditLink" class="edit-link">
25
31
  <VPLink class="edit-link-button" :href="editLink.url" :no-icon="true">
26
32
  <VPIconEdit class="edit-link-icon" />
27
33
  {{ editLink.text }}
@@ -33,16 +39,16 @@ const hasLastUpdated = computed(() => {
33
39
  </div>
34
40
  </div>
35
41
 
36
- <div class="prev-next">
42
+ <div v-if="control.prev || control.next" class="prev-next">
37
43
  <div class="pager">
38
44
  <a v-if="control.prev" class="pager-link prev" :href="normalizeLink(control.prev.link)">
39
- <span class="desc">Previous page</span>
45
+ <span class="desc">{{ theme.docFooter?.prev ?? 'Previous page' }}</span>
40
46
  <span class="title">{{ control.prev.text }} </span>
41
47
  </a>
42
48
  </div>
43
49
  <div class="pager" :class="{ 'has-prev': control.prev }">
44
50
  <a v-if="control.next" class="pager-link next" :href="normalizeLink(control.next.link)">
45
- <span class="desc">Next page</span>
51
+ <span class="desc">{{ theme.docFooter?.next ?? 'Next page' }}</span>
46
52
  <span class="title">{{ control.next.text }}</span>
47
53
  </a>
48
54
  </div>
@@ -1,11 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { ref, watchEffect, onMounted } from 'vue'
2
+ import { ref, computed, watchEffect, onMounted } from 'vue'
3
3
  import { useData } from 'vitepress'
4
4
 
5
5
  const { theme, page } = useData()
6
6
 
7
- const date = new Date(page.value.lastUpdated!)
8
- const isoDatetime = date.toISOString()
7
+ const date = computed(() => new Date(page.value.lastUpdated!))
8
+ const isoDatetime = computed(() => date.value.toISOString())
9
9
  const datetime = ref('')
10
10
 
11
11
  // set time on mounted hook because the locale string might be different
@@ -13,7 +13,7 @@ const datetime = ref('')
13
13
  // calculated at build time
14
14
  onMounted(() => {
15
15
  watchEffect(() => {
16
- datetime.value = date.toLocaleString(window.navigator.language)
16
+ datetime.value = date.value.toLocaleString(window.navigator.language)
17
17
  })
18
18
  })
19
19
  </script>
@@ -60,7 +60,7 @@ function onBlur() {
60
60
  }
61
61
 
62
62
  .VPFlyout:hover {
63
- color: var(--vp-c-bland);
63
+ color: var(--vp-c-brand);
64
64
  transition: color 0.25s;
65
65
  }
66
66
 
@@ -72,6 +72,14 @@ function onBlur() {
72
72
  fill: var(--vp-c-text-2);
73
73
  }
74
74
 
75
+ .VPFlyout.active .text {
76
+ color: var(--vp-c-brand);
77
+ }
78
+
79
+ .VPFlyout.active:hover .text {
80
+ color: var(--vp-c-brand-dark);
81
+ }
82
+
75
83
  .VPFlyout:hover .menu,
76
84
  .button[aria-expanded="true"] + .menu {
77
85
  opacity: 1;
@@ -9,8 +9,8 @@ const { hasSidebar } = useSidebar()
9
9
  <template>
10
10
  <footer v-if="theme.footer" class="VPFooter" :class="{ 'has-sidebar': hasSidebar }">
11
11
  <div class="container">
12
- <p class="message">{{ theme.footer.message }}</p>
13
- <p class="copyright">{{ theme.footer.copyright }}</p>
12
+ <p class="message" v-html="theme.footer.message"></p>
13
+ <p class="copyright" v-html="theme.footer.copyright"></p>
14
14
  </div>
15
15
  </footer>
16
16
  </template>
@@ -1,5 +1,7 @@
1
1
  <script setup lang="ts">
2
+ import type { DefaultTheme } from 'vitepress/theme'
2
3
  import VPButton from './VPButton.vue'
4
+ import VPImage from './VPImage.vue'
3
5
 
4
6
  export interface HeroAction {
5
7
  theme?: 'brand' | 'alt'
@@ -7,16 +9,11 @@ export interface HeroAction {
7
9
  link: string
8
10
  }
9
11
 
10
- export interface Image {
11
- src: string
12
- alt?: string
13
- }
14
-
15
12
  defineProps<{
16
13
  name?: string
17
14
  text: string
18
15
  tagline?: string
19
- image?: Image
16
+ image?: DefaultTheme.ThemeableImage
20
17
  actions?: HeroAction[]
21
18
  }>()
22
19
  </script>
@@ -25,7 +22,9 @@ defineProps<{
25
22
  <div class="VPHero" :class="{ 'has-image': image }">
26
23
  <div class="container">
27
24
  <div class="main">
28
- <h1 v-if="name" class="name"><span class="clip">{{ name }}</span></h1>
25
+ <h1 v-if="name" class="name">
26
+ <span class="clip">{{ name }}</span>
27
+ </h1>
29
28
  <p v-if="text" class="text">{{ text }}</p>
30
29
  <p v-if="tagline" class="tagline">{{ tagline }}</p>
31
30
 
@@ -45,7 +44,7 @@ defineProps<{
45
44
  <div v-if="image" class="image">
46
45
  <div class="image-container">
47
46
  <div class="image-bg" />
48
- <img class="image-src" :src="image.src" :alt="image.alt">
47
+ <VPImage class="image-src" :image="image" />
49
48
  </div>
50
49
  </div>
51
50
  </div>
@@ -293,7 +292,7 @@ defineProps<{
293
292
  }
294
293
  }
295
294
 
296
- .image-src {
295
+ :deep(.image-src) {
297
296
  position: absolute;
298
297
  top: 50%;
299
298
  left: 50%;
@@ -302,13 +301,13 @@ defineProps<{
302
301
  }
303
302
 
304
303
  @media (min-width: 640px) {
305
- .image-src {
304
+ :deep(.image-src) {
306
305
  max-width: 256px;
307
306
  }
308
307
  }
309
308
 
310
309
  @media (min-width: 960px) {
311
- .image-src {
310
+ :deep(.image-src) {
312
311
  max-width: 320px;
313
312
  }
314
313
  }
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import type { DefaultTheme } from 'vitepress/theme'
3
+ import { withBase } from 'vitepress'
4
+
5
+ defineProps<{
6
+ image: DefaultTheme.ThemeableImage
7
+ }>()
8
+ </script>
9
+
10
+ <script lang="ts">
11
+ export default {
12
+ inheritAttrs: false
13
+ }
14
+ </script>
15
+
16
+ <template>
17
+ <template v-if="image">
18
+ <img
19
+ v-if="typeof image === 'string' || 'src' in image"
20
+ class="VPImage"
21
+ v-bind="typeof image === 'string' ? $attrs : { ...image, ...$attrs }"
22
+ :src="withBase(typeof image === 'string' ? image : image.src)"
23
+ />
24
+ <template v-else>
25
+ <VPImage class="dark" :image="image.dark" v-bind="$attrs" />
26
+ <VPImage class="light" :image="image.light" v-bind="$attrs" />
27
+ </template>
28
+ </template>
29
+ </template>
30
+
31
+ <style scoped>
32
+ html:not(.dark) .VPImage.dark {
33
+ display: none;
34
+ }
35
+ .dark .VPImage.light {
36
+ display: none;
37
+ }
38
+ </style>