feat(launch): 热力图与 webhook 发布闭环(T2/T3)
- T2 (op-artalk 车道):gitea heatmap 构建期拉取+fallback+空态三保险,
53×7 灰阶格复用 tokens(亮暗自动),日界锚定 Asia/Shanghai,零 JS,
zh/en 首页挂载;fallback 经死端点实测不破构建
- T3:webhook_receiver.py(HMAC 强制+main-only+构建锁)以 systemd
--user 常驻 13021;gitea hook 注册(events=push, branch=main)
- 三个深坑:gitea 容器回宿主须走桥网关 IP 而非 127.0.0.1;gitea 28
egress 拦私网须 ALLOWED_HOST_LIST(ALLOWLIST 是废弃名);systemd
用户服务缺用户 PATH 致 build 静默失败(stamp 不更新即症状)
验证: 测试投递 202→构建→staging 戳更新(09:48:37 实测);
dist 热力图 376 格真数据(9 次贡献)zh/en 双页
Spec: 2026-10-25_add-blog-launch
Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
0c54a43bb5
commit
938558ad4c
12 files changed
+692
-110
No files matched your search
@@ -0,0 +1,106 @@
|
||||
---
|
||||
// 贡献热力图(极客层 P0):纯 CSS grid,零运行时 JS
|
||||
// 数据聚合见 src/lib/heatmap.ts;灰阶四档直接复用 tokens --ink-1..4(亮暗两套自动跟随)
|
||||
import { loadHeatmap, levelOf } from '../../lib/heatmap';
|
||||
|
||||
// 文案全部由页面层注入(locale 无关挂法),keys 与 ui.ts heatmap_* 一一对应
|
||||
interface Labels {
|
||||
title: string;
|
||||
summary: string; // 含 {n} 占位符
|
||||
less: string;
|
||||
more: string;
|
||||
note: string;
|
||||
}
|
||||
interface Props {
|
||||
labels: Labels;
|
||||
}
|
||||
|
||||
const { labels } = Astro.props;
|
||||
let weeks: import('../../lib/heatmap').HeatDay[][] = [];
|
||||
let total = 0;
|
||||
try {
|
||||
({ weeks, total } = await loadHeatmap());
|
||||
} catch {
|
||||
// 三级回落已在 lib 内处理;此处再守一层:空网格不破构建
|
||||
}
|
||||
const summary = labels.summary.replace('{n}', String(total));
|
||||
---
|
||||
|
||||
<section class="heatmap">
|
||||
<h2 class="hm-title">{labels.title}</h2>
|
||||
<!-- 53 列窄栏触屏放不下,容器横滚(格子原生 title 提示,无 JS) -->
|
||||
<div class="hm-scroll">
|
||||
<div class="hm-grid" role="img" aria-label={summary}>
|
||||
{weeks.map((week) =>
|
||||
week.map((day) => (
|
||||
<span
|
||||
class={`hm-c hm-l${levelOf(day.count)}`}
|
||||
title={`${day.date} · ${day.count}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p class="hm-legend">
|
||||
<span aria-hidden="true">{labels.less}</span>
|
||||
{[0, 1, 2, 3, 4].map((l) => (
|
||||
<span class={`hm-c hm-l${l}`} aria-hidden="true" />
|
||||
))}
|
||||
<span aria-hidden="true">{labels.more}</span>
|
||||
<span class="hm-note">{labels.note}</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.heatmap { margin-top: var(--space-6); }
|
||||
.hm-title {
|
||||
font-size: 14px;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--ink-3);
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
.hm-scroll {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
/* 列优先流:子元素按周展平后自上而下再向右,得 7 行 × 53 列 */
|
||||
.hm-grid {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-template-rows: repeat(7, 11px);
|
||||
grid-auto-columns: 11px;
|
||||
gap: 3px;
|
||||
width: max-content;
|
||||
}
|
||||
.hm-c {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 2px;
|
||||
background: transparent;
|
||||
}
|
||||
/* 空档 l0 用极淡描边标位;l1-l4 = ink-4 → ink-1(亮暗模式各自成对,多则更醒目) */
|
||||
.hm-l0 { border: 1px solid var(--line); }
|
||||
.hm-l1 { background: var(--ink-4); }
|
||||
.hm-l2 { background: var(--ink-3); }
|
||||
.hm-l3 { background: var(--ink-2); }
|
||||
.hm-l4 { background: var(--ink-1); }
|
||||
.hm-legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin: var(--space-2) 0 0;
|
||||
font-size: 11px;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.hm-legend .hm-c { width: 9px; height: 9px; }
|
||||
.hm-note {
|
||||
margin-left: var(--space-3);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
color: var(--ink-4);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user