Move recipe actions to bottom bar icon and edit goosehints to settings (#5864)

This commit is contained in:
Zane
2025-11-25 08:55:43 -07:00
committed by GitHub
parent bc2138ac70
commit e1ab27e554
14 changed files with 254 additions and 281 deletions
+5 -42
View File
@@ -13,7 +13,6 @@ import { type SharedSessionDetails } from './sharedSessions';
import { ErrorUI } from './components/ErrorBoundary';
import { ExtensionInstallModal } from './components/ExtensionInstallModal';
import { ToastContainer } from 'react-toastify';
import { GoosehintsModal } from './components/GoosehintsModal';
import AnnouncementModal from './components/AnnouncementModal';
import ProviderGuard from './components/ProviderGuard';
import { createSession } from './sessions';
@@ -43,34 +42,20 @@ import { useNavigation } from './hooks/useNavigation';
import { errorMessage } from './utils/conversionUtils';
// Route Components
const HubRouteWrapper = ({
setIsGoosehintsModalOpen,
isExtensionsLoading,
}: {
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
isExtensionsLoading: boolean;
}) => {
const HubRouteWrapper = ({ isExtensionsLoading }: { isExtensionsLoading: boolean }) => {
const setView = useNavigation();
return (
<Hub
setView={setView}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
isExtensionsLoading={isExtensionsLoading}
/>
);
return <Hub setView={setView} isExtensionsLoading={isExtensionsLoading} />;
};
const PairRouteWrapper = ({
chat,
setChat,
setIsGoosehintsModalOpen,
activeSessionId,
setActiveSessionId,
}: {
chat: ChatType;
setChat: (chat: ChatType) => void;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
activeSessionId: string | null;
setActiveSessionId: (id: string | null) => void;
}) => {
@@ -178,13 +163,7 @@ const PairRouteWrapper = ({
}, [sessionId, activeSessionId, setActiveSessionId]);
return (
<Pair
key={sessionId}
setChat={setChat}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
sessionId={sessionId}
initialMessage={initialMessage}
/>
<Pair key={sessionId} setChat={setChat} sessionId={sessionId} initialMessage={initialMessage} />
);
};
@@ -356,7 +335,6 @@ const ExtensionsRoute = () => {
export function AppInner() {
const [fatalError, setFatalError] = useState<string | null>(null);
const [isGoosehintsModalOpen, setIsGoosehintsModalOpen] = useState(false);
const [agentWaitingMessage, setAgentWaitingMessage] = useState<string | null>(null);
const [isLoadingSharedSession, setIsLoadingSharedSession] = useState(false);
const [sharedSessionError, setSharedSessionError] = useState<string | null>(null);
@@ -652,27 +630,18 @@ export function AppInner() {
contextKey="hub"
agentWaitingMessage={agentWaitingMessage}
>
<AppLayout setIsGoosehintsModalOpen={setIsGoosehintsModalOpen} />
<AppLayout />
</ChatProvider>
</ProviderGuard>
}
>
<Route
index
element={
<HubRouteWrapper
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
isExtensionsLoading={isExtensionsLoading}
/>
}
/>
<Route index element={<HubRouteWrapper isExtensionsLoading={isExtensionsLoading} />} />
<Route
path="pair"
element={
<PairRouteWrapper
chat={chat}
setChat={setChat}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
activeSessionId={activeSessionId}
setActiveSessionId={setActiveSessionId}
/>
@@ -709,12 +678,6 @@ export function AppInner() {
</Route>
</Routes>
</div>
{isGoosehintsModalOpen && (
<GoosehintsModal
directory={window.appConfig?.get('GOOSE_WORKING_DIR') as string}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
/>
)}
</>
);
}
-3
View File
@@ -43,7 +43,6 @@ export const useCurrentModelInfo = () => useContext(CurrentModelContext);
interface BaseChatProps {
setChat: (chat: ChatType) => void;
setIsGoosehintsModalOpen?: (isOpen: boolean) => void;
onMessageSubmit?: (message: string) => void;
renderHeader?: () => React.ReactNode;
customChatInputProps?: Record<string, unknown>;
@@ -57,7 +56,6 @@ interface BaseChatProps {
}
function BaseChatContent({
setIsGoosehintsModalOpen,
renderHeader,
customChatInputProps = {},
customMainLayoutProps = {},
@@ -423,7 +421,6 @@ function BaseChatContent({
messages={messages}
disableAnimation={disableAnimation}
sessionCosts={sessionCosts}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
recipe={recipe}
recipeAccepted={!hasNotAcceptedRecipe}
initialPrompt={initialPrompt}
+50 -21
View File
@@ -1,5 +1,5 @@
import React, { useRef, useState, useEffect, useMemo, useCallback } from 'react';
import { Bug, FolderKey, ScrollText } from 'lucide-react';
import { Bug, ScrollText, ChefHat } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/Tooltip';
import { Button } from './ui/button';
import type { View } from '../utils/navigationUtils';
@@ -28,6 +28,8 @@ import MessageQueue from './MessageQueue';
import { detectInterruption } from '../utils/interruptionDetector';
import { DiagnosticsModal } from './ui/DownloadDiagnostics';
import { Message } from '../api';
import CreateRecipeFromSessionModal from './recipes/CreateRecipeFromSessionModal';
import CreateEditRecipeModal from './recipes/CreateEditRecipeModal';
interface QueuedMessage {
id: string;
@@ -80,7 +82,6 @@ interface ChatInputProps {
totalCost: number;
};
};
setIsGoosehintsModalOpen?: (isOpen: boolean) => void;
disableAnimation?: boolean;
recipe?: Recipe | null;
recipeId?: string | null;
@@ -107,7 +108,6 @@ export default function ChatInput({
messages = [],
disableAnimation = false,
sessionCosts,
setIsGoosehintsModalOpen,
recipe,
recipeId,
recipeAccepted,
@@ -140,6 +140,8 @@ export default function ChatInput({
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
const [isTokenLimitLoaded, setIsTokenLimitLoaded] = useState(false);
const [diagnosticsOpen, setDiagnosticsOpen] = useState(false);
const [showCreateRecipeModal, setShowCreateRecipeModal] = useState(false);
const [showEditRecipeModal, setShowEditRecipeModal] = useState(false);
// Save queue state (paused/interrupted) to storage
useEffect(() => {
@@ -1486,9 +1488,6 @@ export default function ChatInput({
dropdownRef={dropdownRef}
setView={setView}
alerts={alerts}
recipe={recipe}
recipeId={recipeId}
hasMessages={messages.length > 0}
/>
</div>
</Tooltip>
@@ -1500,21 +1499,34 @@ export default function ChatInput({
<BottomMenuExtensionSelection sessionId={sessionId} />
</>
)}
<div className="flex items-center h-full">
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => setIsGoosehintsModalOpen?.(true)}
variant="ghost"
size="sm"
className="flex items-center justify-center text-text-default/70 hover:text-text-default text-xs cursor-pointer"
>
<FolderKey size={16} />
</Button>
</TooltipTrigger>
<TooltipContent>Configure goosehints</TooltipContent>
</Tooltip>
</div>
{sessionId && (
<>
<div className="w-px h-4 bg-border-default mx-2" />
<div className="flex items-center h-full">
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => {
if (recipe) {
setShowEditRecipeModal(true);
} else {
setShowCreateRecipeModal(true);
}
}}
variant="ghost"
size="sm"
className="flex items-center justify-center text-text-default/70 hover:text-text-default text-xs cursor-pointer"
>
<ChefHat size={16} />
</Button>
</TooltipTrigger>
<TooltipContent>
{recipe ? 'View/Edit Recipe' : 'Create Recipe from Session'}
</TooltipContent>
</Tooltip>
</div>
</>
)}
{sessionId && (
<Tooltip>
<TooltipTrigger asChild>
@@ -1552,6 +1564,23 @@ export default function ChatInput({
setMentionPopover((prev) => ({ ...prev, selectedIndex: index }))
}
/>
{sessionId && showCreateRecipeModal && (
<CreateRecipeFromSessionModal
isOpen={showCreateRecipeModal}
onClose={() => setShowCreateRecipeModal(false)}
sessionId={sessionId}
/>
)}
{recipe && showEditRecipeModal && (
<CreateEditRecipeModal
isOpen={showEditRecipeModal}
onClose={() => setShowEditRecipeModal(false)}
recipe={recipe}
recipeId={recipeId}
/>
)}
</div>
</div>
);
@@ -21,7 +21,6 @@ interface SidebarProps {
onSelectSession: (sessionId: string) => void;
refreshTrigger?: number;
children?: React.ReactNode;
setIsGoosehintsModalOpen?: (isOpen: boolean) => void;
setView?: (view: View, viewOptions?: ViewOptions) => void;
currentPath?: string;
}
@@ -1,134 +0,0 @@
import { useState, useEffect } from 'react';
import { Button } from './ui/button';
import { Check } from './icons';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from './ui/dialog';
const ModalHelpText = () => (
<div className="text-sm flex-col space-y-4">
<p>
.goosehints is a text file used to provide additional context about your project and improve
the communication with Goose.
</p>
<p>
Please make sure <span className="font-bold">Developer</span> extension is enabled in the
settings page. This extension is required to use .goosehints. You'll need to restart your
session for .goosehints updates to take effect.
</p>
<p>
See{' '}
<Button
variant="link"
className="text-blue-500 hover:text-blue-600 p-0 h-auto"
onClick={() =>
window.open('https://block.github.io/goose/docs/guides/using-goosehints/', '_blank')
}
>
using .goosehints
</Button>{' '}
for more information.
</p>
</div>
);
const ModalError = ({ error }: { error: Error }) => (
<div className="text-sm text-textSubtle">
<div className="text-red-600">Error reading .goosehints file: {JSON.stringify(error)}</div>
</div>
);
const ModalFileInfo = ({ filePath, found }: { filePath: string; found: boolean }) => (
<div className="text-sm font-medium">
{found ? (
<div className="text-green-600">
<Check className="w-4 h-4 inline-block" /> .goosehints file found at: {filePath}
</div>
) : (
<div>Creating new .goosehints file at: {filePath}</div>
)}
</div>
);
const getGoosehintsFile = async (filePath: string) => await window.electron.readFile(filePath);
type GoosehintsModalProps = {
directory: string;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
};
export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: GoosehintsModalProps) => {
const goosehintsFilePath = `${directory}/.goosehints`;
const [goosehintsFile, setGoosehintsFile] = useState<string>('');
const [goosehintsFileFound, setGoosehintsFileFound] = useState<boolean>(false);
const [goosehintsFileReadError, setGoosehintsFileReadError] = useState<string>('');
useEffect(() => {
const fetchGoosehintsFile = async () => {
try {
const { file, error, found } = await getGoosehintsFile(goosehintsFilePath);
setGoosehintsFile(file);
setGoosehintsFileFound(found);
// Only set error if file was found but there was an actual read error
// If file is not found, treat it as creating a new file (no error)
setGoosehintsFileReadError(found && error ? error : '');
} catch (error) {
console.error('Error fetching .goosehints file:', error);
setGoosehintsFileReadError('Failed to access .goosehints file');
}
};
if (directory) fetchGoosehintsFile();
}, [directory, goosehintsFilePath]);
const writeFile = async () => {
await window.electron.writeFile(goosehintsFilePath, goosehintsFile);
setIsGoosehintsModalOpen(false);
};
const handleClose = () => {
setIsGoosehintsModalOpen(false);
};
return (
<Dialog open={true} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[80%] sm:max-h-[80%] overflow-auto">
<DialogHeader>
<DialogTitle>Configure .goosehints</DialogTitle>
<DialogDescription>
Configure your project's .goosehints file to provide additional context to Goose.
</DialogDescription>
</DialogHeader>
<ModalHelpText />
<div className="py-4">
{goosehintsFileReadError ? (
<ModalError error={new Error(goosehintsFileReadError)} />
) : (
<div className="space-y-2">
<ModalFileInfo filePath={goosehintsFilePath} found={goosehintsFileFound} />
<textarea
defaultValue={goosehintsFile}
autoFocus
className="w-full h-80 border rounded-md p-2 text-sm resize-none bg-background-default text-textStandard border-borderStandard focus:outline-none"
onChange={(event) => setGoosehintsFile(event.target.value)}
/>
</div>
)}
</div>
<DialogFooter className="pt-2">
<Button variant="outline" onClick={handleClose}>
Cancel
</Button>
<Button onClick={writeFile}>Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
-3
View File
@@ -23,11 +23,9 @@ import { startNewSession } from '../sessions';
export default function Hub({
setView,
setIsGoosehintsModalOpen,
isExtensionsLoading,
}: {
setView: (view: View, viewOptions?: ViewOptions) => void;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
isExtensionsLoading: boolean;
}) {
const handleSubmit = async (e: React.FormEvent) => {
@@ -61,7 +59,6 @@ export default function Hub({
messages={[]}
disableAnimation={false}
sessionCosts={undefined}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
isExtensionsLoading={isExtensionsLoading}
toolCount={0}
/>
@@ -6,12 +6,7 @@ import { AppWindowMac, AppWindow } from 'lucide-react';
import { Button } from '../ui/button';
import { Sidebar, SidebarInset, SidebarProvider, SidebarTrigger, useSidebar } from '../ui/sidebar';
interface AppLayoutProps {
setIsGoosehintsModalOpen?: (isOpen: boolean) => void;
}
// Inner component that uses useSidebar within SidebarProvider context
const AppLayoutContent: React.FC<AppLayoutProps> = ({ setIsGoosehintsModalOpen }) => {
const AppLayoutContent: React.FC = () => {
const navigate = useNavigate();
const location = useLocation();
const safeIsMacOS = (window?.electron?.platform || 'darwin') === 'darwin';
@@ -99,7 +94,6 @@ const AppLayoutContent: React.FC<AppLayoutProps> = ({ setIsGoosehintsModalOpen }
<AppSidebar
onSelectSession={handleSelectSession}
setView={setView}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
currentPath={location.pathname}
/>
</Sidebar>
@@ -110,10 +104,10 @@ const AppLayoutContent: React.FC<AppLayoutProps> = ({ setIsGoosehintsModalOpen }
);
};
export const AppLayout: React.FC<AppLayoutProps> = ({ setIsGoosehintsModalOpen }) => {
export const AppLayout: React.FC = () => {
return (
<SidebarProvider>
<AppLayoutContent setIsGoosehintsModalOpen={setIsGoosehintsModalOpen} />
<AppLayoutContent />
</SidebarProvider>
);
};
+1 -8
View File
@@ -10,21 +10,14 @@ export interface PairRouteState {
interface PairProps {
setChat: (chat: ChatType) => void;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
sessionId: string;
initialMessage?: string;
}
export default function Pair({
setChat,
setIsGoosehintsModalOpen,
sessionId,
initialMessage,
}: PairProps) {
export default function Pair({ setChat, sessionId, initialMessage }: PairProps) {
return (
<BaseChat
setChat={setChat}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
sessionId={sessionId}
initialMessage={initialMessage}
suppressEmptyState={false}
@@ -3,6 +3,7 @@ import { ToolSelectionStrategySection } from '../tool_selection_strategy/ToolSel
import DictationSection from '../dictation/DictationSection';
import { SecurityToggle } from '../security/SecurityToggle';
import { ResponseStylesSection } from '../response_styles/ResponseStylesSection';
import { GoosehintsSection } from './GoosehintsSection';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../ui/card';
export default function ChatSettingsSection() {
@@ -40,6 +41,12 @@ export default function ChatSettingsSection() {
</CardContent>
</Card>
<Card className="pb-2 rounded-lg">
<CardContent className="px-2">
<GoosehintsSection />
</CardContent>
</Card>
<Card className="pb-2 rounded-lg">
<CardHeader className="pb-0">
<CardTitle className="">Tool Selection Strategy (preview)</CardTitle>
@@ -0,0 +1,151 @@
import { useState, useEffect } from 'react';
import { Button } from '../../ui/button';
import { Check } from '../../icons';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '../../ui/dialog';
const HelpText = () => (
<div className="text-sm flex-col space-y-4 text-textSubtle">
<p>
.goosehints is a text file used to provide additional context about your project and improve
the communication with Goose.
</p>
<p>
Please make sure <span className="font-bold">Developer</span> extension is enabled in the
extensions page. This extension is required to use .goosehints. You'll need to restart your
session for .goosehints updates to take effect.
</p>
<p>
See{' '}
<Button
variant="link"
className="text-blue-500 hover:text-blue-600 p-0 h-auto"
onClick={() =>
window.open('https://block.github.io/goose/docs/guides/using-goosehints/', '_blank')
}
>
using .goosehints
</Button>{' '}
for more information.
</p>
</div>
);
const ErrorDisplay = ({ error }: { error: Error }) => (
<div className="text-sm text-textSubtle">
<div className="text-red-600">Error reading .goosehints file: {JSON.stringify(error)}</div>
</div>
);
const FileInfo = ({ filePath, found }: { filePath: string; found: boolean }) => (
<div className="text-sm font-medium mb-2">
{found ? (
<div className="text-green-600">
<Check className="w-4 h-4 inline-block" /> .goosehints file found at: {filePath}
</div>
) : (
<div>Creating new .goosehints file at: {filePath}</div>
)}
</div>
);
const getGoosehintsFile = async (filePath: string) => await window.electron.readFile(filePath);
interface GoosehintsModalProps {
directory: string;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
}
export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: GoosehintsModalProps) => {
const goosehintsFilePath = `${directory}/.goosehints`;
const [goosehintsFile, setGoosehintsFile] = useState<string>('');
const [goosehintsFileFound, setGoosehintsFileFound] = useState<boolean>(false);
const [goosehintsFileReadError, setGoosehintsFileReadError] = useState<string>('');
const [isSaving, setIsSaving] = useState(false);
const [saveSuccess, setSaveSuccess] = useState(false);
useEffect(() => {
const fetchGoosehintsFile = async () => {
try {
const { file, error, found } = await getGoosehintsFile(goosehintsFilePath);
setGoosehintsFile(file);
setGoosehintsFileFound(found);
setGoosehintsFileReadError(found && error ? error : '');
} catch (error) {
console.error('Error fetching .goosehints file:', error);
setGoosehintsFileReadError('Failed to access .goosehints file');
}
};
if (directory) fetchGoosehintsFile();
}, [directory, goosehintsFilePath]);
const writeFile = async () => {
setIsSaving(true);
setSaveSuccess(false);
try {
await window.electron.writeFile(goosehintsFilePath, goosehintsFile);
setSaveSuccess(true);
setGoosehintsFileFound(true);
setTimeout(() => setSaveSuccess(false), 3000);
} catch (error) {
console.error('Error writing .goosehints file:', error);
setGoosehintsFileReadError('Failed to save .goosehints file');
} finally {
setIsSaving(false);
}
};
return (
<Dialog open={true} onOpenChange={(open) => setIsGoosehintsModalOpen(open)}>
<DialogContent className="w-[80vw] max-w-[80vw] sm:max-w-[80vw] max-h-[90vh] flex flex-col">
<DialogHeader>
<DialogTitle>Configure Project Hints (.goosehints)</DialogTitle>
<DialogDescription>
Provide additional context about your project to improve communication with Goose
</DialogDescription>
</DialogHeader>
<div className="flex-1 overflow-y-auto space-y-4 pt-2 pb-4">
<HelpText />
<div>
{goosehintsFileReadError ? (
<ErrorDisplay error={new Error(goosehintsFileReadError)} />
) : (
<div className="space-y-2">
<FileInfo filePath={goosehintsFilePath} found={goosehintsFileFound} />
<textarea
value={goosehintsFile}
className="w-full h-80 border rounded-md p-2 text-sm resize-none bg-background-default text-textStandard border-borderStandard focus:outline-none focus:ring-2 focus:ring-blue-500"
onChange={(event) => setGoosehintsFile(event.target.value)}
placeholder="Enter project hints here..."
/>
</div>
)}
</div>
</div>
<DialogFooter>
{saveSuccess && (
<span className="text-green-600 text-sm flex items-center gap-1 mr-auto">
<Check className="w-4 h-4" />
Saved successfully
</span>
)}
<Button variant="outline" onClick={() => setIsGoosehintsModalOpen(false)}>
Close
</Button>
<Button onClick={writeFile} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
@@ -0,0 +1,34 @@
import { useState } from 'react';
import { Button } from '../../ui/button';
import { FolderKey } from 'lucide-react';
import { GoosehintsModal } from './GoosehintsModal';
export const GoosehintsSection = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const directory = window.appConfig?.get('GOOSE_WORKING_DIR') as string;
return (
<>
<div className="flex items-center justify-between px-2 py-2">
<div className="flex-1">
<h3 className="text-text-default">Project Hints (.goosehints)</h3>
<p className="text-xs text-text-muted mt-[2px]">
Configure your project's .goosehints file to provide additional context to Goose
</p>
</div>
<Button
onClick={() => setIsModalOpen(true)}
variant="outline"
size="sm"
className="flex items-center gap-2"
>
<FolderKey size={16} />
Configure
</Button>
</div>
{isModalOpen && (
<GoosehintsModal directory={directory} setIsGoosehintsModalOpen={setIsModalOpen} />
)}
</>
);
};
@@ -77,7 +77,7 @@ export const VoiceDictationToggle = () => {
</div>
<div
className={`overflow-visible transition-all duration-300 ease-in-out ${
className={`overflow-hidden transition-all duration-300 ease-in-out ${
settings.enabled ? 'max-h-96 opacity-100 mt-2' : 'max-h-0 opacity-0 mt-0'
}`}
>
@@ -23,7 +23,7 @@ export const ConversationLimitsDropdown = ({
onClick={toggleExpanded}
className="w-full flex items-center justify-between py-2 px-2 hover:bg-background-muted rounded-lg transition-all group"
>
<h3 className="text-text-default font-medium">Conversation Limits</h3>
<h3 className="text-text-default">Conversation Limits</h3>
<ChevronDown
className={`w-4 h-4 text-text-muted transition-transform duration-200 ease-in-out ${
@@ -1,4 +1,4 @@
import { Sliders, ChefHat, Bot, Eye } from 'lucide-react';
import { Sliders, Bot } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { useModelAndProvider } from '../../../ModelAndProviderContext';
import { SwitchModelModal } from '../subcomponents/SwitchModelModal';
@@ -9,24 +9,18 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
} from '../../../ui/dropdown-menu';
import { useCurrentModelInfo } from '../../../BaseChat';
import { useConfig } from '../../../ConfigContext';
import { getProviderMetadata } from '../modelInterface';
import { Alert } from '../../../alerts';
import BottomMenuAlertPopover from '../../../bottom_menu/BottomMenuAlertPopover';
import { Recipe } from '../../../../recipe';
import CreateEditRecipeModal from '../../../recipes/CreateEditRecipeModal';
interface ModelsBottomBarProps {
sessionId: string | null;
dropdownRef: React.RefObject<HTMLDivElement>;
setView: (view: View) => void;
alerts: Alert[];
recipe?: Recipe | null;
recipeId?: string | null;
hasMessages?: boolean; // Add prop to know if there are messages to create a recipe from
}
export default function ModelsBottomBar({
@@ -34,9 +28,6 @@ export default function ModelsBottomBar({
dropdownRef,
setView,
alerts,
recipe,
recipeId,
hasMessages = false,
}: ModelsBottomBarProps) {
const {
currentModel,
@@ -54,9 +45,6 @@ export default function ModelsBottomBar({
const [isLeadWorkerActive, setIsLeadWorkerActive] = useState(false);
const [providerDefaultModel, setProviderDefaultModel] = useState<string | null>(null);
// View recipe modal state
const [showViewRecipeModal, setShowViewRecipeModal] = useState(false);
// Check if lead/worker mode is active
useEffect(() => {
const checkLeadWorker = async () => {
@@ -164,13 +152,6 @@ export default function ModelsBottomBar({
})();
}, [currentModel, getCurrentModelDisplayName]);
// Handle view recipe - open modal instead of navigating
const handleViewRecipe = () => {
if (recipe) {
setShowViewRecipeModal(true);
}
};
return (
<div className="relative flex items-center" ref={dropdownRef}>
<BottomMenuAlertPopover alerts={alerts} />
@@ -200,33 +181,6 @@ export default function ModelsBottomBar({
<span>Lead/Worker Settings</span>
<Sliders className="ml-auto h-4 w-4" />
</DropdownMenuItem>
{/* Recipe-specific menu items - only show when actively using a recipe */}
{recipe && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleViewRecipe}>
<span>View/Edit Recipe</span>
<Eye className="ml-auto h-4 w-4" />
</DropdownMenuItem>
</>
)}
{/* Only show "Create a recipe from this session" when there are messages AND no recipe is currently active */}
{hasMessages && !recipe && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
// Signal to create an agent from the current chat
window.dispatchEvent(new CustomEvent('make-agent-from-chat'));
}}
>
<span>Create a recipe from this session</span>
<ChefHat className="ml-auto h-4 w-4" />
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
@@ -241,17 +195,6 @@ export default function ModelsBottomBar({
{isLeadWorkerModalOpen ? (
<LeadWorkerSettings isOpen={isLeadWorkerModalOpen} onClose={handleLeadWorkerModalClose} />
) : null}
{/* View Recipe Modal */}
{/* todo: we don't have the actual recipe name when in chat only in recipes list view so we generate it for now */}
{recipe && (
<CreateEditRecipeModal
isOpen={showViewRecipeModal}
onClose={() => setShowViewRecipeModal(false)}
recipe={recipe}
recipeId={recipeId}
/>
)}
</div>
);
}