惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
博客园 - 司徒正美
美团技术团队
WordPress大学
WordPress大学
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
人人都是产品经理
人人都是产品经理
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Troy Hunt's Blog
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
B
Blog
NISL@THU
NISL@THU
月光博客
月光博客
博客园 - 【当耐特】
AWS News Blog
AWS News Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
L
Lohrmann on Cybersecurity
The Cloudflare Blog
L
LINUX DO - 最新话题
S
Security @ Cisco Blogs
S
Secure Thoughts
Spread Privacy
Spread Privacy
有赞技术团队
有赞技术团队
The Last Watchdog
The Last Watchdog
Project Zero
Project Zero
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Vercel News
Vercel News
H
Hacker News: Front Page
S
SegmentFault 最新的问题
Schneier on Security
Schneier on Security
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
博客园 - 三生石上(FineUI控件)
Forbes - Security
Forbes - Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
I
InfoQ
T
Tailwind CSS Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
GRAHAM CLULEY
W
WeLiveSecurity
小众软件
小众软件
Recorded Future
Recorded Future
Cyberwarzone
Cyberwarzone
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

素生

2026 阅读 记时过夏末2026巡演成都站 观后:《忘梦洞》 最近惦念 20260322 摘:《How to Do Nothing》 觉得会离不开的供应商盘点(2026年3月) 2026 纸书单 OpenClaw 配置 最近惦念 20260213 最近惦念 20260128 最近惦念 20260110 摘:《永恒的时光之旅》 摘:《移民》 摘:《未弃之物》 摘:《建筑养成记》 最近惦念 20251230 佳 2025 2025 阅读 2025 观影 最近惦念 20251211 最近惦念 20251125 普鲁斯特问卷 2025 最近惦念 20251110 摘:《三诗人书简》 摘:《彼岸》 摘:《熊猫的拇指》 最近惦念 20251025 最近惦念 20251013 入蜀记 day145 有依据的复盘 入蜀记 day144 打球被咬 入蜀记 day143 墨墨蚊想杀我 最近惦念 20251001 入蜀记 day142 远山淡影 入蜀记 day141 返蓉 入蜀记 day140 角色扮演 入蜀记 day139 出差 入蜀记 day138 不如搞砸 入蜀记 day137 附近,情绪的回归和筑底 入蜀记 day136 只靠内力生长 入蜀记 day135 休闲的反义词是牛马 入蜀记 day134 共生 入蜀记 day133 草民日记 入蜀记 day132 八哥好奇墨镜 入蜀记 day131 柳荫记 入蜀记 day130 手指、迷路、无限游戏 最近惦念 20250908 入蜀记 day129 代、蠛蚊、模拟、薇依 入蜀记 day128 如同少女 入蜀记 day127 陋习早改 入蜀记 day124 别的解法 入蜀记 day123 人们是用其他方式记住你的 入蜀记 day122 里巷 入蜀记 day119 树夏榴莲味 入蜀记 day118 之前好像没有节奏 入蜀记 day117 每个字都是亲手敲的 入蜀记 day116 温柔小变态 摘:《刀尖上的舞蹈》 入蜀记 day114 家有二老 入蜀记 day113 熊猫笋、百岁、人格 入蜀记 day112 勿自伤、快与慢 入蜀记 day111 在自己的游戏里尽兴 入蜀记 day110 想扩列 入蜀记 day109 活在了古诗里 入蜀记 day108 写写写写写写写 入蜀记 day107 就像解决不了死亡 最近惦念 20250817 入蜀记 day106 青羊宫 2025 纸书单 入蜀记 day105 桂花香 入蜀记 day104 慢行散步,方有诗意生 入蜀记 day103 慢与快 入蜀记 day102 市场和阅兵 入蜀记 day101 1号线终点 入蜀记 day100 变与策 书籍、电影等下载渠道安利总辑 最近惦念 20250727 入蜀记 day85 为谁忧伤 入蜀记 day84 老朋友 入蜀记 day83 落难接待 入蜀记 day82 风和暴雨 入蜀记 day81 曹鸡片 入蜀记 day80 “楚门的世界” 入蜀记 day79 烂肉豇豆 入蜀记 day78 竹林落雨 入蜀记 day76 望到美 入蜀记 day75 立秋、吹风和巴中 入蜀记 day74 内在价值 入蜀记 day73 向外张望 入蜀记 day72 新舊書店 入蜀记 day56 锅巴肉片 摘:《顾客主义》 摘:《廉价王》 入蜀记 day55 散漫恍惚 入蜀记 day54 滑石粉 入蜀记 day53 种了罗勒 入蜀记 day52 没有心 入蜀记 day51 西扎 入蜀记 day50 TRIZ 入蜀记 day49 暑袜 入蜀记 day48 怪味面 时间观
Hexo 增加热力图
素生 · 2026-03-19 · via 素生
1
2
3
4
5
6
7
8
9
10
block content
.post
h1.post-title(onclick="copyUrlSegment()")= page.title

//- 放在正文内容之前(推荐,一打开页面就能看到热力图)
if page.heatmap
include _partial/heatmap.pug

.post-content
!= page.content
1
2
3
4
5
6
7
8
9
10
11
12
block content
.post
h1.post-title(onclick="copyUrlSegment()")= page.title
.post-meta= page.date.format(config.date_format)

//- 放在.post-meta之后.post-content之前,与它们平级
if page.heatmap
div(style="margin: 2rem 0;")
include _partial/heatmap.pug

.post-content
!= page.content
1
2
3
4
5
---
title: INDEX
date: 2025-09-30 09:08:27
heatmap: true
---
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
//- 1. 数据预处理
-
var yearWeekData = {};
var yearsSet = new Set();

site.posts.forEach(function(post) {
var y = post.date.isoWeekYear();
var w = post.date.isoWeek();
yearsSet.add(y);

var key = y + '-' + w;
if (!yearWeekData[key]) {
yearWeekData[key] = { count: 0, posts: [] };
}
yearWeekData[key].count += 1;
yearWeekData[key].posts.push({
title: post.title,
link: url_for(post.path)
});
});

var allYears = Array.from(yearsSet).sort(function(a, b){ return b - a });
if(allYears.length === 0) allYears = [new Date().getFullYear()];
var maxYear = allYears[0];
var minYear = allYears[allYears.length - 1];

var displayYears = [];
for(var i = maxYear; i >= minYear; i--) {
displayYears.push(i);
}

//- 2. DOM 结构
#heatmap-container
#tooltip

//- 3. 样式表
style.
#heatmap-container {
width: 100%;
overflow: visible;
margin-top: 40px;
margin-bottom: 30px;
}

.heatmap-cell {
rx: 1.5px;
ry: 1.5px;
transition: opacity 0.2s ease;
stroke: none !important;
}

.heatmap-cell.has-post {
cursor: default; /* 改为默认箭头,因为现在不直接点击色块了 */
}

.heatmap-cell:hover {
opacity: 0.5;
}

.year-text, .legend-text, .legend-number, #tooltip {
font-family: 'Source Han Serif SC', 'Source Han Serif', 'Noto Serif CJK SC', serif;
}

.year-text {
font-size: 14px;
font-weight: bold;
fill: #888888;
}

.legend-text {
font-size: 14px;
fill: #333333;
font-weight: bold;
}

.legend-number {
font-size: 13px;
fill: #888888;
}

html[data-dark="true"] .legend-text { fill: #dddddd; }
html[data-dark="true"] .year-text,
html[data-dark="true"] .legend-number { fill: #777777; }

/* Tooltip 样式:删除了 pointer-events: none,加入隐藏机制 */
#tooltip {
position: absolute;
background-color: rgba(255, 255, 255, 0.98);
color: #222222;
border: 1px solid #e0e0e0;
border-radius: 2px;
padding: 10px 14px;
opacity: 0;
visibility: hidden; /* 默认隐藏,防止阻挡正常页面点击 */
font-size: 13px;
transform: translate(-50%, -100%);
line-height: 1.6;
z-index: 1000;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}

/* 优雅的链接样式 */
#tooltip a {
color: inherit;
text-decoration: none;
display: block;
border-bottom: 1px dashed rgba(136, 136, 136, 0.3);
padding: 2px 0;
transition: all 0.2s ease;
}
#tooltip a:last-child {
border-bottom: none;
}
#tooltip a:hover {
color: #000;
border-bottom: 1px solid #000;
}

html[data-dark="true"] #tooltip {
background-color: rgba(30, 30, 30, 0.98);
color: #dddddd;
border: 1px solid #444444;
box-shadow: 0 4px 12px rgba(0,0,0,0.6);
}

html[data-dark="true"] #tooltip a:hover {
color: #fff;
border-bottom: 1px solid #fff;
}

//- 4. 脚本逻辑
script(src="https://d3js.org/d3.v5.min.js")
script.
document.addEventListener("DOMContentLoaded", function () {
var data = !{JSON.stringify(yearWeekData)};
var displayYears = !{JSON.stringify(displayYears)};

var fullGrid = [];
displayYears.forEach(function(y, rowIdx) {
for(var w = 1; w <= 53; w++) {
var key = y + '-' + w;
var cellData = data[key] || { count: 0, posts: [] };
fullGrid.push({
year: y,
week: w,
row: rowIdx,
col: w - 1,
count: cellData.count,
posts: cellData.posts
});
}
});

var cellWidth = 10;
var cellHeight = 16.18;
var cellPaddingX = 3;
var cellPaddingY = 8;
var labelWidth = 50;
var topMargin = 45;

var width = labelWidth + (53 * (cellWidth + cellPaddingX));
var height = topMargin + (displayYears.length * (cellHeight + cellPaddingY));

var svg = d3.select("#heatmap-container").append("svg")
.attr("viewBox", "0 0 " + width + " " + height)
.attr("preserveAspectRatio", "xMidYMid meet");

function getFillColor(count, isDark) {
if (isDark) {
if (count === 0) return "#1e1e1e";
if (count === 1) return "#3a3a3a";
if (count === 2) return "#5c5c5c";
if (count === 3) return "#858585";
return "#b3b3b3";
} else {
if (count === 0) return "#ebebeb";
if (count === 1) return "#c2c2c2";
if (count === 2) return "#949494";
if (count === 3) return "#666666";
return "#333333";
}
}

svg.append("text")
.attr("class", "legend-text")
.attr("x", 0)
.attr("y", 16)
.text("Posts by week");

var legendCounts = [0, 1, 2, 3, 4];
var legendGroup = svg.append("g").attr("transform", "translate(125, 4)");
var legendSpacing = 42;

legendGroup.selectAll("rect")
.data(legendCounts)
.enter().append("rect")
.attr("class", "heatmap-cell")
.attr("width", cellWidth)
.attr("height", cellHeight)
.attr("x", function(d, i) { return i * legendSpacing; })
.attr("y", 0);

legendGroup.selectAll("text")
.data(["0", "1", "2", "3", "4+"])
.enter().append("text")
.attr("class", "legend-number")
.attr("x", function(d, i) { return i * legendSpacing + cellWidth + 6; })
.attr("y", 13)
.text(function(d) { return d; });

svg.selectAll(".year-text.axis")
.data(displayYears)
.enter().append("text")
.attr("class", "year-text axis")
.attr("x", 0)
.attr("y", function(d, i) {
return topMargin + i * (cellHeight + cellPaddingY) + cellHeight - 3;
})
.text(function(d) { return d; });

var gridGroup = svg.append("g")
.attr("transform", "translate(" + labelWidth + "," + topMargin + ")");

var cells = gridGroup.selectAll(".heatmap-cell.grid-cell")
.data(fullGrid)
.enter().append("rect")
.attr("class", function(d) { return "heatmap-cell grid-cell" + (d.count > 0 ? " has-post" : ""); })
.attr("width", cellWidth)
.attr("height", cellHeight)
.attr("x", function(d) { return d.col * (cellWidth + cellPaddingX); })
.attr("y", function(d) { return d.row * (cellHeight + cellPaddingY); });

// 【核心改进】:增加缓冲计时器,解决鼠标移动时的断层消失问题
var hideTimeout;
var tooltip = d3.select("#tooltip");

cells.on("mouseover", function(d) {
if (d.count > 0) {
clearTimeout(hideTimeout);
tooltip.style("visibility", "visible").transition().duration(200).style("opacity", 1);

// 渲染顶部周次说明,以及下方的文章可点击链接
var text = "<div style='margin-bottom: 8px; font-weight: bold; color: #888; font-size: 11px; border-bottom: 1px solid #eee; padding-bottom: 4px;'>" + d.year + "年 第" + d.week + "周 (" + d.count + "篇)</div>";
d.posts.forEach(function(p) {
text += "<a href='" + p.link + "'>" + p.title + "</a>";
});
tooltip.html(text);

var cellBox = this.getBoundingClientRect();
tooltip.style("left", (cellBox.left + cellBox.width / 2) + "px")
.style("top", (cellBox.top - 8 + window.scrollY) + "px");
}
})
.on("mouseout", function() {
// 离开色块时,给用户留出 250 毫秒的移动缓冲时间
hideTimeout = setTimeout(function() {
tooltip.transition().duration(200).style("opacity", 0).on("end", function() {
d3.select(this).style("visibility", "hidden");
});
}, 250);
});

// 为 Tooltip 本身增加事件,确保鼠标悬停在框内时不消失
tooltip.on("mouseover", function() {
clearTimeout(hideTimeout);
d3.select(this).style("opacity", 1).style("visibility", "visible");
})
.on("mouseout", function() {
// 鼠标离开悬浮框,彻底隐藏
hideTimeout = setTimeout(function() {
tooltip.transition().duration(200).style("opacity", 0).on("end", function() {
d3.select(this).style("visibility", "hidden");
});
}, 250);
});

function updateCellStyles() {
var isDarkTheme = document.querySelector('html').dataset.dark === 'true';
legendGroup.selectAll("rect").style("fill", function(d, i) {
return getFillColor(i, isDarkTheme);
});
cells.style("fill", function(d) {
return getFillColor(d.count, isDarkTheme);
});

// 暗黑模式下,Tooltip 内的分割线也要适应变暗
if(isDarkTheme) {
document.querySelectorAll('#tooltip div').forEach(el => el.style.borderBottom = '1px solid #444');
} else {
document.querySelectorAll('#tooltip div').forEach(el => el.style.borderBottom = '1px solid #eee');
}
}

updateCellStyles();

document.body.addEventListener("themechange", function () {
updateCellStyles();
});
});