astro-pure 1.3.2 → 1.3.3
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/bun.lock +137 -199
- package/components/advanced/GithubCard.astro +55 -40
- package/components/advanced/index.ts +0 -3
- package/components/basic/Footer.astro +1 -1
- package/components/basic/Header.astro +1 -1
- package/components/pages/Copyright.astro +10 -10
- package/components/pages/Hero.astro +3 -14
- package/components/pages/index.ts +0 -1
- package/index.ts +8 -4
- package/package.json +3 -3
- package/{utils → plugins}/rehype-external-links.ts +2 -1
- package/plugins/rehype-table.ts +37 -0
- package/types/theme-config.ts +13 -4
- package/utils/index.ts +1 -0
- package/utils/is-absolute-url.ts +35 -0
- package/utils/mdast-util-to-string.ts +13 -0
- package/components/advanced/Comment.astro +0 -148
- package/components/pages/PageInfo.astro +0 -18
|
@@ -13,49 +13,57 @@ const [owner, repoName] = repo.split('/')
|
|
|
13
13
|
<a
|
|
14
14
|
href={`https://github.com/${repo}`}
|
|
15
15
|
target='_blank'
|
|
16
|
-
class='group block flex flex-col gap-y-
|
|
16
|
+
class='group block flex flex-col gap-y-2 rounded-xl border px-4 py-3 transition-colors hover:bg-muted hover:text-muted-foreground sm:px-5 sm:py-4'
|
|
17
17
|
>
|
|
18
|
-
<div class='flex items-center justify-between'>
|
|
19
|
-
<div
|
|
18
|
+
<div class='flex items-center justify-between gap-x-2'>
|
|
19
|
+
<div
|
|
20
|
+
class='flex min-w-0 flex-1 items-center gap-x-2 text-foreground group-hover:text-primary'
|
|
21
|
+
>
|
|
20
22
|
<div
|
|
21
23
|
id='gh-avatar'
|
|
22
|
-
class='
|
|
23
|
-
style='border-radius:999px
|
|
24
|
+
class='load-block me-2 size-7 flex-shrink-0 bg-cover sm:size-8'
|
|
25
|
+
style='border-radius:999px'
|
|
24
26
|
>
|
|
25
27
|
</div>
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
<div class='min-w-0 flex-1'>
|
|
29
|
+
<div class='flex items-center gap-x-1 max-sm:flex-wrap'>
|
|
30
|
+
<span class='truncate text-base transition-colors sm:text-lg'>{owner}</span>
|
|
31
|
+
<span class='text-muted-foreground max-sm:hidden'>/</span>
|
|
32
|
+
<span class='truncate text-base font-bold transition-colors sm:text-lg max-sm:w-full'
|
|
33
|
+
>{repoName}</span
|
|
34
|
+
>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
29
37
|
</div>
|
|
30
|
-
<div class='rounded-full bg-primary-foreground p-1'>
|
|
38
|
+
<div class='flex-shrink-0 rounded-full bg-primary-foreground p-1'>
|
|
31
39
|
<Icon name='github' />
|
|
32
40
|
</div>
|
|
33
41
|
</div>
|
|
34
|
-
<p id='gh-description' class='
|
|
35
|
-
<div class='
|
|
36
|
-
<div class='
|
|
37
|
-
<div class='flex items-center gap-x-2'>
|
|
42
|
+
<p id='gh-description' class='load-block text-sm sm:text-base'>Waiting for api.github.com...</p>
|
|
43
|
+
<div class='flex items-center justify-between gap-x-2'>
|
|
44
|
+
<div class='load-block flex flex-wrap items-center gap-x-3 gap-y-1 sm:gap-x-5'>
|
|
45
|
+
<div class='flex items-center gap-x-1.5 sm:gap-x-2'>
|
|
38
46
|
{/* mingcute:star-line */}
|
|
39
47
|
<!-- prettier-ignore -->
|
|
40
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M10.92 2.868a1.25 1.25 0 0 1 2.16 0l2.795 4.798l5.428 1.176a1.25 1.25 0 0 1 .667 2.054l-3.7 4.141l.56 5.525a1.25 1.25 0 0 1-1.748 1.27L12 19.592l-5.082 2.24a1.25 1.25 0 0 1-1.748-1.27l.56-5.525l-3.7-4.14a1.25 1.25 0 0 1 .667-2.055l5.428-1.176zM12 4.987L9.687 8.959a1.25 1.25 0 0 1-.816.592l-4.492.973l3.062 3.427c.234.262.347.61.312.959l-.463 4.573l4.206-1.854a1.25 1.25 0 0 1 1.008 0l4.206 1.854l-.463-4.573a1.25 1.25 0 0 1 .311-.959l3.063-3.427l-4.492-.973a1.25 1.25 0 0 1-.816-.592z"/></g></svg>
|
|
41
|
-
<span id='gh-stars' class='leading-tight'>???</span>
|
|
48
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="sm:w-[22px] sm:h-[22px]"><g fill="none" fill-rule="evenodd"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M10.92 2.868a1.25 1.25 0 0 1 2.16 0l2.795 4.798l5.428 1.176a1.25 1.25 0 0 1 .667 2.054l-3.7 4.141l.56 5.525a1.25 1.25 0 0 1-1.748 1.27L12 19.592l-5.082 2.24a1.25 1.25 0 0 1-1.748-1.27l.56-5.525l-3.7-4.14a1.25 1.25 0 0 1 .667-2.055l5.428-1.176zM12 4.987L9.687 8.959a1.25 1.25 0 0 1-.816.592l-4.492.973l3.062 3.427c.234.262.347.61.312.959l-.463 4.573l4.206-1.854a1.25 1.25 0 0 1 1.008 0l4.206 1.854l-.463-4.573a1.25 1.25 0 0 1 .311-.959l3.063-3.427l-4.492-.973a1.25 1.25 0 0 1-.816-.592z"/></g></svg>
|
|
49
|
+
<span id='gh-stars' class='text-sm leading-tight sm:text-base'>???</span>
|
|
42
50
|
</div>
|
|
43
51
|
|
|
44
|
-
<div class='flex items-center gap-x-2'>
|
|
52
|
+
<div class='flex items-center gap-x-1.5 sm:gap-x-2'>
|
|
45
53
|
{/* mingcute:git-branch-line */}
|
|
46
54
|
<!-- prettier-ignore -->
|
|
47
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M18 3a3 3 0 0 1 1 5.83V9a4 4 0 0 1-4 4H9a2 2 0 0 0-2 2v.17a3.001 3.001 0 1 1-2 0V8.83a3.001 3.001 0 1 1 2 0v2.705A4 4 0 0 1 9 11h6a2 2 0 0 0 2-2v-.17A3.001 3.001 0 0 1 18 3M6 17a1 1 0 1 0 0 2a1 1 0 0 0 0-2M6 5a1 1 0 1 0 0 2a1 1 0 0 0 0-2m12 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2"/></g></svg>
|
|
48
|
-
<span id='gh-forks' class='leading-tight'>???</span>
|
|
55
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="sm:w-[22px] sm:h-[22px]"><g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M18 3a3 3 0 0 1 1 5.83V9a4 4 0 0 1-4 4H9a2 2 0 0 0-2 2v.17a3.001 3.001 0 1 1-2 0V8.83a3.001 3.001 0 1 1 2 0v2.705A4 4 0 0 1 9 11h6a2 2 0 0 0 2-2v-.17A3.001 3.001 0 0 1 18 3M6 17a1 1 0 1 0 0 2a1 1 0 0 0 0-2M6 5a1 1 0 1 0 0 2a1 1 0 0 0 0-2m12 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2"/></g></svg>
|
|
56
|
+
<span id='gh-forks' class='text-sm leading-tight sm:text-base'>???</span>
|
|
49
57
|
</div>
|
|
50
58
|
|
|
51
|
-
<div class='flex items-center gap-x-2'>
|
|
59
|
+
<div class='flex items-center gap-x-1.5 sm:gap-x-2'>
|
|
52
60
|
{/* mingcute:balance-line */}
|
|
53
61
|
<!-- prettier-ignore -->
|
|
54
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M12 3a1 1 0 0 1 1 1v1h.764a2 2 0 0 1 .894.211L16.236 6H20a1 1 0 1 1 0 2h-.382l2.276 4.553c.07.139.106.292.106.447a4 4 0 0 1-8 0c0-.155.036-.308.106-.447L16.382 8h-.146a2 2 0 0 1-.894-.211L13.764 7H13v12h3a1 1 0 1 1 0 2H8a1 1 0 1 1 0-2h3V7h-.764l-1.578.789A2 2 0 0 1 7.764 8h-.146l2.276 4.553A1 1 0 0 1 10 13a4 4 0 0 1-8 0a1 1 0 0 1 .106-.447L4.382 8H4a1 1 0 0 1 0-2h3.764l1.578-.789A2 2 0 0 1 10.236 5H11V4a1 1 0 0 1 1-1M6 9.236l-1.989 3.977a2 2 0 0 0 3.978 0zm12 0l-1.989 3.977a2 2 0 0 0 3.955.157l.023-.156z"/></g></svg>
|
|
55
|
-
<span id='gh-license' class='leading-tight'>???</span>
|
|
62
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="sm:w-[22px] sm:h-[22px]"><g fill="none" fill-rule="evenodd"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M12 3a1 1 0 0 1 1 1v1h.764a2 2 0 0 1 .894.211L16.236 6H20a1 1 0 1 1 0 2h-.382l2.276 4.553c.07.139.106.292.106.447a4 4 0 0 1-8 0c0-.155.036-.308.106-.447L16.382 8h-.146a2 2 0 0 1-.894-.211L13.764 7H13v12h3a1 1 0 1 1 0 2H8a1 1 0 1 1 0-2h3V7h-.764l-1.578.789A2 2 0 0 1 7.764 8h-.146l2.276 4.553A1 1 0 0 1 10 13a4 4 0 0 1-8 0a1 1 0 0 1 .106-.447L4.382 8H4a1 1 0 0 1 0-2h3.764l1.578-.789A2 2 0 0 1 10.236 5H11V4a1 1 0 0 1 1-1M6 9.236l-1.989 3.977a2 2 0 0 0 3.978 0zm12 0l-1.989 3.977a2 2 0 0 0 3.955.157l.023-.156z"/></g></svg>
|
|
63
|
+
<span id='gh-license' class='text-sm leading-tight sm:text-base'>???</span>
|
|
56
64
|
</div>
|
|
57
65
|
</div>
|
|
58
|
-
<span id='gh-language' class='
|
|
66
|
+
<span id='gh-language' class='load-block text-sm leading-tight sm:text-base'>?????</span>
|
|
59
67
|
</div>
|
|
60
68
|
</a>
|
|
61
69
|
</github-card>
|
|
@@ -72,16 +80,19 @@ const [owner, repoName] = repo.split('/')
|
|
|
72
80
|
opacity: 1;
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
|
-
.loading .
|
|
83
|
+
.loading .load-block {
|
|
76
84
|
color: transparent;
|
|
77
85
|
border-radius: calc(var(--radius) - 3px);
|
|
78
86
|
background-color: hsl(var(--primary-foreground) / var(--un-text-opacity));
|
|
79
87
|
animation: pulsate 2s infinite linear;
|
|
80
88
|
user-select: none;
|
|
81
89
|
}
|
|
82
|
-
.loading .
|
|
90
|
+
.loading .load-block:nth-child(2) {
|
|
83
91
|
animation-delay: 1s;
|
|
84
92
|
}
|
|
93
|
+
.no-license {
|
|
94
|
+
opacity: 0.5;
|
|
95
|
+
}
|
|
85
96
|
</style>
|
|
86
97
|
|
|
87
98
|
<script>
|
|
@@ -122,25 +133,22 @@ const [owner, repoName] = repo.split('/')
|
|
|
122
133
|
try {
|
|
123
134
|
const data = await this.fetchGithub(this.dataset.repo)
|
|
124
135
|
if (!data) return
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
;(this.querySelector('#gh-language') as HTMLElement).textContent = data.language || 'N/A'
|
|
132
|
-
;(this.querySelector('#gh-description') as HTMLElement).textContent =
|
|
136
|
+
// Update stats
|
|
137
|
+
this.updateElement('#gh-stars', this.numberFormat(data.stargazers_count))
|
|
138
|
+
this.updateElement('#gh-forks', this.numberFormat(data.forks))
|
|
139
|
+
this.updateElement('#gh-language', data.language || 'N/A')
|
|
140
|
+
// Update description
|
|
141
|
+
const description =
|
|
133
142
|
typeof data.description === 'string'
|
|
134
143
|
? data.description.replace(/:[a-zA-Z0-9_]+:/g, '')
|
|
135
144
|
: 'Description not set'
|
|
136
|
-
|
|
145
|
+
this.updateElement('#gh-description', description)
|
|
146
|
+
// Update license
|
|
147
|
+
const licenseText = data.license?.spdx_id || 'N/A'
|
|
137
148
|
const licenseEl = this.querySelector('#gh-license') as HTMLElement
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
licenseEl.classList.add('no-license')
|
|
142
|
-
}
|
|
143
|
-
|
|
149
|
+
licenseEl.textContent = licenseText
|
|
150
|
+
licenseEl.classList.toggle('no-license', licenseText === 'N/A')
|
|
151
|
+
// Update avatar
|
|
144
152
|
const avatarEl = this.querySelector('#gh-avatar') as HTMLElement
|
|
145
153
|
if (avatarEl) {
|
|
146
154
|
avatarEl.style.backgroundImage = `url(${data.owner.avatar_url})`
|
|
@@ -150,7 +158,14 @@ const [owner, repoName] = repo.split('/')
|
|
|
150
158
|
this.classList.remove('loading')
|
|
151
159
|
} catch (e) {
|
|
152
160
|
console.error('Error setting Github data:', e)
|
|
153
|
-
|
|
161
|
+
this.updateElement('#gh-description', 'Failed to fetch data')
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private updateElement(selector: string, text: string) {
|
|
166
|
+
const element = this.querySelector(selector) as HTMLElement
|
|
167
|
+
if (element) {
|
|
168
|
+
element.textContent = text
|
|
154
169
|
}
|
|
155
170
|
}
|
|
156
171
|
}
|
|
@@ -6,6 +6,3 @@ export { default as LinkPreview } from './LinkPreview.astro'
|
|
|
6
6
|
// Data transformer
|
|
7
7
|
export { default as QRCode } from './QRCode.astro'
|
|
8
8
|
export { default as MediumZoom } from './MediumZoom.astro'
|
|
9
|
-
|
|
10
|
-
// Individual server integration
|
|
11
|
-
export { default as Comment } from './Comment.astro'
|
|
@@ -15,7 +15,7 @@ social.rss = {
|
|
|
15
15
|
const footerLink1 = footerConf.links?.filter(({ pos }) => pos === 1) || []
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
<footer class='mx-auto mb-5 mt-16'>
|
|
18
|
+
<footer class='mx-auto mb-5 mt-16 w-full'>
|
|
19
19
|
<div class='border-t pt-5'>
|
|
20
20
|
<div class='flex items-center gap-y-3 max-sm:flex-col sm:justify-between sm:gap-y-0'>
|
|
21
21
|
<div
|
|
@@ -5,7 +5,7 @@ import { Icon } from '../user'
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<header-component
|
|
8
|
-
class='group sticky top-4 z-
|
|
8
|
+
class='group sticky top-4 z-50 max-md:z-30 mb-12 flex items-center justify-between rounded-xl border border-transparent max-sm:py-1 sm:rounded-2xl'
|
|
9
9
|
>
|
|
10
10
|
<a
|
|
11
11
|
class='z-30 text-xl font-semibold group-[.not-top]:ms-2 sm:group-[.not-top]:ms-3'
|
|
@@ -104,8 +104,7 @@ const shares = {
|
|
|
104
104
|
}
|
|
105
105
|
</div>
|
|
106
106
|
<QRCode
|
|
107
|
-
|
|
108
|
-
class='absolute z-10 -mt-2 box-content max-h-0 max-w-44 overflow-hidden rounded-xl border bg-muted p-4 opacity-0 transition-all duration-300 ease-in-out aria-expanded:max-h-44 aria-expanded:translate-y-4 aria-expanded:opacity-100'
|
|
107
|
+
class='absolute z-10 -mt-2 box-content max-h-0 max-w-44 overflow-hidden rounded-xl border bg-muted p-4 opacity-0 transition-all duration-300 ease-in-out'
|
|
109
108
|
/>
|
|
110
109
|
</div>
|
|
111
110
|
</div>
|
|
@@ -121,6 +120,14 @@ const shares = {
|
|
|
121
120
|
</a>
|
|
122
121
|
</div>
|
|
123
122
|
|
|
123
|
+
<style>
|
|
124
|
+
#qrcode-container.expanded {
|
|
125
|
+
max-height: 11rem;
|
|
126
|
+
transform: translateY(4px);
|
|
127
|
+
opacity: 100;
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
130
|
+
|
|
124
131
|
<script>
|
|
125
132
|
import { showToast } from '../../utils'
|
|
126
133
|
|
|
@@ -135,12 +142,5 @@ const shares = {
|
|
|
135
142
|
// QRCode
|
|
136
143
|
const getQRCode = document.getElementById('get-qrcode')
|
|
137
144
|
const qrcodeContainer = document.getElementById('qrcode-container')
|
|
138
|
-
|
|
139
|
-
getQRCode?.addEventListener('click', () => {
|
|
140
|
-
if (qrcodeContainer.ariaExpanded === 'true') {
|
|
141
|
-
qrcodeContainer.ariaExpanded = 'false'
|
|
142
|
-
} else {
|
|
143
|
-
qrcodeContainer.ariaExpanded = 'true'
|
|
144
|
-
}
|
|
145
|
-
})
|
|
145
|
+
getQRCode?.addEventListener('click', () => qrcodeContainer?.classList.toggle('expanded'))
|
|
146
146
|
</script>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { Image } from 'astro:assets'
|
|
3
3
|
import type { InferEntrySchema } from 'astro:content'
|
|
4
4
|
|
|
5
|
-
import { PageInfo } from '.'
|
|
6
5
|
import { FormattedDate, Icon } from '../user'
|
|
7
6
|
|
|
8
7
|
interface Props {
|
|
@@ -11,17 +10,7 @@ interface Props {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
const {
|
|
14
|
-
data: {
|
|
15
|
-
title,
|
|
16
|
-
description,
|
|
17
|
-
draft,
|
|
18
|
-
heroImage,
|
|
19
|
-
publishDate,
|
|
20
|
-
updatedDate,
|
|
21
|
-
comment: enableComment,
|
|
22
|
-
tags,
|
|
23
|
-
language
|
|
24
|
-
},
|
|
13
|
+
data: { title, description, draft, heroImage, publishDate, updatedDate, tags, language },
|
|
25
14
|
remarkPluginFrontmatter
|
|
26
15
|
} = Astro.props
|
|
27
16
|
|
|
@@ -119,12 +108,12 @@ const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
|
|
119
108
|
|
|
120
109
|
<div class='mt-3 italic'>
|
|
121
110
|
<blockquote class='text-sm text-muted-foreground'><q>{description}</q></blockquote>
|
|
122
|
-
|
|
111
|
+
<slot name='description' />
|
|
123
112
|
</div>
|
|
124
113
|
</div>
|
|
125
114
|
|
|
126
115
|
{/* Dividing line */}
|
|
127
|
-
<div class='mt-4 w-1/2 border-t max-
|
|
116
|
+
<div class='mt-4 w-1/2 border-t max-md:mx-auto sm:mt-6 sm:w-1/3'></div>
|
|
128
117
|
|
|
129
118
|
<script>
|
|
130
119
|
const viewportHeight = window.innerHeight
|
|
@@ -2,7 +2,6 @@ export { default as ArticleBottom } from './ArticleBottom.astro'
|
|
|
2
2
|
export { default as BackToTop } from './BackToTop.astro'
|
|
3
3
|
export { default as Copyright } from './Copyright.astro'
|
|
4
4
|
export { default as Hero } from './Hero.astro'
|
|
5
|
-
export { default as PageInfo } from './PageInfo.astro'
|
|
6
5
|
export { default as Paginator } from './Paginator.astro'
|
|
7
6
|
export { default as PostPreview } from './PostPreview.astro'
|
|
8
7
|
export { default as TOC } from './TOC.astro'
|
package/index.ts
CHANGED
|
@@ -6,9 +6,10 @@ import type { AstroIntegration, RehypePlugins, RemarkPlugins } from 'astro'
|
|
|
6
6
|
// Integrations
|
|
7
7
|
import mdx from '@astrojs/mdx'
|
|
8
8
|
import sitemap from '@astrojs/sitemap'
|
|
9
|
-
import rehypeExternalLinks from 'rehype-external-links'
|
|
10
9
|
import UnoCSS from 'unocss/astro'
|
|
11
10
|
|
|
11
|
+
import rehypeExternalLinks from './plugins/rehype-external-links'
|
|
12
|
+
import rehypeTable from './plugins/rehype-table'
|
|
12
13
|
import { remarkAddZoomable, remarkReadingTime } from './plugins/remark-plugins'
|
|
13
14
|
import { vitePluginUserConfig } from './plugins/virtual-user-config'
|
|
14
15
|
import { UserConfigSchema, type UserInputConfig } from './types/user-config'
|
|
@@ -51,11 +52,14 @@ export default function AstroPureIntegration(opts: UserInputConfig): AstroIntegr
|
|
|
51
52
|
rehypePlugins.push([
|
|
52
53
|
rehypeExternalLinks,
|
|
53
54
|
{
|
|
54
|
-
content: {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
content: {
|
|
56
|
+
type: 'text',
|
|
57
|
+
value: userConfig.content.externalLinks.content
|
|
58
|
+
},
|
|
59
|
+
contentProperties: userConfig.content.externalLinks.properties
|
|
57
60
|
}
|
|
58
61
|
])
|
|
62
|
+
rehypePlugins.push(rehypeTable)
|
|
59
63
|
// Add Starlight directives restoration integration at the end of the list so that remark
|
|
60
64
|
// plugins injected by Starlight plugins through Astro integrations can handle text and
|
|
61
65
|
// leaf directives before they are transformed back to their original form.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-pure",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "A simple, clean but powerful blog theme build by astro.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "CWorld",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"./libs": "./libs/index.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@astrojs/mdx": "^4.3.
|
|
38
|
+
"@astrojs/mdx": "^4.3.1",
|
|
39
39
|
"@astrojs/sitemap": "^3.4.1",
|
|
40
40
|
"@pagefind/default-ui": "^1.3.0",
|
|
41
41
|
"@unocss/reset": "^66.3.3",
|
|
42
|
-
"astro": "^5.
|
|
42
|
+
"astro": "^5.12.0",
|
|
43
43
|
"hast-util-select": "^6.0.4",
|
|
44
44
|
"node-html-parser": "^7.0.1",
|
|
45
45
|
"pagefind": "^1.3.0",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// https://github.com/rehypejs/rehype-external-links
|
|
2
2
|
|
|
3
3
|
import type { Element, ElementContent, Root } from 'hast'
|
|
4
|
-
import isAbsoluteUrl from 'is-absolute-url'
|
|
5
4
|
import { visit } from 'unist-util-visit'
|
|
6
5
|
|
|
6
|
+
import isAbsoluteUrl from '../utils/is-absolute-url'
|
|
7
|
+
|
|
7
8
|
export interface ExternalLinkOptions {
|
|
8
9
|
content?: ElementContent | ElementContent[]
|
|
9
10
|
contentProperties?: Record<string, unknown>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Element, Root } from 'hast'
|
|
2
|
+
import type { Plugin } from 'unified'
|
|
3
|
+
import { visit } from 'unist-util-visit'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rehype plugin that wraps direct table children of #content in a scrollable div
|
|
7
|
+
*/
|
|
8
|
+
const rehypeWrapContentTables: Plugin<[], Root> = () => {
|
|
9
|
+
return (tree) => {
|
|
10
|
+
// console.log('tree', tree)
|
|
11
|
+
// Find all tables and check their parent
|
|
12
|
+
visit(tree, 'element', (node: Element, index: number | undefined, parent?: Element | Root) => {
|
|
13
|
+
if (
|
|
14
|
+
// Check if this is a table element
|
|
15
|
+
node.tagName === 'table' &&
|
|
16
|
+
// Verify it has a parent
|
|
17
|
+
parent &&
|
|
18
|
+
typeof index === 'number'
|
|
19
|
+
) {
|
|
20
|
+
// Create wrapper div with appropriate classes
|
|
21
|
+
const wrapper: Element = {
|
|
22
|
+
type: 'element',
|
|
23
|
+
tagName: 'div',
|
|
24
|
+
properties: {
|
|
25
|
+
className: ['overflow-x-auto', 'w-full', 'flex', 'justify-center']
|
|
26
|
+
},
|
|
27
|
+
children: [node] // Place the table inside the wrapper
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Replace the table with the wrapper (that contains the table)
|
|
31
|
+
parent.children.splice(index, 1, wrapper)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default rehypeWrapContentTables
|
package/types/theme-config.ts
CHANGED
|
@@ -162,14 +162,23 @@ export const ThemeConfigSchema = () =>
|
|
|
162
162
|
}),
|
|
163
163
|
|
|
164
164
|
content: z.object({
|
|
165
|
-
|
|
165
|
+
externalLinks: z.object({
|
|
166
|
+
/** Content to show for external links */
|
|
167
|
+
content: z
|
|
168
|
+
.string()
|
|
169
|
+
.optional()
|
|
170
|
+
.default(' ↗')
|
|
171
|
+
.describe('Content to show for external links'),
|
|
172
|
+
/** Properties for the external links element */
|
|
173
|
+
properties: z
|
|
174
|
+
.record(z.string())
|
|
175
|
+
.optional()
|
|
176
|
+
.describe('Properties for the external links element')
|
|
177
|
+
}),
|
|
166
178
|
|
|
167
179
|
/** Blog page size for pagination */
|
|
168
180
|
blogPageSize: z.number().optional().default(8),
|
|
169
181
|
|
|
170
|
-
/** Show external link arrow */
|
|
171
|
-
externalLinkArrow: z.boolean().optional().default(true),
|
|
172
|
-
|
|
173
182
|
/** Share buttons to show */
|
|
174
183
|
share: ShareSchema()
|
|
175
184
|
})
|
package/utils/index.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export { default as clsx } from './clsx'
|
|
3
3
|
export { default as mdastToString } from './mdast-util-to-string'
|
|
4
4
|
export { default as getReadingTime } from './reading-time'
|
|
5
|
+
export { default as isAbsoluteUrl } from './is-absolute-url'
|
|
5
6
|
|
|
6
7
|
// Class merge
|
|
7
8
|
export { cn } from './class-merge'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// https://github.com/sindresorhus/is-absolute-url
|
|
2
|
+
|
|
3
|
+
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
|
|
4
|
+
// Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3
|
|
5
|
+
const ABSOLUTE_URL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*?:/
|
|
6
|
+
|
|
7
|
+
// Windows paths like `c:\`
|
|
8
|
+
const WINDOWS_PATH_REGEX = /^[a-zA-Z]:\\/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if a URL is absolute.
|
|
12
|
+
* @param url - The URL to check.
|
|
13
|
+
* @example
|
|
14
|
+
* ```
|
|
15
|
+
* import isAbsoluteUrl from 'is-absolute-url';
|
|
16
|
+
*
|
|
17
|
+
* isAbsoluteUrl('http://sindresorhus.com/foo/bar');
|
|
18
|
+
* //=> true
|
|
19
|
+
*
|
|
20
|
+
* isAbsoluteUrl('//sindresorhus.com');
|
|
21
|
+
* //=> false
|
|
22
|
+
*
|
|
23
|
+
* isAbsoluteUrl('foo/bar');
|
|
24
|
+
* //=> false
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export default function isAbsoluteUrl(url: string): boolean {
|
|
28
|
+
if (typeof url !== 'string') {
|
|
29
|
+
throw new TypeError(`Expected a \`string\`, got \`${typeof url}\``)
|
|
30
|
+
}
|
|
31
|
+
if (WINDOWS_PATH_REGEX.test(url)) {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
return ABSOLUTE_URL_REGEX.test(url)
|
|
35
|
+
}
|
|
@@ -3,6 +3,19 @@ type Options = {
|
|
|
3
3
|
includeHtml?: boolean
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Get the text content of a node or list of nodes.
|
|
8
|
+
*
|
|
9
|
+
* Prefers the node’s plain-text fields, otherwise serializes its children,
|
|
10
|
+
* and if the given value is an array, serialize the nodes in it.
|
|
11
|
+
*
|
|
12
|
+
* @param {unknown} [value]
|
|
13
|
+
* Thing to serialize, typically `Node`.
|
|
14
|
+
* @param {Options | null | undefined} [options]
|
|
15
|
+
* Configuration (optional).
|
|
16
|
+
* @returns {string}
|
|
17
|
+
* Serialized `value`.
|
|
18
|
+
*/
|
|
6
19
|
export default function toString(value: unknown, options?: Options): string {
|
|
7
20
|
const { includeImageAlt = true, includeHtml = true } = options || {}
|
|
8
21
|
return serialize(value, includeImageAlt, includeHtml)
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import config from 'virtual:config'
|
|
3
|
-
|
|
4
|
-
import '@waline/client/style'
|
|
5
|
-
|
|
6
|
-
import { cn } from '../../utils'
|
|
7
|
-
|
|
8
|
-
const { class: className } = Astro.props
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
{
|
|
12
|
-
config.integ.waline.enable && (
|
|
13
|
-
<comment-component>
|
|
14
|
-
<div id='waline' class={cn('not-prose', className)}>
|
|
15
|
-
Comment seems to stuck. Try to refresh?✨
|
|
16
|
-
</div>
|
|
17
|
-
</comment-component>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
<script>
|
|
22
|
-
import { init as walineInit } from '@waline/client'
|
|
23
|
-
import type { WalineEmojiPresets } from '@waline/client'
|
|
24
|
-
import config from 'virtual:config'
|
|
25
|
-
|
|
26
|
-
const walineConfig = config.integ.waline
|
|
27
|
-
|
|
28
|
-
class Comment extends HTMLElement {
|
|
29
|
-
constructor() {
|
|
30
|
-
super()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
connectedCallback() {
|
|
34
|
-
// Prevent Vue log errors
|
|
35
|
-
;(globalThis as unknown as { __VUE_OPTIONS_API__: boolean }).__VUE_OPTIONS_API__ = true
|
|
36
|
-
;(globalThis as unknown as { __VUE_PROD_DEVTOOLS__: boolean }).__VUE_PROD_DEVTOOLS__ = false
|
|
37
|
-
;(
|
|
38
|
-
globalThis as unknown as { __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: boolean }
|
|
39
|
-
).__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = false
|
|
40
|
-
|
|
41
|
-
const emoji = walineConfig.emoji?.map(
|
|
42
|
-
(preset) => `${config.npmCDN}/@waline/emojis@1.2.0/${preset}` as WalineEmojiPresets
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
walineInit({
|
|
46
|
-
el: '#waline',
|
|
47
|
-
serverURL: walineConfig.server || '',
|
|
48
|
-
emoji,
|
|
49
|
-
reaction: ['/icons/heart-item.svg'],
|
|
50
|
-
...walineConfig.additionalConfigs
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (walineConfig.enable) customElements.define('comment-component', Comment)
|
|
56
|
-
</script>
|
|
57
|
-
|
|
58
|
-
<style>
|
|
59
|
-
/* Basic theme */
|
|
60
|
-
#waline {
|
|
61
|
-
/* Font size */
|
|
62
|
-
--waline-font-size: 16px;
|
|
63
|
-
/* Basic color */
|
|
64
|
-
--waline-white: hsl(var(--background) / var(--un-bg-opacity, 1));
|
|
65
|
-
--waline-light-grey: #999;
|
|
66
|
-
--waline-dark-grey: #666;
|
|
67
|
-
/* Theme color */
|
|
68
|
-
--waline-theme-color: hsl(var(--foreground) / var(--un-text-opacity, 1));
|
|
69
|
-
--waline-active-color: hsl(var(--primary) / var(--un-text-opacity, 1));
|
|
70
|
-
/* Layout */
|
|
71
|
-
--waline-color: hsl(var(--muted-foreground) / var(--un-text-opacity, 1));
|
|
72
|
-
--waline-bg-color: hsl(var(--muted) / var(--un-bg-opacity, 1));
|
|
73
|
-
--waline-bg-color-light: hsl(var(--input) / var(--un-text-opacity, 1));
|
|
74
|
-
--waline-bg-color-hover: #f0f0f0;
|
|
75
|
-
--waline-border-color: hsl(var(--border) / var(--un-border-opacity, 1));
|
|
76
|
-
--waline-disable-bg-color: #f8f8f8;
|
|
77
|
-
--waline-disable-color: #bbb;
|
|
78
|
-
--waline-code-bg-color: #282c34;
|
|
79
|
-
/* Special */
|
|
80
|
-
--waline-bq-color: #f0f0f0;
|
|
81
|
-
/* Avatar */
|
|
82
|
-
--waline-avatar-size: 3.25rem;
|
|
83
|
-
--waline-m-avatar-size: calc(var(--waline-avatar-size) * 9 / 13);
|
|
84
|
-
/* Badge */
|
|
85
|
-
--waline-badge-color: hsl(var(--border) / var(--un-border-opacity, 1));
|
|
86
|
-
--waline-badge-font-size: 0.775em;
|
|
87
|
-
/* Info */
|
|
88
|
-
--waline-info-bg-color: var(--waline-bg-color-light);
|
|
89
|
-
--waline-info-color: var(--waline-color);
|
|
90
|
-
--waline-info-font-size: 0.625em;
|
|
91
|
-
/* Render selection */
|
|
92
|
-
--waline-border: 1px solid var(--waline-border-color);
|
|
93
|
-
--waline-avatar-radius: 50%;
|
|
94
|
-
--waline-box-shadow: none;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* Reaction buttons */
|
|
98
|
-
#waline :global(.wl-reaction-title, .wl-reaction-text) {
|
|
99
|
-
display: none;
|
|
100
|
-
}
|
|
101
|
-
#waline :global(.wl-reaction) {
|
|
102
|
-
overflow: visible;
|
|
103
|
-
margin-bottom: 0.5em;
|
|
104
|
-
}
|
|
105
|
-
#waline :global(.wl-reaction-img) {
|
|
106
|
-
width: auto;
|
|
107
|
-
display: flex;
|
|
108
|
-
height: 35px;
|
|
109
|
-
align-items: center;
|
|
110
|
-
column-gap: 0.4rem;
|
|
111
|
-
}
|
|
112
|
-
#waline :global(.wl-reaction-votes) {
|
|
113
|
-
position: inherit;
|
|
114
|
-
top: inherit;
|
|
115
|
-
min-width: inherit;
|
|
116
|
-
inset-inline-end: inherit;
|
|
117
|
-
display: flex;
|
|
118
|
-
font-weight: normal;
|
|
119
|
-
border: none;
|
|
120
|
-
background: none;
|
|
121
|
-
color: inherit;
|
|
122
|
-
padding: 0.2rem 0.4rem;
|
|
123
|
-
border-radius: 6px;
|
|
124
|
-
}
|
|
125
|
-
#waline :global(.wl-reaction-loading) {
|
|
126
|
-
position: inherit;
|
|
127
|
-
top: inherit;
|
|
128
|
-
min-width: inherit;
|
|
129
|
-
}
|
|
130
|
-
#waline :global(.wl-reaction-item.active .wl-reaction-votes) {
|
|
131
|
-
background: var(--waline-theme-color);
|
|
132
|
-
color: var(--waline-bg-color);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
#waline :global(.wl-reaction-votes:after) {
|
|
136
|
-
margin-left: 0.25em;
|
|
137
|
-
content: 'Like(s)';
|
|
138
|
-
display: inline-block;
|
|
139
|
-
clear: both;
|
|
140
|
-
border: 0;
|
|
141
|
-
}
|
|
142
|
-
#waline :global(.wl-reaction img) {
|
|
143
|
-
filter: invert(25%);
|
|
144
|
-
}
|
|
145
|
-
:global(.dark) #waline :global(.wl-reaction img) {
|
|
146
|
-
filter: invert(75%);
|
|
147
|
-
}
|
|
148
|
-
</style>
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { cn } from '../../utils'
|
|
3
|
-
|
|
4
|
-
const { class: className, hideComment, ...props } = Astro.props
|
|
5
|
-
|
|
6
|
-
const path = Astro.url.pathname
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<div class={cn('text-base text-sm text-muted-foreground', className)} {...props}>
|
|
10
|
-
<span class='waline-pageview-count' data-path={path}></span> views
|
|
11
|
-
{
|
|
12
|
-
!hideComment && (
|
|
13
|
-
<a href='#waline'>
|
|
14
|
-
| <span class='waline-comment-count' data-path={path} /> comments
|
|
15
|
-
</a>
|
|
16
|
-
)
|
|
17
|
-
}
|
|
18
|
-
</div>
|