floating action pill + auto-scroll on send + channel ordering

- Actions: floating pill top-right with ↩ and × (slides in on hover)
- No more ugly REPLY DEL text — minimal symbols, backdrop shadow
- Delete button only shows for own messages
- Auto-scroll to bottom after sending a message
- Channels sorted by last opened in sidebar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:25:33 +02:00
parent 36561941b4
commit 2b1ed18cde
2 changed files with 24 additions and 9 deletions

View File

@@ -214,7 +214,17 @@ export default function App() {
channelId={activeChannelId}
replyTo={replyTo}
onClearReply={() => setReplyTo(null)}
onMessageSent={loadMessages}
onMessageSent={() => {
loadMessages();
// Force scroll to bottom after sending
setTimeout(() => {
if (scrollRef.current) {
const el = scrollRef.current as unknown as HTMLElement;
const viewport = el.querySelector('[data-slot="scroll-area-viewport"]') || el;
viewport.scrollTop = viewport.scrollHeight;
}
}, 100);
}}
/>
)}
</div>

View File

@@ -149,26 +149,31 @@ export function MessageItem({ message, replyTarget, onReply, onDelete, currentUs
</Tooltip>
</TooltipProvider>
{/* Actions */}
<div className="ml-auto flex items-center gap-1 md:opacity-0 md:group-hover:opacity-100 transition-opacity">
</div>
{/* Floating action pill — top-right, appears on hover */}
{!isDeleted && (
<div className="absolute -top-3 right-3 md:opacity-0 md:translate-y-1 md:group-hover:opacity-100 md:group-hover:translate-y-0 transition-all duration-150 flex border-2 border-border bg-card shadow-lg">
<button
type="button"
onClick={() => onReply(message.id)}
className="font-sans text-[10px] font-bold uppercase tracking-wider text-muted-foreground hover:text-primary min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center"
className="px-2 py-1 text-xs text-muted-foreground hover:text-primary hover:bg-muted/50 transition-colors"
title="Reply"
>
Reply
</button>
{!isDeleted && message.user.username === currentUsername && (
{message.user.username === currentUsername && (
<button
type="button"
onClick={() => onDelete(message.channel_id, message.id)}
className="font-sans text-[10px] font-bold uppercase tracking-wider text-muted-foreground hover:text-destructive min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center"
className="px-2 py-1 text-xs text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors border-l-2 border-border"
title="Delete"
>
Del
×
</button>
)}
</div>
</div>
)}
{/* Content */}
<div className={cn(