diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index 76e1b0858c57..1c01d5b7f56a 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -203,17 +203,34 @@ export default function ChatInput({ setHasUserTyped(false); }, [initialValue]); // Keep only initialValue as a dependency + // Track if we've already set the recipe prompt to avoid re-setting it + const hasSetRecipePromptRef = useRef(false); + // Handle recipe prompt updates useEffect(() => { - // If recipe is accepted and we have an initial prompt, and no messages yet, set the prompt - if (recipeAccepted && initialPrompt && messages.length === 0 && !displayValue.trim()) { + // If recipe is accepted and we have an initial prompt, and no messages yet, and we haven't set it before + if ( + recipeAccepted && + initialPrompt && + messages.length === 0 && + !hasSetRecipePromptRef.current + ) { setDisplayValue(initialPrompt); setValue(initialPrompt); + hasSetRecipePromptRef.current = true; setTimeout(() => { textAreaRef.current?.focus(); }, 0); } - }, [recipeAccepted, initialPrompt, messages.length, displayValue]); + // we don't need hasSetRecipePromptRef in the dependency array because it is a ref that persists across renders + }, [recipeAccepted, initialPrompt, messages.length]); + + // Reset the recipe prompt flag when the recipe changes or messages are added + useEffect(() => { + if (messages.length > 0 || !recipeAccepted || !initialPrompt) { + hasSetRecipePromptRef.current = false; + } + }, [recipeAccepted, initialPrompt, messages.length]); // Draft functionality - load draft if no initial value or recipe useEffect(() => { diff --git a/ui/desktop/src/hooks/useRecipeManager.ts b/ui/desktop/src/hooks/useRecipeManager.ts index 17ed7c41bca2..b61a07c391a0 100644 --- a/ui/desktop/src/hooks/useRecipeManager.ts +++ b/ui/desktop/src/hooks/useRecipeManager.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState, useRef } from 'react'; import { createRecipe, Recipe } from '../recipe'; import { Message, createUserMessage } from '../types/message'; -import { updateSystemPromptWithParameters } from '../utils/providerUtils'; +import { updateSystemPromptWithParameters, substituteParameters } from '../utils/providerUtils'; import { useChatContext } from '../contexts/ChatContext'; interface LocationState { @@ -115,18 +115,6 @@ export const useRecipeManager = (messages: Message[], locationState?: LocationSt setReadyForAutoUserPrompt(true); }, []); - // Substitute parameters in prompt - const substituteParameters = (prompt: string, params: Record): string => { - let substitutedPrompt = prompt; - - for (const key in params) { - // Escape special characters in the key (parameter) and match optional whitespace - const regex = new RegExp(`{{\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*}}`, 'g'); - substitutedPrompt = substitutedPrompt.replace(regex, params[key]); - } - return substitutedPrompt; - }; - // Get the recipe's initial prompt (always return the actual prompt, don't modify based on conversation state) const initialPrompt = useMemo(() => { if (!recipeConfig?.prompt || !recipeAccepted || recipeConfig?.isScheduledExecution) { diff --git a/ui/desktop/src/utils/providerUtils.ts b/ui/desktop/src/utils/providerUtils.ts index fa099f8e110f..778a52d06728 100644 --- a/ui/desktop/src/utils/providerUtils.ts +++ b/ui/desktop/src/utils/providerUtils.ts @@ -61,7 +61,7 @@ There may be (but not always) some tools mentioned in the instructions which you `; // Helper function to substitute parameters in text -const substituteParameters = (text: string, params: Record): string => { +export const substituteParameters = (text: string, params: Record): string => { let substitutedText = text; for (const key in params) {