{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-message",
  "type": "registry:component",
  "title": "ChatMessage",
  "description": "Composable chat message with typed role, density, and lifecycle state.",
  "dependencies": [],
  "registryDependencies": [
    "https://control-ui.dev/r/core.json",
    "https://control-ui.dev/r/effects.json"
  ],
  "files": [
    {
      "path": "src/registry/hooks/use-chat-message.ts",
      "target": "@components/control-ui/hooks/use-chat-message.ts",
      "type": "registry:hook",
      "content": "import type { ComponentProps, CSSProperties } from \"react\";\nimport type { ChatMessageKnobStyle } from \"@/components/control-ui/knob-contracts/chat-message-knobs\";\n\nexport type ChatRole = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\nexport type ChatDensity = \"compact\" | \"comfortable\";\n\nexport type ChatState = \"idle\" | \"streaming\" | \"pending\" | \"error\";\n\nexport type ChatMessageProps = ComponentProps<\"article\"> & {\n  from: ChatRole;\n  state?: ChatState;\n  density?: ChatDensity;\n} & { style?: CSSProperties & ChatMessageKnobStyle };\n\nexport type ChatMessageContext = {\n  from: ChatRole;\n  state: ChatState;\n  density: ChatDensity;\n  isUser: boolean;\n  isAssistant: boolean;\n  isSystem: boolean;\n  isTool: boolean;\n  isCompact: boolean;\n  isStreaming: boolean;\n  isError: boolean;\n};\n\nfunction getChatMessageContext({\n  from,\n  state,\n  density,\n}: Required<Pick<ChatMessageProps, \"from\" | \"state\" | \"density\">>): ChatMessageContext {\n  return {\n    from,\n    state,\n    density,\n    isUser: from === \"user\",\n    isAssistant: from === \"assistant\",\n    isSystem: from === \"system\",\n    isTool: from === \"tool\",\n    isCompact: density === \"compact\",\n    isStreaming: state === \"streaming\",\n    isError: state === \"error\",\n  };\n}\n\nexport function useChatMessage({ from, state = \"idle\", density = \"comfortable\" }: Pick<ChatMessageProps, \"from\" | \"state\" | \"density\">) {\n  return getChatMessageContext({ from, state, density });\n}\n"
    },
    {
      "path": "src/registry/knob-contracts/chat-message-knobs.ts",
      "target": "@components/control-ui/knob-contracts/chat-message-knobs.ts",
      "type": "registry:component",
      "content": "// Generated from src/registry/sources/control-ui/recipes/chat-message.css by scripts/gen-knob-contracts.ts — run `bun run sync:knobs`.\nexport const chatMessageKnobs = [\n  \"--cui-chat-message-avatar-radius\",\n  \"--cui-chat-message-avatar-background\",\n  \"--cui-chat-message-avatar-border-color\",\n  \"--cui-chat-message-avatar-border-width\",\n  \"--cui-chat-message-radius\",\n  \"--cui-chat-message-corner-radius\",\n  \"--cui-chat-message-background\",\n  \"--cui-chat-message-background-image\",\n  \"--cui-chat-message-foreground\",\n  \"--cui-chat-message-border-color\",\n  \"--cui-chat-message-border-width\",\n  \"--cui-chat-message-shadow\",\n] as const;\nexport type ChatMessageKnobStyle = Partial<Record<(typeof chatMessageKnobs)[number], string>>;\n"
    },
    {
      "path": "src/registry/sources/control-ui/chat-message.tsx",
      "target": "@components/control-ui/chat-message.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport type { ComponentProps, CSSProperties } from \"react\";\nimport { createContext, useContext } from \"react\";\n\nimport type { ChatMessageProps } from \"@/components/control-ui/hooks/use-chat-message\";\nimport { useChatMessage } from \"@/components/control-ui/hooks/use-chat-message\";\nimport type { ChatMessageKnobStyle } from \"@/components/control-ui/knob-contracts/chat-message-knobs\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\n\ntype ChatMessageContextValue = ReturnType<typeof useChatMessage>;\n\nconst ChatMessageContext = createContext<ChatMessageContextValue | null>(null);\n\nfunction useChatMessageContext() {\n  const context = useContext(ChatMessageContext);\n  if (!context) throw new Error(\"ChatMessage compound components must be rendered inside <ChatMessage>.\");\n  return context;\n}\n\nexport function ChatMessage({ from, state = \"idle\", density = \"comfortable\", className, children, ...props }: ChatMessageProps) {\n  const message = useChatMessage({ from, state, density });\n\n  return (\n    <ChatMessageContext.Provider value={message}>\n      <article\n        data-control-ui=\"chat-message\"\n        data-control-family=\"chat-message\"\n        data-slot=\"root\"\n        data-role={from}\n        data-state={state}\n        data-density={density}\n        className={cn(\"w-full\", className)}\n        {...props}\n      >\n        {children}\n      </article>\n    </ChatMessageContext.Provider>\n  );\n}\n\nexport type ChatMessageRowProps = ComponentProps<\"div\"> & { style?: CSSProperties & ChatMessageKnobStyle };\n\nexport function ChatMessageRow({ className, children, ...props }: ChatMessageRowProps) {\n  const message = useChatMessageContext();\n\n  return (\n    <div\n      data-control-ui=\"chat-message\"\n      data-control-family=\"chat-message\"\n      data-slot=\"row\"\n      className={cn(\"flex w-full gap-2\", message.isUser ? \"justify-end\" : \"justify-start\", message.isCompact ? \"py-1\" : \"py-2\", className)}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport type ChatMessageAvatarProps = Omit<ComponentProps<\"div\">, \"style\"> & {\n  style?: CSSProperties & ChatMessageKnobStyle;\n};\n\nexport function ChatMessageAvatar({ className, ...props }: ChatMessageAvatarProps) {\n  return (\n    <div\n      data-control-ui=\"chat-message\"\n      data-control-family=\"chat-message\"\n      data-slot=\"avatar\"\n      className={cn(\"mt-0.5 flex size-6 shrink-0 items-center justify-center\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type ChatMessageBodyProps = ComponentProps<\"div\"> & { style?: CSSProperties & ChatMessageKnobStyle };\n\nexport function ChatMessageBody({ className, ...props }: ChatMessageBodyProps) {\n  const message = useChatMessageContext();\n\n  return (\n    <div\n      data-control-ui=\"chat-message\"\n      data-control-family=\"chat-message\"\n      data-slot=\"body\"\n      className={cn(\"min-w-0\", message.isUser ? \"max-w-[76%]\" : \"max-w-[80%] flex-1\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type ChatMessageHeaderProps = Omit<ComponentProps<\"div\">, \"style\"> & {\n  style?: CSSProperties & ChatMessageKnobStyle;\n};\n\nexport function ChatMessageHeader({ className, ...props }: ChatMessageHeaderProps) {\n  return (\n    <div\n      data-control-ui=\"chat-message\"\n      data-control-family=\"chat-message\"\n      data-slot=\"header\"\n      className={cn(\"mb-1 flex items-center gap-2 px-1\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type ChatMessageContentProps = Omit<ComponentProps<\"div\">, \"style\"> & {\n  style?: CSSProperties & ChatMessageKnobStyle;\n};\n\nexport function ChatMessageContent({ className, ...props }: ChatMessageContentProps) {\n  const message = useChatMessageContext();\n\n  return (\n    <div\n      data-control-ui=\"chat-message\"\n      data-control-family=\"chat-message\"\n      data-slot=\"content\"\n      data-role={message.from}\n      data-streaming={message.isStreaming ? \"\" : undefined}\n      className={cn(message.isUser && \"px-[var(--padding-x)] py-[var(--padding-y)]\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type ChatMessageActionsProps = Omit<ComponentProps<\"div\">, \"style\"> & {\n  style?: CSSProperties & ChatMessageKnobStyle;\n};\n\nexport function ChatMessageActions({ className, ...props }: ChatMessageActionsProps) {\n  return (\n    <div\n      data-control-ui=\"chat-message\"\n      data-control-family=\"chat-message\"\n      data-slot=\"actions\"\n      className={cn(\"mt-1 flex items-center gap-1\", className)}\n      {...props}\n    />\n  );\n}\n"
    },
    {
      "path": "src/registry/sources/control-ui/recipes/chat-message.css",
      "target": "@components/control-ui/styles/recipes/chat-message.css",
      "type": "registry:file",
      "content": "@layer components {\n  :where([data-control-family=\"chat-message\"][data-slot=\"root\"]) {\n    --cui-chat-message-avatar-radius: 9999px;\n    --cui-chat-message-avatar-background: var(--card);\n    --cui-chat-message-avatar-border-color: var(--border);\n    --cui-chat-message-avatar-border-width: 1px;\n    --cui-chat-message-radius: 0px;\n    --cui-chat-message-corner-radius: 0px;\n    --cui-chat-message-background: transparent;\n    --cui-chat-message-background-image: none;\n    --cui-chat-message-foreground: var(--foreground);\n    --cui-chat-message-border-color: transparent;\n    --cui-chat-message-border-width: 0px;\n    --cui-chat-message-shadow: none;\n  }\n  :where([data-control-family=\"chat-message\"][data-slot=\"avatar\"]) {\n    border: var(--cui-chat-message-avatar-border-width) solid var(--cui-chat-message-avatar-border-color);\n    border-radius: var(--cui-chat-message-avatar-radius);\n    background: var(--cui-chat-message-avatar-background);\n    font-size: var(--text-caption);\n    color: var(--muted-foreground);\n  }\n\n  :where([data-control-family=\"chat-message\"][data-slot=\"content\"]) {\n    border: var(--cui-chat-message-border-width) solid var(--cui-chat-message-border-color);\n    border-radius: var(--cui-chat-message-radius);\n    border-start-end-radius: var(--cui-chat-message-corner-radius);\n    background-color: var(--cui-chat-message-background);\n    background-image: var(--cui-chat-message-background-image);\n    box-shadow: var(--cui-chat-message-shadow);\n    color: var(--cui-chat-message-foreground);\n    font-size: var(--text-body);\n    line-height: calc(var(--spacing) * 5);\n  }\n\n  :where([data-control-family=\"chat-message\"][data-slot=\"root\"][data-role=\"user\"]) {\n    --cui-chat-message-radius: var(--radius-field);\n    --cui-chat-message-corner-radius: var(--radius-lg);\n    --cui-chat-message-background: var(--primary);\n    --cui-chat-message-foreground: var(--primary-foreground);\n    --cui-chat-message-shadow: var(--shadow-sm);\n  }\n\n  :where([data-control-family=\"chat-message\"][data-slot=\"content\"][data-role=\"assistant\"][data-streaming]) {\n    @apply shimmer-text;\n  }\n  :where([data-control-family=\"chat-message\"][data-slot=\"actions\"]) {\n    opacity: 0;\n    transition-property: opacity;\n    transition-timing-function: var(--ease-standard);\n    transition-duration: var(--duration-fast);\n  }\n\n  @media (hover: hover) {\n    :where([data-control-family=\"chat-message\"][data-slot=\"row\"]:hover [data-control-family=\"chat-message\"][data-slot=\"actions\"]) {\n      opacity: 1;\n    }\n  }\n\n  :where([data-control-family=\"chat-message\"][data-slot=\"header\"]) {\n    font-size: var(--text-caption);\n    color: var(--muted-foreground);\n  }\n}\n\n@property --cui-chat-message-avatar-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-chat-message-avatar-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-chat-message-avatar-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-chat-message-avatar-border-width {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-chat-message-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-chat-message-corner-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-chat-message-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-chat-message-background-image {\n  syntax: \"*\";\n  inherits: true;\n}\n\n@property --cui-chat-message-foreground {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-chat-message-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-chat-message-border-width {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-chat-message-shadow {\n  syntax: \"*\";\n  inherits: true;\n}\n"
    }
  ],
  "css": {
    "@import \"../components/control-ui/styles/recipes/chat-message.css\"": {}
  },
  "meta": {}
}
