为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞

如果你对我的 Code Agent项目感兴趣,可以看这里:

Github Repo: Memo Code - Github

站点:Memo Web Site

大概四年前,我刚接触编程。学的是 C 语言,第一个程序当然是 hello world。

很简单,几行就写完。run 一下,弹出来一个 terminal(我已经忘了当时用的是什么:cmd?PowerShell?反正不重要),然后打印了一行:

"hello, world!"

从那以后,在我还没接触前端之前,我写的所有程序几乎都靠终端完成输入输出:猜数字、九九乘法表,再到后来刷算法,基本就是------写、跑、看终端、改、再跑。

那时候倒也没觉得有什么问题,只是偶尔会突然有点空虚: 难道以后我工作的成果,就是一直对着这个黑框框吗?

第一种认知:终端就是编程的全部

一开始我对"程序结果形态"的理解非常单一:

输入 → 运行 → 终端输出

终端就是一切:日志、交互、结果展示,全在那儿。 它很直接,很原始,也很"学生气"。

第二种认知:页面才是"程序结果"的另一种世界

后来我接触了前端。直到靠前端拿到第一份实习、第一份工作,我才对"程序结果形态"形成了第二种认知:

不仅仅是终端里几行日志,也可以是页面效果、动画、交互。 之后陆续做过小程序、App、桌面程序......那段时间里,终端更像"开发过程的工具",而不是"产品本体"。

也就是从那时候开始,我对 terminal 的误解更深了: 它好像就应该是黑框框 + 命令 + 日志,仅此而已。

第三种认知:原来 terminal 也可以玩得这么花

25 年下半年,自从 Gemini CLI 这类东西开始出现之后,我对"程序结果形态"有了第三种认知:

原来 terminal 也可以玩得这么花。

彩色输出、输入框、选择框、进度条......该有的都有。 当时我没怎么深入研究,只是隐约意识到:以前我对 terminal 的理解偏了,它并不等于"只能打印"。

现实把我拉回来了:写 Agent,界面到底做什么?

后面开始做值班,又不得不把 Linux 命令捡起来:从 pwd / cat / tail / find,到 vi / vim......慢慢也熟练了。

直到最近两个月,我开始认真做我的 code agent:memo github.com/minorcell/m...

等我写好了 MVP,写好了 runtime,写好了 toolrouter、tools 等;下一步突然被一个看起来很"产品"、但本质上很"工程"的问题卡住了:

界面到底做什么? 传统 Web 页面?还是终端交互?

如果做传统 Web UI,我其实很拿手:加一个 HTTP server 包,再来个 Web UI 包就够了。 但现实是,市面上大多数 code agent(比如 claude code cli、codex cli)都是从终端交互做起的。后续再补 VSCode 插件、桌面版,甚至浏览器插件。

题外话:这里也不得不感慨一下------原来我大前端确实挺"六"的:只要界面能画出来,基本都能做。也更坚定一个想法:AI 时代,大前端技术只会更普及。

最终,可能是"理所当然",也可能是对陌生技术栈的兴趣使然,我决定: memo code 的第一种产品形态,先做终端 CLI。

选 Ink:看起来都正常,直到输入框

调研开源的 Gemini CLI 时,我发现他们用的是 Ink(React for CLI)。我也就直接跟了:选 Ink。

一开始真的很顺:

  • 会话记录渲染没问题
  • slash 指令也能做
  • 封装组件库也舒服

似乎都挺好......直到我碰到最难的一块:输入框。

以前做 Web app:

  • 单行用 input
  • 多行用 textarea

天然、顺滑、毫无心理负担。

但在终端里,多行输入并不是默认就"应该支持"的体验。甚至 Ink 的 input 组件,也只有单行。

这时候你才会意识到:在终端里,"输入框"不是 UI 控件,它更像是一个小型编辑器。

我以为我解决了,结果只是解决了"最简单的部分"

我一开始尝试的方案很朴素,比如:

  • Shift + Enter 插入换行符

表面看起来能用了。 但很快更真实的问题出现了:粘贴文本。

粘贴一段文本时,你会遇到:

  • 显示残缺
  • 粘贴后光标位置不对
  • 输入状态偶尔乱跳

这时候我才明白:我不是在做"多行 input",我是在终端里硬写一个"半个 textarea"。

如果对照二八法则:掌握 20% 的技术,就能做出 80% 的功能。

但要把剩下 20% 做好,往往需要补齐另外 80% 的细节。

终端交互就是这样:你很快能做出一个"能用"的 CLI;但要做得像样,细节多到离谱。

于是我最后认真设计了一套方案(写在这个 issue 里): github.com/minorcell/m...

解决方案:在 Ink 里做一个"可控的多行编辑器内核"

我最后没有继续纠结"有没有更好的 input 组件",而是换了一个思路:

把多行输入当成一个小型编辑器来做。 在 Ink 的限制里,把"编辑状态"和"渲染"解耦,然后在输入事件层做适配。

整个方案我拆成三个核心模块。

编辑器状态管理层

我不再把输入框当成"一个字符串",而是当成一个状态机。

核心结构就是:

  • value: string(当前文本)
  • cursor: number(光标在文本中的位置)

听起来很简单,但一旦涉及多行、上下移动、终端折行,坑就开始密集出现:

  • 光标移动要能跨行
  • 上下键移动不能乱跳(要记住"我想待在哪一列")
  • Unicode 也得小心:emoji / 代理对如果按字符串下标移动,光标很容易卡在"半个字符"上
  • 所以要做 clamp,保证 cursor 永远落在合法边界

这一层的目标只有一个: 不管 Ink 怎么渲染,我内部都能稳定得到"当前文本是什么 + 光标在哪里"。

粘贴检测

真正让我没绷住的,其实是粘贴。

终端里粘贴一坨文本时,底层输入事件会被拆成很多个 keypress,然后 Ink / 渲染层每次都会触发更新。你会遇到一种非常诡异的现象:

你粘贴的是 A,但 UI 看起来像是 A 的碎片; 光标也像在"追不上输入",最后漂到一个你完全无法理解的位置。

所以我做了一个"粘贴 burst 检测":用启发式规则把粘贴从普通输入里识别出来,然后改成 缓冲 + 批量插入

  • 时间间隔规则(主机制):字符到达间隔 < 8ms 基本视为粘贴(人不可能这么快)
  • 字符数量规则(备用机制):连续字符 ≥ 16 时也按粘贴处理(对中文/emoji 路径更稳)
  • 识别到粘贴后进入状态机:pending → active → flush 先塞 buffer,等"粘贴结束"再一次性写入 value,避免每个字符都触发一轮复杂计算

这一步做完之后,"粘贴残缺 / 光标乱跳"基本从玄学变成可控问题了。

输入处理适配器:快捷键 + 换行策略 + 视觉换行

终端输入要像编辑器,光靠"插入字符"是不够的,你还得补齐肌肉记忆:

  • 支持常见快捷键(Ctrl+A / Ctrl+E / Ctrl+U / Ctrl+K / Ctrl+W 这类)
  • 换行与提交要分开:
    • Shift + Enter 永远插入新行
    • Enter 默认提交
    • 但如果处在粘贴期间(或粘贴后的短窗口期),Enter 当作插入新行
      • 防止用户"粘贴完顺手一回车"直接把消息提交出去了(这个真的很常见)

还有一个关键点:逻辑行 vs 视觉行分离

  • 逻辑行:真正的 \n
  • 视觉行:终端宽度导致的自动折行

编辑用逻辑行,展示按视觉行计算,这样长段落在不同宽度终端也能保持一致体验。

同时,视觉换行还要能响应终端 resize(不然窗口一变宽/变窄,光标又漂移)。

这一层本质上就是: 把终端输入从"能打字"推到"像个 textarea"。

念头通达,交给 codex 快速帮我实现了一个版本。

结果:我解决了剩下 20% 里最烦的 15%

这套方案不可能一把梭把所有边界问题抹平。 不同终端模拟器、不同输入法路径、极端大文本性能......仍然需要持续打磨。

但至少到这里,我觉得我把剩下 20% 里最难受、最影响体验的那 15% 解决掉了:

  • 多行输入稳定
  • 粘贴不再玄学
  • 光标不再乱飞
  • Enter / Shift+Enter 行为可控

收尾:终端不只是输入输出,它可以是简易版 Web App

memo 的这段实践,让我对终端交互有了更清晰的认知:

它不再只是我最开始学编程时那种"输入输出 + 打日志"。 它完全可以是简易版本的 Web App:有组件、有状态、有布局,甚至能长出一点"编辑器"的味道。

这感觉有点像当年最早的 HTML 刚出来时:朴素、克制,但足够表达。 而我现在做的,就是在这个黑框框里,把"能表达的东西"再往前推一点点。

如果你对我的 Code Agent项目感兴趣,可以看这里:

Github Repo: Memo Code - Github

站点:Memo Web Site

相关推荐
mCell4 小时前
如何零成本搭建个人站点
前端·程序员·github
恋猫de小郭5 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清5 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
deephub5 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
银烛木5 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_607076605 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声5 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易5 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得05 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化