+
{locale === 'zh' ? '数据板' : 'Data board'}
+
+ {boardCards.map((card) => (
+
+
+ {card.dark && (
+
+ )}
+ {card.label}
+
+ ))}
+ {snakeCard && (
+
+
+ {snakeCard.label}
+
+ )}
+
+
+ )}
diff --git a/src/components/geek/ProjectWall.astro b/src/components/geek/ProjectWall.astro
index a2ee66c..bcc1f38 100644
--- a/src/components/geek/ProjectWall.astro
+++ b/src/components/geek/ProjectWall.astro
@@ -73,10 +73,9 @@ try {
.repo {
display: flex;
flex-direction: column;
- /* 显式接管交叉轴:全局 .tap 是 inline-flex + align-items:center,
- 列向 flex 下会把标题/元信息行水平居中(视觉终审 P1,卡片左缘参差) */
- align-items: stretch;
- text-align: left;
+ /* 用户拍板(5i T6):Projects 卡片内容居中,不用左对齐 */
+ align-items: center;
+ text-align: center;
gap: 2px;
padding: var(--space-3);
border: 1px solid var(--line);
@@ -86,7 +85,12 @@ try {
}
.repo:hover { border-color: var(--accent); }
.repo-name { font-family: var(--font-mono); font-size: 14px; color: var(--ink-1); }
- .repo-desc { font-size: 13px; color: var(--ink-3); }
+ .repo-desc {
+ font-size: 13px;
+ color: var(--ink-3);
+ /* 居中后长描述限行宽,避免整行摊平散架 */
+ max-width: 34em;
+ }
.repo-meta { display: flex; gap: var(--space-3); font-size: 12px; color: var(--ink-3); margin-top: 4px; }
.lang { display: inline-flex; align-items: center; gap: 4px; }
.dot {
diff --git a/src/components/geek/TypingLines.astro b/src/components/geek/TypingLines.astro
new file mode 100644
index 0000000..e2af038
--- /dev/null
+++ b/src/components/geek/TypingLines.astro
@@ -0,0 +1,106 @@
+---
+// 自绘打字机动效(5i T8):替代外链 readme-typing-svg
+// 纯 CSS + 极少 JS:SSR 先渲染第一行(无 JS / reduced-motion 时就是静态首行,不闪不跳),
+// JS 接管后循环「逐字打出 → 停留 → 逐字删除 → 下一行」;光标闪烁走 CSS keyframes。
+interface Props {
+ /** 已本地化的文案行(保持 ≤6 行) */
+ lines: string[];
+ /** 每字毫秒 */
+ speed?: number;
+}
+const { lines, speed = 55 } = Astro.props;
+const safe = lines.map((l) => l.trim()).filter(Boolean).slice(0, 6);
+const first = safe[0] ?? '';
+---
+
+{safe.length > 0 && (
+