@xingwangzhe/stalux 1.21.5 → 1.21.7
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/package.json +1 -1
- package/src/components/stalux/common/search.astro +0 -5
- package/src/components/stalux/footer/FooterBadges.astro +3 -3
- package/src/components/stalux/footer/FooterCopyright.astro +1 -0
- package/src/components/stalux/footer/FooterThemeInfo.astro +1 -0
- package/src/components/stalux/layout/navs.astro +41 -14
- package/src/components/stalux/links/linkCard.astro +1 -1
- package/src/components/stalux/posts/postCard.astro +30 -10
- package/src/components/stalux/posts/postContent.astro +26 -19
- package/src/components/stalux/posts/postList.astro +2 -2
- package/src/components/stalux/words/wordList.astro +6 -1
- package/src/pages/404.astro +2 -2
- package/src/plugins/feature-flags.ts +8 -2
- package/src/styles/components/links/socialLinks.module.css +1 -0
- package/src/styles/components/posts/postCard.module.css +22 -0
- package/src/utils/view-transitions.ts +3 -3
package/package.json
CHANGED
|
@@ -93,7 +93,7 @@ function badgeImg(opts: Record<string, any>) {
|
|
|
93
93
|
align-items: center;
|
|
94
94
|
gap: 0.4rem;
|
|
95
95
|
padding: 0.2rem 0;
|
|
96
|
-
color: var(--white-
|
|
96
|
+
color: var(--white-70p);
|
|
97
97
|
font-size: 0.85rem;
|
|
98
98
|
user-select: none;
|
|
99
99
|
list-style: none;
|
|
@@ -116,12 +116,12 @@ function badgeImg(opts: Record<string, any>) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
.badge-group-title {
|
|
119
|
-
color: var(--white-
|
|
119
|
+
color: var(--white-80p);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
.badge-group-hint {
|
|
123
123
|
font-size: 0.7rem;
|
|
124
|
-
color: var(--white-
|
|
124
|
+
color: var(--white-70p);
|
|
125
125
|
}
|
|
126
126
|
.badge-group[open] .badge-group-hint[data-hint="collapsed"],
|
|
127
127
|
.badge-group:not([open]) .badge-group-hint[data-hint="expanded"] {
|
|
@@ -62,7 +62,7 @@ const navs = navsConfig?.items ?? [];
|
|
|
62
62
|
}
|
|
63
63
|
<li class={styles.navItem}>
|
|
64
64
|
<a
|
|
65
|
-
href="
|
|
65
|
+
href="#search"
|
|
66
66
|
class={styles.navLink}
|
|
67
67
|
data-ref="stalux-search-btn"
|
|
68
68
|
aria-label={t("search.title")}
|
|
@@ -87,14 +87,23 @@ const navs = navsConfig?.items ?? [];
|
|
|
87
87
|
</nav>
|
|
88
88
|
<Search />
|
|
89
89
|
<script>
|
|
90
|
+
const navWindow = window as Window & {
|
|
91
|
+
__STALUX_NAV_INITIALIZED__?: boolean;
|
|
92
|
+
};
|
|
93
|
+
|
|
90
94
|
document.addEventListener("astro:page-load", () => {
|
|
95
|
+
if (navWindow.__STALUX_NAV_INITIALIZED__) return;
|
|
96
|
+
navWindow.__STALUX_NAV_INITIALIZED__ = true;
|
|
97
|
+
|
|
91
98
|
const searchBtn = document.querySelector(
|
|
92
99
|
'[data-ref="stalux-search-btn"]',
|
|
93
100
|
);
|
|
94
|
-
searchBtn?.addEventListener("click", (e) => {
|
|
101
|
+
searchBtn?.addEventListener("click", async (e) => {
|
|
95
102
|
e.preventDefault();
|
|
96
103
|
closeNav();
|
|
97
|
-
|
|
104
|
+
await import("@pagefind/component-ui");
|
|
105
|
+
await import("@pagefind/component-ui/css");
|
|
106
|
+
document.getElementById("search")?.open();
|
|
98
107
|
});
|
|
99
108
|
|
|
100
109
|
// 汉堡菜单交互
|
|
@@ -139,7 +148,20 @@ const navs = navsConfig?.items ?? [];
|
|
|
139
148
|
const btnRight = document.querySelector('[data-ref="stalux-nav-scroll-right"]');
|
|
140
149
|
if (!navList || !btnLeft || !btnRight) return;
|
|
141
150
|
|
|
151
|
+
let frameId = 0;
|
|
152
|
+
let lastScale = "1";
|
|
153
|
+
|
|
154
|
+
function updateScrollButtons() {
|
|
155
|
+
const hasOverflow = navList.scrollWidth > navList.clientWidth;
|
|
156
|
+
const atStart = navList.scrollLeft <= 1;
|
|
157
|
+
const atEnd = navList.scrollLeft + navList.clientWidth >= navList.scrollWidth - 1;
|
|
158
|
+
|
|
159
|
+
btnLeft.toggleAttribute("data-visible", hasOverflow && !atStart);
|
|
160
|
+
btnRight.toggleAttribute("data-visible", hasOverflow && !atEnd);
|
|
161
|
+
}
|
|
162
|
+
|
|
142
163
|
function updateButtons() {
|
|
164
|
+
frameId = 0;
|
|
143
165
|
const navWidth = navList.clientWidth;
|
|
144
166
|
const itemCount = navList.children.length;
|
|
145
167
|
const baseFontSize = Number.parseFloat(getComputedStyle(navList).fontSize) || 16;
|
|
@@ -150,10 +172,17 @@ const navs = navsConfig?.items ?? [];
|
|
|
150
172
|
0,
|
|
151
173
|
) + gap * Math.max(0, itemCount - 1);
|
|
152
174
|
const scale = estimatedWidth > navWidth ? Math.max(minFontSize / baseFontSize, navWidth / estimatedWidth) : 1;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
const nextScale = String(scale);
|
|
176
|
+
if (nextScale !== lastScale) {
|
|
177
|
+
navList.style.setProperty("--nav-scale", nextScale);
|
|
178
|
+
lastScale = nextScale;
|
|
179
|
+
}
|
|
180
|
+
updateScrollButtons();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function scheduleUpdate() {
|
|
184
|
+
if (frameId) return;
|
|
185
|
+
frameId = requestAnimationFrame(updateButtons);
|
|
157
186
|
}
|
|
158
187
|
|
|
159
188
|
btnLeft.addEventListener("click", () => {
|
|
@@ -163,18 +192,16 @@ const navs = navsConfig?.items ?? [];
|
|
|
163
192
|
btnRight.addEventListener("click", () => {
|
|
164
193
|
navList.scrollBy({ left: 300, behavior: "smooth" });
|
|
165
194
|
});
|
|
166
|
-
|
|
167
|
-
// 监听滚动事件更新按钮状态
|
|
168
|
-
navList.addEventListener("scroll", updateButtons);
|
|
195
|
+
navList.addEventListener("scroll", scheduleUpdate, { passive: true });
|
|
169
196
|
|
|
170
197
|
// 用 ResizeObserver 监听容器尺寸变化(窗口缩窄/变宽时重新判断)
|
|
171
|
-
const ro = new ResizeObserver(
|
|
198
|
+
const ro = new ResizeObserver(scheduleUpdate);
|
|
172
199
|
ro.observe(navList);
|
|
173
200
|
if (navList.parentElement) ro.observe(navList.parentElement);
|
|
174
|
-
window.addEventListener("resize",
|
|
175
|
-
window.addEventListener("load",
|
|
201
|
+
window.addEventListener("resize", scheduleUpdate, { passive: true });
|
|
202
|
+
window.addEventListener("load", scheduleUpdate, { once: true });
|
|
176
203
|
|
|
177
204
|
// 初始检测
|
|
178
|
-
|
|
205
|
+
scheduleUpdate();
|
|
179
206
|
}
|
|
180
207
|
</script>
|
|
@@ -8,9 +8,10 @@ import { getSiteData, getAuthorData, getHeadData, getNavsData, getLinksData, get
|
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
post: CollectionEntry<"posts">;
|
|
11
|
+
priority?: boolean;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const post = Astro.props
|
|
14
|
+
const { post, priority = false } = Astro.props;
|
|
14
15
|
const configEntries = await getCollection("config");
|
|
15
16
|
const siteConfig = getSiteData(configEntries);
|
|
16
17
|
const timezone = siteConfig.timezone;
|
|
@@ -19,16 +20,35 @@ const timezone = siteConfig.timezone;
|
|
|
19
20
|
const description = post.data.desc || "";
|
|
20
21
|
---
|
|
21
22
|
|
|
22
|
-
<
|
|
23
|
+
<article class={style.post}>
|
|
23
24
|
<div class={style.postContainer}>
|
|
24
25
|
{
|
|
25
26
|
post.data.cover && (
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
<a
|
|
28
|
+
href={`/posts/${post.data.abbrlink}/`}
|
|
29
|
+
class="a-none"
|
|
30
|
+
data-astro-prefetch="viewport"
|
|
31
|
+
aria-label={post.data.title}
|
|
32
|
+
>
|
|
33
|
+
<div class={style.postCover}>
|
|
34
|
+
<img
|
|
35
|
+
src={post.data.cover}
|
|
36
|
+
alt={post.data.title}
|
|
37
|
+
loading={priority ? "eager" : "lazy"}
|
|
38
|
+
decoding="async"
|
|
39
|
+
fetchpriority={priority ? "high" : "auto"}
|
|
40
|
+
width="640"
|
|
41
|
+
height="360"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
</a>
|
|
29
45
|
)
|
|
30
46
|
}
|
|
31
|
-
<h2 class={style.postTitle}>
|
|
47
|
+
<h2 class={style.postTitle}>
|
|
48
|
+
<a href={`/posts/${post.data.abbrlink}/`} class="a-none" data-astro-prefetch="viewport">
|
|
49
|
+
{post.data.title}
|
|
50
|
+
</a>
|
|
51
|
+
</h2>
|
|
32
52
|
<div class={style.time}>
|
|
33
53
|
{
|
|
34
54
|
post.data.date && (
|
|
@@ -57,7 +77,7 @@ const description = post.data.desc || "";
|
|
|
57
77
|
{
|
|
58
78
|
post.data.tags?.map((tag) => (
|
|
59
79
|
<a class="a-none" href={`/tags/${tag}`} data-astro-prefetch>
|
|
60
|
-
<
|
|
80
|
+
<span class={style.postTag}>{tag}</span>
|
|
61
81
|
</a>
|
|
62
82
|
))
|
|
63
83
|
}
|
|
@@ -66,18 +86,18 @@ const description = post.data.desc || "";
|
|
|
66
86
|
{
|
|
67
87
|
post.data.categories?.map((category) => (
|
|
68
88
|
<a class="a-none" href={`/categories/${category}`} data-astro-prefetch>
|
|
69
|
-
<
|
|
89
|
+
<span class={style.postCategory}>{category}</span>
|
|
70
90
|
</a>
|
|
71
91
|
))
|
|
72
92
|
}
|
|
73
93
|
</div>
|
|
74
94
|
</div>
|
|
75
95
|
<a
|
|
76
|
-
href={`/posts/${post.data.abbrlink}
|
|
96
|
+
href={`/posts/${post.data.abbrlink}/`}
|
|
77
97
|
class={style.readMore + " a-none"}
|
|
78
98
|
data-astro-prefetch
|
|
79
99
|
>
|
|
80
100
|
Read More
|
|
81
101
|
</a>
|
|
82
102
|
</div>
|
|
83
|
-
</
|
|
103
|
+
</article>
|
|
@@ -117,24 +117,30 @@ const mdSourceUrl = exportMd
|
|
|
117
117
|
const container = document.querySelector("[data-pagefind-body]");
|
|
118
118
|
if (!container) return;
|
|
119
119
|
|
|
120
|
-
const links = container.querySelectorAll(GALLERY_SELECTOR);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
120
|
+
const links = [...container.querySelectorAll(GALLERY_SELECTOR)];
|
|
121
|
+
for (const link of links) {
|
|
122
|
+
const img = link.querySelector("img");
|
|
123
|
+
if (!img) continue;
|
|
124
|
+
|
|
125
|
+
const width = img.naturalWidth;
|
|
126
|
+
const height = img.naturalHeight;
|
|
127
|
+
if (width && height) {
|
|
128
|
+
link.dataset.pswpWidth = String(width);
|
|
129
|
+
link.dataset.pswpHeight = String(height);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const tempImg = new Image();
|
|
134
|
+
tempImg.src = img.currentSrc || img.src;
|
|
135
|
+
try {
|
|
136
|
+
await tempImg.decode();
|
|
137
|
+
} catch {
|
|
138
|
+
/* 解码失败时跳过尺寸写入 */
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
link.dataset.pswpWidth = String(tempImg.naturalWidth);
|
|
142
|
+
link.dataset.pswpHeight = String(tempImg.naturalHeight);
|
|
143
|
+
}
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
let lightbox = null;
|
|
@@ -150,7 +156,8 @@ const mdSourceUrl = exportMd
|
|
|
150
156
|
lightbox = null;
|
|
151
157
|
}
|
|
152
158
|
|
|
153
|
-
|
|
159
|
+
// 不阻塞文章首屏;已加载图片会立即写入尺寸,其余图片在后台补齐。
|
|
160
|
+
void enrichPhotoSwipeData();
|
|
154
161
|
|
|
155
162
|
lightbox = new PhotoSwipeLightbox({
|
|
156
163
|
gallery: "[data-pagefind-body]",
|
|
@@ -15,9 +15,9 @@ const posts = props.posts ?? (await getCollection("posts", ({ data }) => !data.d
|
|
|
15
15
|
<div data-stalux="postlist" class={styles["container"]}>
|
|
16
16
|
<ul class={styles["post-list"]}>
|
|
17
17
|
{
|
|
18
|
-
posts.map((post) => (
|
|
18
|
+
posts.map((post, index) => (
|
|
19
19
|
<li>
|
|
20
|
-
<PostCard post={post} />
|
|
20
|
+
<PostCard post={post} priority={index === 0} />
|
|
21
21
|
</li>
|
|
22
22
|
))
|
|
23
23
|
}
|
|
@@ -25,7 +25,12 @@ const sorted = [...entries].sort((a, b) => {
|
|
|
25
25
|
return (
|
|
26
26
|
<li class={styles.item}>
|
|
27
27
|
{hash ? (
|
|
28
|
-
<a
|
|
28
|
+
<a
|
|
29
|
+
href={hash}
|
|
30
|
+
class="a-none"
|
|
31
|
+
data-word-hash={hash}
|
|
32
|
+
aria-label={`Jump to ${entry.data.abbrlink}`}
|
|
33
|
+
>
|
|
29
34
|
<WordCard entry={entry} timezone={timezone} />
|
|
30
35
|
</a>
|
|
31
36
|
) : (
|
package/src/pages/404.astro
CHANGED
|
@@ -27,7 +27,7 @@ const { t } = createTranslator(siteConfig.lang || "zh-CN");
|
|
|
27
27
|
</div>
|
|
28
28
|
|
|
29
29
|
<script define:vars={{ countdownText: t("page404.countdown", { n: "{n}" }) }} is:inline>
|
|
30
|
-
let timer
|
|
30
|
+
let timer;
|
|
31
31
|
|
|
32
32
|
function startCountdown() {
|
|
33
33
|
if (timer !== undefined) window.clearInterval(timer);
|
|
@@ -40,7 +40,7 @@ const { t } = createTranslator(siteConfig.lang || "zh-CN");
|
|
|
40
40
|
if (seconds <= 0) {
|
|
41
41
|
if (timer !== undefined) window.clearInterval(timer);
|
|
42
42
|
timer = undefined;
|
|
43
|
-
const home = document.querySelector
|
|
43
|
+
const home = document.querySelector(".home-btn");
|
|
44
44
|
home?.click();
|
|
45
45
|
} else {
|
|
46
46
|
el.textContent = countdownText.replace("{n}", String(seconds));
|
|
@@ -56,13 +56,19 @@ export const featureFlagsHast = defineHastPlugin({
|
|
|
56
56
|
filter: ["img"],
|
|
57
57
|
visit(node, ctx) {
|
|
58
58
|
ctx.data.astro.frontmatter.hasImage = true;
|
|
59
|
-
//
|
|
59
|
+
// 第一张正文图片可能成为 LCP,优先加载;其余图片延迟加载。
|
|
60
|
+
const data = ctx.data as Record<string, unknown>;
|
|
61
|
+
const firstImage = !data.staluxFirstImageSeen;
|
|
62
|
+
data.staluxFirstImageSeen = true;
|
|
60
63
|
if (!node.properties?.loading) {
|
|
61
|
-
ctx.setProperty(node, "loading", "lazy");
|
|
64
|
+
ctx.setProperty(node, "loading", firstImage ? "eager" : "lazy");
|
|
62
65
|
}
|
|
63
66
|
if (!node.properties?.decoding) {
|
|
64
67
|
ctx.setProperty(node, "decoding", "async");
|
|
65
68
|
}
|
|
69
|
+
if (firstImage && !node.properties?.fetchpriority) {
|
|
70
|
+
ctx.setProperty(node, "fetchpriority", "high");
|
|
71
|
+
}
|
|
66
72
|
},
|
|
67
73
|
},
|
|
68
74
|
});
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
.post {
|
|
2
|
+
display: block;
|
|
3
|
+
height: 100%;
|
|
4
|
+
color: inherit;
|
|
5
|
+
text-decoration: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.postTitle {
|
|
9
|
+
margin: 0;
|
|
10
|
+
line-height: 1.35;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.postTitle a {
|
|
14
|
+
color: inherit;
|
|
15
|
+
text-decoration: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.postDescription {
|
|
19
|
+
color: var(--white-70p);
|
|
20
|
+
line-height: 1.6;
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
.postContainer {
|
|
2
24
|
background-color: var(--card-bg);
|
|
3
25
|
border-radius: var(--radius-lg);
|
|
@@ -37,7 +37,7 @@ export type RouteAnimation = {
|
|
|
37
37
|
const easing = "cubic-bezier(0.25, 0.9, 0.2, 1)";
|
|
38
38
|
const exitAnimation: TransitionAnimation = {
|
|
39
39
|
name: "stalux-page-fade-out",
|
|
40
|
-
duration: "0.
|
|
40
|
+
duration: "0.16s",
|
|
41
41
|
easing,
|
|
42
42
|
fillMode: "both",
|
|
43
43
|
};
|
|
@@ -48,7 +48,7 @@ function createRouteAnimation(name: string, backName = `${name}-back`): RouteAni
|
|
|
48
48
|
old: exitAnimation,
|
|
49
49
|
new: {
|
|
50
50
|
name: `stalux-page-${name}-in`,
|
|
51
|
-
duration: "0.
|
|
51
|
+
duration: "0.2s",
|
|
52
52
|
easing,
|
|
53
53
|
fillMode: "both",
|
|
54
54
|
},
|
|
@@ -57,7 +57,7 @@ function createRouteAnimation(name: string, backName = `${name}-back`): RouteAni
|
|
|
57
57
|
old: exitAnimation,
|
|
58
58
|
new: {
|
|
59
59
|
name: `stalux-page-${backName}-in`,
|
|
60
|
-
duration: "0.
|
|
60
|
+
duration: "0.2s",
|
|
61
61
|
easing,
|
|
62
62
|
fillMode: "both",
|
|
63
63
|
},
|