当前的终端用户界面(TUI)经常因为边框(即划分面板和按钮的线条)被视为文本字符而牺牲大量的屏幕空间。由于每个字符都占据一个完整的单元格,嵌套布局很容易仅为了装饰就损失六到八行垂直空间。一种目前在 Ghostty 分支中实现的全新终端协议解决了这一问题,它将边框视为类似于背景颜色的单元格属性,允许它们位于单元格边缘而无需占用任何字符槽位。
框线字符的局限性
标准的终端边框依赖于 Unicode 框线块。由于这些是文本字形,它们必须占据网格的一整行或一整列。这造成了一种“填充”效果,导致文本无法紧贴面板边缘,且随着嵌套边框数量的增加,复杂布局会变得拥挤。

这张图片展示了使用框线字符的当前标准,这些字符占据完整的单元格并造成填充问题。
除了空间限制外,框线字符在美学和功能上也有局限性。例如,它们无法轻松地在两个包含内容的单元格之间创建线条,这使得在不增加额外填充的情况下为文本字段添加下划线变得困难。此外,从 TUI 中复制文本时,这些框线字符经常作为“垃圾”数据出现在剪贴板中。
“边框是问题的一部分。它们必须占用空间,因此它们成为了人们想要构建的面板的一部分。”
— Terminal borders that don't cost a cell | Joshka.net
单元格边框的机制
新协议将单元格边框视为单元格本身的属性。每个单元格可以携带多达十二条线段,代表其四个边缘的两个半部分以及从中心出发的四个臂。终端负责在网格上渲染这些线段,允许特定的颜色、宽度以及实线、双线、虚线或点线等样式。

这张图片展示了新协议在复杂的平铺窗口管理器布局中的实际应用。
为了处理重叠线条,该协议使用优先级系统。当两个相邻单元格共享一个边缘时,终端根据优先级分数、然后是笔画宽度,最后是位置(右侧或底部)来决定绘制哪条线。这确保了即使在靠近其他面板时,聚焦面板仍能保持完整的轮廓。
该协议使用设备控制字符串(DCS)格式。应用程序可以设置一个“画笔”——类似于背景颜色——来定义后续文本的边框样式,或者使用矩形命令将边框应用于现有单元格。如果终端不支持该协议,应用程序将回退到标准的框线字符。

此视觉效果突出了机制部分提到的美学和功能改进,如圆角和下划线。
权衡与实现
在 Ghostty 终端模拟器中的实现涉及大约 1,650 行代码。每个单元格被分配一个 16 位边框 ID,指向共享的边框集表。虽然这允许高效渲染,但如果利用了每一个可能的 ID,该表可能会占用高达 10-15 MB 的内存,尽管大多数 TUI 只需要少数几个不同的集合。
HN Digest 根据 joshka.net 报告的数据绘制的图表;数值按原文显示。
渲染性能因样式而异;虽然直线渲染效率很高,但圆角需要更多的处理,因为它们被绘制成一排小的四边形以实现抗锯齿效果。作者指出,虽然该协议解决了空间问题,但尚未完美解决“半单元格”填充问题,这仍是一个独立的架构挑战。

这张图片说明了该协议可以作为单元格属性渲染的特定样式和线条类型。
Hacker News 读者的反应
- 替代方案: 一位评论者建议在更多终端模拟器中实现覆盖模式(overstrike mode)将是在现有文本单元格上绘制边框字符更好的方法。 评论 ↗
- 质疑: 一位评论者质疑作者为什么不追求更宏大的目标,即创建一个完整的、信息密集的 UI 框架,而仅仅是修改终端协议来赢回屏幕空间。 评论 ↗
- 质疑: 另一位用户对该协议为何无法在 tmux 等多路复用器上运行表示怀疑,并指出如果它是标准的单元格属性,它应该按预期渲染。 评论 ↗
- 替代方案: 一些用户建议完全放弃终端黑科技,通过在 SSH 上提供 HTML 来让浏览器渲染真正的 UI。 评论 ↗
以上为 Hacker News 讨论中观点的摘要,并非经过核实的事实。 阅读完整讨论 ↗