[PR #2922] fix(tui): correct textarea width for full-width chars #10487

Closed
opened 2026-02-16 18:15:09 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/2922

State: closed
Merged: No


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
**Original Pull Request:** https://github.com/anomalyco/opencode/pull/2922 **State:** closed **Merged:** No --- # 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. ```go // 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)`. ```go // 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**: ```go // 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**: ```go // 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
yindo added the pull-request label 2026-02-16 18:15:09 -05:00
yindo closed this issue 2026-02-16 18:15:09 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#10487