Skip to content

Conversation

Message Branches

Switch between regenerated answers and edited prompts — arrows, a mini branch map, and direction-aware swaps.

2 variantsMIT

Preview

Cache resized images by content hash in .pressd/cache. On each build, hash the source file and the resize options; if the key exists, copy the cached output instead of running sharp again.
1 / 1
Original · sonnet-5.5

Version 1 of 1, Original

Installation

Copies the component into components/ui and installs its dependencies and theme tokens.

pnpm dlx shadcn@latest add https://tuffui.com/r/message-branches.json

Usage

Pass your own data and wire the callbacks to your agent runtime.

import { useState } from "react";
import { MessageBranches } from "@/components/ui/message-branches";

export function AssistantTurn({ turn }: { turn: Turn }) {
  const [index, setIndex] = useState(turn.versions.length - 1);

  return (
    <MessageBranches
      data={{
        turnsBefore: turn.position,
        versions: turn.versions.map((v) => ({
          id: v.id,
          kind: v.editedPrompt ? "edited" : v.parentId ? "regenerated" : "original",
          prompt: v.editedPrompt,
          meta: v.model,
          turnsAfter: v.followUps,
          content: <Markdown>{v.text}</Markdown>,
        })),
      }}
      index={index}
      onIndexChange={(next, version) => {
        setIndex(next);
        thread.checkout(version.id); // later turns follow the chosen branch
      }}
      onRegenerate={() => thread.regenerate(turn.id)}
    />
  );
}

Props

PropTypeDefaultDescription
data*{ versions: MessageVersion[]; turnsBefore?: number }—Every version of this answer, oldest first. `turnsBefore` is how many turns precede the fork, drawn as dots in the map.
indexnumber—Controlled index of the visible version. Leave it out to let the component track it.
defaultIndexnumberlast versionInitial version when uncontrolled.
onIndexChange(index: number, version: MessageVersion) => void—Called when the user switches with the arrows, the map or ←/→. Switch the thread to that branch here.
onRegenerate() => void—Shows a regenerate button next to the switcher.
variant"inline" | "map""inline"A ‹ 2 / 3 › switcher under the message, or a branch map with one row per version.
classNamestring—Extra classes for the root element.
type MessageVersion = {
  id: string;
  kind: "original" | "regenerated" | "edited";
  content: ReactNode;
  label?: string;      // overrides "Original" / "Regenerated" / "Edited prompt"
  meta?: string;       // e.g. the model, "opus-5.5"
  prompt?: string;     // the edited prompt, for kind "edited"
  turnsAfter?: number; // follow-up turns on this branch, for the map
};