@@ -10,6 +10,8 @@ export default function UShapeAttentionCurve({
1010} : UShapeAttentionCurveProps ) {
1111 const [ contextFill , setContextFill ] = useState ( initialContextFill ) ;
1212 const [ animatedPath , setAnimatedPath ] = useState < string > ( '' ) ;
13+ const [ animatedEndX , setAnimatedEndX ] = useState < number > ( 0 ) ;
14+ const [ animatedMiddleX , setAnimatedMiddleX ] = useState < number > ( 0 ) ;
1315 const previousPathRef = useRef < string > ( '' ) ;
1416 const animationFrameRef = useRef < number | null > ( null ) ;
1517
@@ -19,23 +21,39 @@ export default function UShapeAttentionCurve({
1921 const padding = 70 ;
2022
2123 // Calculate curve parameters based on context fill percentage
22- // More context = deeper U (worse middle attention)
24+ // More context = deeper U (worse middle attention) AND wider span (longer context)
2325 const curveDepth = 50 + ( contextFill / 100 ) * 100 ; // Range: 50-150
2426
27+ // Calculate available width for curve
28+ const availableWidth = width - 2 * padding ;
29+
30+ // Non-linear mapping for visual clarity: 90% context uses full available width
31+ const getVisualWidth = ( contextPercent : number ) : number => {
32+ if ( contextPercent <= 30 ) return 0.3 ;
33+ if ( contextPercent <= 60 ) return 0.3 + ( contextPercent - 30 ) * ( 0.3 / 30 ) ;
34+ return 0.6 + ( contextPercent - 60 ) * ( 0.4 / 30 ) ;
35+ } ;
36+
37+ const scaledWidth = availableWidth * getVisualWidth ( contextFill ) ;
38+
2539 // Define the U-shaped curve using cubic Bézier
2640 // Start high (left), dip low (middle), end high (right)
2741 const startX = padding ;
2842 const startY = padding ;
29- const endX = width - padding ;
43+ const endX = startX + scaledWidth ;
3044 const endY = padding ;
31- const middleX = width / 2 ;
45+ const middleX = ( startX + endX ) / 2 ; // Middle of the actual curve span
3246 const middleY = padding + curveDepth ;
3347
3448 // Cubic Bézier path: smooth curve through three points
49+ // Use proportional control points for smooth curves at all widths
50+ const controlOffset = scaledWidth / 6 ; // 1/3 of half-width for smooth shoulders
51+ const verticalSmoothing = ( middleY - startY ) * 0.3 ; // 30% interpolation for gentle transitions
52+
3553 const curvePath = `
3654 M ${ startX } ,${ startY }
37- C ${ startX + 100 } ,${ startY } ${ middleX - 100 } ,${ middleY } ${ middleX } ,${ middleY }
38- C ${ middleX + 100 } ,${ middleY } ${ endX - 100 } ,${ startY } ${ endX } ,${ endY }
55+ C ${ startX + controlOffset } ,${ startY + verticalSmoothing } ${ middleX - controlOffset } ,${ middleY } ${ middleX } ,${ middleY }
56+ C ${ middleX + controlOffset } ,${ middleY } ${ endX - controlOffset } ,${ startY + verticalSmoothing } ${ endX } ,${ endY }
3957 ` . trim ( ) ;
4058
4159 // Animate path morphing for Safari compatibility
@@ -44,6 +62,8 @@ export default function UShapeAttentionCurve({
4462 if ( ! previousPathRef . current ) {
4563 previousPathRef . current = curvePath ;
4664 setAnimatedPath ( curvePath ) ;
65+ setAnimatedEndX ( endX ) ;
66+ setAnimatedMiddleX ( middleX ) ;
4767 return ;
4868 }
4969
@@ -115,11 +135,15 @@ export default function UShapeAttentionCurve({
115135 const interpolatedPath = `M ${ m1 } ,${ m2 } C ${ c1 } ,${ c2 } ${ c3 } ,${ c4 } ${ c5 } ,${ c6 } C ${ c7 } ,${ c8 } ${ c9 } ,${ c10 } ${ c11 } ,${ c12 } ` ;
116136
117137 setAnimatedPath ( interpolatedPath ) ;
138+ setAnimatedEndX ( c11 ) ; // Track the animated endpoint X coordinate
139+ setAnimatedMiddleX ( c5 ) ; // Track the animated middle point X coordinate
118140
119141 if ( progress < 1 ) {
120142 animationFrameRef . current = requestAnimationFrame ( animate ) ;
121143 } else {
122144 previousPathRef . current = curvePath ;
145+ setAnimatedEndX ( endX ) ; // Ensure final position is exact
146+ setAnimatedMiddleX ( middleX ) ; // Ensure final position is exact
123147 }
124148 } ;
125149
@@ -135,9 +159,12 @@ export default function UShapeAttentionCurve({
135159 } , [ curvePath ] ) ;
136160
137161 // Area fill path (curve + bottom edge for filled area)
162+ // Use animatedEndX to keep the right edge synchronized during transitions
163+ const currentEndX = animatedEndX || endX ;
164+ const currentMiddleX = animatedMiddleX || middleX ;
138165 const areaPath = `
139166 ${ animatedPath || curvePath }
140- L ${ endX } ,${ height - padding }
167+ L ${ currentEndX } ,${ height - padding }
141168 L ${ startX } ,${ height - padding }
142169 Z
143170 ` . trim ( ) ;
@@ -146,9 +173,9 @@ export default function UShapeAttentionCurve({
146173 const gradientId = 'attentionGradient' ;
147174
148175 const contextLevels = [
149- { label : 'Light' , value : 30 } ,
150- { label : 'Medium' , value : 60 } ,
151- { label : 'Heavy' , value : 90 } ,
176+ { label : 'Light (30%) ' , value : 30 } ,
177+ { label : 'Medium (60%) ' , value : 60 } ,
178+ { label : 'Heavy (90%) ' , value : 90 } ,
152179 ] ;
153180
154181 return (
@@ -204,13 +231,25 @@ export default function UShapeAttentionCurve({
204231 </ defs >
205232
206233 { /* Background grid for reference */ }
234+ { /* Static baseline showing full available width */ }
207235 < line
208- x1 = { startX }
236+ x1 = { padding }
209237 y1 = { height - padding }
210- x2 = { endX }
238+ x2 = { width - padding }
211239 y2 = { height - padding }
212- stroke = "var(--ifm-color-emphasis-300 )"
240+ stroke = "var(--ifm-color-emphasis-200 )"
213241 strokeWidth = "1"
242+ opacity = "0.5"
243+ />
244+
245+ { /* Dynamic baseline showing active context span */ }
246+ < line
247+ x1 = { startX }
248+ y1 = { height - padding }
249+ x2 = { currentEndX }
250+ y2 = { height - padding }
251+ stroke = "var(--ifm-color-emphasis-400)"
252+ strokeWidth = "2"
214253 strokeDasharray = "4 4"
215254 />
216255
@@ -278,15 +317,15 @@ export default function UShapeAttentionCurve({
278317 </ text >
279318
280319 < text
281- x = { middleX }
320+ x = { currentMiddleX }
282321 y = { height - padding + 30 }
283322 textAnchor = "middle"
284323 className = { styles . label }
285324 >
286325 Middle
287326 </ text >
288327 < text
289- x = { middleX }
328+ x = { currentMiddleX }
290329 y = { height - padding + 50 }
291330 textAnchor = "middle"
292331 className = { styles . sublabel }
@@ -295,15 +334,15 @@ export default function UShapeAttentionCurve({
295334 </ text >
296335
297336 < text
298- x = { endX }
337+ x = { currentEndX }
299338 y = { height - padding + 30 }
300339 textAnchor = "middle"
301340 className = { styles . label }
302341 >
303342 End
304343 </ text >
305344 < text
306- x = { endX }
345+ x = { currentEndX }
307346 y = { height - padding + 50 }
308347 textAnchor = "middle"
309348 className = { styles . sublabel }
@@ -348,7 +387,7 @@ export default function UShapeAttentionCurve({
348387 </ svg >
349388
350389 < div className = { styles . controls } >
351- < span className = { styles . controlLabel } > Context Usage :</ span >
390+ < span className = { styles . controlLabel } > Context Length :</ span >
352391 { contextLevels . map ( ( level ) => (
353392 < button
354393 key = { level . value }
@@ -363,11 +402,12 @@ export default function UShapeAttentionCurve({
363402 </ div >
364403
365404 < div className = { styles . explanation } >
366- < strong > The U-Curve Effect:</ strong > As context usage increases, the
367- attention drop in the middle becomes more pronounced. With light usage,
368- degradation is minimal. With medium usage, the U-curve becomes
369- noticeable. With heavy usage, only the beginning and end are reliably
370- processed.
405+ < strong > The U-Curve Effect:</ strong > The curve's width shows context
406+ length, while depth shows attention quality. As context length increases,
407+ the attention drop in the middle becomes more pronounced. Short contexts
408+ (30%) have minimal degradation. Medium contexts (60%) show a noticeable
409+ U-curve. Long contexts (90%) exhibit severe attention loss—only the
410+ beginning and end are reliably processed.
371411 </ div >
372412 </ div >
373413 ) ;
0 commit comments