@movk/nuxt-docs 1.1.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.
Files changed (49) hide show
  1. package/README.md +265 -0
  2. package/app/app.config.ts +53 -0
  3. package/app/app.vue +73 -0
  4. package/app/components/AdsCarbon.vue +3 -0
  5. package/app/components/Footer.vue +32 -0
  6. package/app/components/PageHeaderLinks.vue +73 -0
  7. package/app/components/StarsBg.vue +122 -0
  8. package/app/components/content/ComponentEmits.vue +43 -0
  9. package/app/components/content/ComponentExample.vue +247 -0
  10. package/app/components/content/ComponentProps.vue +105 -0
  11. package/app/components/content/ComponentPropsLinks.vue +20 -0
  12. package/app/components/content/ComponentPropsSchema.vue +55 -0
  13. package/app/components/content/ComponentSlots.vue +50 -0
  14. package/app/components/content/HeroBackground.vue +65 -0
  15. package/app/components/content/HighlightInlineType.vue +39 -0
  16. package/app/components/content/Motion.vue +21 -0
  17. package/app/components/header/Header.vue +60 -0
  18. package/app/components/header/HeaderBody.vue +19 -0
  19. package/app/components/header/HeaderBottom.vue +26 -0
  20. package/app/components/header/HeaderLogo.vue +57 -0
  21. package/app/components/theme-picker/ThemePicker.vue +152 -0
  22. package/app/components/theme-picker/ThemePickerButton.vue +37 -0
  23. package/app/composables/fetchComponentExample.ts +32 -0
  24. package/app/composables/fetchComponentMeta.ts +34 -0
  25. package/app/composables/useCategory.ts +5 -0
  26. package/app/composables/useHeader.ts +6 -0
  27. package/app/composables/useNavigation.ts +89 -0
  28. package/app/error.vue +59 -0
  29. package/app/layouts/default.vue +3 -0
  30. package/app/layouts/docs.vue +31 -0
  31. package/app/pages/docs/[...slug].vue +158 -0
  32. package/app/pages/index.vue +30 -0
  33. package/app/pages/releases.vue +92 -0
  34. package/app/plugins/prettier.ts +67 -0
  35. package/app/plugins/theme.ts +82 -0
  36. package/app/types/index.d.ts +37 -0
  37. package/app/workers/prettier.js +36 -0
  38. package/content.config.ts +68 -0
  39. package/modules/component-example.ts +128 -0
  40. package/modules/component-meta.ts +22 -0
  41. package/modules/config.ts +79 -0
  42. package/modules/css.ts +33 -0
  43. package/nuxt.config.ts +80 -0
  44. package/package.json +55 -0
  45. package/server/api/component-example.get.ts +19 -0
  46. package/server/plugins/llms.ts +24 -0
  47. package/server/routes/raw/[...slug].md.get.ts +27 -0
  48. package/utils/git.ts +108 -0
  49. package/utils/meta.ts +29 -0
package/README.md ADDED
@@ -0,0 +1,265 @@
1
+ [![Movk Nuxt Docs](https://docs.mhaibaraai.cn/__og-image__/static/og.png)](https://docs.mhaibaraai.cn/)
2
+
3
+ > 一款由 Nuxt UI 和 Nuxt Content 强力驱动的优雅文档主题
4
+
5
+ [![npm version][npm-version-src]][npm-version-href]
6
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
7
+ [![License][license-src]][license-href]
8
+ [![Nuxt][nuxt-src]][nuxt-href]
9
+
10
+ 使用此主题可以快速构建美观、专业的文档网站,内置内容管理、SEO、暗黑模式、全文搜索等功能。
11
+
12
+ - 📖 [在线文档](https://docs.mhaibaraai.cn/)
13
+
14
+ ## ✨ 特性
15
+
16
+ 此主题集成了一系列旨在优化文档管理体验的强大功能:
17
+
18
+ - ⚡ **基于 Nuxt 4** - 充分利用最新的 Nuxt 框架,实现卓越性能
19
+ - 🎨 **采用 Nuxt UI** - 集成全面的 UI 组件库,开箱即用
20
+ - 📝 **MDC 语法增强** - 支持 Markdown 与 Vue 组件的无缝集成,实现动态内容
21
+ - 🧩 **组件文档自动生成** - 自动生成 Props、Slots、Emits 文档及交互式示例
22
+ - 📚 **智能侧边栏导航** - 根据内容结构自动生成导航
23
+ - 🔍 **全文搜索** - 内置强大的全文搜索功能
24
+ - 🌙 **暗黑模式** - 支持亮色/暗色主题切换
25
+ - 📱 **响应式设计** - 移动优先的响应式布局
26
+ - 🚀 **SEO 优化** - 内置 SEO 优化功能
27
+ - 🎯 **TypeScript 支持** - 完整的 TypeScript 类型支持
28
+ - 🤖 **AI 助手优化** - 为 LLM 优化,提供更好的 AI 辅助文档体验
29
+
30
+ ## 🚀 快速开始
31
+
32
+ ### 使用模板创建项目
33
+
34
+ ```bash
35
+ # 使用此模板创建新项目
36
+ npx nuxi init -t gh:mhaibaraai/movk-nuxt-docs/template my-docs
37
+
38
+ # 进入项目目录
39
+ cd my-docs
40
+
41
+ # 启动开发服务器
42
+ pnpm dev
43
+ ```
44
+
45
+ ### 作为 Layer 使用
46
+
47
+ 在现有 Nuxt 项目中使用 Movk Nuxt Docs 作为 layer:
48
+
49
+ ```bash
50
+ # 安装依赖
51
+ pnpm add @movk/nuxt-docs better-sqlite3
52
+ ```
53
+
54
+ 在 CSS 中导入 Tailwind CSS 和 Nuxt UI
55
+
56
+ ```css [~/assets/css/main.css]
57
+ @import 'tailwindcss';
58
+ @import '@nuxt/ui';
59
+ ```
60
+
61
+ 在 `nuxt.config.ts` 中配置:
62
+
63
+ ```ts
64
+ export default defineNuxtConfig({
65
+ extends: ['@movk/nuxt-docs'],
66
+ css: ['~/assets/css/main.css'],
67
+ llms: {
68
+ domain: 'https://docs.mhaibaraai.cn',
69
+ title: 'Movk Nuxt Docs',
70
+ description: '一款优雅的 Nuxt 文档主题'
71
+ }
72
+ })
73
+ ```
74
+
75
+ ## 📁 项目结构
76
+
77
+ ### 完整项目结构
78
+
79
+ 使用模板创建的项目结构:
80
+
81
+ ```bash
82
+ my-docs/
83
+ ├── app/assets/css/main.css # 全局样式
84
+ ├── content/ # Markdown 内容
85
+ │ ├── index.md # 首页
86
+ │ └── docs/ # 文档页面
87
+ ├── public/ # 静态资源
88
+ ├── nuxt.config.ts # Nuxt 配置
89
+ ├── tsconfig.json # TypeScript 配置
90
+ ├── package.json # 依赖与脚本
91
+ ├── .npmrc # npm 配置
92
+ ├── pnpm-workspace.yaml # pnpm 工作区配置
93
+ └── README.md # 项目说明
94
+ ```
95
+
96
+ ### Monorepo 结构
97
+
98
+ 本项目采用 monorepo 结构:
99
+
100
+ - `/docs` - 官方文档站点
101
+ - `/layer` - Movk Nuxt Docs 主题 layer(`@movk/nuxt-docs`)
102
+ - `/template` - 项目模板
103
+ - `/scripts` - 构建脚本
104
+
105
+ ## 📝 内容编写
106
+
107
+ ### 基础 Markdown
108
+
109
+ ```md
110
+ ---
111
+ title: 页面标题
112
+ description: 页面描述
113
+ ---
114
+
115
+ # 标题
116
+
117
+ 这是一段普通的文本内容。
118
+
119
+ ## 二级标题
120
+
121
+ - 列表项 1
122
+ - 列表项 2
123
+ ```
124
+
125
+ ### MDC 语法
126
+
127
+ ```md
128
+ ::card
129
+ ---
130
+ title: 卡片标题
131
+ icon: i-lucide-rocket
132
+ ---
133
+ 卡片内容
134
+ ::
135
+ ```
136
+
137
+ 了解更多关于 MDC 语法,请查看 [Nuxt Content 文档](https://content.nuxt.com/docs/files/markdown#mdc-syntax)。
138
+
139
+ ## 🔌 集成第三方服务
140
+
141
+ 本主题不内置任何分析或监控工具,你可以根据需求自由选择。
142
+
143
+ ### Vercel Analytics
144
+
145
+ ```bash
146
+ pnpm add @vercel/analytics @vercel/speed-insights
147
+ ```
148
+
149
+ 创建 `app/plugins/analytics.client.ts`:
150
+
151
+ ```typescript
152
+ import { Analytics } from '@vercel/analytics/nuxt'
153
+ import { SpeedInsights } from '@vercel/speed-insights/nuxt'
154
+ import { createApp, h } from 'vue'
155
+
156
+ export default defineNuxtPlugin({
157
+ name: 'vercel-analytics',
158
+ enforce: 'post',
159
+ hooks: {
160
+ 'app:mounted': () => {
161
+ if (import.meta.dev) return
162
+
163
+ const container = document.createElement('div')
164
+ container.id = 'vercel-analytics'
165
+ document.body.appendChild(container)
166
+
167
+ const app = createApp({
168
+ render: () => h('div', { style: 'display: none;' }, [
169
+ h(Analytics, { debug: false }),
170
+ h(SpeedInsights, { debug: false })
171
+ ])
172
+ })
173
+
174
+ app.mount(container)
175
+ }
176
+ }
177
+ })
178
+ ```
179
+
180
+ ### 其他工具
181
+
182
+ - **Google Analytics** - [@nuxtjs/google-analytics](https://google-analytics.nuxtjs.org/)
183
+ - **Plausible** - [vue-plausible](https://github.com/moritzsternemann/vue-plausible)
184
+ - **Umami** - [nuxt-umami](https://github.com/ijkml/nuxt-umami)
185
+
186
+ 按照各工具的 Nuxt 集成文档在 `plugins` 目录中创建插件即可。
187
+
188
+ ## 🛠️ 开发
189
+
190
+ ### 本地开发
191
+
192
+ ```bash
193
+ # 克隆项目
194
+ git clone https://github.com/mhaibaraai/movk-nuxt-docs.git
195
+
196
+ # 进入项目目录
197
+ cd movk-nuxt-docs
198
+
199
+ # 安装依赖
200
+ pnpm install
201
+
202
+ # 启动开发服务器
203
+ pnpm dev
204
+ ```
205
+
206
+ 开发服务器将在 `http://localhost:3000` 启动。
207
+
208
+ ### 构建生产版本
209
+
210
+ ```bash
211
+ # 构建应用
212
+ pnpm build
213
+
214
+ # 本地预览生产构建
215
+ pnpm preview
216
+ ```
217
+
218
+ ### 发布
219
+
220
+ ```bash
221
+ # 发布 layer 到 npm
222
+ pnpm release:layer
223
+
224
+ # 发布完整项目
225
+ pnpm release
226
+ ```
227
+
228
+ ## ⚡ 技术栈
229
+
230
+ 本项目基于以下优秀的开源项目构建:
231
+
232
+ - [Nuxt 4](https://nuxt.com/) - Web 框架
233
+ - [Nuxt Content](https://content.nuxt.com/) - 基于文件的 CMS
234
+ - [Nuxt UI](https://ui.nuxt.com/) - UI 组件库
235
+ - [Nuxt Image](https://image.nuxt.com/) - 图片优化
236
+ - [Tailwind CSS 4](https://tailwindcss.com/) - CSS 框架
237
+ - [Nuxt SEO](https://nuxtseo.com/) - SEO 优化
238
+ - [Nuxt LLMs](https://github.com/nuxt/llms) - AI 助手优化
239
+
240
+ ## 📖 文档
241
+
242
+ 访问 [Movk Nuxt Docs 文档](https://docs.mhaibaraai.cn/) 了解详细的使用指南和 API 文档。
243
+
244
+ ## 🙏 致谢
245
+
246
+ 本项目基于以下优秀项目构建或受其启发:
247
+
248
+ - [Docus](https://docus.dev/) - 由 Nuxt Content 团队开发的文档主题
249
+ - [Nuxt UI Docs Template](https://docs-template.nuxt.dev/) - Nuxt UI 官方文档模板
250
+
251
+ ## 📄 许可证
252
+
253
+ [MIT](./LICENSE) License © 2024-PRESENT [YiXuan](https://github.com/mhaibaraai)
254
+
255
+
256
+ <!-- Badges -->
257
+
258
+ [npm-version-src]: https://img.shields.io/npm/v/@movk/nuxt-docs/latest.svg?style=flat&colorA=020420&colorB=00DC82
259
+ [npm-version-href]: https://npmjs.com/package/@movk/nuxt-docs
260
+ [npm-downloads-src]: https://img.shields.io/npm/dm/@movk/nuxt-docs.svg?style=flat&colorA=020420&colorB=00DC82
261
+ [npm-downloads-href]: https://npm.chart.dev/@movk/nuxt-docs
262
+ [license-src]: https://img.shields.io/badge/License-MIT-blue.svg
263
+ [license-href]: https://npmjs.com/package/@movk/nuxt-docs
264
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-4-00DC82?logo=nuxt.js&logoColor=fff
265
+ [nuxt-href]: https://nuxt.com
@@ -0,0 +1,53 @@
1
+ import type { ButtonProps } from '@nuxt/ui'
2
+
3
+ export default defineAppConfig({
4
+ toaster: {
5
+ expand: true,
6
+ position: 'top-right' as const,
7
+ duration: 3000,
8
+ max: 5
9
+ },
10
+ theme: {
11
+ radius: 0.25,
12
+ blackAsPrimary: false
13
+ },
14
+ ui: {
15
+ colors: {
16
+ primary: 'indigo',
17
+ neutral: 'zinc'
18
+ },
19
+ contentNavigation: {
20
+ slots: {
21
+ linkLeadingIcon: 'size-4 mr-1',
22
+ linkTrailing: 'hidden'
23
+ },
24
+ defaultVariants: {
25
+ variant: 'link'
26
+ }
27
+ },
28
+ pageLinks: {
29
+ slots: {
30
+ linkLeadingIcon: 'size-4',
31
+ linkLabelExternalIcon: 'size-2.5'
32
+ }
33
+ }
34
+ },
35
+ header: {
36
+ title: 'Movk Nuxt Docs',
37
+ to: '/',
38
+ search: true,
39
+ colorMode: true,
40
+ links: [] as ButtonProps[]
41
+ },
42
+ footer: {
43
+ credits: `Copyright © 2024 - ${new Date().getFullYear()}`,
44
+ socials: [] as ButtonProps[]
45
+ },
46
+ toc: {
47
+ title: '页面导航',
48
+ bottom: {
49
+ title: '社区',
50
+ links: [] as ButtonProps[]
51
+ }
52
+ }
53
+ })
package/app/app.vue ADDED
@@ -0,0 +1,73 @@
1
+ <script setup lang="ts">
2
+ import colors from 'tailwindcss/colors'
3
+
4
+ const site = useSiteConfig()
5
+ const appConfig = useAppConfig()
6
+ const colorMode = useColorMode()
7
+ const route = useRoute()
8
+
9
+ const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs', ['category', 'description']))
10
+ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
11
+ server: false
12
+ })
13
+ const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
14
+ const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
15
+ const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
16
+
17
+ useHead({
18
+ meta: [
19
+ { name: 'viewport', content: 'width=device-width, initial-scale=1' },
20
+ { key: 'theme-color', name: 'theme-color', content: color }
21
+ ],
22
+ style: [
23
+ { innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 },
24
+ { innerHTML: blackAsPrimary, id: 'nuxt-ui-black-as-primary', tagPriority: -2 }
25
+ ]
26
+ })
27
+
28
+ useSeoMeta({
29
+ titleTemplate: appConfig.seo.titleTemplate,
30
+ title: appConfig.seo.title,
31
+ description: appConfig.seo.description,
32
+ ogSiteName: site.name,
33
+ twitterCard: 'summary_large_image'
34
+ })
35
+
36
+ const { rootNavigation } = useNavigation(navigation)
37
+
38
+ provide('navigation', rootNavigation)
39
+ </script>
40
+
41
+ <template>
42
+ <UApp :toaster="appConfig.toaster">
43
+ <NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
44
+
45
+ <div :class="[route.path.startsWith('/docs/') && 'root']">
46
+ <template v-if="!route.path.startsWith('/examples')">
47
+ <Header />
48
+ </template>
49
+
50
+ <NuxtLayout>
51
+ <NuxtPage />
52
+ </NuxtLayout>
53
+
54
+ <template v-if="!route.path.startsWith('/examples')">
55
+ <Footer />
56
+
57
+ <ClientOnly>
58
+ <LazyUContentSearch :files="files" :navigation="navigation" />
59
+ </ClientOnly>
60
+ </template>
61
+ </div>
62
+ </UApp>
63
+ </template>
64
+
65
+ <style>
66
+ /* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 max-h-[341px] */
67
+
68
+ @media (min-width: 1024px) {
69
+ .root {
70
+ --ui-header-height: 112px;
71
+ }
72
+ }
73
+ </style>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <div />
3
+ </template>
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ const route = useRoute()
3
+ const { footer } = useAppConfig()
4
+ </script>
5
+
6
+ <template>
7
+ <USeparator :icon="route.path === '/' ? undefined : 'i-simple-icons-nuxtdotjs'" class="h-px" />
8
+
9
+ <UFooter
10
+ :ui="{
11
+ left: 'text-sm text-muted',
12
+ root: 'border-t border-default'
13
+ }"
14
+ >
15
+ <template #left>
16
+ <MDC v-if="footer?.credits" :value="footer.credits" unwrap="p" />
17
+ </template>
18
+
19
+ <template #right>
20
+ <template v-if="footer.socials?.length">
21
+ <UTooltip
22
+ v-for="(link, count) in footer.socials"
23
+ :key="count"
24
+ :text="link.label || (link as any)['aria-label']"
25
+ class="hidden lg:flex"
26
+ >
27
+ <UButton v-bind="{ color: 'neutral', variant: 'ghost', ...link }" />
28
+ </UTooltip>
29
+ </template>
30
+ </template>
31
+ </UFooter>
32
+ </template>
@@ -0,0 +1,73 @@
1
+ <script setup lang="ts">
2
+ const route = useRoute()
3
+ const toast = useToast()
4
+ const { copy, copied } = useClipboard()
5
+ const site = useSiteConfig()
6
+
7
+ const mdPath = computed(() => `${site.url}/raw${route.path}.md`)
8
+
9
+ const items = [
10
+ {
11
+ label: 'Copy Markdown link',
12
+ icon: 'i-lucide-link',
13
+ onSelect() {
14
+ copy(mdPath.value)
15
+ toast.add({
16
+ title: 'Copied to clipboard',
17
+ icon: 'i-lucide-check-circle'
18
+ })
19
+ }
20
+ },
21
+ {
22
+ label: 'View as Markdown',
23
+ icon: 'i-simple-icons:markdown',
24
+ target: '_blank',
25
+ to: `/raw${route.path}.md`
26
+ },
27
+ {
28
+ label: 'Open in ChatGPT',
29
+ icon: 'i-simple-icons:openai',
30
+ target: '_blank',
31
+ to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
32
+ },
33
+ {
34
+ label: 'Open in Claude',
35
+ icon: 'i-simple-icons:anthropic',
36
+ target: '_blank',
37
+ to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
38
+ }
39
+ ]
40
+
41
+ async function copyPage() {
42
+ copy(await $fetch<string>(`/raw${route.path}.md`))
43
+ }
44
+ </script>
45
+
46
+ <template>
47
+ <UFieldGroup size="sm">
48
+ <UButton
49
+ label="Copy page"
50
+ :icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
51
+ color="neutral"
52
+ variant="outline"
53
+ :ui="{
54
+ leadingIcon: [copied ? 'text-primary' : 'text-neutral', 'size-3.5']
55
+ }"
56
+ @click="copyPage"
57
+ />
58
+ <UDropdownMenu
59
+ size="sm"
60
+ :items="items"
61
+ :content="{
62
+ align: 'end',
63
+ side: 'bottom',
64
+ sideOffset: 8
65
+ }"
66
+ :ui="{
67
+ content: 'w-48'
68
+ }"
69
+ >
70
+ <UButton icon="i-lucide-chevron-down" color="neutral" variant="outline" />
71
+ </UDropdownMenu>
72
+ </UFieldGroup>
73
+ </template>
@@ -0,0 +1,122 @@
1
+ <script setup lang="ts">
2
+ import { kebabCase } from 'scule'
3
+
4
+ interface Star {
5
+ x: number
6
+ y: number
7
+ size: number
8
+ }
9
+
10
+ const { starCount = 300, color = 'var(--ui-primary)', size = { min: 1, max: 2 } } = defineProps<{
11
+ starCount?: number
12
+ color?: string
13
+ size?: { min: number, max: number }
14
+ }>()
15
+
16
+ const route = useRoute()
17
+
18
+ // Generate random star positions and sizes
19
+ function generateStars(count: number): Star[] {
20
+ return Array.from({ length: count }, () => ({
21
+ x: Math.floor(Math.random() * 2000),
22
+ y: Math.floor(Math.random() * 2000),
23
+ size: typeof size === 'number'
24
+ ? size
25
+ : Math.random() * (size.max - size.min) + size.min
26
+ }))
27
+ }
28
+
29
+ // Define speed configurations once
30
+ const speedMap = {
31
+ slow: { duration: 200, opacity: 0.5, ratio: 0.3 },
32
+ normal: { duration: 150, opacity: 0.75, ratio: 0.3 },
33
+ fast: { duration: 100, opacity: 1, ratio: 0.4 }
34
+ }
35
+
36
+ // Use a more efficient approach to generate and store stars
37
+ const stars = useState<{ slow: Star[], normal: Star[], fast: Star[] }>(`${kebabCase(route.path)}-stars`, () => {
38
+ return {
39
+ slow: generateStars(Math.floor(starCount * speedMap.slow.ratio)),
40
+ normal: generateStars(Math.floor(starCount * speedMap.normal.ratio)),
41
+ fast: generateStars(Math.floor(starCount * speedMap.fast.ratio))
42
+ }
43
+ })
44
+
45
+ // Compute star layers with different speeds and opacities
46
+ const starLayers = computed(() => [
47
+ { stars: stars.value.fast, ...speedMap.fast },
48
+ { stars: stars.value.normal, ...speedMap.normal },
49
+ { stars: stars.value.slow, ...speedMap.slow }
50
+ ])
51
+ </script>
52
+
53
+ <template>
54
+ <div class="absolute pointer-events-none z-[-1] inset-y-0 inset-x-5 sm:inset-x-7 lg:inset-x-9 overflow-hidden">
55
+ <div class="stars size-full absolute inset-x-0 top-0">
56
+ <div
57
+ v-for="(layer, index) in starLayers"
58
+ :key="index"
59
+ class="star-layer"
60
+ :style="{
61
+ '--star-duration': `${layer.duration}s`,
62
+ '--star-opacity': layer.opacity,
63
+ '--star-color': color
64
+ }"
65
+ >
66
+ <div
67
+ v-for="(star, starIndex) in layer.stars"
68
+ :key="starIndex"
69
+ class="star absolute rounded-full"
70
+ :style="{
71
+ left: `${star.x}px`,
72
+ top: `${star.y}px`,
73
+ width: `${star.size}px`,
74
+ height: `${star.size}px`,
75
+ backgroundColor: 'var(--star-color)',
76
+ opacity: 'var(--star-opacity)'
77
+ }"
78
+ />
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </template>
83
+
84
+ <style scoped>
85
+ .stars {
86
+ left: 50%;
87
+ transform: translate(-50%);
88
+ -webkit-mask-image: linear-gradient(
89
+ 180deg,
90
+ rgba(217, 217, 217, 0) 0%,
91
+ rgba(217, 217, 217, 0.8) 25%,
92
+ #d9d9d9 50%,
93
+ rgba(217, 217, 217, 0.8) 75%,
94
+ rgba(217, 217, 217, 0) 100%
95
+ );
96
+ mask-image: linear-gradient(
97
+ 180deg,
98
+ rgba(217, 217, 217, 0) 0%,
99
+ rgba(217, 217, 217, 0.8) 25%,
100
+ #d9d9d9 50%,
101
+ rgba(217, 217, 217, 0.8) 75%,
102
+ rgba(217, 217, 217, 0) 100%
103
+ );
104
+ -webkit-mask-size: cover;
105
+ mask-size: cover;
106
+ }
107
+
108
+ .star-layer {
109
+ animation: risingStarsAnimation linear infinite;
110
+ animation-duration: var(--star-duration);
111
+ will-change: transform;
112
+ }
113
+
114
+ @keyframes risingStarsAnimation {
115
+ 0% {
116
+ transform: translateY(0);
117
+ }
118
+ 100% {
119
+ transform: translateY(-2000px);
120
+ }
121
+ }
122
+ </style>
@@ -0,0 +1,43 @@
1
+ <script setup lang="ts">
2
+ import { camelCase } from 'scule'
3
+
4
+ const props = defineProps<{
5
+ /**
6
+ * The slug of the component to fetch emits for.
7
+ * @defaultValue route path's last segment
8
+ */
9
+ slug?: string
10
+ }>()
11
+
12
+ const route = useRoute()
13
+ const componentName = camelCase(props.slug ?? route.path.split('/').pop() ?? '')
14
+
15
+ const meta = await fetchComponentMeta(componentName as any)
16
+ </script>
17
+
18
+ <template>
19
+ <ProseTable>
20
+ <ProseThead>
21
+ <ProseTr>
22
+ <ProseTh>
23
+ Event
24
+ </ProseTh>
25
+ <ProseTh>
26
+ Type
27
+ </ProseTh>
28
+ </ProseTr>
29
+ </ProseThead>
30
+ <ProseTbody>
31
+ <ProseTr v-for="event in (meta?.meta?.events || [])" :key="event.name">
32
+ <ProseTd>
33
+ <ProseCode>
34
+ {{ event.name }}
35
+ </ProseCode>
36
+ </ProseTd>
37
+ <ProseTd>
38
+ <HighlightInlineType v-if="event.type" :type="event.type" />
39
+ </ProseTd>
40
+ </ProseTr>
41
+ </ProseTbody>
42
+ </ProseTable>
43
+ </template>