valaxy 0.28.0-beta.7 → 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.
- package/client/components/.exclude/ValaxyDebug.vue +176 -0
- package/client/components/ValaxyOpenInEditor.vue +53 -0
- package/client/composables/app/useValaxyApp.ts +1 -7
- package/client/composables/categories.ts +3 -56
- package/client/composables/category-utils.ts +50 -0
- package/client/composables/index.ts +1 -0
- package/client/composables/outline/anchor.ts +1 -1
- package/client/composables/tags.ts +0 -5
- package/client/modules/components.ts +7 -0
- package/client/modules/valaxy.ts +28 -3
- package/dist/node/cli/index.mjs +1 -1
- package/dist/node/index.d.mts +7 -1
- package/dist/node/index.mjs +1 -1
- package/dist/shared/{valaxy.JIuR8V4d.d.mts → valaxy.6MW2qn5T.d.mts} +12 -4
- package/dist/shared/{valaxy.DAkHYbg0.mjs → valaxy.DQ6HsU2J.mjs} +126 -11
- package/dist/types/index.d.mts +2 -2
- package/package.json +8 -9
- package/types/config.ts +0 -4
- package/types/frontmatter/page.ts +12 -0
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|
package/client/modules/valaxy.ts
CHANGED
|
@@ -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
|
|
56
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/node/cli/index.mjs
CHANGED
|
@@ -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.
|
|
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';
|
package/dist/node/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ViteSSGOptions } from 'vite-ssg';
|
|
2
2
|
import * as vite from 'vite';
|
|
3
3
|
import { UserConfig, InlineConfig, ViteDevServer, PluginOption, Plugin } from 'vite';
|
|
4
|
-
import { D as DefaultTheme, R as RuntimeConfig, a as RedirectItem, V as ValaxyConfig, P as PartialDeep, b as ValaxyAddon, S as SiteConfig, U as UserSiteConfig } from '../shared/valaxy.
|
|
4
|
+
import { D as DefaultTheme, R as RuntimeConfig, a as RedirectItem, V as ValaxyConfig, P as PartialDeep, b as ValaxyAddon, S as SiteConfig, U as UserSiteConfig } from '../shared/valaxy.6MW2qn5T.mjs';
|
|
5
5
|
import Vue from '@vitejs/plugin-vue';
|
|
6
6
|
import { Hookable } from 'hookable';
|
|
7
7
|
import { PluginVisualizerOptions } from 'rollup-plugin-visualizer';
|
|
@@ -768,6 +768,12 @@ interface ValaxyExtendConfig {
|
|
|
768
768
|
* @default true
|
|
769
769
|
*/
|
|
770
770
|
katex: boolean;
|
|
771
|
+
/**
|
|
772
|
+
* @description:en-US Auto-extract the first image from markdown content for Open Graph fallback
|
|
773
|
+
* @description:zh-CN 自动从 Markdown 内容中提取第一张图片,作为 Open Graph 的回退图片
|
|
774
|
+
* @default true
|
|
775
|
+
*/
|
|
776
|
+
extractFirstImage: boolean;
|
|
771
777
|
};
|
|
772
778
|
/**
|
|
773
779
|
* Enable MathJax3 math rendering (aligned with VitePress `markdown.math`).
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ALL_ROUTE, E as EXCERPT_SEPARATOR, G as GLOBAL_STATE, P as PATHNAME_PROTOCOL_RE, V as ViteValaxyPlugins, b as build, c as cli, a as createServer, d as createValaxyPlugin, e as customElements, f as defaultSiteConfig, g as defaultValaxyConfig, h as defaultViteConfig, i as defineAddon, j as defineConfig, k as defineSiteConfig, l as defineTheme, m as defineValaxyAddon, n as defineValaxyConfig, o as defineValaxyTheme, p as encryptContent, q as generateClientRedirects, r as getGitTimestamp, s as getIndexHtml, t as getServerInfoText, u as isExternal, v as isInstalledGlobally, w as isKatexEnabled, x as isKatexPluginNeeded, y as isMathJaxEnabled, z as isPath, B as loadConfigFromFile, C as mergeValaxyConfig, D as mergeViteConfigs, F as postProcessForSSG, H as processValaxyOptions, I as registerDevCommand, J as resolveAddonsConfig, K as resolveImportPath, L as resolveImportUrl, M as resolveOptions, N as resolveSiteConfig, O as resolveSiteConfigFromRoot, Q as resolveThemeConfigFromRoot, R as resolveThemeValaxyConfig, S as resolveUserThemeConfig, T as resolveValaxyConfig, U as resolveValaxyConfigFromRoot, W as run, X as ssgBuild, Y as ssgBuildLegacy, Z as startValaxyDev, _ as toAtFS, $ as transformObject, a0 as version } from '../shared/valaxy.
|
|
1
|
+
export { A as ALL_ROUTE, E as EXCERPT_SEPARATOR, G as GLOBAL_STATE, P as PATHNAME_PROTOCOL_RE, V as ViteValaxyPlugins, b as build, c as cli, a as createServer, d as createValaxyPlugin, e as customElements, f as defaultSiteConfig, g as defaultValaxyConfig, h as defaultViteConfig, i as defineAddon, j as defineConfig, k as defineSiteConfig, l as defineTheme, m as defineValaxyAddon, n as defineValaxyConfig, o as defineValaxyTheme, p as encryptContent, q as generateClientRedirects, r as getGitTimestamp, s as getIndexHtml, t as getServerInfoText, u as isExternal, v as isInstalledGlobally, w as isKatexEnabled, x as isKatexPluginNeeded, y as isMathJaxEnabled, z as isPath, B as loadConfigFromFile, C as mergeValaxyConfig, D as mergeViteConfigs, F as postProcessForSSG, H as processValaxyOptions, I as registerDevCommand, J as resolveAddonsConfig, K as resolveImportPath, L as resolveImportUrl, M as resolveOptions, N as resolveSiteConfig, O as resolveSiteConfigFromRoot, Q as resolveThemeConfigFromRoot, R as resolveThemeValaxyConfig, S as resolveUserThemeConfig, T as resolveValaxyConfig, U as resolveValaxyConfigFromRoot, W as run, X as ssgBuild, Y as ssgBuildLegacy, Z as startValaxyDev, _ as toAtFS, $ as transformObject, a0 as version } from '../shared/valaxy.DQ6HsU2J.mjs';
|
|
2
2
|
import 'node:path';
|
|
3
3
|
import 'fs-extra';
|
|
4
4
|
import 'consola/utils';
|
|
@@ -167,6 +167,18 @@ interface PageFrontMatter extends BaseFrontMatter {
|
|
|
167
167
|
* @description 封面图片
|
|
168
168
|
*/
|
|
169
169
|
cover: string;
|
|
170
|
+
/**
|
|
171
|
+
* @description:en-US Open Graph image for SEO
|
|
172
|
+
* @description:zh-CN Open Graph 图片,用于 SEO
|
|
173
|
+
*/
|
|
174
|
+
ogImage: string;
|
|
175
|
+
/**
|
|
176
|
+
* @protected
|
|
177
|
+
* @tutorial ⚠️ DO NOT SET MANUALLY (auto-extracted from markdown content)
|
|
178
|
+
* @description:en-US First image URL extracted from markdown content
|
|
179
|
+
* @description:zh-CN 从 Markdown 内容中自动提取的第一张图片 URL
|
|
180
|
+
*/
|
|
181
|
+
firstImage: string;
|
|
170
182
|
/**
|
|
171
183
|
* display toc
|
|
172
184
|
* @description 是否显示目录
|
|
@@ -638,10 +650,6 @@ interface SiteConfig {
|
|
|
638
650
|
* @zh 是否启用
|
|
639
651
|
*/
|
|
640
652
|
enable: boolean;
|
|
641
|
-
/**
|
|
642
|
-
* @deprecated will be deprecated, use search.provider instead
|
|
643
|
-
*/
|
|
644
|
-
type?: SiteConfig['search']['provider'];
|
|
645
653
|
/**
|
|
646
654
|
* Search Type
|
|
647
655
|
* - algolia: Algolia Search
|
|
@@ -28,7 +28,7 @@ import { Feed } from 'feed';
|
|
|
28
28
|
import MarkdownIt from 'markdown-it';
|
|
29
29
|
import { table, getBorderCharacters } from 'table';
|
|
30
30
|
import { createHooks } from 'hookable';
|
|
31
|
-
import { execFileSync, exec } from 'node:child_process';
|
|
31
|
+
import { execFileSync, execSync, exec } from 'node:child_process';
|
|
32
32
|
import v8 from 'node:v8';
|
|
33
33
|
import generateSitemap from 'vite-ssg-sitemap';
|
|
34
34
|
import { createMarkdownItAsync, MarkdownItAsync } from 'markdown-it-async';
|
|
@@ -878,7 +878,8 @@ const defaultValaxyConfig = {
|
|
|
878
878
|
}
|
|
879
879
|
},
|
|
880
880
|
features: {
|
|
881
|
-
katex: true
|
|
881
|
+
katex: true,
|
|
882
|
+
extractFirstImage: true
|
|
882
883
|
},
|
|
883
884
|
math: false,
|
|
884
885
|
cdn: {
|
|
@@ -931,8 +932,9 @@ async function resolveValaxyConfigFromRoot(root, options) {
|
|
|
931
932
|
}
|
|
932
933
|
const mergeValaxyConfig = createDefu((obj, key, value) => {
|
|
933
934
|
if (isFunction(obj[key]) && isFunction(value)) {
|
|
935
|
+
const original = obj[key];
|
|
934
936
|
obj[key] = function(...args) {
|
|
935
|
-
|
|
937
|
+
original.call(this, ...args);
|
|
936
938
|
value.call(this, ...args);
|
|
937
939
|
};
|
|
938
940
|
return true;
|
|
@@ -1719,7 +1721,7 @@ async function setupMarkdownPlugins(md, options, base = "/") {
|
|
|
1719
1721
|
return md;
|
|
1720
1722
|
}
|
|
1721
1723
|
|
|
1722
|
-
const version = "0.28.0
|
|
1724
|
+
const version = "0.28.0";
|
|
1723
1725
|
|
|
1724
1726
|
const GLOBAL_STATE = {
|
|
1725
1727
|
valaxyApp: void 0,
|
|
@@ -3394,10 +3396,24 @@ function inferDescription(frontmatter) {
|
|
|
3394
3396
|
return description;
|
|
3395
3397
|
return head && getHeadMetaContent(head, "description") || "";
|
|
3396
3398
|
}
|
|
3399
|
+
function extractFirstImage(code) {
|
|
3400
|
+
const mdImageMatch = code.match(/!\[.*?\]\((.+?)\)/);
|
|
3401
|
+
if (mdImageMatch)
|
|
3402
|
+
return mdImageMatch[1];
|
|
3403
|
+
const htmlImageMatch = code.match(/<img\s[^>]*?src=["'](.+?)["']/);
|
|
3404
|
+
if (htmlImageMatch)
|
|
3405
|
+
return htmlImageMatch[1];
|
|
3406
|
+
return void 0;
|
|
3407
|
+
}
|
|
3397
3408
|
async function generatePageData(code, id, options) {
|
|
3398
3409
|
const fileInfo = Valaxy.state.idMap.get(id);
|
|
3399
3410
|
const relativePath = path.relative(options.userRoot, id);
|
|
3400
3411
|
const fm = JSON.parse(JSON.stringify(fileInfo?.frontmatter));
|
|
3412
|
+
if (options.config.features?.extractFirstImage !== false && !fm.ogImage && !fm.cover) {
|
|
3413
|
+
const firstImage = extractFirstImage(code);
|
|
3414
|
+
if (firstImage)
|
|
3415
|
+
fm.firstImage = firstImage;
|
|
3416
|
+
}
|
|
3401
3417
|
const pageData = {
|
|
3402
3418
|
title: fm.title || fileInfo?.title || "",
|
|
3403
3419
|
titleTemplate: fm.titleTemplate,
|
|
@@ -3408,7 +3424,8 @@ async function generatePageData(code, id, options) {
|
|
|
3408
3424
|
relativePath,
|
|
3409
3425
|
filePath: id
|
|
3410
3426
|
};
|
|
3411
|
-
|
|
3427
|
+
if (options.config.siteConfig.lastUpdated)
|
|
3428
|
+
pageData.lastUpdated = await getGitTimestamp(id);
|
|
3412
3429
|
return pageData;
|
|
3413
3430
|
}
|
|
3414
3431
|
|
|
@@ -5401,17 +5418,21 @@ async function getPosts(params, options) {
|
|
|
5401
5418
|
return { data, content, excerpt, path: i };
|
|
5402
5419
|
});
|
|
5403
5420
|
const rawPosts = await Promise.all(readFilePromises);
|
|
5421
|
+
const draftPosts = [];
|
|
5404
5422
|
const filteredPosts = rawPosts.filter((p) => {
|
|
5405
5423
|
const { data } = p;
|
|
5406
5424
|
if (data.password)
|
|
5407
5425
|
return false;
|
|
5408
5426
|
if (data.draft) {
|
|
5427
|
+
draftPosts.push(p.path);
|
|
5409
5428
|
return false;
|
|
5410
5429
|
}
|
|
5411
5430
|
if (data.hide)
|
|
5412
5431
|
return false;
|
|
5413
5432
|
return true;
|
|
5414
5433
|
});
|
|
5434
|
+
if (draftPosts.length)
|
|
5435
|
+
consola.log(`[rss] Skipped ${draftPosts.length} draft post(s): ${draftPosts.join(", ")}`);
|
|
5415
5436
|
const posts = [];
|
|
5416
5437
|
for (const rawPost of filteredPosts) {
|
|
5417
5438
|
const { data, path, content, excerpt } = rawPost;
|
|
@@ -6080,14 +6101,108 @@ function registerCleanCommand(cli) {
|
|
|
6080
6101
|
);
|
|
6081
6102
|
}
|
|
6082
6103
|
|
|
6083
|
-
function
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6104
|
+
function getPnpmVersion() {
|
|
6105
|
+
try {
|
|
6106
|
+
return execSync("pnpm --version", { encoding: "utf-8" }).trim();
|
|
6107
|
+
} catch {
|
|
6108
|
+
return "not found";
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
async function collectDebugInfo() {
|
|
6112
|
+
const info = {
|
|
6113
|
+
os: os.platform(),
|
|
6114
|
+
arch: os.arch(),
|
|
6115
|
+
node: process.version,
|
|
6116
|
+
pnpm: getPnpmVersion(),
|
|
6117
|
+
valaxy: version
|
|
6118
|
+
};
|
|
6119
|
+
try {
|
|
6120
|
+
const options = await resolveOptions({ userRoot: process.cwd() });
|
|
6121
|
+
info.userRoot = options.userRoot;
|
|
6122
|
+
info.theme = options.theme;
|
|
6123
|
+
info.themeVersion = options.config.themeConfig?.pkg?.version;
|
|
6124
|
+
info.addons = options.addons.filter((a) => a.enable).map((a) => ({
|
|
6125
|
+
name: a.name,
|
|
6126
|
+
version: a.pkg?.version || "unknown",
|
|
6127
|
+
global: a.global
|
|
6128
|
+
}));
|
|
6129
|
+
info.pages = options.pages.length;
|
|
6130
|
+
} catch {
|
|
6131
|
+
}
|
|
6132
|
+
return info;
|
|
6133
|
+
}
|
|
6134
|
+
function printFancy(info) {
|
|
6135
|
+
const lines = [];
|
|
6136
|
+
lines.push(`${colors.bold(colors.cyan("Environment"))}`);
|
|
6137
|
+
lines.push(` OS: ${colors.green(`${info.os} ${info.arch}`)}`);
|
|
6138
|
+
lines.push(` Node.js: ${colors.green(info.node)}`);
|
|
6139
|
+
lines.push(` Package Manager: ${colors.green(`pnpm ${info.pnpm}`)}`);
|
|
6140
|
+
lines.push(` Valaxy: ${colors.cyan(`v${info.valaxy}`)}`);
|
|
6141
|
+
if (info.theme) {
|
|
6142
|
+
lines.push("");
|
|
6143
|
+
lines.push(`${colors.bold(colors.cyan("Project"))}`);
|
|
6144
|
+
lines.push(` Root: ${colors.dim(info.userRoot)}`);
|
|
6145
|
+
lines.push(` Theme: ${colors.green(info.theme)} ${colors.blue(`v${info.themeVersion || "unknown"}`)}`);
|
|
6146
|
+
if (info.addons && info.addons.length > 0) {
|
|
6147
|
+
lines.push(` Addons:`);
|
|
6148
|
+
info.addons.forEach((addon, i) => {
|
|
6149
|
+
const prefix = i === info.addons.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
6150
|
+
const globalTag = addon.global ? colors.cyan(" (global)") : "";
|
|
6151
|
+
lines.push(` ${prefix} ${colors.yellow(addon.name)} ${colors.blue(`v${addon.version}`)}${globalTag}`);
|
|
6152
|
+
});
|
|
6153
|
+
} else {
|
|
6154
|
+
lines.push(` Addons: ${colors.dim("none")}`);
|
|
6155
|
+
}
|
|
6156
|
+
lines.push(` Pages: ${colors.green(String(info.pages))}`);
|
|
6157
|
+
}
|
|
6158
|
+
consola.box({
|
|
6159
|
+
title: "\u{1F30C} Valaxy Debug Info",
|
|
6160
|
+
message: lines.join("\n"),
|
|
6161
|
+
style: {
|
|
6162
|
+
borderColor: "cyan"
|
|
6163
|
+
}
|
|
6089
6164
|
});
|
|
6090
6165
|
}
|
|
6166
|
+
function printPlain(info) {
|
|
6167
|
+
const lines = [];
|
|
6168
|
+
lines.push("## Environment");
|
|
6169
|
+
lines.push(`- OS: ${info.os} ${info.arch}`);
|
|
6170
|
+
lines.push(`- Node: ${info.node}`);
|
|
6171
|
+
lines.push(`- Package Manager: pnpm ${info.pnpm}`);
|
|
6172
|
+
lines.push(`- Valaxy: v${info.valaxy}`);
|
|
6173
|
+
if (info.theme) {
|
|
6174
|
+
lines.push("");
|
|
6175
|
+
lines.push("## Project");
|
|
6176
|
+
lines.push(`- Root: ${info.userRoot}`);
|
|
6177
|
+
lines.push(`- Theme: ${info.theme} (v${info.themeVersion || "unknown"})`);
|
|
6178
|
+
if (info.addons && info.addons.length > 0) {
|
|
6179
|
+
const addonStr = info.addons.map((a) => `${a.name} (v${a.version})${a.global ? " [global]" : ""}`).join(", ");
|
|
6180
|
+
lines.push(`- Addons: ${addonStr}`);
|
|
6181
|
+
} else {
|
|
6182
|
+
lines.push("- Addons: none");
|
|
6183
|
+
}
|
|
6184
|
+
lines.push(`- Pages: ${info.pages}`);
|
|
6185
|
+
}
|
|
6186
|
+
console.log(lines.join("\n"));
|
|
6187
|
+
}
|
|
6188
|
+
function registerDebugCommand(cli) {
|
|
6189
|
+
cli.command(
|
|
6190
|
+
"debug",
|
|
6191
|
+
"Display debug information for your Valaxy project",
|
|
6192
|
+
(args) => args.option("plain", {
|
|
6193
|
+
type: "boolean",
|
|
6194
|
+
default: false,
|
|
6195
|
+
describe: "Output plain text without colors (for pasting into issues)"
|
|
6196
|
+
}),
|
|
6197
|
+
async (args) => {
|
|
6198
|
+
const info = await collectDebugInfo();
|
|
6199
|
+
if (args.plain)
|
|
6200
|
+
printPlain(info);
|
|
6201
|
+
else
|
|
6202
|
+
printFancy(info);
|
|
6203
|
+
}
|
|
6204
|
+
);
|
|
6205
|
+
}
|
|
6091
6206
|
|
|
6092
6207
|
function registerDeployCommand(cli) {
|
|
6093
6208
|
cli.command("deploy", "deploy your blog to the cloud", async () => {
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as Post } from '../shared/valaxy.
|
|
2
|
-
export { A as Album, B as BaseFrontMatter, D as DefaultTheme, E as ExcerptType, F as FuseListItem, d as Page, e as PageFrontMatter, P as PartialDeep, f as Photo, g as Pkg, h as PostFrontMatter, a as RedirectItem, i as RedirectRule, R as RuntimeConfig, S as SiteConfig, j as SocialLink, U as UserSiteConfig, k as UserValaxyConfig, b as ValaxyAddon, V as ValaxyConfig } from '../shared/valaxy.
|
|
1
|
+
import { c as Post } from '../shared/valaxy.6MW2qn5T.mjs';
|
|
2
|
+
export { A as Album, B as BaseFrontMatter, D as DefaultTheme, E as ExcerptType, F as FuseListItem, d as Page, e as PageFrontMatter, P as PartialDeep, f as Photo, g as Pkg, h as PostFrontMatter, a as RedirectItem, i as RedirectRule, R as RuntimeConfig, S as SiteConfig, j as SocialLink, U as UserSiteConfig, k as UserValaxyConfig, b as ValaxyAddon, V as ValaxyConfig } from '../shared/valaxy.6MW2qn5T.mjs';
|
|
3
3
|
import { Header } from '@valaxyjs/utils';
|
|
4
4
|
import '@vueuse/integrations/useFuse';
|
|
5
5
|
import 'medium-zoom';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "valaxy",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.28.0
|
|
4
|
+
"version": "0.28.0",
|
|
5
5
|
"description": "📄 Vite & Vue powered static blog generator.",
|
|
6
6
|
"author": {
|
|
7
7
|
"email": "me@yunyoujun.cn",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"types"
|
|
58
58
|
],
|
|
59
59
|
"engines": {
|
|
60
|
-
"node": "^
|
|
60
|
+
"node": "^18.0.0 || >=20.0.0"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@antfu/install-pkg": "^1.1.0",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"jiti": "^2.6.1",
|
|
96
96
|
"js-base64": "^3.7.8",
|
|
97
97
|
"js-yaml": "^4.1.1",
|
|
98
|
-
"katex": "^0.16.
|
|
98
|
+
"katex": "^0.16.42",
|
|
99
99
|
"lru-cache": "^11.2.7",
|
|
100
100
|
"markdown-it": "^14.1.1",
|
|
101
101
|
"markdown-it-anchor": "^9.2.0",
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"markdown-it-emoji": "^3.0.0",
|
|
106
106
|
"markdown-it-footnote": "^4.0.0",
|
|
107
107
|
"markdown-it-image-figures": "^2.1.1",
|
|
108
|
-
"markdown-it-table-of-contents": "^1.
|
|
108
|
+
"markdown-it-table-of-contents": "^1.2.0",
|
|
109
109
|
"markdown-it-task-lists": "^2.1.1",
|
|
110
110
|
"medium-zoom": "^1.1.0",
|
|
111
111
|
"mermaid": "^11.13.0",
|
|
@@ -129,9 +129,8 @@
|
|
|
129
129
|
"unplugin-vue-components": "28.0.0",
|
|
130
130
|
"unplugin-vue-markdown": "^30.0.0",
|
|
131
131
|
"vanilla-lazyload": "^19.1.3",
|
|
132
|
-
"vite": "^8.0.
|
|
133
|
-
"vite-
|
|
134
|
-
"vite-plugin-vue-devtools": "^8.1.0",
|
|
132
|
+
"vite": "^8.0.2",
|
|
133
|
+
"vite-plugin-vue-devtools": "^8.1.1",
|
|
135
134
|
"vite-plugin-vue-layouts-next": "^2.1.0",
|
|
136
135
|
"vite-ssg": "^28.3.0",
|
|
137
136
|
"vite-ssg-sitemap": "^0.10.0",
|
|
@@ -140,8 +139,8 @@
|
|
|
140
139
|
"vue-i18n": "^11.3.0",
|
|
141
140
|
"vue-router": "^5.0.4",
|
|
142
141
|
"yargs": "^18.0.0",
|
|
143
|
-
"@valaxyjs/
|
|
144
|
-
"@valaxyjs/
|
|
142
|
+
"@valaxyjs/devtools": "0.28.0",
|
|
143
|
+
"@valaxyjs/utils": "0.28.0"
|
|
145
144
|
},
|
|
146
145
|
"devDependencies": {
|
|
147
146
|
"@mdit-vue/plugin-component": "^3.0.2",
|
package/types/config.ts
CHANGED
|
@@ -172,10 +172,6 @@ export interface SiteConfig {
|
|
|
172
172
|
* @zh 是否启用
|
|
173
173
|
*/
|
|
174
174
|
enable: boolean
|
|
175
|
-
/**
|
|
176
|
-
* @deprecated will be deprecated, use search.provider instead
|
|
177
|
-
*/
|
|
178
|
-
type?: SiteConfig['search']['provider']
|
|
179
175
|
/**
|
|
180
176
|
* Search Type
|
|
181
177
|
* - algolia: Algolia Search
|
|
@@ -109,6 +109,18 @@ export interface PageFrontMatter extends BaseFrontMatter {
|
|
|
109
109
|
* @description 封面图片
|
|
110
110
|
*/
|
|
111
111
|
cover: string
|
|
112
|
+
/**
|
|
113
|
+
* @description:en-US Open Graph image for SEO
|
|
114
|
+
* @description:zh-CN Open Graph 图片,用于 SEO
|
|
115
|
+
*/
|
|
116
|
+
ogImage: string
|
|
117
|
+
/**
|
|
118
|
+
* @protected
|
|
119
|
+
* @tutorial ⚠️ DO NOT SET MANUALLY (auto-extracted from markdown content)
|
|
120
|
+
* @description:en-US First image URL extracted from markdown content
|
|
121
|
+
* @description:zh-CN 从 Markdown 内容中自动提取的第一张图片 URL
|
|
122
|
+
*/
|
|
123
|
+
firstImage: string
|
|
112
124
|
/**
|
|
113
125
|
* display toc
|
|
114
126
|
* @description 是否显示目录
|