把 GitHub 个人主页做成自定义样式并不稀奇,但有人把贡献图立体化成一座等距视角的城市,再把整个页面包装成赛博朋克控制台,事情就变得复杂了。这个项目的出发点是让贡献图不只是绿格子,而是能表达个人偏好的东西:喜欢大城市、霓虹灯、赛博朋克氛围,于是每一天的贡献量变成一栋楼的高度,贡献少的日子留作空地,活跃的日子立起高楼。
GitHub 的 README 不允许运行 JavaScript,script 标签会被去掉,style 标签也会被去掉,连内联属性都保不住。唯一能用的只有 Markdown 和少量 HTML 标签。真正的突破口在于 SVG 被当作图片嵌入时,内部可以包含 CSS 和动画。打字效果、名字的故障闪烁、光标闪动全部藏在 SVG 里,页面上看起来像界面的元素,其实都是图片。

不过图片模式有额外限制。外部资源无法加载,连字体都要嵌入每个 SVG,并且裁剪到只保留该图实际用到的字符。悬停和点击不会生效,链接只能挂在整张图片上,所以每个项目卡片和链接按钮都是独立文件。图片还会被 GitHub 缓存几分钟,调试时改了代码刷新却看到旧效果,容易误以为没改对。
最初每个区块各自成框,头部、简介、统计各占一个面板,单独看都不错,叠在一起却像被摔在地上的终端,缝隙明显。解决办法是画一个完整的大框架,再横向切成多条。只有顶部切片画上边缘和标题栏,只有底部切片画下边缘,中间每个切片只画左右两条边轨。项目卡片和链接按钮也是切片,宽度更窄,只在外侧画边轨。数下来,23 张图片拼成一个连续窗口。

让接缝消失靠几个细节。所有切片高度都是 40 像素的倍数,背景网格纹路跨过切口也不会错位。霓虹光晕从切片上方开始、下方结束,避免在边缘像坏掉的灯管一样断掉。浏览器默认在图片下方留出字母下伸部分的空间,用垂直对齐去掉它,缝隙就没了。

城市本身不是新东西,就是熟悉的贡献图:53 列周、7 行天,只是倾斜成等距视角并给每个方块高度。每栋楼只有三个形状:一个屋顶和两面墙。绘制顺序从后往前,近处的楼自然遮住远处的,不需要 3D 引擎,画家算法加排序列表就够了。
高度如果直接等于贡献数,会出现一座巨塔和一片平地,像停车场中间戳着帝国大厦。改用平方根之后,小贡献的日子被抬起来,大贡献的日子仍然明显更高。零贡献的日子完全不盖楼,留作空地,有些月份看起来像废弃街区,这反而和真实的活动记录一致。

夜间天际线需要灯光,每栋楼有随机分布的窗户,有的亮有的暗,少数会闪烁。再加上星星、月亮和一架闪着灯横穿天空的飞机,城市就有了生气。窗户位置用带种子的随机数生成,同样的数据、同样的种子,得到同样的城市,连窗户位置都不差。否则每天即使没有新贡献,图片也会变,仓库会多出毫无意义的提交。
颜色上,GitHub 的贡献图是绿色,绿色屋顶一看就让人联想到活跃度。但主页其余部分是霓虹蓝,绿色城市放在旁边像从别的网站搬来的。把两个版本并排看了几秒,蓝色胜出。

整个页面每天自动更新,跑在 GitHub 的服务器上,不依赖本地电脑是否开机。流程分四步,其中三步是小型 Python 脚本,用工作流串起来。最后一步会在没有变化时正常退出,只有出现新内容才提交,避免空提交污染历史。
数据来源主要是 GitHub 的 GraphQL API,一次请求就能拿到关注者、PR 数、加入年份、总星标和 fork 数、每个项目的星标数以及语言分布。REST API 要分很多次请求,翻页会翻到不耐烦。发送请求用普通 Python 即可,不需要 SDK。

有一个限制:贡献日历每次请求最多覆盖一年。想要全部年份和不因元旦断裂的连续天数,先查一次拿到活跃年份列表,再动态构造查询,用别名把每一年放进同一次请求。这样一次往返就能拿到从 2016 年至今的每日贡献。今年的贡献和总贡献都是求和,城市取最近 53 周,从周日开始,和 GitHub 自己的图对齐。连续天数的计算就是遍历这些天,其中有一个小细节:工作流在中午运行,如果不做判断,当天第一次提交之前连续天数会归零,看起来像断了。

DEV 的数据来自 Forem API,文章、反应和评论是公开的,浏览量和关注者需要 API 密钥放在请求头里。分页请求直到返回空列表为止,同一个响应还能拿到最新五篇文章用于写作区块。
API 会出错,限流、超时、随机 502 都可能出现。如果主页因为一次抽风就显示零星标、零贡献,比没有统计还糟糕。所以脚本不会从零开始,而是先加载昨天的 JSON,只覆盖成功获取到的部分。GitHub 挂了,DEV 的数据还能更新。

回头看,这个项目把简单的自定义主页推到了很远的地方。它受限于平台规则,却也在规则内找到了足够的空间。是否值得,取决于你有多想让自己每天打开主页时看到一座会呼吸的城市。
把 GitHub 个人主页做成自定义样式并不稀奇,但有人把贡献图立体化成一座等距视角的城市,再把整个页面包装成赛博朋克控制台,事情就变得复杂了。这个项目的出发点是让贡献图不只是绿格子,而是能表达个人偏好的东西:喜欢大城市、霓虹灯、赛博朋克氛围,于是每一天的贡献量变成一栋楼的高度,贡献少的日子留作空地,活跃的日子立起高楼。
GitHub 的 README 不允许运行 JavaScript,scrip
浙公网安备 33010602011771号