Skip to content

Conversation

nogataka
Copy link

@nogataka nogataka commented Oct 2, 2025

Summary

  • Fix LineInfo() width math to sum rune widths instead of relying on uniseg.StringWidth, so
    double-width glyphs (e.g., Japanese) report accurate offsets and cursor positioning.
  • Track both visual width and rune count during wrapping; iterate using the rune count when re-
    inserting spaces to prevent duplicated padding for wide whitespace.
  • Reset tracked space counts after wrapping branches and add trailing spaces with the correct
    rune loop while preserving their display width.
  • Update the chat editor to pass the full component width into SetWidth, letting the textarea
    account for prompt/line-number padding internally.
  • Ensure forced word-wrapping recalculates widths off the most recent wrapped line by
    enumerating each rune or attachment width.
  • Keep a memoized width calculation so attachments respect their own display string widths,
    matching double-width semantics across lines.

Fixes #2920
Addresses #2593, #2013, #379

Testing

  • bun install
  • bun run ./script/format.ts
  • gofmt -w packages/tui/internal/components/textarea/textarea.go packages/tui/internal/
    components/chat/editor.go
  • bun turbo typecheck

This issue occurs because macOS’s native Terminal handles CJK full-width characters and combining marks more consistently, while VS Code and Cursor (VS Code–based integrated terminals, powered by xterm.js) are more prone to misalignment depending on configuration.

(CJK stands for Chinese, Japanese, and Korean — languages that use full-width characters which typically occupy two cells in a fixed-width terminal.)


Fix for Japanese Text Clipping in OpenCode TUI

Problem Description

When entering Japanese (full-width) characters in the OpenCode TUI editor, some characters were not visible, even though width calculations appeared correct. The issues observed:

  1. Invisible characters: Japanese characters after the cursor were missing from display
  2. Clipped lines: Text was cut off prematurely within the textarea
  3. Unexpected padding: Input area appeared taller than intended due to extra spacing

Root Cause

1. Double Width Reduction in SetWidth()

Location: packages/tui/internal/components/chat/editor.go, around line 441

Problem: The editor pre-subtracted 6 characters from the available width before passing it to textarea.SetWidth(). However, SetWidth() itself is responsible for subtracting prompt, line number, and border widths internally.

// BEFORE (Incorrect)
m.textarea.SetWidth(width - 6)

Why this failed:

  • The reserved width was subtracted twice (once in the editor, once internally).
  • As a result, the actual display area became narrower than expected.
  • Full-width characters (Japanese, Chinese, Korean) were clipped or pushed out of view.

2. Excessive Top/Bottom Padding

Location: packages/tui/internal/components/chat/editor.go, textarea style configuration

Problem: The textarea was styled with both PaddingTop(1) and PaddingBottom(1).

// BEFORE (Too much vertical padding)
textarea = styles.NewStyle().
    Background(t.BackgroundElement()).
    Width(width).
    PaddingTop(1).
    PaddingBottom(1).
    BorderStyle(lipgloss.ThickBorder()).
    Render(textarea)

Why this failed:

  • Each padding value adds one line of vertical space.
  • Together, they made the input area appear two lines taller than expected.
  • This created the impression of “extra blank lines” in the input field.

Solutions

Fix 1: Pass Full Width to SetWidth()

File: packages/tui/internal/components/chat/editor.go

Change:

// AFTER (Correct)
m.textarea.SetWidth(width)

Rationale:
SetWidth() is designed to receive the full available width. It internally subtracts reserved widths for prompts, line numbers, and borders. Passing the unmodified width ensures the textarea’s visible area matches the container width.


Fix 2: Remove Extra Vertical Padding

File: packages/tui/internal/components/chat/editor.go

Change:

// AFTER (Reduced padding)
textarea = styles.NewStyle().
    Background(t.BackgroundElement()).
    Width(width).
    PaddingTop(0).
    PaddingBottom(0).
    BorderStyle(lipgloss.ThickBorder()).
    Render(textarea)

Rationale:
Removing top and bottom padding eliminates the unintended two-line increase in height, resulting in a compact and consistent input area.


Testing

After these fixes, Japanese text in the TUI editor:

  1. Displays correctly without disappearing
  2. Wraps properly within the textarea boundaries
  3. Aligns cursor accurately after full-width characters
  4. No longer adds unnecessary blank lines above and below the input field

Technical Details

  • SetWidth() is now always passed the full container width, avoiding double subtraction.
  • Vertical spacing is explicitly controlled by padding values, now set to zero.
  • Full-width character handling (go-runewidth and uniseg) remains correct, ensuring CJK text displays with accurate widths.

Why This Matters

For users working in Japanese (or other CJK languages), correct handling of full-width characters is essential for usability. Fixing width calculation and unnecessary padding ensures:

  • No clipping or disappearing characters
  • Accurate cursor behavior
  • Consistent input area size
  • Better user experience for non-English users

@nogataka nogataka force-pushed the fix-tui-textarea-width branch from bc935be to 735454d Compare October 3, 2025 13:48
@nogataka nogataka force-pushed the fix-tui-textarea-width branch from 735454d to 4338169 Compare October 3, 2025 23:02
@jensenojs
Copy link

may fix this #2256 as well

@zenyr
Copy link

zenyr commented Oct 10, 2025

Easily reproduced using an emoji. (Basically any CJK codepoints are also affected by this)
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants