@@ -55,10 +55,16 @@ export const UsagePreview = ({ onViewDetails }: UsagePreviewProps) => {
5555 vscode . postMessage ( { type : "getUsagePreview" } )
5656
5757 // Listen for the response
58+ let timeoutId : ReturnType < typeof setTimeout > | null = null
5859 const handleMessage = ( event : MessageEvent ) => {
5960 const message = event . data
6061
6162 if ( message . type === "usagePreviewData" ) {
63+ // Clear timeout on success/error to avoid stale timeout flipping UI into error
64+ if ( timeoutId ) {
65+ clearTimeout ( timeoutId )
66+ }
67+
6268 if ( message . error ) {
6369 setError ( message . error )
6470 } else if ( message . data ) {
@@ -76,23 +82,27 @@ export const UsagePreview = ({ onViewDetails }: UsagePreviewProps) => {
7682 window . addEventListener ( "message" , handleMessage )
7783
7884 // Clean up listener after 10 seconds (timeout)
79- const timeout = setTimeout ( ( ) => {
85+ timeoutId = setTimeout ( ( ) => {
8086 if ( isLoading ) {
8187 setError ( t ( "cloud:usagePreview.failedToLoad" ) )
8288 setIsLoading ( false )
8389 }
8490 } , 10000 )
8591
8692 return ( ) => {
87- clearTimeout ( timeout )
93+ if ( timeoutId ) {
94+ clearTimeout ( timeoutId )
95+ }
8896 window . removeEventListener ( "message" , handleMessage )
8997 }
9098 } , [ ] ) // eslint-disable-line react-hooks/exhaustive-deps
9199
92100 const getBarHeight = ( cost : number ) : number => {
93101 if ( ! data || ! data . days || data . days . length === 0 ) return 1
94- const maxCost = ~ ~ Math . max ( ...data . days . map ( ( d ) => d . cost ) ) // Avoid NaN
95- return Math . max ( 1 , ~ ~ ( cost / maxCost ) * 100 ) // Enforce minimum height for visibility
102+ const maxCost = Math . max ( ...data . days . map ( ( d ) => d . cost ) )
103+ if ( ! Number . isFinite ( maxCost ) || maxCost <= 0 ) return 1
104+ // Compute percentage first, then round; enforce minimum height for visibility
105+ return Math . max ( 1 , Math . round ( ( cost / maxCost ) * 100 ) )
96106 }
97107
98108 // Retry loading
0 commit comments