astro-pure 1.3.2 → 1.3.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/bun.lock +137 -199
- package/components/advanced/GithubCard.astro +59 -42
- 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/components/user/Button.astro +4 -1
- package/components/user/Card.astro +9 -0
- package/components/user/CardListChildren.astro +7 -2
- package/components/user/Label.astro +7 -0
- package/components/user/Spoiler.astro +6 -0
- 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/scripts/new.mjs +25 -5
- 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,22 @@ 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
|
-
background-color: hsl(var(--primary-foreground) / var(--un-
|
|
86
|
+
background-color: hsl(var(--primary-foreground) / var(--un-bg-opacity, 1));
|
|
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
|
+
}
|
|
96
|
+
:not(.loading) #gh-avatar {
|
|
97
|
+
background-color: hsl(var(--primary-foreground) / var(--un-bg-opacity));
|
|
98
|
+
}
|
|
85
99
|
</style>
|
|
86
100
|
|
|
87
101
|
<script>
|
|
@@ -122,35 +136,38 @@ const [owner, repoName] = repo.split('/')
|
|
|
122
136
|
try {
|
|
123
137
|
const data = await this.fetchGithub(this.dataset.repo)
|
|
124
138
|
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 =
|
|
139
|
+
// Update stats
|
|
140
|
+
this.updateElement('#gh-stars', this.numberFormat(data.stargazers_count))
|
|
141
|
+
this.updateElement('#gh-forks', this.numberFormat(data.forks))
|
|
142
|
+
this.updateElement('#gh-language', data.language || 'N/A')
|
|
143
|
+
// Update description
|
|
144
|
+
const description =
|
|
133
145
|
typeof data.description === 'string'
|
|
134
146
|
? data.description.replace(/:[a-zA-Z0-9_]+:/g, '')
|
|
135
147
|
: 'Description not set'
|
|
136
|
-
|
|
148
|
+
this.updateElement('#gh-description', description)
|
|
149
|
+
// Update license
|
|
150
|
+
const licenseText = data.license?.spdx_id || 'N/A'
|
|
137
151
|
const licenseEl = this.querySelector('#gh-license') as HTMLElement
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
licenseEl.classList.add('no-license')
|
|
142
|
-
}
|
|
143
|
-
|
|
152
|
+
licenseEl.textContent = licenseText
|
|
153
|
+
licenseEl.classList.toggle('no-license', licenseText === 'N/A')
|
|
154
|
+
// Update avatar
|
|
144
155
|
const avatarEl = this.querySelector('#gh-avatar') as HTMLElement
|
|
145
156
|
if (avatarEl) {
|
|
146
157
|
avatarEl.style.backgroundImage = `url(${data.owner.avatar_url})`
|
|
147
|
-
avatarEl.style.backgroundColor = 'transparent'
|
|
148
158
|
}
|
|
149
159
|
|
|
150
160
|
this.classList.remove('loading')
|
|
151
161
|
} catch (e) {
|
|
152
162
|
console.error('Error setting Github data:', e)
|
|
153
|
-
|
|
163
|
+
this.updateElement('#gh-description', 'Failed to fetch data')
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private updateElement(selector: string, text: string) {
|
|
168
|
+
const element = this.querySelector(selector) as HTMLElement
|
|
169
|
+
if (element) {
|
|
170
|
+
element.textContent = text
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
173
|
}
|
|
@@ -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'
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { HTMLTag, Polymorphic } from 'astro/types'
|
|
3
|
+
|
|
2
4
|
import { cn } from '../../utils'
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
|
|
5
7
|
as?: string
|
|
6
8
|
title?: string
|
|
7
9
|
href?: string
|
|
8
10
|
variant?: 'button' | 'pill' | 'back' | 'ahead'
|
|
11
|
+
target?: string
|
|
9
12
|
class?: string
|
|
10
13
|
}
|
|
11
14
|
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { HTMLTag, Polymorphic } from 'astro/types'
|
|
3
|
+
|
|
2
4
|
import { cn } from '../../utils'
|
|
3
5
|
|
|
6
|
+
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
|
|
7
|
+
heading?: string
|
|
8
|
+
subheading?: string
|
|
9
|
+
date?: string
|
|
10
|
+
class?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
4
13
|
const { as: Tag = 'div', class: className, href, heading, subheading, date } = Astro.props
|
|
5
14
|
---
|
|
6
15
|
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { HTMLTag, Polymorphic } from 'astro/types'
|
|
3
|
+
|
|
2
4
|
import type { CardList } from '../../types'
|
|
3
5
|
import { cn } from '../../utils'
|
|
4
6
|
|
|
5
|
-
type Props = {
|
|
7
|
+
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
|
|
8
|
+
main?: boolean
|
|
9
|
+
children: CardList
|
|
10
|
+
}
|
|
6
11
|
const { main = false, children } = Astro.props
|
|
7
12
|
---
|
|
8
13
|
|
|
9
14
|
<ul class={cn('flex flex-col gap-y-1', !main && 'subitem list-disc ms-5')}>
|
|
10
15
|
{
|
|
11
|
-
children.map((child) => {
|
|
16
|
+
children.map((child: CardList[number]) => {
|
|
12
17
|
const Tag = child.link ? 'a' : 'p'
|
|
13
18
|
return (
|
|
14
19
|
<li>
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { HTMLTag, Polymorphic } from 'astro/types'
|
|
3
|
+
|
|
2
4
|
import { cn } from '../../utils'
|
|
3
5
|
|
|
6
|
+
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
|
|
7
|
+
title: string
|
|
8
|
+
href?: string
|
|
9
|
+
class?: string
|
|
10
|
+
}
|
|
4
11
|
const { class: className, as: Tag = 'div', title, href, ...props } = Astro.props
|
|
5
12
|
---
|
|
6
13
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { HTMLTag, Polymorphic } from 'astro/types'
|
|
3
|
+
|
|
2
4
|
import { cn } from '../../utils'
|
|
3
5
|
|
|
6
|
+
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
|
|
7
|
+
class?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
const { as: Tag = 'span', class: className } = Astro.props
|
|
5
11
|
---
|
|
6
12
|
|
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.4",
|
|
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/scripts/new.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* -l, --lang <en|zh> Set the language (default: en)
|
|
8
8
|
* -d, --draft Create a draft post (default: false)
|
|
9
9
|
* -m, --mdx Use MDX format (default: false)
|
|
10
|
+
* -f, --folder Create the post in a folder (default: false)
|
|
10
11
|
* -h, --help Show this help message
|
|
11
12
|
*
|
|
12
13
|
* Example:
|
|
@@ -32,6 +33,7 @@ function getDate() {
|
|
|
32
33
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/** get blog title slug */
|
|
35
37
|
function getPostSlug(postTitle) {
|
|
36
38
|
let slug = slugify(postTitle).toLocaleLowerCase()
|
|
37
39
|
if (slug === '') {
|
|
@@ -46,6 +48,7 @@ Options:
|
|
|
46
48
|
-l, --lang Set the language (default: null)
|
|
47
49
|
-d, --draft Create a draft post (default: false)
|
|
48
50
|
-m, --mdx Use MDX format (default: false)
|
|
51
|
+
-f, --folder Create the post in a folder (default: false)
|
|
49
52
|
-h, --help Show this help message
|
|
50
53
|
|
|
51
54
|
Example:
|
|
@@ -57,17 +60,19 @@ const TARGET_DIR = 'src/content/blog/'
|
|
|
57
60
|
export default function main(args) {
|
|
58
61
|
const parsedArgs = minimist(args, {
|
|
59
62
|
string: ['lang'],
|
|
60
|
-
boolean: ['draft', 'mdx', 'help'],
|
|
63
|
+
boolean: ['draft', 'mdx', 'help', 'folder'],
|
|
61
64
|
default: {
|
|
62
65
|
lang: null,
|
|
63
66
|
draft: false,
|
|
64
|
-
mdx: false
|
|
67
|
+
mdx: false,
|
|
68
|
+
folder: false
|
|
65
69
|
},
|
|
66
70
|
alias: {
|
|
67
71
|
l: 'lang',
|
|
68
72
|
d: 'draft',
|
|
69
73
|
m: 'mdx',
|
|
70
|
-
h: 'help'
|
|
74
|
+
h: 'help',
|
|
75
|
+
f: 'folder'
|
|
71
76
|
}
|
|
72
77
|
})
|
|
73
78
|
|
|
@@ -84,7 +89,20 @@ export default function main(args) {
|
|
|
84
89
|
|
|
85
90
|
const fileExtension = parsedArgs.mdx ? '.mdx' : '.md'
|
|
86
91
|
const fileName = getPostSlug(postTitle) + fileExtension
|
|
87
|
-
|
|
92
|
+
|
|
93
|
+
let fullPath
|
|
94
|
+
if (parsedArgs.folder) {
|
|
95
|
+
const folderName = getPostSlug(postTitle);
|
|
96
|
+
const folderPath = path.join(TARGET_DIR, folderName);
|
|
97
|
+
if (!fs.existsSync(folderPath)) {
|
|
98
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
const fileName = 'index' + fileExtension;
|
|
101
|
+
fullPath = path.join(folderPath, fileName);
|
|
102
|
+
} else {
|
|
103
|
+
fullPath = path.join(TARGET_DIR, fileName);
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
|
|
89
107
|
console.log('Full path:', fullPath)
|
|
90
108
|
|
|
@@ -100,7 +118,9 @@ publishDate: ${getDate()}
|
|
|
100
118
|
`
|
|
101
119
|
content += parsedArgs.draft ? 'draft: true\n' : ''
|
|
102
120
|
content += parsedArgs.lang ? `lang: ${parsedArgs.lang}\n` : ''
|
|
103
|
-
content += `tags:
|
|
121
|
+
content += `tags:
|
|
122
|
+
- Example
|
|
123
|
+
- Technology
|
|
104
124
|
---
|
|
105
125
|
|
|
106
126
|
Write your content here.
|
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)
|