diff --git a/ui/desktop/src/components/RecipeEditor.tsx b/ui/desktop/src/components/RecipeEditor.tsx index 52ab473e88b2..49f57942942a 100644 --- a/ui/desktop/src/components/RecipeEditor.tsx +++ b/ui/desktop/src/components/RecipeEditor.tsx @@ -17,6 +17,7 @@ import ParameterInput from './parameter/ParameterInput'; import { saveRecipe, generateRecipeFilename } from '../recipe/recipeStorage'; import { toastSuccess, toastError } from '../toasts'; import { Button } from './ui/button'; +import { useEscapeKey } from '../hooks/useEscapeKey'; interface RecipeEditorProps { config?: Recipe; @@ -129,6 +130,12 @@ export default function RecipeEditor({ config }: RecipeEditorProps) { setParameters(allParams); }, [instructions, prompt]); + // Handle Esc key for Save Recipe Dialog + useEscapeKey(showSaveDialog, () => { + setShowSaveDialog(false); + setSaveRecipeName(''); + }); + const getCurrentConfig = useCallback((): Recipe => { // Transform the internal parameters state into the desired output format. const formattedParameters = parameters.map((param) => { diff --git a/ui/desktop/src/components/RecipesView.tsx b/ui/desktop/src/components/RecipesView.tsx index d22cf6b3e315..452dd0a292fa 100644 --- a/ui/desktop/src/components/RecipesView.tsx +++ b/ui/desktop/src/components/RecipesView.tsx @@ -23,6 +23,7 @@ import { Skeleton } from './ui/skeleton'; import { MainPanelLayout } from './Layout/MainPanelLayout'; import { Recipe, decodeRecipe } from '../recipe'; import { toastSuccess, toastError } from '../toasts'; +import { useEscapeKey } from '../hooks/useEscapeKey'; interface RecipesViewProps { onLoadRecipe?: (recipe: Recipe) => void; @@ -58,6 +59,23 @@ export default function RecipesView({ _onLoadRecipe }: RecipesViewProps = {}) { loadSavedRecipes(); }, []); + // Handle Esc key for modals + useEscapeKey(showPreview, () => setShowPreview(false)); + useEscapeKey(showImportDialog, () => { + setShowImportDialog(false); + setImportDeeplink(''); + setImportRecipeName(''); + }); + useEscapeKey(showCreateDialog, () => { + setShowCreateDialog(false); + setCreateTitle(''); + setCreateDescription(''); + setCreateInstructions(''); + setCreatePrompt(''); + setCreateActivities(''); + setCreateRecipeName(''); + }); + // Minimum loading time to prevent skeleton flash useEffect(() => { if (!loading && showSkeleton) { diff --git a/ui/desktop/src/hooks/useEscapeKey.ts b/ui/desktop/src/hooks/useEscapeKey.ts new file mode 100644 index 000000000000..43b2e1e2feb4 --- /dev/null +++ b/ui/desktop/src/hooks/useEscapeKey.ts @@ -0,0 +1,23 @@ +import { useEffect } from 'react'; + +/** + * Custom hook to handle Esc key press for closing modals + * @param isActive - Whether the hook should be active (typically when modal is open) + * @param onEscape - Callback function to execute when Esc key is pressed + */ +export function useEscapeKey(isActive: boolean, onEscape: () => void) { + useEffect(() => { + if (!isActive) return; + + const handleEscKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onEscape(); + } + }; + + document.addEventListener('keydown', handleEscKey); + return () => { + document.removeEventListener('keydown', handleEscKey); + }; + }, [isActive, onEscape]); +}