{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "message-bubble",
  "title": "UI Message Bubble",
  "description": "A chat message bubble whose hidden/visible state is a pure function of the timeline. The renderer is a pure function of a MessageBubbleStyle (opacity, translateY, scale); useMessageBubbleTransition eases the rise-in enter. The incoming/outgoing variant resolves bubble color and alignment from the theme, and an optional reaction badge pops in on its own channel.",
  "dependencies": ["remotion"],
  "registryDependencies": ["@remocn/remocn-ui"],
  "files": [
    {
      "path": "registry/remocn-ui/message-bubble/index.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { type RemocnTheme, useRemocnTheme } from \"@/lib/remocn-ui\";\n\nexport type MessageBubbleState = \"hidden\" | \"visible\";\n\nexport type MessageBubbleVariant = \"incoming\" | \"outgoing\";\n\nexport interface MessageBubbleProps {\n  state?: MessageBubbleState;\n  style?: MessageBubbleStyle;\n  variant?: MessageBubbleVariant;\n  children?: ReactNode;\n  reaction?: string;\n  reactionStyle?: MessageBubbleReactionStyle;\n  maxWidth?: number | string;\n  theme?: Partial<RemocnTheme>;\n  className?: string;\n}\n\nexport interface MessageBubbleStyle {\n  opacity: number;\n  translateY: number;\n  scale: number;\n}\n\nexport interface MessageBubbleReactionStyle {\n  opacity: number;\n  scale: number;\n}\n\nexport interface MessageBubbleStyleContext {\n  background: string;\n  color: string;\n  align: \"flex-start\" | \"flex-end\";\n  reactionSide: \"left\" | \"right\";\n  reactionBackground: string;\n  reactionColor: string;\n  reactionRing: string;\n}\n\nexport function messageBubbleStyleContext(\n  variant: MessageBubbleVariant,\n  theme: RemocnTheme,\n): MessageBubbleStyleContext {\n  const reaction = {\n    reactionBackground: theme.muted,\n    reactionColor: theme.foreground,\n    reactionRing: theme.card,\n  };\n  if (variant === \"outgoing\") {\n    return {\n      background: theme.primary,\n      color: theme.primaryForeground,\n      align: \"flex-end\",\n      reactionSide: \"right\",\n      ...reaction,\n    };\n  }\n  return {\n    background: theme.muted,\n    color: theme.foreground,\n    align: \"flex-start\",\n    reactionSide: \"left\",\n    ...reaction,\n  };\n}\n\nexport function messageBubbleStyle(\n  state: MessageBubbleState,\n): MessageBubbleStyle {\n  switch (state) {\n    case \"visible\":\n      return { opacity: 1, translateY: 0, scale: 1 };\n    default:\n      return { opacity: 0, translateY: 12, scale: 0.94 };\n  }\n}\n\nexport function messageBubbleReactionStyle(\n  state: MessageBubbleState,\n): MessageBubbleReactionStyle {\n  return state === \"visible\"\n    ? { opacity: 1, scale: 1 }\n    : { opacity: 0, scale: 0 };\n}\n\nexport function MessageBubble({\n  state = \"hidden\",\n  style,\n  variant = \"incoming\",\n  children,\n  reaction,\n  reactionStyle,\n  maxWidth = \"80%\",\n  theme: themeOverride,\n  className,\n}: MessageBubbleProps) {\n  const theme = useRemocnTheme(themeOverride, \"light\");\n  const ctx = messageBubbleStyleContext(variant, theme);\n  const v = style ?? messageBubbleStyle(state);\n  const reaction_ = reactionStyle ?? messageBubbleReactionStyle(state);\n\n  return (\n    <div\n      className={className}\n      style={{\n        display: \"flex\",\n        justifyContent: ctx.align,\n        width: \"100%\",\n        opacity: v.opacity,\n        transform: `translateY(${v.translateY}px) scale(${v.scale})`,\n        transformOrigin:\n          ctx.align === \"flex-end\" ? \"bottom right\" : \"bottom left\",\n      }}\n    >\n      <div style={{ position: \"relative\", maxWidth, minWidth: 0 }}>\n        <div\n          style={{\n            display: \"inline-block\",\n            maxWidth: \"100%\",\n            padding: \"10px 14px\",\n            background: ctx.background,\n            color: ctx.color,\n            border: \"1px solid transparent\",\n            borderRadius: 24,\n            fontFamily:\n              \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\",\n            fontSize: 14,\n            lineHeight: 1.625,\n            overflowWrap: \"break-word\",\n            wordBreak: \"break-word\",\n          }}\n        >\n          {children}\n        </div>\n        {reaction !== undefined && (\n          <div\n            style={{\n              position: \"absolute\",\n              bottom: 0,\n              left: ctx.reactionSide === \"left\" ? 12 : undefined,\n              right: ctx.reactionSide === \"right\" ? 12 : undefined,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              gap: 4,\n              minWidth: 24,\n              padding: \"2px 6px\",\n              background: ctx.reactionBackground,\n              color: ctx.reactionColor,\n              borderRadius: 999,\n              fontSize: 14,\n              lineHeight: 1,\n              boxShadow: `0 0 0 3px ${ctx.reactionRing}`,\n              opacity: reaction_.opacity,\n              transform: `translateY(75%) scale(${reaction_.scale})`,\n              transformOrigin: \"center\",\n            }}\n          >\n            {reaction}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/message-bubble.tsx"
    },
    {
      "path": "registry/remocn-ui/message-bubble/use-message-bubble-transition.ts",
      "content": "\"use client\";\n\nimport {\n  type MessageBubbleState,\n  type MessageBubbleStyle,\n  messageBubbleStyle,\n} from \"@/components/remocn/message-bubble\";\nimport { easings, type Step, useStateTransition } from \"@/lib/remocn-ui\";\n\nexport const DEFAULT_DURATION = 14;\n\nexport function tweenMessageBubbleStyle(\n  a: MessageBubbleStyle,\n  b: MessageBubbleStyle,\n  t: number,\n): MessageBubbleStyle {\n  return {\n    opacity: a.opacity + (b.opacity - a.opacity) * t,\n    translateY: a.translateY + (b.translateY - a.translateY) * t,\n    scale: a.scale + (b.scale - a.scale) * t,\n  };\n}\n\nexport interface MessageBubbleTransitionOptions {\n  speed?: number;\n  defaultDuration?: number;\n}\n\nexport function useMessageBubbleTransition(\n  steps: Step<MessageBubbleState>[],\n  opts: MessageBubbleTransitionOptions = {},\n): MessageBubbleStyle {\n  const { speed = 1, defaultDuration = DEFAULT_DURATION } = opts;\n  const { from, to, progress } = useStateTransition(\n    steps,\n    \"hidden\",\n    speed,\n    defaultDuration,\n  );\n  const t = easings.out(progress);\n  return tweenMessageBubbleStyle(\n    messageBubbleStyle(from),\n    messageBubbleStyle(to),\n    t,\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/use-message-bubble-transition.ts"
    }
  ],
  "type": "registry:component"
}
