Featured image of post 把两套封面生成器合并成一个:我如何用 LynxCard 统一博客封面管线并加上拖拽排版

把两套封面生成器合并成一个:我如何用 LynxCard 统一博客封面管线并加上拖拽排版

博客原来有两套封面生成:资讯管线走 LynxCard 的横版信息卡,手写文章走一个独立的 HTML+Playwright 脚本。两套模板、两套渲染逻辑,改一处要顾两边。这次把深色终端风脚本移植成 LynxCard 的 dark_terminal 模板,并给 WebUI 加了画布拖拽排版。

新发布

博客的封面图一直是两条线并行:AI 资讯类由 feed_pipeline 自动调 LynxCard 服务出「横版信息卡」,而我手写的深度文章用一个独立脚本 gen_cover_dark_terminal.py 出「深蓝终端风」封面——HTML 模板 + Playwright 截图 + PIL 量化。两套各自为政,样式、参数、渲染逻辑完全不同。最近我终于把它们合并了,顺带解决了「想微调排版得改代码」的老大难。

为什么要合并

两套并存的问题在改样式时暴露得很彻底。前几天修「封面底部留白过大」,我得先改 gen_cover_dark_terminal.py 的 HTML 模板、删掉「底部 40% 留空」的旧规则,再去核对 LynxCard 那套不受影响。同一种「封面」的概念,散落在两个仓库、两种模板语言(一个 HTML/CSS、一个 SVG 占位符)、两套参数体系里。每次想统一调整,都得在两个地方各做一遍,还得记着哪边是什么逻辑。

更麻烦的是排版。深色终端风的封面,标题、副标题、命令行、品牌这几个元素的位置是写死在 CSS 里的像素值。想挪一下标题往下一点?得改代码、重新渲染、截图看效果,不行再调。一个纯视觉的微调,要走的却是完整的改代码流程。

合并方案:模板归一 + 坐标参数化

合并的核心是把深色终端风做成 LynxCard 的一个新模板 dark_terminal,和原有的竖版语录卡、横版信息卡并列。关键设计有两处。

一是模板用 SVG 占位符。 原脚本用 HTML 的绝对定位,我把它翻成 SVG:深蓝渐变背景、网格、青绿高亮、等宽琥珀命令行都保留,各元素包在 <g transform="translate({{X}}, {{Y}})"> 里,坐标做成占位符,渲染时由 Python 注入。

二是坐标可走 spec.layout_overrides 覆盖。 默认位置放在 LAYOUTS 表里,但每张卡的 spec 可以带一个 layout_overrides 字典覆盖任意元素的 x/y。这一步是后面拖拽排版能成立的前提——拖拽本质上就是往这个字典里写坐标。

拖拽排版:直接在画布上挪

光能参数化还不够,我要的是「所见即所得」地拖。LynxCard 本来就有个 FastAPI WebUI,我在它上面加了模板切换和画布拖拽:选「博客封面·深蓝终端」模板后,预览图上悬停任一元素会出现虚线框,直接拖动就能调位置,松手自动重渲染,坐标写进 layout_overrides 存进 spec。下次载入这张卡,模板和位置都完整恢复。

这个过程里踩了几个真实的坑:品牌字段最初做成了左对齐,挤在栏目眉旁边,改成右对齐才到了右上角;标题两行时,固定位置的副标题和命令行会被大字号标题撞上,改成按标题实际高度动态顺移才错开。这些都是只靠看代码发现不了、必须真渲染出来盯着像素看才暴露的问题。

收尾:一处入口,废弃旧脚本

合并后,做深色终端封面只剩一个入口:代码里调 lynxcard_client.render_dark_terminal_cover(),或者开 WebUI 选模板拖拽。旧的 gen_cover_dark_terminal.py 还在仓库里,但已经标注废弃、运行时会打印警告,记忆库里那条「两条路线并存」的记录也更新成了「已合并」。一套服务、一套 WebUI、一套 spec 存储,封面这件事算是收拢了。