{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "checkpoint",
  "title": "Checkpoint",
  "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
  "description": "Inline checkpoint marker between chat messages: a bookmark icon, a small ghost button with an optional tooltip, and a separator rule that fills the rest of the row. Use it to let people restore a conversation or agent workspace to an earlier point. Composes shadcn button, tooltip and separator.",
  "dependencies": [
    "cn",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "separator",
    "tooltip"
  ],
  "files": [
    {
      "path": "registry/ai/checkpoint.tsx",
      "content": "// Derived from Vercel AI Elements checkpoint.tsx (Apache-2.0, Copyright 2023 Vercel, Inc.).\n// Modified for uifiles: ported from Radix UI to Base UI; dependencies point at @uifiles.\n\"use client\"\n\nimport { cn } from \"cn\"\nimport type { LucideProps } from \"lucide-react\"\nimport { BookmarkIcon } from \"lucide-react\"\nimport type { ComponentProps, HTMLAttributes, ReactNode } from \"react\"\nimport { Fragment, isValidElement, useId } from \"react\"\nimport { Button } from \"@/components/ui/button\"\nimport { Separator } from \"@/components/ui/separator\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\nexport type CheckpointProps = HTMLAttributes<HTMLDivElement>\n\nexport const Checkpoint = ({\n  className,\n  children,\n  ...props\n}: CheckpointProps) => (\n  <div\n    className={cn(\n      \"flex items-center gap-0.5 overflow-hidden text-muted-foreground\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <Separator />\n  </div>\n)\n\nexport type CheckpointIconProps = LucideProps\n\nexport const CheckpointIcon = ({\n  className,\n  children,\n  ...props\n}: CheckpointIconProps) =>\n  children ?? (\n    <BookmarkIcon className={cn(\"size-4 shrink-0\", className)} {...props} />\n  )\n\n/**\n * The text `node` renders, read through arrays, fragments and plain\n * elements; undefined when a component renders part of it, since its\n * output cannot be known here.\n */\nfunction textOf(node: ReactNode): string | undefined {\n  if (node === null || node === undefined || typeof node === \"boolean\") {\n    return \"\"\n  }\n  if (typeof node === \"string\" || typeof node === \"number\") return `${node}`\n  if (Array.isArray(node)) {\n    let text = \"\"\n    for (const child of node) {\n      const part = textOf(child)\n      if (part === undefined) return undefined\n      text += part\n    }\n    return text\n  }\n  if (\n    isValidElement<{ children?: ReactNode }>(node) &&\n    (node.type === Fragment || typeof node.type === \"string\")\n  ) {\n    return textOf(node.props.children)\n  }\n  return undefined\n}\n\n/** Whitespace collapsed and trimmed, as accessible names are. */\nconst normalize = (text: string) => text.replace(/\\s+/g, \" \").trim()\n\nexport type CheckpointTriggerProps = ComponentProps<typeof Button> & {\n  tooltip?: string | undefined\n}\n\nexport const CheckpointTrigger = ({\n  children,\n  variant = \"ghost\",\n  size = \"sm\",\n  tooltip,\n  ...props\n}: CheckpointTriggerProps) => {\n  const descriptionId = useId()\n\n  if (!tooltip) {\n    return (\n      <Button size={size} type=\"button\" variant={variant} {...props}>\n        {children}\n      </Button>\n    )\n  }\n\n  // Base UI tooltips are visual only (no role or aria-describedby), so the\n  // text is mirrored in a visually hidden description unless it would just\n  // repeat the button's own name: a non-empty aria-label, or else the text\n  // its children render (\"Checkpoint {index}\" is two children). A name from\n  // aria-labelledby cannot be read here.\n  const name =\n    props[\"aria-labelledby\"] === undefined\n      ? props[\"aria-label\"]?.trim() || textOf(children)\n      : undefined\n  const describes = name === undefined || normalize(tooltip) !== normalize(name)\n\n  return (\n    <>\n      <Tooltip>\n        <TooltipTrigger\n          render={\n            <Button\n              aria-describedby={describes ? descriptionId : undefined}\n              size={size}\n              type=\"button\"\n              variant={variant}\n              {...props}\n            />\n          }\n        >\n          {children}\n        </TooltipTrigger>\n        <TooltipContent align=\"start\" side=\"bottom\">\n          {tooltip}\n        </TooltipContent>\n      </Tooltip>\n      {describes && (\n        <span className=\"sr-only\" id={descriptionId}>\n          {tooltip}\n        </span>\n      )}\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ai/checkpoint.tsx"
    }
  ],
  "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI port of AI Elements checkpoint. CheckpointTrigger composes TooltipTrigger with render={<Button />} instead of asChild; it works without a TooltipProvider (wrap one to share delay). Base UI tooltips are visual only, so CheckpointTrigger mirrors the tooltip text into a visually hidden description referenced by aria-describedby, unless it repeats the button's accessible name, compared as text with whitespace collapsed: a non-empty aria-label, or else the text its children render (strings and numbers, read through fragments and plain elements, so `Checkpoint {index}` counts). The description stays when there is no aria-label and a child is a component (an icon included), since a component's output cannot be read while rendering, and whenever aria-labelledby names the button; your own aria-describedby wins. Separator is Base UI's: always role=separator, no `decorative` prop. Export names and props are unchanged.",
  "categories": [
    "ai",
    "chat"
  ],
  "type": "registry:component"
}