valaxy 0.5.0 → 0.6.2

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 (64) hide show
  1. package/README.md +0 -4
  2. package/{dist/types/index.mjs → client/app/data.ts} +0 -0
  3. package/client/components/PostCard.vue +4 -2
  4. package/client/components/ValaxyBg.vue +1 -1
  5. package/client/components/ValaxyFooter.vue +1 -1
  6. package/client/components/ValaxyMd.vue +34 -27
  7. package/client/components/ValaxyToc.vue +3 -3
  8. package/client/composables/copy-code.ts +92 -0
  9. package/client/composables/outline.ts +168 -0
  10. package/client/composables/sidebar.ts +46 -1
  11. package/client/config.ts +30 -7
  12. package/client/main.ts +4 -1
  13. package/client/modules/valaxy.ts +19 -7
  14. package/client/shims.d.ts +6 -1
  15. package/client/styles/common/code.scss +191 -167
  16. package/client/styles/common/markdown.scss +0 -2
  17. package/client/styles/css-vars.scss +30 -7
  18. package/client/styles/palette.scss +21 -2
  19. package/client/styles/vars.scss +32 -18
  20. package/client/utils/helper.ts +22 -0
  21. package/client/utils/sidebar.ts +26 -0
  22. package/config/index.ts +18 -0
  23. package/dist/chunk-23IW567G.mjs +40 -0
  24. package/dist/chunk-YUC5WP5Y.js +40 -0
  25. package/dist/{config-7bd43d41.d.ts → config-112ac884.d.ts} +15 -4
  26. package/dist/index.d.ts +361 -0
  27. package/dist/index.js +1 -0
  28. package/dist/index.mjs +1 -0
  29. package/dist/node/cli.js +7 -11
  30. package/dist/node/cli.mjs +7 -11
  31. package/dist/node/index.d.ts +3 -2
  32. package/dist/node/index.js +1 -1
  33. package/dist/node/index.mjs +1 -1
  34. package/index.ts +3 -0
  35. package/node/config.ts +9 -23
  36. package/node/markdown/highlight.ts +27 -39
  37. package/node/markdown/index.ts +30 -16
  38. package/node/markdown/markdown-it/highlightLines.ts +1 -1
  39. package/node/markdown/markdownToVue.ts +275 -0
  40. package/node/options.ts +16 -0
  41. package/node/plugins/extendConfig.ts +4 -3
  42. package/node/plugins/index.ts +104 -7
  43. package/node/plugins/preset.ts +8 -8
  44. package/node/rss.ts +1 -1
  45. package/node/shims.d.ts +0 -5
  46. package/node/utils/getGitTimestamp.ts +13 -0
  47. package/node/utils/index.ts +11 -0
  48. package/package.json +20 -9
  49. package/shared/index.ts +1 -0
  50. package/tsup.config.ts +5 -3
  51. package/types/config.ts +7 -4
  52. package/types/data.ts +31 -0
  53. package/types/index.ts +1 -0
  54. package/types/posts.ts +1 -1
  55. package/dist/chunk-RSQONJW3.mjs +0 -86
  56. package/dist/chunk-XQIGHIAX.js +0 -86
  57. package/dist/client/index.d.ts +0 -188
  58. package/dist/client/index.js +0 -1
  59. package/dist/client/index.mjs +0 -1
  60. package/dist/posts-32f55e33.d.ts +0 -117
  61. package/dist/types/index.d.ts +0 -8
  62. package/dist/types/index.js +0 -1
  63. package/index.d.ts +0 -3
  64. package/node/plugins/markdown.ts +0 -54
package/README.md CHANGED
@@ -7,7 +7,3 @@ Read [YunYouJun/valaxy](https://github.com/YunYouJun/valaxy) for more info.
7
7
  ## FAQ
8
8
 
9
9
  - `exports`: `src/index.ts` for virtual module
10
-
11
- ### Downgrade vite-plugin-md to `v0.13.1`
12
-
13
- `v0.14.0` has some problem with `wrapperComponent`.
File without changes
@@ -20,7 +20,8 @@ const { icon, styles } = usePostProperty(props.post.type)
20
20
  :src="post.cover"
21
21
  :alt="t('post.cover')"
22
22
  w="40%"
23
- class="object-contain self-center"
23
+ h="54"
24
+ class="object-cover object-center md:shadow"
24
25
  >
25
26
 
26
27
  <div class="post-card-image-info-text flex-1" w="full">
@@ -54,7 +55,8 @@ const { icon, styles } = usePostProperty(props.post.type)
54
55
  </div>
55
56
  </div>
56
57
 
57
- <div v-if="post.categories || post.tags" w="full" class="yun-card-actions flex justify-between" border="t" text="sm">
58
+ <!-- always show -->
59
+ <div w="full" class="yun-card-actions flex justify-between" border="t" text="sm">
58
60
  <div class="inline-flex">
59
61
  <router-link
60
62
  v-if="post.categories"
@@ -1,6 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed } from 'vue'
3
- import { useThemeConfig } from 'valaxy/client'
3
+ import { useThemeConfig } from 'valaxy'
4
4
  import { useCssVar } from '@vueuse/core'
5
5
  import { isDark } from '~/composables'
6
6
 
@@ -1,6 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { capitalize, computed } from 'vue'
3
- import { useConfig, useThemeConfig } from 'valaxy/client'
3
+ import { useConfig, useThemeConfig } from 'valaxy'
4
4
  import { useI18n } from 'vue-i18n'
5
5
 
6
6
  import pkg from '../../package.json'
@@ -3,6 +3,7 @@ import type { Post } from 'valaxy'
3
3
  import { onMounted, ref } from 'vue'
4
4
  import { useI18n } from 'vue-i18n'
5
5
  import { useAplayer, useCodePen } from '~/composables'
6
+ import { useCopyCode } from '~/composables/copy-code'
6
7
  import { wrapTable } from '~/utils'
7
8
 
8
9
  const props = defineProps<{
@@ -27,41 +28,47 @@ if (props.frontmatter.aplayer)
27
28
 
28
29
  if (props.frontmatter.codepen)
29
30
  useCodePen()
31
+
32
+ useCopyCode()
30
33
  </script>
31
34
 
32
35
  <template>
33
- <article v-if="$slots.default" :class="frontmatter.markdown !== false && 'markdown-body'">
34
- <slot ref="content" @vnode-updated="updateDom" />
35
-
36
- <div text="center">
37
- <a
38
- v-if="frontmatter.url"
39
- :href="frontmatter.url"
40
- class="post-link-btn shadow hover:shadow-md"
41
- rounded
42
- target="_blank"
43
- m="b-4"
44
- >
45
- {{ t('post.view_link') }}
46
- </a>
47
- </div>
36
+ <Transition appear>
37
+ <article v-if="$slots.default" :class="frontmatter.markdown !== false && 'markdown-body'">
38
+ <slot ref="content" @vnode-updated="updateDom" />
48
39
 
49
- <slot v-if="typeof frontmatter.end !== 'undefined'" name="end">
50
- <div m="y-4" class="end flex justify-center items-center">
51
- <hr class="line inline-flex" w="full" m="!y-2">
52
- <span p="x-4" font="serif bold" class="whitespace-nowrap">
53
- {{ frontmatter.end ? 'Q.E.D.' : 'To Be Continued.' }}
54
- </span>
55
- <hr class="line inline-flex" w="full" m="!y-2">
40
+ <div text="center">
41
+ <a
42
+ v-if="frontmatter.url"
43
+ :href="frontmatter.url"
44
+ class="post-link-btn shadow hover:shadow-md"
45
+ rounded
46
+ target="_blank"
47
+ m="b-4"
48
+ >
49
+ {{ t('post.view_link') }}
50
+ </a>
56
51
  </div>
57
- </slot>
58
- </article>
52
+
53
+ <slot v-if="frontmatter.end !== undefined" name="end">
54
+ <div m="y-4" class="end flex justify-center items-center">
55
+ <hr class="line inline-flex" w="full" m="!y-2">
56
+ <span p="x-4" font="serif bold" class="whitespace-nowrap">
57
+ {{ frontmatter.end ? 'Q.E.D.' : 'To Be Continued.' }}
58
+ </span>
59
+ <hr class="line inline-flex" w="full" m="!y-2">
60
+ </div>
61
+ </slot>
62
+ </article>
63
+ </Transition>
59
64
  </template>
60
65
 
61
66
  <style lang="scss">
62
- .end {
63
- .line {
64
- height: 1px;
67
+ .markdown-body {
68
+ .end {
69
+ .line {
70
+ height: 1px;
71
+ }
65
72
  }
66
73
  }
67
74
  </style>
@@ -1,9 +1,11 @@
1
1
  <script lang="ts" setup>
2
+ // this is a runtime toc
3
+ // prebuild toc see composables/outline.ts
2
4
  import { onMounted, ref, watch } from 'vue'
3
5
  import { useI18n } from 'vue-i18n'
4
6
  import { useRoute } from 'vue-router'
5
7
 
6
- import type { Header } from '../../node/markdown'
8
+ import type { Header } from '../../types'
7
9
  import { useActiveSidebarLinks } from '~/composables'
8
10
  import { useConfig } from '~/config'
9
11
 
@@ -66,8 +68,6 @@ if (import.meta.hot) {
66
68
  }, 600)
67
69
  })
68
70
  }
69
-
70
- // todo mobile toc widget
71
71
  </script>
72
72
 
73
73
  <template>
@@ -0,0 +1,92 @@
1
+ import { nextTick, watch } from 'vue'
2
+ import { isClient } from '@vueuse/core'
3
+ import { useRoute } from 'vue-router'
4
+
5
+ export function useCopyCode() {
6
+ const route = useRoute()
7
+
8
+ if (isClient) {
9
+ watch(
10
+ () => route.path,
11
+ () => {
12
+ nextTick(() => {
13
+ document
14
+ .querySelectorAll<HTMLSpanElement>(
15
+ '.markdown-body div[class*="language-"]>span.copy',
16
+ )
17
+ .forEach(handleElement)
18
+ })
19
+ },
20
+ { immediate: true, flush: 'post' },
21
+ )
22
+ }
23
+ }
24
+
25
+ async function copyToClipboard(text: string) {
26
+ try {
27
+ return navigator.clipboard.writeText(text)
28
+ }
29
+ catch {
30
+ const element = document.createElement('textarea')
31
+ const previouslyFocusedElement = document.activeElement
32
+
33
+ element.value = text
34
+
35
+ // Prevent keyboard from showing on mobile
36
+ element.setAttribute('readonly', '')
37
+
38
+ element.style.contain = 'strict'
39
+ element.style.position = 'absolute'
40
+ element.style.left = '-9999px'
41
+ element.style.fontSize = '12pt' // Prevent zooming on iOS
42
+
43
+ const selection = document.getSelection()
44
+ const originalRange = selection
45
+ ? selection.rangeCount > 0 && selection.getRangeAt(0)
46
+ : null
47
+
48
+ document.body.appendChild(element)
49
+ element.select()
50
+
51
+ // Explicit selection workaround for iOS
52
+ element.selectionStart = 0
53
+ element.selectionEnd = text.length
54
+
55
+ document.execCommand('copy')
56
+ document.body.removeChild(element)
57
+
58
+ if (originalRange) {
59
+ selection!.removeAllRanges() // originalRange can't be truthy when selection is falsy
60
+ selection!.addRange(originalRange)
61
+ }
62
+
63
+ // Get the focus back on the previously focused element, if any
64
+ if (previouslyFocusedElement)
65
+ (previouslyFocusedElement as HTMLElement).focus()
66
+ }
67
+ }
68
+
69
+ function handleElement(el: HTMLElement) {
70
+ el.onclick = () => {
71
+ const parent = el.parentElement
72
+
73
+ if (!parent)
74
+ return
75
+
76
+ const isShell
77
+ = parent.classList.contains('language-sh')
78
+ || parent.classList.contains('language-bash')
79
+
80
+ let { innerText: text = '' } = parent
81
+
82
+ if (isShell)
83
+ text = text.replace(/^ *\$ /gm, '')
84
+
85
+ copyToClipboard(text).then(() => {
86
+ el.classList.add('copied')
87
+ setTimeout(() => {
88
+ el.classList.remove('copied')
89
+ }, 3000)
90
+ })
91
+ }
92
+ }
@@ -0,0 +1,168 @@
1
+ import type { Ref } from 'vue'
2
+ import { onMounted, onUnmounted, onUpdated } from 'vue'
3
+ import type { Header } from '../../types'
4
+ import { throttleAndDebounce } from '~/utils'
5
+
6
+ interface HeaderWithChildren extends Header {
7
+ children?: Header[]
8
+ hidden?: boolean
9
+ }
10
+
11
+ interface MenuItemWithLinkAndChildren {
12
+ text: string
13
+ link: string
14
+ children?: MenuItemWithLinkAndChildren[]
15
+ hidden?: boolean
16
+ lang?: string
17
+ }
18
+
19
+ export function resolveHeaders(headers: Header[]) {
20
+ return mapHeaders(groupHeaders(headers))
21
+ }
22
+
23
+ function groupHeaders(headers: Header[]): HeaderWithChildren[] {
24
+ headers = headers.map(h => Object.assign({}, h))
25
+
26
+ let lastH2: HeaderWithChildren | undefined
27
+
28
+ for (const h of headers) {
29
+ if (h.level === 2)
30
+ lastH2 = h
31
+
32
+ else if (lastH2 && h.level <= 3)
33
+ (lastH2.children || (lastH2.children = [])).push(h)
34
+ }
35
+
36
+ return headers.filter(h => h.level === 2)
37
+ }
38
+
39
+ function mapHeaders(
40
+ headers: HeaderWithChildren[],
41
+ ): MenuItemWithLinkAndChildren[] {
42
+ return headers.map(header => ({
43
+ text: header.title,
44
+ link: `#${header.slug}`,
45
+ children: header.children ? mapHeaders(header.children) : undefined,
46
+ hidden: header.hidden,
47
+ lang: header.lang,
48
+ }))
49
+ }
50
+
51
+ // magic number to avoid repeated retrieval
52
+ const PAGE_OFFSET = 56
53
+
54
+ export function useActiveAnchor(
55
+ container: Ref<HTMLElement>,
56
+ marker: Ref<HTMLElement>,
57
+ ) {
58
+ const onScroll = throttleAndDebounce(setActiveLink, 100)
59
+
60
+ let prevActiveLink: HTMLAnchorElement | null = null
61
+
62
+ onMounted(() => {
63
+ requestAnimationFrame(setActiveLink)
64
+ window.addEventListener('scroll', onScroll)
65
+ })
66
+
67
+ onUpdated(() => {
68
+ // sidebar update means a route change
69
+ activateLink(location.hash)
70
+ })
71
+
72
+ onUnmounted(() => {
73
+ window.removeEventListener('scroll', onScroll)
74
+ })
75
+
76
+ function setActiveLink() {
77
+ const links = [].slice.call(
78
+ container.value.querySelectorAll('.outline-link'),
79
+ ) as HTMLAnchorElement[]
80
+
81
+ const anchors = [].slice
82
+ .call(document.querySelectorAll('.content .header-anchor'))
83
+ .filter((anchor: HTMLAnchorElement) => {
84
+ return links.some((link) => {
85
+ return link.hash === anchor.hash && anchor.offsetParent !== null
86
+ })
87
+ }) as HTMLAnchorElement[]
88
+
89
+ const scrollY = window.scrollY
90
+ const innerHeight = window.innerHeight
91
+ const offsetHeight = (document.querySelector('.yun-main') as HTMLElement)!.offsetHeight
92
+ const isBottom = (scrollY + innerHeight) === offsetHeight
93
+
94
+ // console.log(scrollY, innerHeight, offsetHeight)
95
+ // console.log(isBottom)
96
+
97
+ // page bottom - highlight last one
98
+ if (anchors.length && isBottom) {
99
+ activateLink(null)
100
+ return
101
+ }
102
+
103
+ // isTop
104
+ if (anchors.length && scrollY === 0)
105
+ activateLink('#')
106
+
107
+ for (let i = 0; i < anchors.length; i++) {
108
+ const anchor = anchors[i]
109
+ const nextAnchor = anchors[i + 1]
110
+
111
+ const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor)
112
+
113
+ if (isActive) {
114
+ history.replaceState(null, document.title, hash || ' ')
115
+ activateLink(hash)
116
+ return
117
+ }
118
+ }
119
+ }
120
+
121
+ function activateLink(hash: string | null) {
122
+ if (prevActiveLink)
123
+ prevActiveLink.classList.remove('active')
124
+
125
+ if (hash !== null) {
126
+ prevActiveLink = container.value.querySelector(
127
+ `a[href="${decodeURIComponent(hash)}"]`,
128
+ ) as HTMLAnchorElement
129
+ }
130
+
131
+ const activeLink = prevActiveLink
132
+
133
+ const topOffset = 33
134
+
135
+ if (activeLink) {
136
+ activeLink.classList.add('active')
137
+ marker.value.style.top = `${activeLink.offsetTop + topOffset}px`
138
+ marker.value.style.opacity = '1'
139
+ }
140
+ else {
141
+ marker.value.style.top = `${topOffset}px`
142
+ marker.value.style.opacity = '0'
143
+ }
144
+ }
145
+ }
146
+
147
+ function getAnchorTop(anchor: HTMLAnchorElement): number {
148
+ return anchor.parentElement!.offsetTop - PAGE_OFFSET - 15
149
+ }
150
+
151
+ function isAnchorActive(
152
+ index: number,
153
+ anchor: HTMLAnchorElement,
154
+ nextAnchor: HTMLAnchorElement | undefined,
155
+ ): [boolean, string | null] {
156
+ const scrollTop = window.scrollY
157
+
158
+ if (index === 0 && scrollTop === 0)
159
+ return [true, null]
160
+
161
+ if (scrollTop < getAnchorTop(anchor))
162
+ return [false, null]
163
+
164
+ if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor))
165
+ return [true, anchor.hash]
166
+
167
+ return [false, null]
168
+ }
@@ -1,8 +1,53 @@
1
1
  import type { Ref } from 'vue'
2
- import { onMounted, onUnmounted } from 'vue'
2
+ import { computed, onMounted, onUnmounted, ref } from 'vue'
3
+ import { useRoute } from 'vue-router'
4
+ import { useThemeConfig } from '..'
5
+ import { getSidebar } from '../utils/sidebar'
6
+ import { useFrontmatter } from './common'
3
7
 
4
8
  // todo: refactor
5
9
 
10
+ export function useSidebar() {
11
+ const route = useRoute()
12
+ const frontmatter = useFrontmatter()
13
+ const themeConfig = useThemeConfig()
14
+
15
+ const isOpen = ref(false)
16
+
17
+ const sidebar = computed(() => {
18
+ const sidebarConfig = themeConfig.value.sidebar
19
+ const relativePath = route.path
20
+
21
+ return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : []
22
+ })
23
+
24
+ const hasSidebar = computed(() => {
25
+ // return frontmatter.value.sidebar !== false && sidebar.value.length > 0
26
+ return frontmatter.value.sidebar !== false
27
+ })
28
+
29
+ function open() {
30
+ isOpen.value = true
31
+ }
32
+
33
+ function close() {
34
+ isOpen.value = false
35
+ }
36
+
37
+ function toggle() {
38
+ isOpen.value ? close() : open()
39
+ }
40
+
41
+ return {
42
+ isOpen,
43
+ sidebar,
44
+ hasSidebar,
45
+ open,
46
+ close,
47
+ toggle,
48
+ }
49
+ }
50
+
6
51
  export function useActiveSidebarLinks(container: Ref<HTMLElement>, marker: Ref<HTMLElement>) {
7
52
  const onScroll = throttleAndDebounce(setActiveLink, 200)
8
53
 
package/client/config.ts CHANGED
@@ -1,28 +1,43 @@
1
1
  // @ts-expect-error virtual module @valaxyjs/config
2
2
  import valaxyConfig from '@valaxyjs/config'
3
- import type { ComputedRef, InjectionKey } from 'vue'
3
+ // @ts-expect-error virtual module @valaxyjs/context
4
+ import valaxyContext from '@valaxyjs/context'
5
+ import type { ComputedRef, InjectionKey, Ref } from 'vue'
4
6
  import { computed, inject, readonly, shallowRef } from 'vue'
5
7
  import type { ThemeConfig } from 'valaxy-theme-yun'
6
- import type { ValaxyConfig } from '../types'
8
+ // import type { RouteMeta } from 'vue-router'
9
+ import type { PageData, ValaxyConfig } from '../types'
7
10
 
8
11
  /**
9
12
  * parse valaxy config
10
13
  * @param data
11
14
  * @returns
12
15
  */
13
- function parse(data: string): ValaxyConfig {
16
+ function parse<T=any>(data: string): T {
14
17
  const parsed = JSON.parse(data)
15
- return (import.meta.env.DEV ? readonly(parsed) : parsed) as ValaxyConfig
18
+ return (import.meta.env.DEV ? readonly(parsed) : parsed) as T
19
+ }
20
+
21
+ interface ValaxyContext {
22
+ userRoot: string
16
23
  }
17
24
 
18
25
  export const valaxyConfigSymbol: InjectionKey<ComputedRef<ValaxyConfig<ThemeConfig>>> = Symbol('valaxy:config')
19
- export const valaxyConfigRef = shallowRef<ValaxyConfig>(parse(valaxyConfig))
26
+ export const valaxyConfigRef = shallowRef<ValaxyConfig>(parse<ValaxyConfig>(valaxyConfig))
27
+
28
+ export const valaxyContextRef = shallowRef<ValaxyContext>(parse<ValaxyContext>(valaxyContext))
29
+ // export const valaxyDataRef = shallowRef<PageData>(parse(valaxyConfig))
20
30
 
21
31
  // hmr
22
32
  if (import.meta.hot) {
23
33
  // /@valaxyjs/config must be static string
24
34
  import.meta.hot.accept('/@valaxyjs/config', (m) => {
25
- valaxyConfigRef.value = parse(m.default)
35
+ valaxyConfigRef.value = parse<ValaxyConfig>(m.default)
36
+ })
37
+
38
+ // context
39
+ import.meta.hot.accept('/@valaxyjs/context', (m) => {
40
+ valaxyContextRef.value = parse<ValaxyContext>(m.default)
26
41
  })
27
42
  }
28
43
 
@@ -30,6 +45,10 @@ export function initConfig() {
30
45
  return computed(() => valaxyConfigRef.value)
31
46
  }
32
47
 
48
+ export function initContext() {
49
+ return computed(() => valaxyContextRef.value)
50
+ }
51
+
33
52
  /*
34
53
  * get Config
35
54
  * @returns
@@ -37,7 +56,7 @@ export function initConfig() {
37
56
  export function useConfig() {
38
57
  const config = inject(valaxyConfigSymbol)
39
58
  if (!config)
40
- throw new Error('[Valaxy] config not properly injected in qpp')
59
+ throw new Error('[Valaxy] config not properly injected in app')
41
60
  return config!
42
61
  }
43
62
 
@@ -50,3 +69,7 @@ export function useThemeConfig() {
50
69
  return computed(() => config!.value.themeConfig)
51
70
  }
52
71
 
72
+ export interface ValaxyData<T = any> {
73
+ page: Ref<PageData>
74
+ theme: Ref<T>
75
+ }
package/client/main.ts CHANGED
@@ -18,7 +18,10 @@ export const createApp = ViteSSG(
18
18
  {
19
19
  routes,
20
20
  base: import.meta.env.BASE_URL,
21
- scrollBehavior() { return { top: 0 } },
21
+ scrollBehavior(to, from) {
22
+ if (to.path !== from.path)
23
+ return { top: 0 }
24
+ },
22
25
  },
23
26
  (ctx) => {
24
27
  // install all modules under `modules/`
@@ -10,7 +10,10 @@ import { createI18n } from 'vue-i18n'
10
10
 
11
11
  import { useStorage } from '@vueuse/core'
12
12
 
13
+ import type { Router } from 'vue-router'
14
+ import type { PageDataPayload } from '../../types'
13
15
  import { initConfig, valaxyConfigSymbol } from '../config'
16
+ import { ensureSuffix } from '@antfu/utils'
14
17
 
15
18
  import type { UserModule } from '~/types'
16
19
 
@@ -33,10 +36,11 @@ import messages from '/@valaxyjs/locales'
33
36
  // import zh from '../../../../../demo/yun/locales/zh-CN.yml'
34
37
  // import en from '../../../../../demo/yun/locales/en.yml'
35
38
 
36
- function shouldHotReload(payload: any): boolean {
39
+ function shouldHotReload(payload: PageDataPayload): boolean {
37
40
  const payloadPath = payload.path.replace(/(\bindex)?\.md$/, '')
38
41
  const locationPath = location.pathname.replace(/(\bindex)?\.html$/, '')
39
- return payloadPath === locationPath
42
+ // console.log(payloadPath, locationPath)
43
+ return ensureSuffix('/', payloadPath) === ensureSuffix('/', locationPath)
40
44
  }
41
45
 
42
46
  export const install: UserModule = ({ app, router }) => {
@@ -54,11 +58,19 @@ export const install: UserModule = ({ app, router }) => {
54
58
  })
55
59
  app.use(i18n)
56
60
 
57
- // for dev
58
- if (__DEV__) {
59
- import.meta.hot!.on('valaxy:pageHeaders', (payload) => {
60
- if (shouldHotReload(payload))
61
- router.currentRoute.value.meta.headers = payload.pageHeaders
61
+ router.isReady().then(() => {
62
+ handleHMR(router)
63
+ })
64
+ }
65
+
66
+ function handleHMR(router: Router): void {
67
+ // update route.data on HMR updates of active page
68
+ if (import.meta.hot) {
69
+ import.meta.hot!.on('valaxy:pageData', (payload: PageDataPayload) => {
70
+ if (shouldHotReload(payload)) {
71
+ // console.log(payload.pageData.headers)
72
+ Object.assign(router.currentRoute.value.meta, payload.pageData)
73
+ }
62
74
  })
63
75
  }
64
76
  }
package/client/shims.d.ts CHANGED
@@ -3,7 +3,7 @@ import 'vue-router'
3
3
  import type { Post } from 'valaxy'
4
4
  import type { Header } from '../node/markdown'
5
5
 
6
- // with vite-plugin-md, markdowns can be treat as Vue components
6
+ // markdowns can be treat as Vue components
7
7
  declare module '*.md' {
8
8
  import type { DefineComponent } from 'vue'
9
9
  const component: DefineComponent<{}, {}, any>
@@ -23,6 +23,11 @@ declare module '@valaxyjs/config' {
23
23
  export default config
24
24
  }
25
25
 
26
+ declare module '@valaxyjs/context' {
27
+ const ctx: string
28
+ export default ctx
29
+ }
30
+
26
31
  declare module '/@valaxyjs/locales' {
27
32
  const messages: {}
28
33
  export default messages