@xingwangzhe/stalux 1.27.0 → 1.28.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/README.md +6 -6
- package/package.json +3 -2
- package/src/components/stalux/archives/archivesList.astro +34 -42
- package/src/components/stalux/categories/categoriesList.astro +4 -4
- package/src/components/stalux/common/textType.astro +1 -3
- package/src/components/stalux/footer/FooterBadges.astro +28 -24
- package/src/components/stalux/footer/FooterBeian.astro +22 -26
- package/src/components/stalux/footer/FooterStats.astro +28 -27
- package/src/components/stalux/footer/FooterThemeInfo.astro +24 -28
- package/src/components/stalux/layout/footer.astro +5 -14
- package/src/components/stalux/layout/head.astro +5 -9
- package/src/components/stalux/layout/navs.astro +16 -20
- package/src/components/stalux/links/linkList.astro +4 -4
- package/src/components/stalux/links/socialMedia.astro +34 -34
- package/src/components/stalux/posts/PostTaxonomy.astro +14 -14
- package/src/components/stalux/posts/WalineComment.astro +20 -464
- package/src/components/stalux/posts/postCC.astro +7 -7
- package/src/components/stalux/posts/postCard.astro +41 -41
- package/src/components/stalux/posts/postContent.astro +46 -41
- package/src/components/stalux/posts/postList.astro +4 -4
- package/src/components/stalux/posts/postNavigation.astro +107 -119
- package/src/components/stalux/posts/postToc.astro +20 -20
- package/src/components/stalux/tags/tagsCloud.astro +15 -15
- package/src/components/stalux/words/wordCard.astro +27 -32
- package/src/components/stalux/words/wordList.astro +18 -18
- package/src/i18n/en.json +1 -0
- package/src/i18n/zh-CN.json +1 -0
- package/src/index.ts +82 -34
- package/src/internal/font-slices.ts +151 -136
- package/src/layouts/PostLayout.astro +6 -6
- package/src/layouts/Stalux.astro +4 -8
- package/src/pages/about.astro +6 -6
- package/src/pages/categories/[category].astro +5 -1
- package/src/pages/posts/[post].astro +2 -2
- package/src/plugins/feature-flags.ts +60 -8
- package/src/scripts/waline.ts +32 -11
- package/src/styles/base/init.css +2 -1
- package/src/styles/base/math.css +1 -1
- package/src/styles/components/posts/waline.css +440 -0
- package/src/utils/i18n.ts +16 -5
|
@@ -221,19 +221,71 @@ export const featureFlagsHast = defineHastPlugin({
|
|
|
221
221
|
// 第一张正文图片可能成为 LCP,优先加载;其余图片延迟加载。
|
|
222
222
|
const firstImage = !ctx.data.staluxFirstImageSeen;
|
|
223
223
|
ctx.data.staluxFirstImageSeen = true;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
ctx.setProperty(node, "decoding", "async");
|
|
229
|
-
}
|
|
230
|
-
if (firstImage && !node.properties?.fetchpriority) {
|
|
231
|
-
ctx.setProperty(node, "fetchpriority", "high");
|
|
224
|
+
const properties = applyImageLoadingPolicy({ ...(node.properties ?? {}) }, firstImage);
|
|
225
|
+
for (const key of ["loading", "decoding", "fetchpriority"] as const) {
|
|
226
|
+
const value = properties[key];
|
|
227
|
+
if (typeof value === "string") ctx.setProperty(node, key, value);
|
|
232
228
|
}
|
|
233
229
|
},
|
|
234
230
|
},
|
|
235
231
|
});
|
|
236
232
|
|
|
233
|
+
export function applyImageLoadingPolicy(
|
|
234
|
+
properties: Record<string, unknown>,
|
|
235
|
+
firstImage: boolean,
|
|
236
|
+
): Record<string, unknown> {
|
|
237
|
+
if (!properties.loading) properties.loading = firstImage ? "eager" : "lazy";
|
|
238
|
+
if (!properties.decoding) properties.decoding = "async";
|
|
239
|
+
if (firstImage) {
|
|
240
|
+
if (!properties.fetchpriority) properties.fetchpriority = "high";
|
|
241
|
+
} else {
|
|
242
|
+
properties.loading = "lazy";
|
|
243
|
+
if (properties.fetchpriority === "high") properties.fetchpriority = "auto";
|
|
244
|
+
}
|
|
245
|
+
return properties;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function applyHtmlImageLoadingPolicy(html: string): string {
|
|
249
|
+
return html.replace(
|
|
250
|
+
/<section\b(?=[^>]*\bdata-pagefind-body\b)[^>]*>[\s\S]*?<\/section>/gi,
|
|
251
|
+
(body) => {
|
|
252
|
+
let imageIndex = 0;
|
|
253
|
+
return body.replace(/<img\b[^>]*>/gi, (tag) => {
|
|
254
|
+
const isFirstBodyImage = imageIndex++ === 0;
|
|
255
|
+
const attributes = new Map<string, string>();
|
|
256
|
+
for (const match of tag.matchAll(/\s([\w:-]+)(?:="([^"]*)")?/g)) {
|
|
257
|
+
const name = match[1];
|
|
258
|
+
if (name) attributes.set(name.toLowerCase(), match[2] ?? "");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const updates: Record<string, string> = {
|
|
262
|
+
decoding: attributes.get("decoding") || "async",
|
|
263
|
+
loading: isFirstBodyImage ? "eager" : "lazy",
|
|
264
|
+
};
|
|
265
|
+
if (isFirstBodyImage) {
|
|
266
|
+
updates.fetchpriority = "high";
|
|
267
|
+
} else if (attributes.get("fetchpriority") === "high") {
|
|
268
|
+
updates.fetchpriority = "auto";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
let result = tag;
|
|
272
|
+
for (const [name, value] of Object.entries(updates)) {
|
|
273
|
+
const pattern = new RegExp(`\\s${name}(?:="[^"]*")?`, "i");
|
|
274
|
+
if (pattern.test(result)) {
|
|
275
|
+
result = result.replace(pattern, ` ${name}="${value}"`);
|
|
276
|
+
} else {
|
|
277
|
+
result = result.replace(
|
|
278
|
+
/\s*\/?>$/,
|
|
279
|
+
(ending) => ` ${name}="${value}"${ending.trimStart()}`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
237
289
|
type FeatureFlagsResult = {
|
|
238
290
|
hasImage: boolean;
|
|
239
291
|
readingMinutes: number;
|
package/src/scripts/waline.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { WalineInitOptions } from "@waline/client";
|
|
2
|
+
import walineCss from "@waline/client/style?inline";
|
|
3
|
+
import staluxWalineCss from "../styles/components/posts/waline.css?inline";
|
|
2
4
|
import { createClientLogger } from "./logger";
|
|
3
|
-
|
|
4
5
|
import { registerPageLifecycle } from "./page-runtime";
|
|
5
6
|
|
|
6
7
|
const logger = createClientLogger("waline");
|
|
@@ -31,14 +32,34 @@ registerPageLifecycle("waline", () => {
|
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
let disposed = false;
|
|
36
|
+
let destroy: (() => void) | undefined;
|
|
37
|
+
|
|
38
|
+
void import("@waline/client")
|
|
39
|
+
.then((waline) => {
|
|
40
|
+
if (disposed || !commentElement.isConnected) return;
|
|
41
|
+
const style = document.createElement("style");
|
|
42
|
+
style.dataset.staluxWaline = "true";
|
|
43
|
+
style.textContent = `${walineCss}\n${staluxWalineCss}`;
|
|
44
|
+
document.head.append(style);
|
|
45
|
+
const options = {
|
|
46
|
+
...decoded,
|
|
47
|
+
el: commentElement,
|
|
48
|
+
serverURL: decoded.serverURL,
|
|
49
|
+
path: typeof decoded.path === "string" ? decoded.path : window.location.pathname,
|
|
50
|
+
} as WalineInitOptions;
|
|
51
|
+
const instance = waline.init(options);
|
|
52
|
+
destroy = () => instance?.destroy();
|
|
53
|
+
logger.debug("comment widget initialized");
|
|
54
|
+
})
|
|
55
|
+
.catch((error) => {
|
|
56
|
+
logger.warn(
|
|
57
|
+
`comment assets failed to load (${error instanceof Error ? error.name : "unknown error"})`,
|
|
58
|
+
);
|
|
59
|
+
});
|
|
42
60
|
|
|
43
|
-
return () =>
|
|
61
|
+
return () => {
|
|
62
|
+
disposed = true;
|
|
63
|
+
destroy?.();
|
|
64
|
+
};
|
|
44
65
|
});
|
package/src/styles/base/init.css
CHANGED
|
@@ -200,7 +200,8 @@ ul {
|
|
|
200
200
|
--muted-text: #bbb;
|
|
201
201
|
|
|
202
202
|
/* 字体族栈 */
|
|
203
|
-
|
|
203
|
+
--font-body: "Noto Sans SC", "Noto Sans CJK SC", system-ui, sans-serif;
|
|
204
|
+
/* --font-code 由 Astro Fonts API 提供;构建时将页面 CJK 子集追加为其回退。 */
|
|
204
205
|
|
|
205
206
|
/* 字体大小系统 */
|
|
206
207
|
--font-size-subtitle: 1.35rem;
|
package/src/styles/base/math.css
CHANGED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--waline-accent-rgb: 100, 255, 218;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/* 评论区容器与博客风格一致 */
|
|
6
|
+
.waline-comment-container {
|
|
7
|
+
margin-top: 3rem;
|
|
8
|
+
margin-bottom: 1rem;
|
|
9
|
+
padding: 2rem;
|
|
10
|
+
border-radius: 12px;
|
|
11
|
+
animation: fadeInUpComment 0.8s ease-in-out forwards;
|
|
12
|
+
opacity: 0;
|
|
13
|
+
animation-delay: 0.3s;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* 评论区标题样式与博客风格一致 */
|
|
17
|
+
.waline-comment-title {
|
|
18
|
+
margin: 0 0 1.5rem 0;
|
|
19
|
+
font-size: 2rem;
|
|
20
|
+
text-align: center;
|
|
21
|
+
background: linear-gradient(90deg, var(--white-90p), rgba(var(--waline-accent-rgb), 0.9));
|
|
22
|
+
-webkit-background-clip: text;
|
|
23
|
+
-webkit-text-fill-color: transparent;
|
|
24
|
+
background-clip: text;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Waline根元素自定义 */
|
|
28
|
+
.wl-panel {
|
|
29
|
+
background-color: rgba(17, 17, 17, 0.3);
|
|
30
|
+
border-radius: 12px;
|
|
31
|
+
padding: 1rem;
|
|
32
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
33
|
+
margin-bottom: 1.5rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* 修改输入框样式 */
|
|
37
|
+
.wl-editor,
|
|
38
|
+
.wl-input {
|
|
39
|
+
background-color: rgba(17, 17, 17, 0.5) !important;
|
|
40
|
+
color: var(--white-90p) !important;
|
|
41
|
+
border: 1px solid rgba(100, 100, 100, 0.3) !important;
|
|
42
|
+
border-radius: 8px;
|
|
43
|
+
transition: all 0.3s ease;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* 输入框焦点状态 */
|
|
47
|
+
.wl-editor:focus,
|
|
48
|
+
.wl-input:focus {
|
|
49
|
+
border-color: rgba(var(--waline-accent-rgb), 0.5) !important;
|
|
50
|
+
box-shadow: 0 0 5px rgba(var(--waline-accent-rgb), 0.3) !important;
|
|
51
|
+
outline: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* 修复登录后头像向下偏移 — 全局 a::after 下划线动画撑高了 wl-login-nick */
|
|
55
|
+
.wl-login-nick {
|
|
56
|
+
display: block !important;
|
|
57
|
+
height: var(--waline-avatar-size, 52px);
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
}
|
|
60
|
+
.wl-login-nick::after {
|
|
61
|
+
display: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* 修复 markdown 全局 img margin 误伤 Waline 头像 */
|
|
65
|
+
.waline-comment-container img {
|
|
66
|
+
margin: 0 !important;
|
|
67
|
+
}
|
|
68
|
+
.wl-btn {
|
|
69
|
+
background: rgba(var(--waline-accent-rgb), 0.6) !important;
|
|
70
|
+
color: var(--white-full) !important;
|
|
71
|
+
border: none !important;
|
|
72
|
+
border-radius: 6px;
|
|
73
|
+
transition: all 0.2s ease;
|
|
74
|
+
font-weight: 500;
|
|
75
|
+
padding: 8px 15px;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.wl-btn:hover {
|
|
80
|
+
background: rgba(var(--waline-accent-rgb), 0.7) !important;
|
|
81
|
+
transform: none;
|
|
82
|
+
box-shadow: none !important;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.wl-btn.primary {
|
|
86
|
+
background: rgba(var(--waline-accent-rgb), 0.6) !important;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.wl-btn.primary:hover {
|
|
90
|
+
background: rgba(var(--waline-accent-rgb), 0.7) !important;
|
|
91
|
+
transform: none;
|
|
92
|
+
box-shadow: none !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* 标签文字颜色 */
|
|
96
|
+
.wl-header label {
|
|
97
|
+
color: var(--white-85p);
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* 评论卡片样式 */
|
|
102
|
+
.wl-card {
|
|
103
|
+
background-color: rgba(17, 17, 17, 0.3) !important;
|
|
104
|
+
border-radius: 12px;
|
|
105
|
+
padding: 1.2rem;
|
|
106
|
+
margin-top: 1.2rem;
|
|
107
|
+
border: 1px solid rgba(70, 70, 70, 0.2) !important;
|
|
108
|
+
transition: all 0.3s ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.wl-card:hover {
|
|
112
|
+
transform: translateY(-3px);
|
|
113
|
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
114
|
+
border-color: rgba(var(--waline-accent-rgb), 0.3) !important;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* 修改用户名样式 */
|
|
118
|
+
.wl-nick {
|
|
119
|
+
color: rgba(var(--waline-accent-rgb), 0.95) !important;
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
transition: all 0.2s ease;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.wl-nick:hover {
|
|
125
|
+
color: rgba(var(--waline-accent-rgb), 1) !important;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* 评论内容样式 */
|
|
129
|
+
.wl-content {
|
|
130
|
+
color: var(--white-90p);
|
|
131
|
+
line-height: 1.7;
|
|
132
|
+
margin: 0.8rem 0;
|
|
133
|
+
word-break: break-word;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* 回复按钮样式 */
|
|
137
|
+
.wl-reply {
|
|
138
|
+
opacity: 0.8;
|
|
139
|
+
transition: all 0.2s ease;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.wl-reply:hover {
|
|
143
|
+
opacity: 1;
|
|
144
|
+
text-decoration: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* 表情面板样式 */
|
|
148
|
+
.wl-emoji-popup,
|
|
149
|
+
.wl-gif-popup {
|
|
150
|
+
background-color: rgba(25, 25, 25, 0.95) !important;
|
|
151
|
+
border: 1px solid rgba(70, 70, 70, 0.3) !important;
|
|
152
|
+
border-radius: 10px;
|
|
153
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
154
|
+
backdrop-filter: blur(10px);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* 表情按钮样式 */
|
|
158
|
+
.wl-emoji-popup button {
|
|
159
|
+
border-radius: 6px;
|
|
160
|
+
transition: all 0.2s ease;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.wl-emoji-popup button:hover {
|
|
164
|
+
background-color: rgba(var(--waline-accent-rgb), 0.2);
|
|
165
|
+
transform: scale(1.05);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* 文章评价反应区域 */
|
|
169
|
+
.wl-reaction {
|
|
170
|
+
background-color: rgba(17, 17, 17, 0.3);
|
|
171
|
+
border-radius: 12px;
|
|
172
|
+
padding: 1rem;
|
|
173
|
+
margin-bottom: 1.5rem;
|
|
174
|
+
border: 1px solid rgba(70, 70, 70, 0.2);
|
|
175
|
+
text-align: center;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.wl-reaction-title {
|
|
179
|
+
font-size: 1.1rem;
|
|
180
|
+
margin-bottom: 1rem;
|
|
181
|
+
color: var(--white-90p);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.wl-reaction-list {
|
|
185
|
+
display: flex;
|
|
186
|
+
justify-content: center;
|
|
187
|
+
flex-wrap: wrap;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.wl-reaction-item {
|
|
191
|
+
background-color: rgba(30, 30, 30, 0.5) !important;
|
|
192
|
+
border-radius: 8px;
|
|
193
|
+
margin: 5px;
|
|
194
|
+
transition: all 0.3s ease;
|
|
195
|
+
cursor: pointer;
|
|
196
|
+
border: 1px solid rgba(70, 70, 70, 0.2) !important;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.wl-reaction-item:hover {
|
|
200
|
+
transform: translateY(-3px);
|
|
201
|
+
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
|
202
|
+
background-color: rgba(40, 40, 40, 0.8) !important;
|
|
203
|
+
border-color: rgba(var(--waline-accent-rgb), 0.3) !important;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.wl-reaction-img {
|
|
207
|
+
padding: 6px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.wl-reaction-img img {
|
|
211
|
+
transition: transform 0.3s ease;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.wl-reaction-item:hover .wl-reaction-img img {
|
|
215
|
+
transform: scale(1.15);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.wl-reaction-votes {
|
|
219
|
+
font-size: 0.8rem;
|
|
220
|
+
padding: 5px 0;
|
|
221
|
+
background-color: rgba(var(--waline-accent-rgb), 0.7);
|
|
222
|
+
color: var(--white-full);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* 评论计数和排序 */
|
|
226
|
+
.wl-meta-head {
|
|
227
|
+
margin-bottom: 1.2rem;
|
|
228
|
+
border-bottom: 1px solid rgba(70, 70, 70, 0.3);
|
|
229
|
+
padding-bottom: 0.8rem;
|
|
230
|
+
display: flex;
|
|
231
|
+
justify-content: space-between;
|
|
232
|
+
align-items: center;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.wl-count {
|
|
236
|
+
font-size: 1.1rem;
|
|
237
|
+
font-weight: 500;
|
|
238
|
+
color: var(--white-90p);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.wl-sort {
|
|
242
|
+
display: flex;
|
|
243
|
+
gap: 1rem;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.wl-sort li {
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
transition: all 0.2s ease;
|
|
249
|
+
color: var(--white-70p);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.wl-sort li:hover {
|
|
253
|
+
color: var(--white-90p);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.wl-sort li.active {
|
|
257
|
+
color: rgba(var(--waline-accent-rgb), 1);
|
|
258
|
+
font-weight: 500;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* 页码导航 */
|
|
262
|
+
.wl-page {
|
|
263
|
+
margin-top: 1.2rem;
|
|
264
|
+
text-align: center;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.wl-page button {
|
|
268
|
+
background: rgba(30, 30, 30, 0.5) !important;
|
|
269
|
+
color: var(--white-80p) !important;
|
|
270
|
+
border: 1px solid rgba(70, 70, 70, 0.3) !important;
|
|
271
|
+
border-radius: 6px;
|
|
272
|
+
margin: 0 5px;
|
|
273
|
+
padding: 5px 10px;
|
|
274
|
+
transition: all 0.2s ease;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.wl-page button:hover:not(:disabled) {
|
|
278
|
+
background: rgba(var(--waline-accent-rgb), 0.7) !important;
|
|
279
|
+
color: var(--white-full) !important;
|
|
280
|
+
transform: translateY(-2px);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.wl-page button.active {
|
|
284
|
+
background: rgba(var(--waline-accent-rgb), 0.8) !important;
|
|
285
|
+
color: var(--white-full) !important;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Waline底部版权信息 */
|
|
289
|
+
.wl-power {
|
|
290
|
+
opacity: 0.6;
|
|
291
|
+
font-size: 0.85rem;
|
|
292
|
+
margin-top: 2rem;
|
|
293
|
+
text-align: center;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.wl-power a {
|
|
297
|
+
color: rgba(var(--waline-accent-rgb), 0.9);
|
|
298
|
+
text-decoration: none;
|
|
299
|
+
transition: all 0.2s ease;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.wl-power a:hover {
|
|
303
|
+
color: rgba(var(--waline-accent-rgb), 1);
|
|
304
|
+
text-decoration: underline;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* 评论为空状态 */
|
|
308
|
+
.wl-empty {
|
|
309
|
+
text-align: center;
|
|
310
|
+
padding: 2rem 0;
|
|
311
|
+
color: var(--white-60p);
|
|
312
|
+
font-size: 1.1rem;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* 支持文字数量统计 */
|
|
316
|
+
.wl-text-number {
|
|
317
|
+
font-size: 0.9rem;
|
|
318
|
+
color: var(--white-60p);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* 添加动画效果 */
|
|
322
|
+
@keyframes fadeInUpComment {
|
|
323
|
+
from {
|
|
324
|
+
opacity: 0;
|
|
325
|
+
transform: translateY(30px);
|
|
326
|
+
}
|
|
327
|
+
to {
|
|
328
|
+
opacity: 1;
|
|
329
|
+
transform: translateY(0);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* 降低选中文本高亮程度,移除外边框 */
|
|
334
|
+
.waline-comment-container *::selection {
|
|
335
|
+
background: rgba(var(--waline-accent-rgb), 0.25);
|
|
336
|
+
color: inherit;
|
|
337
|
+
border: none;
|
|
338
|
+
outline: none;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.waline-comment-container *::-moz-selection {
|
|
342
|
+
background: rgba(var(--waline-accent-rgb), 0.25);
|
|
343
|
+
color: inherit;
|
|
344
|
+
border: none;
|
|
345
|
+
outline: none;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* 响应式调整 */
|
|
349
|
+
@media (max-width: 768px) {
|
|
350
|
+
.waline-comment-container {
|
|
351
|
+
padding: 1.5rem;
|
|
352
|
+
margin-top: 2rem;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.waline-comment-title {
|
|
356
|
+
font-size: 1.8rem;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.wl-panel {
|
|
360
|
+
padding: 0.8rem;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.wl-header {
|
|
364
|
+
flex-direction: column;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.wl-header-item {
|
|
368
|
+
width: 100%;
|
|
369
|
+
margin-bottom: 0.5rem;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* 平板设备上的嵌套评论处理 */
|
|
373
|
+
.wl-card {
|
|
374
|
+
padding: 0.8rem;
|
|
375
|
+
margin-left: 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.wl-children {
|
|
379
|
+
margin-left: 0;
|
|
380
|
+
padding-left: 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.wl-children .wl-card {
|
|
384
|
+
margin-left: 0;
|
|
385
|
+
border-left: 3px solid rgba(var(--waline-accent-rgb), 0.4);
|
|
386
|
+
padding-left: 0.8rem;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@media (max-width: 480px) {
|
|
391
|
+
.waline-comment-container {
|
|
392
|
+
padding: 1rem;
|
|
393
|
+
margin-top: 1.5rem;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.waline-comment-title {
|
|
397
|
+
font-size: 1.6rem;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.wl-panel {
|
|
401
|
+
padding: 0.8rem;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.wl-reaction-list {
|
|
405
|
+
gap: 5px;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.wl-card {
|
|
409
|
+
padding: 0.75rem;
|
|
410
|
+
margin-left: 0;
|
|
411
|
+
width: 100%;
|
|
412
|
+
box-sizing: border-box;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* 小屏幕上的嵌套评论处理 */
|
|
416
|
+
.wl-children {
|
|
417
|
+
margin-left: 0;
|
|
418
|
+
padding-left: 0;
|
|
419
|
+
width: 100%;
|
|
420
|
+
box-sizing: border-box;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.wl-children .wl-card {
|
|
424
|
+
margin-left: 0;
|
|
425
|
+
border-left: 3px solid rgba(var(--waline-accent-rgb), 0.4);
|
|
426
|
+
padding-left: 0.6rem;
|
|
427
|
+
width: 100%;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.wl-children .wl-children .wl-card {
|
|
431
|
+
border-left: 3px solid rgba(var(--waline-accent-rgb), 0.3);
|
|
432
|
+
padding-left: 0.6rem;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.wl-meta-head {
|
|
436
|
+
flex-direction: column;
|
|
437
|
+
gap: 0.5rem;
|
|
438
|
+
align-items: flex-start;
|
|
439
|
+
}
|
|
440
|
+
}
|
package/src/utils/i18n.ts
CHANGED
|
@@ -12,7 +12,9 @@ export interface Translator {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function createTranslator(lang: string): Translator {
|
|
15
|
-
const
|
|
15
|
+
const normalizedLang = lang.toLowerCase();
|
|
16
|
+
const language = normalizedLang.startsWith("en") ? "en" : "zh-CN";
|
|
17
|
+
const dict = dicts[language] ?? dicts["zh-CN"] ?? en;
|
|
16
18
|
|
|
17
19
|
function t(key: string, params?: Record<string, string | number>): string {
|
|
18
20
|
const keys = key.split(".");
|
|
@@ -21,12 +23,12 @@ export function createTranslator(lang: string): Translator {
|
|
|
21
23
|
if (text && typeof text === "object") {
|
|
22
24
|
text = (text as Record<string, unknown>)[k];
|
|
23
25
|
} else {
|
|
24
|
-
return key;
|
|
26
|
+
return lookup(en, key) ?? key;
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
|
-
if (typeof text !== "string")
|
|
29
|
+
if (typeof text !== "string" || !text.trim()) text = lookup(en, key) ?? key;
|
|
28
30
|
|
|
29
|
-
let result = text;
|
|
31
|
+
let result = text as string;
|
|
30
32
|
if (params) {
|
|
31
33
|
for (const [k, v] of Object.entries(params)) {
|
|
32
34
|
result = result.replace(`{${k}}`, String(v));
|
|
@@ -35,7 +37,16 @@ export function createTranslator(lang: string): Translator {
|
|
|
35
37
|
return result;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
return { t, lang };
|
|
40
|
+
return { t, lang: language };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function lookup(dictionary: Record<string, unknown>, key: string): string | undefined {
|
|
44
|
+
let value: unknown = dictionary;
|
|
45
|
+
for (const part of key.split(".")) {
|
|
46
|
+
if (!value || typeof value !== "object") return;
|
|
47
|
+
value = (value as Record<string, unknown>)[part];
|
|
48
|
+
}
|
|
49
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
/**
|