谁说终端只能凑合用?一位开发者把 Pager 重新做了一遍,还顺手重写了 TUI 的文本体验

开发工具 2026年4月16日
谁说终端只能凑合用?一位开发者把 Pager 重新做了一遍,还顺手重写了 TUI 的文本体验
一名 Go 开发者把自己在 Kubernetes 日志工具和 Nomad 管理工具中反复打磨的文本视口组件,抽出来做成了一个新的终端 pager。表面看,这只是又一个“less 替代品”,但往深了看,它击中了开发者工作流里一个长期被忽视的问题:终端里的文本交互,早该从“能看”升级到“好用”。

终端世界里,最被低估的基础设施

如果你每天都泡在命令行里,就会知道开发者真正花时间盯着看的,不一定是代码本身,而是海量文本:日志、配置、git diff、数据库结果、man 手册、README,甚至现在还多了 AI agent 吐出来的一长串执行过程。问题是,这些信息往往不是“读一眼就走”,而是要翻、要搜、要定位、要比对。

于是,pager 这种老派工具一直活得很好。less 是其中最经典的那个,几十年过去,依然是大量 Unix-like 系统里的默认选择。它强大、稳定、几乎无处不在,但也正因为它太老、太标准,以至于很多人默认接受了它的使用方式:能翻页、能搜索、能退出,就算完成任务。至于 Unicode 对齐、复杂日志筛选、局部组件化复用,这些事,less 从来不是为它们设计的。

最近,开发者 Robin Ovitch 写了一篇颇有意思的文章,讲他如何把自己在多个终端应用里反复使用的“文本视口”组件抽离出来,用 Go 做成一个可复用库 viewport,并进一步构建了自己的终端 pager——lore。这件事表面上很小,像是程序员又做了一个给程序员用的小玩具;但在我看来,它其实折射出一个更大的趋势:今天的终端,已经不只是“黑底白字的输出窗口”,而是在悄悄进化成开发者最稳定、最高频的工作界面。

从“翻日志”到“造组件”,TUI 的核心其实就是迷你版 pager

Robin 的出发点并不是“我想挑战 less 的江湖地位”,而是一个更务实的需求。他开发了 kl——一个用来查看 Kubernetes 日志的终端应用,也做了 wander——一个面向 Nomad 的工具。这类 TUI(Terminal User Interface,终端交互应用)看起来像是“命令行版 GUI”,但本质上更克制:没有花里胡哨的按钮,没有鼠标优先的操作逻辑,整个交互建立在键盘、焦点切换、文本区域和列表选择之上。

在这种产品里,最重要的模块往往不是菜单栏,也不是状态栏,而是那个负责显示大量文本、支持滚动、搜索、换行、定位的窗口。Robin 说得很直白:很多 TUI 最核心的组件,其实就是“迷你终端 pager”。这话我非常认同。你看 Kubernetes 日志面板、左侧资源树、全屏 JSON 详情页,本质上都在做同一件事——把大量文本塞进一个有限区域里,再让用户高效地找到自己要的那一行。

这也是为什么他把共用能力抽成了 viewport。这不是简单的 UI 封装,而是在终端约束下,把文本导航这件事重新产品化。它支持动态尺寸、垂直滚动、横向平移、自动换行、搜索与匹配跳转、百分比进度提示、ANSI 颜色代码解析、选择条目,甚至还要处理 Unicode 字符宽度。说白了,这不是做一个文本框,而是在做一个“理解终端文本复杂性”的引擎。

真正难的,不是滚动,而是把 Unicode 和日志都伺候明白

很多人低估了终端文本处理的难度,因为在肉眼看来,一行字就是一行字。但对程序来说,终端里的“一个字符”远没有那么简单。文章里举了个很典型的例子:一个闪光 emoji“✨”在终端里占两格宽度,而有些看起来挺宽的字符却只占一格;同样一个“é”,既可以是一个单独字符,也可以由字母 e 加一个组合重音符拼出来,视觉上一样,编码上完全不同。

这类问题听起来学术,实际上非常影响体验。只要字符宽度算错一点,搜索高亮就会歪,横向滚动会错位,光标选中也会飘。你在日志里找错误时,看到整页排版像喝高了一样左右乱晃,心情会瞬间变差。Robin 的做法是把字符串映射成终端网格意义上的“宽度”结构:从字节到码点,再到字形簇和最终显示宽度,逐层处理。这种脏活累活,用户永远不会特意表扬,但一旦没做好,用户会第一时间骂娘。

更有意思的是,他还把不同文本场景拆成了多个 Item 抽象。普通单行文本、带前缀的动态内容、多行内容,都可以用同一套接口处理。比如 Kubernetes 日志常常需要加时间戳、容器名、行号,如果每次都重建整段字符串,性能会很难看;而通过组合型的数据结构,就能避免重复计算。这种设计思路很工程化,也很符合现在开发工具的发展方向:不是拼命加功能,而是先把底层文本模型做扎实。

这让我想到一个老话题:为什么很多开发者宁愿待在终端,也不愿切去图形界面?原因不只是“终端酷”,而是终端一旦好用,信息密度和操作效率都很惊人。前提是它不能在最基础的文本处理上掉链子。今天大家对 AI 编程工具、云原生运维平台投入巨大注意力,但真正决定效率下限的,往往还是这种看上去不够性感的基础组件。

比 less 更现代,但“替代 less”也没那么简单

Robin 最终把这套组件做成了 lore,并且已经作为自己的日常 pager 使用。从功能思路看,它明显是冲着现代开发者工作流去的:支持精确搜索、正则搜索、大小写不敏感搜索,用单键切换;搜索历史保存在内存里,可以上下翻;还能在“只看命中项”和“保留上下文”之间切换。这对查日志尤其有用——你不只是要看到 ERROR,还想知道 ERROR 前后 5 秒发生了什么。

如果把它和 lessbatmostdelta 这些工具放在一起看,lore 的定位其实很清楚。它不是要靠历史包袱取胜,而是要靠交互灵活性和组件复用能力赢得新一代 TUI 开发者。特别是 Bubble Tea 这类 Go 生态 TUI 框架近年来很活跃,大家越来越愿意在终端里做出接近原生应用体验的产品。这个时候,一个现成、性能尚可、能处理 ANSI 和 Unicode 的视口组件,价值会比“一个新的 pager 二进制”更大。

但我也得泼一点冷水。less 之所以难以被替代,不只是因为它功能够用,而是因为它已经深度嵌入 Unix 工作流:所有人都知道怎么用它,几乎所有工具都默认兼容它,配置方式稳定,行为预期明确。新 pager 想杀出重围,技术只是第一步,真正的门槛是习惯和生态。开发者不会因为你多了几个快捷键就立刻换工具,除非你在高频场景里形成压倒性的优势。

换句话说,lore 更像是一个很强的“产品雏形”和“开发组件平台”,而不是明天就能让 less 退休的终局答案。它的意义在于证明:终端 pager 不是一个已经结束创新的话题,文本浏览这件事还远没被做透。

在 AI 时代,终端文本体验反而变得更重要了

这件事为什么偏偏在今天值得聊?因为开发者面对的文本洪流,比几年前只多不少。云原生环境让日志量爆炸,微服务让排障链路更长,基础设施即代码让配置文件更复杂,而 AI agent 又开始在终端里输出大量中间步骤、思考痕迹和命令执行结果。终端没有消失,反而成了这些复杂系统最终汇流的地方。

也正因为如此,文本导航能力正在从“附属体验”变成核心生产力。过去我们觉得搜索、过滤、跳转只是查看输出时的锦上添花,但在今天,它已经接近“人能否驾驭复杂系统”的基础门槛。一个优秀的 pager,某种意义上是在帮你压缩认知负担。它不会直接修复故障,但能让你更快看到故障;它不会替你理解系统,但能让系统别先把你看晕。

我尤其喜欢 Robin 文里那种“从自己每天使用的痛点出发,顺手把抽象层做对”的气质。这和很多为了造轮子而造轮子的开源项目不太一样。它先服务具体问题,再沉淀成可复用能力,这种路径通常更容易长出真正有生命力的工具。

当然,一个值得思考的问题也摆在这里:当终端应用越来越像应用、越来越追求丰富交互时,它会不会反过来失去终端原本的简洁?今天的 TUI 生态确实很热闹,但热闹背后也有风险——快捷键越来越多、状态越来越复杂、学习成本悄悄上升。不是所有人都想在终端里再学一套“桌面软件逻辑”。所以,现代 pager 和 TUI 的平衡点,恐怕不是功能越多越好,而是要在克制中做出更顺手的默认体验。

从这个角度看,viewportlore 的真正价值,不只是“功能更丰富”,而是它们在提醒整个开发工具社区:终端的未来,不一定是更花哨,而应该是更体贴。哪怕只是一个翻页器,只要把细节做到位,也能让人感到一种久违的尊重——原来每天看几万行文本的人,也值得拥有好一点的工具。

Summary: 我倾向于把 `lore` 和它背后的 `viewport` 看作一个信号,而不是一次单点发布:终端工具的创新,正在从“多加几个命令”转向“重做最基础的交互体验”。`less` 短期内当然不会被替代,但围绕文本浏览、日志筛选和终端内搜索的现代化改造,会越来越多。接下来,谁能把这类组件做进更多 TUI、CLI 甚至 AI agent 工作流里,谁就更可能定义下一代开发者的默认操作方式。
终端 pagerTUIlessviewportloreGoRobin Ovitch文本交互体验KubernetesNomad