{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bubble",
  "title": "Bubble",
  "description": "Chat message bubbles with variants, grouped corners, reactions and room for interactive content.",
  "dependencies": [
    "@base-ui/react@^1.8.0",
    "class-variance-authority",
    "cn"
  ],
  "registryDependencies": [
    "https://hextaui.com/r/theme.json"
  ],
  "files": [
    {
      "path": "components/ui/bubble.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { mergeProps } from \"@base-ui/react/merge-props\"\nimport { useRender } from \"@base-ui/react/use-render\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"cn\"\n\ntype BubbleShape = \"uniform\" | \"joined\" | \"tail\"\n\nconst BubbleShapeContext = React.createContext<BubbleShape | undefined>(\n  undefined\n)\n\nconst BubbleAlignContext = React.createContext<\"start\" | \"end\" | undefined>(\n  undefined\n)\n\nfunction dataAttributes(slot: string, extra?: Record<string, string>) {\n  return { \"data-slot\": slot, ...extra } as Record<string, string>\n}\n\ntype BubbleGroupProps = useRender.ComponentProps<\"div\"> & {\n  shape?: BubbleShape\n}\n\nfunction BubbleGroup({ className, shape, render, ...props }: BubbleGroupProps) {\n  const element = useRender({\n    defaultTagName: \"div\",\n    render,\n    props: mergeProps<\"div\">(\n      { className: cn(\"flex min-w-0 flex-col gap-1\", className) },\n      props,\n      dataAttributes(\"bubble-group\", shape ? { \"data-shape\": shape } : {})\n    ),\n  })\n\n  return (\n    <BubbleShapeContext.Provider value={shape}>\n      {element}\n    </BubbleShapeContext.Provider>\n  )\n}\n\nconst bubbleVariants = cva(\n  \"group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 [--bubble-bg:transparent] [--bubble-border:transparent] [--bubble-fg:currentColor] [--bubble-radius-bottom:var(--bubble-radius)] [--bubble-radius-joined:var(--radius-sm)] [--bubble-radius-top:var(--bubble-radius)] [--bubble-radius:var(--radius-xl)] [--bubble-tail:none] group-data-[align=end]/message:ms-auto in-data-[slot=bubble-content]:[--bubble-radius:max(calc(var(--radius-sm)*0.5),calc(var(--bubble-content-radius)-var(--spacing)*2))]\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"[--bubble-bg:var(--color-primary)] [--bubble-fg:var(--color-primary-foreground)]\",\n        secondary:\n          \"[--bubble-bg:var(--color-secondary)] [--bubble-fg:var(--color-secondary-foreground)]\",\n        muted:\n          \"[--bubble-bg:var(--color-muted)] [--bubble-fg:var(--color-foreground)]\",\n        tinted:\n          \"[--bubble-bg:oklch(from_var(--color-primary)_0.94_calc(c*0.4)_h)] [--bubble-fg:var(--color-foreground)] dark:[--bubble-bg:oklch(from_var(--color-primary)_0.3_calc(c*0.4)_h)]\",\n        outline:\n          \"[--bubble-bg:var(--color-background)] [--bubble-border:var(--color-border)] [--bubble-fg:var(--color-foreground)] dark:[--bubble-border:var(--color-input)]\",\n        ghost:\n          \"max-w-full [--bubble-radius:var(--radius-md)] *:data-[slot=bubble-content]:p-0\",\n        destructive:\n          \"[--bubble-accent:var(--color-destructive)] [--bubble-bg:color-mix(in_oklab,var(--color-destructive)_10%,var(--color-background))] [--bubble-fg:var(--color-foreground)] dark:[--bubble-bg:color-mix(in_oklab,var(--color-destructive)_18%,var(--color-background))]\",\n      },\n      align: {\n        start:\n          \"[--bubble-tail-flip:-1] *:data-[slot=bubble-content]:rounded-ss-(--bubble-radius-top) *:data-[slot=bubble-content]:rounded-es-(--bubble-radius-bottom) rtl:[--bubble-tail-flip:1]\",\n        end: \"ms-auto [--bubble-tail-flip:1] *:data-[slot=bubble-content]:self-end *:data-[slot=bubble-content]:rounded-se-(--bubble-radius-top) *:data-[slot=bubble-content]:rounded-ee-(--bubble-radius-bottom) rtl:[--bubble-tail-flip:-1]\",\n      },\n      shape: {\n        uniform: \"\",\n        joined: \"\",\n        tail: \"[--bubble-tail-cut:calc(var(--spacing)*2)] [--bubble-tail-height:calc(var(--spacing)*5)] [--bubble-tail-overlap:calc(var(--spacing)*2.5)] [--bubble-tail-width:calc(var(--spacing)*1.5)] [--bubble-tail:block] *:data-[slot=bubble-content]:relative *:data-[slot=bubble-content]:overflow-visible *:data-[slot=bubble-content]:after:pointer-events-none *:data-[slot=bubble-content]:after:absolute *:data-[slot=bubble-content]:after:bottom-0 *:data-[slot=bubble-content]:after:[display:var(--bubble-tail)] *:data-[slot=bubble-content]:after:h-(--bubble-tail-height) *:data-[slot=bubble-content]:after:w-[calc(var(--bubble-tail-width)+var(--bubble-tail-overlap))] *:data-[slot=bubble-content]:after:[scale:var(--bubble-tail-flip)_1] *:data-[slot=bubble-content]:after:rounded-bl-[calc(var(--spacing)*3.25)_calc(var(--spacing)*2.75)] *:data-[slot=bubble-content]:after:bg-(--bubble-bg) *:data-[slot=bubble-content]:after:[mask-image:linear-gradient(to_right,black_var(--bubble-tail-overlap),transparent_var(--bubble-tail-overlap)),radial-gradient(circle_var(--bubble-tail-cut)_at_100%_0,transparent_var(--bubble-tail-cut),black_calc(var(--bubble-tail-cut)+0.5px))] *:data-[slot=bubble-content]:after:[mask-size:100%_100%,var(--bubble-tail-cut)_var(--bubble-tail-cut)] *:data-[slot=bubble-content]:after:[mask-position:0_0,var(--bubble-tail-overlap)_100%] *:data-[slot=bubble-content]:after:[mask-repeat:no-repeat]\",\n      },\n    },\n    compoundVariants: [\n      {\n        shape: \"joined\",\n        align: \"start\",\n        className:\n          \"[[data-slot=bubble-group]>&:has(+[data-slot=bubble][data-align=start])]:[--bubble-radius-bottom:var(--bubble-radius-joined)] [[data-slot=bubble-group]>[data-slot=bubble][data-align=start]+&]:[--bubble-radius-top:var(--bubble-radius-joined)]\",\n      },\n      {\n        shape: \"joined\",\n        align: \"end\",\n        className:\n          \"[[data-slot=bubble-group]>&:has(+[data-slot=bubble][data-align=end])]:[--bubble-radius-bottom:var(--bubble-radius-joined)] [[data-slot=bubble-group]>[data-slot=bubble][data-align=end]+&]:[--bubble-radius-top:var(--bubble-radius-joined)]\",\n      },\n      {\n        shape: \"tail\",\n        align: \"start\",\n        className:\n          \"ps-(--bubble-tail-width) *:data-[slot=bubble-content]:after:start-[calc(var(--bubble-tail-width)*-1)] [[data-slot=bubble-group]>&:has(+[data-slot=bubble][data-align=start])]:[--bubble-tail:none]\",\n      },\n      {\n        shape: \"tail\",\n        align: \"end\",\n        className:\n          \"pe-(--bubble-tail-width) *:data-[slot=bubble-content]:after:end-[calc(var(--bubble-tail-width)*-1)] [[data-slot=bubble-group]>&:has(+[data-slot=bubble][data-align=end])]:[--bubble-tail:none]\",\n      },\n      {\n        shape: \"tail\",\n        variant: [\"outline\", \"ghost\"],\n        className: \"[--bubble-tail:none]\",\n      },\n    ],\n    defaultVariants: {\n      variant: \"default\",\n      align: \"start\",\n      shape: \"joined\",\n    },\n  }\n)\n\ntype BubbleVariant = NonNullable<VariantProps<typeof bubbleVariants>[\"variant\"]>\ntype BubbleAlign = NonNullable<VariantProps<typeof bubbleVariants>[\"align\"]>\n\ntype BubbleProps = useRender.ComponentProps<\"div\"> & {\n  variant?: BubbleVariant\n  align?: BubbleAlign\n  shape?: BubbleShape\n}\n\nfunction Bubble({\n  className,\n  variant = \"default\",\n  align: alignProp,\n  shape: shapeProp,\n  render,\n  ...props\n}: BubbleProps) {\n  const groupShape = React.useContext(BubbleShapeContext)\n  const inheritedAlign = React.useContext(BubbleAlignContext)\n  const align = alignProp ?? inheritedAlign ?? \"start\"\n  const shape = shapeProp ?? groupShape ?? \"joined\"\n\n  const element = useRender({\n    defaultTagName: \"div\",\n    render,\n    props: mergeProps<\"div\">(\n      { className: cn(bubbleVariants({ variant, align, shape }), className) },\n      props,\n      dataAttributes(\"bubble\", {\n        \"data-variant\": variant,\n        \"data-align\": align,\n        \"data-shape\": shape,\n      })\n    ),\n  })\n\n  return (\n    <BubbleShapeContext.Provider value={undefined}>\n      {element}\n    </BubbleShapeContext.Provider>\n  )\n}\n\nfunction BubbleContent({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\">) {\n  return useRender({\n    defaultTagName: \"div\",\n    render,\n    props: mergeProps<\"div\">(\n      {\n        className: cn(\n          \"w-fit max-w-full min-w-0 overflow-hidden rounded-(--bubble-radius) bg-(--bubble-bg) px-3 py-2 text-sm leading-relaxed wrap-anywhere text-(--bubble-fg) inset-ring-(length:--hairline) inset-ring-(--bubble-border) outline-none [--bubble-content-radius:var(--bubble-radius)] focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:inset-ring-ring focus-visible:outline-hidden forced-colors:border [&:is(button,a)]:text-start [&>svg]:inline-block [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:align-[-0.1875rem] [&>svg]:text-(--bubble-accent,currentColor)\",\n          className\n        ),\n      },\n      props,\n      dataAttributes(\"bubble-content\")\n    ),\n  })\n}\n\nconst bubbleReactionsVariants = cva(\n  \"relative z-10 flex w-max max-w-[calc(100%-var(--spacing)*3)] flex-wrap items-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm leading-5 ring-3 ring-background select-none has-[>button]:p-0 *:data-[slot=button]:rounded-full\",\n  {\n    variants: {\n      side: {\n        top: \"order-first -mb-2.5\",\n        bottom: \"-mt-2.5\",\n      },\n      align: {\n        start: \"ms-3 self-start\",\n        end: \"me-3 self-end\",\n      },\n    },\n    defaultVariants: {\n      side: \"bottom\",\n      align: \"end\",\n    },\n  }\n)\n\ntype BubbleReactionsProps = useRender.ComponentProps<\"div\"> & {\n  side?: NonNullable<VariantProps<typeof bubbleReactionsVariants>[\"side\"]>\n  align?: NonNullable<VariantProps<typeof bubbleReactionsVariants>[\"align\"]>\n}\n\nfunction BubbleReactions({\n  className,\n  side = \"bottom\",\n  align = \"end\",\n  render,\n  ...props\n}: BubbleReactionsProps) {\n  return useRender({\n    defaultTagName: \"div\",\n    render,\n    props: mergeProps<\"div\">(\n      {\n        className: cn(bubbleReactionsVariants({ side, align }), className),\n      },\n      props,\n      dataAttributes(\"bubble-reactions\", {\n        \"data-side\": side,\n        \"data-align\": align,\n      })\n    ),\n  })\n}\n\nexport {\n  Bubble,\n  BubbleAlignContext,\n  BubbleContent,\n  BubbleGroup,\n  BubbleReactions,\n  bubbleReactionsVariants,\n  bubbleVariants,\n}\nexport type {\n  BubbleAlign,\n  BubbleGroupProps,\n  BubbleProps,\n  BubbleReactionsProps,\n  BubbleShape,\n  BubbleVariant,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "chat"
  ],
  "type": "registry:ui"
}