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
{{ message }}
Repository navigation
Bug: status lines accumulate after terminal wrapping #3
When a rendered status line wraps at the terminal boundary, subsequent refreshes can
leave stale lines behind and the output may keep growing.
Reproduction
Use a narrow terminal, or configure a line whose rendered width is exactly near process.stdout.columns.
Include a wide Unicode character such as ✅, ⏳, ❌, CJK text, or a custom symbol.
Let the status line refresh repeatedly.
Actual behavior
The status line wraps. On subsequent repaint cycles, old wrapped rows remain visible,
and additional rows can accumulate.
Screenshot of the issue: multiple stale WORKING rows remain after the
status changes to IDLE. The repeated rows grow during refreshes instead
of being replaced.
Expected behavior
A refresh should replace all physical terminal rows occupied by the previous render,
without leaving stale output.
Root cause
There appear to be two contributing issues:
visibleWidth uses stripAnsi(text).length, which counts JavaScript UTF-16 code
units rather than terminal display columns. For example, ✅.length is 1 but it
normally occupies 2 terminal columns.
The repaint logic tracks only logical output lines. When a logical line wraps, the
cursor rewind/clear logic does not account for the additional physical terminal row.
Clearing with CSI 0J only clears below the cursor, so stale wrapped rows remain.
Proposed direction
Use terminal display-column width calculation, e.g. string-width.
Track the number of physical rows from rendered block width and terminal width
when repainting.
I reproduced the issue with a VT100 emulator and verified that accounting for display
width and physical rows prevents accumulation across repeated repaints.
I can provide a focused PR if this approach is welcome.
This is related to the repaint implementation, but it has a different root cause
from the Unicode display-width / wrapped-row issue described above.
Additional related issue: terminal resize while running
I found a second repaint problem that is related to terminal rendering but has a
different root cause from the Unicode display-width / wrapped-row issue in the
original report.
Reproduction
Start ocstatusline.
Keep it running while OpenCode is active.
Resize the terminal window or a tmux pane.
Actual behavior
After resizing, old status output can reappear or remain on screen, and the next
render is appended in an unexpected position.
Expected behavior
After a resize, the status area should be redrawn cleanly using the new terminal
width, without restoring or retaining stale rows.
Cause
The current repaint logic assumes that the cursor is still positioned at the end
of the previously painted status block, then uses relative cursor movement to
rewind and clear it.
Terminal resize/reflow breaks that assumption: the terminal can reflow the
previous output and change its physical row layout, so the relative rewind no
longer points at the previous status block.
This is independent of the Unicode-width calculation. Correct display widths and
physical-row tracking still cannot guarantee correct repainting after terminal
reflow.
Proposed direction
On SIGWINCH, invalidate the current repaint position and perform a clean
resynchronization redraw after a short debounce. One possible implementation is:
set a resync flag on SIGWINCH;
debounce repeated resize events;
on the next paint, clear and redraw from a known position rather than using
relative movement based on the previous block;
use the updated process.stdout.columns to fit the new output width;
remove the resize listener and pending timer during shutdown.
I verified the behavior with a VT100 emulator. A resync redraw prevents stale
rows from remaining after resize.
I can provide a focused PR if this behavior is in scope.
Additional related issue
Separately, I also found that truncation strips ANSI styling from the retained
prefix, so colors disappear at narrow widths. I will file this as a separate
issue if useful.
Activity
ariera-j commentedon Aug 16, 2026
This is related to the repaint implementation, but it has a different root cause
from the Unicode display-width / wrapped-row issue described above.
Additional related issue: terminal resize while running
I found a second repaint problem that is related to terminal rendering but has a
different root cause from the Unicode display-width / wrapped-row issue in the
original report.
Reproduction
ocstatusline.Actual behavior
After resizing, old status output can reappear or remain on screen, and the next
render is appended in an unexpected position.
Expected behavior
After a resize, the status area should be redrawn cleanly using the new terminal
width, without restoring or retaining stale rows.
Cause
The current repaint logic assumes that the cursor is still positioned at the end
of the previously painted status block, then uses relative cursor movement to
rewind and clear it.
Terminal resize/reflow breaks that assumption: the terminal can reflow the
previous output and change its physical row layout, so the relative rewind no
longer points at the previous status block.
This is independent of the Unicode-width calculation. Correct display widths and
physical-row tracking still cannot guarantee correct repainting after terminal
reflow.
Proposed direction
On
SIGWINCH, invalidate the current repaint position and perform a cleanresynchronization redraw after a short debounce. One possible implementation is:
resyncflag onSIGWINCH;relative movement based on the previous block;
process.stdout.columnsto fit the new output width;I verified the behavior with a VT100 emulator. A resync redraw prevents stale
rows from remaining after resize.
I can provide a focused PR if this behavior is in scope.
Additional related issue
Separately, I also found that truncation strips ANSI styling from the retained
prefix, so colors disappear at narrow widths. I will file this as a separate
issue if useful.