@xingwangzhe/stalux 1.21.5 → 1.21.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingwangzhe/stalux",
3
- "version": "1.21.5",
3
+ "version": "1.21.6",
4
4
  "description": "A powerful, modern Astro blog theme — use as template or install as plugin",
5
5
  "keywords": [
6
6
  "astro",
@@ -7,8 +7,3 @@
7
7
  <div data-pf-theme="dark">
8
8
  <pagefind-modal id="search"></pagefind-modal>
9
9
  </div>
10
-
11
- <script>
12
- import "@pagefind/component-ui";
13
- import "@pagefind/component-ui/css";
14
- </script>
@@ -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-40p);
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-60p);
119
+ color: var(--white-80p);
120
120
  }
121
121
 
122
122
  .badge-group-hint {
123
123
  font-size: 0.7rem;
124
- color: var(--white-25p);
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"] {
@@ -27,6 +27,7 @@ const copyrightYear =
27
27
 
28
28
  <style>
29
29
  .copyright {
30
+ color: var(--white-70p);
30
31
  font-weight: 500;
31
32
  display: flex;
32
33
  gap: 0.3em;
@@ -42,6 +42,7 @@ const showThemeInfoSection = !!(showPoweredBy || showThemeInfo);
42
42
 
43
43
  <style>
44
44
  .theme-info {
45
+ color: var(--white-70p);
45
46
  font-size: 0.85rem;
46
47
  display: flex;
47
48
  justify-content: center;
@@ -62,7 +62,7 @@ const navs = navsConfig?.items ?? [];
62
62
  }
63
63
  <li class={styles.navItem}>
64
64
  <a
65
- href="javascript:void(0)"
65
+ href="#search"
66
66
  class={styles.navLink}
67
67
  data-ref="stalux-search-btn"
68
68
  aria-label={t("search.title")}
@@ -91,10 +91,12 @@ const navs = navsConfig?.items ?? [];
91
91
  const searchBtn = document.querySelector(
92
92
  '[data-ref="stalux-search-btn"]',
93
93
  );
94
- searchBtn?.addEventListener("click", (e) => {
94
+ searchBtn?.addEventListener("click", async (e) => {
95
95
  e.preventDefault();
96
96
  closeNav();
97
- document.getElementById('search')?.open();
97
+ await import("@pagefind/component-ui");
98
+ await import("@pagefind/component-ui/css");
99
+ document.getElementById("search")?.open();
98
100
  });
99
101
 
100
102
  // 汉堡菜单交互
@@ -44,7 +44,7 @@ const icon = link.icon ?? link.avatar ?? "/favicon.ico";
44
44
  />
45
45
  </div>
46
46
  <div class={styles.content}>
47
- <h3 class={styles.title}>{title}</h3>
47
+ <h2 class={styles.title}>{title}</h2>
48
48
  {desc && <p class={styles.description}>{desc}</p>}
49
49
  </div>
50
50
  </a>
@@ -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.post;
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
- <a href={`/posts/${post.data.abbrlink}/`} class={style.post + " a-none"} data-astro-prefetch="viewport">
23
+ <article class={style.post}>
23
24
  <div class={style.postContainer}>
24
25
  {
25
26
  post.data.cover && (
26
- <div class={style.postCover}>
27
- <img src={post.data.cover} alt={post.data.title} loading="lazy" />
28
- </div>
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}>{post.data.title}</h2>
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
- <div class={style.postTag}>{tag}</div>
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
- <div class={style.postCategory}>{category}</div>
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
- </a>
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
- await Promise.all(
122
- [...links].map(async (link) => {
123
- const img = link.querySelector("img");
124
- if (!img) return;
125
-
126
- const tempImg = new Image();
127
- tempImg.src = img.currentSrc || img.src;
128
- try {
129
- await tempImg.decode();
130
- } catch {
131
- /* 解码失败时跳过尺寸写入 */
132
- return;
133
- }
134
- link.dataset.pswpWidth = String(tempImg.naturalWidth);
135
- link.dataset.pswpHeight = String(tempImg.naturalHeight);
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
- await enrichPhotoSwipeData();
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 href="javascript:void(0)" class="a-none" data-word-hash={hash}>
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
  ) : (
@@ -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: number | undefined;
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<HTMLAnchorElement>(".home-btn");
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
- // 添加懒加载和异步解码(封面图不在此处理,由 postCard.astro 控制)
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
  });
@@ -3,6 +3,7 @@
3
3
  ul {
4
4
  display: flex;
5
5
  flex-wrap: wrap;
6
+ justify-content: center;
6
7
  gap: 1em;
7
8
  padding: 0;
8
9
  margin: 0;
@@ -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);