Add recipe install warning (#3537)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Zane
2025-07-22 08:34:25 -07:00
committed by GitHub
parent 9f4e7a88f1
commit ab8089499e
6 changed files with 205 additions and 8 deletions
+21 -3
View File
@@ -60,6 +60,7 @@ import { type View, ViewOptions } from '../App';
import { MainPanelLayout } from './Layout/MainPanelLayout';
import ChatInput from './ChatInput';
import { ScrollArea, ScrollAreaHandle } from './ui/scroll-area';
import { RecipeWarningModal } from './ui/RecipeWarningModal';
import { useChatEngine } from '../hooks/useChatEngine';
import { useRecipeManager } from '../hooks/useRecipeManager';
import { useSessionContinuation } from '../hooks/useSessionContinuation';
@@ -195,6 +196,10 @@ function BaseChatContent({
handleAutoExecution,
recipeError,
setRecipeError,
isRecipeWarningModalOpen,
recipeAccepted,
handleRecipeAccept,
handleRecipeCancel,
} = useRecipeManager(messages, location.state);
// Reset recipe usage tracking when recipe changes
@@ -356,9 +361,9 @@ function BaseChatContent({
{
// Check if we should show splash instead of messages
(() => {
// Show splash if we have a recipe and user hasn't started using it yet
// Show splash if we have a recipe and user hasn't started using it yet, and recipe has been accepted
const shouldShowSplash =
recipeConfig && !hasStartedUsingRecipe && !suppressEmptyState;
recipeConfig && recipeAccepted && !hasStartedUsingRecipe && !suppressEmptyState;
return shouldShowSplash;
})() ? (
@@ -377,7 +382,8 @@ function BaseChatContent({
<PopularChatTopics append={(text: string) => appendWithTracking(text)} />
) : null}
</>
) : filteredMessages.length > 0 || (recipeConfig && hasStartedUsingRecipe) ? (
) : filteredMessages.length > 0 ||
(recipeConfig && recipeAccepted && hasStartedUsingRecipe) ? (
<>
{disableSearch ? (
// Render messages without SearchView wrapper when search is disabled
@@ -523,6 +529,18 @@ function BaseChatContent({
summaryContent={summaryContent}
/>
{/* Recipe Warning Modal */}
<RecipeWarningModal
isOpen={isRecipeWarningModalOpen}
onConfirm={handleRecipeAccept}
onCancel={handleRecipeCancel}
recipeDetails={{
title: recipeConfig?.title,
description: recipeConfig?.description,
instructions: recipeConfig?.instructions,
}}
/>
{/* Recipe Error Modal */}
{recipeError && (
<div className="fixed inset-0 z-[300] flex items-center justify-center bg-black/50">
@@ -0,0 +1,71 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from './dialog';
import { Button } from './button';
interface RecipeWarningModalProps {
isOpen: boolean;
onConfirm: () => void;
onCancel: () => void;
recipeDetails: {
title?: string;
description?: string;
instructions?: string;
};
}
export function RecipeWarningModal({
isOpen,
onConfirm,
onCancel,
recipeDetails,
}: RecipeWarningModalProps) {
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onCancel()}>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle> New Recipe Warning</DialogTitle>
<DialogDescription>
You are about to execute a recipe that you haven't run before. Only proceed if you trust
the source of this recipe.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="bg-background-muted p-4 rounded-lg">
<h3 className="font-medium mb-2 text-text-standard">Recipe Details:</h3>
<div className="space-y-2 text-sm">
{recipeDetails.title && (
<p className="text-text-standard">
<strong>Title:</strong> {recipeDetails.title}
</p>
)}
{recipeDetails.description && (
<p className="text-text-standard">
<strong>Description:</strong> {recipeDetails.description}
</p>
)}
{recipeDetails.instructions && (
<p className="text-text-standard">
<strong>Instructions:</strong> {recipeDetails.instructions}
</p>
)}
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={onCancel}>
Cancel
</Button>
<Button onClick={onConfirm}>Trust and Execute</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}