From 91ecd8de7b651704820d4dd0965213c98444ad18 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Tue, 13 Oct 2026 14:02:10 +0000 Subject: [PATCH] remove false optionality in desktop props (#12776) Co-authored-by: Claude Opus 5.5 --- ui/desktop/src/App.tsx | 3 +- ui/desktop/src/acp/providers.ts | 6 +- ui/desktop/src/acp/recipe.ts | 4 +- ui/desktop/src/components/BaseChat.tsx | 54 ++----- ui/desktop/src/components/ChatInput.tsx | 4 +- .../src/components/ExtensionInstallModal.tsx | 145 ++++++++++-------- .../components/GooseSidebar/ThemeSelector.tsx | 16 +- .../src/components/Layout/NavigationPanel.tsx | 10 +- .../src/components/McpApps/McpAppRenderer.tsx | 23 +-- .../src/components/ToolCallWithResponse.tsx | 10 +- ui/desktop/src/components/alerts/AlertBox.tsx | 1 - .../components/bottom_menu/DirSwitcher.tsx | 14 +- .../src/components/common/InlineEditText.tsx | 18 ++- .../src/components/conversation/SearchBar.tsx | 6 +- .../components/parameter/ParameterInput.tsx | 74 +++++---- .../recipes/CreateEditRecipeModal.tsx | 8 +- .../components/recipes/RecipeActivities.tsx | 1 - .../recipes/shared/CreateSubRecipeInline.tsx | 41 +++-- .../recipes/shared/RecipeFormFields.tsx | 43 +----- .../src/components/schedule/SchedulesView.tsx | 6 +- .../components/sessions/SessionListView.tsx | 105 ++++++------- .../settings/extensions/ExtensionsSection.tsx | 23 +-- .../modal/ExtensionConfigFields.tsx | 6 +- .../subcomponents/ExtensionItem.test.tsx | 8 +- .../subcomponents/ExtensionItem.tsx | 4 +- .../subcomponents/ExtensionList.tsx | 6 +- .../settings/providers/ProviderGrid.tsx | 40 ++--- .../providers/ProviderSettingsPage.tsx | 8 +- .../subcomponents/ProviderCatalogPicker.tsx | 22 +-- .../subcomponents/ProviderSetupActions.tsx | 18 +-- .../forms/CustomProviderForm.tsx | 8 +- .../providers/subcomponents/CardContainer.tsx | 7 +- .../providers/subcomponents/ProviderCard.tsx | 21 +-- .../settings/security/SecurityToggle.tsx | 17 +- ui/desktop/src/contexts/ChatContext.tsx | 4 +- ui/desktop/src/hooks/useAutoSubmit.ts | 4 +- ui/desktop/src/hooks/useNavigationItems.ts | 2 - ui/desktop/src/i18n/messages/de.json | 6 - ui/desktop/src/i18n/messages/en.json | 6 - ui/desktop/src/i18n/messages/es.json | 6 - ui/desktop/src/i18n/messages/fr.json | 6 - ui/desktop/src/i18n/messages/hi.json | 6 - ui/desktop/src/i18n/messages/id.json | 6 - ui/desktop/src/i18n/messages/it.json | 6 - ui/desktop/src/i18n/messages/ja.json | 6 - ui/desktop/src/i18n/messages/ko.json | 6 - ui/desktop/src/i18n/messages/ms.json | 6 - ui/desktop/src/i18n/messages/pt.json | 6 - ui/desktop/src/i18n/messages/ru.json | 6 - ui/desktop/src/i18n/messages/tr.json | 6 - ui/desktop/src/i18n/messages/vi.json | 6 - ui/desktop/src/i18n/messages/zh-CN.json | 6 - ui/desktop/src/i18n/messages/zh-TW.json | 6 - ui/desktop/src/recipe/recipe_management.ts | 4 +- ui/desktop/src/types/providers.ts | 2 +- ui/desktop/src/utils/autoUpdater.ts | 12 +- 56 files changed, 336 insertions(+), 568 deletions(-) diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index 90cb3a3db9..9e52fa6fc7 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -224,8 +224,7 @@ const SessionsRoute = () => { }; const SchedulesRoute = () => { - const navigate = useNavigate(); - return navigate('/')} />; + return ; }; const RecipesRoute = () => { diff --git a/ui/desktop/src/acp/providers.ts b/ui/desktop/src/acp/providers.ts index d32828146f..4c4e471acb 100644 --- a/ui/desktop/src/acp/providers.ts +++ b/ui/desktop/src/acp/providers.ts @@ -88,7 +88,7 @@ function updateRequestToCreate( models: request.models, supportsStreaming: request.supports_streaming ?? null, headers: request.headers ?? undefined, - requiresAuth: request.requires_auth ?? true, + requiresAuth: request.requires_auth, catalogProviderId: request.catalog_provider_id ?? null, basePath: request.base_path ?? null, toolshim: request.toolshim, @@ -324,9 +324,9 @@ export async function acpReadDefaults(): Promise<{ }; } -export async function acpSaveDefaults(providerId: string, modelId?: string | null): Promise { +export async function acpSaveDefaults(providerId: string, modelId: string | null): Promise { const client = await getAcpClient(); - await client.goose.defaultsSave_unstable({ providerId, modelId: modelId ?? null }); + await client.goose.defaultsSave_unstable({ providerId, modelId }); } export async function acpClearDefaults(): Promise { diff --git a/ui/desktop/src/acp/recipe.ts b/ui/desktop/src/acp/recipe.ts index f049159364..2ac623faba 100644 --- a/ui/desktop/src/acp/recipe.ts +++ b/ui/desktop/src/acp/recipe.ts @@ -126,7 +126,7 @@ export async function deleteRecipe(id: string): Promise { } } -export async function scheduleRecipe(id: string, cronSchedule?: string | null): Promise { +export async function scheduleRecipe(id: string, cronSchedule: string | null): Promise { try { const client = await getAcpClient(); await client.goose.recipesSchedule_unstable({ id, cron_schedule: cronSchedule }); @@ -137,7 +137,7 @@ export async function scheduleRecipe(id: string, cronSchedule?: string | null): export async function setRecipeSlashCommand( id: string, - slashCommand?: string | null + slashCommand: string | null ): Promise { try { const client = await getAcpClient(); diff --git a/ui/desktop/src/components/BaseChat.tsx b/ui/desktop/src/components/BaseChat.tsx index 1fa753a181..7f0e6965cf 100644 --- a/ui/desktop/src/components/BaseChat.tsx +++ b/ui/desktop/src/components/BaseChat.tsx @@ -62,12 +62,6 @@ const isUserMessage = (message: Message) => message.role === 'user'; interface BaseChatProps { setChat: (chat: ChatType) => void; - onMessageSubmit?: (message: string) => void; - renderHeader?: () => React.ReactNode; - customChatInputProps?: Record; - customMainLayoutProps?: Record; - contentClassName?: string; - disableSearch?: boolean; suppressEmptyState: boolean; sessionId: string; isActiveSession: boolean; @@ -78,9 +72,6 @@ interface BaseChatProps { export default function BaseChat({ setChat, - renderHeader, - customChatInputProps = {}, - customMainLayoutProps = {}, sessionId, initialMessage, noAutoSubmit, @@ -135,10 +126,7 @@ export default function BaseChat({ setQuoteButtonPos(null); return; } - if ( - !conversationRef.current || - !conversationRef.current.contains(sel.anchorNode) - ) { + if (!conversationRef.current || !conversationRef.current.contains(sel.anchorNode)) { setQuoteButtonPos(null); return; } @@ -439,12 +427,7 @@ export default function BaseChat({ if (sessionLoadError) { return (
- - {renderHeader && renderHeader()} +
@@ -472,14 +455,7 @@ export default function BaseChat({ return (
- - {/* Custom header */} - {renderHeader && renderHeader()} - + {/* Chat container with sticky recipe header */}
{/* Goose watermark - top right */} @@ -521,7 +497,6 @@ export default function BaseChat({
)} @@ -530,17 +505,17 @@ export default function BaseChat({ <>
- +
@@ -636,7 +611,6 @@ export default function BaseChat({ }} appendQuote={pendingQuote} onAppendQuoteConsumed={() => setPendingQuote(null)} - {...customChatInputProps} />
diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index ea9c73ebae..ebd9e2ec20 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -202,7 +202,7 @@ interface ChatInputProps { recipeId?: string | null; initialPrompt?: string; append?: (message: Message) => void; - onWorkingDirChange?: (newDir: string) => Promise | void; + onWorkingDirChange: (newDir: string) => Promise | void; inputRef?: React.RefObject; sessionModel?: string | null; sessionProvider?: string | null; @@ -1746,7 +1746,7 @@ export default function ChatInput({ sessionId={sessionId ?? undefined} workingDir={currentWorkingDir} onWorkingDirChange={async (newDir) => { - await onWorkingDirChange?.(newDir); + await onWorkingDirChange(newDir); setWorkingDirOverride(newDir); }} /> diff --git a/ui/desktop/src/components/ExtensionInstallModal.tsx b/ui/desktop/src/components/ExtensionInstallModal.tsx index 8628aa317b..6ac6f3bf38 100644 --- a/ui/desktop/src/components/ExtensionInstallModal.tsx +++ b/ui/desktop/src/components/ExtensionInstallModal.tsx @@ -29,7 +29,8 @@ const i18n = defineMessages({ }, blockedMessage: { id: 'extensionInstallModal.blockedMessage', - defaultMessage: 'This extension command is not in the allowed list and its installation is blocked.\n\nExtension: {name}\nCommand: {command}\n\nContact your administrator to request approval for this extension.', + defaultMessage: + 'This extension command is not in the allowed list and its installation is blocked.\n\nExtension: {name}\nCommand: {command}\n\nContact your administrator to request approval for this extension.', }, ok: { id: 'extensionInstallModal.ok', @@ -41,15 +42,18 @@ const i18n = defineMessages({ }, untrustedSecurityMessage: { id: 'extensionInstallModal.untrustedSecurityMessage', - defaultMessage: 'This extension command is not in the allowed list and will be able to access your conversations and provide additional functionality.\n\nInstalling extensions from untrusted sources may pose security risks.', + defaultMessage: + 'This extension command is not in the allowed list and will be able to access your conversations and provide additional functionality.\n\nInstalling extensions from untrusted sources may pose security risks.', }, untrustedMessageWithUrl: { id: 'extensionInstallModal.untrustedMessageWithUrl', - defaultMessage: '{securityMessage}\n\nExtension: {name}\nURL: {url}\n\nContact your administrator if you are unsure about this.', + defaultMessage: + '{securityMessage}\n\nExtension: {name}\nURL: {url}\n\nContact your administrator if you are unsure about this.', }, untrustedMessageWithCommand: { id: 'extensionInstallModal.untrustedMessageWithCommand', - defaultMessage: '{securityMessage}\n\nExtension: {name}\nCommand: {command}\n\nContact your administrator if you are unsure about this.', + defaultMessage: + '{securityMessage}\n\nExtension: {name}\nCommand: {command}\n\nContact your administrator if you are unsure about this.', }, installAnyway: { id: 'extensionInstallModal.installAnyway', @@ -81,7 +85,8 @@ const i18n = defineMessages({ }, alreadyInstalledMessage: { id: 'extensionInstallModal.alreadyInstalledMessage', - defaultMessage: "''{name}'' extension has already been installed successfully. Start a new chat session to use it.", + defaultMessage: + "''{name}'' extension has already been installed successfully. Start a new chat session to use it.", }, installing: { id: 'extensionInstallModal.installing', @@ -93,7 +98,7 @@ type ModalType = 'blocked' | 'untrusted' | 'trusted'; interface ExtensionInfo { name: string; - command?: string; + command: string; remoteUrl?: string; link: string; } @@ -116,7 +121,7 @@ interface ExtensionModalConfig { } interface ExtensionInstallModalProps { - addExtension?: (name: string, config: ExtensionConfig, enabled: boolean) => Promise; + addExtension: (name: string, config: ExtensionConfig, enabled: boolean) => Promise; setView: (view: View, options?: ViewOptions) => void; } @@ -200,7 +205,8 @@ export function ExtensionInstallModal({ addExtension, setView }: ExtensionInstal case 'blocked': return { title: intl.formatMessage(i18n.blockedTitle), - message: '\n\n' + intl.formatMessage(i18n.blockedMessage, { name, command: displayCommand }), + message: + '\n\n' + intl.formatMessage(i18n.blockedMessage, { name, command: displayCommand }), confirmLabel: intl.formatMessage(i18n.ok), cancelLabel: '', showSingleButton: true, @@ -210,8 +216,16 @@ export function ExtensionInstallModal({ addExtension, setView }: ExtensionInstal case 'untrusted': { const securityMessage = '\n\n' + intl.formatMessage(i18n.untrustedSecurityMessage); const message = remoteUrl - ? intl.formatMessage(i18n.untrustedMessageWithUrl, { securityMessage, name, url: remoteUrl }) - : intl.formatMessage(i18n.untrustedMessageWithCommand, { securityMessage, name, command: displayCommand }); + ? intl.formatMessage(i18n.untrustedMessageWithUrl, { + securityMessage, + name, + url: remoteUrl, + }) + : intl.formatMessage(i18n.untrustedMessageWithCommand, { + securityMessage, + name, + command: displayCommand, + }); return { title: intl.formatMessage(i18n.untrustedTitle), @@ -236,58 +250,59 @@ export function ExtensionInstallModal({ addExtension, setView }: ExtensionInstal } }; - const handleExtensionRequest = useCallback(async (link: string): Promise => { - if (processingLinkRef.current === link) { - return; - } - processingLinkRef.current = link; - - try { - - const command = extractCommand(link); - const remoteUrl = extractRemoteUrl(link); - const extName = extractExtensionName(link); - const extensionsList = await getExtensionsRef.current(true); - - if (extensionsList?.find((ext) => ext.name === extName)) { - - toastService.success({ - title: intl.formatMessage(i18n.alreadyInstalledTitle, { name: extName }), - msg: intl.formatMessage(i18n.alreadyInstalledMessage, { name: extName }), - }); + const handleExtensionRequest = useCallback( + async (link: string): Promise => { + if (processingLinkRef.current === link) { return; } + processingLinkRef.current = link; - const extensionInfo: ExtensionInfo = { - name: extName, - command: command, - remoteUrl: remoteUrl || undefined, - link: link, - }; + try { + const command = extractCommand(link); + const remoteUrl = extractRemoteUrl(link); + const extName = extractExtensionName(link); + const extensionsList = await getExtensionsRef.current(true); - const modalType = await determineModalType(command, remoteUrl); + if (extensionsList?.find((ext) => ext.name === extName)) { + toastService.success({ + title: intl.formatMessage(i18n.alreadyInstalledTitle, { name: extName }), + msg: intl.formatMessage(i18n.alreadyInstalledMessage, { name: extName }), + }); + return; + } - setModalState({ - isOpen: true, - modalType, - extensionInfo, - isPending: false, - error: null, - }); + const extensionInfo: ExtensionInfo = { + name: extName, + command: command, + remoteUrl: remoteUrl || undefined, + link: link, + }; - setPendingLink(modalType === 'blocked' ? null : link); + const modalType = await determineModalType(command, remoteUrl); - window.electron.logInfo(`Extension modal opened: ${modalType} for ${extName}`); - } catch (error) { - console.error('Error processing extension request:', error); - setModalState((prev) => ({ - ...prev, - error: errorMessage(error, 'Unknown error'), - })); - } finally { - processingLinkRef.current = null; - } - }, [intl]); + setModalState({ + isOpen: true, + modalType, + extensionInfo, + isPending: false, + error: null, + }); + + setPendingLink(modalType === 'blocked' ? null : link); + + window.electron.logInfo(`Extension modal opened: ${modalType} for ${extName}`); + } catch (error) { + console.error('Error processing extension request:', error); + setModalState((prev) => ({ + ...prev, + error: errorMessage(error, 'Unknown error'), + })); + } finally { + processingLinkRef.current = null; + } + }, + [intl] + ); const dismissModal = useCallback(() => { setModalState({ @@ -308,18 +323,13 @@ export function ExtensionInstallModal({ addExtension, setView }: ExtensionInstal setModalState((prev) => ({ ...prev, isPending: true })); try { - - if (addExtension) { - await addExtensionFromDeepLink( - pendingLink, - addExtension, - (view: string, options?: ViewOptions) => { - setView(view as View, options); - } - ); - } else { - throw new Error('addExtension function not provided to component'); - } + await addExtensionFromDeepLink( + pendingLink, + addExtension, + (view: string, options?: ViewOptions) => { + setView(view as View, options); + } + ); dismissModal(); } catch (error) { setModalState((prev) => ({ @@ -331,7 +341,6 @@ export function ExtensionInstallModal({ addExtension, setView }: ExtensionInstal }, [pendingLink, dismissModal, addExtension, setView]); useEffect(() => { - const handleAddExtension = async (_event: IpcRendererEvent, ...args: unknown[]) => { const link = args[0] as string; await handleExtensionRequest(link); diff --git a/ui/desktop/src/components/GooseSidebar/ThemeSelector.tsx b/ui/desktop/src/components/GooseSidebar/ThemeSelector.tsx index b336a2face..321a2a57c4 100644 --- a/ui/desktop/src/components/GooseSidebar/ThemeSelector.tsx +++ b/ui/desktop/src/components/GooseSidebar/ThemeSelector.tsx @@ -28,22 +28,20 @@ const i18n = defineMessages({ }); interface ThemeSelectorProps { - className?: string; - hideTitle?: boolean; - horizontal?: boolean; + className: string; + hideTitle: boolean; + horizontal: boolean; } -const ThemeSelector: React.FC = ({ - className = '', - hideTitle = false, - horizontal = false, -}) => { +const ThemeSelector: React.FC = ({ className, hideTitle, horizontal }) => { const intl = useIntl(); const { userThemePreference, setUserThemePreference } = useTheme(); return (
- {!hideTitle &&
{intl.formatMessage(i18n.theme)}
} + {!hideTitle && ( +
{intl.formatMessage(i18n.theme)}
+ )}
diff --git a/ui/desktop/src/components/Layout/NavigationPanel.tsx b/ui/desktop/src/components/Layout/NavigationPanel.tsx index 3dfaf4dc02..eb4025ffe4 100644 --- a/ui/desktop/src/components/Layout/NavigationPanel.tsx +++ b/ui/desktop/src/components/Layout/NavigationPanel.tsx @@ -109,9 +109,6 @@ const NavRow: React.FC = ({ item, active, onClick }) => { ); }; @@ -242,15 +239,14 @@ const SessionRow: React.FC = ({ }; export const Navigation: React.FC<{ - className?: string; activeLiveVoiceSessionId: string | null; -}> = ({ className, activeLiveVoiceSessionId }) => { +}> = ({ activeLiveVoiceSessionId }) => { const intl = useIntl(); const { isNavExpanded } = useNavigationContext(); const location = useLocation(); const { extensionsList } = useConfig(); - const appsExtensionEnabled = !!extensionsList?.find((ext) => ext.name === 'apps')?.enabled; + const appsExtensionEnabled = !!extensionsList.find((ext) => ext.name === 'apps')?.enabled; const visibleItems = useMemo(() => { return NAV_ITEMS.filter((item) => { @@ -338,7 +334,7 @@ export const Navigation: React.FC<{ animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }} - className={cn('bg-background-primary outline-none flex flex-col h-full', className)} + className="bg-background-primary outline-none flex flex-col h-full" >
diff --git a/ui/desktop/src/components/McpApps/McpAppRenderer.tsx b/ui/desktop/src/components/McpApps/McpAppRenderer.tsx index a99096d75f..a0161f1e5c 100644 --- a/ui/desktop/src/components/McpApps/McpAppRenderer.tsx +++ b/ui/desktop/src/components/McpApps/McpAppRenderer.tsx @@ -18,7 +18,6 @@ import { AppBridge, PostMessageTransport, - type AppInfo, type RequestHandlerExtra, type SandboxConfig, } from '@mcp-ui/client'; @@ -177,7 +176,7 @@ interface GooseAppFrameProps { toolInput?: Record; toolInputPartial?: Record; toolResult?: CallToolResult; - toolCancelled?: boolean; + toolCancelled: boolean; onMessage: (params: { content: Array<{ type: string; text?: string }>; }) => Promise>; @@ -196,9 +195,8 @@ interface GooseAppFrameProps { request: JSONRPCRequest, extra: RequestHandlerExtra ) => Promise>; - onSizeChanged?: (params: McpUiSizeChangedNotification['params']) => void; - onInitialized?: (appInfo: AppInfo) => void; - onError?: (error: Error) => void; + onSizeChanged: (params: McpUiSizeChangedNotification['params']) => void; + onError: (error: Error) => void; } const SANDBOX_PROXY_READY_METHOD = 'ui/notifications/sandbox-proxy-ready'; @@ -228,7 +226,6 @@ function GooseAppFrame({ onLoggingMessage, onFallbackRequest, onSizeChanged, - onInitialized, onError, }: GooseAppFrameProps) { const containerRef = useRef(null); @@ -245,7 +242,6 @@ function GooseAppFrame({ const onLoggingMessageRef = useRef(onLoggingMessage); const onFallbackRequestRef = useRef(onFallbackRequest); const onSizeChangedRef = useRef(onSizeChanged); - const onInitializedRef = useRef(onInitialized); const onErrorRef = useRef(onError); useEffect(() => { @@ -257,7 +253,6 @@ function GooseAppFrame({ onLoggingMessageRef.current = onLoggingMessage; onFallbackRequestRef.current = onFallbackRequest; onSizeChangedRef.current = onSizeChanged; - onInitializedRef.current = onInitialized; onErrorRef.current = onError; }); @@ -305,7 +300,7 @@ function GooseAppFrame({ settled = true; cleanupReadyListener(); if (active) { - onErrorRef.current?.(error); + onErrorRef.current(error); } }; const ready = () => { @@ -330,7 +325,7 @@ function GooseAppFrame({ if (!active || !iframe.contentWindow) return; try { bridge.onsizechange = (params) => { - onSizeChangedRef.current?.(params); + onSizeChangedRef.current(params); if (params.width !== undefined) { iframe.style.width = `${params.width}px`; } @@ -342,10 +337,6 @@ function GooseAppFrame({ bridge.oninitialized = () => { if (!active) return; setInitialized(true); - onInitializedRef.current?.({ - appVersion: bridge.getAppVersion(), - appCapabilities: bridge.getAppCapabilities(), - }); }; await bridge.connect(new PostMessageTransport(iframe.contentWindow, iframe.contentWindow)); if (!active) return; @@ -353,7 +344,7 @@ function GooseAppFrame({ setConnected(true); } catch (error) { if (!active) return; - onErrorRef.current?.(error instanceof Error ? error : new Error(String(error))); + onErrorRef.current(error instanceof Error ? error : new Error(String(error))); } }; @@ -382,7 +373,7 @@ function GooseAppFrame({ if (!connected || !bridge) return; void Promise.resolve(bridge.sendSandboxResourceReady({ html, csp: sandbox.csp })).catch( (error: unknown) => { - onErrorRef.current?.(error instanceof Error ? error : new Error(String(error))); + onErrorRef.current(error instanceof Error ? error : new Error(String(error))); } ); }, [connected, html, sandbox.csp]); diff --git a/ui/desktop/src/components/ToolCallWithResponse.tsx b/ui/desktop/src/components/ToolCallWithResponse.tsx index 34c8ad7d55..616009177b 100644 --- a/ui/desktop/src/components/ToolCallWithResponse.tsx +++ b/ui/desktop/src/components/ToolCallWithResponse.tsx @@ -315,18 +315,16 @@ export default function ToolCallWithResponse({ interface ToolCallExpandableProps { label: string | React.ReactNode; - isStartExpanded?: boolean; + isStartExpanded: boolean; isForceExpand?: boolean; children: React.ReactNode; - className?: string; } function ToolCallExpandable({ label, - isStartExpanded = false, + isStartExpanded, isForceExpand, children, - className = '', }: ToolCallExpandableProps) { const [isExpandedState, setIsExpanded] = React.useState(null); const isExpanded = isExpandedState === null ? isStartExpanded : isExpandedState; @@ -336,7 +334,7 @@ function ToolCallExpandable({ }, [isForceExpand]); return ( -
+
diff --git a/ui/desktop/src/components/conversation/SearchBar.tsx b/ui/desktop/src/components/conversation/SearchBar.tsx index 3d3964f37a..381701163d 100644 --- a/ui/desktop/src/components/conversation/SearchBar.tsx +++ b/ui/desktop/src/components/conversation/SearchBar.tsx @@ -36,8 +36,8 @@ interface SearchBarProps { onSearch: (term: string, caseSensitive: boolean) => void; /** Callback fired when the search bar is closed */ onClose: () => void; - /** Optional callback for navigating between search results */ - onNavigate?: (direction: 'next' | 'prev') => void; + /** Callback for navigating between search results */ + onNavigate: (direction: 'next' | 'prev') => void; /** Current search results state */ searchResults?: { count: number; @@ -153,7 +153,7 @@ export const SearchBar: React.FC = ({ e?.preventDefault(); if (searchResults && searchResults.count > 0) { inputRef.current?.focus(); - onNavigate?.(direction); + onNavigate(direction); } }; diff --git a/ui/desktop/src/components/parameter/ParameterInput.tsx b/ui/desktop/src/components/parameter/ParameterInput.tsx index a5cf00bce9..c5611c3ee5 100644 --- a/ui/desktop/src/components/parameter/ParameterInput.tsx +++ b/ui/desktop/src/components/parameter/ParameterInput.tsx @@ -86,18 +86,18 @@ const i18n = defineMessages({ interface ParameterInputProps { parameter: Parameter; onChange: (name: string, updatedParameter: Partial) => void; - onDelete?: (parameterKey: string) => void; - isUnused?: boolean; - isExpanded?: boolean; - onToggleExpanded?: (parameterKey: string) => void; + onDelete: (parameterKey: string) => void; + isUnused: boolean; + isExpanded: boolean; + onToggleExpanded: (parameterKey: string) => void; } const ParameterInput: React.FC = ({ parameter, onChange, onDelete, - isUnused = false, - isExpanded = true, + isUnused, + isExpanded, onToggleExpanded, }) => { const intl = useIntl(); @@ -106,7 +106,7 @@ const ParameterInput: React.FC = ({ const handleToggleExpanded = (e: React.MouseEvent) => { // Only toggle if we're not clicking on the delete button - if (onToggleExpanded && !(e.target as HTMLElement).closest('button')) { + if (!(e.target as HTMLElement).closest('button')) { onToggleExpanded(key); } }; @@ -115,26 +115,24 @@ const ParameterInput: React.FC = ({
{/* Collapsed header - always visible */}
- {onToggleExpanded && ( - - )} +
@@ -146,25 +144,25 @@ const ParameterInput: React.FC = ({ title={intl.formatMessage(i18n.unusedWarningTitle)} > - {intl.formatMessage(i18n.unused)} + + {intl.formatMessage(i18n.unused)} +
)}
- {onDelete && ( - - )} +
{/* Expandable content - only shown when expanded */} diff --git a/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx b/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx index f237f9ca32..a23f0f6405 100644 --- a/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx +++ b/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx @@ -113,7 +113,6 @@ interface CreateEditRecipeModalProps { recipe?: Recipe; isCreateMode?: boolean; recipeId?: string | null; - onRecipeSaved?: (savedRecipeId: string) => void; } export default function CreateEditRecipeModal({ @@ -122,7 +121,6 @@ export default function CreateEditRecipeModal({ recipe, isCreateMode = false, recipeId, - onRecipeSaved, }: CreateEditRecipeModalProps) { const intl = useIntl(); const getInitialValues = React.useCallback((): RecipeFormData => { @@ -423,11 +421,7 @@ export default function CreateEditRecipeModal({ try { const recipe = getCurrentRecipe(); - const { id: savedRecipeId } = await saveRecipe(recipe, recipeId); - - if (onRecipeSaved) { - onRecipeSaved(savedRecipeId); - } + await saveRecipe(recipe, recipeId); onClose(true); diff --git a/ui/desktop/src/components/recipes/RecipeActivities.tsx b/ui/desktop/src/components/recipes/RecipeActivities.tsx index 00661c10f9..179288824f 100644 --- a/ui/desktop/src/components/recipes/RecipeActivities.tsx +++ b/ui/desktop/src/components/recipes/RecipeActivities.tsx @@ -5,7 +5,6 @@ import MarkdownContent from '../MarkdownContent'; interface RecipeActivitiesProps { append: (text: string) => void; activities: string[] | null; - title?: string; } export default function RecipeActivities({ append, activities }: RecipeActivitiesProps) { diff --git a/ui/desktop/src/components/recipes/shared/CreateSubRecipeInline.tsx b/ui/desktop/src/components/recipes/shared/CreateSubRecipeInline.tsx index e02b70ab6c..8a64f18e09 100644 --- a/ui/desktop/src/components/recipes/shared/CreateSubRecipeInline.tsx +++ b/ui/desktop/src/components/recipes/shared/CreateSubRecipeInline.tsx @@ -129,14 +129,14 @@ interface CreateSubRecipeInlineProps { isOpen: boolean; onClose: () => void; onSubRecipeSaved: (subRecipe: SubRecipeFormData) => void; - existingSubRecipes?: SubRecipeFormData[]; + existingSubRecipes: SubRecipeFormData[]; } export default function CreateSubRecipeInline({ isOpen, onClose, onSubRecipeSaved, - existingSubRecipes = [], + existingSubRecipes, }: CreateSubRecipeInlineProps) { const intl = useIntl(); useEscapeKey(isOpen, onClose); @@ -221,12 +221,24 @@ export default function CreateSubRecipeInline({ toastError({ title: intl.formatMessage(i18n.saveFailed), - msg: intl.formatMessage(i18n.saveFailedMsg, { error: error instanceof Error ? error.message : 'Unknown error' }), + msg: intl.formatMessage(i18n.saveFailedMsg, { + error: error instanceof Error ? error.message : 'Unknown error', + }), }); } finally { setIsSaving(false); } - }, [form, name, toolDescription, sequentialWhenRepeated, values, existingSubRecipes, onSubRecipeSaved, onClose, intl]); + }, [ + form, + name, + toolDescription, + sequentialWhenRepeated, + values, + existingSubRecipes, + onSubRecipeSaved, + onClose, + intl, + ]); if (!isOpen) return null; @@ -236,10 +248,10 @@ export default function CreateSubRecipeInline({ {/* Header */}
-

{intl.formatMessage(i18n.title)}

-

- {intl.formatMessage(i18n.subtitle)} -

+

+ {intl.formatMessage(i18n.title)} +

+

{intl.formatMessage(i18n.subtitle)}

{/* Title Field */} @@ -283,7 +293,8 @@ export default function CreateSubRecipeInline({ htmlFor="subrecipe-title" className="block text-sm font-medium text-text-standard mb-2" > - {intl.formatMessage(i18n.recipeTitleLabel)} * + {intl.formatMessage(i18n.recipeTitleLabel)}{' '} + * - {intl.formatMessage(i18n.recipeDescriptionLabel)} * + {intl.formatMessage(i18n.recipeDescriptionLabel)}{' '} + * - {intl.formatMessage(i18n.instructionsLabel)} * + {intl.formatMessage(i18n.instructionsLabel)}{' '} + *