|
@@ -8,6 +8,8 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
|
|
import useSound from "use-sound"
|
|
import useSound from "use-sound"
|
|
|
import { LRUCache } from "lru-cache"
|
|
import { LRUCache } from "lru-cache"
|
|
|
|
|
|
|
|
|
|
+import { useDebounceEffect } from "@src/utils/useDebounceEffect"
|
|
|
|
|
+
|
|
|
import type { ClineAsk, ClineMessage } from "@roo-code/types"
|
|
import type { ClineAsk, ClineMessage } from "@roo-code/types"
|
|
|
|
|
|
|
|
import { ClineSayBrowserAction, ClineSayTool, ExtensionMessage } from "@roo/ExtensionMessage"
|
|
import { ClineSayBrowserAction, ClineSayTool, ExtensionMessage } from "@roo/ExtensionMessage"
|
|
@@ -154,6 +156,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
ttl: 1000 * 60 * 15, // 15 minutes TTL for long-running tasks
|
|
ttl: 1000 * 60 * 15, // 15 minutes TTL for long-running tasks
|
|
|
}),
|
|
}),
|
|
|
)
|
|
)
|
|
|
|
|
+ const autoApproveTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
|
|
|
|
|
|
|
const clineAskRef = useRef(clineAsk)
|
|
const clineAskRef = useRef(clineAsk)
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
@@ -408,6 +411,12 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
everVisibleMessagesTsRef.current.clear() // Clear for new task
|
|
everVisibleMessagesTsRef.current.clear() // Clear for new task
|
|
|
}, [task?.ts])
|
|
}, [task?.ts])
|
|
|
|
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (isHidden) {
|
|
|
|
|
+ everVisibleMessagesTsRef.current.clear()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [isHidden])
|
|
|
|
|
+
|
|
|
useEffect(() => () => everVisibleMessagesTsRef.current.clear(), [])
|
|
useEffect(() => () => everVisibleMessagesTsRef.current.clear(), [])
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
@@ -714,17 +723,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
// NOTE: the VSCode window needs to be focused for this to work.
|
|
// NOTE: the VSCode window needs to be focused for this to work.
|
|
|
useMount(() => textAreaRef.current?.focus())
|
|
useMount(() => textAreaRef.current?.focus())
|
|
|
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- const timer = setTimeout(() => {
|
|
|
|
|
|
|
+ useDebounceEffect(
|
|
|
|
|
+ () => {
|
|
|
if (!isHidden && !sendingDisabled && !enableButtons) {
|
|
if (!isHidden && !sendingDisabled && !enableButtons) {
|
|
|
textAreaRef.current?.focus()
|
|
textAreaRef.current?.focus()
|
|
|
}
|
|
}
|
|
|
- }, 50)
|
|
|
|
|
-
|
|
|
|
|
- return () => {
|
|
|
|
|
- clearTimeout(timer)
|
|
|
|
|
- }
|
|
|
|
|
- }, [isHidden, sendingDisabled, enableButtons])
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ 50,
|
|
|
|
|
+ [isHidden, sendingDisabled, enableButtons]
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
const visibleMessages = useMemo(() => {
|
|
const visibleMessages = useMemo(() => {
|
|
|
const newVisibleMessages = modifiedMessages.filter((message) => {
|
|
const newVisibleMessages = modifiedMessages.filter((message) => {
|
|
@@ -1086,6 +1093,14 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
[],
|
|
[],
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ if (scrollToBottomSmooth && typeof (scrollToBottomSmooth as any).cancel === 'function') {
|
|
|
|
|
+ (scrollToBottomSmooth as any).cancel()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [scrollToBottomSmooth])
|
|
|
|
|
+
|
|
|
const scrollToBottomAuto = useCallback(() => {
|
|
const scrollToBottomAuto = useCallback(() => {
|
|
|
virtuosoRef.current?.scrollTo({
|
|
virtuosoRef.current?.scrollTo({
|
|
|
top: Number.MAX_SAFE_INTEGER,
|
|
top: Number.MAX_SAFE_INTEGER,
|
|
@@ -1124,13 +1139,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- let timerId: NodeJS.Timeout | undefined
|
|
|
|
|
|
|
+ let timer: NodeJS.Timeout | undefined
|
|
|
if (!disableAutoScrollRef.current) {
|
|
if (!disableAutoScrollRef.current) {
|
|
|
- timerId = setTimeout(() => scrollToBottomSmooth(), 50)
|
|
|
|
|
|
|
+ timer = setTimeout(() => scrollToBottomSmooth(), 50)
|
|
|
}
|
|
}
|
|
|
return () => {
|
|
return () => {
|
|
|
- if (timerId) {
|
|
|
|
|
- clearTimeout(timerId)
|
|
|
|
|
|
|
+ if (timer) {
|
|
|
|
|
+ clearTimeout(timer)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}, [groupedMessages.length, scrollToBottomSmooth])
|
|
}, [groupedMessages.length, scrollToBottomSmooth])
|
|
@@ -1151,21 +1166,23 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
// Effect to handle showing the checkpoint warning after a delay
|
|
// Effect to handle showing the checkpoint warning after a delay
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
// Only show the warning when there's a task but no visible messages yet
|
|
// Only show the warning when there's a task but no visible messages yet
|
|
|
- if (task && modifiedMessages.length === 0 && !isStreaming) {
|
|
|
|
|
|
|
+ if (task && modifiedMessages.length === 0 && !isStreaming && !isHidden) {
|
|
|
const timer = setTimeout(() => {
|
|
const timer = setTimeout(() => {
|
|
|
setShowCheckpointWarning(true)
|
|
setShowCheckpointWarning(true)
|
|
|
}, 5000) // 5 seconds
|
|
}, 5000) // 5 seconds
|
|
|
|
|
|
|
|
return () => clearTimeout(timer)
|
|
return () => clearTimeout(timer)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setShowCheckpointWarning(false)
|
|
|
}
|
|
}
|
|
|
- }, [task, modifiedMessages.length, isStreaming])
|
|
|
|
|
|
|
+ }, [task, modifiedMessages.length, isStreaming, isHidden])
|
|
|
|
|
|
|
|
// Effect to hide the checkpoint warning when messages appear
|
|
// Effect to hide the checkpoint warning when messages appear
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- if (modifiedMessages.length > 0 || isStreaming) {
|
|
|
|
|
|
|
+ if (modifiedMessages.length > 0 || isStreaming || isHidden) {
|
|
|
setShowCheckpointWarning(false)
|
|
setShowCheckpointWarning(false)
|
|
|
}
|
|
}
|
|
|
- }, [modifiedMessages.length, isStreaming])
|
|
|
|
|
|
|
+ }, [modifiedMessages.length, isStreaming, isHidden])
|
|
|
|
|
|
|
|
const placeholderText = task ? t("chat:typeMessage") : t("chat:typeTask")
|
|
const placeholderText = task ? t("chat:typeMessage") : t("chat:typeTask")
|
|
|
|
|
|
|
@@ -1239,31 +1256,26 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- // Only proceed if we have an ask and buttons are enabled.
|
|
|
|
|
|
|
+ if (autoApproveTimeoutRef.current) {
|
|
|
|
|
+ clearTimeout(autoApproveTimeoutRef.current)
|
|
|
|
|
+ autoApproveTimeoutRef.current = null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (!clineAsk || !enableButtons) {
|
|
if (!clineAsk || !enableButtons) {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const autoApprove = async () => {
|
|
const autoApprove = async () => {
|
|
|
if (lastMessage?.ask && isAutoApproved(lastMessage)) {
|
|
if (lastMessage?.ask && isAutoApproved(lastMessage)) {
|
|
|
- // Note that `isAutoApproved` can only return true if
|
|
|
|
|
- // lastMessage is an ask of type "browser_action_launch",
|
|
|
|
|
- // "use_mcp_server", "command", or "tool".
|
|
|
|
|
-
|
|
|
|
|
- // Add delay for write operations.
|
|
|
|
|
if (lastMessage.ask === "tool" && isWriteToolAction(lastMessage)) {
|
|
if (lastMessage.ask === "tool" && isWriteToolAction(lastMessage)) {
|
|
|
- await new Promise((resolve) => setTimeout(resolve, writeDelayMs))
|
|
|
|
|
- if (!isMountedRef.current) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ await new Promise<void>((resolve) => {
|
|
|
|
|
+ autoApproveTimeoutRef.current = setTimeout(resolve, writeDelayMs)
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
|
|
|
|
|
|
|
+ if (autoApproveTimeoutRef.current === null || autoApproveTimeoutRef.current) {
|
|
|
|
|
+ vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
|
|
|
|
|
|
|
|
- // This is copied from `handlePrimaryButtonClick`, which we used
|
|
|
|
|
- // to call from `autoApprove`. I'm not sure how many of these
|
|
|
|
|
- // things are actually needed.
|
|
|
|
|
- if (isMountedRef.current) {
|
|
|
|
|
setSendingDisabled(true)
|
|
setSendingDisabled(true)
|
|
|
setClineAsk(undefined)
|
|
setClineAsk(undefined)
|
|
|
setEnableButtons(false)
|
|
setEnableButtons(false)
|
|
@@ -1271,6 +1283,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
autoApprove()
|
|
autoApprove()
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ if (autoApproveTimeoutRef.current) {
|
|
|
|
|
+ clearTimeout(autoApproveTimeoutRef.current)
|
|
|
|
|
+ autoApproveTimeoutRef.current = null
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}, [
|
|
}, [
|
|
|
clineAsk,
|
|
clineAsk,
|
|
|
enableButtons,
|
|
enableButtons,
|