{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "message",
  "title": "Message",
  "description": "shadcn/ui(new-york-v4) message의 유니버설(웹+네이티브 한 벌 소스) 포팅. 포팅 판단(원본 대비 차이·이유): 있게 AvatarGroupCount 패턴(View + 내부 Text)으로 이중 지정했다.",
  "dependencies": [
    "@unikit-bridge/ui"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/message.tsx",
      "content": "// Message — shadcn/ui（new-york-v4） message.tsx 유니버설 포팅 （사이클 40, T-3d）.\n// 원본: shadcn-ref/ui/message.tsx\n//\n// 포팅 판단（원본 대비 차이이유）:\n// - div 6종 → 브리지 View/Text. 헤더푸터는 문자열 children을 바로 쓸 수\n//   있게 AvatarGroupCount 패턴（View + 내부 Text）으로 이중 지정했다.\n// - data-align 셀렉터（data-［align=end］:flex-row-reverse 등）는 네이티브에\n//   data 속성이 없으므로 React context（MessageContext）로 재현 — Message가\n//   align을 제공하고 자식이 읽어 조건부 클래스를 적용한다（avatar.tsx 패턴）.\n// - MessageAvatar의 group-has-data-［slot=message-footer］:-translate-y-8\n//   （푸터 있을 때 아바타 위로 보정）는 푸터 \"마운트 등록\"으로 근사 —\n//   MessageFooter가 reportFooterMounted（true）를 보내고 Message가 소유한다.\n// - MessageContent의 *:data-slot:self-end（자식 버블 우측 정렬）은 items-end\n//   근사. 실제 버블 정렬은 Bubble이 스스로 처리한다（self-end）.\n// - MessageHeader/Footer의 group-has-data-［variant=ghost］/message:px-0\n//   （내부 버블이 ghost면 패딩 제거）은 감지 수단이 없어 제거 — 항상 px-3.\n// - wrap-break-word는 웹 전용（Tailwind v4 유틸） → web: 접두. RN Text는 기본 줄바꿈.\n// - MessageContent에 shrink（flex-shrink:1）추가（사이클 74）: 원본은 웹 전용이라\n//   flex-shrink 기본값 1로 아바타 옆에서 저절로 수축하지만, RN(Yoga)은 기본값 0이라\n//   w-full 본문이 아바타 폭만큼 행을 넘어 화면 끝까지 흘렀다. 웹 동작과 정합.\n// - data-slot 속성은 브리지 props 계약에 없어 전면 제거（토큰은 유지）.\nimport {\n  createContext,\n  forwardRef,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n  type ReactNode,\n} from \"react\";\nimport {\n  Text,\n  View,\n  type BridgeTextRef,\n  type BridgeViewRef,\n  type TextProps,\n  type ViewProps,\n} from \"@unikit-bridge/ui/primitives\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** 메시지 가로 정렬 — 좌（start, 상대방） / 우（end, 나） */\nexport type MessageAlign = \"start\" | \"end\";\n\n// Message가 제공하는 상태 — 자식 셀렉터 재현의 단일 출처.\ntype MessageContextValue = {\n  align: MessageAlign;\n  /** MessageFooter 마운트 여부 — 아바타 수직 보정 판단 근거 */\n  hasFooter: boolean;\n  reportFooterMounted: (mounted: boolean) => void;\n};\n\nconst MessageContext = createContext<MessageContextValue | null>(null);\n\n// ---------------------------------------------------------------------------\n// MessageGroup — 메시지 묶음（하루발신자 단위）.\n// ---------------------------------------------------------------------------\nexport type MessageGroupProps = ViewProps;\n\nconst MessageGroup = forwardRef<BridgeViewRef, MessageGroupProps>(\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);\nMessageGroup.displayName = \"MessageGroup\";\n\n// ---------------------------------------------------------------------------\n// Message — 개별 메시지 루트. align 상태 제공자역을 겸한다.\n// ---------------------------------------------------------------------------\nexport type MessageProps = ViewProps & {\n  align?: MessageAlign;\n  children?: ReactNode;\n};\n\nconst Message = forwardRef<BridgeViewRef, MessageProps>(\n  ({ className, align = \"start\", ...props }, ref) => {\n    const [hasFooter, setHasFooter] = useState(false);\n    const contextValue = useMemo<MessageContextValue>(\n      () => ({\n        align,\n        hasFooter,\n        // setState 설정자는 참조가 안정적이라 그대로 노출해도 안전하다.\n        reportFooterMounted: setHasFooter,\n      }),\n      [align, hasFooter],\n    );\n    return (\n      <MessageContext.Provider value={contextValue}>\n        <View\n          ref={ref}\n          className={cn(\n            \"relative flex w-full min-w-0 flex-row gap-2\",\n            align === \"end\" && \"flex-row-reverse\",\n            className,\n          )}\n          {...props}\n        />\n      </MessageContext.Provider>\n    );\n  },\n);\nMessage.displayName = \"Message\";\n\n// ---------------------------------------------------------------------------\n// MessageAvatar — 아바타 슬롯（Avatar 컴포넌트를 넣는 컨테이너）.\n// ---------------------------------------------------------------------------\n// MessageAvatar — 아바타 래퍼. 상단 정렬(self-start)로 헤더·버블과 자연스럽게 정렬.\n// ---------------------------------------------------------------------------\nexport type MessageAvatarProps = ViewProps;\n\nconst MessageAvatar = forwardRef<BridgeViewRef, MessageAvatarProps>(\n  ({ className, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(\n        \"flex w-fit min-w-8 shrink-0 items-center justify-center self-start overflow-hidden rounded-full bg-muted\",\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nMessageAvatar.displayName = \"MessageAvatar\";\n\n// ---------------------------------------------------------------------------\n// MessageContent — 본문 영역（버블첨부를 담는다）. align=end면 자식을 우측 정렬.\n// ---------------------------------------------------------------------------\nexport type MessageContentProps = ViewProps;\n\nconst MessageContent = forwardRef<BridgeViewRef, MessageContentProps>(\n  ({ className, ...props }, ref) => {\n    const message = useContext(MessageContext);\n    return (\n      <View\n        ref={ref}\n        className={cn(\n          \"flex w-full shrink min-w-0 flex-col gap-2.5 web:wrap-break-word\",\n          message?.align === \"end\" && \"items-end\",\n          className,\n        )}\n        {...props}\n      />\n    );\n  },\n);\nMessageContent.displayName = \"MessageContent\";\n\n// ---------------------------------------------------------------------------\n// MessageHeader — 발신자시간 등 메타 표시（문자열 children 권장）.\n// ---------------------------------------------------------------------------\nexport type MessageHeaderProps = ViewProps & {\n  children?: ReactNode;\n};\n\nconst MessageHeader = forwardRef<BridgeViewRef, MessageHeaderProps>(\n  ({ className, children, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(\n        \"flex flex-row max-w-full min-w-0 items-center px-3\",\n        className,\n      )}\n      {...props}\n    >\n      {/* 이중 지정 원칙: 텍스트 색크기는 내부 Text에 */}\n      <Text className=\"font-sans text-xs font-medium text-muted-foreground\">\n        {children}\n      </Text>\n    </View>\n  ),\n);\nMessageHeader.displayName = \"MessageHeader\";\n\n// ---------------------------------------------------------------------------\n// MessageFooter — 전송 시각상태（전송 중/실패） 표시. 마운트를 루트에 보고한다.\n// ---------------------------------------------------------------------------\nexport type MessageFooterProps = ViewProps & {\n  children?: ReactNode;\n};\n\nconst MessageFooter = forwardRef<BridgeViewRef, MessageFooterProps>(\n  ({ className, children, ...props }, ref) => {\n    const message = useContext(MessageContext);\n    const report = message?.reportFooterMounted;\n    // 마운트 등록/해제 — 아바타 수직 보정의 근거（provider는 안정적）.\n    useEffect(() => {\n      report?.(true);\n      return () => report?.(false);\n    }, [report]);\n    return (\n      <View\n        ref={ref}\n        className={cn(\n          \"flex flex-row max-w-full min-w-0 items-center px-3\",\n          message?.align === \"end\" && \"justify-end\",\n          className,\n        )}\n        {...props}\n      >\n        <Text className=\"font-sans text-xs font-medium text-muted-foreground\">\n          {children}\n        </Text>\n      </View>\n    );\n  },\n);\nMessageFooter.displayName = \"MessageFooter\";\n\nexport {\n  MessageGroup,\n  Message,\n  MessageAvatar,\n  MessageContent,\n  MessageFooter,\n  MessageHeader,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/message.tsx"
    }
  ],
  "type": "registry:ui"
}
