clarity-theme 0.1.3 → 0.2.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 (62) hide show
  1. package/README.md +15 -5
  2. package/README.zh-CN.md +15 -5
  3. package/nuxt.config.ts +19 -1
  4. package/package.json +14 -2
  5. package/src/app.config.ts +8 -104
  6. package/src/assets/icons/zhilu.svg +8 -0
  7. package/src/blog.config.ts +71 -0
  8. package/src/components/blog/BlogFooter.vue +3 -3
  9. package/src/components/blog/BlogHeader.global.vue +9 -9
  10. package/src/components/blog/BlogSidebar.vue +3 -3
  11. package/src/components/blog/ThemeToggle.vue +2 -2
  12. package/src/components/content/Alert.vue +2 -2
  13. package/src/components/content/FeedCard.vue +2 -2
  14. package/src/components/content/Mermaid.vue +1 -1
  15. package/src/components/content/ProseCode.vue +4 -32
  16. package/src/components/content/ProsePre.vue +2 -2
  17. package/src/components/content/ProseTable.vue +1 -1
  18. package/src/components/popover/Search.vue +3 -3
  19. package/src/components/post/Comment.vue +3 -15
  20. package/src/components/post/Excerpt.vue +3 -3
  21. package/src/components/post/OrderToggle.vue +3 -3
  22. package/src/components/post/PostFooter.vue +3 -4
  23. package/src/components/post/PostHeader.vue +4 -4
  24. package/src/components/post/PostSurround.vue +2 -2
  25. package/src/components/post/Slide.vue +2 -2
  26. package/src/components/util/Img.vue +1 -1
  27. package/src/components/widget/BlogLog.vue +9 -19
  28. package/src/components/widget/BlogStats.vue +4 -4
  29. package/src/components/widget/BlogTech.vue +16 -17
  30. package/src/components/widget/CommGroup.vue +43 -0
  31. package/src/composables/useArticle.ts +9 -9
  32. package/src/composables/usePagination.ts +2 -2
  33. package/src/composables/useWidgets.ts +2 -0
  34. package/src/config/app.ts +40 -11
  35. package/src/config/content.ts +6 -0
  36. package/src/config/feed.ts +2 -42
  37. package/src/config/index.ts +2 -2
  38. package/src/config/pnpm-workspace.ts +19 -0
  39. package/src/config/public.ts +3 -61
  40. package/src/config/ui.ts +111 -0
  41. package/src/modules/anti-mirror/index.ts +38 -0
  42. package/src/modules/{clarity-config/anti-mirror-client.ts → anti-mirror/runtime/client.ts} +3 -3
  43. package/src/modules/clarity-config/index.ts +354 -93
  44. package/src/modules/clarity-source-layout/index.ts +13 -0
  45. package/src/pages/archive.vue +4 -4
  46. package/src/pages/index.vue +4 -3
  47. package/src/pages/link.vue +5 -5
  48. package/src/pages/preview.vue +2 -2
  49. package/src/server/api/stats.get.ts +9 -19
  50. package/src/server/routes/atom.xml.get.ts +48 -50
  51. package/src/server/routes/subscriptions.opml.get.ts +14 -31
  52. package/src/server/utils/clarity.ts +1 -1
  53. package/src/shared/utils/time.ts +13 -18
  54. package/src/types/article.ts +2 -2
  55. package/src/types/feed.ts +35 -1
  56. package/src/types/upstream-compat.d.ts +17 -0
  57. package/src/types/yaml-modules.d.ts +26 -0
  58. package/src/shared/types/content.ts +0 -30
  59. package/src/shared/utils/clarity.ts +0 -34
  60. package/src/types/modal.ts +0 -16
  61. /package/src/assets/icons/{solar-rewind-back-bold-duotone.svg → SolarRewindBackBoldDuotone.svg} +0 -0
  62. /package/src/assets/icons/{solar-rewind-forward-bold-duotone.svg → SolarRewindForwardBoldDuotone.svg} +0 -0
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- const clarity = useClarityConfig()
2
+ const appConfig = useAppConfig()
3
3
  useSeoMeta({
4
4
  title: '预览',
5
- description: `${clarity.site.title}的文章预览。`,
5
+ description: `${appConfig.title}的文章预览。`,
6
6
  })
7
7
  const { data: listRaw } = await useAsyncData('previews:index', () => getArticleIndexOptions('previews/%'), { default: () => [] })
8
8
  const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw)
@@ -1,6 +1,5 @@
1
- import type { ClarityContentRow } from '../../shared/types/content'
2
1
  import { queryCollection } from '@nuxt/content/server'
3
- import { toZonedTemporal } from '../../shared/utils/time'
2
+ import blogConfig from '~~/blog.config'
4
3
 
5
4
  interface StatsEntry {
6
5
  posts: number
@@ -14,11 +13,6 @@ interface CategoryEntry {
14
13
  }
15
14
 
16
15
  export default defineEventHandler(async (event) => {
17
- const { site, stats: statsConfig, features } = useClarityServerConfig()
18
- if (!features.stats) {
19
- throw createError({ statusCode: 404, statusMessage: 'Stats output is disabled' })
20
- }
21
-
22
16
  const stats = {
23
17
  total: { posts: 0, words: 0 },
24
18
  annual: <Record<number, StatsEntry>>{},
@@ -28,18 +22,14 @@ export default defineEventHandler(async (event) => {
28
22
 
29
23
  const existedPaths = new Set<string>()
30
24
 
31
- const query = queryCollection(event, 'content' as never)
32
- if (statsConfig.includePaths.length) {
33
- // @nuxt/content 3.16+ 的 orWhere 组内条件以 OR 连接:
34
- // 多个 includePaths 必须取并集(posts/% + notes/% → 两类文章都计入)。
35
- query.orWhere((group) => {
36
- for (const path of statsConfig.includePaths) {
37
- group.where('stem', 'LIKE', path)
38
- }
39
- return group
40
- })
25
+ const query = queryCollection(event, 'content')
26
+ if (blogConfig.stats.includePaths.length) {
27
+ query.orWhere(group => blogConfig.stats.includePaths.reduce(
28
+ (group, path) => group.where('stem', 'LIKE', path),
29
+ group,
30
+ ))
41
31
  }
42
- const posts = await query.all() as ClarityContentRow[]
32
+ const posts = await query.all()
43
33
 
44
34
  const findOrCreateCategory = (
45
35
  name: string,
@@ -65,7 +55,7 @@ export default defineEventHandler(async (event) => {
65
55
 
66
56
  // 年文章/年字数计数
67
57
  try {
68
- const year = toZonedTemporal(post.date || '', site.timezone).year
58
+ const year = toZonedTemporal(post.date || '').year
69
59
  if (!stats.annual[year]) {
70
60
  stats.annual[year] = { posts: 0, words: 0 }
71
61
  }
@@ -1,7 +1,11 @@
1
- import type { ClarityContentRow } from '../../shared/types/content'
1
+ import type { ContentCollectionItem } from '@nuxt/content'
2
2
  import { queryCollection } from '@nuxt/content/server'
3
+ import { pascalCase } from 'es-toolkit/string'
3
4
  import XmlBuilder from 'fast-xml-builder'
4
- import { toZonedTemporal } from '../../shared/utils/time'
5
+ import { Temporal } from 'temporal-polyfill'
6
+ import blogConfig from '~~/blog.config'
7
+ import packageJson from '~~/package.json'
8
+ import { toZonedTemporal } from '~~/shared/utils/time'
5
9
 
6
10
  const runtimeConfig = useRuntimeConfig()
7
11
 
@@ -13,48 +17,42 @@ const builder = new XmlBuilder({
13
17
  textNodeName: '_',
14
18
  })
15
19
 
16
- export default defineEventHandler(async (event) => {
17
- const { site, feed, features } = useClarityServerConfig()
18
- if (!features.atom) {
19
- throw createError({ statusCode: 404, statusMessage: 'Atom feed is disabled' })
20
+ function formatIsoDate(date?: string) {
21
+ if (!date)
22
+ return
23
+ try {
24
+ return toZonedTemporal(date).toInstant().toString()
20
25
  }
21
- const themeInfo = runtimeConfig.public.clarity as { theme: string, themeVersion: string, themeHomepage: string }
22
-
23
- function formatIsoDate(date?: string) {
24
- if (!date)
25
- return
26
- try {
27
- return toZonedTemporal(date, site.timezone).toInstant().toString()
28
- }
29
- catch {
30
- console.error('Invalid date format', date)
31
- return date
32
- }
26
+ catch {
27
+ console.error('Invalid date format', date)
28
+ return date
33
29
  }
30
+ }
34
31
 
35
- function getUrl(path: string | undefined) {
36
- return new URL(path ?? '', site.url).toString()
37
- }
32
+ function getUrl(path: string | undefined) {
33
+ return new URL(path ?? '', blogConfig.url).toString()
34
+ }
38
35
 
39
- function renderContent(post: ClarityContentRow) {
40
- return [
41
- post.image && `<img src="${post.image}" alt="${post.title}" />`,
42
- post.description && `<p>${post.description}</p>`,
43
- `<a class="view-full" href="${getUrl(post.path)}" target="_blank">点击查看全文</a>`,
44
- ].filter(Boolean).join(' ')
45
- }
36
+ function renderContent(post: ContentCollectionItem) {
37
+ return [
38
+ post.image && `<img src="${post.image}" alt="${post.title}" />`,
39
+ post.description && `<p>${post.description}</p>`,
40
+ `<a class="view-full" href="${getUrl(post.path)}" target="_blank">点击查看全文</a>`,
41
+ ].join(' ')
42
+ }
46
43
 
47
- const posts = await queryCollection(event, 'content' as never)
44
+ export default defineEventHandler(async (event) => {
45
+ const posts = await queryCollection(event, 'content')
48
46
  .where('stem', 'LIKE', 'posts/%')
49
47
  .order('updated', 'DESC')
50
- .limit(feed.limit)
51
- .all() as ClarityContentRow[]
48
+ .limit(blogConfig.feed.limit)
49
+ .all()
52
50
 
53
51
  const entries = posts.map(post => ({
54
52
  id: getUrl(post.path),
55
53
  title: post.title ?? '',
56
54
  updated: formatIsoDate(post.updated),
57
- author: { name: (post as any).author || site.author.name },
55
+ author: { name: post.author || blogConfig.author.name },
58
56
  content: {
59
57
  $type: 'html',
60
58
  $: renderContent(post),
@@ -65,37 +63,37 @@ export default defineEventHandler(async (event) => {
65
63
  published: formatIsoDate(post.published ?? post.date),
66
64
  }))
67
65
 
68
- const feedData = {
66
+ const feed = {
69
67
  $xmlns: 'http://www.w3.org/2005/Atom',
70
- id: site.url,
71
- title: site.title,
68
+ id: blogConfig.url,
69
+ title: blogConfig.title,
72
70
  updated: runtimeConfig.public.buildTime,
73
- description: site.description,
71
+ description: blogConfig.description, // RSS 2.0
74
72
  author: {
75
- name: site.author.name,
76
- email: site.author.email,
77
- uri: site.author.homepage,
73
+ name: blogConfig.author.name,
74
+ email: blogConfig.author.email,
75
+ uri: blogConfig.author.homepage,
78
76
  },
79
77
  link: [
80
78
  { $href: getUrl('atom.xml'), $rel: 'self' },
81
- { $href: site.url, $rel: 'alternate' },
79
+ { $href: blogConfig.url, $rel: 'alternate' },
82
80
  ],
83
- language: site.language,
81
+ language: blogConfig.language, // RSS 2.0
84
82
  generator: {
85
- $uri: themeInfo.themeHomepage,
86
- $version: themeInfo.themeVersion,
87
- _: themeInfo.theme,
83
+ $uri: 'https://github.com/L33Z22L11/blog-v3',
84
+ $version: packageJson.version,
85
+ _: pascalCase(packageJson.name),
88
86
  },
89
- icon: site.favicon,
90
- logo: site.author.avatar,
91
- rights: `© ${new Date().getFullYear()} ${site.author.name}`,
92
- subtitle: site.subtitle || site.description,
87
+ icon: blogConfig.favicon,
88
+ logo: blogConfig.author.avatar, // Ratio should be 2:1
89
+ rights: `© ${Temporal.Now.plainDateISO().year.toString()} ${blogConfig.author.name}`,
90
+ subtitle: blogConfig.subtitle || blogConfig.description,
93
91
  entry: entries,
94
92
  }
95
93
 
96
94
  return builder.build({
97
95
  '?xml': { $version: '1.0', $encoding: 'UTF-8' },
98
- '?xml-stylesheet': feed.enableStyle ? { $type: 'text/xsl', $href: '/assets/atom.xsl' } : undefined,
99
- 'feed': feedData,
96
+ '?xml-stylesheet': blogConfig.feed.enableStyle ? { $type: 'text/xsl', $href: '/assets/atom.xsl' } : undefined,
97
+ feed,
100
98
  })
101
99
  })
@@ -1,6 +1,7 @@
1
1
  import type { FeedEntry, FeedGroup } from '../../types/feed'
2
2
  import XmlBuilder from 'fast-xml-builder'
3
- import { toZonedTemporal } from '../../shared/utils/time'
3
+ import blogConfig, { myFeed } from '~~/blog.config'
4
+ import feeds from '~/feeds'
4
5
 
5
6
  const runtimeConfig = useRuntimeConfig()
6
7
 
@@ -10,54 +11,36 @@ const builder = new XmlBuilder({
10
11
  ignoreAttributes: false,
11
12
  })
12
13
 
13
- function mapEntry(item: FeedEntry, timeZone: string) {
14
+ function mapEntry(item: FeedEntry) {
14
15
  return {
15
16
  $text: item.title || item.sitenick || item.author,
16
17
  $type: 'rss',
17
18
  $xmlUrl: item.feed,
18
- $created: toZonedTemporal(item.date, timeZone).toInstant().toString(),
19
+ $created: toZonedTemporal(item.date).toInstant().toString(),
19
20
  $description: item.desc,
20
21
  $htmlUrl: item.link || item.feed,
21
22
  }
22
23
  }
23
24
 
24
- function flattenGroups(groups: FeedGroup[], timeZone: string) {
25
- return groups.flatMap(({ entries }) => entries.filter(({ feed }) => feed).map(item => mapEntry(item, timeZone)))
25
+ function flattenGroups(groups: FeedGroup[]) {
26
+ return groups.flatMap(({ entries }) => entries.filter(({ feed }) => feed).map(mapEntry))
26
27
  }
27
28
 
28
- export default defineEventHandler(async () => {
29
- const { site, features } = useClarityServerConfig()
30
- if (!features.opml) {
31
- throw createError({ statusCode: 404, statusMessage: 'OPML subscription output is disabled' })
32
- }
33
- const feeds = (await import('#clarity/feeds')).default as FeedGroup[]
34
- const myFeed: FeedEntry = {
35
- author: site.author.name,
36
- title: site.title,
37
- desc: site.subtitle || site.description,
38
- link: site.url,
39
- feed: new URL('/atom.xml', site.url).toString(),
40
- icon: site.favicon,
41
- avatar: site.author.avatar || site.favicon,
42
- date: site.established || '',
43
- }
44
-
29
+ export default defineEventHandler(async (_e) => {
45
30
  const outlines = [
46
- mapEntry(myFeed, site.timezone),
47
- ...flattenGroups(feeds, site.timezone),
31
+ mapEntry(myFeed),
32
+ ...flattenGroups(feeds),
48
33
  ]
49
34
 
50
35
  const opml = {
51
36
  $version: '2.0',
52
37
  head: {
53
- title: `${site.title}的友链订阅`,
54
- dateCreated: site.established
55
- ? toZonedTemporal(site.established, site.timezone).toInstant().toString()
56
- : undefined,
38
+ title: `${blogConfig.title}的友链订阅`,
39
+ dateCreated: toZonedTemporal(blogConfig.timeEstablished).toInstant().toString(),
57
40
  dateModified: runtimeConfig.public.buildTime,
58
- ownerName: site.author.name,
59
- ownerEmail: site.author.email,
60
- ownerId: site.author.homepage,
41
+ ownerName: blogConfig.author.name,
42
+ ownerEmail: blogConfig.author.email,
43
+ ownerId: blogConfig.author.homepage,
61
44
  docs: 'https://opml.org/spec2.opml',
62
45
  },
63
46
  body: { outline: outlines },
@@ -4,7 +4,7 @@ import type { ClarityServerConfig } from '../../config/server'
4
4
  * 获取服务端专用 Clarity 配置(完整 site/feed/stats 与 feature 路由开关)。
5
5
  *
6
6
  * 数据来自 Nitro 私有 runtimeConfig,不会进入客户端 bundle;
7
- * 请只在 server/ 侧调用,客户端渲染请使用 useClarityConfig()。
7
+ * 请只在 server/ 侧调用;客户端渲染读取扁平 app config(useAppConfig())。
8
8
  */
9
9
  export function useClarityServerConfig(): ClarityServerConfig {
10
10
  return useRuntimeConfig().clarity as ClarityServerConfig
@@ -1,9 +1,10 @@
1
1
  import { Temporal } from 'temporal-polyfill'
2
+ import blogConfig from '~~/blog.config'
2
3
 
3
- export function isSameUnit(date1: string, date2: string, unit: 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second', timeZone = 'UTC') {
4
+ export function isSameUnit(date1: string, date2: string, unit: Temporal.DateTimeUnit) {
4
5
  try {
5
- const p1 = toZonedTemporal(date1, timeZone).toPlainDateTime()
6
- const p2 = toZonedTemporal(date2, timeZone).toPlainDateTime()
6
+ const p1 = toZonedTemporal(date1).toPlainDateTime()
7
+ const p2 = toZonedTemporal(date2).toPlainDateTime()
7
8
  return p1.until(p2, {
8
9
  largestUnit: unit,
9
10
  smallestUnit: unit,
@@ -19,7 +20,6 @@ export function isSameUnit(date1: string, date2: string, unit: 'year' | 'month'
19
20
  export function isTimeDiffSignificant(
20
21
  date1?: string,
21
22
  date2?: string,
22
- timeZone = 'UTC',
23
23
  /** 对于时间差的敏感程度,0~1 之间,1:不同则认为显著,>1:始终认为显著 */
24
24
  threshold = 0.6,
25
25
  ) {
@@ -29,8 +29,8 @@ export function isTimeDiffSignificant(
29
29
  return true
30
30
  try {
31
31
  const now = Temporal.Now.instant().epochMilliseconds
32
- const diff1 = now - toZonedTemporal(date1, timeZone).epochMilliseconds
33
- const diff2 = now - toZonedTemporal(date2, timeZone).epochMilliseconds
32
+ const diff1 = now - toZonedTemporal(date1).epochMilliseconds
33
+ const diff2 = now - toZonedTemporal(date2).epochMilliseconds
34
34
  return diff1 / diff2 < threshold || diff2 / diff1 < threshold
35
35
  }
36
36
  catch {
@@ -63,25 +63,20 @@ export function timeElapse(date: string | Temporal.PlainDateTime, maxDepth = 2)
63
63
  return timeString || '刚刚'
64
64
  }
65
65
 
66
- export function toInstantString(date: string | Temporal.ZonedDateTime, timeZone = 'UTC') {
67
- return (typeof date === 'string' ? toZonedTemporal(date, timeZone) : date).toInstant().toString()
66
+ export function toInstantString(date: string | Temporal.ZonedDateTime) {
67
+ return (typeof date === 'string' ? toZonedTemporal(date) : date).toInstant().toString()
68
68
  }
69
69
 
70
- /**
71
- * 将日期字符串解析为带时区的 Temporal 对象
72
- * @param date 日期字符串
73
- * @param timeZone 无时区信息时使用的默认时区(来自站点配置)
74
- */
75
- export function toZonedTemporal(date: string, timeZone = 'UTC') {
70
+ export function toZonedTemporal(date: string) {
76
71
  try {
77
72
  return Temporal.ZonedDateTime.from(date)
78
73
  }
79
74
  catch {
80
75
  try {
81
- return Temporal.Instant.from(date).toZonedDateTimeISO(timeZone)
76
+ return Temporal.Instant.from(date).toZonedDateTimeISO(blogConfig.timeZone)
82
77
  }
83
78
  catch {
84
- return Temporal.PlainDateTime.from(date).toZonedDateTime(timeZone)
79
+ return Temporal.PlainDateTime.from(date).toZonedDateTime(blogConfig.timeZone)
85
80
  }
86
81
  }
87
82
  }
@@ -110,7 +105,7 @@ export const dateTimeFormat = {
110
105
 
111
106
  export type dateTimeFormatOptions = keyof typeof dateTimeFormat | Intl.DateTimeFormatOptions
112
107
 
113
- export function toZdtLocaleString(date: string | Temporal.ZonedDateTime, format: dateTimeFormatOptions = 'full', timeZone = 'UTC') {
114
- return (typeof date === 'string' ? toZonedTemporal(date, timeZone) : date)
108
+ export function toZdtLocaleString(date: string | Temporal.ZonedDateTime, format: dateTimeFormatOptions = 'full') {
109
+ return (typeof date === 'string' ? toZonedTemporal(date) : date)
115
110
  .toLocaleString(undefined, typeof format === 'string' ? dateTimeFormat[format] : format)
116
111
  }
@@ -1,8 +1,8 @@
1
- import type { ClarityAppConfig } from '../config/app'
1
+ import type { AppConfig } from 'nuxt/schema'
2
2
  import type { ArticleSchema } from '../config/content'
3
3
  import type { MetaSlotsTree } from '../remark-plugins/rehype-meta-slots'
4
4
 
5
- export type ArticleOrderType = keyof ClarityAppConfig['article']['order']
5
+ export type ArticleOrderType = keyof AppConfig['article']['order']
6
6
 
7
7
  export interface ArticleProps extends ArticleSchema {
8
8
  path: string
package/src/types/feed.ts CHANGED
@@ -1 +1,35 @@
1
- export type { FeedEntry, FeedGroup } from '../config/feed'
1
+ export interface FeedEntry {
2
+ /** 博客作者,使用作者明确的自称 */
3
+ author: string
4
+ /** 友链卡片的作者名后缀;作者自称明确且较短时,可填简短站名,避免重复作者名 */
5
+ sitenick?: string
6
+ /** 悬浮卡片与订阅源使用的完整站点标题;与作者名相同时省略,默认使用 sitenick 或 author */
7
+ title?: string
8
+ /** 个人简介/博客描述 */
9
+ desc?: string
10
+ /** 博客地址 */
11
+ link: string
12
+ /** 订阅源 */
13
+ feed?: string
14
+ /** 站点小图标 */
15
+ icon: string
16
+ /** 个人头像 */
17
+ avatar: string
18
+ /** 博客技术架构 */
19
+ archs?: Arch[]
20
+ /** 申请/评论日期;主动收录时使用收录日期 */
21
+ date: string
22
+ /** 博主备注 */
23
+ comment?: string
24
+ /** 错误信息 */
25
+ error?: string
26
+ }
27
+
28
+ export interface FeedGroup {
29
+ /** 分组名 */
30
+ name: string
31
+ /** 描述 */
32
+ desc?: string
33
+ /** 友链列表 */
34
+ entries: FeedEntry[]
35
+ }
@@ -0,0 +1,17 @@
1
+ export {}
2
+
3
+ /**
4
+ * 上游同步代码的类型兼容垫片。
5
+ *
6
+ * blog-v3 仓库不运行 typecheck,以下问题在上游真实存在;
7
+ * 为保持同步文件 byte-identical,通过模块增强补齐类型:
8
+ */
9
+
10
+ declare module '@nuxt/content' {
11
+ interface PageCollectionItemBase {
12
+ /** 上游 atom.xml 读取 post.author(frontmatter 可选字段),上游 schema 未声明 */
13
+ author?: string
14
+ }
15
+ }
16
+
17
+ /// <reference path="./yaml-modules.d.ts" />
@@ -0,0 +1,26 @@
1
+ /**
2
+ * YAML 导入类型(与 unplugin-yaml 官方声明一致)。
3
+ *
4
+ * Theme 直接注册 unplugin-yaml/vite 插件而非其 nuxt 模块:后者会向消费项目
5
+ * compilerOptions.types 注入 pnpm 严格布局下无法解析的条目。通配符模块声明
6
+ * 必须位于非模块(script)文件中才能全局生效。
7
+ */
8
+ declare module '*.yaml' {
9
+ const value: Record<string, unknown>
10
+ export default value
11
+ }
12
+
13
+ declare module '*.yml' {
14
+ const value: Record<string, unknown>
15
+ export default value
16
+ }
17
+
18
+ declare module '*.yaml?raw' {
19
+ const value: string
20
+ export default value
21
+ }
22
+
23
+ declare module '*.yml?raw' {
24
+ const value: string
25
+ export default value
26
+ }
@@ -1,30 +0,0 @@
1
- import type { PageCollectionItemBase } from '@nuxt/content'
2
-
3
- /**
4
- * Theme 内部使用的 content 集合行类型。
5
- *
6
- * 消费项目的精确集合类型由 @nuxt/content 按站点 schema 生成,并通过
7
- * module augmentation 注入消费项目上下文。Theme 作为 node_modules Layer
8
- * 解析到的 @nuxt/content 类型实例无法稳定共享该 augmentation(pnpm 严格
9
- * 布局下会形成双实例),因此 Theme 内部只依赖自身 schema 的稳定字段。
10
- */
11
- export type ClarityContentRow = PageCollectionItemBase & {
12
- title?: string
13
- description?: string
14
- date?: string
15
- updated?: string
16
- published?: string
17
- categories?: string[]
18
- tags?: string[]
19
- type?: string
20
- image?: string
21
- recommend?: number
22
- draft?: boolean
23
- permalink?: string
24
- readingTime: {
25
- text: string
26
- minutes: number
27
- time: number
28
- words: number
29
- }
30
- }
@@ -1,34 +0,0 @@
1
- import type { ClarityAppConfig } from '../../config/app'
2
- import type { FeedEntry } from '../../config/feed'
3
- import type { ClaritySiteConfig } from '../../config/schema'
4
-
5
- /** 获取完整 Clarity 配置(站点配置 + UI 配置) */
6
- export function useClarityConfig(): ClarityAppConfig {
7
- return useAppConfig().clarity as unknown as ClarityAppConfig
8
- }
9
-
10
- /** 获取站点配置(标题、作者、URL 等) */
11
- export function useClaritySite(): ClaritySiteConfig {
12
- return useClarityConfig().site
13
- }
14
-
15
- /** 获取文章配置的客户端子集(分类图标/颜色、排序方式) */
16
- export function useClarityArticle(): ClarityAppConfig['article'] {
17
- return useClarityConfig().article
18
- }
19
-
20
- /** 由站点配置生成本站订阅源条目(用于友链页与 OPML) */
21
- export function useClaritySiteFeedEntry(): FeedEntry {
22
- const site = useClaritySite()
23
- return {
24
- author: site.author.name,
25
- title: site.title,
26
- desc: site.subtitle || site.description,
27
- link: site.url,
28
- feed: new URL('/atom.xml', site.url).toString(),
29
- icon: site.favicon,
30
- avatar: site.author.avatar || site.favicon,
31
- date: site.established || '',
32
- comment: '这是我自己',
33
- }
34
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * 与 @bikariya/modals 的 runtime/types 保持一致的类型定义。
3
- *
4
- * 该包 exports 未暴露 runtime/types 子路径,其 `#modals` 虚拟别名在
5
- * 真实 npm 消费项目中无法被 Vue SFC 编译器稳定解析,因此 Theme 内联维护。
6
- */
7
- export interface ModalProps {
8
- /** 过渡动画时长 (ms) @default 400 */
9
- duration?: number
10
- /** 模态框开关状态 @internal */
11
- open?: boolean
12
- }
13
-
14
- export interface ModalEmits {
15
- close: []
16
- }