这不是
git log,是一份有点像日记的迭代记录。
2026-07
去看了眼 herdr.dev,一个终端 AI agent 多路复用器的官网——它走的是"终端配色文化"路线,Catppuccin、Tokyo Night、Dracula、Nord、Gruvbox 随便切,跟这个博客本来的 IDE 分屏、终端 prompt 调性很搭。抄了个作业,加了 Dracula、Nord、Tokyo Night(暗色)和 Gruvbox Light(亮色),主题菜单从 3 个选项变成 7 个。
切主题的时候顺手修了几个之前没注意到的小毛刺:
- 底部状态栏那个
[latte]按钮没有固定宽度,主题名从latte切到gruvbox light按钮跟着变宽,popover 的锚点也跟着跳。按最长的主题名(gruvbox light,13 个字符)算出一个固定的ch宽度焊死。 - 顶部导航 tab 选中态原来用
::before { content: '| ' }插一个竖线字符当指示器,只有选中的 tab 会多出这两个字符宽度,切页面时整排 tab 跟着挪位置。改成绝对定位的一条 2px 竖线叠在 tab 上,不参与排版,只是非选中态时透明——彻底不占布局空间,而不是"占位但隐身"。 - 主题菜单选中一个主题后没有自动收起,点开一次要再点别处才会关掉;补了
setOpen(false)。 - 有几率一进页面主题菜单就是展开状态(没点过按钮);给 popover 挂载后补了一次显式
hidePopover()兜底。
之前: 0:主页 1:文章 ← 切到"文章",因为多了"| ",后面的 tab 全部右移
之后: 0:主页┃ 1:文章 ← 竖线叠在色块上,不吃版面,其余 tab 纹丝不动
之前「按模型 top5」的排行是按 6 个月 token 总量排的,结果 gpt-5.2 只打了 851 次请求就冲到第一——单次平均 7 万多 token,明显是几次长上下文批处理,不是日常用量。换成按调用次数排序,Claude 系列立刻回到该在的位置。
顺手把整个用量维度从「当天按小时」放大到「近30天按天」:上游 /api/data/self 本来就限制单次最多查30天,原来「近1天 + 6×30天」拼7次请求的写法直接简化成1次。
新增一张手写 SVG 折线图看多模型趋势(中点二次贝塞尔平滑,没上任何图表库),和模型调用占比环形图,两个图表共用一份图例。图例、折线、饼图、原来的柱状图全部联动——hover 任意一处,其他地方同步高亮同一个模型,饼图中心文字也会切换成当前 hover 模型的次数和占比。
图例 ●claude-sonnet-5 ← hover
│
├─→ 柱状图对应色块变亮,其余变暗
├─→ 折线图对应曲线加粗,其余变暗
└─→ 饼图对应扇区加粗,中心文字切换成该模型数据
点开一篇文章,那行标题不再是凭空消失重来——它会平滑地移动、缩放到左上角的编辑器 tab 位置。把「打开一个文件」这件事讲清楚了。
用 React <ViewTransition> 给列表项标题和详情页标题挂上同一个 name,浏览器负责 morph。非点击项 exit="none",不跟着乱动,只随页面淡出。
/blog blog/headless-browser…
┌───────────────────────┐ ┌───────────────────────┐
│ > 无头浏览器去点按钮 │ → │ 无头浏览器去点按钮 0 │ ← tab
│ > 环形缓冲区 │ │───────────────────────│
│ > 计算机网络应用层 │ │ 那行标题飞了上来 │
└───────────────────────┘ └───────────────────────┘
折腾点:一开始用手写 CSS view-transition-name,结果 React 接管转场后不认,只剩淡入。改成显式 <ViewTransition> 边界才 morph 起来。
IDE 里 hover 一个符号会弹出定义摘要。把这个隐喻搬到正文内链上:悬停站内文章链接,弹出目标文章的标题 / 摘要 / 日期·阅读时长小卡片,移开即隐藏,点进去立即消失。
同时用 Speculation Rules API 在「即将点击」时后台预渲染 /blog/*,让打开近乎瞬时。都是渐进增强,不支持的浏览器忽略。
…详见 [环形缓冲区](/blog/ring-buffer) 那篇…
│ hover 140ms
▼
┌──────────────────────────────┐
│ 环形缓冲区 │
│ 无锁队列、位运算取模、Go 实现…│
│ 2026-03-25 · 8 min read │
└──────────────────────────────┘
侧栏目录从静态列表升级成会跟着阅读位置走的大纲:左侧一条竖轨,当前读到的 section 那一段亮蓝色,像编辑器左侧的 gutter。点击平滑跳转。
用 IntersectionObserver(以视口为参照),同时兼容「桌面 pane 内滚动」和「移动窗口滚动」两种容器。
目录
│ 背景
┃ 实现思路 ← 当前 section(亮蓝)
│ 位运算取模
│ 性能对比
│ 小结
切换页面时,中间内容区轻微淡入 + 上滑 4px,常驻的 Header / Footer / 桌面宠物 / 进度条纹丝不动。基于 React <ViewTransition>,只包裹路由内容区。
顺手把原来的主题圆形扩散限定到「仅主题切换」——否则每次翻页整个外壳都会被圆形擦除一遍。
Header ────────────── (不动)
┌────────────────────┐
│ 旧页 ░░░ → 新页 │ ← 只有这里淡入上滑
└────────────────────┘
Footer ────────────── (不动)
借这次改造把几个「零 JS、纯平台」的新能力铺上:
Popover API 底部主题下拉 → 原生 popover
自带 light-dismiss / Esc / 顶层渲染 / ARIA
@starting-style 移动端菜单进出场动画,纯 CSS,删掉了
+ allow-discrete 一个从没被 import 的 headless 死组件
animation-timeline 文章顶部滚动阅读进度条(scroll())
field-sizing 评论框随输入自动长高
text-wrap 标题 balance、正文 pretty,消灭孤行字
之前只有主题切换尊重 prefers-reduced-motion,其余动画照跑。这次一次性收口:
- 一个定向
reduce块把所有装饰性@keyframes(fade-up / bar-reveal / 宠物系列…)、跨页与共享 morph 全部降为瞬完终态,平滑滚动降为auto。 - Tabs 补齐
tablist/tab/tabpanel语义 +aria-selected+ 左右方向键;移动菜单按钮补aria-expanded/aria-controls。 Image默认sizes防布局抖动;头像alt从 "avatar" 改成作者名。- 删掉两个从未被引用的死组件(
MobileNav/ThemeSwitch)。
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*) { animation: none !important }
@keyframes fade-up { from, to { opacity: 1; transform: none } }
}
阅读量接口在上游不可用时(缺凭证、限流、token 过期)会因为在非数组上 .find 直接抛 500。改成:拿不到有效数组就返回 null,前端据此隐藏指标位,而不是刷红或误显示「0 阅读」。
上游 OK → { views: 1234 } 显示 "1,234 views"
文章真 0 阅读 → { views: 0 } 显示 "0 views"
上游挂了 → { views: null } ← 组件隐藏,不再 500
2026-04
任意页面输入上上下下左右左右 BA,当前桌面宠物立即升为传说级闪光变体,触发破壳动画并弹出专属台词。
↑ ↑ ↓ ↓ ← → ← → B A
★★★★★ LEGENDARY ★★★★★
[~~~~~]
/\_/\
( ✦ ✦ ) ← 闪光变体激活
( ♥ )
(*)_(*)
> 出金了!
秘籍生效不会持久化,刷新后恢复正常概率——彩蛋猎人专属。
切换主题时,从底部状态栏主题按钮位置向外以圆形扩散过渡,取代瞬间变色。基于 View Transitions API,不支持的浏览器自动降级,prefers-reduced-motion 时也不触发。
@keyframes theme-reveal {
from {
clip-path: circle(0% at 95% 98%);
}
to {
clip-path: circle(150% at 95% 98%);
}
}
切换 latte → mocha → old-hope 时效果尤其明显。
悬停某个小时柱:浮窗显示该时段各模型 token 明细(按用量排序),其他柱半透明。当前小时列用黄色边框标注,x 轴标签同步高亮。
hover 14:00
┌─ 14:00 · 42,830 tok ──────────┐
│ ▌ claude-opus-4-5 28,100 │
│ ▌ claude-sonnet-4 11,200 │
│ ▌ claude-haiku-3 3,530 │
└────────────────────────────────┘
... 12 13 [14] 15 16 ...
▲ 黄色 = 当前时刻
修了两个潜伏已久的问题:
--c-teal/--c-sapphire在 Token 图表里被引用但三套主题都没有定义,颜色一直 fallback 到currentColor。补全了 latte / mocha / old-hope 三套值。IinaTokensPage、CodeStatsWidget、TerminalPet各自内联了几乎相同的@keyframes。全部抽离到css/tailwind.css,同时新增--anim-fast/std/slow/ease全局动画变量。
/* 之前:三个组件各自内联 */
@keyframes cs-bar { ... } /* CodeStats */
@keyframes token-bar { ... } /* Tokens */
/* 现在:统一为 */
@keyframes bar-reveal { from { clip-path: inset(0 100% 0 0) } ... }
@keyframes fade-up { from { opacity:0; transform:translateY(4px) } ... }
@keyframes bar-grow { from { transform: scaleY(0) } ... }
文章顶部加入三项信息,布局改为左右分列:左侧日期 + 阅读时间(contentlayer 构建时计算),右侧实时 PV 来自 Umami API。
2026年4月13日 5 min read 1,234 views
←──────────────────────────────────────────────────→
同期修了 Umami v3 接口兼容问题:type=url 已废弃改为 type=path,在线人数字段 x 改为 visitors。密码重置也顺便做了。
命令面板里的 /search 一直只是跳转到 /blog,算是占位符。这次把它接入了 kbar,执行后会弹出全文搜索框。
根因是 Footer 不在 SearchProvider 内,导致 useKBar() 拿不到 context,Ctrl+K 也跟着失效——把 Footer 移进 SearchProvider 后一起解决了。
> /search█
┌─────────────────────────────┐
│ 🔍 搜索文章… │
│ ────────────────────────── │
│ 环形缓冲区 │
│ 计算机网络应用层 │
└─────────────────────────────┘
接入 Umami 做轻量行为分析。不收集个人信息,只是想知道有没有人真的用了那些快捷键。
track("nav_click") ← 导航点击
track("theme_switch") ← 主题切换
track("lang_switch") ← 语言切换
track("comment_submit") ← 评论发送
track("tag_click") ← 标签跳转
路由跳转时顶部出现一条像素风格进度条,比 spinner 更有终端感。
█▓▒░░░░░░░░░░░░░░░░░░░░ loading…
██████▓▒░░░░░░░░░░░░░░░ loading…
████████████▓▒░░░░░░░░░ loading…
███████████████████████ done ✓
基于 GitHub Discussions 的评论系统,整个设计围绕 git log 的视觉语言展开。
commit a3f9c12
Author: visitor <via OAuth>
Date: 2026-04-05
这篇讲环形缓冲区的文章写得很清楚
1 file changed, 1 insertion(+)
[git push] ← 就是这个按钮
主页接入 Code::Stats,实时显示各语言编码时长。
$ code-stats --user jimmy
TypeScript ████████████░░░░ 62% 1,204 xp
Go ██████░░░░░░░░░░ 31% 612 xp
CSS █░░░░░░░░░░░░░░░ 7% 134 xp
移植并重写了 RingBufferViz 和 GoGateBufferViz 两个交互组件。GoGateBufferViz 支持手动 read / write,可以一步步看位运算取模怎么工作。写这个比写文章花时间多了。
┌───┬───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ │ │
└───┴───┴───┴───┴───┴───┘
▲ ▲
read write
capacity = 6 (must be power of 2)
mask = 5 (capacity - 1)
index = pos & mask ← no division
2026-03
整个博客的视觉基础。用 CSS 自定义属性替换默认 Tailwind 色板,三套主题不跟随系统。
Latte Mocha An Old Hope
────── ────── ───────────
● #4c4f ● #89b4 ● #e5cd52 blue
● #1e66 ● #cba6 ● #c678dd mauve
● #40a0 ● #a6e3 ● #98c379 green
● #d20f ● #f38b ● #e06c75 red
没用任何外部 i18n 库,自己写了一个轻量的 LangProvider + useLang() hook,localStorage 持久化。约 35 个 UI 字符串都有完整中英翻译。
const { t, lang, setLang } = useLang()
t.slashBlog // "博客" | "blog"
t.groupPages // "页面" | "pages"
t.noMatch // "无匹配" | "no match"
最核心的视觉改动。所有页面改为双栏分屏,文章列表用 Unix 文件权限格式渲染。
┌─────────────────────┬──────────────┐
│ $ ls -la ~/blog │ tags/ │
│ │ ────────── │
│ -rw-r--r-- jimmy │ #meta 2 │
│ 2026-04-11 │ #go 1 │
│ 环形缓冲区… │ #network 1 │
│ │ │
│ -rw-r--r-- jimmy │ │
│ 2026-03-20 │ │
│ 计算机网络… │ │
└─────────────────────┴──────────────┘
底部状态栏里的命令面板。按 / 激活,输入命令跳转页面或切换主题。
pages
| /home ~/
| /blog blog/
| /projects projects/
| /about about/
actions
| /search grep
| /theme toggle
↑↓ nav ↵ select esc close
> /█
右下角随机生成的 ASCII 小宠物,5 种稀有度、8 种帽子、5 项属性。传说级有概率出现闪光变体,孵化时有破壳动画。
★ LEGENDARY ★
[~~~~~]
/\_/\
( ✧ ✧ ) ← 闪光变体
( ♥ )
(*)_(*)
DEBUGGING ████████░░ 80
PATIENCE ███░░░░░░░ 30
CHAOS █████████░ 90
WISDOM ██████░░░░ 60
SNARK ████████░░ 80
没有鼠标也能完整操作整个站点。
g → h go home j / k prev / next post
g → b go blog [ / ] prev / next page
g → p go projects n / p older / newer
g → a go about u back to list
g → c go contact ? show this help
/ Ctrl+K command palette
关于页面模拟 nvidia-smi 的终端输出格式。
+-------------------------------------------+
| Jimmy v26.03 sleeprhino.com |
+-------------------------------------------+
| Skills Utilization |
| TypeScript ████████████░ 92% |
| Go ████████░░░░░ 65% |
| React ██████░░░░░░░ 50% |
+-------------------------------------------+
| Processes |
| PID Name Status MEM |
| 001 Adam Blog running ~420MB |
| 002 GoGate shipped ~80MB |
+-------------------------------------------+
主页通过 Lanyard WebSocket 显示 Discord 在线状态和当前活动,状态实时更新。
● online jimmy#0000
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎵 Listening to Spotify
Nightmare — Set It Off
00:53 ───────●────── 03:12
推送即部署,不用手动操作服务器。
git push origin main
│
▼
GitHub Actions
│ checkout → yarn build → docker build
▼
SSH → VPS
│ docker stop → docker run
▼
✓ deployed (~3 min)