{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bubble",
  "title": "Bubble",
  "description": "shadcn/ui(new-york-v4) bubble의 유니버설(웹+네이티브 한 벌 소스) 포팅. 포팅 판단(원본 대비 차이·이유): View(배경·테두리) + 내부 Text(전경색) 이중 지정(AvatarGroupCount 패턴).",
  "dependencies": [
    "@unikit-bridge/ui",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/bubble.tsx",
      "content": "// Bubble — shadcn/ui（new-york-v4） bubble.tsx 유니버설 포팅 （사이클 40, T-3d）.\n// 원본: shadcn-ref/ui/bubble.tsx\n//\n// 포팅 판단（원본 대비 차이이유）:\n// - div/span → 브리지 View/Text. BubbleContent는 문자열 children을 바로 쓰게\n//   View（배경테두리） + 내부 Text（전경색） 이중 지정（AvatarGroupCount 패턴）.\n// - 원본은 Bubble의 variant가 `*:data-［slot=bubble-content］:bg-*` 자식\n//   셀렉터로 BubbleContent에 색을 입힌다. 네이티브에 data 속성이 없어\n//   BubbleContext { variant, align } + 조회 테이블로 재현 — BubbleContent가\n//   variant를 읽어 자기 배경/전경을 적용한다（avatar.tsx 패턴）.\n// - asChild（radix Slot） 제거 — View 렌더 고정（버블을 버튼/링크로 만드는\n//   `［&>［data-slot=bubble-content］:is（button,a）:hover］` 토큰도 함께 제거.\n// - tinted의 oklch（from var（--primary） ） 임의값color-mix hover는 표준\n//   계단 근사 — bg-primary/10 원색 틴트로, hover는 폐기（임의값 금지 규칙）.\n// - dark: 변형 전부 제거（웹은 라이트 토큰만 사용, 규칙 6）.\n// - max-w-［80%］ 임의값 → max-w-lg 표준 계단 근사.\n// - group-data-［align=end］/message:self-end는 Message context 대신 자체\n//   align prop/self-end로 자립（Bubble 단독 사용도 지원）.\n// - wrap-break-word링크 focus 링은 웹 전용 → web: 접두 또는 제거.\n// - 원본 has-［button］:p-0（버튼 포함 시 패딩 제거）는 has 임의값 셀렉터라 px-1.5 py-0.5 고정으로 근사（T-4 감사 반영）.\nimport {\n  createContext,\n  forwardRef,\n  useContext,\n  type ReactNode,\n} from \"react\";\nimport {\n  Text,\n  View,\n  type BridgeViewRef,\n  type ViewProps,\n} from \"@unikit-bridge/ui/primitives\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport type BubbleVariant =\n  | \"default\"\n  | \"secondary\"\n  | \"muted\"\n  | \"tinted\"\n  | \"outline\"\n  | \"ghost\"\n  | \"destructive\";\n\n// Bubble이 제공하는 상태 — BubbleContent 색정렬의 단일 출처.\ntype BubbleContextValue = {\n  variant: BubbleVariant;\n  align: \"start\" | \"end\";\n};\n\nconst BubbleContext = createContext<BubbleContextValue | null>(null);\n\n// BubbleContent 배경테두리（원본 variant별 셀렉터 토큰의 조회 테이블）.\nconst BUBBLE_CONTENT_BG: Record<BubbleVariant, string> = {\n  default: \"bg-primary\",\n  secondary: \"bg-secondary\",\n  muted: \"bg-muted\",\n  tinted: \"bg-primary/10\",\n  outline: \"border-border bg-background\",\n  ghost: \"rounded-none border-none bg-transparent p-0\",\n  destructive: \"bg-destructive/10\",\n};\n\n// BubbleContent 전경색（내부 Text에 — 이중 지정 원칙）.\nconst BUBBLE_CONTENT_FG: Record<BubbleVariant, string> = {\n  default: \"text-primary-foreground\",\n  secondary: \"text-secondary-foreground\",\n  muted: \"text-foreground\",\n  tinted: \"text-foreground\",\n  outline: \"text-foreground\",\n  ghost: \"text-foreground\",\n  destructive: \"text-destructive\",\n};\n\n// ---------------------------------------------------------------------------\n// BubbleGroup — 버블 묶음（연속 발화）.\n// ---------------------------------------------------------------------------\nexport type BubbleGroupProps = ViewProps;\n\nconst BubbleGroup = forwardRef<BridgeViewRef, BubbleGroupProps>(\n  ({ className, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n      {...props}\n    />\n  ),\n);\nBubbleGroup.displayName = \"BubbleGroup\";\n\n// ---------------------------------------------------------------------------\n// Bubble — 버블 래퍼. 색은 BubbleContent가 variant를 읽어 적용한다.\n// 원본 대비 색 토큰은 테이블로 이동했고 flex 레이아웃 토큰만 남았다.\n// ---------------------------------------------------------------------------\nconst bubbleVariants = cva(\n  \"relative flex w-fit min-w-0 max-w-lg flex-col gap-1\",\n  {\n    variants: {\n      variant: {\n        default: \"\",\n        secondary: \"\",\n        muted: \"\",\n        tinted: \"\",\n        outline: \"\",\n        ghost: \"max-w-full\",\n        destructive: \"\",\n      },\n      align: {\n        start: \"\",\n        end: \"self-end\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      align: \"start\",\n    },\n  },\n);\n\nexport type BubbleProps = ViewProps &\n  VariantProps<typeof bubbleVariants> & {\n    children?: ReactNode;\n  };\n\nconst Bubble = forwardRef<BridgeViewRef, BubbleProps>(\n  ({ className, variant, align, ...props }, ref) => {\n    // VariantProps는 null을 허용하므로 기본값 병합 후 context에 제공한다.\n    const resolvedVariant = variant ?? \"default\";\n    const resolvedAlign = align ?? \"start\";\n    const contextValue =\n      resolvedVariant === \"default\" && resolvedAlign === \"start\"\n        ? DEFAULT_BUBBLE_CONTEXT\n        : { variant: resolvedVariant, align: resolvedAlign };\n    return (\n      <BubbleContext.Provider value={contextValue}>\n        <View\n          ref={ref}\n          className={cn(\n            bubbleVariants({ variant: resolvedVariant, align: resolvedAlign }),\n            className,\n          )}\n          {...props}\n        />\n      </BubbleContext.Provider>\n    );\n  },\n);\nBubble.displayName = \"Bubble\";\n\n// 자주 쓰이는 기본 조합 — Provider value 참조 안정화（불필요한 재렌더 방지）.\nconst DEFAULT_BUBBLE_CONTEXT: BubbleContextValue = {\n  variant: \"default\",\n  align: \"start\",\n};\n\n// ---------------------------------------------------------------------------\n// BubbleContent — 실제 메시지 본문（문자열 children 권장）. variant 색 연동.\n// ---------------------------------------------------------------------------\nexport type BubbleContentProps = ViewProps & {\n  children?: ReactNode;\n};\n\nconst BubbleContent = forwardRef<BridgeViewRef, BubbleContentProps>(\n  ({ className, children, ...props }, ref) => {\n    const bubble = useContext(BubbleContext);\n    const variant = bubble?.variant ?? \"default\";\n    return (\n      <View\n        ref={ref}\n        className={cn(\n          \"w-fit max-w-full min-w-0 overflow-hidden rounded-xl border border-transparent px-3 py-2\",\n          BUBBLE_CONTENT_BG[variant],\n          bubble?.align === \"end\" && \"self-end\",\n          className,\n        )}\n        {...props}\n      >\n        <Text\n          className={cn(\n            \"font-sans text-sm leading-relaxed web:wrap-break-word\",\n            BUBBLE_CONTENT_FG[variant],\n          )}\n        >\n          {children}\n        </Text>\n      </View>\n    );\n  },\n);\nBubbleContent.displayName = \"BubbleContent\";\n\n// ---------------------------------------------------------------------------\n// BubbleReactions — 버블에 붙는 리액션 배지（위치는 side/align 변형）.\n// ---------------------------------------------------------------------------\nconst bubbleReactionsVariants = cva(\n  \"absolute z-10 flex flex-row w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card\",\n  {\n    variants: {\n      side: {\n        top: \"top-0 -translate-y-3/4\",\n        bottom: \"bottom-0 translate-y-3/4\",\n      },\n      align: {\n        start: \"left-3\",\n        end: \"right-3\",\n      },\n    },\n    defaultVariants: {\n      side: \"bottom\",\n      align: \"end\",\n    },\n  },\n);\n\nexport type BubbleReactionsProps = ViewProps & {\n  align?: \"start\" | \"end\";\n  side?: \"top\" | \"bottom\";\n  children?: ReactNode;\n};\n\nconst BubbleReactions = forwardRef<BridgeViewRef, BubbleReactionsProps>(\n  ({ className, side, align, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(bubbleReactionsVariants({ side, align }), className)}\n      {...props}\n    />\n  ),\n);\nBubbleReactions.displayName = \"BubbleReactions\";\n\nexport { BubbleGroup, Bubble, BubbleContent, BubbleReactions };\n",
      "type": "registry:ui",
      "target": "components/ui/bubble.tsx"
    }
  ],
  "type": "registry:ui"
}
