@@ -8,17 +8,19 @@ import {
88 Power ,
99 RotateCw ,
1010} from "lucide-react" ;
11+ import { useState } from "react" ;
1112import { type Messages , t , useT } from "../../i18n" ;
1213import type { Bot , BotState } from "../../lib/protocol.gen" ;
1314import { TONES , type Tone as ToneName } from "../../ui/tone" ;
14- import { ReadyIcon , type StateIcon , WorkingIcon } from "./StateIcons" ;
15+ import { type StateIcon , WorkingIcon } from "./StateIcons" ;
1516
1617type Tone = Exclude < ToneName , "accent" > ;
1718
1819export interface StateView {
1920 label : string ;
2021 tone : Tone ;
21- icon : StateIcon ;
22+ /** None for idle: a bot doing nothing is free, which needs no badge. */
23+ icon : StateIcon | null ;
2224 spin ?: boolean ;
2325 /** One sentence on what the state means for the owner. */
2426 hint : string ;
@@ -30,7 +32,7 @@ type Look = Omit<StateView, "label" | "hint">;
3032const looks : Record < BotState , Look > = {
3133 offline : { tone : "quiet" , icon : Power } ,
3234 launching : { tone : "work" , icon : LoaderCircle , spin : true } ,
33- idle : { tone : "ok" , icon : ReadyIcon } ,
35+ idle : { tone : "ok" , icon : null } ,
3436 busy : { tone : "work" , icon : WorkingIcon } ,
3537 needs_approval : { tone : "warn" , icon : Hand } ,
3638 rate_limited : { tone : "warn" , icon : Hourglass } ,
@@ -70,10 +72,19 @@ export function BotStateBadge({
7072 const messages = useT ( ) ;
7173 const view = stateView ( bot , crewPaused , messages ) ;
7274 const Icon = view . icon ;
75+ // The badge fades in when a bot leaves idle in front of the owner; one
76+ // that was already there when the list opened shows still.
77+ const [ wasShown , setWasShown ] = useState ( Icon !== null ) ;
78+ const [ entering , setEntering ] = useState ( false ) ;
79+ if ( ( Icon !== null ) !== wasShown ) {
80+ setWasShown ( Icon !== null ) ;
81+ setEntering ( Icon !== null ) ;
82+ }
83+ if ( ! Icon ) return null ;
7384 return (
7485 < span
7586 title = { view . hint }
76- className = { `inline-flex shrink-0 items-center gap-1 font-medium transition-colors duration-300 ${ compact ? "text-xs" : "text-sm" } ${ TONES [ view . tone ] . text } ` }
87+ className = { `inline-flex shrink-0 items-center gap-1 font-medium transition-colors duration-300 ${ compact ? "text-xs" : "text-sm" } ${ TONES [ view . tone ] . text } ${ entering ? "animate-fade" : "" } ` }
7788 >
7889 < Icon
7990 aria-hidden
0 commit comments