@@ -29,6 +29,8 @@ export interface AnimatedStatusIconProps {
2929 isTyping ?: boolean ;
3030 /** Whether there's an error */
3131 isError ?: boolean ;
32+ /** Volume level (0-1) for volume-responsive animations */
33+ volumeLevel ?: number ;
3234 /** Base color for inactive bars */
3335 baseColor ?: string ;
3436 /** Override animation type */
@@ -61,6 +63,7 @@ const AnimatedStatusIcon: React.FC<AnimatedStatusIconProps> = ({
6163 isSpeaking,
6264 isTyping,
6365 isError,
66+ volumeLevel = 0 ,
6467 baseColor = '#9CA3AF' ,
6568 animationType : overrideAnimationType ,
6669 animationSpeed : overrideAnimationSpeed ,
@@ -226,30 +229,97 @@ const AnimatedStatusIcon: React.FC<AnimatedStatusIconProps> = ({
226229 }
227230
228231 case 'scale' : {
229- // Scale animation for line layout
232+ // Speech pattern animation - each bar reacts differently to volume
230233 if ( useLineLayout && 'delay' in bar ) {
231234 const lineBar = bar as LineBar ;
232- const cycleTime = ( time + lineBar . delay ) % 1 ;
233- const scaleTime = cycleTime < 0.5 ? cycleTime * 2 : 2 - cycleTime * 2 ;
234235
235- // Ease in-out cubic
236- const easeInOutCubic = ( t : number ) => {
237- return t < 0.5 ? 4 * t * t * t : 1 - Math . pow ( - 2 * t + 2 , 3 ) / 2 ;
238- } ;
239-
240- const easedTime = easeInOutCubic ( scaleTime ) ;
241- // Scale from base height to max height while maintaining the relative proportions
242- const scaleFactor = 1 + easedTime * 0.5 ; // Scale up to 1.5x
243- const height = lineBar . baseHeight * scaleFactor ;
244- const y = 12 - height / 2 ; // Keep bars centered vertically
236+ // Use volume level to determine overall activity
237+ const volumeIntensity = Math . max ( 0 , Math . min ( 1 , volumeLevel ) ) ;
238+
239+ // Each bar has different sensitivity and frequency response
240+ const barCharacteristics = [
241+ { sensitivity : 0.8 , frequency : 1.2 , baseActivity : 0.3 } , // Bar 0 - Low freq, less sensitive
242+ { sensitivity : 1.0 , frequency : 1.8 , baseActivity : 0.4 } , // Bar 1 - Mid-low freq
243+ { sensitivity : 1.2 , frequency : 2.5 , baseActivity : 0.5 } , // Bar 2 - Center, most responsive
244+ { sensitivity : 1.0 , frequency : 2.0 , baseActivity : 0.4 } , // Bar 3 - Mid-high freq
245+ { sensitivity : 0.9 , frequency : 1.5 , baseActivity : 0.35 } , // Bar 4 - High freq
246+ ] ;
247+
248+ const barChar = barCharacteristics [ index ] || barCharacteristics [ 2 ] ;
249+
250+ // Create dynamic time-based variation for each bar
251+ const time = animationTime % 1 ;
252+ const barTime = ( time * barChar . frequency ) % 1 ;
253+
254+ // Generate pseudo-random speech-like pattern
255+ const speechPattern =
256+ Math . sin ( barTime * 2 * Math . PI ) * 0.3 +
257+ Math . sin ( barTime * 6 * Math . PI ) * 0.2 +
258+ Math . sin ( barTime * 12 * Math . PI ) * 0.1 ;
259+
260+ // Each bar responds differently based on volume and its characteristics
261+ const barResponse = volumeIntensity * barChar . sensitivity ;
262+
263+ // Combine base activity, volume response, and speech pattern
264+ const activity = Math . max (
265+ 0 ,
266+ Math . min (
267+ 1 ,
268+ barChar . baseActivity +
269+ barResponse * 0.6 +
270+ speechPattern * volumeIntensity * 0.4
271+ )
272+ ) ;
273+
274+ // Scale the bar height based on activity
275+ const heightScale = 0.7 + activity * 1.1 ; // Range from 0.7x to 1.8x
276+ const height = lineBar . baseHeight * heightScale ;
277+ const y = 12 - height / 2 ;
278+
279+ // Opacity varies with activity
280+ const opacity = 0.4 + activity * 0.6 ;
245281
246282 return {
247- opacity : 1 ,
283+ opacity,
248284 height,
249285 y,
250286 transform : '' ,
251287 } ;
252288 }
289+
290+ // For circular layout, create a wave-like speech pattern
291+ if ( ! useLineLayout ) {
292+ const volumeIntensity = Math . max ( 0 , Math . min ( 1 , volumeLevel ) ) ;
293+ const time = animationTime % 1 ;
294+
295+ // Create different wave patterns for each bar
296+ const barAngle = ( index / actualBarCount ) * 2 * Math . PI ;
297+ const wavePattern =
298+ Math . sin ( time * 4 * Math . PI + barAngle ) * 0.3 +
299+ Math . sin ( time * 8 * Math . PI + barAngle * 2 ) * 0.2 +
300+ Math . sin ( time * 16 * Math . PI + barAngle * 3 ) * 0.1 ;
301+
302+ // Each bar has different sensitivity based on position
303+ const positionSensitivity =
304+ 0.7 + 0.3 * Math . sin ( barAngle + Math . PI / 4 ) ;
305+
306+ const activity = Math . max (
307+ 0 ,
308+ Math . min (
309+ 1 ,
310+ 0.3 +
311+ volumeIntensity * positionSensitivity * 0.5 +
312+ wavePattern * volumeIntensity * 0.2
313+ )
314+ ) ;
315+
316+ return {
317+ opacity : 0.4 + activity * 0.6 ,
318+ transform :
319+ activity > 0.5 ? `scale(${ 1 + ( activity - 0.5 ) * 0.4 } )` : '' ,
320+ } ;
321+ }
322+
253323 return { opacity : 1 , transform : '' } ;
254324 }
255325
0 commit comments