diff --git a/ui/desktop/src/components/RecipesView.tsx b/ui/desktop/src/components/RecipesView.tsx index 8fc8d6726ff0..9096c3e6a7c2 100644 --- a/ui/desktop/src/components/RecipesView.tsx +++ b/ui/desktop/src/components/RecipesView.tsx @@ -43,6 +43,17 @@ export default function RecipesView({ onLoadRecipe }: RecipesViewProps = {}) { const [importGlobal, setImportGlobal] = useState(true); const [importing, setImporting] = useState(false); + // Create Recipe state + const [showCreateDialog, setShowCreateDialog] = useState(false); + const [createTitle, setCreateTitle] = useState(''); + const [createDescription, setCreateDescription] = useState(''); + const [createInstructions, setCreateInstructions] = useState(''); + const [createPrompt, setCreatePrompt] = useState(''); + const [createActivities, setCreateActivities] = useState(''); + const [createRecipeName, setCreateRecipeName] = useState(''); + const [createGlobal, setCreateGlobal] = useState(true); + const [creating, setCreating] = useState(false); + useEffect(() => { loadSavedRecipes(); }, []); @@ -223,6 +234,106 @@ export default function RecipesView({ onLoadRecipe }: RecipesViewProps = {}) { } }; + // Create Recipe handlers + const handleCreateClick = () => { + // Reset form with example values + setCreateTitle('Python Development Assistant'); + setCreateDescription( + 'A helpful assistant for Python development tasks including coding, debugging, and code review.' + ); + setCreateInstructions(`You are an expert Python developer assistant. Help users with: + +1. Writing clean, efficient Python code +2. Debugging and troubleshooting issues +3. Code review and optimization suggestions +4. Best practices and design patterns +5. Testing and documentation + +Always provide clear explanations and working code examples. + +Parameters you can use: +- {{project_type}}: The type of Python project (web, data science, CLI, etc.) +- {{python_version}}: Target Python version`); + setCreatePrompt('What Python development task can I help you with today?'); + setCreateActivities('coding, debugging, testing, documentation'); + setCreateRecipeName(''); + setCreateGlobal(true); + setShowCreateDialog(true); + }; + + const handleCreateRecipe = async () => { + if ( + !createTitle.trim() || + !createDescription.trim() || + !createInstructions.trim() || + !createRecipeName.trim() + ) { + return; + } + + setCreating(true); + try { + // Parse activities from comma-separated string + const activities = createActivities + .split(',') + .map((activity) => activity.trim()) + .filter((activity) => activity.length > 0); + + // Create the recipe object + const recipe: Recipe = { + title: createTitle.trim(), + description: createDescription.trim(), + instructions: createInstructions.trim(), + prompt: createPrompt.trim() || undefined, + activities: activities.length > 0 ? activities : undefined, + }; + + await saveRecipe(recipe, { + name: createRecipeName.trim(), + global: createGlobal, + }); + + // Reset dialog state + setShowCreateDialog(false); + setCreateTitle(''); + setCreateDescription(''); + setCreateInstructions(''); + setCreatePrompt(''); + setCreateActivities(''); + setCreateRecipeName(''); + + await loadSavedRecipes(); + + toastSuccess({ + title: createRecipeName.trim(), + msg: 'Recipe created successfully', + }); + } catch (error) { + console.error('Failed to create recipe:', error); + + toastError({ + title: 'Create Failed', + msg: `Failed to create recipe: ${error instanceof Error ? error.message : 'Unknown error'}`, + traceback: error instanceof Error ? error.message : String(error), + }); + } finally { + setCreating(false); + } + }; + + // Auto-generate recipe name when title changes + const handleCreateTitleChange = (value: string) => { + setCreateTitle(value); + if (value.trim() && !createRecipeName.trim()) { + const suggestedName = value + .toLowerCase() + .replace(/[^a-zA-Z0-9\s-]/g, '') + .replace(/\s+/g, '-') + .trim(); + setCreateRecipeName(suggestedName); + } + }; + // Render a recipe item const RecipeItem = ({ savedRecipe }: { savedRecipe: SavedRecipe }) => ( @@ -361,15 +472,26 @@ export default function RecipesView({ onLoadRecipe }: RecipesViewProps = {}) {

Recipes

- +
+ + +

View and manage your saved recipes to quickly start new sessions with predefined @@ -577,6 +699,189 @@ export default function RecipesView({ onLoadRecipe }: RecipesViewProps = {}) {

)} + + {/* Create Recipe Dialog */} + {showCreateDialog && ( +
+
+

Create New Recipe

+ +
+
+ + handleCreateTitleChange(e.target.value)} + className="w-full p-3 border border-border-subtle rounded-lg bg-background-default text-text-standard focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder="Recipe title" + autoFocus + /> +
+ +
+ + setCreateDescription(e.target.value)} + className="w-full p-3 border border-border-subtle rounded-lg bg-background-default text-text-standard focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder="Brief description of what this recipe does" + /> +
+ +
+ +