{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "multi-button",
  "title": "Multi Button",
  "description": "A responsive action rail where Standard reserves width, Compact collapses, Original pairs ease-out redistribution with spring labels, and Gooey adds elastic metaballs.",
  "dependencies": ["framer-motion"],
  "registryDependencies": ["@godui/godui-theme"],
  "files": [
    {
      "path": "packages/components/src/multi-button/multi-button.tsx",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport * as React from \"react\";\n\nexport type MultiButtonVariant = \"default\" | \"outline\" | \"secondary\" | \"ghost\";\nexport type MultiButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport type MultiButtonItem = {\n  id: string;\n  icon: React.ElementType<{ className?: string }>;\n  label: React.ReactNode;\n  ariaLabel?: string;\n  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n  disabled?: boolean;\n  className?: string;\n  /** Alias kept for callers that want a per-action hover treatment. */\n  hoverClassName?: string;\n};\n\nexport type MultiButtonGroupProps = React.HTMLAttributes<HTMLDivElement> & {\n  children: React.ReactNode;\n};\n\ntype MultiButtonRootProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  | \"onAnimationStart\"\n  | \"onAnimationEnd\"\n  | \"onClick\"\n  | \"onDrag\"\n  | \"onDragStart\"\n  | \"onDragEnd\"\n>;\n\ntype MultiButtonSharedProps = MultiButtonRootProps & {\n  items: MultiButtonItem[];\n  /** Optional items used to reserve the same expanded label width as another row. */\n  syncWidthTo?: MultiButtonItem[];\n  /** Optional CSS color or token reference used for a faint active-item tint. */\n  highlightColor?: string;\n  /** Use the more expressive SVG metaball treatment instead of the default pill morph. */\n  gooey?: boolean;\n  variant?: MultiButtonVariant;\n  size?: MultiButtonSize;\n};\n\nexport type MultiButtonProps = MultiButtonSharedProps;\n\nexport type CompactMultiButtonProps = MultiButtonSharedProps & {\n  selectedId: string;\n  /** Keep the compact control icon-only when it opens. */\n  iconOnly?: boolean;\n  /** Optional independent icon shown while the compact control is at rest. */\n  restIcon?: MultiButtonItem[\"icon\"];\n  /** Accessible label for the optional rest trigger. Defaults to \"Open actions\". */\n  restAriaLabel?: string;\n};\n\ntype MultiButtonGroupContextValue = {\n  register: (id: string, labelWidth: number) => void;\n  unregister: (id: string) => void;\n  sharedLabelWidth: number;\n};\n\nconst MultiButtonGroupContext = React.createContext<\n  MultiButtonGroupContextValue | undefined\n>(undefined);\n\nconst SIZE_CONFIG: Record<\n  MultiButtonSize,\n  { cell: number; icon: string; minHeight: string; text: string }\n> = {\n  sm: { cell: 40, icon: \"size-3.5\", minHeight: \"h-10\", text: \"text-xs\" },\n  md: { cell: 40, icon: \"size-4\", minHeight: \"h-10\", text: \"text-sm\" },\n  lg: {\n    cell: 40,\n    icon: \"size-[18px]\",\n    minHeight: \"h-10\",\n    text: \"text-sm\",\n  },\n};\n\nconst VARIANT_CLASSES: Record<MultiButtonVariant, string> = {\n  default:\n    \"bg-primary text-primary-foreground shadow-sm ring-1 ring-primary/20\",\n  outline:\n    \"bg-background text-foreground shadow-xs ring-1 ring-inset ring-border/80\",\n  secondary: \"bg-secondary text-secondary-foreground shadow-xs\",\n  ghost: \"bg-muted/50 text-foreground ring-1 ring-border/60\",\n};\n\nconst ITEM_HOVER_CLASSES: Record<MultiButtonVariant, string> = {\n  default: \"hover:bg-primary-foreground/10 active:bg-primary-foreground/20\",\n  outline: \"hover:bg-accent hover:text-accent-foreground active:bg-accent/80\",\n  secondary:\n    \"hover:bg-secondary-foreground/10 active:bg-secondary-foreground/20\",\n  ghost: \"hover:bg-accent hover:text-accent-foreground active:bg-accent/80\",\n};\n\nconst DIVIDER_CLASSES: Record<MultiButtonVariant, string> = {\n  default: \"bg-primary-foreground/20\",\n  outline: \"bg-border/70\",\n  secondary: \"bg-secondary-foreground/15\",\n  ghost: \"bg-border/70\",\n};\n\nconst GOOEY_BLOB_FILLS: Record<MultiButtonVariant, string> = {\n  default: \"var(--primary)\",\n  outline: \"var(--background)\",\n  secondary: \"var(--secondary)\",\n  ghost: \"color-mix(in oklab, var(--muted) 50%, transparent)\",\n};\n\nconst GOOEY_TEXT_CLASSES: Record<MultiButtonVariant, string> = {\n  default: \"text-primary-foreground\",\n  outline: \"text-foreground\",\n  secondary: \"text-secondary-foreground\",\n  ghost: \"text-foreground\",\n};\n\nconst LABEL_SPRING = {\n  type: \"spring\",\n  stiffness: 520,\n  damping: 32,\n} as const;\n\nconst CONTEXTUAL_ICON_TRANSITION = {\n  type: \"spring\",\n  duration: 0.3,\n  bounce: 0,\n} as const;\n\nconst COMPACT_EXPAND_DURATION_MS = 200;\nconst GOOEY_BOUNCE_DURATION_MS = 300;\nconst GOOEY_BOUNCE_TRANSITION = {\n  duration: GOOEY_BOUNCE_DURATION_MS / 1000,\n  ease: [0.3, 0.7, 0.4, 1.5],\n} as const;\nconst GOOEY_CLOSE_TRANSITION = {\n  duration: COMPACT_EXPAND_DURATION_MS / 1000,\n  ease: [0.3, 0.7, 0.4, 1],\n} as const;\n\nconst GOOEY_RAIL_TRANSITION_CLASSES = {\n  expanded: \"[transition:width_300ms_cubic-bezier(0.3,0.7,0.4,1.5)]\",\n  collapsed: \"[transition:width_200ms_cubic-bezier(0.3,0.7,0.4,1)]\",\n} as const;\n\nconst GOOEY_ITEM_TRANSITION_CLASSES = {\n  expanded:\n    \"[transition:width_300ms_cubic-bezier(0.3,0.7,0.4,1.5),background-color_150ms_ease,color_150ms_ease,scale_120ms_ease]\",\n  collapsed:\n    \"[transition:width_200ms_cubic-bezier(0.3,0.7,0.4,1),background-color_150ms_ease,color_150ms_ease,scale_120ms_ease]\",\n} as const;\n\nconst GOOEY_ICON_TRANSITION_CLASSES = {\n  expanded: \"[transition:transform_300ms_cubic-bezier(0.3,0.7,0.4,1.5)]\",\n  collapsed: \"[transition:transform_200ms_cubic-bezier(0.3,0.7,0.4,1)]\",\n} as const;\n\nconst GOOEY_DIVIDER_TRANSITION_CLASSES = {\n  expanded:\n    \"[transition:width_300ms_cubic-bezier(0.3,0.7,0.4,1.5),opacity_150ms_ease]\",\n  collapsed:\n    \"[transition:width_200ms_cubic-bezier(0.3,0.7,0.4,1),opacity_150ms_ease]\",\n} as const;\n\nfunction gooeyPhase(expanded: boolean) {\n  return expanded ? \"expanded\" : \"collapsed\";\n}\n\nfunction gooeyMotionTransition(reduceMotion: boolean, expanded: boolean) {\n  if (reduceMotion) return { duration: 0 } as const;\n  return expanded ? GOOEY_BOUNCE_TRANSITION : GOOEY_CLOSE_TRANSITION;\n}\n\nfunction railClassName({\n  className,\n  compact,\n  expanded,\n  gooey,\n  minHeight,\n  variant,\n}: {\n  className?: string;\n  compact: boolean;\n  expanded: boolean;\n  gooey: boolean;\n  minHeight: string;\n  variant: MultiButtonVariant;\n}) {\n  const phase = gooeyPhase(expanded);\n  const surface = gooey\n    ? `overflow-visible ${GOOEY_TEXT_CLASSES[variant]}`\n    : `overflow-hidden ${VARIANT_CLASSES[variant]}`;\n  const widthTransition = compact\n    ? gooey\n      ? GOOEY_RAIL_TRANSITION_CLASSES[phase]\n      : \"[transition:width_200ms_ease-out]\"\n    : \"\";\n\n  return `relative inline-flex items-stretch rounded-full ${minHeight} ${surface} ${widthTransition} motion-reduce:[transition:none] ${className ?? \"\"}`;\n}\n\nfunction useMultiButtonInteractions() {\n  const reduceMotion = Boolean(useReducedMotion());\n  const filterId = React.useId().replace(/:/g, \"\");\n  const [hoveredId, setHoveredId] = React.useState<string | null>(null);\n  const [touchExpandedId, setTouchExpandedId] = React.useState<string | null>(\n    null,\n  );\n  const containerRef = React.useRef<HTMLDivElement>(null);\n\n  return {\n    containerRef,\n    filterId,\n    hoveredId,\n    reduceMotion,\n    setHoveredId,\n    setTouchExpandedId,\n    touchExpandedId,\n  };\n}\n\nfunction useOutsidePointerDown(\n  active: boolean,\n  containerRef: React.RefObject<HTMLDivElement | null>,\n  onOutside: () => void,\n) {\n  React.useEffect(() => {\n    if (!active) return;\n    // The rail may be portaled into a preview iframe, whose document is\n    // different from the page document running this component.\n    const ownerDocument = containerRef.current?.ownerDocument ?? document;\n    const handlePointerDown = (event: PointerEvent) => {\n      if (!containerRef.current?.contains(event.target as Node)) onOutside();\n    };\n    ownerDocument.addEventListener(\"pointerdown\", handlePointerDown);\n    return () =>\n      ownerDocument.removeEventListener(\"pointerdown\", handlePointerDown);\n  }, [active, containerRef, onOutside]);\n}\n\nfunction labelText(label: React.ReactNode) {\n  return typeof label === \"string\" || typeof label === \"number\"\n    ? label.toString()\n    : \"\";\n}\n\nfunction estimateLabelWidth(label: React.ReactNode, size: MultiButtonSize) {\n  const textWidth = labelText(label).length * (size === \"sm\" ? 6.5 : 7.5);\n  return Math.ceil(textWidth) + (size === \"lg\" ? 10 : size === \"md\" ? 8 : 6);\n}\n\nfunction maxLabelWidth(items: MultiButtonItem[], size: MultiButtonSize) {\n  return items.reduce(\n    (max, item) => Math.max(max, estimateLabelWidth(item.label, size)),\n    0,\n  );\n}\n\nfunction estimatedLabelWidths(items: MultiButtonItem[], size: MultiButtonSize) {\n  return Object.fromEntries(\n    items.map((item) => [item.id, estimateLabelWidth(item.label, size)]),\n  );\n}\n\nfunction equalLabelWidths(\n  current: Record<string, number>,\n  next: Record<string, number>,\n) {\n  const currentIds = Object.keys(current);\n  const nextIds = Object.keys(next);\n  return (\n    currentIds.length === nextIds.length &&\n    nextIds.every((id) => current[id] === next[id])\n  );\n}\n\nfunction measuredLabelWidth(\n  element: HTMLElement | undefined,\n  fallback: number,\n) {\n  if (!element) return fallback;\n  const style = getComputedStyle(element);\n  const marginLeft = Number.parseFloat(style.marginLeft) || 0;\n  const marginRight = Number.parseFloat(style.marginRight) || 0;\n  const measured = Math.ceil(\n    element.getBoundingClientRect().width + marginLeft + marginRight,\n  );\n  return measured > 0 ? measured : fallback;\n}\n\nfunction itemAriaLabel(item: MultiButtonItem) {\n  return item.ariaLabel ?? (labelText(item.label) || item.id);\n}\n\nfunction useLabelWidths(\n  items: MultiButtonItem[],\n  syncWidthTo: MultiButtonItem[] | undefined,\n  size: MultiButtonSize,\n) {\n  const group = React.useContext(MultiButtonGroupContext);\n  const instanceId = React.useId();\n  const reserveItems = syncWidthTo ?? items;\n  const itemEstimates = React.useMemo(\n    () => estimatedLabelWidths(items, size),\n    [items, size],\n  );\n  const estimatedReserveWidth = React.useMemo(\n    () => maxLabelWidth(reserveItems, size),\n    [reserveItems, size],\n  );\n  const [labelWidths, setLabelWidths] =\n    React.useState<Record<string, number>>(itemEstimates);\n  const [ownReservedWidth, setOwnReservedWidth] = React.useState(\n    estimatedReserveWidth,\n  );\n  const measurementRef = React.useRef<HTMLDivElement>(null);\n  const register = group?.register;\n  const unregister = group?.unregister;\n\n  const measureLabels = React.useCallback(() => {\n    const itemLabels = measurementRef.current?.querySelectorAll<HTMLElement>(\n      '[data-slot=\"multi-button-item-label-measure\"]',\n    );\n    const nextLabelWidths = Object.fromEntries(\n      items.map((item, index) => [\n        item.id,\n        measuredLabelWidth(itemLabels?.[index], itemEstimates[item.id] ?? 0),\n      ]),\n    );\n    setLabelWidths((current) =>\n      equalLabelWidths(current, nextLabelWidths) ? current : nextLabelWidths,\n    );\n\n    const reserveLabels = measurementRef.current?.querySelectorAll<HTMLElement>(\n      '[data-slot=\"multi-button-reserve-label-measure\"]',\n    );\n    const measuredReserveWidth = reserveItems.reduce(\n      (largest, item, index) =>\n        Math.max(\n          largest,\n          measuredLabelWidth(\n            reserveLabels?.[index],\n            estimateLabelWidth(item.label, size),\n          ),\n        ),\n      0,\n    );\n    setOwnReservedWidth((current) =>\n      current === measuredReserveWidth ? current : measuredReserveWidth,\n    );\n  }, [itemEstimates, items, reserveItems, size]);\n\n  React.useLayoutEffect(() => {\n    const measurementNode = measurementRef.current;\n    if (!measurementNode) return;\n\n    let active = true;\n    const measure = () => {\n      if (active) measureLabels();\n    };\n    measure();\n\n    const observer =\n      typeof ResizeObserver === \"undefined\"\n        ? undefined\n        : new ResizeObserver(measure);\n    const labels = measurementNode.querySelectorAll<HTMLElement>(\n      '[data-slot$=\"-label-measure\"]',\n    );\n    labels.forEach((label) => {\n      observer?.observe(label);\n    });\n\n    void document.fonts?.ready.then(measure);\n\n    return () => {\n      active = false;\n      observer?.disconnect();\n    };\n  }, [measureLabels]);\n\n  React.useLayoutEffect(() => {\n    if (!register || !unregister) return;\n    register(instanceId, ownReservedWidth);\n    return () => unregister(instanceId);\n  }, [instanceId, ownReservedWidth, register, unregister]);\n\n  return {\n    labelWidths,\n    reservedLabelWidth: group\n      ? Math.max(group.sharedLabelWidth, ownReservedWidth)\n      : ownReservedWidth,\n    measurementRef,\n    reserveItems,\n  };\n}\n\nfunction MultiButtonLabelMeasurement({\n  items,\n  reserveItems,\n  measurementRef,\n  size,\n}: {\n  items: MultiButtonItem[];\n  reserveItems: MultiButtonItem[];\n  measurementRef: React.RefObject<HTMLDivElement | null>;\n  size: MultiButtonSize;\n}) {\n  return (\n    <div\n      ref={measurementRef}\n      aria-hidden=\"true\"\n      className=\"pointer-events-none invisible absolute top-0 left-0 flex h-0 w-0 overflow-hidden\"\n    >\n      {items.map((item) => (\n        <span\n          key={`item-${item.id}`}\n          data-slot=\"multi-button-item-label-measure\"\n          className={`-ml-1 shrink-0 whitespace-nowrap pr-2 font-medium leading-none ${SIZE_CONFIG[size].text}`}\n        >\n          {item.label}\n        </span>\n      ))}\n      {reserveItems.map((item) => (\n        <span\n          key={`reserve-${item.id}`}\n          data-slot=\"multi-button-reserve-label-measure\"\n          className={`-ml-1 shrink-0 whitespace-nowrap pr-2 font-medium leading-none ${SIZE_CONFIG[size].text}`}\n        >\n          {item.label}\n        </span>\n      ))}\n    </div>\n  );\n}\n\nfunction actionWidth(\n  item: MultiButtonItem,\n  activeId: string | null,\n  itemCount: number,\n  cellWidth: number,\n  labelWidths: Record<string, number>,\n  reservedLabelWidth: number,\n) {\n  if (activeId === null) {\n    return itemCount > 0\n      ? cellWidth + reservedLabelWidth / itemCount\n      : cellWidth;\n  }\n\n  const activeLabelWidth = labelWidths[activeId] ?? reservedLabelWidth;\n  if (item.id === activeId) return cellWidth + activeLabelWidth;\n\n  const remainingLabelSpace = Math.max(\n    0,\n    reservedLabelWidth - activeLabelWidth,\n  );\n  return itemCount > 1\n    ? cellWidth + remainingLabelSpace / (itemCount - 1)\n    : cellWidth;\n}\n\ntype MultiButtonBlobGeometry = {\n  item: MultiButtonItem;\n  width: number;\n  x: number;\n};\n\nfunction useMultiButtonLayout({\n  activeId,\n  items,\n  reserveLabelSpace = true,\n  size,\n  syncWidthTo,\n}: {\n  activeId: string | null;\n  items: MultiButtonItem[];\n  reserveLabelSpace?: boolean;\n  size: MultiButtonSize;\n  syncWidthTo?: MultiButtonItem[];\n}) {\n  const cfg = SIZE_CONFIG[size];\n  const { labelWidths, reservedLabelWidth, measurementRef, reserveItems } =\n    useLabelWidths(items, syncWidthTo, size);\n  const effectiveLabelWidth = reserveLabelSpace ? reservedLabelWidth : 0;\n  const dividerWidth = Math.max(0, items.length - 1);\n  const expandedWidth =\n    items.length > 0\n      ? items.length * cfg.cell + effectiveLabelWidth + dividerWidth\n      : cfg.cell;\n  const itemWidths = items.map((item) =>\n    actionWidth(\n      item,\n      activeId,\n      items.length,\n      cfg.cell,\n      labelWidths,\n      effectiveLabelWidth,\n    ),\n  );\n  let blobX = 0;\n  const blobGeometries = items.map((item, index) => {\n    const geometry = {\n      item,\n      width: itemWidths[index] ?? cfg.cell,\n      x: blobX,\n    };\n    blobX += geometry.width + (index < items.length - 1 ? 1 : 0);\n    return geometry;\n  });\n\n  return {\n    blobGeometries,\n    cfg,\n    expandedWidth,\n    itemWidths,\n    measurementRef,\n    reserveItems,\n  };\n}\n\nfunction MultiButtonBlobLayer({\n  activeId,\n  cellWidth,\n  expanded,\n  filterId,\n  geometries,\n  highlightColor,\n  reduceMotion,\n  selectedId,\n  variant,\n}: {\n  activeId: string | null;\n  cellWidth: number;\n  expanded: boolean;\n  filterId: string;\n  geometries: MultiButtonBlobGeometry[];\n  highlightColor?: string;\n  reduceMotion: boolean;\n  selectedId: string | undefined;\n  variant: MultiButtonVariant;\n}) {\n  const baseFill = GOOEY_BLOB_FILLS[variant];\n\n  return (\n    <svg\n      data-slot=\"multi-button-blob\"\n      aria-hidden=\"true\"\n      className=\"pointer-events-none absolute top-0 left-0 z-base drop-shadow-sm\"\n      style={{ overflow: \"visible\" }}\n      width=\"100%\"\n      height={cellWidth}\n    >\n      <defs>\n        <filter\n          id={filterId}\n          x=\"-100%\"\n          y=\"-400%\"\n          width=\"300%\"\n          height=\"900%\"\n          colorInterpolationFilters=\"sRGB\"\n        >\n          <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"6\" result=\"blur\" />\n          <feColorMatrix\n            in=\"blur\"\n            mode=\"matrix\"\n            values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -10\"\n          />\n        </filter>\n      </defs>\n      <g filter={reduceMotion ? undefined : `url(#${filterId})`}>\n        {geometries.map(({ item, width, x }) => {\n          const selected = item.id === selectedId;\n          const active = item.id === activeId;\n          const fill =\n            active && highlightColor\n              ? `color-mix(in oklch, ${baseFill} 86%, ${highlightColor})`\n              : baseFill;\n\n          return (\n            <motion.rect\n              key={item.id}\n              x={0}\n              y={0}\n              height={cellWidth}\n              rx={cellWidth / 2}\n              fill={fill}\n              style={{\n                transformBox: \"fill-box\",\n                transformOrigin: \"center\",\n              }}\n              initial={false}\n              animate={\n                expanded\n                  ? { x, width, scale: 1, opacity: 1 }\n                  : selected\n                    ? { x: 0, width: cellWidth, scale: 1, opacity: 1 }\n                    : {\n                        x: 0,\n                        width: cellWidth,\n                        scale: 0.2,\n                        opacity: 0,\n                      }\n              }\n              transition={gooeyMotionTransition(reduceMotion, expanded)}\n            />\n          );\n        })}\n      </g>\n    </svg>\n  );\n}\n\nfunction labelMotion(reduceMotion: boolean) {\n  return {\n    initial: reduceMotion\n      ? false\n      : { opacity: 0, scale: 0.25, filter: \"blur(4px)\" },\n    animate: { opacity: 1, scale: 1, filter: \"blur(0px)\" },\n    exit: reduceMotion\n      ? undefined\n      : { opacity: 0, scale: 0.25, filter: \"blur(4px)\" },\n    transition: reduceMotion\n      ? { duration: 0 }\n      : {\n          opacity: LABEL_SPRING,\n          scale: LABEL_SPRING,\n          filter: { duration: 0.15, ease: \"easeOut\" },\n        },\n  } as const;\n}\n\ntype MultiButtonLabelProps = {\n  item: MultiButtonItem;\n  reduceMotion: boolean;\n  textClass: string;\n};\n\nconst MultiButtonLabel = React.forwardRef<\n  HTMLSpanElement,\n  MultiButtonLabelProps\n>(({ item, reduceMotion, textClass }, ref) => (\n  <motion.span\n    ref={ref}\n    {...labelMotion(reduceMotion)}\n    className={`relative z-raised -ml-1 shrink-0 whitespace-nowrap pr-2 font-medium leading-none ${textClass}`}\n  >\n    {item.label}\n  </motion.span>\n));\nMultiButtonLabel.displayName = \"MultiButtonLabel\";\n\ntype MultiButtonItemButtonProps = {\n  item: MultiButtonItem;\n  active: boolean;\n  accessible?: boolean;\n  ariaLabel?: string;\n  disclosureExpanded?: boolean;\n  disabled: boolean;\n  highlighted: boolean;\n  gooey?: boolean;\n  gooeyExpanded?: boolean;\n  transparent?: boolean;\n  size: MultiButtonSize;\n  variant: MultiButtonVariant;\n  width: number;\n  iconOffset: number;\n  reduceMotion: boolean;\n  restIcon?: MultiButtonItem[\"icon\"];\n  showRestIcon?: boolean;\n  visible?: boolean;\n  onTouchAction: (\n    event: React.PointerEvent<HTMLButtonElement>,\n    id: string,\n  ) => void;\n  onHover: (id: string | null) => void;\n  onAction?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n};\n\ntype MultiButtonItemIconProps = {\n  actionIcon: MultiButtonItem[\"icon\"];\n  gooey: boolean;\n  gooeyExpanded: boolean;\n  iconClassName: string;\n  iconOffset: number;\n  laneWidth: number;\n  reduceMotion: boolean;\n  restIcon?: MultiButtonItem[\"icon\"];\n  showRestIcon: boolean;\n};\n\nfunction MultiButtonItemIcon({\n  actionIcon: ActionIcon,\n  gooey,\n  gooeyExpanded,\n  iconClassName,\n  iconOffset,\n  laneWidth,\n  reduceMotion,\n  restIcon: RestIcon,\n  showRestIcon,\n}: MultiButtonItemIconProps) {\n  const phase = gooeyPhase(gooeyExpanded);\n  const iconTransition = reduceMotion\n    ? ({ duration: 0 } as const)\n    : CONTEXTUAL_ICON_TRANSITION;\n  const visibleState = { opacity: 1, scale: 1, filter: \"blur(0px)\" };\n  const hiddenState = { opacity: 0, scale: 0.25, filter: \"blur(4px)\" };\n\n  return (\n    <span\n      data-slot=\"multi-button-icon\"\n      className={`relative z-raised flex h-full shrink-0 items-center justify-center motion-reduce:[transition:none] ${gooey ? GOOEY_ICON_TRANSITION_CLASSES[phase] : \"[transition:transform_200ms_ease-out]\"}`}\n      style={{\n        width: `${laneWidth}px`,\n        transform: `translateX(${iconOffset}px)`,\n      }}\n      aria-hidden=\"true\"\n    >\n      {RestIcon ? (\n        <>\n          <motion.span\n            data-slot=\"multi-button-action-icon\"\n            className=\"absolute inset-0 flex items-center justify-center\"\n            initial={false}\n            animate={showRestIcon ? hiddenState : visibleState}\n            transition={iconTransition}\n          >\n            <ActionIcon className={iconClassName} />\n          </motion.span>\n          <motion.span\n            data-slot=\"multi-button-rest-icon\"\n            className=\"absolute inset-0 flex items-center justify-center\"\n            initial={false}\n            animate={showRestIcon ? visibleState : hiddenState}\n            transition={iconTransition}\n          >\n            <RestIcon className={iconClassName} />\n          </motion.span>\n        </>\n      ) : (\n        <ActionIcon className={iconClassName} />\n      )}\n    </span>\n  );\n}\n\nfunction MultiButtonItemButton({\n  item,\n  active,\n  accessible = true,\n  ariaLabel,\n  disclosureExpanded,\n  disabled,\n  highlighted,\n  gooey = false,\n  gooeyExpanded = true,\n  transparent = false,\n  size,\n  variant,\n  width,\n  iconOffset,\n  reduceMotion,\n  restIcon,\n  showRestIcon = false,\n  visible = true,\n  onTouchAction,\n  onHover,\n  onAction,\n}: MultiButtonItemButtonProps) {\n  const cfg = SIZE_CONFIG[size];\n  const phase = gooeyPhase(gooeyExpanded);\n\n  return (\n    <motion.button\n      type=\"button\"\n      data-state={active ? \"open\" : \"closed\"}\n      data-multi-button-item-id={item.id}\n      aria-label={ariaLabel ?? itemAriaLabel(item)}\n      aria-expanded={disclosureExpanded}\n      aria-hidden={accessible ? undefined : true}\n      tabIndex={accessible ? undefined : -1}\n      disabled={disabled}\n      onClick={(event) => {\n        if (item.disabled) return;\n        item.onClick?.(event);\n        onAction?.(event);\n      }}\n      onPointerDown={(event) => onTouchAction(event, item.id)}\n      onMouseEnter={() => onHover(item.id)}\n      onMouseLeave={() => onHover(null)}\n      onFocus={() => onHover(item.id)}\n      onBlur={() => onHover(null)}\n      initial={false}\n      animate={\n        gooey\n          ? {\n              opacity: visible ? 1 : 0,\n              scale: visible ? 1 : 0.4,\n            }\n          : undefined\n      }\n      transition={\n        gooey ? gooeyMotionTransition(reduceMotion, gooeyExpanded) : undefined\n      }\n      style={{ width: `${width}px` }}\n      className={`relative isolate flex shrink-0 cursor-pointer items-center justify-start overflow-hidden ${cfg.minHeight} focus:outline-none focus-visible:z-raised focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset disabled:cursor-not-allowed disabled:opacity-40 motion-reduce:[transition:none] ${accessible ? \"\" : \"pointer-events-none\"} ${gooey ? GOOEY_ITEM_TRANSITION_CLASSES[phase] : \"[transition:width_200ms_ease-out,background-color_150ms_ease,color_150ms_ease,scale_120ms_ease]\"} ${reduceMotion ? \"\" : \"active:scale-[0.96]\"} ${transparent ? \"\" : ITEM_HOVER_CLASSES[variant]} ${item.hoverClassName ?? \"\"} ${item.className ?? \"\"}`}\n    >\n      <span\n        data-slot=\"multi-button-highlight\"\n        aria-hidden=\"true\"\n        className={`pointer-events-none absolute inset-0 z-base bg-[var(--multi-button-highlight)] [transition:opacity_150ms_ease] motion-reduce:[transition:none] ${highlighted ? \"opacity-[0.14]\" : \"opacity-0\"}`}\n      />\n      <MultiButtonItemIcon\n        actionIcon={item.icon}\n        gooey={gooey}\n        gooeyExpanded={gooeyExpanded}\n        iconClassName={cfg.icon}\n        iconOffset={iconOffset}\n        laneWidth={cfg.cell}\n        reduceMotion={reduceMotion}\n        restIcon={restIcon}\n        showRestIcon={showRestIcon}\n      />\n      <AnimatePresence initial={false} mode=\"popLayout\">\n        {active && (\n          <MultiButtonLabel\n            key={item.id}\n            item={item}\n            reduceMotion={reduceMotion}\n            textClass={cfg.text}\n          />\n        )}\n      </AnimatePresence>\n    </motion.button>\n  );\n}\n\nfunction MultiButtonDivider({\n  activeId,\n  compact,\n  expanded,\n  gooey,\n  variant,\n}: {\n  activeId: string | null;\n  compact: boolean;\n  expanded: boolean;\n  gooey: boolean;\n  variant: MultiButtonVariant;\n}) {\n  if (!compact) {\n    return (\n      <span\n        aria-hidden=\"true\"\n        className={`my-2 w-px shrink-0 transition-opacity duration-150 motion-reduce:[transition:none] ${DIVIDER_CLASSES[variant]} ${activeId ? \"opacity-0\" : \"opacity-100\"}`}\n      />\n    );\n  }\n\n  const phase = gooeyPhase(expanded);\n\n  return (\n    <span\n      aria-hidden=\"true\"\n      style={{\n        width: `${expanded ? 1 : 0}px`,\n        opacity: expanded && activeId === null ? 1 : 0,\n      }}\n      className={`my-2 shrink-0 motion-reduce:[transition:none] ${gooey ? GOOEY_DIVIDER_TRANSITION_CLASSES[phase] : \"[transition:width_200ms_ease-out,opacity_200ms_ease-out]\"} ${DIVIDER_CLASSES[variant]}`}\n    />\n  );\n}\n\ntype MultiButtonItemsProps = {\n  activeId: string | null;\n  compact: boolean;\n  expanded: boolean;\n  gooey: boolean;\n  highlightColor?: string;\n  itemWidths: number[];\n  interactionReady: boolean;\n  items: MultiButtonItem[];\n  onAction?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n  onHover: (id: string | null) => void;\n  onTouchAction: (\n    event: React.PointerEvent<HTMLButtonElement>,\n    id: string,\n  ) => void;\n  reduceMotion: boolean;\n  restAriaLabel?: string;\n  restIcon?: MultiButtonItem[\"icon\"];\n  selectedId?: string;\n  size: MultiButtonSize;\n  variant: MultiButtonVariant;\n};\n\nfunction MultiButtonItems({\n  activeId,\n  compact,\n  expanded,\n  gooey,\n  highlightColor,\n  itemWidths,\n  interactionReady,\n  items,\n  onAction,\n  onHover,\n  onTouchAction,\n  reduceMotion,\n  restAriaLabel,\n  restIcon,\n  selectedId,\n  size,\n  variant,\n}: MultiButtonItemsProps) {\n  const cfg = SIZE_CONFIG[size];\n\n  return items.map((item, index) => {\n    const active = activeId === item.id;\n    const selected = selectedId === item.id;\n    const collapsedSelectedTrigger = compact && selected && !expanded;\n    const width =\n      compact && !expanded\n        ? selected\n          ? cfg.cell\n          : 0\n        : (itemWidths[index] ?? cfg.cell);\n    const iconOffset =\n      compact && !expanded\n        ? 0\n        : active\n          ? 0\n          : Math.max(0, (width - cfg.cell) / 2);\n\n    return (\n      <React.Fragment key={item.id}>\n        {index > 0 && (\n          <MultiButtonDivider\n            activeId={activeId}\n            compact={compact}\n            expanded={expanded}\n            gooey={gooey}\n            variant={variant}\n          />\n        )}\n        <MultiButtonItemButton\n          item={item}\n          active={active}\n          accessible={!compact || interactionReady || selected}\n          ariaLabel={\n            collapsedSelectedTrigger\n              ? (restAriaLabel ?? (restIcon ? \"Open actions\" : undefined))\n              : undefined\n          }\n          disclosureExpanded={compact && selected ? expanded : undefined}\n          disabled={Boolean(item.disabled && !collapsedSelectedTrigger)}\n          highlighted={!gooey && Boolean(highlightColor) && active}\n          gooey={gooey}\n          gooeyExpanded={expanded}\n          transparent={gooey}\n          size={size}\n          variant={variant}\n          width={width}\n          iconOffset={iconOffset}\n          reduceMotion={reduceMotion}\n          restIcon={compact && selected ? restIcon : undefined}\n          showRestIcon={compact && selected && Boolean(restIcon) && !expanded}\n          visible={!compact || expanded || selected}\n          onTouchAction={onTouchAction}\n          onHover={onHover}\n          onAction={onAction}\n        />\n      </React.Fragment>\n    );\n  });\n}\n\ntype MultiButtonRailContentProps = MultiButtonItemsProps & {\n  blobGeometries: MultiButtonBlobGeometry[];\n  cellWidth: number;\n  expandedWidth: number;\n  filterId: string;\n  measurementRef: React.RefObject<HTMLDivElement | null>;\n  reserveItems: MultiButtonItem[];\n};\n\nfunction MultiButtonRailContent({\n  activeId,\n  blobGeometries,\n  cellWidth,\n  compact,\n  expanded,\n  filterId,\n  gooey,\n  highlightColor,\n  itemWidths,\n  interactionReady,\n  items,\n  measurementRef,\n  onAction,\n  onHover,\n  onTouchAction,\n  reduceMotion,\n  restAriaLabel,\n  restIcon,\n  reserveItems,\n  selectedId,\n  size,\n  variant,\n}: MultiButtonRailContentProps) {\n  return (\n    <>\n      {gooey && (\n        <MultiButtonBlobLayer\n          activeId={activeId}\n          cellWidth={cellWidth}\n          expanded={expanded}\n          filterId={filterId}\n          geometries={blobGeometries}\n          highlightColor={highlightColor}\n          reduceMotion={reduceMotion}\n          selectedId={selectedId}\n          variant={variant}\n        />\n      )}\n      <MultiButtonLabelMeasurement\n        items={items}\n        reserveItems={reserveItems}\n        measurementRef={measurementRef}\n        size={size}\n      />\n      <MultiButtonItems\n        activeId={activeId}\n        compact={compact}\n        expanded={expanded}\n        gooey={gooey}\n        highlightColor={highlightColor}\n        itemWidths={itemWidths}\n        interactionReady={interactionReady}\n        items={items}\n        onAction={onAction}\n        onHover={onHover}\n        onTouchAction={onTouchAction}\n        reduceMotion={reduceMotion}\n        restAriaLabel={restAriaLabel}\n        restIcon={restIcon}\n        selectedId={selectedId}\n        size={size}\n        variant={variant}\n      />\n    </>\n  );\n}\n\ntype MultiButtonRailProps = MultiButtonRailContentProps & {\n  className?: string;\n  containerRef: React.RefObject<HTMLDivElement | null>;\n  containerWidth: number;\n  forwardedRef: React.ForwardedRef<HTMLDivElement>;\n  rootProps: MultiButtonRootProps;\n  slot: \"multi-button\" | \"compact-multi-button\";\n  style?: React.CSSProperties;\n};\n\nfunction MultiButtonRail({\n  className,\n  compact,\n  containerRef,\n  containerWidth,\n  expanded,\n  forwardedRef,\n  gooey,\n  highlightColor,\n  rootProps,\n  size,\n  slot,\n  style,\n  variant,\n  ...contentProps\n}: MultiButtonRailProps) {\n  const cfg = SIZE_CONFIG[size];\n\n  return (\n    <motion.div\n      ref={(node) => {\n        containerRef.current = node;\n        if (typeof forwardedRef === \"function\") forwardedRef(node);\n        else if (forwardedRef) forwardedRef.current = node;\n      }}\n      data-slot={slot}\n      role=\"group\"\n      aria-expanded={compact ? expanded : undefined}\n      style={\n        {\n          ...style,\n          width: `${containerWidth}px`,\n          \"--multi-button-highlight\": highlightColor,\n        } as React.CSSProperties\n      }\n      className={railClassName({\n        className,\n        compact,\n        expanded,\n        gooey,\n        minHeight: cfg.minHeight,\n        variant,\n      })}\n      {...rootProps}\n    >\n      <MultiButtonRailContent\n        {...contentProps}\n        compact={compact}\n        expanded={expanded}\n        gooey={gooey}\n        highlightColor={highlightColor}\n        size={size}\n        variant={variant}\n      />\n    </motion.div>\n  );\n}\n\nconst MultiButtonGroup = React.forwardRef<\n  HTMLDivElement,\n  MultiButtonGroupProps\n>(({ children, className, ...props }, ref) => {\n  const [registry, setRegistry] = React.useState<Record<string, number>>({});\n\n  const register = React.useCallback((id: string, width: number) => {\n    setRegistry((current) =>\n      current[id] === width ? current : { ...current, [id]: width },\n    );\n  }, []);\n\n  const unregister = React.useCallback((id: string) => {\n    setRegistry((current) => {\n      if (!(id in current)) return current;\n      const next = { ...current };\n      delete next[id];\n      return next;\n    });\n  }, []);\n\n  const sharedLabelWidth = Math.max(0, ...Object.values(registry));\n  const context = React.useMemo(\n    () => ({ register, unregister, sharedLabelWidth }),\n    [register, unregister, sharedLabelWidth],\n  );\n\n  return (\n    <MultiButtonGroupContext.Provider value={context}>\n      <div\n        ref={ref}\n        data-slot=\"multi-button-group\"\n        className={`contents ${className ?? \"\"}`}\n        {...props}\n      >\n        {children}\n      </div>\n    </MultiButtonGroupContext.Provider>\n  );\n});\nMultiButtonGroup.displayName = \"MultiButtonGroup\";\n\nconst MultiButton = React.forwardRef<HTMLDivElement, MultiButtonProps>(\n  (\n    {\n      items,\n      syncWidthTo,\n      highlightColor,\n      gooey = false,\n      variant = \"default\",\n      size = \"md\",\n      className,\n      style,\n      ...props\n    },\n    ref,\n  ) => {\n    const {\n      containerRef,\n      filterId,\n      hoveredId,\n      reduceMotion,\n      setHoveredId,\n      setTouchExpandedId,\n      touchExpandedId,\n    } = useMultiButtonInteractions();\n    const activeId = hoveredId ?? touchExpandedId;\n    const {\n      blobGeometries,\n      cfg,\n      expandedWidth,\n      itemWidths,\n      measurementRef,\n      reserveItems,\n    } = useMultiButtonLayout({ activeId, items, size, syncWidthTo });\n\n    const collapseTouchAction = React.useCallback(\n      () => setTouchExpandedId(null),\n      [setTouchExpandedId],\n    );\n    useOutsidePointerDown(\n      Boolean(touchExpandedId),\n      containerRef,\n      collapseTouchAction,\n    );\n\n    const onTouchAction = (\n      event: React.PointerEvent<HTMLButtonElement>,\n      id: string,\n    ) => {\n      if (event.pointerType !== \"touch\") return;\n      if (touchExpandedId !== id) {\n        setTouchExpandedId(id);\n      }\n    };\n\n    return (\n      <MultiButtonRail\n        activeId={activeId}\n        blobGeometries={blobGeometries}\n        cellWidth={cfg.cell}\n        className={className}\n        compact={false}\n        containerRef={containerRef}\n        containerWidth={expandedWidth}\n        expanded\n        expandedWidth={expandedWidth}\n        filterId={filterId}\n        forwardedRef={ref}\n        gooey={gooey}\n        highlightColor={highlightColor}\n        itemWidths={itemWidths}\n        interactionReady\n        items={items}\n        measurementRef={measurementRef}\n        onHover={setHoveredId}\n        onTouchAction={onTouchAction}\n        reduceMotion={reduceMotion}\n        reserveItems={reserveItems}\n        rootProps={props}\n        size={size}\n        slot=\"multi-button\"\n        style={style}\n        variant={variant}\n      />\n    );\n  },\n);\nMultiButton.displayName = \"MultiButton\";\n\nconst CompactMultiButton = React.forwardRef<\n  HTMLDivElement,\n  CompactMultiButtonProps\n>(\n  (\n    {\n      items,\n      selectedId,\n      iconOnly = false,\n      restIcon,\n      restAriaLabel,\n      syncWidthTo,\n      highlightColor,\n      gooey = false,\n      variant = \"default\",\n      size = \"md\",\n      className,\n      style,\n      onMouseEnter: onMouseEnterProp,\n      onMouseLeave: onMouseLeaveProp,\n      onFocusCapture: onFocusCaptureProp,\n      onBlurCapture: onBlurCaptureProp,\n      onTransitionEnd: onTransitionEndProp,\n      ...rootProps\n    },\n    ref,\n  ) => {\n    const {\n      containerRef,\n      filterId,\n      hoveredId,\n      reduceMotion,\n      setHoveredId,\n      setTouchExpandedId,\n      touchExpandedId,\n    } = useMultiButtonInteractions();\n    const [mouseExpanded, setMouseExpanded] = React.useState(false);\n    const [touchExpanded, setTouchExpanded] = React.useState(false);\n    const [focusExpanded, setFocusExpanded] = React.useState(false);\n    const [compactRailReady, setCompactRailReady] = React.useState(false);\n    const focusRestoreFrameRef = React.useRef<number | null>(null);\n    const suppressFocusExpansionRef = React.useRef(false);\n    const selectedItem =\n      items.find((item) => item.id === selectedId) ?? items[0];\n    const isExpanded = mouseExpanded || touchExpanded || focusExpanded;\n    const activeId =\n      iconOnly || !isExpanded || !compactRailReady\n        ? null\n        : (hoveredId ?? touchExpandedId);\n    const {\n      blobGeometries,\n      cfg,\n      expandedWidth,\n      itemWidths,\n      measurementRef,\n      reserveItems,\n    } = useMultiButtonLayout({\n      activeId,\n      items,\n      reserveLabelSpace: !iconOnly,\n      size,\n      syncWidthTo,\n    });\n    const containerWidth = isExpanded ? expandedWidth : cfg.cell;\n    const expansionStateRef = React.useRef({\n      expanded: isExpanded,\n      expandedWidth,\n    });\n\n    React.useLayoutEffect(() => {\n      expansionStateRef.current = { expanded: isExpanded, expandedWidth };\n      if (!isExpanded) {\n        setCompactRailReady(false);\n        return;\n      }\n\n      if (reduceMotion) {\n        setCompactRailReady(true);\n        return;\n      }\n\n      setCompactRailReady(expandedWidth === cfg.cell);\n    }, [cfg.cell, expandedWidth, isExpanded, reduceMotion]);\n\n    React.useEffect(\n      () => () => {\n        if (focusRestoreFrameRef.current !== null) {\n          cancelAnimationFrame(focusRestoreFrameRef.current);\n        }\n      },\n      [],\n    );\n\n    const handleRailTransitionEnd = React.useCallback(\n      (event: React.TransitionEvent<HTMLDivElement>) => {\n        if (\n          event.target !== event.currentTarget ||\n          (event.propertyName && event.propertyName !== \"width\") ||\n          reduceMotion\n        ) {\n          return;\n        }\n\n        const expected = expansionStateRef.current;\n        const inlineWidth = Number.parseFloat(event.currentTarget.style.width);\n        const renderedWidth = event.currentTarget.getBoundingClientRect().width;\n        if (\n          !expected.expanded ||\n          Math.abs(inlineWidth - expected.expandedWidth) > 0.5 ||\n          (renderedWidth > 0 &&\n            Math.abs(renderedWidth - expected.expandedWidth) > 1)\n        ) {\n          return;\n        }\n\n        setCompactRailReady(true);\n      },\n      [reduceMotion],\n    );\n\n    const collapseTouchAction = React.useCallback(() => {\n      setTouchExpanded(false);\n      setTouchExpandedId(null);\n    }, [setTouchExpandedId]);\n    useOutsidePointerDown(touchExpanded, containerRef, collapseTouchAction);\n\n    const onTouchAction = (\n      event: React.PointerEvent<HTMLButtonElement>,\n      id: string,\n    ) => {\n      if (event.pointerType && event.pointerType !== \"touch\") return;\n      if (!touchExpanded) {\n        event.preventDefault();\n        setTouchExpanded(true);\n        setTouchExpandedId(id);\n      } else {\n        setTouchExpandedId(id);\n      }\n    };\n\n    const collapseAfterAction = (\n      event: React.MouseEvent<HTMLButtonElement>,\n    ) => {\n      if (event.currentTarget.dataset.multiButtonItemId !== selectedItem?.id) {\n        const selectedButton = Array.from(\n          containerRef.current?.querySelectorAll<HTMLButtonElement>(\n            \"button[data-multi-button-item-id]\",\n          ) ?? [],\n        ).find(\n          (button) => button.dataset.multiButtonItemId === selectedItem?.id,\n        );\n        selectedButton?.focus({ preventScroll: true });\n\n        if (focusRestoreFrameRef.current !== null) {\n          cancelAnimationFrame(focusRestoreFrameRef.current);\n        }\n        focusRestoreFrameRef.current = requestAnimationFrame(() => {\n          focusRestoreFrameRef.current = null;\n          const visibleButton = Array.from(\n            containerRef.current?.querySelectorAll<HTMLButtonElement>(\n              \"button[data-multi-button-item-id]\",\n            ) ?? [],\n          ).find((button) => !button.hasAttribute(\"aria-hidden\"));\n\n          if (visibleButton && document.activeElement !== visibleButton) {\n            suppressFocusExpansionRef.current = true;\n            visibleButton.focus({ preventScroll: true });\n          }\n        });\n      }\n\n      setMouseExpanded(false);\n      setTouchExpanded(false);\n      setFocusExpanded(false);\n      setCompactRailReady(false);\n      setHoveredId(null);\n      setTouchExpandedId(null);\n    };\n\n    return (\n      <MultiButtonRail\n        activeId={activeId}\n        blobGeometries={blobGeometries}\n        cellWidth={cfg.cell}\n        className={className}\n        compact\n        containerRef={containerRef}\n        containerWidth={containerWidth}\n        expanded={isExpanded}\n        expandedWidth={expandedWidth}\n        filterId={filterId}\n        forwardedRef={ref}\n        gooey={gooey}\n        highlightColor={highlightColor}\n        itemWidths={itemWidths}\n        interactionReady={compactRailReady}\n        items={items}\n        measurementRef={measurementRef}\n        onAction={collapseAfterAction}\n        onHover={setHoveredId}\n        onTouchAction={onTouchAction}\n        reduceMotion={reduceMotion}\n        restAriaLabel={restAriaLabel}\n        restIcon={restIcon}\n        reserveItems={reserveItems}\n        rootProps={{\n          ...rootProps,\n          onMouseEnter: (event) => {\n            setMouseExpanded(true);\n            onMouseEnterProp?.(event);\n          },\n          onMouseLeave: (event) => {\n            setMouseExpanded(false);\n            setHoveredId(null);\n            onMouseLeaveProp?.(event);\n          },\n          onFocusCapture: (event) => {\n            if (suppressFocusExpansionRef.current) {\n              suppressFocusExpansionRef.current = false;\n            } else {\n              setFocusExpanded(true);\n            }\n            onFocusCaptureProp?.(event);\n          },\n          onBlurCapture: (event) => {\n            if (\n              !event.relatedTarget ||\n              !event.currentTarget.contains(event.relatedTarget as Node)\n            ) {\n              setFocusExpanded(false);\n              setHoveredId(null);\n            }\n            onBlurCaptureProp?.(event);\n          },\n          onTransitionEnd: (event) => {\n            handleRailTransitionEnd(event);\n            onTransitionEndProp?.(event);\n          },\n        }}\n        selectedId={selectedItem?.id}\n        size={size}\n        slot=\"compact-multi-button\"\n        style={style}\n        variant={variant}\n      />\n    );\n  },\n);\nCompactMultiButton.displayName = \"CompactMultiButton\";\n\nexport { CompactMultiButton, MultiButton, MultiButtonGroup };\n",
      "type": "registry:ui",
      "target": "components/godui/multi-button.tsx"
    }
  ],
  "type": "registry:ui"
}
