diff --git a/ui/desktop/src/components/GooseMessage.tsx b/ui/desktop/src/components/GooseMessage.tsx index abd26ed191a3..cbc8aa6039d2 100644 --- a/ui/desktop/src/components/GooseMessage.tsx +++ b/ui/desktop/src/components/GooseMessage.tsx @@ -1,8 +1,5 @@ import { useEffect, useMemo, useRef } from 'react'; -import LinkPreview from './LinkPreview'; import ImagePreview from './ImagePreview'; -import GooseResponseForm from './GooseResponseForm'; -import { extractUrls } from '../utils/urlUtils'; import { extractImagePaths, removeImagePathsFromText } from '../utils/imageUtils'; import { formatMessageTimestamp } from '../utils/timeUtils'; import MarkdownContent from './MarkdownContent'; @@ -44,7 +41,6 @@ export default function GooseMessage({ sessionId, messageHistoryIndex, message, - metadata, messages, toolCallNotifications, append, @@ -135,15 +131,6 @@ export default function GooseMessage({ // Get the chain this message belongs to const messageChain = getChainForMessage(messageIndex, toolCallChains); - - // Extract URLs under a few conditions - // 1. The message is purely text - // 2. The link wasn't also present in the previous message - // 3. The message contains the explicit http:// or https:// protocol at the beginning - const previousMessage = messageIndex > 0 ? messages[messageIndex - 1] : null; - const previousUrls = previousMessage ? extractUrls(getTextContent(previousMessage)) : []; - const urls = toolRequests.length === 0 ? extractUrls(displayText, previousUrls) : []; - const toolConfirmationContent = getToolConfirmationContent(message); const hasToolConfirmation = toolConfirmationContent !== undefined; @@ -302,27 +289,6 @@ export default function GooseMessage({ /> )} - - {/* TODO(alexhancock): Re-enable link previews once styled well again */} - {/* TEMPORARILY DISABLED (dorien-koelemeijer): This is causing issues in properly "generating" tool calls - that contain links and prevents security scanning */} - {/* eslint-disable-next-line no-constant-binary-expression */} - {false && urls.length > 0 && ( -
- {urls.map((url, index) => ( - - ))} -
- )} - - {/* enable or disable prompts here */} - {/* NOTE from alexhancock on 1/14/2025 - disabling again temporarily due to non-determinism in when the forms show up */} - {/* eslint-disable-next-line no-constant-binary-expression */} - {false && metadata && ( -
- -
- )} ); } diff --git a/ui/desktop/src/components/GooseResponseForm.tsx b/ui/desktop/src/components/GooseResponseForm.tsx deleted file mode 100644 index 6d6855c32f10..000000000000 --- a/ui/desktop/src/components/GooseResponseForm.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import MarkdownContent from './MarkdownContent'; -import { Button } from './ui/button'; -import { cn } from '../utils'; -import { Send } from './icons'; -// Prefixing unused imports with underscore -import { createUserMessage as _createUserMessage } from '../types/message'; - -interface FormField { - label: string; - type: 'text' | 'textarea'; - name: string; - placeholder: string; - required: boolean; -} - -interface DynamicForm { - title: string; - description: string; - fields: FormField[]; -} - -interface GooseResponseFormProps { - message: string; - metadata: string[] | null; - append: (value: string) => void; -} - -export default function GooseResponseForm({ - message: _message, - metadata, - append, -}: GooseResponseFormProps) { - const [selectedOption, setSelectedOption] = useState(null); - const [formValues, setFormValues] = useState>({}); - const prevStatusRef = useRef(null); - - let isQuestion = false; - let isOptions = false; - let options: Array<{ optionTitle: string; optionDescription: string }> = []; - let dynamicForm: DynamicForm | null = null; - - if (metadata) { - window.electron.logInfo('metadata:' + JSON.stringify(metadata, null, 2)); - } - - // Process metadata outside of conditional - const currentStatus = metadata?.[0] ?? null; - isQuestion = currentStatus === 'QUESTION'; - isOptions = metadata?.[1] === 'OPTIONS'; - - // Parse dynamic form data if it exists in metadata[3] - if (metadata?.[3]) { - try { - dynamicForm = JSON.parse(metadata[3]); - } catch (err) { - console.error('Failed to parse form data:', err); - dynamicForm = null; - } - } - - if (isQuestion && isOptions && metadata?.[2]) { - try { - let optionsData = metadata[2]; - // Use a regular expression to extract the JSON block - const jsonBlockMatch = optionsData.match(/```json([\s\S]*?)```/); - - // If a JSON block is found, extract and clean it - if (jsonBlockMatch) { - optionsData = jsonBlockMatch[1].trim(); // Extract the content inside the block - } else { - // Optionally, handle the case where there is no explicit ```json block - console.warn('No JSON block found in the provided string.'); - } - options = JSON.parse(optionsData); - options = options.filter( - (opt) => typeof opt.optionTitle === 'string' && typeof opt.optionDescription === 'string' - ); - } catch (err) { - console.error('Failed to parse options data:', err); - options = []; - } - } - - // Move useEffect to top level - useEffect(() => { - const currentMetadataStatus = metadata?.[0]; - const shouldNotify = - currentMetadataStatus && - (currentMetadataStatus === 'QUESTION' || currentMetadataStatus === 'OPTIONS') && - prevStatusRef.current !== currentMetadataStatus; - - if (shouldNotify) { - window.electron.showNotification({ - title: 'Goose has a question for you', - body: `Please check with Goose to approve the plan of action`, - }); - } - - prevStatusRef.current = currentMetadataStatus ?? null; - }, [metadata]); - - const handleOptionClick = (index: number) => { - setSelectedOption(index); - }; - - const handleAccept = () => { - append('Yes - go ahead.'); - }; - - const handleSubmit = () => { - if (selectedOption !== null && options[selectedOption]) { - append(`Yes - continue with: ${options[selectedOption].optionTitle}`); - } - }; - - const handleFormSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (dynamicForm) { - append(JSON.stringify(formValues)); - } - }; - - const handleFormChange = (name: string, value: string) => { - setFormValues((prev) => ({ - ...prev, - [name]: value, - })); - }; - - if (!metadata) { - return null; - } - - function isForm(f: DynamicForm | null): f is DynamicForm { - return ( - !!f && - !!f.title && - !!f.description && - !!f.fields && - Array.isArray(f.fields) && - f.fields.length > 0 - ); - } - - return ( -
- {isQuestion && !isOptions && !isForm(dynamicForm) && ( -
- -
- )} - {isQuestion && isOptions && Array.isArray(options) && options.length > 0 && ( -
- {options.map((opt, index) => ( -
handleOptionClick(index)} - className={cn( - 'p-4 rounded-lg border transition-colors cursor-pointer', - selectedOption === index - ? 'bg-primary/10 dark:bg-dark-primary border-primary dark:border-dark-primary' - : 'bg-tool-card dark:bg-tool-card-dark hover:bg-accent dark:hover:bg-dark-accent' - )} - > -

{opt.optionTitle}

-
- -
-
- ))} - -
- )} - {isForm(dynamicForm) && !isOptions && ( -
-

{dynamicForm.title}

-

{dynamicForm.description}

- - {dynamicForm.fields.map((field) => ( -
- - {field.type === 'textarea' ? ( -