@@ -142,6 +142,7 @@ import {
142142 type TerminalContextSelection ,
143143} from "../lib/terminalContext" ;
144144import { shouldUseCompactComposerFooter } from "./composerFooterLayout" ;
145+ import { useMediaQuery } from "../hooks/useMediaQuery" ;
145146import { selectThreadTerminalState , useTerminalStateStore } from "../terminalStateStore" ;
146147import { ComposerPromptEditor , type ComposerPromptEditorHandle } from "./ComposerPromptEditor" ;
147148import { PullRequestThreadDialog } from "./PullRequestThreadDialog" ;
@@ -336,6 +337,9 @@ export default function ChatView({ threadId }: ChatViewProps) {
336337 const [ expandedWorkGroups , setExpandedWorkGroups ] = useState < Record < string , boolean > > ( { } ) ;
337338 const [ planSidebarOpen , setPlanSidebarOpen ] = useState ( false ) ;
338339 const [ isComposerFooterCompact , setIsComposerFooterCompact ] = useState ( false ) ;
340+ const [ isComposerFocused , setIsComposerFocused ] = useState ( false ) ;
341+ const isMobileViewport = useMediaQuery ( "max-sm" ) ;
342+ const isComposerCollapsedMobile = isMobileViewport && ! isComposerFocused ;
339343 // Tracks whether the user explicitly dismissed the sidebar for the active turn.
340344 const planSidebarDismissedForTurnRef = useRef < string | null > ( null ) ;
341345 // When set, the thread-change reset effect will open the sidebar instead of closing it.
@@ -3586,8 +3590,24 @@ export default function ChatView({ threadId }: ChatViewProps) {
35863590 isDragOverComposer ? "border-primary/70 bg-accent/30" : "border-border" ,
35873591 composerProviderState . composerSurfaceClassName ,
35883592 ) }
3593+ onFocusCapture = { ( ) => setIsComposerFocused ( true ) }
3594+ onBlurCapture = { ( e ) => {
3595+ // Only collapse if focus leaves the composer entirely
3596+ if ( ! e . currentTarget . contains ( e . relatedTarget as Node ) ) {
3597+ setIsComposerFocused ( false ) ;
3598+ }
3599+ } }
3600+ onClick = { ( ) => {
3601+ if ( isComposerCollapsedMobile ) {
3602+ // First expand the composer, then focus the editor after it renders
3603+ setIsComposerFocused ( true ) ;
3604+ requestAnimationFrame ( ( ) => {
3605+ composerEditorRef . current ?. focusAtEnd ( ) ;
3606+ } ) ;
3607+ }
3608+ } }
35893609 >
3590- { activePendingApproval ? (
3610+ { ! isComposerCollapsedMobile && ( activePendingApproval ? (
35913611 < div className = "rounded-t-[19px] border-b border-border/65 bg-muted/20" >
35923612 < ComposerPendingApprovalPanel
35933613 approval = { activePendingApproval }
@@ -3612,11 +3632,38 @@ export default function ChatView({ threadId }: ChatViewProps) {
36123632 planTitle = { proposedPlanTitle ( activeProposedPlan . planMarkdown ) ?? null }
36133633 />
36143634 </ div >
3615- ) : null }
3635+ ) : null ) }
3636+ { isComposerCollapsedMobile && (
3637+ < div className = "flex items-center justify-between gap-2 px-3 py-2" >
3638+ < span className = { cn (
3639+ "min-w-0 truncate text-[14px]" ,
3640+ ( activePendingProgress ? activePendingProgress . customAnswer : prompt . trim ( ) )
3641+ ? "text-foreground"
3642+ : "text-muted-foreground/35" ,
3643+ ) } >
3644+ { activePendingProgress
3645+ ? ( activePendingProgress . customAnswer || "Type your own answer, or leave this blank to use the selected option" )
3646+ : ( prompt . trim ( ) || "Ask anything..." ) }
3647+ </ span >
3648+ < button
3649+ type = "submit"
3650+ className = "flex size-8 shrink-0 items-center justify-center rounded-full bg-primary/90 text-primary-foreground disabled:opacity-30"
3651+ disabled = {
3652+ isSendBusy || isConnecting || ! composerSendState . hasSendableContent
3653+ }
3654+ aria-label = "Send message"
3655+ >
3656+ < svg width = "16" height = "16" viewBox = "0 0 16 16" fill = "none" aria-hidden = "true" >
3657+ < path d = "M8 3L8 13M8 3L4 7M8 3L12 7" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" />
3658+ </ svg >
3659+ </ button >
3660+ </ div >
3661+ ) }
36163662 < div
36173663 className = { cn (
36183664 "relative px-3 pb-2 sm:px-4" ,
36193665 hasComposerHeader ? "pt-2.5 sm:pt-3" : "pt-3.5 sm:pt-4" ,
3666+ isComposerCollapsedMobile && "hidden" ,
36203667 ) }
36213668 >
36223669 { composerMenuOpen && ! isComposerApprovalState && (
@@ -3633,7 +3680,8 @@ export default function ChatView({ threadId }: ChatViewProps) {
36333680 </ div >
36343681 ) }
36353682
3636- { ! isComposerApprovalState &&
3683+ { ! isComposerCollapsedMobile &&
3684+ ! isComposerApprovalState &&
36373685 pendingUserInputs . length === 0 &&
36383686 composerImages . length > 0 && (
36393687 < div className = "mb-3 flex flex-wrap gap-2" >
@@ -3738,7 +3786,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
37383786 </ div >
37393787
37403788 { /* Bottom toolbar */ }
3741- { activePendingApproval ? (
3789+ { isComposerCollapsedMobile ? null : activePendingApproval ? (
37423790 < div className = "flex items-center justify-end gap-2 px-2.5 pb-2.5 sm:px-3 sm:pb-3" >
37433791 < ComposerPendingApprovalActions
37443792 requestId = { activePendingApproval . requestId }
0 commit comments