Skip to content

[Bug]: Chat text shows through the seam between an attached composer banner and the composer #8546

Description

@vitalyiegorov

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. Open a thread with enough scrollback that chat text sits directly behind the composer.
  2. Get any attached banner to appear above the composer — for example let a server update land ("Server update available"), or resume a session with a large context so "Resume with less context" appears.
  3. Look closely at the join between the banner's bottom edge and the composer's top edge, with bright text scrolled behind it.

Seen on the macOS desktop app, dark theme, Retina.

Expected behavior

The banner and the composer read as one continuous surface. Whatever is behind them is blurred by the glass, and nothing from the chat shows through the join.

Actual behavior

A row roughly one device pixel tall at the join is painted by neither surface, so the chat behind shows through it crisp and unblurred — a sliver of a text line running the width of the banner. It is most obvious when a bright line of text scrolls behind the seam.

It affects every attached banner, not one of them: the two screenshots below are "Resume with less context" and "Server update available".

Impact

Cosmetic issue

Version or commit

Desktop app on macOS; reproduced against main at 4c51b4c (v0.0.35-nightly).

Logs or stack traces

Investigation, in case it saves someone the bisect:

The composer's own backdrop-filter loses about a device pixel of backdrop at its top edge whenever its box lands off the device-pixel grid. Under an attached drawer the content that goes missing is the drawer's surface, so the page behind shows through that row. This is why the leak is unblurred: nothing is painting there at all.

Two controls that pin it down, measured in a harness driving the real ComposerBannerStack and the composer shell over a saturated backdrop, sweeping device pixel ratios against sub-pixel phase offsets:

  • Forcing the composer's backdrop-filter: none → leaks drop to 0/64.
  • Disabling the drawer's backdrop-filter, or adding translateZ(0) → no change at all (31/64 either way).

466 of 768 swept configurations leak. It is not mask or overlap arithmetic: --chat-composer-attachment-overlap drives both the mask cut-off and the slot's negative margin, so the two painted regions are designed to abut with exactly zero overlap (measured bannerLastPaintedRow + 1 === composerFirstPaintedRow), which leaves no margin for the lost row. Widening that overlap reduces the leak but then plateaus — 8px behaves the same as 2px — which is what rules the overlap out as the cause.

A fractional offset on an ancestor (the real app has these from panel layout and view transitions) reproduces it at every ratio including an exact 2×, so Retina is not exempt.

Screenshots, recordings, or supporting files

"Resume with less context" — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

"Server update available" — same defect, different banner:

Server update available banner with chat text showing through the seam above the composer

For contrast, a banner that happened to land clean. Worth including because it is misleading: it is not structurally different, it just landed on a sub-pixel phase that does not leak. Both variants leak identically once phase is swept, so banner height and content are not the discriminator.

Background work banner sitting flush against the composer with no seam

Workaround

None from the UI. Dismissing the banner removes it, which removes the seam with it.

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