8000 Bug: status lines accumulate after terminal wrapping · Issue #3 · amirlehmam/ocstatusline · GitHub
[go: up one dir, main page]

Skip to content

Bug: status lines accumulate after terminal wrapping #3

Open

Description

@ariera-j

When a rendered status line wraps at the terminal boundary, subsequent refreshes can
leave stale lines behind and the output may keep growing.

Reproduction

  1. Use a narrow terminal, or configure a line whose rendered width is exactly near
    process.stdout.columns.
  2. Include a wide Unicode character such as ✅, ⏳, ❌, CJK text, or a custom symbol.
  3. 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.

Image

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:

  1. 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.
  2. 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.

Activity

  1. ariera-j commented on Aug 16, 2026

    @ariera-j
    Author

    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

    1. Start ocstatusline.
    2. Keep it running while OpenCode is active.
    3. 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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0