click-to-select for multi-reply + /new command + layout fix
- Click any message to select it for reply (click again to deselect) - Multiple messages can be selected — compose shows all - Selected messages get orange left border + tinted bg - Floating pill shows ✓ when selected - /new <name> slash command creates channels from compose box - Proper multi-reply context in compose with count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,10 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp
|
||||
interface Props {
|
||||
message: Message;
|
||||
replyTarget?: Message;
|
||||
onReply: (id: string) => void;
|
||||
onSelect: (id: string) => void;
|
||||
onDelete: (channelId: string, msgId: string) => void;
|
||||
currentUsername: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
const TYPE_CONFIG: Record<string, { border: string; label: string; labelBg: string }> = {
|
||||
@@ -55,7 +56,7 @@ function userHue(username: string): number {
|
||||
return Math.abs(hash) % 360;
|
||||
}
|
||||
|
||||
export function MessageItem({ message, replyTarget, onReply, onDelete, currentUsername }: Props) {
|
||||
export function MessageItem({ message, replyTarget, onSelect, onDelete, currentUsername, selected }: Props) {
|
||||
const [metaOpen, setMetaOpen] = useState(false);
|
||||
const isAgent = message.user.role === "agent";
|
||||
const isDeleted = !!message.deleted_at;
|
||||
@@ -74,10 +75,11 @@ export function MessageItem({ message, replyTarget, onReply, onDelete, currentUs
|
||||
return (
|
||||
<div
|
||||
id={`msg-${message.id}`}
|
||||
onClick={() => onSelect(message.id)}
|
||||
className={cn(
|
||||
"group border-b border-border/50 border-l-4 transition-all duration-300",
|
||||
"group relative border-b border-border/50 border-l-4 transition-all duration-300 cursor-pointer",
|
||||
cfg.border,
|
||||
isAgent ? "bg-card" : "bg-background",
|
||||
selected ? "!border-l-primary bg-primary/5" : isAgent ? "bg-card" : "bg-background",
|
||||
"hover:bg-muted/30",
|
||||
)}
|
||||
>
|
||||
@@ -155,16 +157,19 @@ export function MessageItem({ message, replyTarget, onReply, onDelete, currentUs
|
||||
<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 z-10">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onReply(message.id)}
|
||||
className="px-2.5 py-1.5 text-sm text-muted-foreground hover:text-primary hover:bg-muted/50 transition-colors"
|
||||
title="Reply"
|
||||
onClick={(e) => { e.stopPropagation(); onSelect(message.id); }}
|
||||
className={cn(
|
||||
"px-2.5 py-1.5 text-sm hover:bg-muted/50 transition-colors",
|
||||
selected ? "text-primary" : "text-muted-foreground hover:text-primary",
|
||||
)}
|
||||
title={selected ? "Deselect" : "Select for reply"}
|
||||
>
|
||||
↩
|
||||
{selected ? "✓" : "↩"}
|
||||
</button>
|
||||
{!isDeleted && message.user.username === currentUsername && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onDelete(message.channel_id, message.id)}
|
||||
onClick={(e) => { e.stopPropagation(); onDelete(message.channel_id, message.id); }}
|
||||
className="px-2.5 py-1.5 text-sm text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors border-l-2 border-border"
|
||||
title="Delete"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user