From ff1e043bbe587808e2ec51b7e2f82038499fd98d Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 22 Apr 2026 17:50:59 +0300 Subject: [PATCH] fix(ui): scroll-shadow bottom fade + hide tasks row when nothing active - ScrollShadow: 1px subpixel tolerance so bottom fade clears at scroll end on Retina (fractional scrollTop was keeping data-bottom-scroll stuck) - StatusRow: hide tasks indicator when no in-progress/pending todos remain --- packages/ui/src/components/chat/StatusRow.tsx | 2 +- packages/ui/src/components/ui/ScrollShadow.tsx | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/chat/StatusRow.tsx b/packages/ui/src/components/chat/StatusRow.tsx index 36666fd3..039e5ff2 100644 --- a/packages/ui/src/components/chat/StatusRow.tsx +++ b/packages/ui/src/components/chat/StatusRow.tsx @@ -200,7 +200,7 @@ export const StatusRow: React.FC = ({ return { active, left }; }, [visibleTodos]); - const hasTodoContent = showTodos && visibleTodos.length > 0; + const hasTodoContent = showTodos && statusSummary.left > 0; const hasAssistantContent = showAssistantStatus && ( isWorking || Boolean(wasAborted) || diff --git a/packages/ui/src/components/ui/ScrollShadow.tsx b/packages/ui/src/components/ui/ScrollShadow.tsx index 9d421add..cd17df9c 100644 --- a/packages/ui/src/components/ui/ScrollShadow.tsx +++ b/packages/ui/src/components/ui/ScrollShadow.tsx @@ -89,11 +89,18 @@ export const ScrollShadow = React.forwardRef( return; } - const hasBefore = orientation === "vertical" ? el.scrollTop > offset : el.scrollLeft > offset; + // Subpixel tolerance: on hi-DPI (Retina) and with fractional scrollTop, + // scrollTop+clientHeight can fall ~0.5px short of scrollHeight at the very end, + // which would otherwise keep the bottom fade visible after fully scrolling. + const SUBPIXEL_TOLERANCE = 1; + const hasBefore = + orientation === "vertical" + ? el.scrollTop > offset + SUBPIXEL_TOLERANCE + : el.scrollLeft > offset + SUBPIXEL_TOLERANCE; let hasAfter = orientation === "vertical" - ? el.scrollTop + el.clientHeight + offset < el.scrollHeight - : el.scrollLeft + el.clientWidth + offset < el.scrollWidth; + ? el.scrollHeight - (el.scrollTop + el.clientHeight) > offset + SUBPIXEL_TOLERANCE + : el.scrollWidth - (el.scrollLeft + el.clientWidth) > offset + SUBPIXEL_TOLERANCE; const effectiveHasBefore = hideTopShadow && orientation === "vertical" ? false : hasBefore;