fix(client): suppress run/stop button flicker during CLI restart transitions
ModelSelector and PermissionMode changes trigger a server-side CLI
restart (handler.ts:521 restartSessionWithRuntimeConfig). The server
emits status:'thinking' with verb 'Switching provider and model...' or
'Restarting session...' to keep the spinner spinning during the ~3s
restart window. ChatInput's isActive=`chatState !== 'idle' && hasMessages`
guard let the run button flash to a red 「stop」 affordance during that
window for any session with prior messages — confusing because hitting
stop has nothing to interrupt.
Add a statusVerb prefix check so isActive stays false during these
system transitions:
isSystemRestartTransition =
statusVerb.startsWith('Switching provider and model') ||
statusVerb.startsWith('Restarting session')
isActive = chatState !== 'idle' && hasMessages && !isSystemRestartTransition
Match by prefix so any future suffix (e.g. " (CLI starting...)") still
trips the guard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,7 @@ export function ChatInput({ variant = 'default' }: ChatInputProps) {
|
|||||||
const activeTabId = useTabStore((s) => s.activeTabId)
|
const activeTabId = useTabStore((s) => s.activeTabId)
|
||||||
const sessionState = useChatStore((s) => activeTabId ? s.sessions[activeTabId] : undefined)
|
const sessionState = useChatStore((s) => activeTabId ? s.sessions[activeTabId] : undefined)
|
||||||
const chatState = sessionState?.chatState ?? 'idle'
|
const chatState = sessionState?.chatState ?? 'idle'
|
||||||
|
const statusVerb = sessionState?.statusVerb ?? ''
|
||||||
const slashCommands = sessionState?.slashCommands ?? []
|
const slashCommands = sessionState?.slashCommands ?? []
|
||||||
const composerPrefill = sessionState?.composerPrefill ?? null
|
const composerPrefill = sessionState?.composerPrefill ?? null
|
||||||
const activeSession = useSessionStore((state) => activeTabId ? state.sessions.find((session) => session.id === activeTabId) ?? null : null)
|
const activeSession = useSessionStore((state) => activeTabId ? state.sessions.find((session) => session.id === activeTabId) ?? null : null)
|
||||||
@@ -69,12 +70,19 @@ export function ChatInput({ variant = 'default' }: ChatInputProps) {
|
|||||||
const hasMessages = useChatStore((s) => activeTabId ? (s.sessions[activeTabId]?.messages?.length ?? 0) > 0 : false)
|
const hasMessages = useChatStore((s) => activeTabId ? (s.sessions[activeTabId]?.messages?.length ?? 0) > 0 : false)
|
||||||
|
|
||||||
const isMemberSession = !!memberInfo
|
const isMemberSession = !!memberInfo
|
||||||
// Bug fix: ModelSelector / runtime config changes briefly flip
|
// Bug fix: ModelSelector / permission-mode changes trigger a server-side
|
||||||
// chatState off-idle (CLI reconnect / startup), which used to make the
|
// CLI restart that briefly flips chatState off-idle with one of the
|
||||||
// run button render as a red 「stop」 even on an empty session. Gate by
|
// verbs below. There is no inference to stop during that window — the
|
||||||
// hasMessages so 「stop」 only appears once we actually have something
|
// user just has to wait a couple seconds — so don't render the red
|
||||||
// running for the user to stop.
|
// 「stop」 button regardless of hasMessages. Match the verbs as
|
||||||
const isActive = chatState !== 'idle' && hasMessages
|
// prefixes since server may append context (e.g. " (CLI starting...)").
|
||||||
|
const isSystemRestartTransition =
|
||||||
|
statusVerb.startsWith('Switching provider and model') ||
|
||||||
|
statusVerb.startsWith('Restarting session')
|
||||||
|
// Also: empty sessions can't be active for the user, even if chatState
|
||||||
|
// briefly flips during initial CLI startup.
|
||||||
|
const isActive =
|
||||||
|
chatState !== 'idle' && hasMessages && !isSystemRestartTransition
|
||||||
const isWorkspaceMissing = activeSession?.workDirExists === false
|
const isWorkspaceMissing = activeSession?.workDirExists === false
|
||||||
const canSubmit = !isWorkspaceMissing && (input.trim().length > 0 || (!isMemberSession && attachments.length > 0))
|
const canSubmit = !isWorkspaceMissing && (input.trim().length > 0 || (!isMemberSession && attachments.length > 0))
|
||||||
const isHeroComposer = variant === 'hero' && !isMemberSession
|
const isHeroComposer = variant === 'hero' && !isMemberSession
|
||||||
|
|||||||
Reference in New Issue
Block a user