From c320cedfcbad42dd6088770154febe4fef23a47e Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Mon, 27 Jul 2026 15:06:52 -0700 Subject: [PATCH] fix(composer): keep strip edge fades live when pill content grows after a resize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The overflow-fade hook resize-observes the scroller and its content wrapper, but the wrapper was a min-w-0 flex item: under a narrow container it collapsed to the scroller's content width, so the shrink-0 pill slots overflowed it invisibly. When a pill's deferred label ticker swapped in a longer label AFTER the resize settled, scrollWidth grew without resizing any observed element — no ResizeObserver callback, and the edge fades stayed stale (missing) until a manual scroll. Caught by the "narrow strip scrolls horizontally with edge fades" e2e spec, which failed deterministically because label swaps are intentionally deferred until slot layout settles. - BotActivityBar.tsx: give the scroller's content wrapper min-w-max on a multi-item strip so its border box always tracks content size and every late growth fires the ResizeObserver; keep min-w-0 for the lone-item case so a single pill/typing group still shrinks to fit (shrinkToFit) instead of overflowing into scroll - Side effect: pills no longer visually compress below their natural width inside a constrained strip (the collapsed wrapper was squeezing them); they keep readable widths and the strip scrolls as designed - Document the min-w-max dependency at the ResizeObserver in useStripOverflowFades so the wrapper's sizing isn't "simplified" back into a stale-fade regression Verified: all 13 activity/pill/typing specs in channels.spec.ts pass (including the previously failing narrow-strip fade spec and the new typing-inside-strip spec), plus both channel-composer-overflow specs. Co-authored-by: Taylor Ho Signed-off-by: Taylor Ho --- .../features/channels/ui/BotActivityBar.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/desktop/src/features/channels/ui/BotActivityBar.tsx b/desktop/src/features/channels/ui/BotActivityBar.tsx index bfe322764..32d6572c7 100644 --- a/desktop/src/features/channels/ui/BotActivityBar.tsx +++ b/desktop/src/features/channels/ui/BotActivityBar.tsx @@ -105,7 +105,11 @@ function useStripOverflowFades( const observer = new ResizeObserver(updateFades); observer.observe(node); // The content wrapper is the scroller's only child; observing it catches - // overflow changes that don't touch the scroller's own box. + // overflow changes that don't touch the scroller's own box. This relies + // on the wrapper being min-w-max (multi-item strip): if it could + // collapse to the scroller's width, content growing INSIDE it (label + // tickers swap after a resize settles) would change scrollWidth without + // resizing any observed element, leaving the fades stale. const content = node.firstElementChild; if (content !== null) { observer.observe(content); @@ -602,7 +606,19 @@ export function BotActivityComposerAction({ onScroll={updateFades} ref={scrollerRef} > -
+
{orderedAgents.map((agent) => (