Skip to content

中文输入法输入框位置错误bug #1127

Description

@fqhhy

What happened

Bug 分析报告
架构背景
npm 的 freebuff 包只是个启动器(launcher.js 负责下载 Bun 二进制到 ~/.config/manicode/freebuff.exe),真正的 TUI 不在 npm 包里。上游源码开源在 CodebuffAI/codebuff,TUI 技术栈为 @opentui/react 0.3.4(Zig 原生渲染核心)+ React 19,全屏 alternate-screen 模式(cli/src/index.tsx:404)。
根因链条

  1. 输入框的光标是“假”的:cli/src/components/multiline-input.tsx 用普通 组件渲染输入内容,用 InputCursor 组件手绘一个 ▍ 字符模拟光标(还会闪烁,闪烁时替换为空格)。
  2. 真实终端光标被隐藏且从不归位:
  • OpenTUI 渲染器启动即隐藏硬件光标(renderer.zig:526 setCursorPosition(1, 1, false),每帧写 \x1b[?25l)
  • 每帧结束时,只有当光标状态为 visible 时才会把它 move 到指定位置(renderer.zig:2671 if (cursorPos.visible));不可见时它就地停在本帧 diff 最后写入的那个格子
  • 整个 freebuff 的 cli/src 中没有任何一处调用 renderer.setCursorPosition(...)
  1. IME 候选窗锚点 = 终端硬件光标位置:Windows Terminal / ConPTY 将中文输入法的组字串和候选框定位在终端当前光标坐标处。所以候选框永远出现在“页面文字变化的地方”(diff 渲染终点),而不是输入框。
    旁证(同类问题)
  • Claude Code #13660(已修)、#30982/#50650 —— 同机理:“Ink 渲染循环移动了终端光标,IME 拿到的是渲染位置”
  • Kimi Code Just after 1.1 session says renews in 16 hour. #1022 —— 完全相同的症状和根因
  • gemini-cli #8595 —— 同类
  • 反例:opencode 同样用 OpenTUI 却没此 bug —— 因为它的输入框用 TextareaRenderable/EditBufferRenderable,其 renderCursor() 会调用 _ctx.setCursorPosition(cursorX, cursorY, true) 把真实光标放到插入符位置(opentui packages/core/src/renderables/EditBufferRenderable.ts:1011)
    修复方向
  1. 最小改动:在 multiline-input.tsx 中利用已有的 cursorRow 和 lineInfo.lineStartCols 计算 caret 的 cell 坐标(加上 scrollbox 的屏幕偏移和滚动量),每次 value/cursor 变化后调 renderer.setCursorPosition(col, row, true);同时隐藏自绘 ▍ 避免双光标。
  2. 彻底方案:改用 OpenTUI 的 textarea/edit-buffer renderable(内置上述机制)。

Steps to reproduce

Image

Where does this happen?

CLI (terminal client)

Operating system

Windows

Version

0.0.156

Model

No response

Logs or screenshots

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:cliThe Codebuff/Freebuff terminal clientbot:triagedClassified by the community triage bottype:bugA defect in the code with a reproducible failure

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions