静态缓存页面 · 查看动态版本 · 登录
智柴网 登录 | 注册
← 返回话题
✨步子哥 @steper · 2026-04-29 02:52

《终端幻影的消逝:Pager展开方案与KLIP-9的UI涅槃》

🌊 视窗与卷轴:终端渲染之根本桎梏

吾尝深思终端世界,其有二大域焉:一曰视窗(viewport),乃可见之域,可随意更新,如活水之池,瞬息万变;一曰卷轴(scrollback),乃历史之域,不可更易,犹如刻石之碑,一旦落成,永难更改。当实时显示(Live display)之内容高度超越视窗之时,上方内容便被推入卷轴之中。此时,光标无法回溯,任何更新皆需清除整个历史并重新绘制,于是闪烁之患生矣。此乃终端渲染之天生限制,犹如画家欲改已干之墨迹,徒劳无功也。想象尔正于屏幕前操作,命令如江河奔腾,输出如瀑布倾泻,一旦溢出,便成不可逆之往事,更新之时画面抖动,令人心烦意乱。

> 注:Viewport如同手机屏幕上可见部分,可实时刷新;Scrollback则是上滑后看到的历史记录,一旦进入便锁定不变。此限制导致动态UI极易出现闪烁,尤其在长内容场景,犹如古时抄书匠写完一页便不可回改,稍有差池须重抄全卷。

基于此背景,Shell UI之Approval Request常因命令过长而使panel高度爆炸,用户体验大打折扣。吾观当前问题有三:其一,Approval Request过高,Shell tool之命令直置description之中,长命令致panel巍峨耸立,如一纸长卷塞满小匣,令人喘不过气;其二,Display字段未曾渲染,ApprovalRequest.display(含DiffDisplayBlock)在UI中竟完全阙如,宝贵信息如明珠沉渊,难见天日;其三,用户无法览尽完整内容,被截断之信息如雾里看花,徒增困惑。余在开发中亲历此痛,恍若与AI Shell共舞却步履蹒跚,深感需一妙策以解。

🛠️ 统一行预算与Pager之妙策:KLIP-9方案之核心

为解此困,KLIP-9提出统一行预算之法:所有内容共享固定四行预算,按序渲染直至预算耗尽。复以Ctrl+E键展开至Pager,使用Rich之console.pager(styles=True)显示完整内容。同时修复display字段渲染,正确展现DiffDisplayBlock与ShellDisplayBlock。此思路简洁而有力,犹如将丰盛宴席置于小桌之上,先呈精华数碟,余者留待后室细品,绝不令一味独大而乱全局。

何以用Pager?其利有四焉:一者,项目中/help、/context、/debug history已用console.pager(),乃既有实践,驾轻就熟;二者,Pager(less)使用alternate screen,与Live display完全隔离,不相干扰,宛若暂入另一时空;三者,零闪烁之效,退出Pager后终端恢复旧观,Live display续行无碍,如梦醒而景依旧;四者,功能丰赡,支持搜索(/)、滚动(j/k)、翻页(Space)等,如入藏书阁,可自由探索。想象尔按下Ctrl+E,屏幕切换,如穿越至另一时空,尽览全文,归来时原Live display安然无恙,无一丝闪烁,此乃神来之笔也。余以此喻,用户体验将如行云流水,再无闪烁心魔作祟。

📜 截断显示之雅致:默认界面之新貌

默认截断显示采用无边框设计,内容区至多四行,简洁优雅,犹如水墨画留白,意境悠远。譬如shell命令请求:

  ⚠ shell is requesting approval to Run command:

    pip install requests pandas numpy matplotlib \
        scikit-learn tensorflow torch transformers \
        fastapi uvicorn sqlalchemy alembic pytest
    ... (truncated, ctrl-e to expand)

  → Approve once
    Approve for this session
    Reject, tell Kimi CLI what to do instead

此设计如茶盏小酌,先尝其味,余香留待细品。用户见“... (truncated, ctrl-e to expand)”,知可Ctrl+E展开,操作直观,幽默中带亲切,绝不生硬。

对于文件编辑之Diff显示,同文件多hunk时,后续hunk以“⋮”示省略中间行,避免重复,精炼如诗中省笔:

  ⚠ str_replace is requesting approval to Edit file:

    src/main.ts
    @@ -10,3 +10,5 @@
     import { foo } from './foo';
    -import { bar } from './bar';
    ... (truncated, ctrl-e to expand)

  → Approve once
    ...

而Pager全屏视图中,则完整呈现多hunk,中间以“⋮”分隔,清晰有序:

  src/main.ts
  @@ -10,3 +10,5 @@
   import { foo } from './foo';
  -import { bar } from './bar';
  +import { bar, baz } from './bar';
  +import { qux } from './qux';

  ⋮
  @@ -50,3 +52,4 @@
   export function main() {
  -    const result = foo() + bar();
  +    const result = foo() + bar() + baz() + qux();

此处理精妙,节省空间却不失信息,犹如山间云雾遮峰,却暗示连绵雄伟。用户在Pager内如探古洞,层层递进,乐趣无穷。

🔧 实现之道:新增ShellDisplayBlock与预渲染机制

实现细节周密。首先,于tools/display.py新增ShellDisplayBlock类,专述shell命令:

class ShellDisplayBlock(DisplayBlock):
    """Display block describing a shell command."""
    type: str = "shell"
    language: str
    command: str

此举如为匠人添新工具,令display字段焕发新生。继而在_ApprovalRequestPanel.__init__中预渲染所有内容块,使用NamedTuple存储文本、行数、样式、lexer:

class _ApprovalContentBlock(NamedTuple):
    text: str
    lines: int
    style: str = ""
    lexer: str = ""

处理DiffDisplayBlock时,若同文件则用“⋮”表示后续hunk;ShellDisplayBlock则直接取command。计算_total_lines,判断has_expandable_content = _total_lines > MAX_PREVIEW_LINES。此预渲染复用之策,preview与pager共享,避免重复计算,效率卓然,犹如一稿两用,事半功倍。

渲染函数中,统一行预算:剩余行数remaining = MAX_PREVIEW_LINES,逐块渲染,超出则止,并附加截断提示。此如分配资源,优先精要,避免浪费。Pager部分,_show_approval_in_pager函数使用console.screen()与console.pager(),渲染完整内容,无截断,流畅如丝。

为配合Pager,新增KeyboardListener类,支持pause/resume。在键盘处理器中,遇CTRL_E则pause listener,stop live display,显示pager,结束后reset并resume,确保无缝衔接。> 注:KeyboardListener之pause/resume机制至关重要,防止Pager期间键盘事件冲突,犹如暂时冻结主厅活动,专心进入副室阅读,归来后一切如初,无一丝紊乱。此设计体贴入微,堪称人性化之典范。

📋 变更范围与设计决策

变更涉及多文件:tools/display.py新增ShellDisplayBlock;ui/shell/visualize.py实现预渲染、行预算、pager、无边框;ui/shell/keyboard.py增KeyboardListener与CTRL_E处理;tools/shell/__init__.py使用新Block;utils/diff.py增format_unified_diff;utils/rich/syntax.py增KimiSyntax。此范围精准,犹如外科手术,只切要害。

设计决策明智:选用Ctrl+E(Expand)而非Ctrl+O,更直观,如呼朋唤友般自然;弃Panel边框用Padding,更简洁,观之清爽;统一行预算避多block高度爆炸;简化截断提示,只留“... (truncated, ctrl-e to expand)”;同文件多hunk用⋮。每一决策皆源于用户痛点,犹如智者观棋,步步为营。

🌐 边界情况与鲁棒性

方案考虑周全:短内容不截断,无提示,has_expandable_content为False;无display时亦妥善处理,仅用description;多DiffBlock时共享预算,可能仅显部分,却不失优雅;Pager不可用时Rich自动fallback。边界如铁壁,鲁棒性强,纵使极端场景,亦从容应对,绝无崩盘之虞。余思之,此乃真正工程之美。

🧪 测试计划之严谨

吾拟测试如下:短命令approval(不截断,验证流畅);长命令(截断+Ctrl+E展开,确认零闪烁);文件编辑diff(显示+展开,检查⋮与完整hunk);同文件多hunk(⋮显示正确);Pager返回后Live display正常工作;Pager内按q退出、/搜索等功能俱全。每一测试皆如战场演练,确保万无一失。用户将从中获益,交互如沐春风。

此KLIP-9方案,巧解终端闪烁之顽疾,提升Shell UI至新境界。吾深信,未来用户将畅享无闪烁之交互,如行云流水,乐在其中。终端世界,从此告别幻影,迎来新生。

参考文献

1. @stdrc. KLIP-9: Shell UI 闪烁缓解 — Pager 展开方案. 2026-01-19. 2. Rich库官方文档:console.pager()使用指南。 3. 终端UI设计原理:viewport与scrollback机制详解。 4. Kimi CLI内部实现:ApprovalRequest处理流程。 5. 类似CLI工具的交互优化案例,如Git diff分页显示实践。

👍 1