Files
blog/src/pages/en/index.astro
T
wait4xandGLM-5.3 ebafa415b2 fix(polish): 视觉复核三轮——热力图末列可见与 en 首页版面平权
- 热力图:桌面 10px 格+2px 缝收进版心(53 列 634px 无横滚——唯一有
  数据的最新列曾被裁在滚出区呈空表);移动端锚定 scrollLeft 到最新列
- en 首页补 IdCard 名片区 + Latest photos 影像条(视觉复核版面平权建议)
- en/index 重复导入 getCollection 修复(构建炸点)

验证: pnpm astro build 21 页 exit 0;node tests/e2e.mjs 30/30 PASS
      (新构建全链)

Spec: 2026-10-25_fix-site-polish

Co-authored-by: GLM-5.3
2026-10-25 11:12:04 +08:00

87 lines
4.0 KiB
Plaintext

---
// /en/ 首页:英文 UI + 双语内容(postsEn 优先,缺则中文回退)
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { t } from '../../i18n/ui';
import IdCard from '../../components/IdCard.astro';
import { Image } from 'astro:assets';
const photos = (await getCollection('photos')).slice(0, 4);
import Heatmap from '../../components/geek/Heatmap.astro';
import { POST_TRACKS, trackOf } from '../../content.config';
const en = await getCollection('postsEn');
const zh = await getCollection('posts');
const bySlug = new Map(en.map((e) => [e.id, e]));
const posts = zh
.filter(({ data }) => !data.draft)
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
.map((p) => ({ zh: p, en: bySlug.get(p.id) }));
const years = [...new Set(posts.map((p) => p.zh.data.pubDate.getFullYear()))].sort((a, b) => b - a);
const t9 = (cat: keyof typeof POST_TRACKS) => trackOf(cat);
---
<BaseLayout title="wait4x" description="wait4x's blog: reading · code · photography" track="tech">
<h1 class="visually-hidden">wait4x</h1>
<IdCard>
<a href="https://github.com/wait4xx" rel="noopener noreferrer" aria-label="GitHub">↗</a>
<a href="/en/rss.xml" aria-label="RSS">RSS</a>
</IdCard>
{years.map((year) => (
<section class="latest" aria-label={t('latest_photos', 'en')}>
<span class="lab">{t('latest_photos', 'en')}</span>
<a href="/en/gallery/">{t('enter_gallery', 'en')}</a>
<div class="strip">
{photos.map((p) => (<a href="/en/gallery/"><Image src={p.data.image} alt={p.data.alt ?? p.data.title} widths={[160, 320]} sizes="20vw" loading="lazy" /></a>))}
</div>
</section>
<section class="year">
<h2 class="y-label">{year}</h2>
{(['reading', 'tech', 'photo'] as const).map((cat) => {
const list = posts.filter((p) => p.zh.data.category === cat && p.zh.data.pubDate.getFullYear() === year);
return list.length ? (
<>
<h3 class="track">{t(cat === 'reading' ? 'category_reading' : cat === 'tech' ? 'category_tech' : 'category_photo', 'en')}</h3>
<ul class="plist">
{list.map(({ zh: p, en: e }) => (
<li>
<a href={`/en/archives/${p.id}/`} class="plink">
<span class="ptitle" style={p.data.mood ? `--mood:${p.data.mood}` : undefined}>
{p.data.mood && <i class="mdot" aria-hidden="true" />}
{e?.data.title ?? p.data.title}
</span>
{!e && <i class="fall">zh</i>}
<time class="pdate">{p.data.pubDate.toISOString().slice(5, 10)}</time>
</a>
</li>
))}
</ul>
</>
) : null;
})}
</section>
))}
<Heatmap
labels={{
title: t('heatmap_title', 'en'),
summary: t('heatmap_summary', 'en'),
less: t('heatmap_less', 'en'),
more: t('heatmap_more', 'en'),
note: t('heatmap_note', 'en'),
}}
/>
</BaseLayout>
<style>
.year { margin-top: var(--space-5); }
.y-label { font-family: var(--font-mono); font-size: 20px; color: var(--ink-1); }
.track { font-size: 13px; color: var(--ink-3); margin: var(--space-3) 0 var(--space-2); letter-spacing: 0.06em; }
.plist { list-style: none; padding: 0; display: grid; gap: var(--space-1); }
.plink { display: flex; align-items: baseline; gap: var(--space-2); padding-block: var(--space-1); }
.plink:hover .ptitle { color: var(--accent); }
.ptitle { color: var(--ink-1); transition: color var(--dur) var(--ease); }
.mdot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--mood, var(--ink-4)); margin-right: 6px; }
.fall { font-family: var(--font-mono); font-size: 10px; color: var(--ink-4); border: 1px solid var(--line); border-radius: 3px; padding: 0 3px; font-style: normal; }
.pdate { margin-left: auto; font-family: var(--font-mono); font-size: 12px; color: var(--ink-3); }
</style>