import React, { PureComponent } from 'react'; import Box from '@mui/material/Box'; import Fab from '@mui/material/Fab'; import Badge from '@mui/material/Badge'; import Tooltip from '@mui/material/Tooltip'; import CircularProgress from '@mui/material/CircularProgress'; import ChatRoundedIcon from '@mui/icons-material/ChatRounded'; import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; import AutoAwesomeRoundedIcon from '@mui/icons-material/AutoAwesomeRounded'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; import chatAutomat, { selectPendingAskUser, selectHasPendingAsk } from '../../automats/chatAutomat'; import { CHAT_BUSY, CHAT_FAB_BUSY, CHAT_FAB_PENDING, CHAT_FAB_SHADOW_BUSY, CHAT_FAB_SHADOW_IDLE, CHAT_FAB_SHADOW_PENDING, RED, RED_GLOW, WHITE, } from '../../colors'; export default class ChatFab extends PureComponent { constructor(props) { super(props); const s = chatAutomat.state; this.state = { isOpen: s.isOpen, conversationState: s.conversationState, streamPhase: s.streamPhase, isSending: s.isSending, hasPendingAsk: selectHasPendingAsk(s), pendingAsk: selectPendingAskUser(s), sseStatus: s.sseStatus, }; } componentDidMount() { this.unsub = chatAutomat.subscribe(this, (state) => ({ isOpen: state.isOpen, conversationState: state.conversationState, streamPhase: state.streamPhase, isSending: state.isSending, hasPendingAsk: selectHasPendingAsk(state), pendingAsk: selectPendingAskUser(state), sseStatus: state.sseStatus, })); } componentWillUnmount() { this.unsub?.(); } handleClick = () => { chatAutomat.actions.toggleChat(); }; render() { const { isOpen, conversationState, streamPhase, isSending, hasPendingAsk, pendingAsk } = this.state; const isBusy = conversationState === 'busy' || streamPhase === 'thinking' || streamPhase === 'working' || isSending; let tooltipTitle = 'KI-Chat öffnen'; let ariaLabel = 'KI-Chat'; if (isOpen) { tooltipTitle = 'KI-Chat schließen'; ariaLabel = 'KI-Chat schließen'; } else if (hasPendingAsk) { tooltipTitle = pendingAsk?.question ? `Antwort erforderlich: „${pendingAsk.question}“` : 'Antwort erforderlich: Das Modell wartet auf Ihre Eingabe'; ariaLabel = 'KI-Chat: Antwort erforderlich'; } else if (isBusy) { tooltipTitle = 'Modell antwortet gerade…'; ariaLabel = 'KI-Chat: Modell antwortet'; } return ( ({ position: 'fixed', bottom: { xs: 20, sm: 28 }, left: { xs: 20, sm: 164 }, zIndex: theme.zIndex.speedDial || 1050, })} > {/* Animated pulsing halo when awaiting user answer */} {hasPendingAsk && !isOpen && ( )} {/* Animated pulsating halo when model is busy thinking/working */} {!hasPendingAsk && isBusy && !isOpen && ( )} {/* Circular spinner ring when model is busy */} {!hasPendingAsk && isBusy && !isOpen && ( )} ({ width: 54, height: 54, boxShadow: hasPendingAsk ? CHAT_FAB_SHADOW_PENDING : isBusy ? CHAT_FAB_SHADOW_BUSY : CHAT_FAB_SHADOW_IDLE, background: hasPendingAsk ? CHAT_FAB_PENDING : isBusy ? CHAT_FAB_BUSY : `linear-gradient(135deg, ${theme.palette.primary.main} 0%, ${theme.palette.primary.dark} 100%)`, })} > {isOpen ? ( ) : hasPendingAsk ? ( ) : isBusy ? ( ) : ( )} ); } }