Skip to content

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 03 Jun 18:10
· 108 commits to main since this release
06214a9

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)和 ToolCallMsgPromptQueued 等全部消息类型。

Bug 2:visual_default_countto_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

Full Changelog: v0.5.0...v0.5.1