hexo-theme-solitude 3.0.20 → 4.0.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.
- package/.github/FUNDING.yml +3 -3
- package/README.md +11 -1
- package/README_en-US.md +11 -1
- package/_config.yml +16 -7
- package/languages/default.yml +56 -36
- package/languages/en.yml +90 -69
- package/languages/es.yml +94 -73
- package/languages/zh-CN.yml +56 -34
- package/languages/zh-TW.yml +80 -57
- package/layout/archive.pug +60 -14
- package/layout/category.pug +13 -11
- package/layout/includes/body/gadsense.pug +2 -2
- package/layout/includes/console.pug +71 -72
- package/layout/includes/footer.pug +38 -24
- package/layout/includes/head/config.pug +83 -39
- package/layout/includes/head/mode.pug +7 -7
- package/layout/includes/head/page_config.pug +20 -11
- package/layout/includes/inject/body.pug +18 -40
- package/layout/includes/keyboard.pug +3 -3
- package/layout/includes/loading.pug +1 -37
- package/layout/includes/mixins/articleSort.pug +3 -3
- package/layout/includes/mixins/common.pug +8 -4
- package/layout/includes/mixins/pace.pug +1 -1
- package/layout/includes/mixins/pagination.pug +3 -3
- package/layout/includes/nav.pug +5 -4
- package/layout/includes/page/about.pug +9 -8
- package/layout/includes/page/brevity.pug +2 -2
- package/layout/includes/page/links.pug +23 -11
- package/layout/includes/page/music.pug +0 -5
- package/layout/includes/rightmenu.pug +20 -20
- package/layout/includes/sidebar.pug +2 -2
- package/layout/includes/widgets/aside/asideInfoCard.pug +2 -2
- package/layout/includes/widgets/aside/asideTag.pug +2 -2
- package/layout/includes/widgets/home/bbTimeList.pug +3 -3
- package/layout/includes/widgets/home/categoryBar.pug +30 -15
- package/layout/includes/widgets/home/hometop.pug +113 -8
- package/layout/includes/widgets/home/postList.pug +1 -2
- package/layout/includes/widgets/home/topGroup.pug +3 -7
- package/layout/includes/widgets/nav/group.pug +4 -2
- package/layout/includes/widgets/nav/menu.pug +6 -4
- package/layout/includes/widgets/nav/right.pug +7 -6
- package/layout/includes/widgets/page/about/award.pug +67 -38
- package/layout/includes/widgets/page/about/hobbies.pug +12 -3
- package/layout/includes/widgets/page/banner.pug +1 -1
- package/layout/includes/widgets/page/kit/content.pug +2 -2
- package/layout/includes/widgets/page/links/banner.pug +30 -19
- package/layout/includes/widgets/page/message/artalk.pug +3 -3
- package/layout/includes/widgets/page/message/content.pug +3 -3
- package/layout/includes/widgets/page/message/twikoo.pug +3 -3
- package/layout/includes/widgets/page/message/valine.pug +4 -4
- package/layout/includes/widgets/page/message/waline.pug +3 -3
- package/layout/includes/widgets/page/recentcomment/artalk.pug +5 -9
- package/layout/includes/widgets/page/recentcomment/twikoo.pug +5 -9
- package/layout/includes/widgets/page/recentcomment/valine.pug +6 -10
- package/layout/includes/widgets/page/recentcomment/waline.pug +5 -9
- package/layout/includes/widgets/post/award.pug +1 -28
- package/layout/includes/widgets/post/copyright.pug +10 -10
- package/layout/includes/widgets/post/postInfo.pug +10 -11
- package/layout/includes/widgets/post/postMeta.pug +3 -2
- package/layout/includes/widgets/rightside/hide.pug +5 -5
- package/layout/includes/widgets/rightside/show.pug +7 -7
- package/layout/includes/widgets/third-party/comments/artalk.pug +9 -9
- package/layout/includes/widgets/third-party/comments/giscus.pug +2 -2
- package/layout/includes/widgets/third-party/comments/twikoo.pug +5 -5
- package/layout/includes/widgets/third-party/comments/valine.pug +6 -6
- package/layout/includes/widgets/third-party/comments/waline.pug +6 -6
- package/layout/includes/widgets/third-party/hot/index.pug +1 -1
- package/layout/includes/widgets/third-party/hot/twikoo.pug +1 -1
- package/layout/includes/widgets/third-party/music.pug +9 -5
- package/layout/includes/widgets/third-party/news-comment/artalk.pug +6 -6
- package/layout/includes/widgets/third-party/news-comment/twikoo.pug +7 -7
- package/layout/includes/widgets/third-party/news-comment/valine.pug +8 -8
- package/layout/includes/widgets/third-party/news-comment/waline.pug +7 -7
- package/layout/includes/widgets/third-party/pjax.pug +16 -10
- package/layout/includes/widgets/third-party/search/algolia-search.pug +2 -2
- package/layout/includes/widgets/third-party/search/docsearch.pug +3 -3
- package/layout/includes/widgets/third-party/search/index.pug +1 -3
- package/layout/includes/widgets/third-party/search/local-search.pug +9 -9
- package/layout/index.pug +10 -8
- package/package.json +8 -7
- package/plugins.yml +22 -21
- package/scripts/event/cdn.js +11 -6
- package/scripts/event/friend_links.js +30 -0
- package/scripts/event/merge_config.js +15 -7
- package/scripts/filter/comment.js +2 -2
- package/scripts/filter/default.js +1 -1
- package/scripts/helper/inject_head_js.js +79 -33
- package/scripts/helper/related_post.js +16 -21
- package/scripts/tags/chart.js +1 -1
- package/scripts/tags/typeit.js +1 -1
- package/source/css/_components/capsule.styl +259 -168
- package/source/css/_components/rightside.styl +98 -242
- package/source/css/_global/animation.styl +1 -10
- package/source/css/_global/index.styl +27 -1
- package/source/css/_layout/aside.styl +7 -1
- package/source/css/_layout/console.styl +144 -163
- package/source/css/_layout/header.styl +238 -47
- package/source/css/_layout/pagination.styl +176 -132
- package/source/css/_layout/rightmenu.styl +11 -2
- package/source/css/_layout/sidebar.styl +39 -17
- package/source/css/_mode/index.styl +7 -1
- package/source/css/_page/_about/about.styl +18 -2
- package/source/css/_page/_about/buff.styl +1 -1
- package/source/css/_page/_about/contentinfo.styl +2 -1
- package/source/css/_page/_about/game.styl +57 -1
- package/source/css/_page/_about/like.styl +7 -2
- package/source/css/_page/_about/maxim.styl +1 -1
- package/source/css/_page/_about/reward.styl +226 -108
- package/source/css/_page/_about/skills.styl +5 -3
- package/source/css/_page/_home/category-bar.styl +162 -88
- package/source/css/_page/_home/home-top.styl +309 -263
- package/source/css/_page/_home/home.styl +34 -7
- package/source/css/_page/archive.styl +412 -0
- package/source/css/_page/category.styl +27 -1
- package/source/css/_page/index.styl +2 -0
- package/source/css/_page/links.styl +190 -0
- package/source/css/_page/other.styl +37 -0
- package/source/css/_post/index.styl +2 -1
- package/source/css/_post/meta.styl +245 -217
- package/source/css/_search/local-search.styl +158 -82
- package/source/css/var.styl +2 -2
- package/source/js/archive-page.js +191 -0
- package/source/js/core/actions.js +48 -0
- package/source/js/core/api.js +32 -0
- package/source/js/core/config.js +13 -0
- package/source/js/core/lifecycle.js +52 -0
- package/source/js/core/preloader.js +34 -0
- package/source/js/core/resources.js +65 -0
- package/source/js/covercolor/api.js +13 -103
- package/source/js/covercolor/ave.js +14 -77
- package/source/js/covercolor/local.js +22 -116
- package/source/js/covercolor/shared.js +102 -0
- package/source/js/friend_links.js +502 -0
- package/source/js/main.js +793 -177
- package/source/js/music.js +24 -14
- package/source/js/post_ai.js +13 -2
- package/source/js/right_menu.js +44 -24
- package/source/js/search/algolia.js +29 -21
- package/source/js/search/local.js +231 -266
- package/source/js/tw_cn.js +37 -12
- package/source/js/utils.js +37 -25
- package/layout/includes/widgets/randomlink.pug +0 -43
- package/scripts/filter/randomPosts.js +0 -16
|
@@ -1,67 +1,145 @@
|
|
|
1
|
+
html.search-open
|
|
2
|
+
overflow hidden
|
|
3
|
+
|
|
1
4
|
.search-dialog
|
|
2
5
|
position fixed
|
|
3
|
-
|
|
4
|
-
left 50%
|
|
6
|
+
inset 0
|
|
5
7
|
z-index 1001
|
|
6
8
|
display none
|
|
7
|
-
margin
|
|
9
|
+
margin auto
|
|
8
10
|
padding 1rem
|
|
9
11
|
width 30rem
|
|
12
|
+
height fit-content
|
|
13
|
+
max-height 90dvh
|
|
14
|
+
overflow hidden
|
|
10
15
|
box-shadow var(--efu-shadow-lightblack)
|
|
11
16
|
background var(--efu-card-bg)
|
|
12
17
|
border var(--style-border)
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
border-radius 12px
|
|
19
|
+
transition .3s
|
|
15
20
|
animation slide-in .6s ease 0s 1 normal none running
|
|
16
21
|
gap 16px
|
|
17
|
-
flex-direction
|
|
22
|
+
flex-direction column
|
|
18
23
|
|
|
19
24
|
&:hover
|
|
20
25
|
border var(--style-border-hover)
|
|
21
26
|
box-shadow var(--efu-shadow-theme)
|
|
22
|
-
+maxWidth768()
|
|
23
|
-
border none
|
|
24
|
-
|
|
25
|
-
+maxWidth768()
|
|
26
|
-
top 0
|
|
27
|
-
left 0
|
|
28
|
-
margin 0
|
|
29
|
-
width 100%
|
|
30
|
-
height 100%
|
|
31
|
-
border 0
|
|
32
27
|
|
|
33
28
|
.search-dialog-title
|
|
34
|
-
font-weight 700
|
|
35
29
|
color var(--efu-main)
|
|
36
30
|
font-size 1.4em
|
|
31
|
+
font-weight 700
|
|
37
32
|
line-height 1
|
|
38
33
|
|
|
39
34
|
.search-close-button
|
|
35
|
+
display inline-flex
|
|
36
|
+
align-items center
|
|
37
|
+
justify-content center
|
|
38
|
+
flex 0 0 32px
|
|
39
|
+
width 32px
|
|
40
|
+
height 32px
|
|
41
|
+
margin-left 4px
|
|
42
|
+
padding 4px
|
|
40
43
|
color var(--efu-gray)
|
|
44
|
+
background transparent
|
|
45
|
+
border 0
|
|
46
|
+
border-radius 50%
|
|
41
47
|
font-size 1.4em
|
|
42
48
|
line-height 1
|
|
43
49
|
cursor pointer
|
|
44
|
-
transition color .2s ease-in-out
|
|
50
|
+
transition color .2s ease-in-out, background .2s ease-in-out
|
|
45
51
|
|
|
46
52
|
&:hover
|
|
47
53
|
color var(--efu-main)
|
|
54
|
+
background var(--efu-main-op)
|
|
55
|
+
|
|
56
|
+
.search-wrap
|
|
57
|
+
display flex
|
|
58
|
+
min-height 0
|
|
59
|
+
overflow hidden
|
|
60
|
+
flex-direction column
|
|
61
|
+
gap 16px
|
|
62
|
+
|
|
63
|
+
.search-box
|
|
64
|
+
position relative
|
|
65
|
+
width 100%
|
|
66
|
+
max-width 100%
|
|
67
|
+
margin 0 auto
|
|
68
|
+
|
|
69
|
+
input
|
|
70
|
+
width 100%
|
|
71
|
+
height 44px
|
|
72
|
+
padding .25rem .7rem
|
|
73
|
+
outline 0
|
|
74
|
+
color var(--efu-fontcolor)
|
|
75
|
+
background var(--efu-secondbg)
|
|
76
|
+
border var(--style-border)
|
|
77
|
+
border-radius 12px
|
|
78
|
+
caret-color var(--efu-theme)
|
|
79
|
+
|
|
80
|
+
&:focus
|
|
81
|
+
border var(--style-border-hover)
|
|
82
|
+
|
|
83
|
+
&::placeholder
|
|
84
|
+
color var(--efu-gray)
|
|
85
|
+
opacity .8
|
|
86
|
+
|
|
87
|
+
#search-suggestions
|
|
88
|
+
display flex
|
|
89
|
+
flex-wrap wrap
|
|
90
|
+
gap 8px
|
|
91
|
+
|
|
92
|
+
&[hidden]
|
|
93
|
+
display none
|
|
94
|
+
|
|
95
|
+
.tag-list
|
|
96
|
+
min-height 30px
|
|
97
|
+
padding 3px 8px
|
|
98
|
+
color var(--efu-fontcolor)
|
|
99
|
+
background transparent
|
|
100
|
+
border var(--style-border-always)
|
|
101
|
+
border-radius 8px
|
|
102
|
+
line-height 1.2
|
|
103
|
+
cursor pointer
|
|
104
|
+
transition color .2s ease-in-out, background .2s ease-in-out, border-color .2s ease-in-out
|
|
105
|
+
|
|
106
|
+
&:hover,
|
|
107
|
+
&:focus-visible
|
|
108
|
+
color var(--efu-white)
|
|
109
|
+
background var(--efu-main)
|
|
110
|
+
border-color var(--efu-main)
|
|
111
|
+
outline 0
|
|
48
112
|
|
|
49
113
|
#search-results
|
|
50
|
-
|
|
114
|
+
display flex
|
|
115
|
+
max-height calc(90dvh - 250px)
|
|
116
|
+
min-height 0
|
|
117
|
+
margin 0
|
|
118
|
+
padding 0
|
|
51
119
|
overflow-y auto
|
|
52
|
-
|
|
120
|
+
flex 1
|
|
121
|
+
flex-direction column
|
|
122
|
+
list-style none
|
|
123
|
+
|
|
124
|
+
&[hidden]
|
|
125
|
+
display none
|
|
53
126
|
|
|
54
127
|
.search-result-item
|
|
128
|
+
width 100%
|
|
55
129
|
padding-left 0
|
|
56
130
|
font-weight 700
|
|
131
|
+
line-height 1.5
|
|
57
132
|
|
|
58
133
|
.search-result-title
|
|
134
|
+
display block
|
|
59
135
|
position relative
|
|
60
|
-
|
|
136
|
+
width 100%
|
|
137
|
+
padding 3px 0
|
|
138
|
+
overflow hidden
|
|
139
|
+
color var(--efu-fontcolor)
|
|
61
140
|
white-space nowrap
|
|
62
141
|
text-overflow ellipsis
|
|
63
|
-
|
|
64
|
-
display block
|
|
142
|
+
transition color .2s ease-in-out
|
|
65
143
|
|
|
66
144
|
&:hover
|
|
67
145
|
color var(--efu-hovertext)
|
|
@@ -70,39 +148,50 @@
|
|
|
70
148
|
color var(--efu-theme)
|
|
71
149
|
font-style normal
|
|
72
150
|
|
|
73
|
-
|
|
74
|
-
|
|
151
|
+
.search-result-empty,
|
|
152
|
+
.search-result-error,
|
|
153
|
+
.search-result-loading
|
|
154
|
+
color var(--efu-secondtext)
|
|
75
155
|
|
|
76
156
|
#search-pagination
|
|
157
|
+
&:empty
|
|
158
|
+
display none
|
|
159
|
+
|
|
77
160
|
.pagination-list
|
|
161
|
+
display flex
|
|
162
|
+
justify-content center
|
|
163
|
+
gap 8px
|
|
164
|
+
margin 0
|
|
78
165
|
padding 0
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
166
|
+
list-style none
|
|
167
|
+
|
|
168
|
+
.pagination-button
|
|
169
|
+
display inline-flex
|
|
170
|
+
align-items center
|
|
171
|
+
justify-content center
|
|
172
|
+
min-width 1.2rem
|
|
173
|
+
height 1.2rem
|
|
174
|
+
padding 0
|
|
175
|
+
color var(--efu-fontcolor)
|
|
176
|
+
background transparent
|
|
177
|
+
border 0
|
|
178
|
+
border-radius 4px
|
|
179
|
+
cursor pointer
|
|
92
180
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
background var(--efu-main)
|
|
96
|
-
color var(--efu-white)
|
|
97
|
-
cursor default
|
|
181
|
+
&:hover
|
|
182
|
+
color var(--efu-main)
|
|
98
183
|
|
|
99
|
-
|
|
100
|
-
|
|
184
|
+
&.select
|
|
185
|
+
color var(--efu-white)
|
|
186
|
+
background var(--efu-main)
|
|
187
|
+
cursor default
|
|
101
188
|
|
|
102
189
|
#search-tips
|
|
190
|
+
width 100%
|
|
103
191
|
color var(--efu-secondtext)
|
|
104
|
-
|
|
105
|
-
|
|
192
|
+
|
|
193
|
+
&:empty
|
|
194
|
+
display none
|
|
106
195
|
|
|
107
196
|
.search-result-count
|
|
108
197
|
color var(--efu-gray)
|
|
@@ -111,51 +200,38 @@
|
|
|
111
200
|
b
|
|
112
201
|
color var(--efu-main)
|
|
113
202
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
width 100%
|
|
119
|
-
|
|
120
|
-
input
|
|
121
|
-
height 100%
|
|
122
|
-
width 100%
|
|
123
|
-
border-radius 8px
|
|
124
|
-
border var(--style-border)
|
|
125
|
-
padding 0.25rem 0.7rem;
|
|
126
|
-
outline 0
|
|
127
|
-
background var(--efu-secondbg)
|
|
128
|
-
color var(--efu-fontcolor)
|
|
129
|
-
caret-color var(--efu-theme)
|
|
130
|
-
|
|
131
|
-
&::placeholder
|
|
132
|
-
color var(--efu-gray)
|
|
133
|
-
opacity .8
|
|
203
|
+
.search-nav
|
|
204
|
+
display flex
|
|
205
|
+
align-items center
|
|
206
|
+
justify-content space-between
|
|
134
207
|
|
|
135
208
|
#search-mask
|
|
136
209
|
position fixed
|
|
137
210
|
inset 0
|
|
138
211
|
z-index 1000
|
|
139
212
|
display none
|
|
140
|
-
background
|
|
213
|
+
background var(--efu-maskbgdeep)
|
|
141
214
|
backdrop-filter blur(12px)
|
|
142
215
|
-webkit-backdrop-filter blur(12px)
|
|
143
216
|
transform translateZ(0)
|
|
144
|
-
background var(--efu-maskbgdeep)
|
|
145
217
|
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
218
|
+
@media screen and (max-width: 767px)
|
|
219
|
+
.search-dialog
|
|
220
|
+
inset 0
|
|
221
|
+
width 100vw
|
|
222
|
+
height var(--search-height, 100dvh)
|
|
223
|
+
max-height none
|
|
224
|
+
margin 0
|
|
225
|
+
border 0
|
|
226
|
+
border-radius 0
|
|
153
227
|
|
|
154
228
|
&:hover
|
|
155
|
-
|
|
156
|
-
|
|
229
|
+
border 0
|
|
230
|
+
box-shadow var(--efu-shadow-lightblack)
|
|
157
231
|
|
|
158
|
-
.search-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
232
|
+
.search-wrap
|
|
233
|
+
flex 1
|
|
234
|
+
|
|
235
|
+
#search-results
|
|
236
|
+
max-height none
|
|
237
|
+
flex 1
|
package/source/css/var.styl
CHANGED
|
@@ -6,12 +6,12 @@ $dark_theme = convert(hexo-config('theme_color.dark'))
|
|
|
6
6
|
$dark_theme_op = convert(hexo-config('theme_color.dark') + '23')
|
|
7
7
|
$dark_theme_op_deep = convert(hexo-config('theme_color.dark') + 'dd')
|
|
8
8
|
$dark_theme_none = convert(hexo-config('theme_color.dark') + '00')
|
|
9
|
+
$dark_nav_hover_text = convert(hexo-config('theme_color.nav_hover_text.dark'))
|
|
9
10
|
$light_theme = convert(hexo-config('theme_color.light'))
|
|
10
11
|
$light_theme_op = convert(hexo-config('theme_color.light') + '23')
|
|
11
12
|
$light_theme_op_deep = convert(hexo-config('theme_color.light') + 'dd')
|
|
12
13
|
$light_theme_none = convert(hexo-config('theme_color.light') + '00')
|
|
13
|
-
|
|
14
|
-
$todayCardColor = convert(hexo-config('hometop.recommendList.color'))
|
|
14
|
+
$light_nav_hover_text = convert(hexo-config('theme_color.nav_hover_text.light'))
|
|
15
15
|
|
|
16
16
|
// code
|
|
17
17
|
if hexo-config('highlight.theme') == 'default'
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Solitude } from "./core/api.js";
|
|
2
|
+
|
|
3
|
+
export const archivePageController = (() => {
|
|
4
|
+
const createElement = (tag, className, text) => {
|
|
5
|
+
const element = document.createElement(tag);
|
|
6
|
+
if (className) element.className = className;
|
|
7
|
+
if (text !== undefined) element.textContent = text;
|
|
8
|
+
return element;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const controller = {
|
|
12
|
+
init() {
|
|
13
|
+
const shell = document.getElementById('archives-page');
|
|
14
|
+
const dataElement = document.getElementById('archive-page-data');
|
|
15
|
+
if (!shell || !dataElement || shell.dataset.archiveInitialized === 'true') return;
|
|
16
|
+
|
|
17
|
+
let posts;
|
|
18
|
+
try {
|
|
19
|
+
posts = JSON.parse(dataElement.content?.textContent || dataElement.textContent || '[]');
|
|
20
|
+
} catch (error) {
|
|
21
|
+
this.renderState(shell, '文章数据加载失败,请稍后再试。');
|
|
22
|
+
console.error('Failed to parse archive data:', error);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
shell.dataset.archiveInitialized = 'true';
|
|
27
|
+
this.shell = shell;
|
|
28
|
+
this.list = shell.querySelector('#archives-page-list');
|
|
29
|
+
this.yearList = shell.querySelector('#archives-year-filter-list');
|
|
30
|
+
this.pagination = shell.querySelector('#pagination .pagination');
|
|
31
|
+
this.paginationSection = shell.querySelector('.archive-page-section-pagination');
|
|
32
|
+
this.posts = Array.isArray(posts) ? posts : [];
|
|
33
|
+
this.perPage = Math.max(Number(shell.dataset.perPage) || 10, 1);
|
|
34
|
+
this.years = [...new Set(this.posts.map(post => String(post.year)))].sort((a, b) => Number(b) - Number(a));
|
|
35
|
+
this.activeYear = this.years.includes(shell.dataset.initialYear) ? shell.dataset.initialYear : 'all';
|
|
36
|
+
this.currentPage = Math.max(Number(shell.dataset.initialPage) || 1, 1);
|
|
37
|
+
|
|
38
|
+
this.renderYears();
|
|
39
|
+
this.bindEvents();
|
|
40
|
+
this.render();
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
bindEvents() {
|
|
44
|
+
this.shell.addEventListener('click', event => {
|
|
45
|
+
const yearButton = event.target.closest('.archive-year-button');
|
|
46
|
+
if (yearButton) {
|
|
47
|
+
this.activeYear = yearButton.dataset.year;
|
|
48
|
+
this.currentPage = 1;
|
|
49
|
+
this.render();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const pageButton = event.target.closest('[data-archive-page]');
|
|
54
|
+
if (!pageButton || pageButton.disabled) return;
|
|
55
|
+
this.currentPage = Number(pageButton.dataset.archivePage);
|
|
56
|
+
this.render();
|
|
57
|
+
this.shell.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
renderYears() {
|
|
62
|
+
this.yearList.replaceChildren();
|
|
63
|
+
this.years.forEach(year => {
|
|
64
|
+
const button = createElement('button', 'archive-year-button', year);
|
|
65
|
+
button.type = 'button';
|
|
66
|
+
button.dataset.year = year;
|
|
67
|
+
button.setAttribute('aria-pressed', 'false');
|
|
68
|
+
this.yearList.appendChild(button);
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
render() {
|
|
73
|
+
const filteredPosts = this.activeYear === 'all'
|
|
74
|
+
? this.posts
|
|
75
|
+
: this.posts.filter(post => String(post.year) === this.activeYear);
|
|
76
|
+
const totalPages = Math.max(Math.ceil(filteredPosts.length / this.perPage), 1);
|
|
77
|
+
this.currentPage = Math.min(this.currentPage, totalPages);
|
|
78
|
+
|
|
79
|
+
this.shell.querySelectorAll('.archive-year-button').forEach(button => {
|
|
80
|
+
const active = button.dataset.year === this.activeYear;
|
|
81
|
+
button.classList.toggle('is-active', active);
|
|
82
|
+
button.setAttribute('aria-pressed', String(active));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
this.list.replaceChildren();
|
|
86
|
+
if (!filteredPosts.length) {
|
|
87
|
+
this.renderState(this.shell, '当前年份下暂无文章。');
|
|
88
|
+
} else {
|
|
89
|
+
const start = (this.currentPage - 1) * this.perPage;
|
|
90
|
+
filteredPosts.slice(start, start + this.perPage).forEach(post => {
|
|
91
|
+
this.list.appendChild(this.createPostItem(post));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.renderPagination(totalPages);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
createPostItem(post) {
|
|
99
|
+
const item = createElement('a', 'archive-page-item');
|
|
100
|
+
item.href = post.url;
|
|
101
|
+
item.title = post.title;
|
|
102
|
+
|
|
103
|
+
const thumb = createElement('div', 'archive-page-thumb');
|
|
104
|
+
const image = createElement('img');
|
|
105
|
+
image.src = post.cover;
|
|
106
|
+
image.alt = post.title;
|
|
107
|
+
image.loading = 'lazy';
|
|
108
|
+
image.addEventListener('error', () => thumb.classList.add('is-fallback'), { once: true });
|
|
109
|
+
const fallback = createElement('span', 'archive-page-thumb-fallback', (post.title || '文').trim().charAt(0) || '文');
|
|
110
|
+
thumb.append(image, fallback);
|
|
111
|
+
|
|
112
|
+
const main = createElement('div', 'archive-page-item-main');
|
|
113
|
+
const title = createElement('div', 'archive-page-item-title', post.title);
|
|
114
|
+
const meta = createElement('div', 'archive-page-item-meta');
|
|
115
|
+
meta.append(
|
|
116
|
+
createElement('span', 'archive-page-item-category', post.primaryCategory || '未分类'),
|
|
117
|
+
createElement('span', 'archive-page-item-divider', '/'),
|
|
118
|
+
createElement('span', 'archive-page-item-date', post.dateLabel)
|
|
119
|
+
);
|
|
120
|
+
main.append(title, meta);
|
|
121
|
+
|
|
122
|
+
const arrow = createElement('div', 'archive-page-item-arrow');
|
|
123
|
+
const arrowIcon = createElement('i', 'solitude fas fa-chevron-right');
|
|
124
|
+
arrow.setAttribute('aria-hidden', 'true');
|
|
125
|
+
arrow.appendChild(arrowIcon);
|
|
126
|
+
item.append(thumb, main, arrow);
|
|
127
|
+
return item;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
renderPagination(totalPages) {
|
|
131
|
+
this.pagination.replaceChildren();
|
|
132
|
+
this.paginationSection.hidden = totalPages <= 1;
|
|
133
|
+
if (totalPages <= 1) return;
|
|
134
|
+
|
|
135
|
+
this.pagination.appendChild(this.createPageButton(this.currentPage - 1, '上一页', 'archive-page-extend prev', this.currentPage === 1, 'fa-chevron-left'));
|
|
136
|
+
|
|
137
|
+
this.getPageRange(totalPages).forEach(page => {
|
|
138
|
+
if (page === 'space') {
|
|
139
|
+
this.pagination.appendChild(createElement('span', 'archive-page-space', '...'));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const button = this.createPageButton(page, String(page), 'archive-page-number', false);
|
|
143
|
+
if (page === this.currentPage) {
|
|
144
|
+
button.classList.add('is-current');
|
|
145
|
+
button.setAttribute('aria-current', 'page');
|
|
146
|
+
}
|
|
147
|
+
this.pagination.appendChild(button);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
this.pagination.appendChild(this.createPageButton(this.currentPage + 1, '下一页', 'archive-page-extend next', this.currentPage === totalPages, 'fa-chevron-right'));
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
createPageButton(page, label, className, disabled, iconName) {
|
|
154
|
+
const button = createElement('button', className);
|
|
155
|
+
button.type = 'button';
|
|
156
|
+
button.disabled = disabled;
|
|
157
|
+
button.dataset.archivePage = String(page);
|
|
158
|
+
button.setAttribute('aria-label', label);
|
|
159
|
+
if (iconName === 'fa-chevron-left') button.appendChild(createElement('i', `solitude fas ${iconName}`));
|
|
160
|
+
button.appendChild(createElement('span', '', label));
|
|
161
|
+
if (iconName === 'fa-chevron-right') button.appendChild(createElement('i', `solitude fas ${iconName}`));
|
|
162
|
+
return button;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
getPageRange(totalPages) {
|
|
166
|
+
if (totalPages <= 7) return Array.from({ length: totalPages }, (_, index) => index + 1);
|
|
167
|
+
if (this.currentPage <= 4) return [1, 2, 3, 4, 5, 'space', totalPages];
|
|
168
|
+
if (this.currentPage >= totalPages - 3) return [1, 'space', totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages];
|
|
169
|
+
return [1, 'space', this.currentPage - 1, this.currentPage, this.currentPage + 1, 'space', totalPages];
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
renderState(shell, message) {
|
|
173
|
+
const list = shell.querySelector('#archives-page-list');
|
|
174
|
+
if (!list) return;
|
|
175
|
+
list.replaceChildren(createElement('div', 'archive-page-state', message));
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
destroy() {
|
|
179
|
+
this.shell = null;
|
|
180
|
+
this.list = null;
|
|
181
|
+
this.yearList = null;
|
|
182
|
+
this.pagination = null;
|
|
183
|
+
this.paginationSection = null;
|
|
184
|
+
this.posts = [];
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return controller;
|
|
189
|
+
})();
|
|
190
|
+
|
|
191
|
+
export const initArchivePage = () => archivePageController.init();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const initActionDelegation = (api) => {
|
|
2
|
+
if (document.documentElement.dataset.solitudeActions === "true") return;
|
|
3
|
+
document.documentElement.dataset.solitudeActions = "true";
|
|
4
|
+
|
|
5
|
+
const dispatch = (event) => {
|
|
6
|
+
if (!(event.target instanceof Element)) return;
|
|
7
|
+
const element = event.target.closest("[data-solitude-action]");
|
|
8
|
+
if (!element) return;
|
|
9
|
+
const action = api[element.dataset.solitudeAction];
|
|
10
|
+
if (typeof action !== "function") return;
|
|
11
|
+
|
|
12
|
+
if (element.dataset.solitudePrevent === "true") event.preventDefault();
|
|
13
|
+
if (element.dataset.solitudeStop === "true") event.stopPropagation();
|
|
14
|
+
|
|
15
|
+
let argument;
|
|
16
|
+
if (element.dataset.solitudeTarget) argument = element.dataset.solitudeTarget;
|
|
17
|
+
else if (element.dataset.solitudeUrl) argument = element.dataset.solitudeUrl;
|
|
18
|
+
else if (element.dataset.solitudeValue) argument = element.dataset.solitudeValue;
|
|
19
|
+
else if (element.dataset.solitudeEvent === "true") argument = event;
|
|
20
|
+
|
|
21
|
+
action.call(api, argument, event, element);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
document.addEventListener("click", dispatch);
|
|
25
|
+
document.addEventListener("keydown", (event) => {
|
|
26
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
27
|
+
if (!(event.target instanceof Element)) return;
|
|
28
|
+
const element = event.target.closest("[data-solitude-action]");
|
|
29
|
+
if (!element || /^(A|BUTTON|INPUT)$/.test(element.tagName)) return;
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
element.click();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
document.addEventListener("error", (event) => {
|
|
35
|
+
const element = event.target;
|
|
36
|
+
if (!(element instanceof Element)) return;
|
|
37
|
+
if (element.matches("[data-solitude-hide-ads]")) {
|
|
38
|
+
document
|
|
39
|
+
.querySelectorAll(".google-ads-warp")
|
|
40
|
+
.forEach((item) => { item.style.display = "none"; });
|
|
41
|
+
}
|
|
42
|
+
const fallback = element.dataset.solitudeFallback;
|
|
43
|
+
if (fallback && element.getAttribute("src") !== fallback) {
|
|
44
|
+
element.removeAttribute("data-solitude-fallback");
|
|
45
|
+
element.setAttribute("src", fallback);
|
|
46
|
+
}
|
|
47
|
+
}, true);
|
|
48
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getConfig, getPageConfig } from "./config.js";
|
|
2
|
+
import { lifecycle } from "./lifecycle.js";
|
|
3
|
+
import { loadScript, loadStyle } from "./resources.js";
|
|
4
|
+
|
|
5
|
+
const api = window.Solitude || {};
|
|
6
|
+
|
|
7
|
+
Object.defineProperties(api, {
|
|
8
|
+
config: { configurable: true, get: getConfig },
|
|
9
|
+
page: { configurable: true, get: getPageConfig },
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
Object.assign(api, {
|
|
13
|
+
loadScript(url, options) {
|
|
14
|
+
if (/barrage(?:\.min)?\.js(?:\?|$)/.test(url)) api.installLegacyAdapter?.();
|
|
15
|
+
return loadScript(url, options);
|
|
16
|
+
},
|
|
17
|
+
loadStyle,
|
|
18
|
+
on: lifecycle.on.bind(lifecycle),
|
|
19
|
+
listen: lifecycle.listen.bind(lifecycle),
|
|
20
|
+
onPageCleanup: lifecycle.add.bind(lifecycle),
|
|
21
|
+
disposePage: lifecycle.disposePage.bind(lifecycle),
|
|
22
|
+
navigate(url) {
|
|
23
|
+
if (!url) return;
|
|
24
|
+
const instance = api.pjax;
|
|
25
|
+
if (instance?.loadUrl) instance.loadUrl(url);
|
|
26
|
+
else window.location.assign(url);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
window.Solitude = api;
|
|
31
|
+
|
|
32
|
+
export { api as Solitude };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const parseConfig = (id, fallback = {}) => {
|
|
2
|
+
const element = document.getElementById(id);
|
|
3
|
+
if (!element) return fallback;
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(element.content?.textContent || element.textContent || "{}");
|
|
6
|
+
} catch (error) {
|
|
7
|
+
console.error(`Invalid Solitude configuration in #${id}:`, error);
|
|
8
|
+
return fallback;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getConfig = () => parseConfig("site-config");
|
|
13
|
+
export const getPageConfig = () => parseConfig("config-diff");
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const EVENT_PREFIX = "solitude:";
|
|
2
|
+
|
|
3
|
+
class Lifecycle {
|
|
4
|
+
#pageController = new AbortController();
|
|
5
|
+
#disposers = new Set();
|
|
6
|
+
|
|
7
|
+
get signal() {
|
|
8
|
+
return this.#pageController.signal;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
add(disposer) {
|
|
12
|
+
if (typeof disposer !== "function") return () => {};
|
|
13
|
+
this.#disposers.add(disposer);
|
|
14
|
+
return () => this.#disposers.delete(disposer);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
listen(target, type, handler, options = {}) {
|
|
18
|
+
if (!target?.addEventListener) return () => {};
|
|
19
|
+
const normalized =
|
|
20
|
+
typeof options === "boolean" ? { capture: options } : { ...options };
|
|
21
|
+
normalized.signal ??= this.signal;
|
|
22
|
+
target.addEventListener(type, handler, normalized);
|
|
23
|
+
return () => target.removeEventListener(type, handler, normalized);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
disposePage() {
|
|
27
|
+
this.#pageController.abort();
|
|
28
|
+
this.#disposers.forEach((dispose) => {
|
|
29
|
+
try {
|
|
30
|
+
dispose();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error("Failed to dispose a Solitude page resource:", error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
this.#disposers.clear();
|
|
36
|
+
this.#pageController = new AbortController();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
emit(type, detail) {
|
|
40
|
+
document.dispatchEvent(
|
|
41
|
+
new CustomEvent(`${EVENT_PREFIX}${type}`, { detail })
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
on(type, handler) {
|
|
46
|
+
const eventName = `${EVENT_PREFIX}${type}`;
|
|
47
|
+
document.addEventListener(eventName, handler);
|
|
48
|
+
return () => document.removeEventListener(eventName, handler);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const lifecycle = new Lifecycle();
|