reply UX: bigger chip, click-to-scroll with highlight

- Reply chip: larger with border-l-4, bg tint, proper padding
- Reply context in messages: click to smooth-scroll to quoted message
- Quoted message flashes primary/10 bg for 1.5s on scroll
- Each message gets id="msg-{id}" for scroll targeting
- Reply context text bumped to 11px

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:16:50 +02:00
parent 1f9201dd03
commit d6b7429925
2 changed files with 27 additions and 10 deletions

View File

@@ -126,14 +126,17 @@ export function ComposeBox({
)}> )}>
{/* Reply chip */} {/* Reply chip */}
{replyTo && ( {replyTo && (
<div className="flex items-center gap-1.5 mb-1.5 text-[10px] font-mono"> <div className="flex items-center gap-2 mb-3 px-3 py-2 border-l-4 border-primary bg-muted/30 text-xs font-mono">
<span className="text-primary">^</span> <div className="flex-1 min-w-0">
<span className="text-foreground font-bold">{replyTo.username}</span> <span className="text-primary font-bold">{replyTo.username}</span>
<span className="text-muted-foreground truncate max-w-48 md:max-w-96">{replyTo.content}</span> <span className="text-muted-foreground ml-2 truncate inline-block max-w-48 md:max-w-96 align-bottom">
{replyTo.content}
</span>
</div>
<button <button
type="button" type="button"
onClick={onClearReply} onClick={onClearReply}
className="text-muted-foreground hover:text-primary ml-auto flex-shrink-0" className="text-muted-foreground hover:text-primary flex-shrink-0 text-sm font-bold min-w-[32px] min-h-[32px] flex items-center justify-center"
> >
x x
</button> </button>

View File

@@ -60,22 +60,36 @@ export function MessageItem({ message, replyTarget, onReply }: Props) {
const cfg = TYPE_CONFIG[message.type] || TYPE_CONFIG.text; const cfg = TYPE_CONFIG[message.type] || TYPE_CONFIG.text;
const meta = message.metadata as Record<string, string> | null; const meta = message.metadata as Record<string, string> | null;
function scrollToMessage(id: string) {
const el = document.getElementById(`msg-${id}`);
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "center" });
el.classList.add("!bg-primary/10");
setTimeout(() => el.classList.remove("!bg-primary/10"), 1500);
}
}
return ( return (
<div <div
id={`msg-${message.id}`}
className={cn( className={cn(
"group border-b border-border/50 border-l-4 transition-colors", "group border-b border-border/50 border-l-4 transition-all duration-300",
cfg.border, cfg.border,
isAgent ? "bg-card" : "bg-background", isAgent ? "bg-card" : "bg-background",
"hover:bg-muted/30", "hover:bg-muted/30",
)} )}
> >
{/* Reply context */} {/* Reply context — click to scroll to quoted message */}
{replyTarget && ( {replyTarget && (
<div className="px-4 pt-1.5 text-[10px] text-muted-foreground flex items-center gap-1"> <button
type="button"
onClick={() => scrollToMessage(replyTarget.id)}
className="w-full text-left px-4 pt-2 pb-0.5 text-[11px] text-muted-foreground flex items-center gap-1.5 hover:text-foreground transition-colors"
>
<span className="text-primary font-bold">^</span> <span className="text-primary font-bold">^</span>
<span className="font-bold">{replyTarget.user.display_name}</span> <span className="font-bold">{replyTarget.user.display_name}</span>
<span className="truncate max-w-40 md:max-w-80 opacity-60">{replyTarget.content}</span> <span className="truncate max-w-48 md:max-w-96 opacity-60">{replyTarget.content}</span>
</div> </button>
)} )}
<div className="px-4 py-3 md:px-5 md:py-4"> <div className="px-4 py-3 md:px-5 md:py-4">