{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "breadcrumbs",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion", "lucide-react"],
  "devDependencies": [],
  "files": [
    {
      "path": "breadcrumbs.tsx",
      "content": "\"use client\";\n\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronRightIcon, MoreHorizontalIcon } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport type * as React from \"react\";\nimport {\n  forwardRef,\n  type ReactNode,\n  useCallback,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst componentThemeClassName =\n  \"[--ic-background:#ffffff] [--ic-foreground:#111111] [--ic-primary:#111111] [--ic-secondary:#646b75] [--ic-surface-border:#e9edf2] [--ic-border:#e3e7ec] [--ic-card:#ffffff] [--ic-card-foreground:#111111] [--ic-muted:#f5f7fa] [--ic-muted-foreground:#6d7480] [--ic-accent:#f3f5f8] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--ic-accent-foreground:#111111] [--ic-input:#e3e7ec] [--ic-ring:rgba(17,17,17,0.16)] [--ic-destructive:#dc2626] [--ic-paper:#fcfcfd] [--ic-popover-foreground:#111111] [--ic-brand:#0ea5e9] [--ic-brand-soft:#bae6fd] [--ic-shadow-soft:0_18px_38px_-24px_rgba(15,23,42,0.35)] [--ic-chart-1:oklch(0.52_0.19_254)] [--ic-chart-2:oklch(0.74_0.11_232)] [--ic-chart-3:oklch(0.42_0.16_262)] [--ic-chart-4:oklch(0.84_0.07_228)] [--ic-chart-5:oklch(0.62_0.14_240)] [--color-background:var(--ic-background)] [--color-foreground:var(--ic-foreground)] [--color-primary:var(--ic-primary)] [--color-secondary:var(--ic-secondary)] [--color-border:var(--ic-border)] [--color-card:var(--ic-card)] [--color-card-foreground:var(--ic-card-foreground)] [--color-muted:var(--ic-muted)] [--color-muted-foreground:var(--ic-muted-foreground)] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--color-input:var(--ic-input)] [--color-ring:var(--ic-ring)] [--color-destructive:var(--ic-destructive)] [--color-paper:var(--ic-paper)] [--color-popover-foreground:var(--ic-popover-foreground)] [--color-brand:var(--ic-brand)] [--color-brand-soft:var(--ic-brand-soft)] [--color-chart-1:var(--ic-chart-1)] [--color-chart-2:var(--ic-chart-2)] [--color-chart-3:var(--ic-chart-3)] [--color-chart-4:var(--ic-chart-4)] [--color-chart-5:var(--ic-chart-5)] dark:[--ic-background:#111111] dark:[--ic-foreground:#f6f3ec] dark:[--ic-primary:#f6f3ec] dark:[--ic-secondary:#cbc6bb] dark:[--ic-surface-border:#2a2a25] dark:[--ic-border:#2b2a25] dark:[--ic-card:#111111] dark:[--ic-card-foreground:#f6f3ec] dark:[--ic-muted:#171716] dark:[--ic-muted-foreground:#9a958a] dark:[--ic-accent:#1a1a18] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] dark:[--ic-accent-foreground:#f6f3ec] dark:[--ic-input:#2b2a25] dark:[--ic-ring:rgba(246,243,236,0.18)] dark:[--ic-destructive:#f87171] dark:[--ic-paper:#171716] dark:[--ic-popover-foreground:#f6f3ec] dark:[--ic-brand:#38bdf8] dark:[--ic-brand-soft:#0c4a6e] dark:[--ic-shadow-soft:0_20px_44px_-28px_rgba(0,0,0,0.6)] dark:[--ic-chart-1:oklch(0.68_0.17_250)] dark:[--ic-chart-2:oklch(0.82_0.09_225)] dark:[--ic-chart-3:oklch(0.58_0.15_260)] dark:[--ic-chart-4:oklch(0.75_0.12_235)] dark:[--ic-chart-5:oklch(0.88_0.06_220)]\";\n\nconst subtleEase = [0.22, 1, 0.36, 1] as const;\n\nconst itemVariants = {\n  initial: { opacity: 0, x: -6 },\n  animate: { opacity: 1, x: 0 },\n  exit: { opacity: 0, x: 6 },\n};\n\nconst separatorVariants = {\n  initial: { opacity: 0, x: -4 },\n  animate: { opacity: 1, x: 0 },\n  exit: { opacity: 0, x: 4 },\n};\n\nconst breadcrumbLinkClassName =\n  \"rounded-sm outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\";\n\nconst breadcrumbTruncateClassName =\n  \"block max-w-[9rem] truncate sm:max-w-[14rem]\";\n\nconst breadcrumbIconClassName =\n  \"flex shrink-0 items-center justify-center text-current [&>svg]:size-3.5\";\n\nconst menuCornerClassName =\n  \"rounded-lg supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[11px]\";\n\nconst menuCornerInheritClassName =\n  \"rounded-[inherit] supports-[corner-shape:squircle]:[corner-shape:inherit]\";\n\nconst menuPanelClassName = cn(\n  menuCornerClassName,\n  \"absolute top-[calc(100%+0.35rem)] left-0 z-50 min-w-[10rem] overflow-hidden border border-border/60 bg-card p-1 text-card-foreground shadow-[var(--ic-shadow-soft)]\"\n);\n\nconst menuItemClassName = cn(\n  menuCornerClassName,\n  \"relative isolate flex min-h-11 w-full cursor-pointer touch-manipulation select-none items-center gap-2 px-3 py-2.5 text-left text-foreground text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\"\n);\n\nconst menuItemHighlightClassName = cn(\n  menuCornerInheritClassName,\n  \"absolute inset-0 -z-10 bg-accent/68\"\n);\n\nconst menuHighlightSpring = {\n  type: \"spring\" as const,\n  stiffness: 380,\n  damping: 41,\n  mass: 0.82,\n};\n\ntype MotionOlProps = React.ComponentProps<typeof motion.ol>;\ntype MotionLiProps = React.ComponentProps<typeof motion.li>;\n\ntype BreadcrumbItemData = {\n  href?: string;\n  icon?: ReactNode;\n  label: ReactNode;\n  title?: string;\n};\n\ntype BreadcrumbEllipsisMenuItem = {\n  href?: string;\n  icon?: ReactNode;\n  label: ReactNode;\n};\n\ntype BreadcrumbJsonLdItem = {\n  href?: string;\n  label: string;\n};\n\ntype BreadcrumbJsonLdProps = {\n  currentPath?: string;\n  items: BreadcrumbJsonLdItem[];\n  siteUrl: string;\n};\n\ntype BreadcrumbListItem = {\n  \"@type\": \"ListItem\";\n  item: string;\n  name: string;\n  position: number;\n};\n\ntype BreadcrumbProps = React.ComponentProps<\"nav\"> & {\n  ariaLabel?: string;\n};\n\ntype BreadcrumbLinkProps = useRender.ComponentProps<\"a\"> & {\n  truncate?: boolean;\n};\n\ntype BreadcrumbPageProps = React.ComponentProps<\"span\"> & {\n  truncate?: boolean;\n};\n\ntype BreadcrumbEllipsisMenuProps = {\n  className?: string;\n  items: BreadcrumbEllipsisMenuItem[];\n  menuLabel?: string;\n};\n\ntype BreadcrumbsProps = Omit<BreadcrumbProps, \"children\"> & {\n  currentPath?: string;\n  items: BreadcrumbItemData[];\n  listClassName?: string;\n  maxItems?: number;\n  renderLink?: (props: {\n    children: ReactNode;\n    className?: string;\n    href: string;\n    title?: string;\n    truncate?: boolean;\n  }) => ReactNode;\n  separator?: ReactNode;\n  siteUrl?: string;\n  truncate?: boolean;\n};\n\nfunction getBreadcrumbItemTitle(item: BreadcrumbItemData) {\n  if (item.title) {\n    return item.title;\n  }\n\n  return typeof item.label === \"string\" ? item.label : undefined;\n}\n\nfunction getBreadcrumbItemKey(item: BreadcrumbItemData, index: number) {\n  if (typeof item.label === \"string\") {\n    return `breadcrumb-${index}-${item.label}`;\n  }\n\n  return `breadcrumb-${index}`;\n}\n\nfunction getMenuItemLabel(item: BreadcrumbEllipsisMenuItem, index: number) {\n  return typeof item.label === \"string\" ? item.label : `Item ${index + 1}`;\n}\n\nfunction collapseBreadcrumbItems<T>(items: T[], maxItems: number) {\n  if (items.length <= maxItems) {\n    return {\n      collapsed: [] as T[],\n      leading: items,\n      trailing: [] as T[],\n    };\n  }\n\n  const trailingCount = Math.max(1, maxItems - 2);\n  const leading = items.slice(0, 1);\n  const trailing = items.slice(items.length - trailingCount);\n  const collapsed = items.slice(1, items.length - trailingCount);\n\n  return { collapsed, leading, trailing };\n}\n\nfunction toAbsoluteBreadcrumbUrl(siteUrl: string, path: string) {\n  if (path.startsWith(\"http://\") || path.startsWith(\"https://\")) {\n    return path;\n  }\n\n  const normalizedSiteUrl = siteUrl.endsWith(\"/\")\n    ? siteUrl.slice(0, -1)\n    : siteUrl;\n  const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n\n  return `${normalizedSiteUrl}${normalizedPath}`;\n}\n\nfunction BreadcrumbSegmentContent({\n  icon,\n  label,\n  truncate = false,\n}: {\n  icon?: ReactNode;\n  label: ReactNode;\n  truncate?: boolean;\n}) {\n  return (\n    <span\n      className={cn(\n        \"inline-flex min-w-0 items-center\",\n        icon ? \"gap-1.5\" : undefined\n      )}\n    >\n      {icon ? <span className={breadcrumbIconClassName}>{icon}</span> : null}\n      <span className={cn(\"min-w-0\", truncate && breadcrumbTruncateClassName)}>\n        {label}\n      </span>\n    </span>\n  );\n}\n\nfunction BreadcrumbMenuItemContent({\n  icon,\n  label,\n}: {\n  icon?: ReactNode;\n  label: ReactNode;\n}) {\n  return (\n    <>\n      {icon ? (\n        <span\n          className={cn(\n            breadcrumbIconClassName,\n            \"relative z-10 size-4 text-muted-foreground [&>svg]:size-4\"\n          )}\n        >\n          {icon}\n        </span>\n      ) : null}\n      <span className=\"relative z-10 min-w-0 flex-1 truncate text-left\">\n        {label}\n      </span>\n    </>\n  );\n}\n\nconst Breadcrumb = forwardRef<HTMLElement, BreadcrumbProps>(\n  ({ ariaLabel = \"Breadcrumb\", className, ...props }, ref) => {\n    return (\n      <nav\n        aria-label={ariaLabel}\n        className={cn(componentThemeClassName, className)}\n        data-slot=\"breadcrumb\"\n        ref={ref}\n        {...props}\n      />\n    );\n  }\n);\nBreadcrumb.displayName = \"Breadcrumb\";\n\nconst BreadcrumbList = forwardRef<HTMLOListElement, React.ComponentProps<\"ol\">>(\n  ({ children, className, ...props }, ref) => {\n    return (\n      <motion.ol\n        className={cn(\n          \"wrap-break-word flex flex-wrap items-center gap-1.5 text-muted-foreground text-sm\",\n          className\n        )}\n        data-slot=\"breadcrumb-list\"\n        ref={ref}\n        transition={{ duration: 0.18, ease: subtleEase }}\n        {...(props as MotionOlProps)}\n      >\n        <AnimatePresence initial={false} mode=\"popLayout\">\n          {children}\n        </AnimatePresence>\n      </motion.ol>\n    );\n  }\n);\nBreadcrumbList.displayName = \"BreadcrumbList\";\n\nconst BreadcrumbItem = forwardRef<HTMLLIElement, React.ComponentProps<\"li\">>(\n  ({ className, ...props }, ref) => {\n    return (\n      <motion.li\n        animate=\"animate\"\n        className={cn(\"inline-flex items-center gap-1\", className)}\n        data-slot=\"breadcrumb-item\"\n        exit=\"exit\"\n        initial=\"initial\"\n        layout=\"position\"\n        ref={ref}\n        transition={{ duration: 0.18, ease: subtleEase }}\n        variants={itemVariants}\n        {...(props as MotionLiProps)}\n      />\n    );\n  }\n);\nBreadcrumbItem.displayName = \"BreadcrumbItem\";\n\nconst BreadcrumbLink = forwardRef<HTMLAnchorElement, BreadcrumbLinkProps>(\n  ({ className, render, truncate = false, title, ...props }, ref) => {\n    return useRender({\n      defaultTagName: \"a\",\n      props: mergeProps<\"a\">(\n        {\n          className: cn(\n            breadcrumbLinkClassName,\n            truncate && breadcrumbTruncateClassName,\n            className\n          ),\n          ref,\n          title,\n        },\n        props\n      ),\n      render,\n      state: {\n        slot: \"breadcrumb-link\",\n      },\n    });\n  }\n);\nBreadcrumbLink.displayName = \"BreadcrumbLink\";\n\nconst BreadcrumbPage = forwardRef<HTMLSpanElement, BreadcrumbPageProps>(\n  ({ className, truncate = false, title, ...props }, ref) => {\n    return (\n      <span\n        aria-current=\"page\"\n        className={cn(\n          \"inline-flex min-w-0 items-center font-medium text-foreground\",\n          truncate && breadcrumbTruncateClassName,\n          className\n        )}\n        data-slot=\"breadcrumb-page\"\n        ref={ref}\n        title={title}\n        {...props}\n      />\n    );\n  }\n);\nBreadcrumbPage.displayName = \"BreadcrumbPage\";\n\nconst BreadcrumbSeparator = forwardRef<\n  HTMLLIElement,\n  React.ComponentProps<\"li\">\n>(({ children, className, ...props }, ref) => {\n  return (\n    <motion.li\n      animate=\"animate\"\n      aria-hidden=\"true\"\n      className={cn(\"[&>svg]:size-3.5\", className)}\n      data-slot=\"breadcrumb-separator\"\n      exit=\"exit\"\n      initial=\"initial\"\n      layout=\"position\"\n      ref={ref}\n      role=\"presentation\"\n      transition={{ duration: 0.18, ease: subtleEase }}\n      variants={separatorVariants}\n      {...(props as MotionLiProps)}\n    >\n      {children ?? <ChevronRightIcon className=\"cn-rtl-flip\" />}\n    </motion.li>\n  );\n});\nBreadcrumbSeparator.displayName = \"BreadcrumbSeparator\";\n\nfunction BreadcrumbEllipsis({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        \"flex size-5 items-center justify-center [&>svg]:size-4\",\n        className\n      )}\n      data-slot=\"breadcrumb-ellipsis\"\n      {...props}\n    >\n      <MoreHorizontalIcon />\n    </span>\n  );\n}\nBreadcrumbEllipsis.displayName = \"BreadcrumbEllipsis\";\n\nfunction BreadcrumbEllipsisMenuRow({\n  activeIndex,\n  highlightLayoutId,\n  index,\n  item,\n  itemRef,\n  onActivate,\n  onHighlight,\n}: {\n  activeIndex: number;\n  highlightLayoutId: string;\n  index: number;\n  item: BreadcrumbEllipsisMenuItem;\n  itemRef: (node: HTMLAnchorElement | HTMLDivElement | null) => void;\n  onActivate: (index: number) => void;\n  onHighlight: (index: number) => void;\n}) {\n  const isActive = index === activeIndex;\n  const label = getMenuItemLabel(item, index);\n  const content = (\n    <BreadcrumbMenuItemContent icon={item.icon} label={item.label} />\n  );\n\n  if (item.href) {\n    return (\n      <motion.a\n        className={menuItemClassName}\n        href={item.href}\n        onClick={() => onActivate(index)}\n        onMouseEnter={() => onHighlight(index)}\n        onPointerMove={() => onHighlight(index)}\n        ref={itemRef}\n        role=\"menuitem\"\n        tabIndex={isActive ? 0 : -1}\n        title={label}\n        whileTap={{ scale: 0.96 }}\n      >\n        {isActive ? (\n          <motion.span\n            className={menuItemHighlightClassName}\n            initial={false}\n            layoutId={highlightLayoutId}\n            transition={menuHighlightSpring}\n          />\n        ) : null}\n        {content}\n      </motion.a>\n    );\n  }\n\n  return (\n    <motion.div\n      aria-disabled=\"true\"\n      className={cn(menuItemClassName, \"text-muted-foreground\")}\n      onMouseEnter={() => onHighlight(index)}\n      onPointerMove={() => onHighlight(index)}\n      ref={itemRef}\n      role=\"menuitem\"\n      tabIndex={-1}\n      title={label}\n    >\n      {isActive ? (\n        <motion.span\n          className={menuItemHighlightClassName}\n          initial={false}\n          layoutId={highlightLayoutId}\n          transition={menuHighlightSpring}\n        />\n      ) : null}\n      {content}\n    </motion.div>\n  );\n}\n\nfunction getEllipsisMenuIndexForKey(\n  key: string,\n  activeIndex: number,\n  itemCount: number\n) {\n  switch (key) {\n    case \"ArrowDown\":\n      return (activeIndex + 1) % itemCount;\n    case \"ArrowUp\":\n      return (activeIndex - 1 + itemCount) % itemCount;\n    case \"Home\":\n      return 0;\n    case \"End\":\n      return itemCount - 1;\n    default:\n      return null;\n  }\n}\n\nfunction handleEllipsisMenuKeyDown({\n  activeIndex,\n  closeMenu,\n  event,\n  itemRefs,\n  items,\n  setActiveIndex,\n}: {\n  activeIndex: number;\n  closeMenu: () => void;\n  event: KeyboardEvent;\n  itemRefs: { current: Array<HTMLAnchorElement | HTMLDivElement | null> };\n  items: BreadcrumbEllipsisMenuItem[];\n  setActiveIndex: (index: number) => void;\n}) {\n  if (event.key === \"Escape\") {\n    event.preventDefault();\n    closeMenu();\n    return;\n  }\n\n  if (!items.length) {\n    return;\n  }\n\n  const nextIndex = getEllipsisMenuIndexForKey(\n    event.key,\n    activeIndex,\n    items.length\n  );\n\n  if (nextIndex !== null) {\n    event.preventDefault();\n    setActiveIndex(nextIndex);\n    return;\n  }\n\n  if (event.key !== \"Enter\" && event.key !== \" \") {\n    return;\n  }\n\n  const item = items[activeIndex];\n\n  if (!item?.href) {\n    return;\n  }\n\n  event.preventDefault();\n  itemRefs.current[activeIndex]?.click();\n}\n\nfunction BreadcrumbEllipsisMenu({\n  className,\n  items,\n  menuLabel = \"Show collapsed breadcrumb items\",\n}: BreadcrumbEllipsisMenuProps) {\n  const menuId = useId();\n  const highlightLayoutId = useId();\n  const rootRef = useRef<HTMLDivElement>(null);\n  const itemRefs = useRef<Array<HTMLAnchorElement | HTMLDivElement | null>>([]);\n  const [open, setOpen] = useState(false);\n  const [activeIndex, setActiveIndex] = useState(0);\n\n  const closeMenu = useCallback(() => {\n    setOpen(false);\n  }, []);\n\n  const activateItem = useCallback(\n    (index: number) => {\n      const item = items[index];\n\n      if (item?.href) {\n        closeMenu();\n      }\n    },\n    [closeMenu, items]\n  );\n\n  useEffect(() => {\n    if (!open) {\n      return;\n    }\n\n    setActiveIndex(0);\n    const frameId = window.requestAnimationFrame(() => {\n      itemRefs.current[0]?.focus();\n    });\n\n    return () => {\n      window.cancelAnimationFrame(frameId);\n    };\n  }, [open]);\n\n  useEffect(() => {\n    if (!open) {\n      return;\n    }\n\n    itemRefs.current[activeIndex]?.focus();\n  }, [activeIndex, open]);\n\n  useEffect(() => {\n    if (!open) {\n      return;\n    }\n\n    const handlePointerDown = (event: MouseEvent) => {\n      if (!rootRef.current?.contains(event.target as Node)) {\n        closeMenu();\n      }\n    };\n\n    const handleKeyDown = (event: KeyboardEvent) => {\n      handleEllipsisMenuKeyDown({\n        activeIndex,\n        closeMenu,\n        event,\n        itemRefs,\n        items,\n        setActiveIndex,\n      });\n    };\n\n    document.addEventListener(\"mousedown\", handlePointerDown);\n    document.addEventListener(\"keydown\", handleKeyDown);\n\n    return () => {\n      document.removeEventListener(\"mousedown\", handlePointerDown);\n      document.removeEventListener(\"keydown\", handleKeyDown);\n    };\n  }, [activeIndex, closeMenu, items, open]);\n\n  if (!items.length) {\n    return (\n      <span\n        className={cn(\n          \"inline-flex size-5 items-center justify-center text-muted-foreground\",\n          className\n        )}\n        data-slot=\"breadcrumb-ellipsis-empty\"\n      >\n        <BreadcrumbEllipsis />\n      </span>\n    );\n  }\n\n  return (\n    <div className={cn(\"relative inline-flex\", className)} ref={rootRef}>\n      <button\n        aria-controls={open ? menuId : undefined}\n        aria-expanded={open}\n        aria-haspopup=\"menu\"\n        aria-label={menuLabel}\n        className={cn(\n          breadcrumbLinkClassName,\n          \"flex size-5 items-center justify-center text-muted-foreground hover:text-foreground [&>svg]:size-4\"\n        )}\n        data-slot=\"breadcrumb-ellipsis-menu-trigger\"\n        onClick={() => setOpen((current) => !current)}\n        type=\"button\"\n      >\n        <MoreHorizontalIcon />\n      </button>\n      {open ? (\n        <div className={menuPanelClassName} id={menuId} role=\"menu\">\n          {items.map((item, index) => (\n            <div key={`breadcrumb-menu-${index}`} role=\"none\">\n              <BreadcrumbEllipsisMenuRow\n                activeIndex={activeIndex}\n                highlightLayoutId={highlightLayoutId}\n                index={index}\n                item={item}\n                itemRef={(node) => {\n                  itemRefs.current[index] = node;\n                }}\n                onActivate={activateItem}\n                onHighlight={setActiveIndex}\n              />\n            </div>\n          ))}\n        </div>\n      ) : null}\n    </div>\n  );\n}\nBreadcrumbEllipsisMenu.displayName = \"BreadcrumbEllipsisMenu\";\n\nfunction BreadcrumbJsonLd({\n  currentPath = \"/\",\n  items,\n  siteUrl,\n}: BreadcrumbJsonLdProps) {\n  const itemListElement = items\n    .map((item, index) => {\n      const isLast = index === items.length - 1;\n      const url = item.href\n        ? toAbsoluteBreadcrumbUrl(siteUrl, item.href)\n        : isLast\n          ? toAbsoluteBreadcrumbUrl(siteUrl, currentPath)\n          : null;\n\n      if (!url) {\n        return null;\n      }\n\n      return {\n        \"@type\": \"ListItem\",\n        item: url,\n        name: item.label,\n        position: index + 1,\n      } satisfies BreadcrumbListItem;\n    })\n    .filter((item): item is BreadcrumbListItem => item !== null);\n\n  if (itemListElement.length < 2) {\n    return null;\n  }\n\n  const jsonLd = {\n    \"@context\": \"https://schema.org\",\n    \"@type\": \"BreadcrumbList\",\n    itemListElement,\n  };\n\n  return (\n    <script\n      // biome-ignore lint/security/noDangerouslySetInnerHtml: JSON-LD script payload\n      dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}\n      type=\"application/ld+json\"\n    />\n  );\n}\nBreadcrumbJsonLd.displayName = \"BreadcrumbJsonLd\";\n\nfunction BreadcrumbSegment({\n  isCurrent,\n  item,\n  renderLink,\n  truncate,\n}: {\n  isCurrent: boolean;\n  item: BreadcrumbItemData;\n  renderLink?: BreadcrumbsProps[\"renderLink\"];\n  truncate: boolean;\n}) {\n  const title = getBreadcrumbItemTitle(item);\n  const content = (\n    <BreadcrumbSegmentContent\n      icon={item.icon}\n      label={item.label}\n      truncate={truncate}\n    />\n  );\n\n  if (isCurrent || !item.href) {\n    return <BreadcrumbPage title={title}>{content}</BreadcrumbPage>;\n  }\n\n  if (renderLink) {\n    return renderLink({\n      children: content,\n      className: \"inline-flex min-w-0 items-center\",\n      href: item.href,\n      title,\n      truncate,\n    });\n  }\n\n  return (\n    <BreadcrumbLink\n      className=\"inline-flex min-w-0 items-center\"\n      href={item.href}\n      title={title}\n    >\n      {content}\n    </BreadcrumbLink>\n  );\n}\n\nfunction buildBreadcrumbTrailNodes({\n  items,\n  maxItems,\n  renderLink,\n  separator,\n  truncate,\n}: {\n  items: BreadcrumbItemData[];\n  maxItems?: number;\n  renderLink?: BreadcrumbsProps[\"renderLink\"];\n  separator?: ReactNode;\n  truncate: boolean;\n}) {\n  const nodes: ReactNode[] = [];\n\n  const pushSeparator = (key: string) => {\n    nodes.push(\n      <BreadcrumbSeparator key={key}>{separator}</BreadcrumbSeparator>\n    );\n  };\n\n  const pushSegment = (\n    item: BreadcrumbItemData,\n    index: number,\n    isCurrent: boolean\n  ) => {\n    nodes.push(\n      <BreadcrumbItem key={getBreadcrumbItemKey(item, index)}>\n        <BreadcrumbSegment\n          isCurrent={isCurrent}\n          item={item}\n          renderLink={renderLink}\n          truncate={truncate}\n        />\n      </BreadcrumbItem>\n    );\n  };\n\n  const shouldCollapse =\n    typeof maxItems === \"number\" && maxItems >= 2 && items.length > maxItems;\n\n  if (!shouldCollapse) {\n    items.forEach((item, index) => {\n      if (index > 0) {\n        pushSeparator(`breadcrumb-separator-${index}`);\n      }\n\n      pushSegment(item, index, index === items.length - 1);\n    });\n\n    return nodes;\n  }\n\n  const { collapsed, leading, trailing } = collapseBreadcrumbItems(\n    items,\n    maxItems\n  );\n\n  leading.forEach((item, index) => {\n    if (index > 0) {\n      pushSeparator(`breadcrumb-leading-separator-${index}`);\n    }\n\n    pushSegment(item, index, false);\n  });\n\n  if (collapsed.length > 0) {\n    pushSeparator(\"breadcrumb-ellipsis-separator\");\n    nodes.push(\n      <BreadcrumbItem key=\"breadcrumb-ellipsis-menu\">\n        <BreadcrumbEllipsisMenu\n          items={collapsed.map((item) => ({\n            href: item.href,\n            icon: item.icon,\n            label: item.label,\n          }))}\n        />\n      </BreadcrumbItem>\n    );\n  }\n\n  trailing.forEach((item, index) => {\n    const itemIndex = items.length - trailing.length + index;\n\n    pushSeparator(`breadcrumb-trailing-separator-${itemIndex}`);\n    pushSegment(item, itemIndex, itemIndex === items.length - 1);\n  });\n\n  return nodes;\n}\n\nfunction Breadcrumbs({\n  ariaLabel,\n  className,\n  currentPath,\n  items,\n  listClassName,\n  maxItems,\n  renderLink,\n  separator,\n  siteUrl,\n  truncate = false,\n  ...navProps\n}: BreadcrumbsProps) {\n  const jsonLdItems = useMemo(\n    () =>\n      items.map((item, index) => ({\n        href: item.href,\n        label:\n          typeof item.label === \"string\" ? item.label : `Item ${index + 1}`,\n      })),\n    [items]\n  );\n\n  const trailNodes = useMemo(\n    () =>\n      buildBreadcrumbTrailNodes({\n        items,\n        maxItems,\n        renderLink,\n        separator,\n        truncate,\n      }),\n    [items, maxItems, renderLink, separator, truncate]\n  );\n\n  return (\n    <Breadcrumb ariaLabel={ariaLabel} className={className} {...navProps}>\n      {siteUrl ? (\n        <BreadcrumbJsonLd\n          currentPath={currentPath}\n          items={jsonLdItems}\n          siteUrl={siteUrl}\n        />\n      ) : null}\n      <BreadcrumbList className={listClassName}>{trailNodes}</BreadcrumbList>\n    </Breadcrumb>\n  );\n}\nBreadcrumbs.displayName = \"Breadcrumbs\";\n\nexport {\n  Breadcrumb,\n  BreadcrumbEllipsis,\n  BreadcrumbEllipsisMenu,\n  BreadcrumbItem,\n  BreadcrumbJsonLd,\n  BreadcrumbLink,\n  BreadcrumbList,\n  BreadcrumbPage,\n  Breadcrumbs,\n  BreadcrumbSeparator,\n};\nexport type {\n  BreadcrumbEllipsisMenuItem,\n  BreadcrumbItemData,\n  BreadcrumbJsonLdItem,\n  BreadcrumbJsonLdProps,\n  BreadcrumbsProps,\n};\n",
      "type": "registry:ui"
    }
  ],
  "title": "Breadcrumbs",
  "description": "Compound breadcrumb trail with Base UI render composition, subtle Motion transitions, and dedicated current-page and separator parts."
}
