valaxy 0.28.0-beta.6 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,176 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import { useRoute } from 'vue-router'
4
+ import { useFrontmatter } from '../../composables/common'
5
+ import { useScreenSize } from '../../composables/helper/useScreenSize'
6
+ import { useLayout } from '../../composables/layout'
7
+ import { useSiteConfig, useThemeConfig } from '../../config'
8
+ import ValaxySvgLogo from '../ValaxySvgLogo.vue'
9
+
10
+ const show = ref(true)
11
+ const expanded = ref<Record<string, boolean>>({
12
+ breakpoints: true,
13
+ route: false,
14
+ frontmatter: false,
15
+ config: false,
16
+ })
17
+
18
+ function toggleSection(key: string) {
19
+ expanded.value[key] = !expanded.value[key]
20
+ }
21
+
22
+ // Breakpoints
23
+ const screenSize = useScreenSize()
24
+ const breakpoints = computed(() => [
25
+ { label: 'xs', value: screenSize.isXs.value },
26
+ { label: 'sm', value: screenSize.isSm.value },
27
+ { label: 'md', value: screenSize.isMd.value },
28
+ { label: 'lg', value: screenSize.isLg.value },
29
+ { label: 'xl', value: screenSize.isXl.value },
30
+ { label: '2xl', value: screenSize.is2xl.value },
31
+ ])
32
+
33
+ // Route
34
+ const route = useRoute()
35
+ const routeInfo = computed(() => ({
36
+ path: route.path,
37
+ name: route.name as string,
38
+ layout: route.meta?.layout || 'default',
39
+ query: Object.keys(route.query).length ? route.query : undefined,
40
+ params: Object.keys(route.params).length ? route.params : undefined,
41
+ }))
42
+
43
+ // Frontmatter
44
+ const frontmatter = useFrontmatter()
45
+ const fmSummary = computed(() => {
46
+ const fm = frontmatter.value
47
+ if (!fm || !Object.keys(fm).length)
48
+ return null
49
+ return fm
50
+ })
51
+
52
+ // Config
53
+ const siteConfig = useSiteConfig()
54
+ const themeConfig = useThemeConfig()
55
+ const layout = useLayout()
56
+
57
+ const configSummary = computed(() => ({
58
+ theme: siteConfig.value.lang ? undefined : undefined,
59
+ lang: siteConfig.value.lang,
60
+ title: siteConfig.value.title,
61
+ url: siteConfig.value.url,
62
+ layout: layout.value,
63
+ }))
64
+ </script>
65
+
66
+ <template>
67
+ <div
68
+ v-if="show"
69
+ class="valaxy-debug fixed bottom-4 left-2 z-9999 max-h-[80vh] w-72 overflow-y-auto rounded-lg bg-black/80 p-3 text-xs text-white shadow-lg backdrop-blur-sm"
70
+ @click.stop
71
+ >
72
+ <!-- Header -->
73
+ <div class="mb-2 flex items-center justify-between border-b border-white/20 pb-2">
74
+ <span class="flex items-center gap-1 font-bold text-cyan-400">
75
+ <ValaxySvgLogo class="size-4" />
76
+ Valaxy Debug
77
+ </span>
78
+ <button
79
+ class="rounded px-1 text-white/60 transition hover:bg-white/20 hover:text-white"
80
+ title="Close"
81
+ @click="show = false"
82
+ >
83
+
84
+ </button>
85
+ </div>
86
+
87
+ <!-- Breakpoints -->
88
+ <div class="mb-1">
89
+ <button
90
+ class="w-full rounded px-1 py-0.5 text-left font-bold text-emerald-400 transition hover:bg-white/10"
91
+ @click="toggleSection('breakpoints')"
92
+ >
93
+ {{ expanded.breakpoints ? '▾' : '▸' }} Breakpoints
94
+ </button>
95
+ <div v-if="expanded.breakpoints" class="mt-1 flex flex-wrap gap-1 pl-3">
96
+ <span
97
+ v-for="bp in breakpoints"
98
+ :key="bp.label"
99
+ class="rounded px-1.5 py-0.5"
100
+ :class="bp.value ? 'bg-emerald-500/30 text-emerald-300' : 'bg-white/5 text-white/40'"
101
+ >
102
+ {{ bp.label }}
103
+ </span>
104
+ </div>
105
+ </div>
106
+
107
+ <!-- Route -->
108
+ <div class="mb-1">
109
+ <button
110
+ class="w-full rounded px-1 py-0.5 text-left font-bold text-blue-400 transition hover:bg-white/10"
111
+ @click="toggleSection('route')"
112
+ >
113
+ {{ expanded.route ? '▾' : '▸' }} Route
114
+ </button>
115
+ <div v-if="expanded.route" class="mt-1 space-y-0.5 pl-3">
116
+ <div><span class="text-white/50">path:</span> {{ routeInfo.path }}</div>
117
+ <div><span class="text-white/50">name:</span> {{ routeInfo.name }}</div>
118
+ <div><span class="text-white/50">layout:</span> {{ routeInfo.layout }}</div>
119
+ <div v-if="routeInfo.query">
120
+ <span class="text-white/50">query:</span> {{ JSON.stringify(routeInfo.query) }}
121
+ </div>
122
+ <div v-if="routeInfo.params">
123
+ <span class="text-white/50">params:</span> {{ JSON.stringify(routeInfo.params) }}
124
+ </div>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- Frontmatter -->
129
+ <div class="mb-1">
130
+ <button
131
+ class="w-full rounded px-1 py-0.5 text-left font-bold text-amber-400 transition hover:bg-white/10"
132
+ @click="toggleSection('frontmatter')"
133
+ >
134
+ {{ expanded.frontmatter ? '▾' : '▸' }} Frontmatter
135
+ </button>
136
+ <div v-if="expanded.frontmatter" class="mt-1 pl-3">
137
+ <pre v-if="fmSummary" class="max-h-48 overflow-auto whitespace-pre-wrap break-all rounded bg-white/5 p-1.5 text-white/80">{{ JSON.stringify(fmSummary, null, 2) }}</pre>
138
+ <span v-else class="text-white/40">No frontmatter</span>
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Config -->
143
+ <div class="mb-1">
144
+ <button
145
+ class="w-full rounded px-1 py-0.5 text-left font-bold text-purple-400 transition hover:bg-white/10"
146
+ @click="toggleSection('config')"
147
+ >
148
+ {{ expanded.config ? '▾' : '▸' }} Config
149
+ </button>
150
+ <div v-if="expanded.config" class="mt-1 space-y-2 pl-3">
151
+ <div>
152
+ <div class="mb-0.5 text-white/50">
153
+ Site Config
154
+ </div>
155
+ <pre class="max-h-48 overflow-auto whitespace-pre-wrap break-all rounded bg-white/5 p-1.5 text-white/80">{{ JSON.stringify(configSummary, null, 2) }}</pre>
156
+ </div>
157
+ <div>
158
+ <div class="mb-0.5 text-white/50">
159
+ Theme Config
160
+ </div>
161
+ <pre class="max-h-48 overflow-auto whitespace-pre-wrap break-all rounded bg-white/5 p-1.5 text-white/80">{{ JSON.stringify(themeConfig, null, 2) }}</pre>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Collapsed toggle button -->
168
+ <button
169
+ v-if="!show"
170
+ class="fixed bottom-4 left-2 z-9999 rounded-lg bg-black/60 px-2 py-1 text-xs text-white/60 shadow-lg backdrop-blur-sm transition hover:bg-black/80 hover:text-white"
171
+ title="Open Valaxy Debug"
172
+ @click="show = true"
173
+ >
174
+ <ValaxySvgLogo class="size-4" />
175
+ </button>
176
+ </template>
@@ -0,0 +1,53 @@
1
+ <script lang="ts" setup>
2
+ import { useData } from '../composables'
3
+
4
+ const { page } = useData()
5
+
6
+ const isDev = import.meta.env.DEV
7
+
8
+ /**
9
+ * Open the current page source file in editor (dev mode only)
10
+ * Uses Vite's built-in `/__open-in-editor` endpoint
11
+ */
12
+ function openInEditor() {
13
+ const filePath = page.value?.filePath
14
+ if (filePath) {
15
+ fetch(`${window.location.origin}/__open-in-editor?file=${encodeURIComponent(filePath)}`)
16
+ .catch((err) => {
17
+ console.error('[valaxy] Failed to open in editor:', err)
18
+ })
19
+ }
20
+ }
21
+ </script>
22
+
23
+ <template>
24
+ <button
25
+ v-if="isDev"
26
+ class="valaxy-open-in-editor"
27
+ title="Open in Editor"
28
+ @click="openInEditor"
29
+ >
30
+ <slot>
31
+ <div i-ri-code-s-slash-line />
32
+ </slot>
33
+ </button>
34
+ </template>
35
+
36
+ <style>
37
+ .valaxy-open-in-editor {
38
+ display: inline-flex;
39
+ align-items: center;
40
+ cursor: pointer;
41
+ opacity: 0.4;
42
+ transition: opacity 0.2s;
43
+ background: none;
44
+ border: none;
45
+ padding: 0;
46
+ color: inherit;
47
+ font-size: inherit;
48
+ }
49
+
50
+ .valaxy-open-in-editor:hover {
51
+ opacity: 1;
52
+ }
53
+ </style>
@@ -1,12 +1,8 @@
1
1
  import { definePerson, defineWebPage, defineWebSite, useSchemaOrg } from '@unhead/schema-org/vue'
2
2
  import { useSeoMeta } from '@unhead/vue'
3
-
4
- // TODO: add docs to override ValaxyApp
5
3
  import { computed } from 'vue'
6
4
  import { useI18n } from 'vue-i18n'
7
-
8
5
  import { useRoute } from 'vue-router'
9
-
10
6
  import { useFrontmatter, useLocale, useValaxyHead, useValaxyI18n } from '../../composables'
11
7
  import { useTimezone } from '../../composables/global'
12
8
  // https://github.com/vueuse/head
@@ -16,7 +12,6 @@ import { useSiteConfig } from '../../config'
16
12
 
17
13
  export function useValaxyApp() {
18
14
  const siteConfig = useSiteConfig()
19
- // todo, allow user config
20
15
  const fm = useFrontmatter()
21
16
 
22
17
  const { locale } = useI18n()
@@ -32,7 +27,6 @@ export function useValaxyApp() {
32
27
  }
33
28
 
34
29
  // seo
35
- // todo: get first image url from markdown
36
30
  const siteUrl = computed(() => fm.value.url || siteConfig.value.url)
37
31
  const description = computed(() => $tO(fm.value.excerpt) || $tO(fm.value.description) || $t(siteConfig.value.description))
38
32
 
@@ -43,7 +37,7 @@ export function useValaxyApp() {
43
37
  ogLocaleAlternate: computed(() => siteConfig.value.languages.filter(l => l !== locale.value)),
44
38
  ogSiteName: computed(() => $t(siteConfig.value.title)),
45
39
  ogTitle: computed(() => $tO(fm.value.title) || $t(siteConfig.value.title)),
46
- ogImage: computed(() => fm.value.ogImage || fm.value.cover || siteConfig.value.favicon),
40
+ ogImage: computed(() => fm.value.ogImage || fm.value.cover || fm.value.firstImage || siteConfig.value.favicon),
47
41
  ogType: 'website',
48
42
  ogUrl: siteUrl,
49
43
  })
@@ -1,47 +1,11 @@
1
1
  import type { MaybeRef } from 'vue'
2
2
  import type { Post } from '../../types'
3
+ import type { CategoryList } from './category-utils'
3
4
  import { computed, unref } from 'vue'
4
5
  import { useSiteStore } from '../stores'
5
6
 
6
- /**
7
- * 基础分类
8
- */
9
- export interface BaseCategory {
10
- /**
11
- * 分类下的文章数量
12
- */
13
- total: number
14
- }
15
-
16
- /**
17
- * @en
18
- * Category list
19
- *
20
- * @zh
21
- * 分类列表
22
- */
23
- export interface CategoryList {
24
- /**
25
- * category name
26
- */
27
- name: string
28
- /**
29
- * total posts
30
- */
31
- total: number
32
- children: Map<string, Post | CategoryList>
33
- }
34
- export type Category = CategoryList
35
- export type Categories = Map<string, Post | CategoryList>
36
-
37
- /**
38
- * For theme development, you can use this function to determine whether the category is a category list.
39
- * @todo write unit test
40
- * @param category
41
- */
42
- export function isCategoryList(category: any): category is CategoryList {
43
- return category.children
44
- }
7
+ export type { BaseCategory, Categories, Category, CategoryList } from './category-utils'
8
+ export { isCategoryList, removeItemFromCategory } from './category-utils'
45
9
 
46
10
  /**
47
11
  * get categories from posts
@@ -158,20 +122,3 @@ export function useCategories(category?: MaybeRef<string>, posts: Post[] = []) {
158
122
  }
159
123
  })
160
124
  }
161
-
162
- /**
163
- * remove item from category
164
- * @param categoryList
165
- * @param categoryName
166
- */
167
- export function removeItemFromCategory(categoryList: CategoryList, categoryName: string) {
168
- if (isCategoryList(categoryList)) {
169
- const categoryArr = categoryName.split('/')
170
- categoryList.children.delete(categoryArr[0])
171
- }
172
- }
173
-
174
- /**
175
- * @deprecated use `useCategories` instead
176
- */
177
- export const useCategory = useCategories
@@ -0,0 +1,50 @@
1
+ import type { Post } from '../../types'
2
+
3
+ /**
4
+ * 基础分类
5
+ */
6
+ export interface BaseCategory {
7
+ /**
8
+ * 分类下的文章数量
9
+ */
10
+ total: number
11
+ }
12
+
13
+ /**
14
+ * @en
15
+ * Category list
16
+ *
17
+ * @zh
18
+ * 分类列表
19
+ */
20
+ export interface CategoryList {
21
+ /**
22
+ * category name
23
+ */
24
+ name: string
25
+ /**
26
+ * total posts
27
+ */
28
+ total: number
29
+ children: Map<string, Post | CategoryList>
30
+ }
31
+ export type Category = CategoryList
32
+ export type Categories = Map<string, Post | CategoryList>
33
+
34
+ /**
35
+ * For theme development, you can use this function to determine whether the category is a category list.
36
+ * @param category
37
+ */
38
+ export function isCategoryList(category: unknown): category is CategoryList {
39
+ return !!category && typeof category === 'object' && 'children' in category && category.children instanceof Map
40
+ }
41
+
42
+ /**
43
+ * remove item from category
44
+ * @param categoryList
45
+ * @param categoryName
46
+ */
47
+ export function removeItemFromCategory(categoryList: CategoryList, categoryName: string) {
48
+ const categoryArr = categoryName.split('/')
49
+ categoryList.children.delete(categoryArr[0])
50
+ }
@@ -4,6 +4,7 @@ export * from './app'
4
4
  export * from './back'
5
5
  // for classify
6
6
  export * from './categories'
7
+ export * from './category-utils'
7
8
  export * from './collections'
8
9
 
9
10
  // common
@@ -1,12 +1,12 @@
1
1
  import type { Ref } from 'vue'
2
+ import type { TaxonomyNamespace } from '../../shared/utils/i18n'
2
3
  import { isClient, useStorage } from '@vueuse/core'
3
4
  import dayjs from 'dayjs'
4
- import { computed } from 'vue'
5
5
 
6
+ import { computed, watch } from 'vue'
6
7
  // not optimize deps all locales
7
8
  import { useI18n } from 'vue-i18n'
8
- import { tObject } from '../../shared/utils/i18n'
9
- import { LOCALE_PREFIX } from '../utils'
9
+ import { isLocaleKey, resolveTaxonomyLocaleKey, stripLocalePrefix, tObject } from '../../shared/utils/i18n'
10
10
  import 'dayjs/locale/en'
11
11
  import 'dayjs/locale/zh-cn'
12
12
 
@@ -63,13 +63,16 @@ export function useLocale() {
63
63
  export function useLocaleTitle(fm: Ref<{
64
64
  title?: string | Record<string, string>
65
65
  } | null>) {
66
- const { locale } = useI18n()
66
+ const { t, locale } = useI18n()
67
67
  return computed(() => {
68
68
  if (!fm.value)
69
69
  return ''
70
70
 
71
71
  const lang = locale.value
72
- return tObject(fm.value.title || '', lang) || ''
72
+ const title = tObject(fm.value.title || '', lang) || ''
73
+ if (typeof title === 'string' && isLocaleKey(title))
74
+ return t(stripLocalePrefix(title))
75
+ return title
73
76
  })
74
77
  }
75
78
 
@@ -79,16 +82,19 @@ export function useLocaleTitle(fm: Ref<{
79
82
  * 会从 locales/ 目录中获取对应的翻译
80
83
  */
81
84
  export function useValaxyI18n() {
82
- const { t, locale } = useI18n()
85
+ const { t, te, locale } = useI18n()
86
+ const termCache = new Map<string, string>()
87
+
88
+ // Clear cache on locale switches so each composable instance stays bounded.
89
+ watch(locale, () => termCache.clear())
83
90
 
84
91
  /**
85
92
  * translate `$locale:key`
86
93
  * @param key
87
94
  */
88
95
  const $t = (key: string) => {
89
- if (key.startsWith(LOCALE_PREFIX)) {
90
- return t(key.slice(LOCALE_PREFIX.length))
91
- }
96
+ if (isLocaleKey(key))
97
+ return t(stripLocalePrefix(key))
92
98
  return key
93
99
  }
94
100
 
@@ -104,6 +110,47 @@ export function useValaxyI18n() {
104
110
  return tObject(data || '', locale.value)
105
111
  }
106
112
 
113
+ /**
114
+ * @en
115
+ * Translate a taxonomy term.
116
+ *
117
+ * Resolution order:
118
+ * 1. `$locale:` prefix → strip and translate via `t()`
119
+ * 2. Locale key `{namespace}.{key}` exists → translate via `t()`
120
+ * 3. Fallback → return the original key as-is
121
+ *
122
+ * The result is cached by `locale + namespace + key` to avoid repeated
123
+ * `te()` / `t()` lookups in tag clouds and category trees.
124
+ *
125
+ * @zh
126
+ * 翻译 taxonomy 术语。
127
+ *
128
+ * 解析顺序:
129
+ * 1. `$locale:` 前缀 → 去掉前缀后通过 `t()` 翻译
130
+ * 2. locale 中存在 `{namespace}.{key}` → 通过 `t()` 翻译
131
+ * 3. 兜底 → 原样返回
132
+ *
133
+ * 结果会按 `locale + namespace + key` 做轻量缓存,
134
+ * 避免标签云和分类树中重复执行 `te()` / `t()`。
135
+ */
136
+ const $tTerm = (namespace: TaxonomyNamespace, key: string) => {
137
+ const cacheKey = `${locale.value}:${namespace}:${key}`
138
+ const cached = termCache.get(cacheKey)
139
+ if (cached !== undefined)
140
+ return cached
141
+
142
+ const { localeKey, isExplicitLocaleKey } = resolveTaxonomyLocaleKey(namespace, key)
143
+ const result = isExplicitLocaleKey || te(localeKey)
144
+ ? `${t(localeKey)}`
145
+ : key
146
+
147
+ termCache.set(cacheKey, result)
148
+ return result
149
+ }
150
+
151
+ const $tTag = (key: string) => $tTerm('tag', key)
152
+ const $tCategory = (key: string) => $tTerm('category', key)
153
+
107
154
  return {
108
155
  locale,
109
156
  /**
@@ -111,5 +158,17 @@ export function useValaxyI18n() {
111
158
  */
112
159
  $t,
113
160
  $tO,
161
+ /**
162
+ * translate taxonomy term (auto-lookup `{namespace}.{key}` in locale files)
163
+ */
164
+ $tTerm,
165
+ /**
166
+ * translate tag name (auto-lookup `tag.{key}` in locale files)
167
+ */
168
+ $tTag,
169
+ /**
170
+ * translate category name (auto-lookup `category.{key}` in locale files)
171
+ */
172
+ $tCategory,
114
173
  }
115
174
  }
@@ -49,7 +49,7 @@ export function useActiveAnchor(
49
49
 
50
50
  /**
51
51
  * 长目录自动滚动
52
- * @TODO add e2e test
52
+ * @see e2e/theme-yun/outline.spec.ts
53
53
  */
54
54
  const checkActiveLinkInViewport = () => {
55
55
  const activeLink = prevActiveLink
@@ -6,15 +6,18 @@ import { orderByMeta, useSiteConfig } from 'valaxy'
6
6
  import { computed } from 'vue'
7
7
  import { useI18n } from 'vue-i18n'
8
8
  import { useRouterStore } from '../../stores'
9
- import { tObject } from '../../utils'
9
+ import { isLocaleKey, stripLocalePrefix, tObject } from '../../utils'
10
10
 
11
11
  export * from './usePagination'
12
12
  export * from './usePrevNext'
13
13
 
14
14
  export function usePostTitle(post: ComputedRef<Post>) {
15
- const { locale } = useI18n()
15
+ const { t, locale } = useI18n()
16
16
  return computed(() => {
17
- return tObject(post.value.title || '', locale.value)
17
+ const title = tObject(post.value.title || '', locale.value)
18
+ if (typeof title === 'string' && isLocaleKey(title))
19
+ return t(stripLocalePrefix(title))
20
+ return title
18
21
  })
19
22
  }
20
23
 
@@ -51,8 +51,3 @@ export function useTags() {
51
51
  return tagMap
52
52
  })
53
53
  }
54
-
55
- /**
56
- * @deprecated use `useTags` instead
57
- */
58
- export const useTag = useTags
@@ -1,5 +1,6 @@
1
1
  import type { ValaxySSGContext } from '../setups'
2
2
 
3
+ import { defineAsyncComponent } from 'vue'
3
4
  import AppLink from '../components/AppLink.vue'
4
5
  import ValaxyTranslate from '../components/builtin/ValaxyTranslate.vue'
5
6
 
@@ -10,4 +11,10 @@ import ValaxyTranslate from '../components/builtin/ValaxyTranslate.vue'
10
11
  export function registerGlobalComponents(ctx: ValaxySSGContext) {
11
12
  ctx.app.component('AppLink', AppLink)
12
13
  ctx.app.component('VT', ValaxyTranslate)
14
+
15
+ // DEV-only: register ValaxyDebug component (tree-shaken in production)
16
+ if (import.meta.env.DEV) {
17
+ const ValaxyDebug = defineAsyncComponent(() => import('../components/.exclude/ValaxyDebug.vue'))
18
+ ctx.app.component('ValaxyDebug', ValaxyDebug)
19
+ }
13
20
  }
@@ -14,6 +14,7 @@ import type { PageDataPayload } from '../../types'
14
14
  import type { ValaxySSGContext } from '../setups'
15
15
  import { ensureSuffix } from '@antfu/utils'
16
16
  import { useStorage } from '@vueuse/core'
17
+ import { nextTick, watch } from 'vue'
17
18
  import { createI18n } from 'vue-i18n'
18
19
 
19
20
  // @ts-expect-error virtual
@@ -52,11 +53,35 @@ export const i18n = createI18n({
52
53
  })
53
54
 
54
55
  export async function install({ app, router }: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
55
- const locale = useStorage('valaxy-locale', config?.value.siteConfig.lang || 'en')
56
- i18n.global.locale.value = locale.value
56
+ const defaultLang = config?.value.siteConfig.lang || 'en'
57
+
58
+ // During SSR/SSG build **and** the initial client hydration pass we must
59
+ // keep the locale at `defaultLang` so that the rendered HTML matches on
60
+ // both sides — no hydration mismatch for any i18n-dependent attribute
61
+ // (title, class, text content, etc.).
62
+ //
63
+ // The stored user preference is restored **after** hydration is complete
64
+ // (router.isReady + nextTick) so Vue can patch the DOM normally.
65
+ i18n.global.locale.value = defaultLang
57
66
 
58
67
  app.use(i18n)
59
- router.isReady().then(() => {
68
+
69
+ router.isReady().then(async () => {
70
+ // Wait for the hydration to finish before restoring the stored locale.
71
+ await nextTick()
72
+
73
+ const storedLocale = useStorage('valaxy-locale', defaultLang)
74
+
75
+ // Apply the stored locale (if different from default)
76
+ if (storedLocale.value && storedLocale.value !== i18n.global.locale.value)
77
+ i18n.global.locale.value = storedLocale.value
78
+
79
+ // Keep i18n locale in sync when the stored value changes later
80
+ watch(storedLocale, (val) => {
81
+ if (val)
82
+ i18n.global.locale.value = val
83
+ })
84
+
60
85
  handleHMR(router)
61
86
  })
62
87
  }
@@ -1,7 +1,7 @@
1
1
  import 'node:process';
2
2
  import 'yargs';
3
3
  import 'yargs/helpers';
4
- export { c as cli, I as registerDevCommand, W as run, Z as startValaxyDev } from '../../shared/valaxy.BVsZMcdc.mjs';
4
+ export { c as cli, I as registerDevCommand, W as run, Z as startValaxyDev } from '../../shared/valaxy.DQ6HsU2J.mjs';
5
5
  import 'node:os';
6
6
  import 'node:path';
7
7
  import 'consola';