clarity-theme 0.1.3 → 0.1.4
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 +12 -2
- package/README.zh-CN.md +12 -2
- package/nuxt.config.ts +17 -1
- package/package.json +6 -2
- package/src/app.config.ts +8 -104
- package/src/assets/icons/zhilu.svg +8 -0
- package/src/blog.config.ts +71 -0
- package/src/components/blog/BlogFooter.vue +3 -3
- package/src/components/blog/BlogHeader.global.vue +9 -9
- package/src/components/blog/BlogSidebar.vue +3 -3
- package/src/components/blog/ThemeToggle.vue +2 -2
- package/src/components/content/Alert.vue +2 -2
- package/src/components/content/FeedCard.vue +2 -2
- package/src/components/content/Mermaid.vue +1 -1
- package/src/components/content/ProseCode.vue +4 -32
- package/src/components/content/ProsePre.vue +2 -2
- package/src/components/content/ProseTable.vue +1 -1
- package/src/components/popover/Search.vue +3 -3
- package/src/components/post/Comment.vue +3 -15
- package/src/components/post/Excerpt.vue +3 -3
- package/src/components/post/OrderToggle.vue +3 -3
- package/src/components/post/PostFooter.vue +3 -4
- package/src/components/post/PostHeader.vue +4 -4
- package/src/components/post/PostSurround.vue +2 -2
- package/src/components/post/Slide.vue +2 -2
- package/src/components/util/Img.vue +1 -1
- package/src/components/widget/BlogLog.vue +9 -19
- package/src/components/widget/BlogStats.vue +4 -4
- package/src/components/widget/BlogTech.vue +16 -17
- package/src/components/widget/CommGroup.vue +43 -0
- package/src/composables/useArticle.ts +9 -9
- package/src/composables/usePagination.ts +2 -2
- package/src/composables/useWidgets.ts +2 -0
- package/src/config/app.ts +50 -4
- package/src/config/content.ts +6 -0
- package/src/config/feed.ts +2 -42
- package/src/config/pnpm-workspace.ts +19 -0
- package/src/config/ui.ts +111 -0
- package/src/generated/blog.config.mjs +3 -0
- package/src/generated/package-json.mjs +4 -0
- package/src/generated/pnpm-workspace.mjs +1 -0
- package/src/modules/anti-mirror/index.ts +33 -0
- package/src/modules/{clarity-config/anti-mirror-client.ts → anti-mirror/runtime/client.ts} +3 -3
- package/src/modules/clarity-config/index.ts +335 -77
- package/src/modules/clarity-source-layout/index.ts +13 -0
- package/src/pages/archive.vue +4 -4
- package/src/pages/index.vue +4 -3
- package/src/pages/link.vue +5 -5
- package/src/pages/preview.vue +2 -2
- package/src/server/api/stats.get.ts +9 -19
- package/src/server/routes/atom.xml.get.ts +48 -50
- package/src/server/routes/subscriptions.opml.get.ts +14 -31
- package/src/shared/utils/time.ts +13 -18
- package/src/types/article.ts +2 -2
- package/src/types/feed.ts +35 -1
- package/src/types/upstream-compat.d.ts +17 -0
- package/src/types/yaml-modules.d.ts +26 -0
- package/src/shared/types/content.ts +0 -30
- package/src/types/modal.ts +0 -16
- /package/src/assets/icons/{solar-rewind-back-bold-duotone.svg → SolarRewindBackBoldDuotone.svg} +0 -0
- /package/src/assets/icons/{solar-rewind-forward-bold-duotone.svg → SolarRewindForwardBoldDuotone.svg} +0 -0
package/src/pages/link.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { myFeed } from '~~/blog.config'
|
|
3
|
+
import feeds from '~/feeds'
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
const myFeed = useClaritySiteFeedEntry()
|
|
5
|
+
const appConfig = useAppConfig()
|
|
6
6
|
|
|
7
7
|
const { data: postLink } = await useAsyncData(
|
|
8
8
|
'content:/link',
|
|
@@ -12,7 +12,7 @@ const { data: postLink } = await useAsyncData(
|
|
|
12
12
|
useSeoMeta({
|
|
13
13
|
title: '友链',
|
|
14
14
|
ogType: 'profile',
|
|
15
|
-
description: `${
|
|
15
|
+
description: `${appConfig.title}的友链页面,收集了添加他为友链的网站和他订阅的网站列表。`,
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
const copyFields = {
|
|
@@ -33,7 +33,7 @@ const copyFields = {
|
|
|
33
33
|
v-for="group in feeds"
|
|
34
34
|
:key="group.name"
|
|
35
35
|
v-bind="group"
|
|
36
|
-
:shuffle="
|
|
36
|
+
:shuffle="appConfig.link.randomInGroup"
|
|
37
37
|
/>
|
|
38
38
|
|
|
39
39
|
<Tab :tabs="['我的博客信息', '申请友链']" center>
|
package/src/pages/preview.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
const
|
|
2
|
+
const appConfig = useAppConfig()
|
|
3
3
|
useSeoMeta({
|
|
4
4
|
title: '预览',
|
|
5
|
-
description: `${
|
|
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
|
|
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'
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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()
|
|
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 || ''
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
function formatIsoDate(date?: string) {
|
|
21
|
+
if (!date)
|
|
22
|
+
return
|
|
23
|
+
try {
|
|
24
|
+
return toZonedTemporal(date).toInstant().toString()
|
|
20
25
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
function getUrl(path: string | undefined) {
|
|
33
|
+
return new URL(path ?? '', blogConfig.url).toString()
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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()
|
|
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:
|
|
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
|
|
66
|
+
const feed = {
|
|
69
67
|
$xmlns: 'http://www.w3.org/2005/Atom',
|
|
70
|
-
id:
|
|
71
|
-
title:
|
|
68
|
+
id: blogConfig.url,
|
|
69
|
+
title: blogConfig.title,
|
|
72
70
|
updated: runtimeConfig.public.buildTime,
|
|
73
|
-
description:
|
|
71
|
+
description: blogConfig.description, // RSS 2.0
|
|
74
72
|
author: {
|
|
75
|
-
name:
|
|
76
|
-
email:
|
|
77
|
-
uri:
|
|
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:
|
|
79
|
+
{ $href: blogConfig.url, $rel: 'alternate' },
|
|
82
80
|
],
|
|
83
|
-
language:
|
|
81
|
+
language: blogConfig.language, // RSS 2.0
|
|
84
82
|
generator: {
|
|
85
|
-
$uri:
|
|
86
|
-
$version:
|
|
87
|
-
_:
|
|
83
|
+
$uri: 'https://github.com/L33Z22L11/blog-v3',
|
|
84
|
+
$version: packageJson.version,
|
|
85
|
+
_: pascalCase(packageJson.name),
|
|
88
86
|
},
|
|
89
|
-
icon:
|
|
90
|
-
logo:
|
|
91
|
-
rights: `© ${
|
|
92
|
-
subtitle:
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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[]
|
|
25
|
-
return groups.flatMap(({ entries }) => entries.filter(({ feed }) => feed).map(
|
|
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
|
|
47
|
-
...flattenGroups(feeds
|
|
31
|
+
mapEntry(myFeed),
|
|
32
|
+
...flattenGroups(feeds),
|
|
48
33
|
]
|
|
49
34
|
|
|
50
35
|
const opml = {
|
|
51
36
|
$version: '2.0',
|
|
52
37
|
head: {
|
|
53
|
-
title: `${
|
|
54
|
-
dateCreated:
|
|
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:
|
|
59
|
-
ownerEmail:
|
|
60
|
-
ownerId:
|
|
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 },
|
package/src/shared/utils/time.ts
CHANGED
|
@@ -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:
|
|
4
|
+
export function isSameUnit(date1: string, date2: string, unit: Temporal.DateTimeUnit) {
|
|
4
5
|
try {
|
|
5
|
-
const p1 = toZonedTemporal(date1
|
|
6
|
-
const p2 = toZonedTemporal(date2
|
|
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
|
|
33
|
-
const diff2 = now - toZonedTemporal(date2
|
|
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
|
|
67
|
-
return (typeof date === 'string' ? toZonedTemporal(date
|
|
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'
|
|
114
|
-
return (typeof date === 'string' ? toZonedTemporal(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
|
}
|
package/src/types/article.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
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
|
|
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
|
-
}
|
package/src/types/modal.ts
DELETED
|
@@ -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
|
-
}
|
/package/src/assets/icons/{solar-rewind-back-bold-duotone.svg → SolarRewindBackBoldDuotone.svg}
RENAMED
|
File without changes
|
|
File without changes
|