{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "message",
  "title": "Message",
  "description": "A user's message in a bubble, or an answer as the page, with an actions row.",
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "registryDependencies": [
    "https://constella.aayush.cv/r/base.json"
  ],
  "files": [
    {
      "path": "src/components/ai/message.tsx",
      "content": "import * as stylex from \"@stylexjs/stylex\";\nimport type { ComponentProps } from \"react\";\n\nimport type { Sx } from \"@/lib/constella/styles\";\nimport { typeStyles } from \"@/lib/constella/styles\";\nimport { colors, radii, space } from \"@/lib/constella/tokens.stylex\";\n\nconst messageRoles = [\"user\", \"assistant\"] as const;\n\ntype MessageRole = (typeof messageRoles)[number];\n\ntype Styled<T> = Omit<T, \"className\" | \"style\"> & {\n  sx?: Sx;\n};\n\nconst fromUser = \":is([data-slot='message'][data-from='user'] > *)\";\nconst emptyReply =\n  \":is([data-slot='message']:has(> [data-slot='message-content']:empty) > *)\";\n\nconst styles = stylex.create({\n  message: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    gap: space.xs,\n    width: \"100%\",\n  },\n  user: {\n    alignItems: \"flex-end\",\n  },\n  // The user's words sit in a resting `fillOpaque` bubble; the answer reads as the page.\n  content: {\n    backgroundColor: { default: \"transparent\", [fromUser]: colors.fillOpaque },\n    borderStartStartRadius: { default: 0, [fromUser]: radii.lg },\n    borderStartEndRadius: { default: 0, [fromUser]: radii.lg },\n    borderEndStartRadius: { default: 0, [fromUser]: radii.lg },\n    borderEndEndRadius: { default: 0, [fromUser]: radii.lg },\n    color: colors.textPrimary,\n    maxWidth: { default: \"100%\", [fromUser]: \"80%\" },\n    overflowWrap: \"break-word\",\n    // A user's line breaks are part of what they wrote.\n    whiteSpace: { default: null, [fromUser]: \"pre-wrap\" },\n    paddingBlockEnd: { default: 0, [fromUser]: space.xs },\n    paddingBlockStart: { default: 0, [fromUser]: space.xs },\n    paddingInlineEnd: { default: 0, [fromUser]: space.md },\n    paddingInlineStart: { default: 0, [fromUser]: space.md },\n    width: \"fit-content\",\n  },\n  // Nothing to copy or rate until the reply has words, so they wait for its first.\n  actions: {\n    alignItems: \"center\",\n    color: colors.textSecondary,\n    display: { default: \"flex\", [emptyReply]: \"none\" },\n    gap: space.xxxs,\n  },\n});\n\n/** One turn of a conversation, from the user or the assistant. */\nfunction Message({\n  from,\n  sx,\n  ...props\n}: Styled<ComponentProps<\"div\">> & { from: MessageRole }) {\n  return (\n    <div\n      data-from={from}\n      data-slot=\"message\"\n      {...props}\n      {...stylex.props(styles.message, from === \"user\" && styles.user, sx)}\n    />\n  );\n}\n\nfunction MessageContent({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"message-content\"\n      {...props}\n      {...stylex.props([typeStyles.textBase, styles.content], sx)}\n    />\n  );\n}\n\n/** A row of actions under a message: copy, retry, rate. */\nfunction MessageActions({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"message-actions\"\n      {...props}\n      {...stylex.props(styles.actions, sx)}\n    />\n  );\n}\n\nexport { Message, MessageActions, MessageContent, messageRoles };\nexport type { MessageRole };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Usage: https://constella.aayush.cv/docs/components/message/index.md",
  "type": "registry:ui"
}