diff --git a/src/components/chat/agent-selector.tsx b/src/components/chat/agent-selector.tsx index f79a6e5..cb1e472 100644 --- a/src/components/chat/agent-selector.tsx +++ b/src/components/chat/agent-selector.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react" +import { useEffect, useRef, useState } from "react" import { useTranslations } from "next-intl" import { acpListAgents } from "@/lib/tauri" import type { AgentType, AcpAgentInfo } from "@/lib/types" @@ -30,6 +30,16 @@ export function AgentSelector({ const [selected, setSelected] = useState( defaultAgentType ?? null ) + const onSelectRef = useRef(onSelect) + const onAgentsLoadedRef = useRef(onAgentsLoaded) + + useEffect(() => { + onSelectRef.current = onSelect + }, [onSelect]) + + useEffect(() => { + onAgentsLoadedRef.current = onAgentsLoaded + }, [onAgentsLoaded]) useEffect(() => { let cancelled = false @@ -46,7 +56,7 @@ export function AgentSelector({ ) const visible = sorted.filter((a) => a.enabled) setAgents(visible) - onAgentsLoaded?.(visible) + onAgentsLoadedRef.current?.(visible) if (defaultAgentType) { const found = visible.find( (a) => a.agent_type === defaultAgentType && a.available @@ -57,20 +67,20 @@ export function AgentSelector({ const first = visible.find((a) => a.available) if (first) { setSelected(first.agent_type) - onSelect(first.agent_type) + onSelectRef.current(first.agent_type) } } } else { const first = visible.find((a) => a.available) if (first) { setSelected(first.agent_type) - onSelect(first.agent_type) + onSelectRef.current(first.agent_type) } } } catch { if (!cancelled && requestId === latestRequestId) { setAgents([]) - onAgentsLoaded?.([]) + onAgentsLoadedRef.current?.([]) } } } @@ -106,7 +116,7 @@ export function AgentSelector({ unlisten() } } - }, [defaultAgentType, onAgentsLoaded, onSelect]) + }, [defaultAgentType]) const handleSelect = (agentType: AgentType) => { setSelected(agentType) diff --git a/src/components/chat/welcome-input-panel.tsx b/src/components/chat/welcome-input-panel.tsx index 3aef442..8be580b 100644 --- a/src/components/chat/welcome-input-panel.tsx +++ b/src/components/chat/welcome-input-panel.tsx @@ -301,6 +301,8 @@ export function WelcomeInputPanel({ disconnect: connDisconnect, sessionId: connSessionId, } = conn + const isConnecting = + connStatus === "connecting" || connStatus === "downloading" const connectionModes = useMemo( () => conn.modes?.available_modes ?? [], [conn.modes?.available_modes] @@ -326,11 +328,38 @@ export function WelcomeInputPanel({ const externalIdSavedRef = useRef(false) const sessionIdRef = useRef(null) const refreshingCurrentAgentRef = useRef(false) + const agentStatusRefreshTimerRef = useRef | null>(null) + const phaseRef = useRef(phase) + const workingDirRef = useRef(workingDir) + const connStatusRef = useRef(connStatus) + const isConnectingRef = useRef(false) + const connConnectRef = useRef(connConnect) + const connDisconnectRef = useRef(connDisconnect) useEffect(() => { if (connSessionId) { sessionIdRef.current = connSessionId } }, [connSessionId]) + useEffect(() => { + phaseRef.current = phase + }, [phase]) + useEffect(() => { + workingDirRef.current = workingDir + }, [workingDir]) + useEffect(() => { + connStatusRef.current = connStatus + }, [connStatus]) + useEffect(() => { + isConnectingRef.current = isConnecting + }, [isConnecting]) + useEffect(() => { + connConnectRef.current = connConnect + }, [connConnect]) + useEffect(() => { + connDisconnectRef.current = connDisconnect + }, [connDisconnect]) const trySaveExternalId = useCallback(() => { if ( @@ -353,29 +382,33 @@ export function WelcomeInputPanel({ if (connSessionId) trySaveExternalId() }, [connSessionId, trySaveExternalId]) - const isConnecting = - connStatus === "connecting" || connStatus === "downloading" - useEffect(() => { let cancelled = false let unlisten: (() => void) | null = null const syncCurrentAgentStatus = async () => { if (cancelled) return - if (phase !== "welcome") return - if (!workingDir) return + if (phaseRef.current !== "welcome") return + const currentWorkingDir = workingDirRef.current + if (!currentWorkingDir) return if (refreshingCurrentAgentRef.current) return - if (connStatus === "prompting" || isConnecting) return + const currentConnStatus = connStatusRef.current + if (currentConnStatus === "prompting" || isConnectingRef.current) return refreshingCurrentAgentRef.current = true try { setAgentConnectError(null) - if (connStatus === "connected") { - await connDisconnect() + if (currentConnStatus === "connected") { + await connDisconnectRef.current() } - await connConnect(selectedAgentRef.current, workingDir, undefined, { - source: "auto_link", - }) + await connConnectRef.current( + selectedAgentRef.current, + currentWorkingDir, + undefined, + { + source: "auto_link", + } + ) if (!cancelled) { setAgentConnectError(null) } @@ -403,7 +436,12 @@ export function WelcomeInputPanel({ ) { return } - void syncCurrentAgentStatus() + if (agentStatusRefreshTimerRef.current) { + clearTimeout(agentStatusRefreshTimerRef.current) + } + agentStatusRefreshTimerRef.current = setTimeout(() => { + void syncCurrentAgentStatus() + }, 120) }) ) .then((dispose) => { @@ -419,11 +457,15 @@ export function WelcomeInputPanel({ return () => { cancelled = true + if (agentStatusRefreshTimerRef.current) { + clearTimeout(agentStatusRefreshTimerRef.current) + agentStatusRefreshTimerRef.current = null + } if (unlisten) { unlisten() } } - }, [connConnect, connDisconnect, connStatus, isConnecting, phase, workingDir]) + }, []) const prevStatusRef = useRef(connStatus)