1- import { memo , useState , useMemo } from 'react' ;
1+ import { memo , useState , useMemo , useRef , useEffect } from 'react' ;
22
33import { motion } from 'framer-motion' ;
44import { BrainIcon , ChevronDownIcon } from 'lucide-react' ;
@@ -21,6 +21,9 @@ type TaskMessageReasoningProps = {
2121
2222function TaskMessageReasoningImpl ( { message } : TaskMessageReasoningProps ) {
2323 const [ isCollapsed , setIsCollapsed ] = useState ( true ) ;
24+ const [ showTopBlur , setShowTopBlur ] = useState ( false ) ;
25+ const [ showBottomBlur , setShowBottomBlur ] = useState ( false ) ;
26+ const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
2427
2528 const { taskID } = useSafeSearchParams ( ) ;
2629 const { agentexClient } = useAgentexClient ( ) ;
@@ -61,6 +64,30 @@ function TaskMessageReasoningImpl({ message }: TaskMessageReasoningProps) {
6164 ] . join ( '\n\n' ) ;
6265 } , [ message . content ] ) ;
6366
67+ const updateBlurEffects = ( ) => {
68+ const container = scrollContainerRef . current ;
69+ if ( ! container ) return ;
70+
71+ const { scrollTop, scrollHeight, clientHeight } = container ;
72+ const isScrollable = scrollHeight > clientHeight ;
73+
74+ setShowTopBlur ( isScrollable && scrollTop > 10 ) ;
75+ setShowBottomBlur (
76+ isScrollable && scrollTop < scrollHeight - clientHeight - 10
77+ ) ;
78+ } ;
79+
80+ useEffect ( ( ) => {
81+ const container = scrollContainerRef . current ;
82+ if ( ! container ) return ;
83+
84+ updateBlurEffects ( ) ;
85+ const resizeObserver = new ResizeObserver ( updateBlurEffects ) ;
86+ resizeObserver . observe ( container ) ;
87+
88+ return ( ) => resizeObserver . disconnect ( ) ;
89+ } , [ isCollapsed , reasoningText ] ) ;
90+
6491 return (
6592 < motion . div
6693 className = "w-full"
@@ -90,9 +117,29 @@ function TaskMessageReasoningImpl({ message }: TaskMessageReasoningProps) {
90117 />
91118 </ button >
92119 < Collapsible collapsed = { isCollapsed } >
93- < MarkdownResponse className = "ml-6 grid border-l-4 border-gray-300 pl-3 text-gray-500" >
94- { reasoningText }
95- </ MarkdownResponse >
120+ < div className = "relative ml-6" >
121+ < div
122+ className = { cn (
123+ 'pointer-events-none absolute inset-x-0 top-0 z-10 h-8 bg-gradient-to-b from-white to-transparent transition-opacity' ,
124+ showTopBlur ? 'opacity-100' : 'opacity-0'
125+ ) }
126+ />
127+ < div
128+ ref = { scrollContainerRef }
129+ onScroll = { updateBlurEffects }
130+ className = "max-h-48 overflow-y-auto"
131+ >
132+ < MarkdownResponse className = "grid border-l-4 border-gray-300 pl-3 text-gray-500" >
133+ { reasoningText }
134+ </ MarkdownResponse >
135+ </ div >
136+ < div
137+ className = { cn (
138+ 'pointer-events-none absolute inset-x-0 bottom-0 z-10 h-8 bg-gradient-to-t from-white to-transparent transition-opacity' ,
139+ showBottomBlur ? 'opacity-100' : 'opacity-0'
140+ ) }
141+ />
142+ </ div >
96143 </ Collapsible >
97144 </ motion . div >
98145 ) ;
0 commit comments