@@ -4,7 +4,8 @@ import { useQuery } from "@tanstack/react-query";
44import { getRepo } from "@/api/repos" ;
55import { MessageThread } from "@/components/message/MessageThread" ;
66import { PromptInput , type PromptInputHandle } from "@/components/message/PromptInput" ;
7- import { X , VolumeX , FolderOpen , Plug , Settings , CornerUpLeft , GitCommitHorizontal , Brain , ShieldOff , Code } from "lucide-react" ;
7+ import { FloatingTTSButton } from '@/components/message/FloatingTTSButton'
8+ import { X , FolderOpen , Plug , Settings , CornerUpLeft , GitCommitHorizontal , Brain , ShieldOff , Code } from "lucide-react" ;
89import { ModelSelectDialog } from "@/components/model/ModelSelectDialog" ;
910import { Header } from "@/components/ui/header" ;
1011import { SessionList } from "@/components/session/SessionList" ;
@@ -130,13 +131,14 @@ export function SessionDetail() {
130131 const { open : openSettings } = useSettingsDialog ( ) ;
131132 const { model, modelString } = useModelSelection ( opcodeUrl , repoDirectory ) ;
132133 const isEditingMessage = useUIState ( ( state ) => state . isEditingMessage ) ;
133- const { isPlaying , stop } = useTTS ( ) ;
134+ const { isEnabled : ttsEnabled } = useTTS ( ) ;
134135 const setSessionStatus = useSessionStatus ( ( state ) => state . setStatus ) ;
135136 const { current : currentQuestion , reply : replyToQuestion , reject : rejectQuestion } = useQuestions ( ) ;
136137
137138 const sessionStatus = useSessionStatusForSession ( sessionId ) ;
138139 const isSessionActive = sessionStatus . type === 'busy' || sessionStatus . type === 'retry' ;
139140 const lastAssistantMessage = messages ?. filter ( m => m . info . role === 'assistant' ) . at ( - 1 ) ;
141+ const lastAssistantText = lastAssistantMessage ?. parts . filter ( p => p . type === 'text' ) . map ( p => p . text ) . join ( '\n\n' ) || '' ;
140142 const hasIncompleteMessages = lastAssistantMessage ? ! ( 'completed' in lastAssistantMessage . info . time && lastAssistantMessage . info . time . completed ) : false ;
141143 const hasActiveStream = hasIncompleteMessages && isSessionActive ;
142144
@@ -497,20 +499,9 @@ export function SessionDetail() {
497499 < span className = "text-sm font-medium" > Waiting for shortcut key...</ span >
498500 </ div >
499501 ) }
500- { isPlaying && ! leaderActive && (
501- < button
502- onMouseDown = { ( e ) => e . preventDefault ( ) }
503- onTouchEnd = { ( e ) => {
504- e . preventDefault ( )
505- stop ( )
506- } }
507- onClick = { stop }
508- className = "absolute -top-12 left-0 md:left-4 z-50 flex items-center gap-2 px-4 py-2 rounded-xl bg-gradient-to-br from-red-700 to-red-800 hover:from-red-600 hover:to-red-700 text-destructive-foreground border-2 border-red-600/80 hover:border-red-500 shadow-2xl shadow-red-600/40 hover:shadow-red-600/60 backdrop-blur-md transition-all duration-200 active:scale-95 hover:scale-105 ring-2 ring-red-600/30 hover:ring-red-600/50 animate-[pulse_2s_cubic-bezier(0.4,0,0.6,1)_infinite]"
509- aria-label = "Stop Audio"
510- >
511- < VolumeX className = "w-6 h-6" />
512- < span className = "text-sm font-medium hidden sm:inline" > Stop Audio</ span >
513- </ button >
502+
503+ { ttsEnabled && lastAssistantText && ! hasPromptContent && ! hasActiveStream && (
504+ < FloatingTTSButton content = { lastAssistantText } />
514505 ) }
515506 { currentQuestion && currentQuestion . sessionID === sessionId && (
516507 < QuestionPrompt
0 commit comments