| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- import { useCallback } from "react"
- import { randomUUID } from "crypto"
- import type { WebviewMessage } from "@roo-code/types"
- import { getGlobalCommand } from "../../lib/utils/commands.js"
- import { useCLIStore } from "../store.js"
- import { useUIStateStore } from "../stores/uiStateStore.js"
- export interface UseTaskSubmitOptions {
- sendToExtension: ((msg: WebviewMessage) => void) | null
- runTask: ((prompt: string) => Promise<void>) | null
- seenMessageIds: React.MutableRefObject<Set<string>>
- firstTextMessageSkipped: React.MutableRefObject<boolean>
- }
- export interface UseTaskSubmitReturn {
- handleSubmit: (text: string) => Promise<void>
- handleApprove: () => void
- handleReject: () => void
- }
- /**
- * Hook to handle task submission, user responses, and approvals.
- *
- * Responsibilities:
- * - Process user message submissions
- * - Detect and handle global commands (like /new)
- * - Handle pending ask responses
- * - Start new tasks or continue existing ones
- * - Handle Y/N approval responses
- */
- export function useTaskSubmit({
- sendToExtension,
- runTask,
- seenMessageIds,
- firstTextMessageSkipped,
- }: UseTaskSubmitOptions): UseTaskSubmitReturn {
- const {
- pendingAsk,
- hasStartedTask,
- isComplete,
- addMessage,
- setPendingAsk,
- setHasStartedTask,
- setLoading,
- setComplete,
- setError,
- } = useCLIStore()
- const { setShowCustomInput, setIsTransitioningToCustomInput } = useUIStateStore()
- /**
- * Handle user text submission (from input or followup question)
- */
- const handleSubmit = useCallback(
- async (text: string) => {
- if (!sendToExtension || !text.trim()) {
- return
- }
- const trimmedText = text.trim()
- if (trimmedText === "__CUSTOM__") {
- return
- }
- // Check for CLI global action commands (e.g., /new)
- if (trimmedText.startsWith("/")) {
- const commandMatch = trimmedText.match(/^\/(\w+)(?:\s|$)/)
- if (commandMatch && commandMatch[1]) {
- const globalCommand = getGlobalCommand(commandMatch[1])
- if (globalCommand?.action === "clearTask") {
- // Reset CLI state and send clearTask to extension.
- useCLIStore.getState().reset()
- // Reset component-level refs to avoid stale message tracking.
- seenMessageIds.current.clear()
- firstTextMessageSkipped.current = false
- sendToExtension({ type: "clearTask" })
- // Re-request state, commands and modes since reset() cleared them.
- sendToExtension({ type: "requestCommands" })
- sendToExtension({ type: "requestModes" })
- return
- }
- }
- }
- if (pendingAsk) {
- addMessage({ id: randomUUID(), role: "user", content: trimmedText })
- sendToExtension({
- type: "askResponse",
- askResponse: "messageResponse",
- text: trimmedText,
- })
- setPendingAsk(null)
- setShowCustomInput(false)
- setIsTransitioningToCustomInput(false)
- setLoading(true)
- } else if (!hasStartedTask) {
- setHasStartedTask(true)
- setLoading(true)
- addMessage({ id: randomUUID(), role: "user", content: trimmedText })
- try {
- if (runTask) {
- await runTask(trimmedText)
- }
- } catch (err) {
- setError(err instanceof Error ? err.message : String(err))
- setLoading(false)
- }
- } else {
- if (isComplete) {
- setComplete(false)
- }
- setLoading(true)
- addMessage({ id: randomUUID(), role: "user", content: trimmedText })
- sendToExtension({
- type: "askResponse",
- askResponse: "messageResponse",
- text: trimmedText,
- })
- }
- },
- [
- sendToExtension,
- runTask,
- pendingAsk,
- hasStartedTask,
- isComplete,
- addMessage,
- setPendingAsk,
- setHasStartedTask,
- setLoading,
- setComplete,
- setError,
- setShowCustomInput,
- setIsTransitioningToCustomInput,
- seenMessageIds,
- firstTextMessageSkipped,
- ],
- )
- /**
- * Handle approval (Y key)
- */
- const handleApprove = useCallback(() => {
- if (!sendToExtension) {
- return
- }
- sendToExtension({ type: "askResponse", askResponse: "yesButtonClicked" })
- setPendingAsk(null)
- setLoading(true)
- }, [sendToExtension, setPendingAsk, setLoading])
- /**
- * Handle rejection (N key)
- */
- const handleReject = useCallback(() => {
- if (!sendToExtension) {
- return
- }
- sendToExtension({ type: "askResponse", askResponse: "noButtonClicked" })
- setPendingAsk(null)
- setLoading(true)
- }, [sendToExtension, setPendingAsk, setLoading])
- return {
- handleSubmit,
- handleApprove,
- handleReject,
- }
- }
|