Skip to content

drawBox border lines ignore scissor clipping: card borders bleed outside a scrollbox viewport when partially scrolled out (text is clipped correctly, borders are not) #1311

Description

@chenkumi

Summary

When a bordered box inside a scrollbox is partially scrolled out of the viewport, its border lines are still drawn outside the scrollbox bounds and bleed into neighboring UI (e.g., a header bar above). Text content inside the same box is clipped correctly — only the border cells leak. This is a leftover gap from the scissor-clipping fix in #389: drawText/fillRect are scissor-aware, but drawBox's border loops are not.

Expected

Content (text and borders) is clipped to the scrollbox viewport. A card partially scrolled out at the top edge should show only the visible portion of its border, disappearing cleanly at the boundary.

Actual

Border segments outside the viewport are painted over the UI above the scrollbox. The further the card is scrolled out, the more border characters accumulate over the header area:

Expected                                        Actual
┌───────────────────┐                          ┌───────────────────┐
│ header            │                          │┌─────────────────┐│  ← card top border bleeds
│                   │                          ││                 ││  into header row
└───────────────────┘                          └│─────────────────│┘
│ ┌───────────────┐ │                           │ ┌───────────────┐ │
│ │ Thinking  done│ │                           │ │ Thinking  done│ │
│ │ reasoning...  │ │                           │ │ reasoning...  │ │
│ └───────────────┘ │                           │ └───────────────┘ │
│                   │                           │                   │
│ card scrolled out │                           │ card scrolled out │
│ cleanly at edge   │                           │ borders keep      │
└───────────────────┘                           └───────────────────┘

Concrete rendering (80×24 terminal, conversation scrollbox scrolled up 100 lines):

00|┌─ App title ─────────────────────────────────────────────────────────┐
01|│┌──────────────────────────────────────────────────────────────────┐ │  ← card top border + corners on header row
02|└│──────────────────────────────────────────────────────────────────│─┘  ← card borders overlay header's bottom border
03| │ REASONING CONTENT 3                                              │
04| └──────────────────────────────────────────────────────────────────┘

Steps to Reproduce

  1. Render a scrollbox with height: 17, scrollY, overflow: "hidden", stickyScroll, stickyStart="bottom".
  2. Inside it, render multiple <box border={true}> cards (e.g., a "Thinking" card and a "Tool" card) whose total height exceeds the viewport.
  3. Scroll (or rely on sticky-bottom) so a card straddles the top edge of the viewport.
  4. Observe the card's border characters drawn over the UI above the scrollbox.

Observed with the React renderer on macOS (Terminal.app/iTerm2) using @opentui/core@0.4.5.

Root Cause

In packages/core/src/zig/buffer.zig, drawBox (line ~1863):

  • Entry check is only a coarse whole-box reject: if (!self.isRectInScissor(...)) return (line ~1897). A box that partially intersects the scissor passes and continues drawing.
  • The horizontal/vertical border loops only check buffer bounds (0..width/height) and never intersect against the current scissor rect.
  • The useTransparentBorderFastPath path writes directly to self.buffer.char/fg/attributes with no clipping at all.

For comparison, drawText (line ~1026) performs both isRectInScissor (line ~1169) and clipRectToScissor (line ~1175), which is why text never leaks.

Suggested Fix

In drawBox, clip the border loops per-cell against the current scissor rect (similar to fillRectClipped / drawText), and route useTransparentBorderFastPath through a scissor-aware path. Related prior work: #388 / #389 (nested scrollbox text clipping) — this is the border-specific gap those left behind.

Environment

  • @opentui/core 0.4.5 (also reproducible against main)
  • React renderer (@opentui/react)
  • macOS, Terminal.app / iTerm2 / kitty

Activity

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions