@movk/nuxt-docs 1.3.12 → 1.4.1
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/README.md +0 -41
- package/app/app.config.ts +11 -7
- package/app/app.vue +8 -2
- package/app/components/PageHeaderLinks.vue +16 -3
- package/app/components/content/CommitChangelog.vue +1 -1
- package/app/components/header/HeaderLogo.vue +2 -50
- package/app/components/theme-picker/ThemePicker.vue +303 -7
- package/app/composables/useAnalytics.ts +7 -0
- package/app/error.vue +3 -1
- package/app/plugins/theme.ts +29 -0
- package/app/types/index.d.ts +2 -0
- package/app/utils/theme.ts +136 -0
- package/nuxt.config.ts +11 -0
- package/package.json +8 -6
- package/server/api/github/commits.get.ts +35 -18
package/README.md
CHANGED
|
@@ -159,47 +159,6 @@ icon: i-lucide-rocket
|
|
|
159
159
|
|
|
160
160
|
了解更多关于 MDC 语法,请查看 [Nuxt Content 文档](https://content.nuxt.com/docs/files/markdown#mdc-syntax)。
|
|
161
161
|
|
|
162
|
-
## 🔌 集成第三方服务
|
|
163
|
-
|
|
164
|
-
本主题不内置任何分析或监控工具,你可以根据需求自由选择。
|
|
165
|
-
|
|
166
|
-
### Vercel Analytics
|
|
167
|
-
|
|
168
|
-
```bash [Terminal]
|
|
169
|
-
pnpm add @vercel/analytics @vercel/speed-insights
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
创建 `app/plugins/analytics.client.ts`:
|
|
173
|
-
|
|
174
|
-
```typescript [app/plugins/analytics.client.ts]
|
|
175
|
-
import { Analytics } from '@vercel/analytics/nuxt'
|
|
176
|
-
import { SpeedInsights } from '@vercel/speed-insights/nuxt'
|
|
177
|
-
import { createApp, h } from 'vue'
|
|
178
|
-
|
|
179
|
-
export default defineNuxtPlugin({
|
|
180
|
-
name: 'vercel-analytics',
|
|
181
|
-
enforce: 'post',
|
|
182
|
-
hooks: {
|
|
183
|
-
'app:mounted': () => {
|
|
184
|
-
if (import.meta.dev) return
|
|
185
|
-
|
|
186
|
-
const container = document.createElement('div')
|
|
187
|
-
container.id = 'vercel-analytics'
|
|
188
|
-
document.body.appendChild(container)
|
|
189
|
-
|
|
190
|
-
const app = createApp({
|
|
191
|
-
render: () => h('div', { style: 'display: none;' }, [
|
|
192
|
-
h(Analytics, { debug: false }),
|
|
193
|
-
h(SpeedInsights, { debug: false })
|
|
194
|
-
])
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
app.mount(container)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
```
|
|
202
|
-
|
|
203
162
|
### 其他工具
|
|
204
163
|
|
|
205
164
|
- **Google Analytics** - [@nuxtjs/google-analytics](https://google-analytics.nuxtjs.org/)
|
package/app/app.config.ts
CHANGED
|
@@ -2,19 +2,21 @@ import type { ButtonProps } from '@nuxt/ui'
|
|
|
2
2
|
|
|
3
3
|
export default defineAppConfig({
|
|
4
4
|
toaster: {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
position: 'bottom-right' as const,
|
|
6
|
+
duration: 5000,
|
|
7
|
+
max: 5,
|
|
8
|
+
expand: true
|
|
9
9
|
},
|
|
10
10
|
theme: {
|
|
11
11
|
radius: 0.25,
|
|
12
|
-
blackAsPrimary: false
|
|
12
|
+
blackAsPrimary: false,
|
|
13
|
+
icons: 'lucide',
|
|
14
|
+
font: 'Public Sans'
|
|
13
15
|
},
|
|
14
16
|
ui: {
|
|
15
17
|
colors: {
|
|
16
|
-
primary: '
|
|
17
|
-
neutral: '
|
|
18
|
+
primary: 'green',
|
|
19
|
+
neutral: 'slate'
|
|
18
20
|
},
|
|
19
21
|
contentNavigation: {
|
|
20
22
|
slots: {
|
|
@@ -32,7 +34,9 @@ export default defineAppConfig({
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
},
|
|
37
|
+
vercelAnalytics: false,
|
|
35
38
|
header: {
|
|
39
|
+
avatar: 'https://docs.mhaibaraai.cn/avatar.png',
|
|
36
40
|
title: 'Movk Nuxt Docs',
|
|
37
41
|
to: '/',
|
|
38
42
|
search: true,
|
package/app/app.vue
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import colors from 'tailwindcss/colors'
|
|
3
|
+
import { Analytics } from '@vercel/analytics/nuxt'
|
|
4
|
+
import { SpeedInsights } from '@vercel/speed-insights/nuxt'
|
|
3
5
|
|
|
4
6
|
const site = useSiteConfig()
|
|
5
7
|
const appConfig = useAppConfig()
|
|
@@ -13,6 +15,7 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
|
|
|
13
15
|
const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
|
|
14
16
|
const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
|
|
15
17
|
const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
|
|
18
|
+
const font = computed(() => `:root { --font-sans: '${appConfig.theme.font}', sans-serif; }`)
|
|
16
19
|
|
|
17
20
|
useHead({
|
|
18
21
|
meta: [
|
|
@@ -21,7 +24,8 @@ useHead({
|
|
|
21
24
|
],
|
|
22
25
|
style: [
|
|
23
26
|
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 },
|
|
24
|
-
{ innerHTML: blackAsPrimary, id: 'nuxt-ui-black-as-primary', tagPriority: -2 }
|
|
27
|
+
{ innerHTML: blackAsPrimary, id: 'nuxt-ui-black-as-primary', tagPriority: -2 },
|
|
28
|
+
{ innerHTML: font, id: 'nuxt-ui-font', tagPriority: -2 }
|
|
25
29
|
]
|
|
26
30
|
})
|
|
27
31
|
|
|
@@ -41,6 +45,8 @@ provide('navigation', rootNavigation)
|
|
|
41
45
|
<template>
|
|
42
46
|
<UApp :toaster="appConfig.toaster">
|
|
43
47
|
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
|
|
48
|
+
<Analytics v-if="appConfig.vercelAnalytics" />
|
|
49
|
+
<SpeedInsights v-if="appConfig.vercelAnalytics" />
|
|
44
50
|
|
|
45
51
|
<div :class="{ root: route.path.startsWith('/docs/') }">
|
|
46
52
|
<template v-if="!route.path.startsWith('/examples')">
|
|
@@ -55,7 +61,7 @@ provide('navigation', rootNavigation)
|
|
|
55
61
|
<Footer />
|
|
56
62
|
|
|
57
63
|
<ClientOnly>
|
|
58
|
-
<LazyUContentSearch :files="files" :navigation="rootNavigation" :fuse="{ resultLimit: 1000 }"/>
|
|
64
|
+
<LazyUContentSearch :files="files" :navigation="rootNavigation" :fuse="{ resultLimit: 1000 }" />
|
|
59
65
|
</ClientOnly>
|
|
60
66
|
</template>
|
|
61
67
|
</div>
|
|
@@ -3,6 +3,8 @@ const route = useRoute()
|
|
|
3
3
|
const toast = useToast()
|
|
4
4
|
const { copy, copied } = useClipboard()
|
|
5
5
|
const site = useSiteConfig()
|
|
6
|
+
const { vercelAnalytics } = useAppConfig()
|
|
7
|
+
const { track } = useAnalytics()
|
|
6
8
|
|
|
7
9
|
const mdPath = computed(() => `${site.url}/raw${route.path}.md`)
|
|
8
10
|
|
|
@@ -11,6 +13,7 @@ const items = [
|
|
|
11
13
|
label: 'Copy Markdown link',
|
|
12
14
|
icon: 'i-lucide-link',
|
|
13
15
|
onSelect() {
|
|
16
|
+
if (vercelAnalytics) track ('Page Action', { action: 'Copy Markdown Link' })
|
|
14
17
|
copy(mdPath.value)
|
|
15
18
|
toast.add({
|
|
16
19
|
title: 'Copied to clipboard',
|
|
@@ -22,23 +25,33 @@ const items = [
|
|
|
22
25
|
label: 'View as Markdown',
|
|
23
26
|
icon: 'i-simple-icons:markdown',
|
|
24
27
|
target: '_blank',
|
|
25
|
-
to: `/raw${route.path}.md
|
|
28
|
+
to: `/raw${route.path}.md`,
|
|
29
|
+
onSelect() {
|
|
30
|
+
if (vercelAnalytics) track('Page Action', { action: 'View as Markdown' })
|
|
31
|
+
}
|
|
26
32
|
},
|
|
27
33
|
{
|
|
28
34
|
label: 'Open in ChatGPT',
|
|
29
35
|
icon: 'i-simple-icons:openai',
|
|
30
36
|
target: '_blank',
|
|
31
|
-
to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}
|
|
37
|
+
to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`,
|
|
38
|
+
onSelect() {
|
|
39
|
+
if (vercelAnalytics) track('Page Action', { action: 'Open in ChatGPT' })
|
|
40
|
+
}
|
|
32
41
|
},
|
|
33
42
|
{
|
|
34
43
|
label: 'Open in Claude',
|
|
35
44
|
icon: 'i-simple-icons:anthropic',
|
|
36
45
|
target: '_blank',
|
|
37
|
-
to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}
|
|
46
|
+
to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`,
|
|
47
|
+
onSelect() {
|
|
48
|
+
if (vercelAnalytics) track('Page Action', { action: 'Open in Claude' })
|
|
49
|
+
}
|
|
38
50
|
}
|
|
39
51
|
]
|
|
40
52
|
|
|
41
53
|
async function copyPage() {
|
|
54
|
+
if (vercelAnalytics) track('Page Action', { action: 'Copy Page Content' })
|
|
42
55
|
copy(await $fetch<string>(`/raw${route.path}.md`))
|
|
43
56
|
}
|
|
44
57
|
</script>
|
|
@@ -55,7 +55,7 @@ const filePath = computed(() => {
|
|
|
55
55
|
|
|
56
56
|
const { data: commits } = await useLazyFetch<Commit[]>('/api/github/commits', {
|
|
57
57
|
key: `commit-changelog-${props.name ?? routeName.value}-${props.author ?? 'all'}`,
|
|
58
|
-
query: { path: filePath.value, author: props.author }
|
|
58
|
+
query: { path: [filePath.value], author: props.author }
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
// 格式化提交消息
|
|
@@ -3,55 +3,7 @@ const { header } = useAppConfig()
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<template>
|
|
6
|
-
<NuxtLink
|
|
7
|
-
:
|
|
8
|
-
class="flex items-center gap-2 font-bold text-xl text-highlighted min-w-0 focus-visible:outline-primary shrink-0"
|
|
9
|
-
:aria-label="header.title"
|
|
10
|
-
>
|
|
11
|
-
<svg
|
|
12
|
-
width="256"
|
|
13
|
-
height="256"
|
|
14
|
-
viewBox="0 0 256 256"
|
|
15
|
-
fill="none"
|
|
16
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
17
|
-
class="w-auto h-10 shrink-0"
|
|
18
|
-
>
|
|
19
|
-
<circle
|
|
20
|
-
cx="128"
|
|
21
|
-
cy="128"
|
|
22
|
-
r="120"
|
|
23
|
-
fill="var(--ui-primary)"
|
|
24
|
-
/>
|
|
25
|
-
<g fill="white" opacity="0.95">
|
|
26
|
-
<path
|
|
27
|
-
d="M38.5 175.6 c-1.5 -1 -1.9 -2.7 -1.9 -7.7 0 -5.5 0.4 -7.1 3.1 -11.4 1.8 -2.7 4.8 -6.3 6.8 -7.8 1.9 -1.5 3.5 -3.2 3.5 -3.8 0 -1.2 -5.8 -0.4 -11.5 1.6 -11.7 4.1 -16.3 3.7 -19 -1.5 -2.6 -5 -1.2 -8.4 4.2 -11 6.6 -3.1 10.4 -3.5 14.3 -1.5 2.1 1.1 4.1 1.4 5.9 0.9 1.4 -0.4 3.5 -0.9 4.6 -1.1 4.9 -0.9 12.3 -3.8 16 -6.3 13.7 -9.4 18.6 -11.7 34 -16 6.6 -1.9 11.9 -2.4 31.3 -3.1 l23.4 -0.8 8.4 -8.5 c5.7 -5.8 9.5 -8.8 11.9 -9.5 1.9 -0.5 5.8 -2.4 8.7 -4.1 4.5 -2.8 5.4 -3 7.5 -1.9 2 1.1 2.9 0.9 6.2 -0.9 6.4 -3.6 7.6 -2.5 4.4 4.3 l-1.7 3.5 3.3 4.1 c2.8 3.5 3.2 4.6 2.7 7.7 -0.3 2 0 5.1 0.6 6.8 1.8 5 -0.4 8.9 -6.3 11.6 -2.9 1.4 -4.5 2.6 -4 3.3 0.4 0.5 1.6 3.5 2.6 6.5 2.8 8.8 1.9 8.2 13.5 8.5 10.2 0.2 21.7 1.9 25.3 3.7 2 1 2.3 5.1 0.3 6.7 -0.7 0.6 -5.1 1.5 -9.7 2.1 -4.6 0.5 -12.7 1.9 -17.9 3 -8.5 1.8 -12.8 2 -42 1.9 -17.9 -0.1 -35 -0.3 -38 -0.5 -3 -0.1 -10.7 -0.6 -17.1 -1 -9.7 -0.6 -12.4 -0.4 -17 1.1 -7.2 2.3 -7.3 2.4 -5.5 4.4 2.2 2.5 3.1 7.8 1.6 9.6 -2.7 3.3 -9.3 1.2 -17.4 -5.5 -5.6 -4.7 -8.8 -5 -15.6 -1.7 -4.4 2.2 -4.5 2.3 -4.8 7.6 -0.2 3.7 -0.9 5.9 -2 6.8 -2.3 1.7 -10.3 1.6 -12.7 -0.1z"
|
|
28
|
-
/>
|
|
29
|
-
</g>
|
|
30
|
-
<g fill="white" opacity="0.98">
|
|
31
|
-
<path
|
|
32
|
-
d="M142 209.3 c-0.7 -1.6 -2.3 -5.7 -3.5 -9.3 -1.3 -3.6 -2.6 -7.2 -3 -8.1 -0.4 -1.1 0 -1.8 1.4 -2.2 1.7 -0.4 2.4 0.4 4.3 5.4 1.3 3.2 2.5 5.9 2.8 5.9 0.3 0 1.9 -2.6 3.5 -5.8 2.1 -4 3.7 -5.8 5.3 -6 1.2 -0.2 2.2 -0.1 2.2 0.3 0 0.5 -10.8 21.5 -11.5 22.3 -0.1 0.1 -0.8 -1 -1.5 -2.5z"
|
|
33
|
-
/>
|
|
34
|
-
<path
|
|
35
|
-
d="M84.5 201.3 c1 -12.9 1.6 -13.8 4.8 -6.9 1.5 3.1 2.9 5.6 3.3 5.6 0.3 0 3.1 -2.2 6.2 -5 3 -2.7 5.7 -4.8 5.9 -4.7 0.3 0.4 3.1 19.1 2.9 19.4 -0.1 0.2 -1.3 0.4 -2.7 0.5 -2.2 0.3 -2.4 0 -2.7 -4.9 -0.2 -2.9 -0.6 -5.3 -0.9 -5.3 -0.3 0 -2.3 1.4 -4.4 3.1 -3.7 2.9 -4.1 3 -5.8 1.5 -1.7 -1.6 -1.9 -1.6 -2.5 -0.1 -0.3 0.9 -0.6 2.8 -0.6 4.1 0 1.8 -0.5 2.4 -2.1 2.4 -2 0 -2.1 -0.3 -1.4 -9.7z"
|
|
36
|
-
/>
|
|
37
|
-
<path
|
|
38
|
-
d="M115.9 209.5 c-5.9 -3.2 -6.4 -12.8 -0.9 -17.7 3.4 -3.1 5.6 -3.4 10.3 -1.5 4.3 1.8 5.9 4 6 7.9 0.1 6.4 -1.2 10.4 -3.9 11.6 -3.3 1.6 -8.3 1.4 -11.5 -0.3z m9.6 -4.5 c2.1 -2.4 2.4 -7.2 0.5 -9.4 -2 -2.5 -7.7 -2.1 -9.1 0.6 -3.9 7.3 3.3 14.7 8.6 8.8z"
|
|
39
|
-
/>
|
|
40
|
-
<path
|
|
41
|
-
d="M158 200 c0 -11 0 -11 2.4 -11 2.3 0 2.4 0.3 1.9 3.6 -0.6 3.5 -0.5 3.6 1.6 2.5 1.2 -0.7 2.7 -2 3.3 -3.1 0.9 -1.4 1.5 -1.6 2.6 -0.8 1 0.9 0.6 1.9 -2.5 5 l-3.8 3.9 2.5 1.9 c1.4 1.1 3.7 2.5 5.3 3.2 3 1.4 3.3 2.1 1.4 3.6 -0.9 0.8 -2.4 0.3 -5.7 -1.9 l-4.5 -3.1 -0.3 3.6 c-0.3 2.8 -0.8 3.6 -2.3 3.6 -1.8 0 -1.9 -0.8 -1.9 -11z"
|
|
42
|
-
/>
|
|
43
|
-
</g>
|
|
44
|
-
<circle
|
|
45
|
-
cx="128"
|
|
46
|
-
cy="128"
|
|
47
|
-
r="118"
|
|
48
|
-
stroke="white"
|
|
49
|
-
stroke-width="1.5"
|
|
50
|
-
fill="none"
|
|
51
|
-
opacity="0.15"
|
|
52
|
-
/>
|
|
53
|
-
</svg>
|
|
54
|
-
|
|
55
|
-
<p class="font-medium text-highlighted">{{ header.title }}</p>
|
|
6
|
+
<NuxtLink :to="header.to">
|
|
7
|
+
<UUser :avatar="{ src: header.avatar }" :name="header.title" />
|
|
56
8
|
</NuxtLink>
|
|
57
9
|
</template>
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { omit } from '@movk/core'
|
|
3
3
|
import colors from 'tailwindcss/colors'
|
|
4
|
+
import { useClipboard } from '@vueuse/core'
|
|
5
|
+
import { themeIcons } from '../../utils/theme'
|
|
4
6
|
|
|
5
7
|
const appConfig = useAppConfig()
|
|
6
8
|
const colorMode = useColorMode()
|
|
7
9
|
const site = useSiteConfig()
|
|
8
10
|
|
|
11
|
+
const { track } = useAnalytics()
|
|
12
|
+
|
|
13
|
+
const open = ref(false)
|
|
14
|
+
|
|
15
|
+
watch(open, (isOpen) => {
|
|
16
|
+
if (isOpen && appConfig.vercelAnalytics) {
|
|
17
|
+
track('Theme Picker Opened')
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const { copy: copyCSS, copied: copiedCSS } = useClipboard()
|
|
22
|
+
const { copy: copyAppConfig, copied: copiedAppConfig } = useClipboard()
|
|
23
|
+
|
|
9
24
|
const neutralColors = ['slate', 'gray', 'zinc', 'neutral', 'stone']
|
|
10
25
|
const neutral = computed({
|
|
11
26
|
get() {
|
|
@@ -14,6 +29,7 @@ const neutral = computed({
|
|
|
14
29
|
set(option) {
|
|
15
30
|
appConfig.ui.colors.neutral = option
|
|
16
31
|
window.localStorage.setItem(`${site.name}-ui-neutral`, appConfig.ui.colors.neutral)
|
|
32
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'neutral', value: option })
|
|
17
33
|
}
|
|
18
34
|
})
|
|
19
35
|
|
|
@@ -27,6 +43,7 @@ const primary = computed({
|
|
|
27
43
|
appConfig.ui.colors.primary = option
|
|
28
44
|
window.localStorage.setItem(`${site.name}-ui-primary`, appConfig.ui.colors.primary)
|
|
29
45
|
setBlackAsPrimary(false)
|
|
46
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'primary', value: option })
|
|
30
47
|
}
|
|
31
48
|
})
|
|
32
49
|
|
|
@@ -38,6 +55,7 @@ const radius = computed({
|
|
|
38
55
|
set(option) {
|
|
39
56
|
appConfig.theme.radius = option
|
|
40
57
|
window.localStorage.setItem(`${site.name}-ui-radius`, String(appConfig.theme.radius))
|
|
58
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'radius', value: option })
|
|
41
59
|
}
|
|
42
60
|
})
|
|
43
61
|
|
|
@@ -52,18 +70,155 @@ const mode = computed({
|
|
|
52
70
|
},
|
|
53
71
|
set(option) {
|
|
54
72
|
colorMode.preference = option
|
|
73
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'color mode', value: option })
|
|
55
74
|
}
|
|
56
75
|
})
|
|
57
76
|
|
|
58
77
|
function setBlackAsPrimary(value: boolean) {
|
|
59
78
|
appConfig.theme.blackAsPrimary = value
|
|
60
79
|
window.localStorage.setItem(`${site.name}-ui-black-as-primary`, String(value))
|
|
80
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'black as primary', value })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const fonts = ['Public Sans', 'DM Sans', 'Geist', 'Inter', 'Poppins', 'Outfit', 'Raleway']
|
|
84
|
+
const font = computed({
|
|
85
|
+
get() {
|
|
86
|
+
return appConfig.theme.font
|
|
87
|
+
},
|
|
88
|
+
set(option) {
|
|
89
|
+
appConfig.theme.font = option
|
|
90
|
+
window.localStorage.setItem(`${site.name}-ui-font`, appConfig.theme.font)
|
|
91
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'font', value: option })
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const icons = [{
|
|
96
|
+
label: 'Lucide',
|
|
97
|
+
icon: 'i-lucide-feather',
|
|
98
|
+
value: 'lucide'
|
|
99
|
+
}, {
|
|
100
|
+
label: 'Phosphor',
|
|
101
|
+
icon: 'i-ph-phosphor-logo',
|
|
102
|
+
value: 'phosphor'
|
|
103
|
+
}, {
|
|
104
|
+
label: 'Tabler',
|
|
105
|
+
icon: 'i-tabler-brand-tabler',
|
|
106
|
+
value: 'tabler'
|
|
107
|
+
}]
|
|
108
|
+
const icon = computed({
|
|
109
|
+
get() {
|
|
110
|
+
return appConfig.theme.icons
|
|
111
|
+
},
|
|
112
|
+
set(option) {
|
|
113
|
+
appConfig.theme.icons = option
|
|
114
|
+
appConfig.ui.icons = themeIcons[option as keyof typeof themeIcons] as any
|
|
115
|
+
window.localStorage.setItem(`${site.name}-ui-icons`, appConfig.theme.icons)
|
|
116
|
+
if (appConfig.vercelAnalytics) track('Theme Changed', { setting: 'icons', value: option })
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const hasCSSChanges = computed(() => {
|
|
121
|
+
return appConfig.theme.radius !== 0.25
|
|
122
|
+
|| appConfig.theme.blackAsPrimary
|
|
123
|
+
|| appConfig.theme.font !== 'Public Sans'
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const hasAppConfigChanges = computed(() => {
|
|
127
|
+
return appConfig.ui.colors.primary !== 'green'
|
|
128
|
+
|| appConfig.ui.colors.neutral !== 'slate'
|
|
129
|
+
|| appConfig.theme.icons !== 'lucide'
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
function exportCSS() {
|
|
133
|
+
if (appConfig.vercelAnalytics) track('Theme Exported', { type: 'css' })
|
|
134
|
+
|
|
135
|
+
const lines = [
|
|
136
|
+
'@import "tailwindcss";',
|
|
137
|
+
'@import "@nuxt/ui";'
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
if (appConfig.theme.font !== 'Public Sans') {
|
|
141
|
+
lines.push('', '@theme {', ` --font-sans: '${appConfig.theme.font}', sans-serif;`, '}')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const rootLines: string[] = []
|
|
145
|
+
if (appConfig.theme.radius !== 0.25) {
|
|
146
|
+
rootLines.push(` --ui-radius: ${appConfig.theme.radius}rem;`)
|
|
147
|
+
}
|
|
148
|
+
if (appConfig.theme.blackAsPrimary) {
|
|
149
|
+
rootLines.push(' --ui-primary: black;')
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (rootLines.length) {
|
|
153
|
+
lines.push('', ':root {', ...rootLines, '}')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (appConfig.theme.blackAsPrimary) {
|
|
157
|
+
lines.push('', '.dark {', ' --ui-primary: white;', '}')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
copyCSS(lines.join('\n'))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function exportAppConfig() {
|
|
164
|
+
if (appConfig.vercelAnalytics) track('Theme Exported', { type: 'appConfig' })
|
|
165
|
+
|
|
166
|
+
const config: Record<string, any> = {}
|
|
167
|
+
|
|
168
|
+
if (appConfig.ui.colors.primary !== 'green' || appConfig.ui.colors.neutral !== 'slate') {
|
|
169
|
+
config.ui = { colors: {} }
|
|
170
|
+
if (appConfig.ui.colors.primary !== 'green') {
|
|
171
|
+
config.ui.colors.primary = appConfig.ui.colors.primary
|
|
172
|
+
}
|
|
173
|
+
if (appConfig.ui.colors.neutral !== 'slate') {
|
|
174
|
+
config.ui.colors.neutral = appConfig.ui.colors.neutral
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (appConfig.theme.icons !== 'lucide') {
|
|
179
|
+
const iconSet = appConfig.theme.icons
|
|
180
|
+
const icons = themeIcons[iconSet as keyof typeof themeIcons]
|
|
181
|
+
config.ui = config.ui || {}
|
|
182
|
+
config.ui.icons = icons
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const configString = JSON.stringify(config, null, 2)
|
|
186
|
+
.replace(/"([^"]+)":/g, '$1:')
|
|
187
|
+
.replace(/"/g, '\'')
|
|
188
|
+
|
|
189
|
+
const output = `export default defineAppConfig(${configString})`
|
|
190
|
+
|
|
191
|
+
copyAppConfig(output)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function resetTheme() {
|
|
195
|
+
if (appConfig.vercelAnalytics) track('Theme Reset')
|
|
196
|
+
|
|
197
|
+
// Reset without triggering individual tracking events
|
|
198
|
+
appConfig.ui.colors.primary = 'green'
|
|
199
|
+
window.localStorage.removeItem(`${site.name}-ui-primary`)
|
|
200
|
+
|
|
201
|
+
appConfig.ui.colors.neutral = 'slate'
|
|
202
|
+
window.localStorage.removeItem(`${site.name}-ui-neutral`)
|
|
203
|
+
|
|
204
|
+
appConfig.theme.radius = 0.25
|
|
205
|
+
window.localStorage.removeItem(`${site.name}-ui-radius`)
|
|
206
|
+
|
|
207
|
+
appConfig.theme.font = 'Public Sans'
|
|
208
|
+
window.localStorage.removeItem(`${site.name}-ui-font`)
|
|
209
|
+
|
|
210
|
+
appConfig.theme.icons = 'lucide'
|
|
211
|
+
appConfig.ui.icons = themeIcons.lucide as any
|
|
212
|
+
window.localStorage.removeItem(`${site.name}-ui-icons`)
|
|
213
|
+
|
|
214
|
+
appConfig.theme.blackAsPrimary = false
|
|
215
|
+
window.localStorage.removeItem(`${site.name}-ui-black-as-primary`)
|
|
61
216
|
}
|
|
62
217
|
</script>
|
|
63
218
|
|
|
64
219
|
<template>
|
|
65
|
-
<UPopover :ui="{ content: 'w-72 px-6 py-4 flex flex-col gap-4' }">
|
|
66
|
-
<template #default
|
|
220
|
+
<UPopover v-model:open="open" :ui="{ content: 'w-72 px-6 py-4 flex flex-col gap-4 overflow-y-auto max-h-[calc(100vh-5rem)]' }">
|
|
221
|
+
<template #default>
|
|
67
222
|
<UButton
|
|
68
223
|
icon="i-lucide-swatch-book"
|
|
69
224
|
color="neutral"
|
|
@@ -76,8 +231,19 @@ function setBlackAsPrimary(value: boolean) {
|
|
|
76
231
|
|
|
77
232
|
<template #content>
|
|
78
233
|
<fieldset>
|
|
79
|
-
<legend class="text-[11px] leading-none font-semibold mb-2">
|
|
234
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none flex items-center gap-1">
|
|
80
235
|
Primary
|
|
236
|
+
|
|
237
|
+
<UButton
|
|
238
|
+
to="https://ui.nuxt.com/docs/getting-started/theme/css-variables#colors"
|
|
239
|
+
size="xs"
|
|
240
|
+
color="neutral"
|
|
241
|
+
variant="link"
|
|
242
|
+
target="_blank"
|
|
243
|
+
icon="i-lucide-circle-help"
|
|
244
|
+
class="p-0 -my-0.5"
|
|
245
|
+
:ui="{ leadingIcon: 'size-3' }"
|
|
246
|
+
/>
|
|
81
247
|
</legend>
|
|
82
248
|
|
|
83
249
|
<div class="grid grid-cols-3 gap-1 -mx-2">
|
|
@@ -99,8 +265,19 @@ function setBlackAsPrimary(value: boolean) {
|
|
|
99
265
|
</fieldset>
|
|
100
266
|
|
|
101
267
|
<fieldset>
|
|
102
|
-
<legend class="text-[11px] leading-none font-semibold mb-2">
|
|
268
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none flex items-center gap-1">
|
|
103
269
|
Neutral
|
|
270
|
+
|
|
271
|
+
<UButton
|
|
272
|
+
to="https://ui.nuxt.com/docs/getting-started/theme/css-variables#text"
|
|
273
|
+
size="xs"
|
|
274
|
+
color="neutral"
|
|
275
|
+
variant="link"
|
|
276
|
+
target="_blank"
|
|
277
|
+
icon="i-lucide-circle-help"
|
|
278
|
+
class="p-0 -my-0.5"
|
|
279
|
+
:ui="{ leadingIcon: 'size-3' }"
|
|
280
|
+
/>
|
|
104
281
|
</legend>
|
|
105
282
|
|
|
106
283
|
<div class="grid grid-cols-3 gap-1 -mx-2">
|
|
@@ -116,8 +293,19 @@ function setBlackAsPrimary(value: boolean) {
|
|
|
116
293
|
</fieldset>
|
|
117
294
|
|
|
118
295
|
<fieldset>
|
|
119
|
-
<legend class="text-[11px] leading-none font-semibold mb-2">
|
|
296
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none flex items-center gap-1">
|
|
120
297
|
Radius
|
|
298
|
+
|
|
299
|
+
<UButton
|
|
300
|
+
to="https://ui.nuxt.com/docs/getting-started/theme/css-variables#radius"
|
|
301
|
+
size="xs"
|
|
302
|
+
color="neutral"
|
|
303
|
+
variant="link"
|
|
304
|
+
target="_blank"
|
|
305
|
+
icon="i-lucide-circle-help"
|
|
306
|
+
class="p-0 -my-0.5"
|
|
307
|
+
:ui="{ leadingIcon: 'size-3' }"
|
|
308
|
+
/>
|
|
121
309
|
</legend>
|
|
122
310
|
|
|
123
311
|
<div class="grid grid-cols-5 gap-1 -mx-2">
|
|
@@ -133,8 +321,77 @@ function setBlackAsPrimary(value: boolean) {
|
|
|
133
321
|
</fieldset>
|
|
134
322
|
|
|
135
323
|
<fieldset>
|
|
136
|
-
<legend class="text-[11px] leading-none font-semibold mb-2">
|
|
137
|
-
|
|
324
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none flex items-center gap-1">
|
|
325
|
+
Font
|
|
326
|
+
|
|
327
|
+
<UButton
|
|
328
|
+
to="https://ui.nuxt.com/docs/getting-started/integrations/fonts"
|
|
329
|
+
size="xs"
|
|
330
|
+
color="neutral"
|
|
331
|
+
variant="link"
|
|
332
|
+
target="_blank"
|
|
333
|
+
icon="i-lucide-circle-help"
|
|
334
|
+
class="p-0 -my-0.5"
|
|
335
|
+
:ui="{ leadingIcon: 'size-3' }"
|
|
336
|
+
/>
|
|
337
|
+
</legend>
|
|
338
|
+
|
|
339
|
+
<div class="-mx-2">
|
|
340
|
+
<USelect
|
|
341
|
+
v-model="font"
|
|
342
|
+
size="sm"
|
|
343
|
+
color="neutral"
|
|
344
|
+
icon="i-lucide-type"
|
|
345
|
+
:items="fonts"
|
|
346
|
+
class="w-full ring-default rounded-sm hover:bg-elevated/50 text-[11px] data-[state=open]:bg-elevated/50"
|
|
347
|
+
:ui="{ trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200' }"
|
|
348
|
+
/>
|
|
349
|
+
</div>
|
|
350
|
+
</fieldset>
|
|
351
|
+
|
|
352
|
+
<fieldset>
|
|
353
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none flex items-center gap-1">
|
|
354
|
+
Icons
|
|
355
|
+
|
|
356
|
+
<UButton
|
|
357
|
+
to="https://ui.nuxt.com/docs/getting-started/integrations/icons"
|
|
358
|
+
size="xs"
|
|
359
|
+
color="neutral"
|
|
360
|
+
variant="link"
|
|
361
|
+
target="_blank"
|
|
362
|
+
icon="i-lucide-circle-help"
|
|
363
|
+
class="p-0 -my-0.5"
|
|
364
|
+
:ui="{ leadingIcon: 'size-3' }"
|
|
365
|
+
/>
|
|
366
|
+
</legend>
|
|
367
|
+
|
|
368
|
+
<div class="-mx-2">
|
|
369
|
+
<USelect
|
|
370
|
+
v-model="icon"
|
|
371
|
+
size="sm"
|
|
372
|
+
color="neutral"
|
|
373
|
+
:icon="icons.find(i => i.value === icon)?.icon"
|
|
374
|
+
:items="icons"
|
|
375
|
+
class="w-full ring-default rounded-sm hover:bg-elevated/50 capitalize text-[11px] data-[state=open]:bg-elevated/50"
|
|
376
|
+
:ui="{ item: 'capitalize text-[11px]', trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200' }"
|
|
377
|
+
/>
|
|
378
|
+
</div>
|
|
379
|
+
</fieldset>
|
|
380
|
+
|
|
381
|
+
<fieldset>
|
|
382
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none flex items-center gap-1">
|
|
383
|
+
Color Mode
|
|
384
|
+
|
|
385
|
+
<UButton
|
|
386
|
+
to="https://ui.nuxt.com/docs/getting-started/integrations/color-mode"
|
|
387
|
+
size="xs"
|
|
388
|
+
color="neutral"
|
|
389
|
+
variant="link"
|
|
390
|
+
target="_blank"
|
|
391
|
+
icon="i-lucide-circle-help"
|
|
392
|
+
class="p-0 -my-0.5"
|
|
393
|
+
:ui="{ leadingIcon: 'size-3' }"
|
|
394
|
+
/>
|
|
138
395
|
</legend>
|
|
139
396
|
|
|
140
397
|
<div class="grid grid-cols-3 gap-1 -mx-2">
|
|
@@ -147,6 +404,45 @@ function setBlackAsPrimary(value: boolean) {
|
|
|
147
404
|
/>
|
|
148
405
|
</div>
|
|
149
406
|
</fieldset>
|
|
407
|
+
|
|
408
|
+
<fieldset v-if="hasCSSChanges || hasAppConfigChanges">
|
|
409
|
+
<legend class="text-[11px] leading-none font-semibold mb-2 select-none">
|
|
410
|
+
Export
|
|
411
|
+
</legend>
|
|
412
|
+
|
|
413
|
+
<div class="flex items-center justify-between gap-1 -mx-2">
|
|
414
|
+
<UButton
|
|
415
|
+
v-if="hasCSSChanges"
|
|
416
|
+
color="neutral"
|
|
417
|
+
variant="soft"
|
|
418
|
+
size="sm"
|
|
419
|
+
label="main.css"
|
|
420
|
+
class="flex-1 text-[11px]"
|
|
421
|
+
:icon="copiedCSS ? 'i-lucide-copy-check' : 'i-lucide-copy'"
|
|
422
|
+
@click="exportCSS"
|
|
423
|
+
/>
|
|
424
|
+
<UButton
|
|
425
|
+
v-if="hasAppConfigChanges"
|
|
426
|
+
color="neutral"
|
|
427
|
+
variant="soft"
|
|
428
|
+
size="sm"
|
|
429
|
+
label="app.config.ts"
|
|
430
|
+
:icon="copiedAppConfig ? 'i-lucide-copy-check' : 'i-lucide-copy'"
|
|
431
|
+
class="flex-1 text-[11px]"
|
|
432
|
+
@click="exportAppConfig"
|
|
433
|
+
/>
|
|
434
|
+
<UTooltip text="Reset theme">
|
|
435
|
+
<UButton
|
|
436
|
+
color="neutral"
|
|
437
|
+
variant="outline"
|
|
438
|
+
size="sm"
|
|
439
|
+
icon="i-lucide-rotate-ccw"
|
|
440
|
+
class="ms-auto ring-default hover:bg-elevated/50"
|
|
441
|
+
@click="resetTheme"
|
|
442
|
+
/>
|
|
443
|
+
</UTooltip>
|
|
444
|
+
</div>
|
|
445
|
+
</fieldset>
|
|
150
446
|
</template>
|
|
151
447
|
</UPopover>
|
|
152
448
|
</template>
|
package/app/error.vue
CHANGED
|
@@ -18,6 +18,7 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
|
|
|
18
18
|
const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
|
|
19
19
|
const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
|
|
20
20
|
const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
|
|
21
|
+
const font = computed(() => `:root { --font-sans: '${appConfig.theme.font}', sans-serif; }`)
|
|
21
22
|
|
|
22
23
|
useHead({
|
|
23
24
|
meta: [
|
|
@@ -26,7 +27,8 @@ useHead({
|
|
|
26
27
|
],
|
|
27
28
|
style: [
|
|
28
29
|
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 },
|
|
29
|
-
{ innerHTML: blackAsPrimary, id: 'nuxt-ui-black-as-primary', tagPriority: -2 }
|
|
30
|
+
{ innerHTML: blackAsPrimary, id: 'nuxt-ui-black-as-primary', tagPriority: -2 },
|
|
31
|
+
{ innerHTML: font, id: 'nuxt-ui-font', tagPriority: -2 }
|
|
30
32
|
]
|
|
31
33
|
})
|
|
32
34
|
|
package/app/plugins/theme.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { themeIcons } from '../utils/theme'
|
|
2
|
+
|
|
1
3
|
export default defineNuxtPlugin({
|
|
2
4
|
enforce: 'post',
|
|
3
5
|
setup() {
|
|
@@ -26,12 +28,32 @@ export default defineNuxtPlugin({
|
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
function updateFont() {
|
|
32
|
+
const font = localStorage.getItem(`${site.name}-ui-font`)
|
|
33
|
+
if (font) {
|
|
34
|
+
appConfig.theme.font = font
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
updateColor('primary')
|
|
30
39
|
updateColor('neutral')
|
|
31
40
|
updateRadius()
|
|
32
41
|
updateBlackAsPrimary()
|
|
42
|
+
updateFont()
|
|
33
43
|
}
|
|
34
44
|
|
|
45
|
+
onNuxtReady(() => {
|
|
46
|
+
function updateIcons() {
|
|
47
|
+
const icons = localStorage.getItem(`${site.name}-ui-icons`)
|
|
48
|
+
if (icons) {
|
|
49
|
+
appConfig.theme.icons = icons
|
|
50
|
+
appConfig.ui.icons = themeIcons[icons as keyof typeof themeIcons] as any
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
updateIcons()
|
|
55
|
+
})
|
|
56
|
+
|
|
35
57
|
if (import.meta.server) {
|
|
36
58
|
useHead({
|
|
37
59
|
script: [{
|
|
@@ -75,6 +97,13 @@ export default defineNuxtPlugin({
|
|
|
75
97
|
document.querySelector('style#nuxt-ui-black-as-primary').innerHTML = '';
|
|
76
98
|
}
|
|
77
99
|
`.replace(/\s+/g, ' ')
|
|
100
|
+
}, {
|
|
101
|
+
innerHTML: `
|
|
102
|
+
if (localStorage.getItem('${site.name}-ui-font')) {
|
|
103
|
+
const font = localStorage.getItem('${site.name}-ui-font');
|
|
104
|
+
document.querySelector('style#nuxt-ui-font').innerHTML = ':root { --font-sans: \\'' + font + '\\', sans-serif; }';
|
|
105
|
+
}
|
|
106
|
+
`.replace(/\s+/g, ' ')
|
|
78
107
|
}]
|
|
79
108
|
})
|
|
80
109
|
}
|
package/app/types/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ButtonProps } from '@nuxt/ui'
|
|
|
2
2
|
|
|
3
3
|
declare module 'nuxt/schema' {
|
|
4
4
|
interface AppConfig {
|
|
5
|
+
vercelAnalytics: boolean
|
|
5
6
|
seo: {
|
|
6
7
|
titleTemplate: string
|
|
7
8
|
title: string
|
|
@@ -9,6 +10,7 @@ declare module 'nuxt/schema' {
|
|
|
9
10
|
}
|
|
10
11
|
header: {
|
|
11
12
|
title: string
|
|
13
|
+
avatar: string
|
|
12
14
|
to: string
|
|
13
15
|
search: boolean
|
|
14
16
|
colorMode: boolean
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export const themeIcons = {
|
|
2
|
+
lucide: {
|
|
3
|
+
arrowDown: 'i-lucide-arrow-down',
|
|
4
|
+
arrowLeft: 'i-lucide-arrow-left',
|
|
5
|
+
arrowRight: 'i-lucide-arrow-right',
|
|
6
|
+
arrowUp: 'i-lucide-arrow-up',
|
|
7
|
+
caution: 'i-lucide-circle-alert',
|
|
8
|
+
check: 'i-lucide-check',
|
|
9
|
+
chevronDoubleLeft: 'i-lucide-chevrons-left',
|
|
10
|
+
chevronDoubleRight: 'i-lucide-chevrons-right',
|
|
11
|
+
chevronDown: 'i-lucide-chevron-down',
|
|
12
|
+
chevronLeft: 'i-lucide-chevron-left',
|
|
13
|
+
chevronRight: 'i-lucide-chevron-right',
|
|
14
|
+
chevronUp: 'i-lucide-chevron-up',
|
|
15
|
+
close: 'i-lucide-x',
|
|
16
|
+
copy: 'i-lucide-copy',
|
|
17
|
+
copyCheck: 'i-lucide-copy-check',
|
|
18
|
+
dark: 'i-lucide-moon',
|
|
19
|
+
drag: 'i-lucide-grip-vertical',
|
|
20
|
+
ellipsis: 'i-lucide-ellipsis',
|
|
21
|
+
error: 'i-lucide-circle-x',
|
|
22
|
+
external: 'i-lucide-arrow-up-right',
|
|
23
|
+
eye: 'i-lucide-eye',
|
|
24
|
+
eyeOff: 'i-lucide-eye-off',
|
|
25
|
+
file: 'i-lucide-file',
|
|
26
|
+
folder: 'i-lucide-folder',
|
|
27
|
+
folderOpen: 'i-lucide-folder-open',
|
|
28
|
+
hash: 'i-lucide-hash',
|
|
29
|
+
info: 'i-lucide-info',
|
|
30
|
+
light: 'i-lucide-sun',
|
|
31
|
+
loading: 'i-lucide-loader-circle',
|
|
32
|
+
menu: 'i-lucide-menu',
|
|
33
|
+
minus: 'i-lucide-minus',
|
|
34
|
+
panelClose: 'i-lucide-panel-left-close',
|
|
35
|
+
panelOpen: 'i-lucide-panel-left-open',
|
|
36
|
+
plus: 'i-lucide-plus',
|
|
37
|
+
reload: 'i-lucide-rotate-ccw',
|
|
38
|
+
search: 'i-lucide-search',
|
|
39
|
+
stop: 'i-lucide-square',
|
|
40
|
+
success: 'i-lucide-circle-check',
|
|
41
|
+
system: 'i-lucide-monitor',
|
|
42
|
+
tip: 'i-lucide-lightbulb',
|
|
43
|
+
upload: 'i-lucide-upload',
|
|
44
|
+
warning: 'i-lucide-triangle-alert'
|
|
45
|
+
},
|
|
46
|
+
phosphor: {
|
|
47
|
+
arrowDown: 'i-ph-arrow-down',
|
|
48
|
+
arrowLeft: 'i-ph-arrow-left',
|
|
49
|
+
arrowRight: 'i-ph-arrow-right',
|
|
50
|
+
arrowUp: 'i-ph-arrow-up',
|
|
51
|
+
caution: 'i-ph-warning-circle',
|
|
52
|
+
check: 'i-ph-check',
|
|
53
|
+
chevronDoubleLeft: 'i-ph-caret-double-left',
|
|
54
|
+
chevronDoubleRight: 'i-ph-caret-double-right',
|
|
55
|
+
chevronDown: 'i-ph-caret-down',
|
|
56
|
+
chevronLeft: 'i-ph-caret-left',
|
|
57
|
+
chevronRight: 'i-ph-caret-right',
|
|
58
|
+
chevronUp: 'i-ph-caret-up',
|
|
59
|
+
close: 'i-ph-x',
|
|
60
|
+
copy: 'i-ph-copy',
|
|
61
|
+
copyCheck: 'i-ph-check-circle',
|
|
62
|
+
dark: 'i-ph-moon',
|
|
63
|
+
drag: 'i-ph-dots-six-vertical',
|
|
64
|
+
ellipsis: 'i-ph-dots-three',
|
|
65
|
+
error: 'i-ph-x-circle',
|
|
66
|
+
external: 'i-ph-arrow-up-right',
|
|
67
|
+
eye: 'i-ph-eye',
|
|
68
|
+
eyeOff: 'i-ph-eye-slash',
|
|
69
|
+
file: 'i-ph-file',
|
|
70
|
+
folder: 'i-ph-folder',
|
|
71
|
+
folderOpen: 'i-ph-folder-open',
|
|
72
|
+
hash: 'i-ph-hash',
|
|
73
|
+
info: 'i-ph-info',
|
|
74
|
+
light: 'i-ph-sun',
|
|
75
|
+
loading: 'i-ph-circle-notch',
|
|
76
|
+
menu: 'i-ph-list',
|
|
77
|
+
minus: 'i-ph-minus',
|
|
78
|
+
panelClose: 'i-ph-caret-left',
|
|
79
|
+
panelOpen: 'i-ph-caret-right',
|
|
80
|
+
plus: 'i-ph-plus',
|
|
81
|
+
reload: 'i-ph-arrow-counter-clockwise',
|
|
82
|
+
search: 'i-ph-magnifying-glass',
|
|
83
|
+
stop: 'i-ph-square',
|
|
84
|
+
success: 'i-ph-check-circle',
|
|
85
|
+
system: 'i-ph-monitor',
|
|
86
|
+
tip: 'i-ph-lightbulb',
|
|
87
|
+
upload: 'i-ph-upload',
|
|
88
|
+
warning: 'i-ph-warning'
|
|
89
|
+
},
|
|
90
|
+
tabler: {
|
|
91
|
+
arrowDown: 'i-tabler-arrow-down',
|
|
92
|
+
arrowLeft: 'i-tabler-arrow-left',
|
|
93
|
+
arrowRight: 'i-tabler-arrow-right',
|
|
94
|
+
arrowUp: 'i-tabler-arrow-up',
|
|
95
|
+
caution: 'i-tabler-alert-square-rounded',
|
|
96
|
+
check: 'i-tabler-check',
|
|
97
|
+
chevronDoubleLeft: 'i-tabler-chevrons-left',
|
|
98
|
+
chevronDoubleRight: 'i-tabler-chevrons-right',
|
|
99
|
+
chevronDown: 'i-tabler-chevron-down',
|
|
100
|
+
chevronLeft: 'i-tabler-chevron-left',
|
|
101
|
+
chevronRight: 'i-tabler-chevron-right',
|
|
102
|
+
chevronUp: 'i-tabler-chevron-up',
|
|
103
|
+
close: 'i-tabler-x',
|
|
104
|
+
copy: 'i-tabler-copy',
|
|
105
|
+
copyCheck: 'i-tabler-copy-check',
|
|
106
|
+
dark: 'i-tabler-moon',
|
|
107
|
+
drag: 'i-tabler-grip-vertical',
|
|
108
|
+
ellipsis: 'i-tabler-dots',
|
|
109
|
+
error: 'i-tabler-square-rounded-x',
|
|
110
|
+
external: 'i-tabler-external-link',
|
|
111
|
+
eye: 'i-tabler-eye',
|
|
112
|
+
eyeOff: 'i-tabler-eye-off',
|
|
113
|
+
file: 'i-tabler-file',
|
|
114
|
+
folder: 'i-tabler-folder',
|
|
115
|
+
folderOpen: 'i-tabler-folder-open',
|
|
116
|
+
hash: 'i-tabler-hash',
|
|
117
|
+
info: 'i-tabler-info-square-rounded',
|
|
118
|
+
light: 'i-tabler-sun',
|
|
119
|
+
loading: 'i-tabler-loader-2',
|
|
120
|
+
menu: 'i-tabler-menu',
|
|
121
|
+
minus: 'i-tabler-minus',
|
|
122
|
+
panelClose: 'i-tabler-layout-sidebar-left-collapse',
|
|
123
|
+
panelOpen: 'i-tabler-layout-sidebar-left-expand',
|
|
124
|
+
plus: 'i-tabler-plus',
|
|
125
|
+
reload: 'i-tabler-reload',
|
|
126
|
+
search: 'i-tabler-search',
|
|
127
|
+
stop: 'i-tabler-player-stop',
|
|
128
|
+
success: 'i-tabler-square-rounded-check',
|
|
129
|
+
system: 'i-tabler-device-desktop',
|
|
130
|
+
tip: 'i-tabler-bulb',
|
|
131
|
+
upload: 'i-tabler-upload',
|
|
132
|
+
warning: 'i-tabler-alert-triangle'
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export type ThemeIcons = keyof typeof themeIcons
|
package/nuxt.config.ts
CHANGED
|
@@ -73,6 +73,17 @@ export default defineNuxtConfig({
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
|
+
fonts: {
|
|
77
|
+
families: [
|
|
78
|
+
{ name: 'Public Sans', provider: 'google', global: true },
|
|
79
|
+
{ name: 'DM Sans', provider: 'google', global: true },
|
|
80
|
+
{ name: 'Geist', provider: 'google', global: true },
|
|
81
|
+
{ name: 'Inter', provider: 'google', global: true },
|
|
82
|
+
{ name: 'Poppins', provider: 'google', global: true },
|
|
83
|
+
{ name: 'Outfit', provider: 'google', global: true },
|
|
84
|
+
{ name: 'Raleway', provider: 'google', global: true }
|
|
85
|
+
]
|
|
86
|
+
},
|
|
76
87
|
icon: {
|
|
77
88
|
provider: 'iconify'
|
|
78
89
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@movk/nuxt-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "An elegant documentation theme for Nuxt, powered by Nuxt UI and Nuxt Content.",
|
|
7
7
|
"author": "YiXuan <mhaibaraai@gmail.com>",
|
|
@@ -27,16 +27,18 @@
|
|
|
27
27
|
"README.md"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@iconify-json/lucide": "^1.2.
|
|
31
|
-
"@iconify-json/simple-icons": "^1.2.
|
|
30
|
+
"@iconify-json/lucide": "^1.2.81",
|
|
31
|
+
"@iconify-json/simple-icons": "^1.2.63",
|
|
32
32
|
"@iconify-json/vscode-icons": "^1.2.37",
|
|
33
33
|
"@movk/core": "^1.0.2",
|
|
34
34
|
"@nuxt/content": "^3.9.0",
|
|
35
35
|
"@nuxt/image": "^2.0.0",
|
|
36
36
|
"@nuxt/kit": "^4.2.2",
|
|
37
|
-
"@nuxt/ui": "^4.
|
|
38
|
-
"@nuxtjs/seo": "^3.
|
|
37
|
+
"@nuxt/ui": "^4.3.0",
|
|
38
|
+
"@nuxtjs/seo": "^3.3.0",
|
|
39
39
|
"@octokit/rest": "^22.0.1",
|
|
40
|
+
"@vercel/analytics": "^1.6.1",
|
|
41
|
+
"@vercel/speed-insights": "^1.3.1",
|
|
40
42
|
"@vueuse/core": "^14.1.0",
|
|
41
43
|
"@vueuse/nuxt": "^14.1.0",
|
|
42
44
|
"defu": "^6.1.4",
|
|
@@ -50,7 +52,7 @@
|
|
|
50
52
|
"pkg-types": "^2.3.0",
|
|
51
53
|
"prettier": "^3.7.4",
|
|
52
54
|
"scule": "^1.3.0",
|
|
53
|
-
"tailwindcss": "^4.1.
|
|
55
|
+
"tailwindcss": "^4.1.18",
|
|
54
56
|
"ufo": "^1.6.1"
|
|
55
57
|
}
|
|
56
58
|
}
|
|
@@ -5,8 +5,10 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
5
5
|
return []
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const { path, author } = getQuery(event) as { path: string, author
|
|
9
|
-
|
|
8
|
+
const { path, author } = getQuery(event) as { path: string | string[], author: string }
|
|
9
|
+
const paths = Array.isArray(path) ? path : [path]
|
|
10
|
+
|
|
11
|
+
if (!paths.length || !paths[0]) {
|
|
10
12
|
throw createError({
|
|
11
13
|
statusCode: 400,
|
|
12
14
|
statusMessage: 'Path is required'
|
|
@@ -15,26 +17,41 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
15
17
|
|
|
16
18
|
const { github } = useAppConfig()
|
|
17
19
|
const octokit = new Octokit({ auth: process.env.NUXT_GITHUB_TOKEN })
|
|
18
|
-
const commits = await octokit.paginate(octokit.rest.repos.listCommits, {
|
|
19
|
-
sha: github.branch,
|
|
20
|
-
owner: github.owner,
|
|
21
|
-
repo: github.name,
|
|
22
|
-
path,
|
|
23
|
-
since: github.since,
|
|
24
|
-
per_page: github.per_page,
|
|
25
|
-
until: github.until,
|
|
26
|
-
...(author && { author })
|
|
27
|
-
})
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
const allCommits = await Promise.all(
|
|
22
|
+
paths.map(path =>
|
|
23
|
+
octokit.paginate(octokit.rest.repos.listCommits, {
|
|
24
|
+
sha: github.branch,
|
|
25
|
+
owner: github.owner,
|
|
26
|
+
repo: github.name,
|
|
27
|
+
path,
|
|
28
|
+
since: github.since,
|
|
29
|
+
per_page: github.per_page,
|
|
30
|
+
until: github.until,
|
|
31
|
+
author
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const uniqueCommits = new Map<string, { sha: string, date: string, message: string }>()
|
|
37
|
+
for (const commits of allCommits) {
|
|
38
|
+
for (const commit of commits) {
|
|
39
|
+
if (!uniqueCommits.has(commit.sha)) {
|
|
40
|
+
uniqueCommits.set(commit.sha, {
|
|
41
|
+
sha: commit.sha,
|
|
42
|
+
date: commit.commit.author?.date ?? '',
|
|
43
|
+
message: (commit.commit.message?.split('\n')[0] ?? '')
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return Array.from(uniqueCommits.values()).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
|
|
34
50
|
}, {
|
|
35
51
|
maxAge: 60 * 60,
|
|
36
52
|
getKey: (event) => {
|
|
37
53
|
const { path, author } = getQuery(event)
|
|
38
|
-
|
|
54
|
+
const paths = Array.isArray(path) ? path : [path]
|
|
55
|
+
return `commits-${paths.join(',')}${author ? `-${author}` : ''}`
|
|
39
56
|
}
|
|
40
57
|
})
|