feat(pages): /stack 装备页 + /colophon 制作页(双语)与页脚入口
- colophon:五节灰阶数据表讲"这站怎么造的"(build/visual/写作流
/i18n/features,变字体提速等实数入表)
- stack:homelab/自托管/站点依赖/日常四节(版本号实读
package.json;硬件细节与 daily 清单 TODO(下轮) 待补)
- Footer 顺手修真缺陷:locale prop 机制存在但从未透传,en 全站
页脚实为 zh 文案——改 Astro.url.pathname 构建期自推断(localeOf
复用,显式 prop 优先),零 BaseLayout 改动
- 页脚「装备 · 制作」双入口(en 轨自动 /en/ 前缀)
车道:op-artalk(自验 15/15:双页双语桌面+390 溢出断言+Footer
双轨感知);收编验收 6 检全过
验证: pnpm astro build 34 页 exit 0;双页双语 390 视口无溢出;E2E
31/31 PASS
Spec: none (两静态页+Footer 修复,车道级交付)
Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
a347ce8baf
commit
aa050d8035
6 files changed
+479
-2
No files matched your search
@@ -3,11 +3,14 @@
|
||||
import { t } from '../i18n/ui';
|
||||
import type { Locale } from '../lib/locale';
|
||||
|
||||
// /en/ 侧由 T4 薄页传 locale='en';zh 路由无人传参,默认 zh 零回归
|
||||
// locale 感知:显式 prop 优先;无 prop 时构建期按页面 URL 推断轨道
|
||||
// (BaseLayout 引用本组件不透传 locale,靠 Astro.url 静态解析补全 en 轨)
|
||||
import { localeOf } from '../lib/locale';
|
||||
interface Props {
|
||||
locale?: Locale;
|
||||
}
|
||||
const { locale = 'zh' } = Astro.props;
|
||||
const { locale: propLocale } = Astro.props;
|
||||
const locale = propLocale ?? localeOf(Astro.url.pathname);
|
||||
---
|
||||
|
||||
<footer class="site-footer safe-area">
|
||||
@@ -17,6 +20,10 @@ const { locale = 'zh' } = Astro.props;
|
||||
<a href="https://beian.miit.gov.cn/" rel="noopener noreferrer" target="_blank" class="link">
|
||||
{t('footer_icp', locale)}
|
||||
</a>
|
||||
·
|
||||
<a href={locale === 'en' ? '/en/stack/' : '/stack/'} class="link">{t('footer_stack', locale)}</a>
|
||||
·
|
||||
<a href={locale === 'en' ? '/en/colophon/' : '/colophon/'} class="link">{t('footer_colophon', locale)}</a>
|
||||
</p>
|
||||
<!-- T7 注入:built from {commit} · {time} · Astro(链到 gitea commit) -->
|
||||
<slot name="build-info" />
|
||||
|
||||
@@ -74,6 +74,10 @@ const dict = {
|
||||
heatmap_more: { zh: '多', en: 'More' },
|
||||
heatmap_note: { zh: '数据来自 gitea · 构建期生成', en: 'via gitea · generated at build time' },
|
||||
|
||||
// ===== B 车道:Footer 极客双页入口(stack/colophon 页内文案各自内联,不入本表) =====
|
||||
footer_stack: { zh: '装备', en: 'stack' },
|
||||
footer_colophon: { zh: '制作', en: 'colophon' },
|
||||
|
||||
// ===== 命令面板(Phase 4 T1 车道):动作/导航/分组与键位提示 =====
|
||||
nav_home: { zh: '首页', en: 'Home' },
|
||||
nav_moments: { zh: '瞬间', en: 'Moments' },
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
// /colophon 制作页:本站如何造出来——灰阶数据表叙事,不做营销页
|
||||
// 事实源:package.json 实版本、部署实录、docs/博客重设计调研报告.md 拍板项
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
const SECTIONS = [
|
||||
{
|
||||
tag: 'build',
|
||||
items: [
|
||||
['Astro 7.3.8', '静态生成(SSG),零运行时框架 JS;交互由原生 island 脚本承担'],
|
||||
['Pagefind', '搜索索引构建期生成,静态站零后端搜索'],
|
||||
['Shiki', '代码双主题高亮,构建期完成'],
|
||||
['构建产物', '约 30 页 / 数秒完成,homelab 上由 push 触发'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'visual',
|
||||
items: [
|
||||
['丙路线', '灰阶骨架 × 分轨正文:结构用灰阶与留白说话,正文按内容轨道换字体'],
|
||||
['四字体族', '思源宋(书评正文)· 思源黑(UI/技术)· JetBrains Mono(代码)· 霞鹜文楷(金句,系统栈回退)'],
|
||||
['@fontsource', '字体自托管分片加载;思源黑/宋切换变字体后 CSS 345KB→106KB 全站提速'],
|
||||
['tokens', '颜色/间距/时长全部变量化,亮暗两套一套代码'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'writing flow',
|
||||
items: [
|
||||
['gitea', '自托管 git 服务:本地编辑器与 gitea 网页写的是同一个仓库'],
|
||||
['webhook', 'push 触发,HMAC 签名验证,仅 main 分支'],
|
||||
['发布链', 'homelab 构建 → rsync → nginx 静态目录,全程无云厂商构建分钟'],
|
||||
['透明化', '页脚 built-from 一行链到本次构建对应的 commit'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'i18n',
|
||||
items: [
|
||||
['预翻译', '腾讯 TMT 构建期机翻,.en.md 入仓版本化'],
|
||||
['双语轨', '/en/ 前缀预构建,切换是纯导航零等待'],
|
||||
['译注', '机翻文章带 AI 译注小标,欢迎指正'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'features',
|
||||
items: [
|
||||
['评论', 'Artalk 2.10 自托管(sqlite),挂主域 /artalk 路径'],
|
||||
['图库', 'EXIF 构建期提取(exifr)+ sharp 宽高比驱动 masonry,零运行时 JS'],
|
||||
['边缘', 'ESA CDN + HTTPS 通配证书;页脚 ICP 备案法定保留'],
|
||||
],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout title="colophon" description="这个博客怎么造的:构建、视觉、写作流与自托管全家桶">
|
||||
<section class="colo">
|
||||
<h1 class="colo-title">制作 <span class="dim">/ colophon</span></h1>
|
||||
<p class="colo-lead">
|
||||
一个静态博客能自己决定的事情,尽量自己决定:自己构建、自己托管、自己翻译、自己记版本。
|
||||
</p>
|
||||
{SECTIONS.map((s) => (
|
||||
<section class="sec">
|
||||
<h2 class="sec-tag">{s.tag}</h2>
|
||||
<dl class="rows">
|
||||
{s.items.map(([term, desc]) => (
|
||||
<div class="row">
|
||||
<dt>{term}</dt>
|
||||
<dd>{desc}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.colo { max-width: 720px; }
|
||||
.colo-title {
|
||||
font-size: 22px;
|
||||
color: var(--ink-1);
|
||||
margin: var(--space-5) 0 var(--space-2);
|
||||
}
|
||||
.dim { color: var(--ink-4); font-family: var(--font-mono); font-size: 16px; }
|
||||
.colo-lead {
|
||||
font-size: 14px;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.sec { margin-bottom: var(--space-5); }
|
||||
.sec-tag {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-4);
|
||||
letter-spacing: 0.08em;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding-bottom: var(--space-1);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.rows { margin: 0; }
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 120px 1fr;
|
||||
gap: var(--space-3);
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
.row + .row { border-top: 1px dashed var(--line); }
|
||||
dt {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
color: var(--ink-2);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
dd { margin: 0; font-size: 13px; color: var(--ink-3); line-height: 1.7; }
|
||||
/* 窄屏:键值上下堆叠,避免 120px 定宽挤压长译文 */
|
||||
@media (max-width: 560px) {
|
||||
.row { grid-template-columns: 1fr; gap: 2px; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
// en/colophon: English track of /colophon — same structure, condensed copy
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
|
||||
const SECTIONS = [
|
||||
{
|
||||
tag: 'build',
|
||||
items: [
|
||||
['Astro 7.3.8', 'static site generation, zero runtime framework JS; interactions are small native scripts'],
|
||||
['Pagefind', 'search index built at compile time — search on a static site, no backend'],
|
||||
['Shiki', 'dual-theme syntax highlighting, done at build'],
|
||||
['output', '~30 pages in seconds, triggered by every push'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'visual',
|
||||
items: [
|
||||
['grayscale × tracks', 'a grayscale skeleton for structure; body typography switches per content track'],
|
||||
['four families', 'Noto Serif SC (books) · Noto Sans SC (UI/tech) · JetBrains Mono (code) · LXGW WenKai (quotes, system fallback)'],
|
||||
['@fontsource', 'self-hosted, subset fonts; switching to variable fonts cut font CSS 345KB → 106KB'],
|
||||
['tokens', 'colors, spacing and motion are all CSS variables — light/dark in one codebase'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'writing flow',
|
||||
items: [
|
||||
['gitea', 'self-hosted git: my local editor and the web UI write to the same repository'],
|
||||
['webhook', 'push-triggered, HMAC-verified, main branch only'],
|
||||
['pipeline', 'homelab build → rsync → nginx; no cloud build minutes'],
|
||||
['transparency', 'the built-from line in the footer links to the exact commit'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'i18n',
|
||||
items: [
|
||||
['pre-translation', 'machine-translated by Tencent TMT at build; .en.md files live in the repo'],
|
||||
['dual track', '/en/ pages are prebuilt — switching language is pure navigation'],
|
||||
['honesty', 'machine-translated posts carry a note; corrections welcome'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'features',
|
||||
items: [
|
||||
['comments', 'self-hosted Artalk 2.10 (sqlite) at /artalk'],
|
||||
['gallery', 'EXIF extracted at build (exifr) + aspect-ratio-driven masonry via sharp, no runtime JS'],
|
||||
['edge', 'ESA CDN, wildcard HTTPS; ICP filing kept in the footer as required'],
|
||||
],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout title="colophon" description="How this blog is built: pipeline, typography, and the self-hosted stack">
|
||||
<section class="colo">
|
||||
<h1 class="colo-title">colophon <span class="dim">/ 制作</span></h1>
|
||||
<p class="colo-lead">
|
||||
A static blog that decides for itself wherever it can: its own build, its own hosting, its own translations, its own version history.
|
||||
</p>
|
||||
{SECTIONS.map((s) => (
|
||||
<section class="sec">
|
||||
<h2 class="sec-tag">{s.tag}</h2>
|
||||
<dl class="rows">
|
||||
{s.items.map(([term, desc]) => (
|
||||
<div class="row">
|
||||
<dt>{term}</dt>
|
||||
<dd>{desc}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.colo { max-width: 720px; }
|
||||
.colo-title {
|
||||
font-size: 22px;
|
||||
color: var(--ink-1);
|
||||
margin: var(--space-5) 0 var(--space-2);
|
||||
}
|
||||
.dim { color: var(--ink-4); font-family: var(--font-mono); font-size: 16px; }
|
||||
.colo-lead {
|
||||
font-size: 14px;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.sec { margin-bottom: var(--space-5); }
|
||||
.sec-tag {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-4);
|
||||
letter-spacing: 0.08em;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding-bottom: var(--space-1);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.rows { margin: 0; }
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
gap: var(--space-3);
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
.row + .row { border-top: 1px dashed var(--line); }
|
||||
dt {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
color: var(--ink-2);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
dd { margin: 0; font-size: 13px; color: var(--ink-3); line-height: 1.7; }
|
||||
@media (max-width: 560px) {
|
||||
.row { grid-template-columns: 1fr; gap: 2px; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
// en/stack: English track of /stack — same structure, condensed copy
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
|
||||
const SECTIONS = [
|
||||
{
|
||||
tag: 'homelab',
|
||||
items: [
|
||||
['Debian 13', 'trixie, the host system'],
|
||||
['Docker', 'Engine + Compose; every service runs containerized'],
|
||||
// TODO(next round): hardware details (machine/CPU/RAM/storage) pending
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'self-hosted',
|
||||
items: [
|
||||
['gitea 28.2', 'git hosting — the source of the writing & build flow (git.wait4x.online)'],
|
||||
['nginx', 'static site serving and reverse proxy'],
|
||||
['Artalk 2.10', 'comments, sqlite storage'],
|
||||
['ddns-go', 'IPv6 dynamic DNS'],
|
||||
['clash', 'outbound traffic routing'],
|
||||
['new-api', 'LLM API gateway (build-time translation goes through it)'],
|
||||
['crawl4ai', 'headless-browser fetching service'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'site deps',
|
||||
items: [
|
||||
['Astro 7.3.8', 'the SSG framework'],
|
||||
['sharp 0.35', 'image optimization pipeline'],
|
||||
['exifr 7.1', 'EXIF extraction at build'],
|
||||
['@fontsource 5.3', 'self-hosted subset fonts: Noto Sans/Serif SC, JetBrains Mono'],
|
||||
['Pagefind', 'static search index'],
|
||||
['Playwright 1.64', 'E2E self-checks (chromium screenshots & assertions)'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'daily',
|
||||
items: [
|
||||
// TODO(next round): daily software list pending
|
||||
],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout title="stack" description="The hardware, software and services behind this site">
|
||||
<section class="stack">
|
||||
<h1 class="stack-title">stack <span class="dim">/ 装备</span></h1>
|
||||
<p class="stack-lead">One small machine and a handful of open source. Self-host what can be self-hosted; version what can be versioned.</p>
|
||||
{SECTIONS.map((s) => (
|
||||
<section class="sec">
|
||||
<h2 class="sec-tag">{s.tag}</h2>
|
||||
{s.items.length === 0 ? (
|
||||
<p class="todo">TODO(next round): list pending</p>
|
||||
) : (
|
||||
<dl class="rows">
|
||||
{s.items.map(([term, desc]) => (
|
||||
<div class="row">
|
||||
<dt>{term}</dt>
|
||||
<dd>{desc}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.stack { max-width: 720px; }
|
||||
.stack-title {
|
||||
font-size: 22px;
|
||||
color: var(--ink-1);
|
||||
margin: var(--space-5) 0 var(--space-2);
|
||||
}
|
||||
.dim { color: var(--ink-4); font-family: var(--font-mono); font-size: 16px; }
|
||||
.stack-lead {
|
||||
font-size: 14px;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.sec { margin-bottom: var(--space-5); }
|
||||
.sec-tag {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-4);
|
||||
letter-spacing: 0.08em;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding-bottom: var(--space-1);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.rows { margin: 0; }
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
gap: var(--space-3);
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
.row + .row { border-top: 1px dashed var(--line); }
|
||||
dt {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
color: var(--ink-2);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
dd { margin: 0; font-size: 13px; color: var(--ink-3); line-height: 1.7; }
|
||||
.todo {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-4);
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.row { grid-template-columns: 1fr; gap: 2px; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
// /stack 装备页:跑着这个站与日常的一切——按实情条目化,硬件细节未定处宁缺留 TODO
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
const SECTIONS = [
|
||||
{
|
||||
tag: 'homelab',
|
||||
items: [
|
||||
['Debian 13', 'trixie,宿主系统'],
|
||||
['Docker', 'Engine + Compose,全部服务容器化部署'],
|
||||
// TODO(下轮): 机型/CPU/内存/存储等硬件细节待用户补
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'self-hosted',
|
||||
items: [
|
||||
['gitea 28.2', 'git 托管:写作流与构建触发源头(git.wait4x.online)'],
|
||||
['nginx', '静态站点服务与反代出口'],
|
||||
['Artalk 2.10', '评论系统,sqlite 存储'],
|
||||
['ddns-go', 'IPv6 动态解析'],
|
||||
['clash', '出网分流'],
|
||||
['new-api', 'LLM API 网关(构建期翻译走它)'],
|
||||
['crawl4ai', '无头浏览器抓取服务'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'site deps',
|
||||
items: [
|
||||
['Astro 7.3.8', 'SSG 框架'],
|
||||
['sharp 0.35', '图像优化管线'],
|
||||
['exifr 7.1', 'EXIF 构建期提取'],
|
||||
['@fontsource 5.3', '思源黑/思源宋/JetBrains Mono 自托管分片'],
|
||||
['Pagefind', '静态搜索索引'],
|
||||
['Playwright 1.64', 'E2E 自验(chromium 截图与断言)'],
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'daily',
|
||||
items: [
|
||||
// TODO(下轮): 日常软件清单待用户补(编辑器/浏览器/终端等)
|
||||
],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout title="stack" description="跑着这个站与日常的装备清单">
|
||||
<section class="stack">
|
||||
<h1 class="stack-title">装备 <span class="dim">/ stack</span></h1>
|
||||
<p class="stack-lead">一台小机器 + 一把开源工具。能用自托管解决的不上云,能版本化的不靠记忆。</p>
|
||||
{SECTIONS.map((s) => (
|
||||
<section class="sec">
|
||||
<h2 class="sec-tag">{s.tag}</h2>
|
||||
{s.items.length === 0 ? (
|
||||
<p class="todo">TODO(下轮):清单待补</p>
|
||||
) : (
|
||||
<dl class="rows">
|
||||
{s.items.map(([term, desc]) => (
|
||||
<div class="row">
|
||||
<dt>{term}</dt>
|
||||
<dd>{desc}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.stack { max-width: 720px; }
|
||||
.stack-title {
|
||||
font-size: 22px;
|
||||
color: var(--ink-1);
|
||||
margin: var(--space-5) 0 var(--space-2);
|
||||
}
|
||||
.dim { color: var(--ink-4); font-family: var(--font-mono); font-size: 16px; }
|
||||
.stack-lead {
|
||||
font-size: 14px;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.sec { margin-bottom: var(--space-5); }
|
||||
.sec-tag {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-4);
|
||||
letter-spacing: 0.08em;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding-bottom: var(--space-1);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.rows { margin: 0; }
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
gap: var(--space-3);
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
.row + .row { border-top: 1px dashed var(--line); }
|
||||
dt {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
color: var(--ink-2);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
dd { margin: 0; font-size: 13px; color: var(--ink-3); line-height: 1.7; }
|
||||
.todo {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-4);
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.row { grid-template-columns: 1fr; gap: 2px; }
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user