mirror of https://github.com/kortix-ai/suna.git
fe improvements
This commit is contained in:
parent
d8791a48aa
commit
6e229b3830
|
@ -81,8 +81,8 @@ services:
|
|||
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
# ports:
|
||||
# - "127.0.0.1:6379:6379"
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
- ./services/docker/redis.conf:/usr/local/etc/redis/redis.conf:ro
|
||||
|
@ -104,8 +104,8 @@ services:
|
|||
|
||||
rabbitmq:
|
||||
image: rabbitmq
|
||||
# ports:
|
||||
# - "127.0.0.1:5672:5672"
|
||||
ports:
|
||||
- "127.0.0.1:5672:5672"
|
||||
volumes:
|
||||
- rabbitmq_data:/var/lib/rabbitmq
|
||||
restart: unless-stopped
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
Link as LinkIcon,
|
||||
MoreHorizontal,
|
||||
Trash2,
|
||||
Plus,
|
||||
MessagesSquare,
|
||||
Loader2,
|
||||
Share2,
|
||||
|
@ -383,97 +382,37 @@ export function NavAgents() {
|
|||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="text-muted-foreground hover:text-foreground h-7 w-7 flex items-center justify-center rounded-md"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
<span className="sr-only">New Agent</span>
|
||||
</Link>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New Agent</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<SidebarMenu className="overflow-y-auto max-h-[calc(100vh-200px)] [&::-webkit-scrollbar]:hidden [-ms-overflow-style:'none'] [scrollbar-width:'none']">
|
||||
{state === 'collapsed' && (
|
||||
<SidebarMenuItem>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>
|
||||
<SidebarMenuButton asChild>
|
||||
<Link href="/dashboard" className="flex items-center">
|
||||
<Plus className="h-4 w-4" />
|
||||
<span>New Agent</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New Agent</TooltipContent>
|
||||
</Tooltip>
|
||||
</SidebarMenuItem>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
// Show skeleton loaders while loading
|
||||
Array.from({ length: 3 }).map((_, index) => (
|
||||
<SidebarMenuItem key={`skeleton-${index}`}>
|
||||
<SidebarMenuButton>
|
||||
<div className="h-4 w-4 bg-sidebar-foreground/10 rounded-md animate-pulse"></div>
|
||||
<div className="h-3 bg-sidebar-foreground/10 rounded w-3/4 animate-pulse"></div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
))
|
||||
) : combinedThreads.length > 0 ? (
|
||||
// Show all threads with project info
|
||||
|
||||
{state !== 'collapsed' && (
|
||||
<>
|
||||
{combinedThreads.map((thread) => {
|
||||
// Check if this thread is currently active
|
||||
const isActive = pathname?.includes(thread.threadId) || false;
|
||||
const isThreadLoading = loadingThreadId === thread.threadId;
|
||||
const isSelected = selectedThreads.has(thread.threadId);
|
||||
{isLoading ? (
|
||||
// Show skeleton loaders while loading
|
||||
Array.from({ length: 3 }).map((_, index) => (
|
||||
<SidebarMenuItem key={`skeleton-${index}`}>
|
||||
<SidebarMenuButton>
|
||||
<div className="h-4 w-4 bg-sidebar-foreground/10 rounded-md animate-pulse"></div>
|
||||
<div className="h-3 bg-sidebar-foreground/10 rounded w-3/4 animate-pulse"></div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
))
|
||||
) : combinedThreads.length > 0 ? (
|
||||
// Show all threads with project info
|
||||
<>
|
||||
{combinedThreads.map((thread) => {
|
||||
// Check if this thread is currently active
|
||||
const isActive = pathname?.includes(thread.threadId) || false;
|
||||
const isThreadLoading = loadingThreadId === thread.threadId;
|
||||
const isSelected = selectedThreads.has(thread.threadId);
|
||||
|
||||
return (
|
||||
<SidebarMenuItem key={`thread-${thread.threadId}`} className="group">
|
||||
{state === 'collapsed' ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className={
|
||||
isActive ? 'bg-accent text-accent-foreground' :
|
||||
isSelected ? 'bg-primary/10' : ''
|
||||
}
|
||||
>
|
||||
<Link
|
||||
href={thread.url}
|
||||
onClick={(e) =>
|
||||
handleThreadClick(e, thread.threadId, thread.url)
|
||||
}
|
||||
>
|
||||
{isThreadLoading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<MessagesSquare className="h-4 w-4" />
|
||||
)}
|
||||
<span>{thread.projectName}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{thread.projectName}</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<div className="relative">
|
||||
return (
|
||||
<SidebarMenuItem key={`thread-${thread.threadId}`} className="group/row">
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className={`relative ${isActive
|
||||
|
@ -483,113 +422,101 @@ export function NavAgents() {
|
|||
: ''
|
||||
}`}
|
||||
>
|
||||
<Link
|
||||
href={thread.url}
|
||||
onClick={(e) =>
|
||||
handleThreadClick(e, thread.threadId, thread.url)
|
||||
}
|
||||
className="flex items-center"
|
||||
>
|
||||
<div className="flex items-center group/icon relative">
|
||||
{/* Show checkbox on hover or when selected, otherwise show MessagesSquare */}
|
||||
{isThreadLoading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
{/* MessagesSquare icon - hidden on hover if not selected */}
|
||||
<MessagesSquare
|
||||
className={`h-4 w-4 transition-opacity duration-150 ${isSelected ? 'opacity-0' : 'opacity-100 group-hover/icon:opacity-0'
|
||||
}`}
|
||||
/>
|
||||
|
||||
{/* Checkbox - appears on hover or when selected */}
|
||||
<div
|
||||
className={`absolute inset-0 flex items-center justify-center transition-opacity duration-150 ${isSelected
|
||||
? 'opacity-100'
|
||||
: 'opacity-0 group-hover/icon:opacity-100'
|
||||
}`}
|
||||
onClick={(e) => toggleThreadSelection(thread.threadId, e)}
|
||||
>
|
||||
<div
|
||||
className={`h-4 w-4 border rounded cursor-pointer hover:bg-muted/50 transition-colors flex items-center justify-center ${isSelected
|
||||
? 'bg-primary border-primary'
|
||||
: 'border-muted-foreground/30 bg-background'
|
||||
}`}
|
||||
>
|
||||
{isSelected && <Check className="h-3 w-3 text-primary-foreground" />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<span className="ml-2">{thread.projectName}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</div>
|
||||
)}
|
||||
{state !== 'collapsed' && !isSelected && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuAction
|
||||
showOnHover
|
||||
className="group-hover:opacity-100"
|
||||
onClick={() => {
|
||||
// Ensure pointer events are enabled when dropdown opens
|
||||
document.body.style.pointerEvents = 'auto';
|
||||
}}
|
||||
>
|
||||
<MoreHorizontal />
|
||||
<span className="sr-only">More</span>
|
||||
</SidebarMenuAction>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-56 rounded-lg"
|
||||
side={isMobile ? 'bottom' : 'right'}
|
||||
align={isMobile ? 'end' : 'start'}
|
||||
>
|
||||
<DropdownMenuItem onClick={() => {
|
||||
setSelectedItem({ threadId: thread?.threadId, projectId: thread?.projectId })
|
||||
setShowShareModal(true)
|
||||
}}>
|
||||
<Share2 className="text-muted-foreground" />
|
||||
<span>Share Chat</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<a
|
||||
<div className="flex items-center w-full">
|
||||
<Link
|
||||
href={thread.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) =>
|
||||
handleThreadClick(e, thread.threadId, thread.url)
|
||||
}
|
||||
className="flex items-center flex-1 min-w-0"
|
||||
>
|
||||
<ArrowUpRight className="text-muted-foreground" />
|
||||
<span>Open in New Tab</span>
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
handleDeleteThread(
|
||||
thread.threadId,
|
||||
thread.projectName,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 className="text-muted-foreground" />
|
||||
<span>Delete</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
})}
|
||||
{isThreadLoading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin mr-2 flex-shrink-0" />
|
||||
) : null}
|
||||
<span className="truncate">{thread.projectName}</span>
|
||||
</Link>
|
||||
|
||||
{/* Checkbox - only visible on hover of this specific area */}
|
||||
<div
|
||||
className="mr-1 flex-shrink-0 w-4 h-4 flex items-center justify-center group/checkbox"
|
||||
onClick={(e) => toggleThreadSelection(thread.threadId, e)}
|
||||
>
|
||||
<div
|
||||
className={`h-4 w-4 border rounded cursor-pointer transition-all duration-150 flex items-center justify-center ${isSelected
|
||||
? 'opacity-100 bg-primary border-primary hover:bg-primary/90'
|
||||
: 'opacity-0 group-hover/checkbox:opacity-100 border-muted-foreground/30 bg-background hover:bg-muted/50'
|
||||
}`}
|
||||
>
|
||||
{isSelected && <Check className="h-3 w-3 text-primary-foreground" />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dropdown Menu - inline with content */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className="flex-shrink-0 w-4 h-4 flex items-center justify-center hover:bg-muted/50 rounded transition-all duration-150 text-muted-foreground hover:text-foreground opacity-0 group-hover/row:opacity-100"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Ensure pointer events are enabled when dropdown opens
|
||||
document.body.style.pointerEvents = 'auto';
|
||||
}}
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">More actions</span>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-56 rounded-lg"
|
||||
side={isMobile ? 'bottom' : 'right'}
|
||||
align={isMobile ? 'end' : 'start'}
|
||||
>
|
||||
<DropdownMenuItem onClick={() => {
|
||||
setSelectedItem({ threadId: thread?.threadId, projectId: thread?.projectId })
|
||||
setShowShareModal(true)
|
||||
}}>
|
||||
<Share2 className="text-muted-foreground" />
|
||||
<span>Share Chat</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<a
|
||||
href={thread.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ArrowUpRight className="text-muted-foreground" />
|
||||
<span>Open in New Tab</span>
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
handleDeleteThread(
|
||||
thread.threadId,
|
||||
thread.projectName,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 className="text-muted-foreground" />
|
||||
<span>Delete</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton className="text-sidebar-foreground/70">
|
||||
<span>No tasks yet</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton className="text-sidebar-foreground/70">
|
||||
<MessagesSquare className="h-4 w-4" />
|
||||
<span>No tasks yet</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)}
|
||||
</SidebarMenu>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Bot, Menu, Store, Shield, Key, Workflow } from 'lucide-react';
|
||||
import { Bot, Menu, Store, Shield, Key, Workflow, Plus } from 'lucide-react';
|
||||
|
||||
import { NavAgents } from '@/components/sidebar/nav-agents';
|
||||
import { NavUserWithTeams } from '@/components/sidebar/nav-user-with-teams';
|
||||
|
@ -132,22 +132,20 @@ export function SidebarLeft({
|
|||
<SidebarContent className="[&::-webkit-scrollbar]:hidden [-ms-overflow-style:'none'] [scrollbar-width:'none']">
|
||||
{!flagsLoading && (customAgentsEnabled || marketplaceEnabled) && (
|
||||
<SidebarGroup>
|
||||
{customAgentsEnabled && (
|
||||
<Link href="/agents">
|
||||
<SidebarMenuButton className={cn({
|
||||
'bg-primary/10 font-medium': pathname === '/agents',
|
||||
})}>
|
||||
<Bot className="h-4 w-4 mr-2" />
|
||||
<span className="flex items-center justify-between w-full">
|
||||
Agent Playground
|
||||
</span>
|
||||
</SidebarMenuButton>
|
||||
</Link>
|
||||
)}
|
||||
<Link href="/dashboard">
|
||||
<SidebarMenuButton className={cn({
|
||||
'bg-accent text-accent-foreground font-medium': pathname === '/dashboard',
|
||||
})}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
<span className="flex items-center justify-between w-full">
|
||||
New Task
|
||||
</span>
|
||||
</SidebarMenuButton>
|
||||
</Link>
|
||||
{marketplaceEnabled && (
|
||||
<Link href="/marketplace">
|
||||
<SidebarMenuButton className={cn({
|
||||
'bg-primary/10 font-medium': pathname === '/marketplace',
|
||||
'bg-accent text-accent-foreground font-medium': pathname === '/marketplace',
|
||||
})}>
|
||||
<Store className="h-4 w-4 mr-2" />
|
||||
<span className="flex items-center justify-between w-full">
|
||||
|
@ -156,10 +154,22 @@ export function SidebarLeft({
|
|||
</SidebarMenuButton>
|
||||
</Link>
|
||||
)}
|
||||
{customAgentsEnabled && (
|
||||
<Link href="/agents">
|
||||
<SidebarMenuButton className={cn({
|
||||
'bg-accent text-accent-foreground font-medium': pathname === '/agents',
|
||||
})}>
|
||||
<Bot className="h-4 w-4 mr-2" />
|
||||
<span className="flex items-center justify-between w-full">
|
||||
Agent Playground
|
||||
</span>
|
||||
</SidebarMenuButton>
|
||||
</Link>
|
||||
)}
|
||||
{customAgentsEnabled && (
|
||||
<Link href="/settings/credentials">
|
||||
<SidebarMenuButton className={cn({
|
||||
'bg-primary/10 font-medium': pathname === '/settings/credentials',
|
||||
'bg-accent text-accent-foreground font-medium': pathname === '/settings/credentials',
|
||||
})}>
|
||||
<Key className="h-4 w-4 mr-2" />
|
||||
<span className="flex items-center justify-between w-full">
|
||||
|
|
|
@ -311,7 +311,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
</div>
|
||||
</Card>
|
||||
|
||||
{isAgentRunning && (
|
||||
{/* {isAgentRunning && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
|
@ -322,7 +322,8 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
<span>{agentName ? `${agentName} is working...` : 'Suna is working...'}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -264,7 +264,7 @@ export function SiteHeader({
|
|||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
{/* <Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
@ -278,7 +278,7 @@ export function SiteHeader({
|
|||
<TooltipContent>
|
||||
<p>Toggle Computer Preview (CMD+I)</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</Tooltip> */}
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -143,7 +143,7 @@ export const siteConfig = {
|
|||
hours: '2 hours',
|
||||
features: [
|
||||
'$20/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'More models',
|
||||
],
|
||||
|
@ -165,7 +165,7 @@ export const siteConfig = {
|
|||
hours: '6 hours',
|
||||
features: [
|
||||
'$50/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'More models',
|
||||
],
|
||||
|
@ -186,7 +186,7 @@ export const siteConfig = {
|
|||
hours: '12 hours',
|
||||
features: [
|
||||
'$100/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'Priority support',
|
||||
],
|
||||
|
@ -208,7 +208,7 @@ export const siteConfig = {
|
|||
hours: '25 hours',
|
||||
features: [
|
||||
'$200/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'More models',
|
||||
],
|
||||
|
@ -229,7 +229,7 @@ export const siteConfig = {
|
|||
hours: '50 hours',
|
||||
features: [
|
||||
'$400/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'Access to intelligent Model (Full Suna)',
|
||||
'Priority support',
|
||||
|
@ -253,7 +253,7 @@ export const siteConfig = {
|
|||
hours: '125 hours',
|
||||
features: [
|
||||
'$800/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'Access to intelligent Model (Full Suna)',
|
||||
'Priority support',
|
||||
|
@ -278,7 +278,7 @@ export const siteConfig = {
|
|||
hours: '200 hours',
|
||||
features: [
|
||||
'$1000/month usage',
|
||||
'+ $5 free included',
|
||||
// '+ $5 free included',
|
||||
'Private projects',
|
||||
'Access to intelligent Model (Full Suna)',
|
||||
'Priority support',
|
||||
|
|
Loading…
Reference in New Issue