{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "message-story",
  "title": "Message Story",
  "author": "Lloyd Richards <lloyd.d.richards@gmail.com>",
  "description": "Storybook stories demonstrating message component layout, avatars, actions, status, and attachments",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "message",
    "avatar",
    "bubble",
    "button",
    "attachment",
    "marker",
    "spinner"
  ],
  "files": [
    {
      "path": "registry/ui/message-story/message-radix.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport {\n  CopyIcon,\n  DownloadIcon,\n  FileTextIcon,\n  RefreshCcwIcon,\n} from \"lucide-react\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\nimport {\n  Attachment,\n  AttachmentAction,\n  AttachmentActions,\n  AttachmentContent,\n  AttachmentDescription,\n  AttachmentMedia,\n  AttachmentTitle,\n} from \"@/components/ui/attachment\";\nimport {\n  Avatar,\n  AvatarFallback,\n  AvatarImage,\n} from \"@/components/ui/avatar\";\nimport { Bubble, BubbleContent } from \"@/components/ui/bubble\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Marker,\n  MarkerContent,\n  MarkerIcon,\n} from \"@/components/ui/marker\";\nimport {\n  Message,\n  MessageAvatar,\n  MessageContent,\n  MessageFooter,\n  MessageGroup,\n  MessageHeader,\n} from \"@/components/ui/message\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\n/**\n * Lays out conversation messages with avatar, content, header, footer, and alignment.\n */\nconst meta: Meta<typeof Message> = {\n  title: \"ui/radix/Message\",\n  component: Message,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n  argTypes: {\n    align: {\n      control: \"select\",\n      options: [\"start\", \"end\"],\n    },\n  },\n  args: {\n    align: \"start\",\n  },\n  decorators: (Story) => (\n    <div className=\"w-full min-w-sm max-w-md\">\n      <Story />\n    </div>\n  ),\n} satisfies Meta<typeof Message>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\nfunction AvatarSlot() {\n  return (\n    <MessageAvatar>\n      <Avatar className=\"size-8\">\n        <AvatarImage src=\"https://github.com/shadcn.png\" alt=\"@shadcn\" />\n        <AvatarFallback>CN</AvatarFallback>\n      </Avatar>\n    </MessageAvatar>\n  );\n}\n\n/**\n * The default message row wraps a bubble surface.\n */\nexport const Default: Story = {\n  render: (args) => (\n    <Message {...args}>\n      <MessageContent>\n        <Bubble variant=\"secondary\">\n          <BubbleContent>How can I help you today?</BubbleContent>\n        </Bubble>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Align a message to the end for current-user messages.\n */\nexport const AlignedEnd: Story = {\n  args: {\n    align: \"end\",\n  },\n  render: (args) => (\n    <Message {...args}>\n      <MessageContent>\n        <Bubble>\n          <BubbleContent>Deploying to prod real quick.</BubbleContent>\n        </Bubble>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Add an avatar beside a message.\n */\nexport const WithAvatar: Story = {\n  render: (args) => (\n    <Message {...args}>\n      <AvatarSlot />\n      <MessageContent>\n        <Bubble variant=\"secondary\">\n          <BubbleContent>\n            The build failed during dependency installation.\n          </BubbleContent>\n        </Bubble>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Group consecutive messages from the same sender.\n */\nexport const Group: Story = {\n  render: () => (\n    <MessageGroup>\n      <Message>\n        <MessageAvatar />\n        <MessageContent>\n          <Bubble variant=\"secondary\">\n            <BubbleContent>I checked the registry addresses.</BubbleContent>\n          </Bubble>\n        </MessageContent>\n      </Message>\n      <Message>\n        <AvatarSlot />\n        <MessageContent>\n          <Bubble variant=\"secondary\">\n            <BubbleContent>\n              The component and example JSON now live under the UI registry.\n            </BubbleContent>\n          </Bubble>\n        </MessageContent>\n      </Message>\n    </MessageGroup>\n  ),\n};\n\n/**\n * Use MessageHeader for sender names or concise context.\n */\nexport const Header: Story = {\n  render: (args) => (\n    <Message {...args}>\n      <AvatarSlot />\n      <MessageContent>\n        <MessageHeader>Olivia</MessageHeader>\n        <Bubble variant=\"secondary\">\n          <BubbleContent>I already checked the logs.</BubbleContent>\n        </Bubble>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Use MessageFooter for delivery state or timestamps.\n */\nexport const Footer: Story = {\n  args: {\n    align: \"end\",\n  },\n  render: (args) => (\n    <Message {...args}>\n      <MessageContent>\n        <Bubble>\n          <BubbleContent>Send the report to the team.</BubbleContent>\n        </Bubble>\n        <MessageFooter>Read yesterday</MessageFooter>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Place message-level actions in MessageFooter.\n */\nexport const Actions: Story = {\n  render: (args) => (\n    <Message {...args}>\n      <MessageContent>\n        <Bubble variant=\"secondary\">\n          <BubbleContent>\n            The install failure is coming from the workspace package.\n          </BubbleContent>\n        </Bubble>\n        <MessageFooter className=\"gap-1\">\n          <Button variant=\"ghost\" size=\"icon-xs\" aria-label=\"Copy message\">\n            <CopyIcon />\n          </Button>\n          <Button variant=\"ghost\" size=\"icon-xs\" aria-label=\"Retry message\">\n            <RefreshCcwIcon />\n          </Button>\n        </MessageFooter>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Add attachments inside message content.\n */\nexport const WithAttachment: Story = {\n  render: (args) => (\n    <Message {...args}>\n      <AvatarSlot />\n      <MessageContent>\n        <Bubble variant=\"secondary\">\n          <BubbleContent>\n            Done. Here is the PDF with the image added as the cover page.\n          </BubbleContent>\n        </Bubble>\n        <Attachment>\n          <AttachmentMedia>\n            <FileTextIcon />\n          </AttachmentMedia>\n          <AttachmentContent>\n            <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>\n            <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>\n          </AttachmentContent>\n          <AttachmentActions>\n            <AttachmentAction aria-label=\"Download sales-dashboard.pdf\">\n              <DownloadIcon />\n            </AttachmentAction>\n          </AttachmentActions>\n        </Attachment>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Use a status marker for in-progress message work.\n */\nexport const WithStatus: Story = {\n  render: (args) => (\n    <Message {...args}>\n      <MessageContent>\n        <Marker role=\"status\">\n          <MarkerIcon>\n            <Spinner />\n          </MarkerIcon>\n          <MarkerContent>Checking the logs...</MarkerContent>\n        </Marker>\n      </MessageContent>\n    </Message>\n  ),\n};\n\n/**\n * Verify copy actions in the message footer are clickable.\n */\nexport const CopyAction: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => (\n    <Message>\n      <MessageContent>\n        <Bubble variant=\"secondary\">\n          <BubbleContent>Copy this message.</BubbleContent>\n        </Bubble>\n        <MessageFooter>\n          <Button variant=\"ghost\" size=\"icon-xs\" aria-label=\"Copy message\">\n            <CopyIcon />\n          </Button>\n        </MessageFooter>\n      </MessageContent>\n    </Message>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const button = canvas.getByRole(\"button\", { name: /copy message/i });\n\n    await userEvent.click(button);\n    expect(button).toHaveFocus();\n  },\n};\n\n/**\n * Verify retry actions in failed messages are clickable.\n */\nexport const RetryAction: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => (\n    <Message>\n      <MessageContent>\n        <Bubble variant=\"destructive\">\n          <BubbleContent>Failed to send.</BubbleContent>\n        </Bubble>\n        <MessageFooter>\n          <Button variant=\"ghost\" size=\"icon-xs\" aria-label=\"Retry message\">\n            <RefreshCcwIcon />\n          </Button>\n        </MessageFooter>\n      </MessageContent>\n    </Message>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const button = canvas.getByRole(\"button\", { name: /retry message/i });\n\n    await userEvent.click(button);\n    expect(button).toHaveFocus();\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/message.stories.tsx"
    }
  ],
  "categories": [
    "ui",
    "storybook",
    "message",
    "chat"
  ],
  "type": "registry:component"
}