feat: enhance UI and functionality across chat and settings
- Added custom scrollbar styles for improved aesthetics and usability across the interface. - Implemented responsive adjustments for chat sidebar and input components to enhance mobile experience. - Integrated tooltip components in select elements for better user guidance and information display. - Updated chat input and message components for improved styling and functionality. - Enhanced settings page with semantic query expansion options and improved model selection UI. - Refactored sidebar list to include better date formatting and tooltip support for session titles. - Introduced GPU stats monitoring in the system panel for better hardware insights.
This commit is contained in:
1 parent
84ea2dec19
commit
e5f062040c
33 files changed
+4670
-465
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { SessionSummary } from "@/hooks/use-chat-storage";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Trash2, Plus } from "lucide-react";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Trash2, Plus, MessageSquare } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface SidebarListProps {
|
||||
@@ -13,11 +14,36 @@ interface SidebarListProps {
|
||||
|
||||
function formatDate(dateString: string) {
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInMs = now.getTime() - date.getTime();
|
||||
const diffInHours = diffInMs / (1000 * 60 * 60);
|
||||
const diffInDays = diffInMs / (1000 * 60 * 60 * 24);
|
||||
|
||||
// Se foi hoje, mostrar apenas hora
|
||||
if (diffInHours < 24) {
|
||||
return new Intl.DateTimeFormat('pt-BR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
// Se foi ontem
|
||||
if (diffInDays < 2) {
|
||||
return 'Ontem';
|
||||
}
|
||||
|
||||
// Se foi nos últimos 7 dias
|
||||
if (diffInDays < 7) {
|
||||
return new Intl.DateTimeFormat('pt-BR', {
|
||||
weekday: 'short'
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
// Caso contrário, mostrar data completa
|
||||
return new Intl.DateTimeFormat('pt-BR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
year: diffInDays > 365 ? 'numeric' : undefined
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
@@ -29,61 +55,122 @@ export function SidebarList({
|
||||
onNewChat
|
||||
}: SidebarListProps) {
|
||||
return (
|
||||
<div className="flex flex-col h-full border-r bg-muted/10">
|
||||
<div className="p-4 border-b">
|
||||
<Button onClick={onNewChat} className="w-full justify-start gap-2" variant="outline">
|
||||
<Plus className="w-4 h-4" />
|
||||
Nova Conversa
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="p-2 space-y-2">
|
||||
{sessions.length === 0 && (
|
||||
<div className="text-center text-muted-foreground text-sm py-8">
|
||||
Nenhuma conversa salva
|
||||
</div>
|
||||
)}
|
||||
|
||||
{sessions.map((session) => (
|
||||
<div
|
||||
key={session.id}
|
||||
className={cn(
|
||||
"group flex items-center justify-between p-2 rounded-lg cursor-pointer hover:bg-accent transition-colors",
|
||||
currentSessionId === session.id && "bg-accent"
|
||||
)}
|
||||
onClick={() => onSelectSession(session.id)}
|
||||
>
|
||||
<div className="flex items-start gap-3 overflow-hidden flex-1">
|
||||
<span className="text-lg shrink-0 mt-0.5" role="img" aria-label="emoji">
|
||||
{session.emoji || '💬'}
|
||||
</span>
|
||||
<div className="flex flex-col overflow-hidden flex-1">
|
||||
<span className="text-sm font-medium truncate" title={session.title}>
|
||||
{session.title}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground truncate">
|
||||
{formatDate(session.updated_at)}
|
||||
</span>
|
||||
</div>
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<div className="flex flex-col h-full border-r border-sidebar-border bg-sidebar min-w-[240px] max-w-[360px] w-full">
|
||||
{/* Header com botão Nova Conversa */}
|
||||
<div className="p-3 sm:p-4 border-b border-sidebar-border flex-shrink-0">
|
||||
<Button
|
||||
onClick={onNewChat}
|
||||
className="w-full justify-start gap-2 text-sm font-medium h-9 sm:h-10"
|
||||
variant="outline"
|
||||
>
|
||||
<Plus className="w-4 h-4 shrink-0" />
|
||||
<span className="truncate">Nova Conversa</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Lista de chats com scroll customizado */}
|
||||
<div className="flex-1 overflow-y-auto min-h-0 sidebar-chat-list">
|
||||
<div className="p-3 sm:p-4 space-y-2 sm:space-y-2.5">
|
||||
{sessions.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-12 px-4 text-center">
|
||||
<MessageSquare className="w-10 h-10 text-muted-foreground/40 mb-3" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhuma conversa salva
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground/70 mt-1">
|
||||
Comece uma nova conversa para começar
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{sessions.map((session) => {
|
||||
const isSelected = currentSessionId === session.id;
|
||||
const showTooltip = session.title.length > 30;
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity shrink-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDeleteSession(session.id);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="w-3 h-3 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
return (
|
||||
<Tooltip key={session.id} delayDuration={300} disableHoverableContent>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn(
|
||||
"group relative bg-card border rounded-lg cursor-pointer transition-all duration-200",
|
||||
"hover:shadow-md hover:border-primary/20 hover:-translate-y-0.5",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
isSelected && "border-primary/40 shadow-sm bg-accent/50",
|
||||
"active:scale-[0.98]"
|
||||
)}
|
||||
onClick={() => onSelectSession(session.id)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onSelectSession(session.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Conteúdo do card */}
|
||||
<div className="p-3 sm:p-3.5 flex items-start gap-3 overflow-hidden">
|
||||
{/* Ícone/Emoji */}
|
||||
<div className="shrink-0 flex items-center justify-center w-8 h-8 sm:w-9 sm:h-9 rounded-md bg-muted/50 text-base sm:text-lg">
|
||||
{session.emoji || '💬'}
|
||||
</div>
|
||||
|
||||
{/* Texto do card */}
|
||||
<div className="flex-1 min-w-0 overflow-hidden">
|
||||
<h3
|
||||
className="text-sm sm:text-base font-medium text-card-foreground truncate leading-tight mb-1"
|
||||
>
|
||||
{session.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{formatDate(session.updated_at)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Botão de deletar - aparece no hover */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(
|
||||
"h-7 w-7 sm:h-8 sm:w-8 shrink-0 transition-all duration-200",
|
||||
"opacity-0 group-hover:opacity-100",
|
||||
"hover:bg-destructive/10 hover:text-destructive",
|
||||
"active:scale-95"
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onDeleteSession(session.id);
|
||||
}}
|
||||
aria-label={`Deletar conversa "${session.title}"`}
|
||||
>
|
||||
<Trash2 className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Indicador de seleção */}
|
||||
{isSelected && (
|
||||
<div className="absolute left-0 top-0 bottom-0 w-1 bg-primary rounded-l-lg" />
|
||||
)}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
{showTooltip && (
|
||||
<TooltipContent side="right" className="max-w-xs">
|
||||
<p className="break-words font-medium">{session.title}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{formatDate(session.updated_at)}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user