diff --git a/frontend/src/app/(dashboard)/dashboard/_components/suggestions/examples.tsx b/frontend/src/app/(dashboard)/dashboard/_components/suggestions/examples.tsx index 0ad0ffd5..702f3c54 100644 --- a/frontend/src/app/(dashboard)/dashboard/_components/suggestions/examples.tsx +++ b/frontend/src/app/(dashboard)/dashboard/_components/suggestions/examples.tsx @@ -196,9 +196,9 @@ export const Examples = ({ onClick={() => onSelectPrompt && onSelectPrompt(prompt.query)} > -
-
- {React.cloneElement(prompt.icon as React.ReactElement, { size: 14 })} +
+
+ {React.cloneElement(prompt.icon as React.ReactElement, { size: 14, })}
{prompt.title} diff --git a/frontend/src/components/sidebar/nav-user-with-teams.tsx b/frontend/src/components/sidebar/nav-user-with-teams.tsx index 29be7a41..de7595a3 100644 --- a/frontend/src/components/sidebar/nav-user-with-teams.tsx +++ b/frontend/src/components/sidebar/nav-user-with-teams.tsx @@ -182,7 +182,7 @@ export function NavUserWithTeams({ { align="end" className="w-64 p-0 border" sideOffset={4} - style={{ - borderRadius: '16px' - }} > ( isVisible={showToolPreview} /> { diff --git a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx b/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx index bfd52076..9bea4d28 100644 --- a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx +++ b/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx @@ -179,15 +179,18 @@ export const ChatSettingsDropdown: React.FC = ({ @@ -198,10 +201,17 @@ export const ChatSettingsDropdown: React.FC = ({ - -
+ +
- + = ({ value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={handleSearchInputKeyDown} - className="w-full pl-8 pr-3 py-2 text-sm bg-transparent border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" + className="w-full pl-10 pr-3 py-2 text-sm bg-transparent border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" />
+
{agentsLoading ? ( -
+
Loading agents...
) : filteredAgents.length === 0 ? ( -
+
No agents found
) : ( @@ -231,37 +242,40 @@ export const ChatSettingsDropdown: React.FC = ({ -
- handleAgentSelect(agent.id)} - onMouseEnter={() => setHighlightedIndex(index)} - > + handleAgentSelect(agent.id)} + onMouseEnter={() => setHighlightedIndex(index)} + > +
+ {agent.icon} +
+
-
- {agent.icon} -
-
-
- - {agent.name} - - {agent.type === 'custom' && ( - - custom - - )} -
-
+ + {agent.name} + + {agent.type === 'custom' && ( + + custom + + )}
- {isSelected && ( - - )} - -
+ + {agent.description} + +
+ {isSelected && ( + + )} +

{truncateString(agent.description, 35)}

@@ -272,27 +286,28 @@ export const ChatSettingsDropdown: React.FC = ({ }) )}
-
-
+ +
+
- +
diff --git a/frontend/src/components/thread/chat-input/floating-tool-preview.tsx b/frontend/src/components/thread/chat-input/floating-tool-preview.tsx index 70308a53..fa5be96c 100644 --- a/frontend/src/components/thread/chat-input/floating-tool-preview.tsx +++ b/frontend/src/components/thread/chat-input/floating-tool-preview.tsx @@ -71,7 +71,7 @@ export const FloatingToolPreview: React.FC = ({ setIsExpanding(false); } }, [isVisible]); - + if (!currentToolCall || totalCalls === 0) return null; const toolName = currentToolCall.assistantCall?.name || 'Tool Call'; @@ -106,19 +106,19 @@ export const FloatingToolPreview: React.FC = ({ layoutId={CONTENT_LAYOUT_ID} whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.99 }} - className="bg-sidebar border border-border rounded-2xl p-2 w-full cursor-pointer group" + className="bg-sidebar border border-border rounded-3xl p-2 w-full cursor-pointer group" onClick={handleClick} style={{ opacity: isExpanding ? 0 : 1 }} >
- = ({ )}
- +

@@ -143,27 +143,27 @@ export const FloatingToolPreview: React.FC = ({

- +
- {isStreaming - ? `${agentName || 'Suna'} is working...` - : isSuccess - ? "Success" + {isStreaming + ? `${agentName || 'Suna'} is working...` + : isSuccess + ? "Success" : "Failed" }
- + diff --git a/frontend/src/components/thread/content/ThreadContent.tsx b/frontend/src/components/thread/content/ThreadContent.tsx index f287389f..d3f3ef9b 100644 --- a/frontend/src/components/thread/content/ThreadContent.tsx +++ b/frontend/src/components/thread/content/ThreadContent.tsx @@ -110,11 +110,11 @@ export function renderMarkdownContent( if (isNewXmlFormat(content)) { const contentParts: React.ReactNode[] = []; let lastIndex = 0; - + // Find all function_calls blocks const functionCallsRegex = /([\s\S]*?)<\/function_calls>/gi; let match; - + while ((match = functionCallsRegex.exec(content)) !== null) { // Add text before the function_calls block if (match.index > lastIndex) { @@ -127,22 +127,22 @@ export function renderMarkdownContent( ); } } - + // Parse the tool calls in this block const toolCalls = parseXmlToolCalls(match[0]); - + toolCalls.forEach((toolCall, index) => { const toolName = toolCall.functionName.replace(/_/g, '-'); - + if (toolName === 'ask') { // Handle ask tool specially - extract text and attachments const askText = toolCall.parameters.text || ''; const attachments = toolCall.parameters.attachments || []; - + // Convert single attachment to array for consistent handling - const attachmentArray = Array.isArray(attachments) ? attachments : - (typeof attachments === 'string' ? attachments.split(',').map(a => a.trim()) : []); - + const attachmentArray = Array.isArray(attachments) ? attachments : + (typeof attachments === 'string' ? attachments.split(',').map(a => a.trim()) : []); + // Render ask tool content with attachment UI contentParts.push(
@@ -152,7 +152,7 @@ export function renderMarkdownContent( ); } else { const IconComponent = getToolIcon(toolName); - + // Extract primary parameter for display let paramDisplay = ''; if (toolCall.parameters.file_path) { @@ -164,12 +164,12 @@ export function renderMarkdownContent( } else if (toolCall.parameters.url) { paramDisplay = toolCall.parameters.url; } - + contentParts.push(
- ); + ); } lastIndex = xmlRegex.lastIndex; } @@ -328,7 +328,7 @@ export const ThreadContent: React.FC = ({ // React Query file preloader const { preloadFiles } = useFilePreloader(); - const containerClassName = isPreviewMode + const containerClassName = isPreviewMode ? "flex-1 overflow-y-auto scrollbar-thin scrollbar-track-secondary/0 scrollbar-thumb-primary/10 scrollbar-thumb-rounded-full hover:scrollbar-thumb-primary/10 px-6 py-4 pb-72" : "flex-1 overflow-y-auto scrollbar-thin scrollbar-track-secondary/0 scrollbar-thumb-primary/10 scrollbar-thumb-rounded-full hover:scrollbar-thumb-primary/10 px-6 py-4 pb-72 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"; @@ -428,14 +428,14 @@ export const ThreadContent: React.FC = ({ groupedMessages.push({ type: 'user', messages: [message], key }); } else if (messageType === 'assistant' || messageType === 'tool' || messageType === 'browser_state') { // Check if we can add to existing assistant group (same agent) - const canAddToExistingGroup = currentGroup && + const canAddToExistingGroup = currentGroup && currentGroup.type === 'assistant_group' && (() => { // For assistant messages, check if agent matches if (messageType === 'assistant') { const lastAssistantMsg = currentGroup.messages.findLast(m => m.type === 'assistant'); if (!lastAssistantMsg) return true; // No assistant message yet, can add - + // Compare agent info - both null/undefined should be treated as same (default agent) const currentAgentId = message.agent_id; const lastAgentId = lastAssistantMsg.agent_id; @@ -567,7 +567,7 @@ export const ThreadContent: React.FC = ({ if (debugMode) { return (
-
+
                                                             {message.content}
                                                         
@@ -590,7 +590,7 @@ export const ThreadContent: React.FC = ({ return (
-
+
{cleanContent && ( {cleanContent} @@ -609,14 +609,14 @@ export const ThreadContent: React.FC = ({
{(() => { - const firstAssistantWithAgent = group.messages.find(msg => + const firstAssistantWithAgent = group.messages.find(msg => msg.type === 'assistant' && (msg.agents?.avatar || msg.agents?.avatar_color) ); if (firstAssistantWithAgent?.agents?.avatar) { const avatar = firstAssistantWithAgent.agents.avatar; const color = firstAssistantWithAgent.agents.avatar_color; return ( -
{avatar} @@ -628,7 +628,7 @@ export const ThreadContent: React.FC = ({

{(() => { - const firstAssistantWithAgent = group.messages.find(msg => + const firstAssistantWithAgent = group.messages.find(msg => msg.type === 'assistant' && msg.agents?.name ); if (firstAssistantWithAgent?.agents?.name) { @@ -638,7 +638,7 @@ export const ThreadContent: React.FC = ({ })()}

- + {/* Message content - ALL messages in the group */}
@@ -690,7 +690,7 @@ export const ThreadContent: React.FC = ({ if (message.type === 'assistant') { const parsedContent = safeJsonParse(message.content, {}); const msgKey = message.message_id || `submsg-assistant-${msgIndex}`; - + if (!parsedContent.content) return; const renderedContent = renderMarkdownContent( @@ -710,7 +710,7 @@ export const ThreadContent: React.FC = ({
); - + assistantMessageCount++; // Increment after adding the element } }); @@ -877,11 +877,11 @@ export const ThreadContent: React.FC = ({ > - {detectedTag === 'function_calls' ? + {detectedTag === 'function_calls' ? (() => { const extractedToolName = extractToolNameFromStream(streamingText); return extractedToolName ? getUserFriendlyToolName(extractedToolName) : 'Using Tool...'; - })() : + })() : getUserFriendlyToolName(detectedTag) } @@ -916,7 +916,7 @@ export const ThreadContent: React.FC = ({

{agentName || 'Suna'}

- + {/* Loader content */}
@@ -936,7 +936,7 @@ export const ThreadContent: React.FC = ({

{agentName || 'Suna'}

- + {/* Tool call content */}
@@ -961,7 +961,7 @@ export const ThreadContent: React.FC = ({

{agentName || 'Suna'}

- + {/* Streaming indicator content */}
diff --git a/frontend/src/components/thread/file-attachment.tsx b/frontend/src/components/thread/file-attachment.tsx index a98f137f..1f294731 100644 --- a/frontend/src/components/thread/file-attachment.tsx +++ b/frontend/src/components/thread/file-attachment.tsx @@ -294,7 +294,7 @@ export function FileAttachment({