22 / 122 · Messages
Regenerate with
Fork the same turn to a different model instead of rolling the same dice.
Installation
npx shadcn@latest add "@assistant-ui/elements-regenerate-menu"
Usage
import { RegenerateMenu } from "@/components/elements/regenerate-menu";
<RegenerateMenu
options={options}
open={open}
currentId="sonnet"
onOpenChange={setOpen}
onPick={regenerate}
/>Props
options*RegenerateOption[]What the turn can be re-run with.
open*booleanWhether the menu is showing.
currentId*stringWhich option produced the answer on screen; it is labelled current instead of showing its detail.
onOpenChange(open: boolean) => voidCalled when the trigger is toggled.
onPick(id: string) => voidCalled with the chosen option.
classNamestringExtra classes merged onto the root.
Source
regenerate-menu.tsx"use client";
import { RefreshCwIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { floating, ghostButton, mono } from "./surfaces";
export interface RegenerateOption {
id: string;
label: string;
detail: string;
}
export function RegenerateMenu({
options,
open,
currentId,
onOpenChange,
onPick,
className,
}: {
options: readonly RegenerateOption[];
open: boolean;
currentId: string;
onOpenChange?: (open: boolean) => void;
onPick?: (id: string) => void;
className?: string;
}) {
return (
<div className={cn("flex w-full max-w-sm flex-col gap-2", className)}>
<button
type="button"
aria-expanded={open}
aria-label="Regenerate with a different model"
onClick={() => onOpenChange?.(!open)}
className={cn(
ghostButton,
"size-7 self-start",
open && "bg-foreground/[0.06] text-foreground/90",
)}
>
<RefreshCwIcon className="size-3.5" />
</button>
{open && (
<div
className={cn(
floating,
"fade-in zoom-in-95 slide-in-from-top-1 animate-in flex flex-col gap-0.5 rounded-2xl p-1.5 duration-200",
)}
>
{options.map((option) => (
<button
key={option.id}
type="button"
onClick={() => onPick?.(option.id)}
className="hover:bg-foreground/[0.05] flex items-baseline gap-2 rounded-xl px-2.5 py-1.5 text-start transition-colors"
>
<span className="min-w-0 flex-1 truncate text-[13px]">
{option.label}
</span>
<span className={cn(mono, "text-foreground/30 shrink-0")}>
{option.id === currentId ? "current" : option.detail}
</span>
</button>
))}
</div>
)}
</div>
);
}