ui: Avatar + Badge + Tooltip + @mention autocomplete
- MessageItem: shadcn Avatar (square, brutalist), Badge for AGT/type labels - Tooltip on timestamps: tap/hover shows full date + seq number - ComposeBox: typing @ opens mention autocomplete popup - Arrow keys to navigate, Enter/Tab to select, Escape to close - Shows username, display name, role - Fetches user list on mount - More breathing room in messages (py-3/4) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import type { Message } from "@/types/Message";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
interface Props {
|
||||
message: Message;
|
||||
@@ -68,7 +71,17 @@ export function MessageItem({ message, replyTarget, onReply }: Props) {
|
||||
|
||||
<div className="px-4 py-3 md:px-5 md:py-4">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-2 text-[11px] flex-wrap">
|
||||
<div className="flex items-center gap-2.5 text-[11px] flex-wrap">
|
||||
{/* Avatar */}
|
||||
<Avatar size="sm" className="rounded-none">
|
||||
<AvatarFallback className={cn(
|
||||
"rounded-none font-mono text-[10px] font-bold",
|
||||
isAgent ? "bg-primary/20 text-primary" : "bg-muted text-muted-foreground"
|
||||
)}>
|
||||
{message.user.display_name[0]}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
{/* Name */}
|
||||
<span className={cn(
|
||||
"font-sans font-bold text-xs",
|
||||
@@ -79,32 +92,34 @@ export function MessageItem({ message, replyTarget, onReply }: Props) {
|
||||
|
||||
{/* Agent badge */}
|
||||
{isAgent && (
|
||||
<span className="font-mono text-[9px] font-bold px-1.5 py-0.5 bg-primary/15 text-primary uppercase tracking-wider">
|
||||
<Badge variant="outline" className="font-mono text-[9px] font-bold px-1.5 py-0 h-4 rounded-none border-primary/30 text-primary uppercase tracking-wider">
|
||||
AGT
|
||||
</span>
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Type badge */}
|
||||
{cfg.label && (
|
||||
<span className={cn("font-mono text-[9px] font-bold px-1.5 py-0.5 uppercase tracking-wider", cfg.labelBg)}>
|
||||
<Badge variant="secondary" className={cn("font-mono text-[9px] font-bold px-1.5 py-0 h-4 rounded-none uppercase tracking-wider", cfg.labelBg)}>
|
||||
{cfg.label}
|
||||
</span>
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Time */}
|
||||
<span className="text-muted-foreground font-mono tabular-nums md:hidden text-[10px]">
|
||||
{timeAgo(message.created_at)}
|
||||
</span>
|
||||
<span className="text-muted-foreground font-mono tabular-nums hidden md:inline text-[10px]">
|
||||
{new Date(message.created_at).toLocaleTimeString("en-US", {
|
||||
hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit",
|
||||
})}
|
||||
</span>
|
||||
|
||||
{/* Seq */}
|
||||
<span className="text-muted-foreground/30 font-mono tabular-nums text-[10px] hidden md:inline">
|
||||
#{Number(message.seq)}
|
||||
</span>
|
||||
{/* Time — tooltip shows full timestamp on mobile */}
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger className="text-muted-foreground font-mono tabular-nums text-[10px] cursor-default">
|
||||
<span className="md:hidden">{timeAgo(message.created_at)}</span>
|
||||
<span className="hidden md:inline">
|
||||
{new Date(message.created_at).toLocaleTimeString("en-US", {
|
||||
hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit",
|
||||
})}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="font-mono text-[10px] rounded-none">
|
||||
{new Date(message.created_at).toLocaleString()} · seq #{Number(message.seq)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
{/* Reply button */}
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user