问题
在 Deep Code 的终端界面中,输入框和提交后的用户消息回显(> … 行)会在行尾把英文单词从中间硬截断,而不是把放不下的单词整体移到下一行。Claude Code、Codex、Copilot、Antigravity 等 CLI 工具均为整词换行(仅当单词本身超过整行宽度时才在词内断开)。
受影响位置:
- 输入框:
packages/cli/src/ui/views/PromptInput.tsx 中 <Text wrap="hard">(约第 825 行)
- 用户消息回显:
packages/cli/src/ui/components/MessageView/index.tsx 中 PromptEchoLine 的 <Text wrap="hard">(约第 157 行)
复现
- 启动 Deep Code,终端尺寸 120×30。
- 在输入框输入:
Let's attach a screenshot. I just type aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…a and now you see, I did not break the word only consists of a, so it should show up on the next line a whole. It did not.
(其中为一串约 80 多个 a 组成的单词,总长度不足整行 120 列。)
- 观察:
a…a 这个单词在第 1 行行尾被截断,剩余几个 a 折到第 2 行开头,而不是整体移到第 2 行。
截图
期望
- 这两处文本框改为整词换行:行尾放不下的单词整体移到下一行;仅当单个单词超过整行宽度时才在词内断开(与 Ink 默认
wrap="wrap" 及上述竞品一致)。
- 中文/日文/韩文等无空格文本的换行行为保持不变(仍可在任意字符处换行)。
原因与建议(点击展开)
现状
- Ink 的
wrap="hard" 等价于 wrap-ansi(text, width, { wordWrap: false }):每行填满到列宽,必要时在词内断行,这正是“单词被截断”的来源。
- Ink 默认
wrap="wrap" 为整词换行,项目中其它位置(assistant Markdown 正文、Plan 预览、StatusLine)已在用;输入框与回显行是仅有的两处 wrap="hard"。
- 输入框的光标定位在
packages/cli/src/ui/hooks/cursor.ts 的 measureTextPosition 中按“逐字符列宽换行”计算,与 wrap="hard" 一致;若只改 wrap 属性,光标位置会与渲染结果错位。
- 现有测试锁定了硬换行行为:
packages/cli/src/tests/message-view.test.ts 期望 "abcdefg" 渲染为 " > abcde\n fg"。
建议实现
- 回显行(
MessageView):将 wrap="hard" 改为 wrap="wrap",并同步更新 message-view.test.ts 的期望值。
- 输入框(
PromptInput):将 wrap="hard" 改为 wrap="wrap",同时把 cursor.ts 的 measureTextPosition 改为与 wrap-ansi 一致的整词换行算法(含 ANSI 与 emoji 宽度处理),保持光标与渲染一致;补充/更新 prompt-input-keys.test.ts 中相关用例。
边界
- 单个单词超过整行宽度时仍需在词内断开(终端固有限制,与其它工具一致)。
- 仅影响显示与光标定位,不改变提交文本内容。
- CJK 等无空格文本的换行行为不变。
Co-authored-by: deepseek-v4-pro noreply@deepseek.com
问题
在 Deep Code 的终端界面中,输入框和提交后的用户消息回显(
> …行)会在行尾把英文单词从中间硬截断,而不是把放不下的单词整体移到下一行。Claude Code、Codex、Copilot、Antigravity 等 CLI 工具均为整词换行(仅当单词本身超过整行宽度时才在词内断开)。受影响位置:
packages/cli/src/ui/views/PromptInput.tsx中<Text wrap="hard">(约第 825 行)packages/cli/src/ui/components/MessageView/index.tsx中PromptEchoLine的<Text wrap="hard">(约第 157 行)复现
Let's attach a screenshot. I just type aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…a and now you see, I did not break the word only consists of a, so it should show up on the next line a whole. It did not.(其中为一串约 80 多个
a组成的单词,总长度不足整行 120 列。)a…a这个单词在第 1 行行尾被截断,剩余几个a折到第 2 行开头,而不是整体移到第 2 行。截图
期望
wrap="wrap"及上述竞品一致)。原因与建议(点击展开)
现状
wrap="hard"等价于wrap-ansi(text, width, { wordWrap: false }):每行填满到列宽,必要时在词内断行,这正是“单词被截断”的来源。wrap="wrap"为整词换行,项目中其它位置(assistant Markdown 正文、Plan 预览、StatusLine)已在用;输入框与回显行是仅有的两处wrap="hard"。packages/cli/src/ui/hooks/cursor.ts的measureTextPosition中按“逐字符列宽换行”计算,与wrap="hard"一致;若只改wrap属性,光标位置会与渲染结果错位。packages/cli/src/tests/message-view.test.ts期望"abcdefg"渲染为" > abcde\n fg"。建议实现
MessageView):将wrap="hard"改为wrap="wrap",并同步更新message-view.test.ts的期望值。PromptInput):将wrap="hard"改为wrap="wrap",同时把cursor.ts的measureTextPosition改为与 wrap-ansi 一致的整词换行算法(含 ANSI 与 emoji 宽度处理),保持光标与渲染一致;补充/更新prompt-input-keys.test.ts中相关用例。边界
Co-authored-by: deepseek-v4-pro noreply@deepseek.com