@mintfolio/theme-verdant 0.2.0

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 (67) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +66 -0
  3. package/THIRD_PARTY_NOTICES.md +11 -0
  4. package/assets/favicon.ico +0 -0
  5. package/assets/favicon.svg +9 -0
  6. package/assets/fonts/Inter-300.woff2 +0 -0
  7. package/assets/fonts/Inter-400.woff2 +0 -0
  8. package/assets/fonts/Inter-500.woff2 +0 -0
  9. package/assets/fonts/Inter-600.woff2 +0 -0
  10. package/assets/fonts/Inter-700.woff2 +0 -0
  11. package/assets/fonts/JetBrainsMono-400.woff2 +0 -0
  12. package/assets/fonts/JetBrainsMono-500.woff2 +0 -0
  13. package/assets/fonts/PlayfairDisplay-400.woff2 +0 -0
  14. package/assets/fonts/PlayfairDisplay-500.woff2 +0 -0
  15. package/assets/fonts/PlayfairDisplay-600.woff2 +0 -0
  16. package/assets/fonts/PlayfairDisplay-700.woff2 +0 -0
  17. package/components/base/Contact.astro +173 -0
  18. package/components/base/Hero.astro +228 -0
  19. package/components/base/Navigation.astro +288 -0
  20. package/components/base/Projects.astro +207 -0
  21. package/components/base/Skills.astro +158 -0
  22. package/components/blog/ArticleProse.astro +217 -0
  23. package/components/blog/CodeBlockEnhancer.astro +310 -0
  24. package/components/blog/PostCard.astro +98 -0
  25. package/components/blog/ProtectedArticle.astro +89 -0
  26. package/config/theme-verdant.config.mjs +74 -0
  27. package/layouts/Base.astro +460 -0
  28. package/lib/palettes.ts +10 -0
  29. package/licenses/Inter-OFL.txt +92 -0
  30. package/licenses/JetBrainsMono-OFL.txt +93 -0
  31. package/licenses/PlayfairDisplay-OFL.txt +191 -0
  32. package/package.json +68 -0
  33. package/pages/archive.astro +384 -0
  34. package/pages/home.astro +2049 -0
  35. package/pages/page.astro +434 -0
  36. package/pages/post.astro +1029 -0
  37. package/scripts/article.ts +42 -0
  38. package/scripts/articleOrigin.ts +31 -0
  39. package/scripts/articleTables.ts +15 -0
  40. package/scripts/blog.ts +26 -0
  41. package/scripts/codeBlocks.ts +172 -0
  42. package/scripts/hero.ts +173 -0
  43. package/scripts/home.ts +12 -0
  44. package/scripts/homePanels.ts +100 -0
  45. package/scripts/hotContent.ts +16 -0
  46. package/scripts/lifecycle.ts +3 -0
  47. package/scripts/lightbox.ts +14 -0
  48. package/scripts/postFilters.ts +101 -0
  49. package/scripts/postList.ts +132 -0
  50. package/scripts/progress.ts +29 -0
  51. package/scripts/protectedArticle.ts +49 -0
  52. package/scripts/protectedToc.ts +29 -0
  53. package/scripts/site.ts +145 -0
  54. package/scripts/tagFold.ts +61 -0
  55. package/scripts/theme.ts +102 -0
  56. package/scripts/toc.ts +149 -0
  57. package/settings.ts +21 -0
  58. package/styles/fonts.css +120 -0
  59. package/styles/global.css +146 -0
  60. package/styles/themes/happyhues/effects.css +62 -0
  61. package/styles/themes/happyhues/index.css +2 -0
  62. package/styles/themes/happyhues/theme.css +225 -0
  63. package/theme.mjs +60 -0
  64. package/utils/blogNavigation.ts +3 -0
  65. package/utils/homeReturnHint.ts +34 -0
  66. package/utils/storage.ts +16 -0
  67. package/utils/types.ts +8 -0
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # Verdant
2
+
3
+ 一个给 [Mintfolio](https://github.com/cnflwzh/mintfolio) 使用的个人主页与博客主题。
4
+
5
+ Verdant 保留了宽松的留白、文章卡片和绿色主色,也提供其他七套配色。主页可以放资料、项目与推荐文章;阅读页提供目录、代码复制、图片预览和阅读进度。
6
+
7
+ ![Verdant 首页](docs/assets/home-desktop.webp)
8
+
9
+ ## 安装
10
+
11
+ 先按 [入门教程](https://github.com/cnflwzh/mintfolio/wiki/Getting-Started) 建好站点,再在站点目录运行:
12
+
13
+ ~~~sh
14
+ mintfolio theme install verdant --use
15
+ mintfolio dev
16
+ ~~~
17
+
18
+ 项目仍在持续开发中。verdant 别名需要 Core >= 0.2.0。
19
+
20
+ Verdant 的 npm 包名是 @mintfolio/theme-verdant,清单 id 为 verdant,配置文件是 theme-verdant.config.mjs。命令行可使用 verdant 简写。
21
+
22
+ ## 调整主题
23
+
24
+ 站点根目录的 theme-verdant.config.mjs 是显示设置入口。它会自动生成,重复初始化或升级会保留你的修改。
25
+
26
+ ~~~js
27
+ export default {
28
+ initialMode: 'auto',
29
+ initialPalette: '1',
30
+ homePageSize: 6,
31
+ archivePageSize: 8,
32
+ sidebar: {
33
+ quote: { enabled: true, text: '慢慢写,也认真读。', author: '' },
34
+ },
35
+ };
36
+ ~~~
37
+
38
+ initialPalette 使用字符串 '1' 到 '8'。访客在浏览器中保存的配色偏好优先于初始值。网站标题、头像和项目内容放在 site.config.ts;侧栏、推荐文章和文末插图放在主题设置中。
39
+
40
+ 全部选项见 [配置模板](config/theme-verdant.config.mjs) 与 [主题配置教程](https://github.com/cnflwzh/mintfolio/wiki/Themes)。设置合并顺序为清单默认值、独立主题文件、旧内联 settings;数组整体替换。
41
+
42
+ ## 页面与资源
43
+
44
+ 主题提供首页、文章、归档和普通页面。404 页面由 Core 补齐。首页和普通页面会显示 site.config.ts 顶层的 icp 字段;其他页面目前未统一显示备案信息。
45
+
46
+ 字体与图标随包分发。React 和 Tailwind 在启用本主题时由 Core 加载。文章读取、URL、搜索和解锁逻辑使用 Core 公共 API。
47
+
48
+ ![阅读页面](docs/assets/article-reading.webp)
49
+
50
+ ## 修改源码
51
+
52
+ ~~~sh
53
+ git clone https://github.com/cnflwzh/mintfolio-theme-verdant.git
54
+ cd mintfolio-theme-verdant
55
+ npm ci
56
+ npm run check
57
+ npm pack
58
+ ~~~
59
+
60
+ 将打出的 tgz 安装到测试站点,检查桌面、手机和密码文章页面。定制请保存在自己的主题仓库中,直接修改 node_modules 会在重新安装后丢失。
61
+
62
+ [主题开发教程](https://github.com/cnflwzh/mintfolio/wiki/Theme-Development) · [贡献说明](CONTRIBUTING.md) · [安全问题](SECURITY.md)
63
+
64
+ ## 许可证
65
+
66
+ 代码为 [GPL-3.0-only](LICENSE)。Inter、JetBrains Mono、Playfair Display 按 SIL OFL 1.1 分发,原始声明保存在 [licenses](licenses) 中。
@@ -0,0 +1,11 @@
1
+ # 第三方资源
2
+
3
+ 字体文件保留原项目许可证:
4
+
5
+ | 字体 | 来源 | 许可副本 |
6
+ | --- | --- | --- |
7
+ | Inter | https://github.com/rsms/inter | [SIL OFL 1.1](licenses/Inter-OFL.txt) |
8
+ | JetBrains Mono | https://github.com/JetBrains/JetBrainsMono | [SIL OFL 1.1](licenses/JetBrainsMono-OFL.txt) |
9
+ | Playfair Display | https://github.com/google/fonts/tree/main/ofl/playfairdisplay | [SIL OFL 1.1](licenses/PlayfairDisplay-OFL.txt) |
10
+
11
+ 本包分发的是 WOFF2 字体。npm 依赖各自采用上游许可证。README 截图来自 Mintfolio 示例站点,使用演示资料。
Binary file
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
2
+ <path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
3
+ <style>
4
+ path { fill: #000; }
5
+ @media (prefers-color-scheme: dark) {
6
+ path { fill: #FFF; }
7
+ }
8
+ </style>
9
+ </svg>
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,173 @@
1
+ ---
2
+ /**
3
+ * Contact - Contact section with email and social links.
4
+ *
5
+ * Displays contact information from the public theme context:
6
+ * - Primary email with mailto link
7
+ * - Social media links (filtered by contact.social platform names)
8
+ */
9
+ import type { ThemeContext } from '@mintfolio/theme-api';
10
+ import type { VerdantSettings } from '../../settings';
11
+
12
+ /** Public site data supplied by the containing theme page. */
13
+ interface Props { theme: ThemeContext<VerdantSettings>; }
14
+ import type { SocialLink } from '@mintfolio/theme-api';
15
+
16
+ const { theme } = Astro.props;
17
+ // Filter social links based on contact.social platform names
18
+ const contactSocials: SocialLink[] = theme.site.social.filter((link) =>
19
+ theme.site.contact.social.includes(link.platform)
20
+ );
21
+ ---
22
+
23
+ <section id="contact" class="contact-section">
24
+ <div class="contact-container">
25
+ <h2 class="section-title gradient-text">联系我</h2>
26
+ <p class="cta-text">保持联系</p>
27
+
28
+ <div class="glass-card contact-card">
29
+ <div class="contact-email">
30
+ <span class="label">Email</span>
31
+ <a href={`mailto:${theme.site.contact.email}`} class="email-link">
32
+ {theme.site.contact.email}
33
+ </a>
34
+ </div>
35
+
36
+ <div class="contact-socials">
37
+ <span class="label">社交媒体</span>
38
+ <div class="social-links">
39
+ {contactSocials.map((social) => (
40
+ <a
41
+ href={social.url}
42
+ target="_blank"
43
+ rel="noopener noreferrer"
44
+ class="social-link"
45
+ aria-label={social.platform}
46
+ >
47
+ <span class="social-icon">{social.icon}</span>
48
+ <span class="social-platform">{social.platform}</span>
49
+ </a>
50
+ ))}
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </section>
56
+
57
+ <style>
58
+ .contact-section {
59
+ padding: var(--section-padding);
60
+ display: flex;
61
+ justify-content: center;
62
+ align-items: center;
63
+ min-height: 50vh;
64
+ }
65
+
66
+ .contact-container {
67
+ max-width: var(--max-width);
68
+ width: 100%;
69
+ text-align: center;
70
+ }
71
+
72
+ .section-title {
73
+ font-size: clamp(2rem, 5vw, 3rem);
74
+ font-weight: 700;
75
+ margin-bottom: 0.5rem;
76
+ }
77
+
78
+ .cta-text {
79
+ color: var(--color-text-muted);
80
+ font-size: 1.125rem;
81
+ margin-bottom: 2rem;
82
+ }
83
+
84
+ .contact-card {
85
+ padding: 2rem;
86
+ max-width: 600px;
87
+ margin: 0 auto;
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 2rem;
91
+ }
92
+
93
+ .contact-email,
94
+ .contact-socials {
95
+ display: flex;
96
+ flex-direction: column;
97
+ gap: 0.75rem;
98
+ }
99
+
100
+ .label {
101
+ font-size: 0.875rem;
102
+ color: var(--color-text-muted);
103
+ text-transform: uppercase;
104
+ letter-spacing: 0.05em;
105
+ }
106
+
107
+ .email-link {
108
+ color: var(--color-primary);
109
+ font-size: 1.25rem;
110
+ font-weight: 500;
111
+ text-decoration: none;
112
+ transition: color 0.2s ease;
113
+ }
114
+
115
+ .email-link:hover {
116
+ color: var(--color-secondary);
117
+ }
118
+
119
+ .social-links {
120
+ display: flex;
121
+ flex-wrap: wrap;
122
+ gap: 1rem;
123
+ justify-content: center;
124
+ }
125
+
126
+ .social-link {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 0.5rem;
130
+ padding: 0.75rem 1.25rem;
131
+ background: var(--color-surface);
132
+ border: 1px solid var(--glass-border);
133
+ border-radius: 8px;
134
+ color: var(--color-text);
135
+ text-decoration: none;
136
+ font-size: 0.9375rem;
137
+ transition: all 0.2s ease;
138
+ }
139
+
140
+ .social-link:hover {
141
+ background: rgba(255, 255, 255, 0.1);
142
+ border-color: var(--color-primary);
143
+ color: var(--color-primary);
144
+ }
145
+
146
+ .social-icon {
147
+ font-size: 1.125rem;
148
+ }
149
+
150
+ .social-platform {
151
+ text-transform: capitalize;
152
+ }
153
+
154
+ @media (max-width: 640px) {
155
+ .contact-card {
156
+ padding: 1.5rem;
157
+ }
158
+
159
+ .email-link {
160
+ font-size: 1rem;
161
+ word-break: break-all;
162
+ }
163
+
164
+ .social-links {
165
+ flex-direction: column;
166
+ align-items: stretch;
167
+ }
168
+
169
+ .social-link {
170
+ justify-content: center;
171
+ }
172
+ }
173
+ </style>
@@ -0,0 +1,228 @@
1
+ ---
2
+ /**
3
+ * Hero - Full-screen hero section component for the home page.
4
+ *
5
+ * Displays the main profile introduction with:
6
+ * - Avatar image from the public profile
7
+ * - Name with gradient text styling
8
+ * - Bio and location information
9
+ * - Social media links (GitHub, Twitter, Email with icon support)
10
+ */
11
+ import type { ThemeContext } from '@mintfolio/theme-api';
12
+ import type { VerdantSettings } from '../../settings';
13
+
14
+ /** Public site data supplied by the containing theme page. */
15
+ interface Props { theme: ThemeContext<VerdantSettings>; }
16
+ import Image from '@mintfolio/core/components/Image.astro';
17
+
18
+ const { theme } = Astro.props;
19
+ const { profile, social } = theme.site;
20
+ const profileAvatar = profile.avatar;
21
+ ---
22
+
23
+ <section id="hero" class="section-bg">
24
+ <div class="hero-container">
25
+ <div class="hero-content">
26
+ <Image
27
+ src={profileAvatar}
28
+ alt={`${profile.name}'s avatar`}
29
+ class="avatar"
30
+ width={150}
31
+ height={150}
32
+ />
33
+
34
+ <h1 class="name gradient-text">{profile.name}</h1>
35
+
36
+ <p class="bio">{profile.bio}</p>
37
+
38
+ <div class="location">
39
+ <svg
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ width="16"
42
+ height="16"
43
+ viewBox="0 0 24 24"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ stroke-width="2"
47
+ stroke-linecap="round"
48
+ stroke-linejoin="round"
49
+ class="location-icon"
50
+ >
51
+ <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
52
+ <circle cx="12" cy="10" r="3"></circle>
53
+ </svg>
54
+ <span>{profile.location}</span>
55
+ </div>
56
+
57
+ <nav class="social-links" aria-label="Social media links">
58
+ {social.map((link) => (
59
+ <a
60
+ href={link.url}
61
+ target="_blank"
62
+ rel="noopener noreferrer"
63
+ class="social-link"
64
+ aria-label={link.platform}
65
+ >
66
+ {link.icon === 'github' && (
67
+ <svg
68
+ xmlns="http://www.w3.org/2000/svg"
69
+ width="24"
70
+ height="24"
71
+ viewBox="0 0 24 24"
72
+ fill="currentColor"
73
+ >
74
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
75
+ </svg>
76
+ )}
77
+ {link.icon === 'mail' && (
78
+ <svg
79
+ xmlns="http://www.w3.org/2000/svg"
80
+ width="24"
81
+ height="24"
82
+ viewBox="0 0 24 24"
83
+ fill="none"
84
+ stroke="currentColor"
85
+ stroke-width="2"
86
+ stroke-linecap="round"
87
+ stroke-linejoin="round"
88
+ >
89
+ <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
90
+ <polyline points="22,6 12,13 2,6"></polyline>
91
+ </svg>
92
+ )}
93
+ {link.icon === 'twitter' && (
94
+ <svg
95
+ xmlns="http://www.w3.org/2000/svg"
96
+ width="24"
97
+ height="24"
98
+ viewBox="0 0 24 24"
99
+ fill="currentColor"
100
+ >
101
+ <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
102
+ </svg>
103
+ )}
104
+ {!['github', 'mail', 'twitter'].includes(link.icon) && (
105
+ <span class="fallback-icon">{link.platform.charAt(0).toUpperCase()}</span>
106
+ )}
107
+ </a>
108
+ ))}
109
+ </nav>
110
+ </div>
111
+ </div>
112
+ </section>
113
+
114
+ <style>
115
+ section {
116
+ min-height: 100vh;
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ padding: 4rem 1.5rem;
121
+ }
122
+
123
+ .hero-container {
124
+ max-width: 800px;
125
+ width: 100%;
126
+ margin: 0 auto;
127
+ }
128
+
129
+ .hero-content {
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: center;
133
+ text-align: center;
134
+ gap: 1.5rem;
135
+ }
136
+
137
+ .avatar {
138
+ width: 150px;
139
+ height: 150px;
140
+ border-radius: 50%;
141
+ object-fit: cover;
142
+ border: 3px solid var(--glass-border);
143
+ box-shadow: var(--glow-primary);
144
+ }
145
+
146
+ .name {
147
+ font-size: clamp(2.5rem, 8vw, 4rem);
148
+ font-weight: 700;
149
+ line-height: 1.2;
150
+ margin: 0;
151
+ letter-spacing: -0.02em;
152
+ }
153
+
154
+ .bio {
155
+ font-size: clamp(1rem, 3vw, 1.25rem);
156
+ color: var(--color-text-muted);
157
+ max-width: 600px;
158
+ line-height: 1.6;
159
+ margin: 0;
160
+ }
161
+
162
+ .location {
163
+ display: flex;
164
+ align-items: center;
165
+ gap: 0.5rem;
166
+ color: var(--color-text-muted);
167
+ font-size: 0.95rem;
168
+ }
169
+
170
+ .location-icon {
171
+ opacity: 0.7;
172
+ }
173
+
174
+ .social-links {
175
+ display: flex;
176
+ gap: 1rem;
177
+ margin-top: 0.5rem;
178
+ }
179
+
180
+ .social-link {
181
+ display: flex;
182
+ align-items: center;
183
+ justify-content: center;
184
+ width: 48px;
185
+ height: 48px;
186
+ border-radius: 12px;
187
+ background: var(--color-surface);
188
+ border: 1px solid var(--glass-border);
189
+ color: var(--color-text);
190
+ transition: all 0.2s ease;
191
+ }
192
+
193
+ .social-link:hover {
194
+ background: rgba(255, 255, 255, 0.1);
195
+ border-color: rgba(255, 255, 255, 0.2);
196
+ transform: translateY(-2px);
197
+ }
198
+
199
+ .social-link svg {
200
+ width: 22px;
201
+ height: 22px;
202
+ }
203
+
204
+ .fallback-icon {
205
+ font-size: 1.25rem;
206
+ font-weight: 600;
207
+ }
208
+
209
+ @media (max-width: 640px) {
210
+ section {
211
+ padding: 3rem 1rem;
212
+ }
213
+
214
+ .avatar {
215
+ width: 120px;
216
+ height: 120px;
217
+ }
218
+
219
+ .social-links {
220
+ gap: 0.75rem;
221
+ }
222
+
223
+ .social-link {
224
+ width: 44px;
225
+ height: 44px;
226
+ }
227
+ }
228
+ </style>