clarity-theme 0.1.0 → 0.1.2

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 (154) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +70 -4
  3. package/README.zh-CN.md +67 -4
  4. package/nuxt.config.ts +217 -214
  5. package/package.json +35 -37
  6. package/{app → src}/app.config.ts +110 -110
  7. package/{app → src}/app.vue +5 -5
  8. package/{app → src}/components/blog/BlogAside.vue +25 -25
  9. package/{app → src}/components/blog/BlogFooter.vue +25 -25
  10. package/{app → src}/components/blog/BlogHeader.global.vue +48 -48
  11. package/{app → src}/components/blog/BlogPanel.vue +43 -43
  12. package/{app → src}/components/blog/BlogSidebar.vue +53 -53
  13. package/{app → src}/components/blog/BlogWidget.vue +42 -42
  14. package/{app → src}/components/blog/Mask.vue +25 -25
  15. package/{app → src}/components/blog/SkipToContent.vue +9 -9
  16. package/{app → src}/components/blog/ThemeToggle.vue +22 -22
  17. package/{app → src}/components/content/Badge.vue +45 -45
  18. package/{app → src}/components/content/Blur.vue +14 -14
  19. package/{app → src}/components/content/CardList.vue +8 -8
  20. package/{app → src}/components/content/Chat.vue +41 -41
  21. package/{app → src}/components/content/Copy.vue +71 -71
  22. package/{app → src}/components/content/EmojiClock.vue +49 -49
  23. package/{app → src}/components/content/FeedCard.vue +96 -96
  24. package/{app → src}/components/content/FeedGroup.vue +61 -61
  25. package/{app → src}/components/content/Folding.vue +19 -19
  26. package/{app → src}/components/content/Key.vue +143 -143
  27. package/{app → src}/components/content/LinkBanner.vue +27 -27
  28. package/{app → src}/components/content/LinkCard.vue +28 -28
  29. package/{app → src}/components/content/MdTitle.vue +8 -8
  30. package/{app → src}/components/content/Mermaid.vue +99 -99
  31. package/{app → src}/components/content/MusicScore.vue +63 -63
  32. package/{app → src}/components/content/Pic.vue +49 -49
  33. package/{app → src}/components/content/Poetry.vue +27 -27
  34. package/{app → src}/components/content/ProseA.vue +24 -24
  35. package/{app → src}/components/content/ProseCode.vue +16 -16
  36. package/{app → src}/components/content/ProsePre.vue +117 -117
  37. package/{app → src}/components/content/ProseTable.vue +23 -23
  38. package/{app → src}/components/content/Quote.vue +21 -21
  39. package/{app → src}/components/content/Tab.vue +32 -32
  40. package/{app → src}/components/content/Timeline.vue +33 -33
  41. package/{app → src}/components/content/Tip.vue +40 -40
  42. package/{app → src}/components/content/VideoEmbed.vue +112 -112
  43. package/{app → src}/components/partial/Button.vue +36 -36
  44. package/{app → src}/components/partial/DlGroup.vue +31 -31
  45. package/{app → src}/components/partial/Dropdown.vue +21 -21
  46. package/{app → src}/components/partial/Error.vue +39 -39
  47. package/{app → src}/components/partial/Expand.vue +26 -26
  48. package/{app → src}/components/partial/IconNavList.vue +24 -24
  49. package/{app → src}/components/partial/Pagination.vue +62 -62
  50. package/{app → src}/components/partial/RadioGroup.vue +30 -30
  51. package/{app → src}/components/partial/Secret.vue +11 -11
  52. package/{app → src}/components/partial/Slider.vue +48 -48
  53. package/{app → src}/components/partial/Toggle.vue +17 -17
  54. package/{app → src}/components/popover/Lightbox.vue +44 -44
  55. package/{app → src}/components/popover/Search.vue +143 -143
  56. package/{app → src}/components/popover/SearchItem.vue +28 -28
  57. package/{app → src}/components/post/Archive.vue +40 -40
  58. package/{app → src}/components/post/Article.vue +49 -49
  59. package/{app → src}/components/post/Comment.vue +119 -119
  60. package/{app → src}/components/post/Excerpt.vue +40 -40
  61. package/{app → src}/components/post/OrderToggle.vue +70 -70
  62. package/{app → src}/components/post/PostFooter.vue +52 -52
  63. package/{app → src}/components/post/PostHeader.vue +66 -66
  64. package/{app → src}/components/post/PostSurround.vue +51 -51
  65. package/{app → src}/components/post/Slide.vue +85 -85
  66. package/{app → src}/components/util/Date.vue +62 -62
  67. package/{app → src}/components/util/HydrateSafe.vue +10 -10
  68. package/{app → src}/components/util/Img.vue +38 -38
  69. package/{app → src}/components/util/Link.vue +11 -11
  70. package/{app → src}/components/widget/BlogLog.vue +23 -23
  71. package/{app → src}/components/widget/BlogStats.vue +38 -38
  72. package/{app → src}/components/widget/BlogTech.vue +57 -57
  73. package/{app → src}/components/widget/Empty.vue +3 -3
  74. package/{app → src}/components/widget/Toc.vue +67 -67
  75. package/{app → src}/composables/useArticle.ts +115 -115
  76. package/{app → src}/composables/useAvoid.ts +87 -87
  77. package/{app → src}/composables/useCopy.ts +21 -21
  78. package/{app → src}/composables/usePagination.ts +70 -70
  79. package/{app → src}/composables/useShiki.ts +129 -129
  80. package/{app → src}/composables/useToc.ts +57 -57
  81. package/{app → src}/composables/useWidgets.ts +56 -56
  82. package/{config → src/config}/app.ts +62 -62
  83. package/{config → src/config}/content.ts +75 -75
  84. package/{config → src/config}/define.ts +3 -3
  85. package/src/config/feeds.empty.ts +6 -0
  86. package/{config → src/config}/index.ts +1 -1
  87. package/{config → src/config}/schema.ts +89 -89
  88. package/{app → src}/error.vue +31 -31
  89. package/{img → src/img}/index.ts +2 -2
  90. package/{app → src}/layouts/default.vue +25 -25
  91. package/{modules → src/modules}/clarity-config/index.ts +168 -162
  92. package/src/modules/clarity-source-layout/index.ts +78 -0
  93. package/{app → src}/pages/[...slug].vue +59 -59
  94. package/{app → src}/pages/archive.vue +129 -129
  95. package/{app → src}/pages/index.vue +76 -76
  96. package/{app → src}/pages/link.vue +62 -62
  97. package/{app → src}/pages/preview.vue +46 -46
  98. package/{app → src}/plugins/easter-egg.ts +23 -23
  99. package/{app → src}/plugins/init.ts +6 -6
  100. package/{app → src}/plugins/tippy.ts +22 -22
  101. package/{public → src/public}/assets/atom.css +274 -274
  102. package/{public → src/public}/assets/atom.xsl +109 -109
  103. package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.ts +58 -58
  104. package/{remark-plugins → src/remark-plugins}/remark-code-component.ts +41 -41
  105. package/{server → src/server}/api/stats.get.ts +84 -83
  106. package/{server → src/server}/routes/atom.xml.get.ts +85 -85
  107. package/{server → src/server}/routes/subscriptions.opml.get.ts +64 -64
  108. package/{shared → src/shared}/utils/clarity.ts +24 -24
  109. package/{shared → src/shared}/utils/icon.ts +218 -218
  110. package/{shared → src/shared}/utils/link.ts +52 -52
  111. package/{shared → src/shared}/utils/str.ts +88 -88
  112. package/{shared → src/shared}/utils/time.ts +115 -115
  113. package/{app → src}/shiki.config.ts +8 -8
  114. package/{app → src}/stores/layout.ts +34 -34
  115. package/{app → src}/stores/search.ts +37 -37
  116. package/{app → src}/types/article.ts +16 -16
  117. package/src/types/feed.ts +1 -0
  118. package/{app → src}/types/index.ts +14 -14
  119. package/{app → src}/types/nav.ts +11 -11
  120. package/{app → src}/utils/anim.ts +37 -37
  121. package/{app → src}/utils/img.ts +83 -83
  122. package/app/types/feed.ts +0 -1
  123. package/config/feeds.empty.ts +0 -4
  124. package/{app → src}/assets/css/_variable.scss +0 -0
  125. package/{app → src}/assets/css/animation.scss +0 -0
  126. package/{app → src}/assets/css/article.scss +0 -0
  127. package/{app → src}/assets/css/color.scss +0 -0
  128. package/{app → src}/assets/css/font.scss +0 -0
  129. package/{app → src}/assets/css/main.scss +0 -0
  130. package/{app → src}/assets/css/reusable.scss +0 -0
  131. package/{app → src}/assets/icons/solar-rewind-back-bold-duotone.svg +0 -0
  132. package/{app → src}/assets/icons/solar-rewind-forward-bold-duotone.svg +0 -0
  133. package/{app → src}/components/content/Alert.vue +0 -0
  134. package/{config → src/config}/content.d.mts +0 -0
  135. package/{config → src/config}/content.mjs +0 -0
  136. package/{config → src/config}/define.mjs +0 -0
  137. package/{config → src/config}/feed.ts +0 -0
  138. package/{config → src/config}/index.d.mts +0 -0
  139. package/{config → src/config}/index.mjs +0 -0
  140. package/{config → src/config}/public.ts +1 -1
  141. package/{config → src/config}/schema.d.mts +0 -0
  142. package/{config → src/config}/schema.mjs +0 -0
  143. package/{config → src/config}/server.ts +1 -1
  144. /package/{img → src/img}/index.d.mts +0 -0
  145. /package/{img → src/img}/index.mjs +0 -0
  146. /package/{modules → src/modules}/clarity-config/anti-mirror-client.ts +0 -0
  147. /package/{public → src/public}/fonts/AlimamaFangYuanTi.woff2 +0 -0
  148. /package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.d.mts +0 -0
  149. /package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.mjs +0 -0
  150. /package/{remark-plugins → src/remark-plugins}/remark-code-component.d.mts +0 -0
  151. /package/{remark-plugins → src/remark-plugins}/remark-code-component.mjs +0 -0
  152. /package/{server → src/server}/utils/clarity.ts +0 -0
  153. /package/{shared → src/shared}/types/content.ts +0 -0
  154. /package/{app → src}/types/modal.ts +0 -0
@@ -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 '../../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
+ }