@@ -31,22 +31,30 @@ export const WorkspaceStatusIndicator = memo<{
3131 const [ isCollapsingPhaseSlot , setIsCollapsingPhaseSlot ] = useState ( false ) ;
3232 const shouldCollapsePhaseSlot =
3333 isCollapsingPhaseSlot || ( previousPhaseRef . current === "starting" && phase === "streaming" ) ;
34+ const showPhaseSlot = phase === "starting" || shouldCollapsePhaseSlot ;
3435
3536 useEffect ( ( ) => {
3637 const previousPhase = previousPhaseRef . current ;
3738 previousPhaseRef . current = phase ;
3839
3940 if ( previousPhase === "starting" && phase === "streaming" ) {
4041 setIsCollapsingPhaseSlot ( true ) ;
41- const timeoutId = window . setTimeout ( ( ) => {
42- setIsCollapsingPhaseSlot ( false ) ;
43- } , 150 ) ;
44- return ( ) => window . clearTimeout ( timeoutId ) ;
42+ return ;
4543 }
4644
47- setIsCollapsingPhaseSlot ( false ) ;
45+ if ( phase !== "streaming" ) {
46+ setIsCollapsingPhaseSlot ( false ) ;
47+ }
4848 } , [ phase ] ) ;
4949
50+ // Let the CSS transition decide when the handoff slot can disappear so the JS logic
51+ // does not need a mirrored timeout that can drift from the rendered duration.
52+ const handlePhaseSlotTransitionEnd = ( ) => {
53+ if ( phase === "streaming" && isCollapsingPhaseSlot ) {
54+ setIsCollapsingPhaseSlot ( false ) ;
55+ }
56+ } ;
57+
5058 // Show prompt when ask_user_question is pending - make it prominent
5159 if ( awaitingUserQuestion ) {
5260 return (
@@ -109,35 +117,19 @@ export const WorkspaceStatusIndicator = memo<{
109117 : ( currentModel ?? pendingStreamModel ?? fallbackModel ) ;
110118 const suffix = phase === "starting" ? "- starting..." : "- streaming..." ;
111119
112- if ( phase === "streaming" && ! shouldCollapsePhaseSlot ) {
113- return (
114- < div className = "text-muted flex min-w-0 items-center gap-1.5 text-xs" >
115- { modelToShow ? (
116- < >
117- < span className = "min-w-0 truncate" >
118- < ModelDisplay modelString = { modelToShow } showTooltip = { false } />
119- </ span >
120- < span className = "shrink-0 opacity-70" > { suffix } </ span >
121- </ >
122- ) : (
123- < span className = "min-w-0 truncate" > Assistant - streaming...</ span >
124- ) }
125- </ div >
126- ) ;
127- }
128-
129120 return (
130121 < div className = "text-muted flex min-w-0 items-center text-xs" >
131122 { /* Keep the old steady-state layout, but hold the spinner slot just long enough to
132123 animate the start -> stream handoff instead of flashing the label left. */ }
133- { ( phase === "starting" || shouldCollapsePhaseSlot ) && (
124+ { showPhaseSlot && (
134125 < span
135126 className = {
136127 phase === "starting"
137128 ? "mr-1.5 inline-flex w-3 shrink-0 overflow-hidden opacity-100"
138129 : "mr-0 inline-flex w-0 shrink-0 overflow-hidden opacity-0 transition-[margin,width,opacity] duration-150 ease-out"
139130 }
140131 data-phase-slot
132+ onTransitionEnd = { handlePhaseSlotTransitionEnd }
141133 >
142134 < Loader2
143135 aria-hidden = "true"
0 commit comments