fe improvements

This commit is contained in:
marko-kraemer 2025-07-05 15:56:09 +02:00
parent d8791a48aa
commit 6e229b3830
6 changed files with 156 additions and 218 deletions

View File

@ -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

View File

@ -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>

View File

@ -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">

View File

@ -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>
);
},

View File

@ -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>

View File

@ -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',