feat(22-03): add ChatStopButton and ChatMessageActions components

- ChatStopButton: centered outline button with Square icon and 'Stop generating' label
- ChatMessageActions: edit Pencil for user messages (absolute, group-hover)
- ChatMessageActions: retry RefreshCw for assistant messages (right-aligned, group-hover)
- Both action buttons return null when isStreaming is true
- Proper aria-labels and tooltips on all interactive elements
This commit is contained in:
Nexus Dev 2026-04-01 18:23:53 +00:00
parent 8ebfd1a8c1
commit e91651caaa
2 changed files with 81 additions and 0 deletions

View file

@ -0,0 +1,58 @@
import { Pencil, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
interface ChatMessageActionsProps {
role: "user" | "assistant" | "system";
isStreaming?: boolean;
onEdit?: () => void;
onRetry?: () => void;
}
export function ChatMessageActions({ role, isStreaming, onEdit, onRetry }: ChatMessageActionsProps) {
if (isStreaming) return null;
if (role === "user" && onEdit) {
return (
<div className="absolute top-1 right-1 hidden group-hover:flex">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={onEdit}
aria-label="Edit message"
>
<Pencil className="h-3.5 w-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit message</TooltipContent>
</Tooltip>
</div>
);
}
if (role === "assistant" && onRetry) {
return (
<div className="flex justify-end mt-1">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 hidden group-hover:inline-flex"
onClick={onRetry}
aria-label="Retry response"
>
<RefreshCw className="h-3.5 w-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent>Retry response</TooltipContent>
</Tooltip>
</div>
);
}
return null;
}

View file

@ -0,0 +1,23 @@
import { Square } from "lucide-react";
import { Button } from "@/components/ui/button";
interface ChatStopButtonProps {
onStop: () => void;
}
export function ChatStopButton({ onStop }: ChatStopButtonProps) {
return (
<div className="flex justify-center py-2 border-t border-border">
<Button
variant="outline"
size="sm"
onClick={onStop}
aria-label="Stop generating response"
className="gap-1.5"
>
<Square className="h-3 w-3 fill-current" />
Stop generating
</Button>
</div>
);
}