diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index ba03ed6dc5..8a9d1fcdd8 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -84,17 +84,23 @@ const PairRouteWrapper = ({ const resumeSessionId = searchParams.get('resumeSessionId') ?? undefined; const recipeDeeplinkFromConfig = window.appConfig?.get('recipeDeeplink') as string | undefined; + const recipeIdFromConfig = window.appConfig?.get('recipeId') as string | undefined; const initialMessage = routeState.initialMessage; - // Create session if we have an initialMessage or recipeDeeplink but no sessionId + // Create session if we have an initialMessage, recipeDeeplink, or recipeId but no sessionId useEffect(() => { - if ((initialMessage || recipeDeeplinkFromConfig) && !resumeSessionId && !isCreatingSession) { + if ( + (initialMessage || recipeDeeplinkFromConfig || recipeIdFromConfig) && + !resumeSessionId && + !isCreatingSession + ) { setIsCreatingSession(true); (async () => { try { const newSession = await createSession(getInitialWorkingDir(), { recipeDeeplink: recipeDeeplinkFromConfig, + recipeId: recipeIdFromConfig, allExtensions: extensionsList, }); @@ -126,7 +132,14 @@ const PairRouteWrapper = ({ // Note: isCreatingSession is intentionally NOT in the dependency array // It's only used as a guard to prevent concurrent session creation // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialMessage, recipeDeeplinkFromConfig, resumeSessionId, setSearchParams, extensionsList]); + }, [ + initialMessage, + recipeDeeplinkFromConfig, + recipeIdFromConfig, + resumeSessionId, + setSearchParams, + extensionsList, + ]); // Add resumed session to active sessions if not already there useEffect(() => { @@ -450,7 +463,7 @@ export function AppInner() { if ((isMac ? event.metaKey : event.ctrlKey) && event.key === 'n') { event.preventDefault(); try { - window.electron.createChatWindow(undefined, getInitialWorkingDir()); + window.electron.createChatWindow({ dir: getInitialWorkingDir() }); } catch (error) { console.error('Error creating new window:', error); } diff --git a/ui/desktop/src/components/LauncherView.tsx b/ui/desktop/src/components/LauncherView.tsx index fbebb2afe0..a09a9f8ac7 100644 --- a/ui/desktop/src/components/LauncherView.tsx +++ b/ui/desktop/src/components/LauncherView.tsx @@ -10,7 +10,7 @@ export default function LauncherView() { if (query.trim()) { const initialMessage = query; setQuery(''); - window.electron.createChatWindow(initialMessage, getInitialWorkingDir()); + window.electron.createChatWindow({ query: initialMessage, dir: getInitialWorkingDir() }); setTimeout(() => { window.electron.closeWindow(); }, 200); diff --git a/ui/desktop/src/components/Layout/AppLayout.tsx b/ui/desktop/src/components/Layout/AppLayout.tsx index ef2fb2ae7c..ddd7fc2c73 100644 --- a/ui/desktop/src/components/Layout/AppLayout.tsx +++ b/ui/desktop/src/components/Layout/AppLayout.tsx @@ -84,10 +84,9 @@ const AppLayoutContent: React.FC = ({ activeSessions }) = }; const handleNewWindow = () => { - window.electron.createChatWindow( - undefined, - window.appConfig.get('GOOSE_WORKING_DIR') as string | undefined - ); + window.electron.createChatWindow({ + dir: window.appConfig.get('GOOSE_WORKING_DIR') as string | undefined, + }); }; return ( diff --git a/ui/desktop/src/components/ParameterInputModal.tsx b/ui/desktop/src/components/ParameterInputModal.tsx index afb7b86e00..bd0ea6787b 100644 --- a/ui/desktop/src/components/ParameterInputModal.tsx +++ b/ui/desktop/src/components/ParameterInputModal.tsx @@ -75,7 +75,7 @@ const ParameterInputModal: React.FC = ({ if (option === 'new-chat') { try { const workingDir = getInitialWorkingDir(); - window.electron.createChatWindow(undefined, workingDir); + window.electron.createChatWindow({ dir: workingDir }); window.electron.hideWindow(); } catch (error) { console.error('Error creating new window:', error); diff --git a/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx b/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx index b98c3fa3fc..695deba12c 100644 --- a/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx +++ b/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx @@ -1,12 +1,6 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useForm } from '@tanstack/react-form'; -import { - Recipe, - generateDeepLink, - Parameter, - encodeRecipe, - stripEmptyExtensions, -} from '../../recipe'; +import { Recipe, generateDeepLink, Parameter } from '../../recipe'; import { Check, ExternalLink, Play, Save, X } from 'lucide-react'; import { Geese } from '../icons/Geese'; import Copy from '../icons/Copy'; @@ -333,21 +327,12 @@ export default function CreateEditRecipeModal({ try { const recipe = getCurrentRecipe(); - await saveRecipe(recipe, recipeId); + const savedId = await saveRecipe(recipe, recipeId); // Close modal first onClose(true); - // Encode the recipe as a deeplink before passing to the new window - const encodedRecipe = await encodeRecipe(stripEmptyExtensions(recipe)); - window.electron.createChatWindow( - undefined, - undefined, - undefined, - undefined, - undefined, - encodedRecipe - ); + window.electron.createChatWindow({ recipeId: savedId }); toastSuccess({ title: recipe.title, diff --git a/ui/desktop/src/components/recipes/CreateRecipeFromSessionModal.tsx b/ui/desktop/src/components/recipes/CreateRecipeFromSessionModal.tsx index 3760850322..4151427c88 100644 --- a/ui/desktop/src/components/recipes/CreateRecipeFromSessionModal.tsx +++ b/ui/desktop/src/components/recipes/CreateRecipeFromSessionModal.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { useForm } from '@tanstack/react-form'; -import { Recipe, encodeRecipe, stripEmptyExtensions } from '../../recipe'; +import { Recipe } from '../../recipe'; import { Geese } from '../icons/Geese'; import { X, Save, Play, Loader2 } from 'lucide-react'; import { Button } from '../ui/button'; @@ -182,21 +182,13 @@ export default function CreateRecipeFromSessionModal({ : undefined, }; - await saveRecipe(recipe, null); + const recipeId = await saveRecipe(recipe, null); onRecipeCreated?.(recipe); onClose(); if (runAfterSave) { - const encodedRecipe = await encodeRecipe(stripEmptyExtensions(recipe)); - window.electron.createChatWindow( - undefined, - undefined, - undefined, - undefined, - undefined, - encodedRecipe - ); + window.electron.createChatWindow({ recipeId }); } } catch (error) { console.error('Failed to create recipe:', error); diff --git a/ui/desktop/src/components/recipes/RecipesView.tsx b/ui/desktop/src/components/recipes/RecipesView.tsx index b7c2a81380..7773dd92f4 100644 --- a/ui/desktop/src/components/recipes/RecipesView.tsx +++ b/ui/desktop/src/components/recipes/RecipesView.tsx @@ -32,7 +32,7 @@ import { } from '../../api'; import ImportRecipeForm, { ImportRecipeButton } from './ImportRecipeForm'; import CreateEditRecipeModal from './CreateEditRecipeModal'; -import { generateDeepLink, encodeRecipe, Recipe, stripEmptyExtensions } from '../../recipe'; +import { generateDeepLink } from '../../recipe'; import { useNavigation } from '../../hooks/useNavigation'; import { CronPicker } from '../schedule/CronPicker'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'; @@ -139,12 +139,12 @@ export default function RecipesView() { } }; - const handleStartRecipeChat = async (recipe: Recipe) => { + const handleStartRecipeChat = async (recipeId: string) => { try { const newAgent = await startAgent({ body: { working_dir: getInitialWorkingDir(), - recipe: stripEmptyExtensions(recipe) as Recipe, + recipe_id: recipeId, }, throwOnError: true, }); @@ -165,17 +165,13 @@ export default function RecipesView() { } }; - const handleStartRecipeChatInNewWindow = async (recipe: Recipe) => { + const handleStartRecipeChatInNewWindow = async (recipeId: string) => { try { - const encodedRecipe = await encodeRecipe(stripEmptyExtensions(recipe) as Recipe); - window.electron.createChatWindow( - undefined, - getInitialWorkingDir(), - undefined, - undefined, - 'pair', - encodedRecipe - ); + window.electron.createChatWindow({ + dir: getInitialWorkingDir(), + viewType: 'pair', + recipeId, + }); trackRecipeStarted(true, undefined, true); } catch (error) { console.error('Failed to open recipe in new window:', error); @@ -509,9 +505,9 @@ export default function RecipesView() {