valaxy 0.27.0 → 0.28.0-beta.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 (49) hide show
  1. package/client/components/AppLink.vue +4 -0
  2. package/client/components/ClientOnly.ts +12 -0
  3. package/client/components/ValaxyDynamicComponent.vue +22 -14
  4. package/client/components/ValaxyLogo.vue +1 -1
  5. package/client/components/ValaxyOverlay.vue +1 -1
  6. package/client/composables/codeGroups.ts +3 -3
  7. package/client/composables/collections.ts +101 -2
  8. package/client/composables/common.ts +6 -5
  9. package/client/composables/dark.ts +1 -1
  10. package/client/composables/features/collapse-code.ts +1 -1
  11. package/client/composables/features/copy-markdown.ts +85 -0
  12. package/client/composables/features/index.ts +1 -0
  13. package/client/composables/outline/anchor.ts +1 -1
  14. package/client/composables/post/index.ts +104 -2
  15. package/client/config.ts +4 -2
  16. package/client/entry-ssr.ts +25 -0
  17. package/client/layouts/README.md +1 -1
  18. package/client/locales/en.yml +12 -0
  19. package/client/locales/zh-CN.yml +12 -0
  20. package/client/main.ts +122 -24
  21. package/client/modules/components.ts +2 -2
  22. package/client/modules/floating-vue.ts +2 -3
  23. package/client/modules/valaxy.ts +2 -3
  24. package/client/setup/main.ts +2 -3
  25. package/client/setups.ts +24 -3
  26. package/client/styles/common/code.scss +8 -8
  27. package/client/styles/common/hamburger.scss +2 -2
  28. package/client/styles/common/markdown.scss +2 -2
  29. package/client/styles/common/transition.scss +2 -2
  30. package/client/styles/components/code-group.scss +2 -2
  31. package/client/styles/components/custom-block.scss +1 -1
  32. package/client/styles/css-vars.scss +7 -6
  33. package/client/styles/vars.scss +4 -3
  34. package/client/tsconfig.json +1 -1
  35. package/client/types/collection.ts +27 -0
  36. package/client/types/index.ts +2 -2
  37. package/client/utils/time.ts +1 -1
  38. package/dist/node/cli/index.mjs +14 -9
  39. package/dist/node/index.d.mts +216 -11
  40. package/dist/node/index.mjs +19 -10
  41. package/dist/shared/{valaxy.DpV6HHc6.mjs → valaxy.DXqMwOZX.mjs} +4034 -2514
  42. package/dist/shared/{valaxy._i636HSR.d.mts → valaxy.JIuR8V4d.d.mts} +111 -2
  43. package/dist/types/index.d.mts +2 -2
  44. package/index.d.ts +1 -0
  45. package/package.json +36 -36
  46. package/shared/node/i18n.ts +15 -1
  47. package/types/config.ts +74 -0
  48. package/types/frontmatter/page.ts +6 -1
  49. package/types/posts.ts +9 -1
package/client/main.ts CHANGED
@@ -1,12 +1,15 @@
1
+ import type { ValaxySSGContext } from './setups'
2
+
1
3
  import { dataSymbol, initValaxyConfig, valaxyConfigSymbol } from 'valaxy'
2
4
  import { setupLayouts } from 'virtual:generated-layouts'
3
- import { ViteSSG } from 'vite-ssg'
5
+ import { createSSRApp, createApp as vueCreateApp } from 'vue'
6
+ import { createMemoryHistory, createRouter, createWebHistory } from 'vue-router'
4
7
  import { routes as autoRoutes } from 'vue-router/auto-routes'
5
8
 
6
9
  // import App from '/@valaxyjs/App.vue'
7
10
  import App from './App.vue'
8
-
9
11
  import { initData } from './app/data'
12
+ import ClientOnly from './components/ClientOnly'
10
13
 
11
14
  import setupMain from './setup/main'
12
15
  import { setupValaxyDevTools } from './utils/dev'
@@ -32,6 +35,45 @@ routes.forEach((i) => {
32
35
  })
33
36
  })
34
37
 
38
+ /**
39
+ * Flatten nested routes so that `setupLayouts` can wrap every leaf route.
40
+ *
41
+ * `vue-router/vite` (file-based routing) generates nested route trees for subdirectories
42
+ * (e.g. `pages/zh/guide/foo.md` → `{ path: '/zh', children: [{ path: 'guide', children: … }] }`).
43
+ * `vite-plugin-vue-layouts-next`' `setupLayouts` only wraps **top-level** routes,
44
+ * so nested children would miss the layout wrapper entirely.
45
+ *
46
+ * This function extracts every leaf (component-bearing or redirect) route into a flat
47
+ * top-level entry with a fully-resolved path, preserving its `meta`.
48
+ */
49
+ function flattenRoutes(routes: any[]): any[] {
50
+ const flat: any[] = []
51
+ function walk(route: any, parentPath: string) {
52
+ const fullPath = parentPath
53
+ ? `${parentPath.replace(/\/$/, '')}/${route.path.replace(/^\//, '')}`
54
+ : route.path
55
+
56
+ if (route.component || route.components || route.redirect) {
57
+ flat.push({
58
+ ...route,
59
+ path: fullPath,
60
+ children: undefined,
61
+ })
62
+ }
63
+
64
+ if (route.children) {
65
+ for (const child of route.children) {
66
+ walk(child, fullPath)
67
+ }
68
+ }
69
+ }
70
+
71
+ for (const route of routes) {
72
+ walk(route, '')
73
+ }
74
+ return flat
75
+ }
76
+
35
77
  // filter children recursive
36
78
  function filterDraft(routes: any[]) {
37
79
  return routes.filter((i) => {
@@ -43,39 +85,95 @@ function filterDraft(routes: any[]) {
43
85
  }
44
86
 
45
87
  // not filter hide for ssg
46
- const routesWithLayout = setupLayouts(import.meta.env.DEV
47
- ? routes
48
- : filterDraft(routes),
88
+ export const routesWithLayout = setupLayouts(import.meta.env.DEV
89
+ ? flattenRoutes(routes)
90
+ : filterDraft(flattenRoutes(routes)),
49
91
  )
50
92
 
51
93
  if (import.meta.env.DEV)
52
94
  setupValaxyDevTools()
53
95
 
54
- // https://github.com/antfu/vite-ssg
55
- export const createApp = ViteSSG(
56
- App,
57
- {
96
+ interface CreateValaxyAppOptions {
97
+ routePath?: string
98
+ createHead?: () => any
99
+ hydrate?: boolean
100
+ }
101
+
102
+ export function createValaxyApp(options: CreateValaxyAppOptions = {}): ValaxySSGContext {
103
+ const { routePath, createHead, hydrate } = options
104
+ const isSSR = import.meta.env.SSR
105
+
106
+ // Use createSSRApp for server-side rendering and for client-side hydration
107
+ // of SSG pre-rendered pages. vueCreateApp is only for pure SPA mode.
108
+ const app = (isSSR || hydrate) ? createSSRApp(App) : vueCreateApp(App)
109
+
110
+ const history = isSSR
111
+ ? createMemoryHistory(import.meta.env.BASE_URL)
112
+ : createWebHistory(import.meta.env.BASE_URL)
113
+
114
+ const router = createRouter({
115
+ history,
58
116
  routes: routesWithLayout,
59
- base: import.meta.env.BASE_URL,
60
117
  scrollBehavior(to, from) {
61
118
  if (to.path !== from.path)
62
119
  return { top: 0 }
63
120
  },
64
- },
65
- (ctx) => {
66
- // app-level provide
67
- const { app, router } = ctx
121
+ })
68
122
 
69
- const data = initData(router)
70
- app.provide(dataSymbol, data)
123
+ const head = createHead ? createHead() : undefined
71
124
 
72
- // Note: DataLoaderPlugin is not compatible with vite-ssg because vite-ssg
73
- // calls this callback BEFORE app.use(router), but DataLoaderPlugin requires
74
- // the router to be installed first for useRouter()/useRoute() to work.
75
- // The page data is loaded via route.meta.frontmatter instead.
125
+ app.use(router)
126
+ if (head)
127
+ app.use(head)
128
+ app.component('ClientOnly', ClientOnly)
76
129
 
77
- app.provide(valaxyConfigSymbol, valaxyConfig)
130
+ // SSR render callback management
131
+ const appRenderCallbacks: (() => void)[] = []
78
132
 
79
- setupMain(ctx, valaxyConfig)
80
- },
81
- )
133
+ const ctx: ValaxySSGContext = {
134
+ app,
135
+ router,
136
+ head,
137
+ routes: routesWithLayout,
138
+ isClient: !isSSR,
139
+ initialState: {},
140
+ onSSRAppRendered: isSSR ? (cb: () => void) => appRenderCallbacks.push(cb) : () => {},
141
+ triggerOnSSRAppRendered: () => Promise.all(appRenderCallbacks.map(cb => cb())),
142
+ routePath,
143
+ }
144
+
145
+ // Restore serialised state on client
146
+ if (!isSSR && typeof window !== 'undefined' && (window as any).__INITIAL_STATE__) {
147
+ ctx.initialState = JSON.parse((window as any).__INITIAL_STATE__)
148
+ }
149
+
150
+ // app-level provide
151
+ const data = initData(router)
152
+ app.provide(dataSymbol, data)
153
+ app.provide(valaxyConfigSymbol, valaxyConfig)
154
+
155
+ setupMain(ctx, valaxyConfig)
156
+
157
+ return ctx
158
+ }
159
+
160
+ // Client-side auto-mount
161
+ if (!import.meta.env.SSR) {
162
+ ;(async () => {
163
+ const { createHead } = await import('@unhead/vue/client')
164
+ // Detect SSG pre-rendered content for proper hydration
165
+ const appEl = document.getElementById('app')
166
+ const hydrate = !!(appEl && appEl.innerHTML.trim())
167
+ const { app, router } = createValaxyApp({ createHead, hydrate })
168
+ await router.isReady()
169
+ app.mount('#app', hydrate)
170
+ })()
171
+ }
172
+
173
+ /**
174
+ * Legacy compatibility export for vite-ssg.
175
+ * vite-ssg expects `createApp(routePath)` returning `{ app, router, ... }`.
176
+ * This wraps `createValaxyApp` to match that signature so
177
+ * `--ssg-engine vite-ssg` continues to work.
178
+ */
179
+ export const createApp = (routePath?: string) => createValaxyApp({ routePath })
@@ -1,4 +1,4 @@
1
- import type { ViteSSGContext } from 'vite-ssg'
1
+ import type { ValaxySSGContext } from '../setups'
2
2
 
3
3
  import AppLink from '../components/AppLink.vue'
4
4
  import ValaxyTranslate from '../components/builtin/ValaxyTranslate.vue'
@@ -7,7 +7,7 @@ import ValaxyTranslate from '../components/builtin/ValaxyTranslate.vue'
7
7
  * register global components
8
8
  * @param ctx
9
9
  */
10
- export function registerGlobalComponents(ctx: ViteSSGContext) {
10
+ export function registerGlobalComponents(ctx: ValaxySSGContext) {
11
11
  ctx.app.component('AppLink', AppLink)
12
12
  ctx.app.component('VT', ValaxyTranslate)
13
13
  }
@@ -1,11 +1,10 @@
1
1
  import type { DefaultTheme, ValaxyConfig } from 'valaxy/types'
2
- import type { ViteSSGContext } from 'vite-ssg'
3
-
4
2
  import type { ComputedRef } from 'vue'
3
+ import type { ValaxySSGContext } from '../setups'
5
4
  import FloatingVue from 'floating-vue'
6
5
  import 'floating-vue/dist/style.css'
7
6
 
8
- export async function install({ app }: ViteSSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
7
+ export async function install({ app }: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
9
8
  // @see https://floating-vue.starpad.dev/guide/config#default-values
10
9
  const defaultFloatingVueConfig = {}
11
10
  app.use(FloatingVue, Object.assign(defaultFloatingVueConfig, config.value.siteConfig.floatingVue || {}))
@@ -8,11 +8,10 @@ import type { DefaultTheme, ValaxyConfig } from 'valaxy/types'
8
8
  */
9
9
  // import messages from '@intlify/unplugin-vue-i18n/messages'
10
10
 
11
- import type { ViteSSGContext } from 'vite-ssg'
12
-
13
11
  import type { ComputedRef } from 'vue'
14
12
  import type { Router } from 'vue-router'
15
13
  import type { PageDataPayload } from '../../types'
14
+ import type { ValaxySSGContext } from '../setups'
16
15
  import { ensureSuffix } from '@antfu/utils'
17
16
  import { useStorage } from '@vueuse/core'
18
17
  import { createI18n } from 'vue-i18n'
@@ -52,7 +51,7 @@ export const i18n = createI18n({
52
51
  missingWarn: false,
53
52
  })
54
53
 
55
- export async function install({ app, router }: ViteSSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
54
+ export async function install({ app, router }: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
56
55
  const locale = useStorage('valaxy-locale', config?.value.siteConfig.lang || 'en')
57
56
  i18n.global.locale.value = locale.value
58
57
 
@@ -4,9 +4,8 @@
4
4
  // https://github.com/microsoft/TypeScript/issues/42873
5
5
  import type { DefaultTheme, ValaxyConfig } from 'valaxy/types'
6
6
  /* __imports__ */
7
- import type { ViteSSGContext } from 'vite-ssg'
8
-
9
7
  import type { ComputedRef } from 'vue'
8
+ import type { ValaxySSGContext } from '../setups'
10
9
 
11
10
  import { consola } from 'consola'
12
11
  import { registerGlobalComponents } from '../modules/components'
@@ -16,7 +15,7 @@ import { install as installPinia } from '../modules/pinia'
16
15
  import { install as installUnhead } from '../modules/unhead'
17
16
  import { install as installValaxy } from '../modules/valaxy'
18
17
 
19
- export default function setupMain(ctx: ViteSSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
18
+ export default function setupMain(ctx: ValaxySSGContext, config: ComputedRef<ValaxyConfig<DefaultTheme.Config>>) {
20
19
  // @ts-expect-error inject in runtime
21
20
  // eslint-disable-next-line unused-imports/no-unused-vars
22
21
  const injection_arg = ctx
package/client/setups.ts CHANGED
@@ -1,16 +1,37 @@
1
1
  import type { Awaitable } from '@antfu/utils'
2
- import type { ViteSSGContext } from 'vite-ssg'
2
+ import type { App } from 'vue'
3
+ import type { Router, RouteRecordRaw } from 'vue-router'
3
4
  import type { MermaidOptions } from './types'
4
5
 
6
+ /**
7
+ * @en
8
+ * SSG context interface — property-compatible with ViteSSGContext so that
9
+ * downstream themes / addons only need to change their import path.
10
+ *
11
+ * @zh
12
+ * SSG 上下文接口 — 属性签名与 ViteSSGContext 完全兼容,下游主题/插件只需更改 import 路径。
13
+ */
14
+ export interface ValaxySSGContext {
15
+ app: App
16
+ router: Router
17
+ routes: RouteRecordRaw[]
18
+ head: any
19
+ isClient: boolean
20
+ initialState: Record<string, any>
21
+ onSSRAppRendered: (cb: () => void) => void
22
+ triggerOnSSRAppRendered: () => Promise<unknown[]>
23
+ routePath?: string
24
+ }
25
+
5
26
  /**
6
27
  * @see https://github.com/antfu-collective/vite-ssg
7
28
  * @en
8
29
  * The context object for the application setup function.
9
30
  *
10
31
  * @zh
11
- * 应用 setup 函数的上下文对象。(包括了 `ViteSSGContext`)
32
+ * 应用 setup 函数的上下文对象。
12
33
  */
13
- export type AppContext = ViteSSGContext
34
+ export type AppContext = ValaxySSGContext
14
35
 
15
36
  export type AppSetup = (ctx: AppContext) => Awaitable<void>
16
37
 
@@ -80,12 +80,12 @@ html:not(.dark) .vp-code-dark {
80
80
  line-height: var(--va-code-line-height);
81
81
  font-size: var(--va-code-font-size);
82
82
  color: var(--va-code-block-color);
83
- transition: color 0.5s;
83
+ transition: color var(--va-transition-duration-moderate);
84
84
  }
85
85
 
86
86
  [class*='language-'] code .highlighted {
87
87
  background-color: var(--va-code-line-highlight-color);
88
- transition: background-color 0.5s;
88
+ transition: background-color var(--va-transition-duration-moderate);
89
89
  margin: 0 -24px;
90
90
  padding: 0 24px;
91
91
  width: calc(100% + 2 * 24px);
@@ -122,9 +122,9 @@ html:not(.dark) .vp-code-dark {
122
122
  background-size: 20px;
123
123
  background-repeat: no-repeat;
124
124
  transition:
125
- border-color 0.25s,
126
- background-color 0.25s,
127
- opacity 0.25s;
125
+ border-color var(--va-transition-duration),
126
+ background-color var(--va-transition-duration),
127
+ opacity var(--va-transition-duration);
128
128
  }
129
129
 
130
130
  [class*='language-']:hover>button.copy,
@@ -184,8 +184,8 @@ html:not(.dark) .vp-code-dark {
184
184
  font-weight: 500;
185
185
  color: var(--va-code-lang-color);
186
186
  transition:
187
- color 0.4s,
188
- opacity 0.4s;
187
+ color var(--va-transition-duration-moderate),
188
+ opacity var(--va-transition-duration-moderate);
189
189
  }
190
190
 
191
191
  [class*='language-']:hover>button.copy+span.lang,
@@ -195,7 +195,7 @@ html:not(.dark) .vp-code-dark {
195
195
 
196
196
  // diff
197
197
  [class*='language-'] code .diff {
198
- transition: background-color 0.5s;
198
+ transition: background-color var(--va-transition-duration-moderate);
199
199
  margin: 0 -24px;
200
200
  padding: 0 24px;
201
201
  width: calc(100% + 2 * 24px);
@@ -22,7 +22,7 @@ $hamburger-size: 22px;
22
22
  .vt-hamburger.is-active:hover .vt-hamburger-middle,
23
23
  .vt-hamburger.is-active:hover .vt-hamburger-bottom {
24
24
  background-color: var(--va-c-primary);
25
- transition: top .25s, background-color .25s, transform .25s;
25
+ transition: top var(--va-transition-duration), background-color var(--va-transition-duration), transform var(--va-transition-duration);
26
26
  }
27
27
 
28
28
  .vt-hamburger-container {
@@ -40,7 +40,7 @@ $hamburger-size: 22px;
40
40
  width: $hamburger-size;
41
41
  height: 2px;
42
42
  background-color: var(--va-c-primary);
43
- transition: top .25s, background-color .5s, transform .25s;
43
+ transition: top var(--va-transition-duration), background-color var(--va-transition-duration-moderate), transform var(--va-transition-duration);
44
44
  }
45
45
 
46
46
  .vt-hamburger-top {
@@ -69,8 +69,8 @@
69
69
  opacity: 0;
70
70
  text-decoration: none;
71
71
  transition:
72
- color 0.25s,
73
- opacity 0.25s;
72
+ color var(--va-transition-duration),
73
+ opacity var(--va-transition-duration);
74
74
  }
75
75
 
76
76
  .header-anchor::before {
@@ -1,7 +1,7 @@
1
1
  .v {
2
2
  &-enter-active,
3
3
  &-leave-active {
4
- transition: opacity var(--va-transition-duration, 0.4s) ease;
4
+ transition: opacity var(--va-transition-duration-moderate, 0.4s) ease;
5
5
  }
6
6
 
7
7
  &-enter-from,
@@ -13,7 +13,7 @@
13
13
  .fade {
14
14
  &-enter-active,
15
15
  &-leave-active {
16
- transition: opacity var(--va-transition-duration, 0.4s) ease;
16
+ transition: opacity var(--va-transition-duration-moderate, 0.4s) ease;
17
17
  }
18
18
 
19
19
  &-enter-from,
@@ -38,7 +38,7 @@
38
38
  color: var(--va-code-tab-text-color);
39
39
  white-space: nowrap;
40
40
  cursor: pointer;
41
- transition: color 0.25s;
41
+ transition: color var(--va-transition-duration);
42
42
  }
43
43
 
44
44
  .vp-code-group .tabs label::after {
@@ -51,7 +51,7 @@
51
51
  border-radius: 2px;
52
52
  content: '';
53
53
  background-color: transparent;
54
- transition: background-color 0.25s;
54
+ transition: background-color var(--va-transition-duration);
55
55
  }
56
56
 
57
57
  .vp-code-group label:hover {
@@ -172,7 +172,7 @@
172
172
  color: inherit;
173
173
  font-weight: 600;
174
174
  text-underline-offset: 2px;
175
- transition: opacity 0.25s;
175
+ transition: opacity var(--va-transition-duration);
176
176
  }
177
177
 
178
178
  .custom-block a:hover {
@@ -5,11 +5,8 @@
5
5
 
6
6
  $c-primary: #0078e7 !default;
7
7
 
8
- @use "./palette" with (
9
- $colors: (
10
- "primary": $c-primary,
11
- )
12
- );
8
+ @use "./palette" with ($colors: ("primary": $c-primary,
9
+ ));
13
10
  @use './css-vars/borders.css' as *;
14
11
  @use './css-vars/palette.css' as *;
15
12
  @use './css-vars/function.css' as *;
@@ -75,6 +72,8 @@ html.dark {
75
72
  --va-c-bg-soft: #f9f9f9;
76
73
  --va-c-bg-alt: #f9f9f9;
77
74
  --va-c-bg-mute: #f1f1f1;
75
+
76
+ --va-c-bg-elevated: #f9f9f9;
78
77
  }
79
78
 
80
79
  html.dark {
@@ -85,6 +84,8 @@ html.dark {
85
84
  --va-c-bg-alt: #161618;
86
85
  --va-c-bg-soft: #202127;
87
86
  --va-c-bg-mute: #2f2f2f;
87
+
88
+ --va-c-bg-elevated: #202127;
88
89
  }
89
90
 
90
91
  /* code */
@@ -144,4 +145,4 @@ html.dark {
144
145
 
145
146
  :root {
146
147
  --va-header-anchor-symbol: '#';
147
- }
148
+ }
@@ -28,10 +28,11 @@ $font: map.merge(
28
28
  $transition: () !default;
29
29
  $transition: map.merge(
30
30
  (
31
- "duration-fast": 0.2s,
32
- "duration": 0.4s,
31
+ "duration-fast": 0.15s,
32
+ "duration": 0.25s,
33
+ "duration-moderate": 0.4s,
33
34
  "duration-slow": 0.6s,
34
- "": all var(--va-transition-duration-fast) ease-in-out,
35
+ "": all var(--va-transition-duration) ease-in-out,
35
36
  ),
36
37
  $transition
37
38
  );
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "types": [
9
9
  "vite/client",
10
- "vite-plugin-vue-layouts/client",
10
+ "vite-plugin-vue-layouts-next/client",
11
11
  "@intlify/unplugin-vue-i18n/messages"
12
12
  ]
13
13
  },
@@ -20,6 +20,19 @@ export interface CollectionConfig {
20
20
  categories?: string[]
21
21
  tags?: string[]
22
22
 
23
+ /**
24
+ * @en Whether to show collection as a single collapsed entry in homepage/archive lists.
25
+ * When true (default), a synthetic collection card is added to the post list,
26
+ * representing all articles in the collection as one entry.
27
+ * When false, no synthetic entry is added.
28
+ * @zh 是否在首页/归档列表中以单个条目展示合集。
29
+ * 为 true(默认)时,合集以一张卡片出现在文章列表中,
30
+ * 代表合集内的所有文章。
31
+ * 为 false 时,不添加合集条目。
32
+ * @default true
33
+ */
34
+ collapse?: boolean
35
+
23
36
  /**
24
37
  * items
25
38
  */
@@ -31,5 +44,19 @@ export interface CollectionConfig {
31
44
  * 对应路径为 `/collections/${key}/${item.key}`
32
45
  */
33
46
  key?: string
47
+ /**
48
+ * @en
49
+ * Link to an existing page or external URL.
50
+ * Internal links (starting with `/`) use `<RouterLink>`.
51
+ * External links (e.g. `https://...`) open in a new tab.
52
+ * Mutually exclusive with `key` — if both are set, `link` takes precedence.
53
+ *
54
+ * @zh
55
+ * 链接到已有页面或外部 URL。
56
+ * 内部链接(以 `/` 开头)使用 `<RouterLink>`。
57
+ * 外部链接(如 `https://...`)在新标签页中打开。
58
+ * 与 `key` 互斥——若同时设置,`link` 优先。
59
+ */
60
+ link?: string
34
61
  }[]
35
62
  }
@@ -1,7 +1,7 @@
1
1
  import type mermaid from 'mermaid'
2
- import type { ViteSSGContext } from 'vite-ssg'
2
+ import type { ValaxySSGContext } from '../setups'
3
3
 
4
- export type UserModule = (ctx: ViteSSGContext) => void
4
+ export type UserModule = (ctx: ValaxySSGContext) => void
5
5
 
6
6
  /**
7
7
  * @see https://mermaid.js.org/config/schema-docs/config.html#mermaid-config-schema
@@ -47,7 +47,7 @@ export function sortByUpdated(posts: Post[], desc = true) {
47
47
  export function orderByMeta(posts: Post[], orderBy: 'date' | 'updated' = 'date', desc = true) {
48
48
  return posts.sort((a, b) => {
49
49
  const aDate = +new Date(a[orderBy] || a.date || '')
50
- const bDate = +new Date(b[orderBy] || a.date || '')
50
+ const bDate = +new Date(b[orderBy] || b.date || '')
51
51
  if (desc)
52
52
  return bDate - aDate
53
53
  else
@@ -1,7 +1,7 @@
1
1
  import 'node:process';
2
2
  import 'yargs';
3
3
  import 'yargs/helpers';
4
- export { c as cli, D as registerDevCommand, S as run, U as startValaxyDev } from '../../shared/valaxy.DpV6HHc6.mjs';
4
+ export { c as cli, I as registerDevCommand, W as run, Z as startValaxyDev } from '../../shared/valaxy.DXqMwOZX.mjs';
5
5
  import 'node:os';
6
6
  import 'node:path';
7
7
  import 'consola';
@@ -29,13 +29,9 @@ import 'feed';
29
29
  import 'markdown-it';
30
30
  import 'table';
31
31
  import 'hookable';
32
+ import 'node:child_process';
33
+ import 'node:v8';
32
34
  import 'vite-ssg-sitemap';
33
- import 'vite-ssg/node';
34
- import '@intlify/unplugin-vue-i18n/vite';
35
- import '@unhead/addons/vite';
36
- import 'unplugin-vue-components/vite';
37
- import 'vite-plugin-vue-layouts';
38
- import 'vitepress-plugin-group-icons';
39
35
  import 'markdown-it-async';
40
36
  import '@shikijs/transformers';
41
37
  import 'shiki';
@@ -46,21 +42,30 @@ import 'markdown-it-emoji';
46
42
  import 'markdown-it-footnote';
47
43
  import 'markdown-it-image-figures';
48
44
  import 'markdown-it-task-lists';
45
+ import 'vitepress-plugin-group-icons';
49
46
  import 'node:url';
50
47
  import 'markdown-it-container';
51
48
  import 'katex';
49
+ import 'katex/contrib/mhchem';
52
50
  import 'unplugin-vue-markdown/vite';
53
51
  import 'js-base64';
52
+ import '@intlify/unplugin-vue-i18n/vite';
53
+ import '@unhead/addons/vite';
54
+ import 'unplugin-vue-components/vite';
55
+ import 'vite-plugin-vue-layouts-next';
56
+ import 'p-map';
57
+ import 'node:buffer';
58
+ import 'minisearch';
59
+ import 'lru-cache';
54
60
  import 'node:fs';
55
61
  import 'jiti';
56
62
  import 'unocss';
57
63
  import 'pascalcase';
58
- import 'lru-cache';
59
64
  import 'html-to-text';
60
65
  import 'vue-router/vite';
66
+ import '@unhead/vue/server';
61
67
  import '@clack/prompts';
62
68
  import 'node:net';
63
- import 'node:child_process';
64
69
  import 'node:readline';
65
70
  import 'qrcode';
66
71
  import 'ejs';