Agent chat: the minimap's hover card blurs the chat behind it, not just tints it

Merged
TE
13d ago
+220-15

Follow-up to #23782 (the minimap hover card went glass).

What was wrong. After #23782 the card hovering a thread-minimap mark had the tooltip tint and backdrop-filter: blur(20px) saturate(1.4), but the transcript text behind it showed through sharp. The result was tint without blur, which reads worse than the old solid card. The card was drawn inside the transcript scroller .acpmux-scroll, and that scroller carries the soft-edge mask-image (conversation.css). A masked ancestor is the card's backdrop root, so the blur had nothing to read. Removing the mask in a diagnostic capture made the blur work. Turning off contain: strict changed nothing.

What changed (minimap card, both rails: compact and full map).

  • PreviewCard.tsx: the card is portaled into the scroller's parent (the stage), which has no mask. A zero-size anchor (.acpmux-minimap__anchor) stays in the rail, and the card's x and y are measured from it before paint, so the card lands exactly where it did. The wheel over the card is handed to the scroller, so it still scrolls the chat as it did when the card was inside it. Without a scroller, the card draws in place.
  • ThreadMinimap.tsx, ScrollMap.tsx: pass scroller={scroller.current}.
  • threadMinimap.css: the card takes the rail's --acpmux-minimap-* tokens (and the borders-none rim) because it no longer inherits them from the rail. It sits on --layer-tooltip, like the chat's other hover cards. The scroller's fade mask is unchanged.

Before and after (Linux gallery, agent-pane.thread-minimap / long-chat, compact rail, row 8 hovered, Chromium, 880 px):

before and after crops, dark and light

before (dark)after (dark)
before darkafter dark

The card's position is pixel-identical before and after. Only the backdrop changes.

WebKit note. The gallery's headless WebKit (Playwright, Linux) draws no backdrop-filter at all: a bare test page with a blurred card over text renders the text sharp there too, while Chromium blurs it. So the WebKit gallery stills cannot show the blur either way. The fix does not depend on the engine: the card has no masked, filtered or opaque ancestor any more. Its ancestors are only .acpmux-stage, .acpmux-main and .acpmux-shell, which have no mask, filter, opacity or contain.

Other glass inside .acpmux-scroll (audited). One more surface has the same problem and is left out of this PR: the Find in Chat pill (ChatFindBar, a direct child of the scroller; blur from findPill.css). It needs its own anchoring to the scroller's top-right, so it gets a separate PR. Every other glass surface is either portaled (link and PR hover cards, session hover cards, tooltips, hints, menus, popovers, context menus) or rendered outside the scroller (image viewer, jump pill, composer). The user bubble in the rows has no blur (--glass-backdrop: none).

Tests.

  • New: thread-minimap-rail.test.tsx and thread-scroll-map-rail.test.tsx check that the open card is not a descendant of .acpmux-scroll, that it sits in the scroller's parent, and that the rail keeps the anchor. The rail test also checks that a wheel over the card scrolls the transcript. The descendant tests fail without the fix (2 fail) and pass with it.
  • On huge-blue at the head: bun test for thread-minimap-rail, thread-scroll-map-rail, thread-minimap-card-glass, hover-intent-cards, thread-minimap-feel and subtle-elevation: 56 pass, 1 skip (browser lane), 0 fail. bun run typecheck passes. check:layers ok. vp fmt --check and vp lint are clean on the changed files.

Agent session: cosmic-gray

šŸ¤– Generated with Claude Code