You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When typing a review note in the draft composer (press c on a hunk), CJK text never soft-wraps at the right moment. The composer stays one row high and only shows the visual row the cursor is on, so previously typed text becomes invisible while typing.
Right before the wrap threshold the box looks full but is still one row
(figure 1):
After typing one more character, the view jumps to the overflow visual row and the composer appears to "lose" everything except the last typed character (figure 2). The text is not lost — the 1-row-high viewport
follows the cursor and hides the rest:
(figure 2):
An English note of the same display length wraps and grows rows correctly.
OpenTUI's TextareaRenderable reports virtualLineCount capped at the viewport height: a 1-row-high textarea never starts wrapping, so the max(estimate, virtualLineCount) correction in onContentChange can never grow the height either — a deadlock. The editor keeps the cursor visible by scrolling the capped viewport vertically, which is exactly what produces figure 2.
The deadlock only breaks when the character count finally exceeds the box width, the estimate grows to 2, and wrapping suddenly kicks in — which is why the composer sometimes appears to wrap after enough input.
English text escapes the deadlock because code units == cells: the estimate grows as soon as a line exceeds the box width. (A milder variant is still reachable in English with pathological word lengths: three words each slightly over half the box width word-wrap to 3 rows while the estimate says 2, and the cap hides the third row.)
Two smaller observations from instrumenting the component:
useEffect([draft?.body]) resets the line-count hint to the hard newline count on every keystroke, wiping the virtualLineCount-based correction from onContentChange one render later.
The same code-unit estimate feeds measureAgentInlineNoteHeight, so planned row heights drift from mounted heights for CJK drafts (the row-windowed stream relies on these agreeing).
Steps to reproduce
hunk diff on any change (split layout).
Press c to start a review note.
Type a long CJK sentence without spaces (e.g. repeat 测试 ~40 times).
The composer stays one row high and shows only the cursor's visual row (figures 1-2); an English sentence of similar display length wraps instead. Typing past ~2× the visual width suddenly self-heals.
Expected behavior
The draft composer wraps by terminal cells and grows a row whenever the text exceeds the box width — for CJK exactly as it already does for English, and consistent with how the saved note renders (cell-aware wrapText since #567). The height estimate, the editor's actual wrapping, and measureAgentInlineNoteHeight should all agree.
A natural fix is to give draftVisualLineCount the same cell-aware word wrap semantics as wrapText (or share one helper), so the estimate always matches the editor's real wrapping and the height-1 deadlock never forms.
What happened?
When typing a review note in the draft composer (press

con a hunk), CJK text never soft-wraps at the right moment. The composer stays one row high and only shows the visual row the cursor is on, so previously typed text becomes invisible while typing.Right before the wrap threshold the box looks full but is still one row
(figure 1):
After typing one more character, the view jumps to the overflow visual row and the composer appears to "lose" everything except the last typed character (figure 2). The text is not lost — the 1-row-high viewport

follows the cursor and hides the rest:
(figure 2):
An English note of the same display length wraps and grows rows correctly.
Root cause — two compounding issues:
draftVisualLineCountinsrc/ui/components/panes/AgentInlineNote.tsxestimates the composer height withString#length(UTF-16 code units) instead of terminal cells — the same bug class as Agent note text with CJK characters is truncated instead of wrapped (wrapText measures UTF-16 code units, not terminal cells) #566, in a code path that fix(ui): wrap agent note text by terminal cells #567 didn't touch. A 60-char CJK string (120 cells) in a ~76-cell box estimates toceil(60/76) = 1row, so the composer height stays 1 until the character count exceeds the box width (for CJK that means roughly two full visual rows of text).TextareaRenderablereportsvirtualLineCountcapped at the viewport height: a 1-row-high textarea never starts wrapping, so themax(estimate, virtualLineCount)correction inonContentChangecan never grow the height either — a deadlock. The editor keeps the cursor visible by scrolling the capped viewport vertically, which is exactly what produces figure 2.The deadlock only breaks when the character count finally exceeds the box width, the estimate grows to 2, and wrapping suddenly kicks in — which is why the composer sometimes appears to wrap after enough input.
English text escapes the deadlock because code units == cells: the estimate grows as soon as a line exceeds the box width. (A milder variant is still reachable in English with pathological word lengths: three words each slightly over half the box width word-wrap to 3 rows while the estimate says 2, and the cap hides the third row.)
Two smaller observations from instrumenting the component:
useEffect([draft?.body])resets the line-count hint to the hard newline count on every keystroke, wiping thevirtualLineCount-based correction fromonContentChangeone render later.measureAgentInlineNoteHeight, so planned row heights drift from mounted heights for CJK drafts (the row-windowed stream relies on these agreeing).Steps to reproduce
hunk diffon any change (split layout).cto start a review note.Expected behavior
The draft composer wraps by terminal cells and grows a row whenever the text exceeds the box width — for CJK exactly as it already does for English, and consistent with how the saved note renders (cell-aware
wrapTextsince #567). The height estimate, the editor's actual wrapping, andmeasureAgentInlineNoteHeightshould all agree.A natural fix is to give
draftVisualLineCountthe same cell-aware word wrap semantics aswrapText(or share one helper), so the estimate always matches the editor's real wrapping and the height-1 deadlock never forms.Version
0.18.0-beta.0