v0.5.1
v0.5.1 — Prompt 回显截断 & 多余空行修复
问题描述
当用户在 Input 区域粘贴长代码或多行内容时,Ratatui 回显的 prompt 末尾出现内容丢失(截断)。同时,thinking(推理)和 content(回复)内容的末尾容易出现大量多余空行。
根因分析
Bug 1:visual_line_count 未考虑终端宽度折行(核心截断原因)
文件: oy-tui/src/message.rs
所有 visual_*_count 函数(用于计算每条消息在聊天区的视觉高度)都忽略 width 参数,仅统计 render_markdown() 输出的逻辑行数,而不考虑这些行在终端宽度下会被 ratatui 的 Wrap 自动折行。
后果: 当 prompt 包含长行代码(几乎所有粘贴的代码)时,visual_line_count 返回的数值远小于实际渲染所需行数。消息区域分配的 height 不足,prompt 末尾的多行内容被 Paragraph 裁剪掉。
✅ UiMessages 已正确保留了折行计算(使用 div_ceil):
Message::UiMessages(text) => {
let line = format!("> {}", text);
let w = UnicodeWidthStr::width(line.as_str());
1.max(w.div_ceil(width)) // 正确
}❌ 其他所有消息类型都忽略了折行:
fn visual_default_count(&self, chat: &ChatMessage, _width: usize, theme: &Theme) -> usize {
// width 被完全忽略!
if let Some(c) = &chat.content {
count += Self::render_markdown(c, Style::default(), theme).len();
// 只数逻辑行数,不计算折行
}
}影响范围: AgentMessages(User、Assistant、Tool)和 ToolCallMsg、PromptQueued 等全部消息类型。
Bug 2:visual_default_count 与 to_lines 渲染不一致(末尾空行原因)
to_lines() 方法对 reasoning 和 content 的实际输出与 visual_default_count 的统计方式存在差异:
- reasoning_content:
to_lines将整个 thinking 文本(含内部换行)放在一个Line中,但旧的visual_default_count通过render_markdown拆成多个Line。两者行数不一致,导致分配高度与实际渲染不匹配。 - content:
to_lines在内容首行加了[Role]前缀(如[Assistant]),但visual_default_count没计入此前缀宽度,造成首行被低估,在宽度边界处产生空行或截断。
Bug 3:wrap_input_text 静默丢弃尾部空白(次要)
文件: oy-tui/src/ui.rs
wrap_input_text 函数中,当行末空白字符触发折行时,pending_ws 中的空白被直接丢弃,不追加到当前行。
Bug 4:Input Paragraph 缺少 .wrap() 保护(次要)
Input 区域的 Paragraph 没有设置 .wrap(),完全依赖 wrap_input_text 的预折行。预折行如有 off-by-one 错误,超长行会被直接裁剪。
修改清单
oy-tui/src/message.rs
| 修改项 | 说明 |
|---|---|
新增 count_wrapped_content_lines() |
辅助函数:对带固定前缀的文本行,按宽度计算折行后的视觉行数 |
visual_default_count |
重写:reasoning 按 [Role - thinking] 前缀 + 内容单行折行,content 按 render_markdown 各行 + 首行 [Role] 前缀折行 |
visual_read_count |
引入 [Tool - Read] 前缀宽度,使用 count_wrapped_content_lines |
visual_bash_count |
引入 [Tool - Bash] 前缀宽度 |
visual_edit_count |
result/old/new 各段分别计算前缀宽度 + 折行 |
visual_write_count |
从硬编码返回 2 改为计算 [Tool - Write] + 文件路径行的实际折行 |
ToolCallMsg 分支 |
header 按实际字符串宽度折行 + 内容 " " 前缀折行 |
PromptQueued 分支 |
从硬编码返回 2 改为计算显示文本的实际折行 |
移除 count_wrapped_lines |
原辅助函数不再使用 |
oy-tui/src/ui.rs
| 修改项 | 说明 |
|---|---|
wrap_input_text |
非空白字符触发的折行前,先将 pending_ws 刷入当前行,保留尾部空白 |
| Input Paragraph | 添加 .wrap(Wrap { trim: false }) 作为预折行的兜底保护 |
测试
- 全量测试 164 个(4 个 crate)全部通过 ✅
- 编译零警告 ✅
受影响文件
oy-tui/src/message.rs (核心修复 — visual_line_count 系列函数)
oy-tui/src/ui.rs (输入区显示修复)
What's Changed
- Fix v0.5.1 by @cherish-ltt in #6
Full Changelog: v0.5.0...v0.5.1