clarity-theme 0.1.0 → 0.1.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.
Files changed (119) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +18 -4
  3. package/README.zh-CN.md +18 -4
  4. package/app/app.config.ts +110 -110
  5. package/app/app.vue +5 -5
  6. package/app/components/blog/BlogAside.vue +25 -25
  7. package/app/components/blog/BlogFooter.vue +25 -25
  8. package/app/components/blog/BlogHeader.global.vue +48 -48
  9. package/app/components/blog/BlogPanel.vue +43 -43
  10. package/app/components/blog/BlogSidebar.vue +53 -53
  11. package/app/components/blog/BlogWidget.vue +42 -42
  12. package/app/components/blog/Mask.vue +25 -25
  13. package/app/components/blog/SkipToContent.vue +9 -9
  14. package/app/components/blog/ThemeToggle.vue +22 -22
  15. package/app/components/content/Badge.vue +45 -45
  16. package/app/components/content/Blur.vue +14 -14
  17. package/app/components/content/CardList.vue +8 -8
  18. package/app/components/content/Chat.vue +41 -41
  19. package/app/components/content/Copy.vue +71 -71
  20. package/app/components/content/EmojiClock.vue +49 -49
  21. package/app/components/content/FeedCard.vue +96 -96
  22. package/app/components/content/FeedGroup.vue +61 -61
  23. package/app/components/content/Folding.vue +19 -19
  24. package/app/components/content/Key.vue +143 -143
  25. package/app/components/content/LinkBanner.vue +27 -27
  26. package/app/components/content/LinkCard.vue +28 -28
  27. package/app/components/content/MdTitle.vue +8 -8
  28. package/app/components/content/Mermaid.vue +99 -99
  29. package/app/components/content/MusicScore.vue +63 -63
  30. package/app/components/content/Pic.vue +49 -49
  31. package/app/components/content/Poetry.vue +27 -27
  32. package/app/components/content/ProseA.vue +24 -24
  33. package/app/components/content/ProseCode.vue +16 -16
  34. package/app/components/content/ProsePre.vue +117 -117
  35. package/app/components/content/ProseTable.vue +23 -23
  36. package/app/components/content/Quote.vue +21 -21
  37. package/app/components/content/Tab.vue +32 -32
  38. package/app/components/content/Timeline.vue +33 -33
  39. package/app/components/content/Tip.vue +40 -40
  40. package/app/components/content/VideoEmbed.vue +112 -112
  41. package/app/components/partial/Button.vue +36 -36
  42. package/app/components/partial/DlGroup.vue +31 -31
  43. package/app/components/partial/Dropdown.vue +21 -21
  44. package/app/components/partial/Error.vue +39 -39
  45. package/app/components/partial/Expand.vue +26 -26
  46. package/app/components/partial/IconNavList.vue +24 -24
  47. package/app/components/partial/Pagination.vue +62 -62
  48. package/app/components/partial/RadioGroup.vue +30 -30
  49. package/app/components/partial/Secret.vue +11 -11
  50. package/app/components/partial/Slider.vue +48 -48
  51. package/app/components/partial/Toggle.vue +17 -17
  52. package/app/components/popover/Lightbox.vue +44 -44
  53. package/app/components/popover/Search.vue +143 -143
  54. package/app/components/popover/SearchItem.vue +28 -28
  55. package/app/components/post/Archive.vue +40 -40
  56. package/app/components/post/Article.vue +49 -49
  57. package/app/components/post/Comment.vue +119 -119
  58. package/app/components/post/Excerpt.vue +40 -40
  59. package/app/components/post/OrderToggle.vue +70 -70
  60. package/app/components/post/PostFooter.vue +52 -52
  61. package/app/components/post/PostHeader.vue +66 -66
  62. package/app/components/post/PostSurround.vue +51 -51
  63. package/app/components/post/Slide.vue +85 -85
  64. package/app/components/util/Date.vue +62 -62
  65. package/app/components/util/HydrateSafe.vue +10 -10
  66. package/app/components/util/Img.vue +38 -38
  67. package/app/components/util/Link.vue +11 -11
  68. package/app/components/widget/BlogLog.vue +23 -23
  69. package/app/components/widget/BlogStats.vue +38 -38
  70. package/app/components/widget/BlogTech.vue +57 -57
  71. package/app/components/widget/Empty.vue +3 -3
  72. package/app/components/widget/Toc.vue +67 -67
  73. package/app/composables/useArticle.ts +113 -113
  74. package/app/composables/useAvoid.ts +87 -87
  75. package/app/composables/useCopy.ts +21 -21
  76. package/app/composables/usePagination.ts +70 -70
  77. package/app/composables/useShiki.ts +129 -129
  78. package/app/composables/useToc.ts +57 -57
  79. package/app/composables/useWidgets.ts +56 -56
  80. package/app/error.vue +31 -31
  81. package/app/layouts/default.vue +25 -25
  82. package/app/pages/[...slug].vue +59 -59
  83. package/app/pages/archive.vue +129 -129
  84. package/app/pages/index.vue +76 -76
  85. package/app/pages/link.vue +62 -62
  86. package/app/pages/preview.vue +46 -46
  87. package/app/plugins/easter-egg.ts +23 -23
  88. package/app/plugins/init.ts +6 -6
  89. package/app/plugins/tippy.ts +22 -22
  90. package/app/shiki.config.ts +8 -8
  91. package/app/stores/layout.ts +34 -34
  92. package/app/stores/search.ts +37 -37
  93. package/app/types/article.ts +16 -16
  94. package/app/types/index.ts +14 -14
  95. package/app/types/nav.ts +11 -11
  96. package/app/utils/anim.ts +37 -37
  97. package/app/utils/img.ts +83 -83
  98. package/config/app.ts +61 -61
  99. package/config/content.ts +75 -75
  100. package/config/define.ts +3 -3
  101. package/config/feeds.empty.ts +6 -4
  102. package/config/public.ts +1 -1
  103. package/config/schema.ts +89 -89
  104. package/config/server.ts +1 -1
  105. package/modules/clarity-config/index.ts +162 -159
  106. package/nuxt.config.ts +212 -212
  107. package/package.json +25 -21
  108. package/public/assets/atom.css +274 -274
  109. package/public/assets/atom.xsl +109 -109
  110. package/remark-plugins/rehype-meta-slots.ts +58 -58
  111. package/remark-plugins/remark-code-component.ts +41 -41
  112. package/server/api/stats.get.ts +84 -83
  113. package/server/routes/atom.xml.get.ts +85 -85
  114. package/server/routes/subscriptions.opml.get.ts +64 -64
  115. package/shared/utils/clarity.ts +24 -24
  116. package/shared/utils/icon.ts +218 -218
  117. package/shared/utils/link.ts +52 -52
  118. package/shared/utils/str.ts +88 -88
  119. package/shared/utils/time.ts +115 -115
@@ -1,101 +1,101 @@
1
1
  import type { ClarityContentRow } from '../../shared/types/content'
2
2
  import { queryCollection } from '@nuxt/content/server'
3
3
  import XmlBuilder from 'fast-xml-builder'
4
- import { toZonedTemporal } from '../../shared/utils/time'
5
-
6
- const runtimeConfig = useRuntimeConfig()
7
-
8
- const builder = new XmlBuilder({
9
- attributeNamePrefix: '$',
10
- cdataPropName: '$',
11
- format: true,
12
- ignoreAttributes: false,
13
- textNodeName: '_',
14
- })
15
-
4
+ import { toZonedTemporal } from '../../shared/utils/time'
5
+
6
+ const runtimeConfig = useRuntimeConfig()
7
+
8
+ const builder = new XmlBuilder({
9
+ attributeNamePrefix: '$',
10
+ cdataPropName: '$',
11
+ format: true,
12
+ ignoreAttributes: false,
13
+ textNodeName: '_',
14
+ })
15
+
16
16
  export default defineEventHandler(async (event) => {
17
17
  const { site, feed, features } = useClarityServerConfig()
18
18
  if (!features.atom) {
19
19
  throw createError({ statusCode: 404, statusMessage: 'Atom feed is disabled' })
20
20
  }
21
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
- }
33
- }
34
-
35
- function getUrl(path: string | undefined) {
36
- return new URL(path ?? '', site.url).toString()
37
- }
38
-
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
+ }
33
+ }
34
+
35
+ function getUrl(path: string | undefined) {
36
+ return new URL(path ?? '', site.url).toString()
37
+ }
38
+
39
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
- }
46
-
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
+ }
46
+
47
47
  const posts = await queryCollection(event, 'content' as never)
48
48
  .where('stem', 'LIKE', 'posts/%')
49
49
  .order('updated', 'DESC')
50
50
  .limit(feed.limit)
51
51
  .all() as ClarityContentRow[]
52
-
53
- const entries = posts.map(post => ({
54
- id: getUrl(post.path),
55
- title: post.title ?? '',
56
- updated: formatIsoDate(post.updated),
52
+
53
+ const entries = posts.map(post => ({
54
+ id: getUrl(post.path),
55
+ title: post.title ?? '',
56
+ updated: formatIsoDate(post.updated),
57
57
  author: { name: (post as any).author || site.author.name },
58
- content: {
59
- $type: 'html',
60
- $: renderContent(post),
61
- },
62
- link: { $href: getUrl(post.path) },
63
- summary: post.description,
64
- category: { $term: post.categories?.[0] },
65
- published: formatIsoDate(post.published ?? post.date),
66
- }))
67
-
68
- const feedData = {
69
- $xmlns: 'http://www.w3.org/2005/Atom',
70
- id: site.url,
71
- title: site.title,
72
- updated: runtimeConfig.public.buildTime,
73
- description: site.description,
74
- author: {
75
- name: site.author.name,
76
- email: site.author.email,
77
- uri: site.author.homepage,
78
- },
79
- link: [
80
- { $href: getUrl('atom.xml'), $rel: 'self' },
81
- { $href: site.url, $rel: 'alternate' },
82
- ],
83
- language: site.language,
84
- generator: {
85
- $uri: themeInfo.themeHomepage,
86
- $version: themeInfo.themeVersion,
87
- _: themeInfo.theme,
88
- },
89
- icon: site.favicon,
90
- logo: site.author.avatar,
91
- rights: `© ${new Date().getFullYear()} ${site.author.name}`,
92
- subtitle: site.subtitle || site.description,
93
- entry: entries,
94
- }
95
-
96
- return builder.build({
97
- '?xml': { $version: '1.0', $encoding: 'UTF-8' },
98
- '?xml-stylesheet': feed.enableStyle ? { $type: 'text/xsl', $href: '/assets/atom.xsl' } : undefined,
99
- 'feed': feedData,
100
- })
101
- })
58
+ content: {
59
+ $type: 'html',
60
+ $: renderContent(post),
61
+ },
62
+ link: { $href: getUrl(post.path) },
63
+ summary: post.description,
64
+ category: { $term: post.categories?.[0] },
65
+ published: formatIsoDate(post.published ?? post.date),
66
+ }))
67
+
68
+ const feedData = {
69
+ $xmlns: 'http://www.w3.org/2005/Atom',
70
+ id: site.url,
71
+ title: site.title,
72
+ updated: runtimeConfig.public.buildTime,
73
+ description: site.description,
74
+ author: {
75
+ name: site.author.name,
76
+ email: site.author.email,
77
+ uri: site.author.homepage,
78
+ },
79
+ link: [
80
+ { $href: getUrl('atom.xml'), $rel: 'self' },
81
+ { $href: site.url, $rel: 'alternate' },
82
+ ],
83
+ language: site.language,
84
+ generator: {
85
+ $uri: themeInfo.themeHomepage,
86
+ $version: themeInfo.themeVersion,
87
+ _: themeInfo.theme,
88
+ },
89
+ icon: site.favicon,
90
+ logo: site.author.avatar,
91
+ rights: `© ${new Date().getFullYear()} ${site.author.name}`,
92
+ subtitle: site.subtitle || site.description,
93
+ entry: entries,
94
+ }
95
+
96
+ return builder.build({
97
+ '?xml': { $version: '1.0', $encoding: 'UTF-8' },
98
+ '?xml-stylesheet': feed.enableStyle ? { $type: 'text/xsl', $href: '/assets/atom.xsl' } : undefined,
99
+ 'feed': feedData,
100
+ })
101
+ })
@@ -1,70 +1,70 @@
1
- import type { FeedEntry, FeedGroup } from '../../app/types/feed'
2
- import XmlBuilder from 'fast-xml-builder'
3
- import { toZonedTemporal } from '../../shared/utils/time'
4
-
5
- const runtimeConfig = useRuntimeConfig()
6
-
7
- const builder = new XmlBuilder({
8
- attributeNamePrefix: '$',
9
- format: true,
10
- ignoreAttributes: false,
11
- })
12
-
13
- function mapEntry(item: FeedEntry, timeZone: string) {
14
- return {
15
- $text: item.title || item.sitenick || item.author,
16
- $type: 'rss',
17
- $xmlUrl: item.feed,
18
- $created: toZonedTemporal(item.date, timeZone).toInstant().toString(),
19
- $description: item.desc,
20
- $htmlUrl: item.link || item.feed,
21
- }
22
- }
23
-
24
- function flattenGroups(groups: FeedGroup[], timeZone: string) {
25
- return groups.flatMap(({ entries }) => entries.filter(({ feed }) => feed).map(item => mapEntry(item, timeZone)))
26
- }
27
-
1
+ import type { FeedEntry, FeedGroup } from '../../app/types/feed'
2
+ import XmlBuilder from 'fast-xml-builder'
3
+ import { toZonedTemporal } from '../../shared/utils/time'
4
+
5
+ const runtimeConfig = useRuntimeConfig()
6
+
7
+ const builder = new XmlBuilder({
8
+ attributeNamePrefix: '$',
9
+ format: true,
10
+ ignoreAttributes: false,
11
+ })
12
+
13
+ function mapEntry(item: FeedEntry, timeZone: string) {
14
+ return {
15
+ $text: item.title || item.sitenick || item.author,
16
+ $type: 'rss',
17
+ $xmlUrl: item.feed,
18
+ $created: toZonedTemporal(item.date, timeZone).toInstant().toString(),
19
+ $description: item.desc,
20
+ $htmlUrl: item.link || item.feed,
21
+ }
22
+ }
23
+
24
+ function flattenGroups(groups: FeedGroup[], timeZone: string) {
25
+ return groups.flatMap(({ entries }) => entries.filter(({ feed }) => feed).map(item => mapEntry(item, timeZone)))
26
+ }
27
+
28
28
  export default defineEventHandler(async () => {
29
29
  const { site, features } = useClarityServerConfig()
30
30
  if (!features.opml) {
31
31
  throw createError({ statusCode: 404, statusMessage: 'OPML subscription output is disabled' })
32
32
  }
33
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
-
45
- const outlines = [
46
- mapEntry(myFeed, site.timezone),
47
- ...flattenGroups(feeds, site.timezone),
48
- ]
49
-
50
- const opml = {
51
- $version: '2.0',
52
- head: {
53
- title: `${site.title}的友链订阅`,
54
- dateCreated: site.established
55
- ? toZonedTemporal(site.established, site.timezone).toInstant().toString()
56
- : undefined,
57
- dateModified: runtimeConfig.public.buildTime,
58
- ownerName: site.author.name,
59
- ownerEmail: site.author.email,
60
- ownerId: site.author.homepage,
61
- docs: 'https://opml.org/spec2.opml',
62
- },
63
- body: { outline: outlines },
64
- }
65
-
66
- return builder.build({
67
- '?xml': { $version: '1.0', $encoding: 'UTF-8' },
68
- opml,
69
- })
70
- })
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
+
45
+ const outlines = [
46
+ mapEntry(myFeed, site.timezone),
47
+ ...flattenGroups(feeds, site.timezone),
48
+ ]
49
+
50
+ const opml = {
51
+ $version: '2.0',
52
+ head: {
53
+ title: `${site.title}的友链订阅`,
54
+ dateCreated: site.established
55
+ ? toZonedTemporal(site.established, site.timezone).toInstant().toString()
56
+ : undefined,
57
+ dateModified: runtimeConfig.public.buildTime,
58
+ ownerName: site.author.name,
59
+ ownerEmail: site.author.email,
60
+ ownerId: site.author.homepage,
61
+ docs: 'https://opml.org/spec2.opml',
62
+ },
63
+ body: { outline: outlines },
64
+ }
65
+
66
+ return builder.build({
67
+ '?xml': { $version: '1.0', $encoding: 'UTF-8' },
68
+ opml,
69
+ })
70
+ })
@@ -1,34 +1,34 @@
1
1
  import type { ClarityAppConfig } from '../../config/app'
2
2
  import type { FeedEntry } from '../../config/feed'
3
3
  import type { ClaritySiteConfig } from '../../config/schema'
4
-
5
- /** 获取完整 Clarity 配置(站点配置 + UI 配置) */
4
+
5
+ /** 获取完整 Clarity 配置(站点配置 + UI 配置) */
6
6
  export function useClarityConfig(): ClarityAppConfig {
7
7
  return useAppConfig().clarity as unknown as ClarityAppConfig
8
8
  }
9
-
10
- /** 获取站点配置(标题、作者、URL 等) */
11
- export function useClaritySite(): ClaritySiteConfig {
12
- return useClarityConfig().site
13
- }
14
-
9
+
10
+ /** 获取站点配置(标题、作者、URL 等) */
11
+ export function useClaritySite(): ClaritySiteConfig {
12
+ return useClarityConfig().site
13
+ }
14
+
15
15
  /** 获取文章配置的客户端子集(分类图标/颜色、排序方式) */
16
16
  export function useClarityArticle(): ClarityAppConfig['article'] {
17
17
  return useClarityConfig().article
18
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
- }
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
+ }