Skip to content

fix(ink): 禁止 Unicode 制表符在 CJK 终端下渲染 — 自动映射为 ASCII(- | +)#2368

Open
zzhan111 wants to merge 2 commits into
esengine:v1from
zzhan111:fix/cjk-terminal-border-rendering
Open

fix(ink): 禁止 Unicode 制表符在 CJK 终端下渲染 — 自动映射为 ASCII(- | +)#2368
zzhan111 wants to merge 2 commits into
esengine:v1from
zzhan111:fix/cjk-terminal-border-rendering

Conversation

@zzhan111
Copy link
Copy Markdown

问题

在 zh-CN/ja-JP/ko-KR locale(Windows 代码页 936/932/949)下,Unicode 制表符(U+2500–U+257F:─│┌┐└┘ 等)被终端当作 2 列宽渲染,但 Reasonix 的 yoga-layout 按 1 列计算,导致:

  • 水平分隔线 overflow 到 2 倍宽 → 换行碎片
  • 状态栏文字错位、多行重叠
  • 对话框(/effort /model 等)边框与内容交错

Closes #2366

改动

packages/ink/src/render-border.ts

  1. 新增 `isCJKLocale()` — 通过 `Intl.DateTimeFormat().resolvedOptions().locale` 检测 CJK 区域
  2. 新增 `cjkAsciiEq` 映射表 — 158 个 Unicode 盒线字符 → ASCII(`-` `|` `+`)
  3. 在 `renderBorder` 中,CJK 模式下将 `box` 全部映射为 ASCII 等价字符后生成 border
  4. 重写 `embedTextInBorder` — 改用 `stringWidth()` 统一计算视觉宽度,不再混合 `.length`(JS 字符数)和 `stringWidth()`(终端列数)

packages/ink/src/stringWidth.ts

  1. `eastAsianWidth()` 调用从硬编码 `{ ambiguousAsWide: false }` 改为 `eawOpts`(CJK 下 = true)
  2. `BUN_STRING_WIDTH_OPTS` 从 `{ ambiguousIsNarrow: true }` 改为 `{ ambiguousIsNarrow: !isCJK }`

测试

在 Windows cmd.exe(chcp 936)下验证:

  • 分隔线、状态栏、MCP toast、对话框全部对齐
  • 非 CJK 终端(en-US locale)行为不变
  • 无性能回退(`isCJKLocale()` 在模块加载时已求值完毕)

zzhan111 added 2 commits May 30, 2026 17:41
Box-drawing characters (U+2500–U+257F) are East Asian Ambiguous Width
and render as double-width cells in CJK terminals (zh/ja/ko locale,
Windows code page 936/932/949).  Yoga-layout assumes single-width,
causing horizontal overflow, status-bar fragmentation, and overlapping
dialogs.

Changes:
- render-border.ts: add isCJKLocale() detection and cjkAsciiEq mapping
  table; remap all border glyphs to ASCII (- | +) when on a CJK system
- render-border.ts: rewrite embedTextInBorder to use stringWidth()
  throughout instead of mixing JS .length with visual widths
- stringWidth.ts: make eastAsianWidth() and Bun string-width respect
  ambiguous-as-wide when the system locale is CJK

Fixes: separator overflow, status bar misalignment, dialog overlap.
Characters like ✓ (U+2713), ⚡ (U+26A1), ⛁ (U+26C1), ⚑ (U+2691)
fall in the 0x2600-0x27bf block (Miscellaneous Symbols).  They are
matched by the emoji regex but rendered single-cell in terminal
emulators and CJK code-page consoles — unlike true emoji presentation
glyphs that occupy 2 cells.

getEmojiWidth now returns 1 for this range, preventing yoga-layout
from overcounting the status-bar and toast line widths.
@esengine esengine added the v1 Legacy TypeScript line (0.x) — v1 branch, maintenance only label May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v1 Legacy TypeScript line (0.x) — v1 branch, maintenance only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unicode 制表符在 CJK 终端 (zh/ja/ko, code page 936) 下渲染错位 → 状态栏碎片化、分隔线溢出

2 participants