Visitar URL original
fix: measure terminal columns in cells so CJK text lands correctly by heeseon87 · Pull Request #3 · plannotator/herdr-annotate · GitHub
Skip to content

fix: measure terminal columns in cells so CJK text lands correctly - #3

Merged
backnotprop merged 1 commit into
plannotator:mainfrom
heeseon87:fix/wide-char-cursor
Aug 28, 2026
Merged

backnotprop merged 1 commit into
plannotator:mainfrom
heeseon87:fix/wide-char-cursor

Conversation

@heeseon87

@heeseon87 heeseon87 commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Typing Korean (or any East Asian text) into the annotation editor puts the cursor in the wrong place, and the character being composed appears to duplicate itself inside the text that is already there.

Both symptoms come from one line. commentLines() in src/editor.ts advances the column by one for every character:

lines[row] += char;
col += 1;

East Asian characters occupy two terminal cells. After typing five Hangul syllables the text is ten cells wide, but the editor reports column five and places the cursor there with \x1b[{row};{col}H. The cursor sits in the middle of the text.

The apparent duplication is the same bug seen through the IME. While a syllable is being composed the terminal paints the preedit overlay at the cursor position the application reported, which is mid-text, so the composing syllable shows up inside what was already typed. On commit the application redraws and the syllable jumps to the end.

Fix

Add src/width.ts, which reports how many cells a character occupies: two for Hangul syllables and jamo, CJK ideographs, kana, fullwidth forms, and emoji, one for the rest, zero for control characters and combining marks.

Measure every column in cells rather than characters:

  • src/layout.ts (extracted from editor.ts) computes the cursor row and column in cells, and wraps a line before a wide character would straddle the right edge.
  • wrapText() in src/format.ts wraps on cells, so Korean selections no longer overflow the box.
  • The editor footer and the manager's list entries clip on cells.
  • Vertical cursor motion keeps the visual column through mixed-width text.

commentLines() moved into its own module because editor.ts starts the TUI as an import side effect, which leaves the geometry untestable where it was.

Tests

15 new tests covering cell widths, comment layout, and wrapping. The full suite is 53 tests, all passing, and bun run typecheck is clean.

bun test    53 pass, 0 fail

Reported and reproduced on macOS with a Korean IME.

The editor counted every character as one terminal column. East Asian
characters occupy two cells, so the reported cursor column fell short of
the real one. The cursor drifted into the middle of the text, and the IME
drew its composition overlay at that wrong position, which looked like a
duplicated syllable appearing inside what was already typed.

Add a cell-width helper and use it wherever a column is measured: the
comment layout, the selection wrap, the footer clip, the manager list
clip, and vertical cursor motion. Extract the comment layout into its own
module so the geometry can be tested.
@backnotprop

Copy link
Copy Markdown
Contributor

thanks, reviewing

@backnotprop
backnotprop merged commit 4789e85 into plannotator:main Aug 28, 2026
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.

2 participants